RUN sed -i -re 's/([a-z]{2}.)?archive.ubuntu.com|security.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list
dockerfile이 주어지면 제일먼저 위 구문을 상단에 박아둔다. 한국에 있기 때문에 apt 서버를 카카오미러로 바꾸면 매우 빠르게 도커 파일을 빌드할 수 있음.
run 할 때 -v
옵션과(호스트 머신과 FS공유하기 위함) --cap-add=SYS_PTRACE
를 통해 debuggable하게 설정해주면 된다.
docker run -i -t --entrypoint bash -v /tmp:/1234 -v gateway:/app/gateway -p '127.0.0.1:1235:9999' --name "ps_name" image_name
➜ binary_flag wc -l patch.diff
2818 patch.diff
➜ binary_flag
--> 양심이 없다, 혹시 몰라 add된 것을 검색해봤더니 이미 있는 경우가 있어, 새로 받아서 넣어봤다. (radare2-extras)
➜ radare2-extras git:(master) ✗ git remote -v
origin https://github.com/radareorg/radare2-extras.git (fetch)
origin https://github.com/radareorg/radare2-extras.git (push)
new file: libr/asm/arch/pyc/pyc_disasm.c
new file: libr/asm/p/Makefile.orig
new file: libr/asm/p/Makefile.rej
new file: libr/bin/p/Makefile.orig
new file: libr/bin/p/Makefile.rej
new file: plugins.def.cfg.orig
new file: prob/Makefile
new file: prob/main.c
코럼 그렇지~ 문제 파일과 pyc_disasm.c만 추가 되었다. 300줄 가량으로 줄어들었다.
충성 군인 임준오는 밤 9시가 되면 잠이 온다. (= 코드가 눈에 안들어온다.) 분석 좀 하다가 뭐가 뭔지 모르겠어서 우주의 기운을 빌려 퍼징을 하기로 했다.
pyc 포맷을 몰라 컴파일 해서 쓰기로 했다. 처음에 당연히 python3 인줄 알았는데 로드하니깐 에러가 났다. 시대가 언젠데 파이썬2를? ㅎㅁㅎ 무튼 컴파일은 아래와 같이 했다.
import py_compile
py_compile.compile("pypypyppyp.py")
첫번째 시드는 "왜"인지는 기억이 안나지만 코드를 보아하니 튜플과 스트링을 넣어야 터질 것 같이 생겨먹어서 나의 운에 맡겨 보기로 했다.
a=["absadfsadfsadfasdfasdfsadfsadfsadfsadfsadf", "sdafjnasdjkfnsdakjfnjk23n4kj32n4n32kj4nkjsdnakjfasdf", "dsfjsnafjlksandflkm2l3k4mlkxczmvlkxzmcklvzxc", 2134123128,123123,123123123,1231.23123123,"sdfkmnsdlkafmasdlkfasdfasdf"]
b=["absadfsadfsadfasdfasdfsadfsadfsadfsadfsadf", "sdafjnasdjkfnsdakjfnjk23n4kj32n4n32kj4nkjsdnakjfasdf", "dsfjsnafjlksandflkm2l3k4mlkxczmvlkxzmcklvzxc", 2134123128,123123,123123123,1231.23123123,"sdfkmnsdlkafmasdlkfasdfasdf"]
c=["absadfsadfsadfasdfasdfsadfsadfsadfsadfsadf", "sdafjnasdjkfnsdakjfnjk23n4kj32n4n32kj4nkjsdnakjfasdf", "dsfjsnafjlksandflkm2l3k4mlkxczmvlkxzmcklvzxc", 2134123128,123123,123123123,1231.23123123,"sdfkmnsdlkafmasdlkfasdfasdf"]
d=["absadfsadfsadfasdfasdfsadfsadfsadfsadfsadf", "sdafjnasdjkfnsdakjfnjk23n4kj32n4n32kj4nkjsdnakjfasdf", "dsfjsnafjlksandflkm2l3k4mlkxczmvlkxzmcklvzxc", 2134123128,123123,123123123,1231.23123123,"sdfkmnsdlkafmasdlkfasdfasdf"]
e=["absadfsadfsadfasdfasdfsadfsadfsadfsadfsadf", "sdafjnasdjkfnsdakjfnjk23n4kj32n4n32kj4nkjsdnakjfasdf", "dsfjsnafjlksandflkm2l3k4mlkxczmvlkxzmcklvzxc", 2134123128,123123,123123123,1231.23123123,"sdfkmnsdlkafmasdlkfasdfasdf"]
f=["absadfsadfsadfasdfasdfsadfsadfsadfsadfsadf", "sdafjnasdjkfnsdakjfnjk23n4kj32n4n32kj4nkjsdnakjfasdf", "dsfjsnafjlksandflkm2l3k4mlkxczmvlkxzmcklvzxc", 2134123128,123123,123123123,1231.23123123,"sdfkmnsdlkafmasdlkfasdfasdf"]
print a, b, c, d, e, f
아마 여기서 널디레가 떴다.. (모니터 만들기 귀찮아서 30개씩 돌리고 세그폴 나면 손으로 미니마이즈 했다 ㅎ..)
from pwn import *
import py_compile
context.terminal = ['tmux', 'splitw', '-h']
py_compile.compile("r.py")
import os
r = process('/usr/local/bin/prob', aslr=True)
# r = remote('211.117.60.23', 7777)
for i in xrange(0, 20):
os.system('cat r.pyc | ./radamsa > seeds/b%d.pyc' % i)
data = open('seeds/b%d.pyc' % i, 'rb').read()
# data = open('get_list_0_full.pyc', 'rb').read()
# data = open('heap_leak.pyc', 'rb').read()
print 'len', len(data)
if len(data) > 4096: continue
r.sendline('1')
r.sendlineafter('length:', str(len(data)))
r.send(data)
data = r.recvuntil('[1]')
context.log_level = 'debug'
import string
count = 0
while True:
r.sendline('2')
r.sendline(str(count))
data = r.recvuntil('[1]')
r.interactive()
우선 sigsegv가 발생하면 pwntools에서 알려주기 때문에 시드를 바꿔가면서 돌려봤다. (numpy, os, 등등)
그러던 중 우주가 나에게 퍼저 코드를 퍼징하면 어떨까? 라는 질문을 던져주셨다. 대충 검색해서 맨 위에 나온 퍼저를 시드로 해서 돌려봤다. (https://github.com/nccgroup/Hodor/blob/01be1077a1ede236fac78103816e7d58b64e43e6/post_hodor.py)
그랬더니 This module handles the processing of mutated output
이 문자열 즈음.. [rax] 레퍼런스 하다가 죽는것이 아니겠는가 ?!
소스코드 심볼까지 다 있어서 대충 분석했더니 list->head
가 덮이는 것이었다. list
는 r2에서 제공하는 data structure이다. 해당 자료구조를 쉽게 사용할 수 있도록 API를 제공해주는데 r_list_get_n
을 하다가 죽는 것이었다. 그럼 이제 임의 주소를 list
의 elem으로 줄 수 있으니 주소를 알아내야 한다.
난 우주의 기운을 받은 사나이.
count = 0
while True:
r.sendline('2')
r.sendline(str(count))
data = r.recvuntil('[1]')
for y in list(set([chr(x) for x in range(256)]) - set(list(string.printable))):
if data.find(y) != -1:
print('found nonprintable char!!!!!')
raw_input('zzzz!@@!')
count += 1
r.interactive()
위 코드를 추가해두고 이번엔 다른 퍼저 (https://github.com/rmadair/fuzzer/blob/bf77b05d19aa53d6317481318faf5598e7fd65f2/client.py#L27) 를 시드로 해서 돌렸더니,,, 하 역시!!!!!
ㅋㅋ
힙 주소 아니깐 근처에 libc관련 주소들 (search -t dword 0x7f??
) 찾아서 오프셋으로 libc_base를 구해올 수 있다. 미니마이저 / 모니터 없이 손으로 libc base 까지 구하는데 2시간 정도 소요되었다. Docker 환경이랑 local과 remote의 동작이 정확히 일치해 리모트에서도 릭이 정상적으로 되었다.
이제 write만 찾으면 끝나는데, 원래 전략은 이러했다.
(1) 한번 더 우주의 기운을 받아 r_list_appned
함수의 인자를 조작할 수 있다. (?)
(2) r_list_get_n
의 elem의 데이터를 잘 조작해 arbitrary address에 arbitrary value를 write하게 하자였다..
결론은 그런거 없었다. (2번이 인텐디드 솔루션이었는데 내 시드에 constant tuple가 없어서 안나온듯, (이거 쓰면서 넣고 해도 안나왔음 ㅜ)) 좀 더하다가 가망이 없어보이고 졸려서 꿈나라로 도망갔다 (ㅜㅜ) -- 이때가 한 4시였나 그랬는데 그냥 자도 파이널은 갈 것 같았다ㅋㅋㅋ (halfeed 못 풀었으면 광탈 할 뻔 했지만.,.)
대회가 끝나고 출제자의 공식 라이트업이 올라왔다. (https://pr0cf5.github.io/ctf/2020/02/09/exploiting-a-bug-in-radare-plugin.html)
char *generic_array_obj_to_string (RList *l) {
RListIter *iter = NULL;
pyc_object *e = NULL;
ut32 size = 256, used = 0;
char *r = NULL, *buf = NULL;
buf = (char*)calloc (1024, 0);
r_list_foreach (l, iter, e) {
while ( !(strlen (e->data) < size) ) { /* [1] */
size *= 2;
buf = realloc (buf, used + size);
if (!buf) {
eprintf ("generic_array_obj_to_string cannot request more memory");
return NULL;
}
}
strcat (buf, e->data);
strcat (buf, ",");
size -= strlen (e->data) + 1; /* [2] */
used += strlen (e->data) + 1;
}
/* remove last , */
buf[ strlen(buf)-1 ] = '\0';
r = r_str_newf ("(%s)", buf);
free(buf);
return r;
}
그렇다. 흠 tuple list 갖고오다가 뒤지는거를 열심히 퍼징했지만 안나와서 일단은 여기까지만 하고,, 최대한 빠른 시일내에 퍼징으로 풀어보겠다.. 시드를 잘 맞춰야겠다 ㅋㅋㅋ
010 editor structure file등,, 이미 알려진 포맷을 자동으로 임포트해서 (타입 같은거) 쉽게 퍼징할 수 있는 라이브러리를 만들어야겠다.
heap_leak.pyc
arbitrary_read.pyc