ShellCode
익스플로잇을 위해 제작된 어셈블리 코드 조각
목적 ) shell을 획득하기 위함
* 어셈블리어 ↔ 기계어 : 일대일 대응 ▶ 원하는 모든 명령을 CPU에 내릴 수 있음
orw shell code
shellcode를 깊이 있게 이해해보기 위해 예시 shellcode를 만들어보겠다.
/tmp/flag 파일을 열고, 읽은 뒤 화면에 출력해주는 shell code
char buf[0x30];
int fd = open("/tmp/flag", RD_ONLY, NULL);
read(fd, buf, 0x30);
write(1, buf, 0x30);
>/tmp/flag를 읽는 shell code를 c언어 형식의 의사코드로 표현한 것
<Syscall Table>
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 |
/* File access modes for `open' and `fcntl'. */
#define O_RDONLY 0 /* Open read-only. */
#define O_WRONLY 1 /* Open write-only. */
#define O_RDWR 2 /* Open read/write. */
> reference : https://codebrowser.dev/glibc/glibc/bits/fcntl.h.html#24
fcntl.h source code [glibc/bits/fcntl.h] - Codebrowser
Warning: This file is not a C or C++ file. It does not have highlighting. 1/* O_*, F_*, FD_* bit values. 4.4BSD/Generic version. 2 Copyright (C) 1991-2022 Free Software Foundation, Inc. 3 This file is part of the GNU C Library. 4 5 The GNU C Library is fre
codebrowser.dev
1) int fd = open("/tmp/flag", O_RDONLY, NULL)
▶ "/tmp/flag" 문자열 메모리에 위치시키기
- /tmp/flag(0x616c662f706d742f67) push
* 8바이트 단위로 push 할 수 있으므로 0x67 먼저 push한 뒤 0x616c662f706d742f push해야함
▶레지스터에 맞는 값 넣기
- rdi가 이를 가르키도록 함 → rdi에 rsp 값을 복사(현재 스택포인터 값)
- rdi (filename) : rsp를 가르킴
- rsi (flags) : O_RDONLY > 0
- rdx (mode) : mode 의미 없음 > 0
- rax : open의 syscall 값 > 2
push 0x67 ; 0x67 push > rsp 이동
mov rax, 0x616c662f706d742f ; rax에 8바이트 넣기
push rax ; rax에 저장한 값을 push
mov rdi, rsp ; rdi = "/tmp/flag"
xor rsi, rsi ; rsi = 0 ; RD_ONLY
xor rdx, rdx ; rdx = 0 ; mode x
mov rax, 2 ; rax = 2 ; syscall_open
syscall ; open("/tmp/flag", RD_ONLY, NULL)
> 구현한 어셈블리 코드
*syscall 의 반환 값 > rax 저장
2) read(fd, buf, 0x30)
▶레지스터에 맞는 값 넣기
- rax에 syscall에 반환값인 fd가 있으므로 rdi에 rax 값을 복사함
- rdi (fd) : rax를 가르킴
- rsi (buf) : 읽을 데이터를 저장할 주소 > rsp-0x30(0x30만큼 읽을 것이므로)
- rdx (count) : 읽어낼 데이터의 길이 > 0x30
- rax : read의 syscall 값 > 0
mov rdi, rax ; rdi = fd
mov rsi, rsp ; 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)
- rdi (fd) : stdout 출력 > 0x1
- rax : write의 syscall 값 > 1
read랑 같은 값을 쓰는 레지스터
- rsi (buf) : 읽을 데이터를 저장할 주소 > rsp-0x30(0x30만큼 읽을 것이므로)
- rdx (count) : 읽어낼 데이터의 길이 > 0x30
mov rdi, 1 ; rdi = 1 ; fd = stdout
mov rax, 0x1 ; rax = 1 ; syscall_write
syscall ; write(fd, buf, 0x30)
> 구현한 어셈블리 코드
orw shell code compile
push 0x67 ; 0x67 push > rsp 이동
mov rax, 0x616c662f706d742f ; rax에 8바이트 넣기
push rax ; rax에 저장한 값을 push
mov rdi, rsp ; rdi = "/tmp/flag"
xor rsi, rsi ; rsi = 0 ; RD_ONLY
xor rdx, rdx ; rdx = 0 ; mode x
mov rax, 2 ; rax = 2 ; syscall_open
syscall ; open("/tmp/flag", RD_ONLY, NULL)
mov rdi, rax ; rdi = fd
mov rsi, rsp ; 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)
mov rdi, 1 ; rdi = 1 ; fd = stdout
mov rax, 0x1 ; rax = 1 ; syscall_write
syscall ; write(fd, buf, 0x30)
> orw.S
__asm__(
".global run_sh\n"
"run_sh:\n"
"push 0x67\n"
"mov rax, 0x616c662f706d742f \n"
"push rax\n"
"mov rdi, rsp # rdi = '/tmp/flag'\n"
"xor rsi, rsi # rsi = 0 ; RD_ONLY\n"
"xor rdx, rdx # rdx = 0\n"
"mov rax, 2 # rax = 2 ; syscall_open\n"
"syscall # open('/tmp/flag', RD_ONLY, NULL)\n"
"\n"
"mov rdi, rax # rdi = fd\n"
"mov rsi, rsp\n"
"sub rsi, 0x30 # rsi = rsp-0x30 ; buf\n"
"mov rdx, 0x30 # rdx = 0x30 ; len\n"
"mov rax, 0x0 # rax = 0 ; syscall_read\n"
"syscall # read(fd, buf, 0x30)\n"
"\n"
"mov rdi, 1 # rdi = 1 ; fd = stdout\n"
"mov rax, 0x1 # rax = 1 ; syscall_write\n"
"syscall # write(fd, buf, 0x30)\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(); }
> orw.c
orw shell code debugee
Breakpoint 1, 0x0000555555555129 in run_sh ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────
RAX 0x0
RBX 0x0
*RCX 0x555555557df8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x5555555550e0 (__do_global_dtors_aux) ◂— endbr64
*RDX 0x7fffffffdff8 —▸ 0x7fffffffe37f ◂— 'SHELL=/bin/bash'
*RDI 0x1
*RSI 0x7fffffffdfe8 —▸ 0x7fffffffe358 ◂— '/home/minzu/exploit_test/shellcode/orw'
*R8 0x7ffff7e1bf10 (initial+16) ◂— 0x4
*R9 0x7ffff7fc9040 (_dl_fini) ◂— endbr64
*R10 0x7ffff7fc3908 ◂— 0xd00120000000e
*R11 0x7ffff7fde660 (_dl_audit_preinit) ◂— endbr64
*R12 0x7fffffffdfe8 —▸ 0x7fffffffe358 ◂— '/home/minzu/exploit_test/shellcode/orw'
*R13 0x55555555517e (main) ◂— endbr64
*R14 0x555555557df8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x5555555550e0 (__do_global_dtors_aux) ◂— endbr64
*R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
*RBP 0x7fffffffded0 ◂— 0x1
*RSP 0x7fffffffdec8 —▸ 0x555555555190 (main+18) ◂— mov eax, 0
*RIP 0x555555555129 (run_sh) ◂— push 0x67
──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
► 0x555555555129 <run_sh> push 0x67
0x55555555512b <run_sh+2> movabs rax, 0x616c662f706d742f
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
0x555555555148 <run_sh+31> mov rdi, rax
0x55555555514b <run_sh+34> mov rsi, rsp
0x55555555514e <run_sh+37> sub rsi, 0x30
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rsp 0x7fffffffdec8 —▸ 0x555555555190 (main+18) ◂— mov eax, 0
01:0008│ rbp 0x7fffffffded0 ◂— 0x1
02:0010│+008 0x7fffffffded8 —▸ 0x7ffff7c29d90 (__libc_start_call_main+128) ◂— mov edi, eax
03:0018│+010 0x7fffffffdee0 ◂— 0x0
04:0020│+018 0x7fffffffdee8 —▸ 0x55555555517e (main) ◂— endbr64
05:0028│+020 0x7fffffffdef0 ◂— 0x100000000
06:0030│+028 0x7fffffffdef8 —▸ 0x7fffffffdfe8 —▸ 0x7fffffffe358 ◂— '/home/minzu/exploit_test/shellcode/orw'
07:0038│+030 0x7fffffffdf00 ◂— 0x0
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► 0 0x555555555129 run_sh
1 0x555555555190 main+18
2 0x7ffff7c29d90 __libc_start_call_main+128
3 0x7ffff7c29e40 __libc_start_main+128
4 0x555555555065 _start+37
────────────────────────────────────────────────────────────────────────────────
1) int fd = open("tmp/flag", O_RDONLY, NULL)
Breakpoint 2, 0x0000555555555146 in run_sh ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────[ 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 0x7fffffffdeb8 ◂— '/tmp/flag'
*RSI 0x0
R8 0x7ffff7e1bf10 (initial+16) ◂— 0x4
R9 0x7ffff7fc9040 (_dl_fini) ◂— endbr64
R10 0x7ffff7fc3908 ◂— 0xd00120000000e
R11 0x7ffff7fde660 (_dl_audit_preinit) ◂— endbr64
R12 0x7fffffffdfe8 —▸ 0x7fffffffe358 ◂— '/home/minzu/exploit_test/shellcode/orw'
R13 0x55555555517e (main) ◂— endbr64
R14 0x555555557df8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x5555555550e0 (__do_global_dtors_aux) ◂— endbr64
R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
RBP 0x7fffffffded0 ◂— 0x1
*RSP 0x7fffffffdeb8 ◂— '/tmp/flag'
*RIP 0x555555555146 (run_sh+29) ◂— syscall
──────────────────────[ 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: 0x7fffffffdeb8 ◂— '/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
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rdi rsp 0x7fffffffdeb8 ◂— '/tmp/flag'
01:0008│-010 0x7fffffffdec0 ◂— 0x67 /* 'g' */
02:0010│-008 0x7fffffffdec8 —▸ 0x555555555190 (main+18) ◂— mov eax, 0
03:0018│ rbp 0x7fffffffded0 ◂— 0x1
04:0020│+008 0x7fffffffded8 —▸ 0x7ffff7c29d90 (__libc_start_call_main+128) ◂— mov edi, eax
05:0028│+010 0x7fffffffdee0 ◂— 0x0
06:0030│+018 0x7fffffffdee8 —▸ 0x55555555517e (main) ◂— endbr64
07:0038│+020 0x7fffffffdef0 ◂— 0x100000000
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► 0 0x555555555146 run_sh+29
1 0x616c662f706d742f
2 0x67
3 0x555555555190 main+18
4 0x7ffff7c29d90 __libc_start_call_main+128
5 0x7ffff7c29e40 __libc_start_main+128
6 0x555555555065 _start+37
────────────────────────────────────────────────────────────────────────────────
> rax : open(2) / rdi : /tmp/flag
2) read(fd, buf, 0x30)
Breakpoint 3, 0x0000555555555160 in run_sh ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────
*RAX 0x0
RBX 0x0
*RCX 0x555555555044 (_start+4) ◂— xor ebp, ebp
*RDX 0x30
*RDI 0xfffffffffffffffe
*RSI 0x7fffffffde88 ◂— 0x0
R8 0x7ffff7e1bf10 (initial+16) ◂— 0x4
R9 0x7ffff7fc9040 (_dl_fini) ◂— endbr64
R10 0x7ffff7fc3908 ◂— 0xd00120000000e
*R11 0x346
R12 0x7fffffffdfe8 —▸ 0x7fffffffe358 ◂— '/home/minzu/exploit_test/shellcode/orw'
R13 0x55555555517e (main) ◂— endbr64
R14 0x555555557df8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x5555555550e0 (__do_global_dtors_aux) ◂— endbr64
R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
RBP 0x7fffffffded0 ◂— 0x1
RSP 0x7fffffffdeb8 ◂— '/tmp/flag'
*RIP 0x555555555160 (run_sh+55) ◂— syscall
──────────────────────[ DISASM / x86-64 / set emulate on ]──────────────────────
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
► 0x555555555160 <run_sh+55> syscall <SYS_read>
fd: 0xfffffffffffffffe
buf: 0x7fffffffde88 ◂— 0x0
nbytes: 0x30
0x555555555162 <run_sh+57> mov rdi, 1
0x555555555169 <run_sh+64> mov rax, 1
0x555555555170 <run_sh+71> syscall
0x555555555172 <run_sh+73> xor rdi, rdi
0x555555555175 <run_sh+76> mov rax, 0x3c
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rsp 0x7fffffffdeb8 ◂— '/tmp/flag'
01:0008│-010 0x7fffffffdec0 ◂— 0x67 /* 'g' */
02:0010│-008 0x7fffffffdec8 —▸ 0x555555555190 (main+18) ◂— mov eax, 0
03:0018│ rbp 0x7fffffffded0 ◂— 0x1
04:0020│+008 0x7fffffffded8 —▸ 0x7ffff7c29d90 (__libc_start_call_main+128) ◂— mov edi, eax
05:0028│+010 0x7fffffffdee0 ◂— 0x0
06:0030│+018 0x7fffffffdee8 —▸ 0x55555555517e (main) ◂— endbr64
07:0038│+020 0x7fffffffdef0 ◂— 0x100000000
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► 0 0x555555555160 run_sh+55
1 0x616c662f706d742f
2 0x67
3 0x555555555190 main+18
4 0x7ffff7c29d90 __libc_start_call_main+128
5 0x7ffff7c29e40 __libc_start_main+128
6 0x555555555065 _start+37
────────────────────────────────────────────────────────────────────────────────
> rax : read(0) / rdx : 0x30 / rsi : rsb - 0x30
3) write(1,buf,0x30)
Breakpoint 4, 0x0000555555555170 in run_sh ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────
*RAX 0x1
RBX 0x0
RCX 0x555555555044 (_start+4) ◂— xor ebp, ebp
RDX 0x30
RDI 0x1
RSI 0x7fffffffde88 ◂— 0x0
R8 0x7ffff7e1bf10 (initial+16) ◂— 0x4
R9 0x7ffff7fc9040 (_dl_fini) ◂— endbr64
R10 0x7ffff7fc3908 ◂— 0xd00120000000e
R11 0x306
R12 0x7fffffffdfe8 —▸ 0x7fffffffe357 ◂— '/home/minzu/exploit_test/shellcode/orw'
R13 0x55555555517e (main) ◂— endbr64
R14 0x555555557df8 (__do_global_dtors_aux_fini_array_entry) —▸ 0x5555555550e0 (__do_global_dtors_aux) ◂— endbr64
R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
RBP 0x7fffffffded0 ◂— 0x1
RSP 0x7fffffffdeb8 ◂— '/tmp/flag'
*RIP 0x555555555170 (run_sh+71) ◂— syscall
──────────────────────[ 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/0)
buf: 0x7fffffffde88 ◂— 0x0
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
───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ rsp 0x7fffffffdeb8 ◂— '/tmp/flag'
01:0008│-010 0x7fffffffdec0 ◂— 0x67 /* 'g' */
02:0010│-008 0x7fffffffdec8 —▸ 0x555555555190 (main+18) ◂— mov eax, 0
03:0018│ rbp 0x7fffffffded0 ◂— 0x1
04:0020│+008 0x7fffffffded8 —▸ 0x7ffff7c29d90 (__libc_start_call_main+128) ◂— mov edi, eax
05:0028│+010 0x7fffffffdee0 ◂— 0x0
06:0030│+018 0x7fffffffdee8 —▸ 0x55555555517e (main) ◂— endbr64
07:0038│+020 0x7fffffffdef0 ◂— 0x100000000
─────────────────────────────────[ BACKTRACE ]──────────────────────────────────
► 0 0x555555555170 run_sh+71
1 0x616c662f706d742f
2 0x67
3 0x555555555190 main+18
4 0x7ffff7c29d90 __libc_start_call_main+128
5 0x7ffff7c29e40 __libc_start_main+128
6 0x555555555065 _start+37
────────────────────────────────────────────────────────────────────────────────
> rax : write(1) / rid : stdout(1)
Question & Learn
Assembly | push의 operand 제한
mov rax, 0x616c662f706d742f ; rax에 8바이트 넣기
push rax ; rax에 저장한 값을 push
Q) 왜 바로 push하는게 아닌, rax에 넣은 후에 push를 하는 것인가?
만약, rax에 넣지 않고 바로 push하게 된다면
/tmp/cc2fWjNo.s:8: Error: operand type mismatch for 'push'
라는 오류가 발생한다.
push의 경우, 32비트 정수까지만 push를 할 수 있기 때문에, 이 이상인 경우 레지스터에 넣어 push해주도록 하자
C언어로 어셈블리 코드를 ELF로 바꿀 때 \n를 넣는 이유
어셈블리 코드를 다른 줄로 인식하게끔 하여, 다음 쉘 코드까지 인식하여 혼란이 없도록 한다.
마지막 코드 부분을 보면, syscall로 마무리를 한다.
이는 리눅스 시스템 호출 인터페이스를 이용해 정상종료 exit(0)을 실행하는 것이다.
이것이 필요한 이유는 프로그램이 리턴 없이 계속 실행되면 잘못된 주소 참조로 세그폴트가 일어나게 된다.
혹은, shellcode 이후의 다른 쓰레기 값을 읽게 되면서 의도하지 않은 결과가 생길 수 있다.
shellcode에서 실행이 안전하게 종료되도록 한다.
reference
- Dreamhack SystemHacking > ExploitTech : Shellcode
'Hacking > Pwnable' 카테고리의 다른 글
[Mitigation] Stack Canary (0) | 2024.08.24 |
---|---|
gdb를 pwntools와 디버깅하기 (0) | 2024.07.26 |
[ Domato ] 사용법 (4) | 2024.05.18 |
[Memory Corruption] Stack Buffer Overflow (1) | 2024.01.29 |
[Exploit] execve shell code (2) | 2024.01.27 |