今天做了一下午的比较复杂的题,写一下wp,不能保证没有错误,欢迎大家指出
拿到题目后一开始还没意识到是花指令混淆反汇编,用pwndbg调试了很长时间,问了群里的大佬才意识到这点,事实上flower已经暗示了这个为花指令混淆;
首先file和checksec一下:
发现是一个静态编译的64位程序,而且开了NX堆栈不可执行;
静态编译程序无动态链接库,ret2libc是行不通的,考虑写入shellcode后ret2shellcode;
拖进ida64, 发现ida64逆向出来了一堆看不懂的奇怪函数,而且无法找到main函数:
问了问群里的大佬,说是这个不是main函数,先运行一下看看:
直接运行看上去很正常,去ida里找字符串:
根据字符串找交叉引用,发现了没有被反汇编的main函数:
这里被标红说明有问题,注意这里的jz和jnz:
jz和jnz指向了同一个地址,说明无论ZF是何值都会跳转,且夹在中间的指令为死指令,不会被执行,
我们把这两个地址之间的指令全改成0x90(nop):
去main上按p重新生成函数:
发现还有一段没有接上;
这时删除下边那个400A30函数,把main的结束地址放到400A74:
成功合并拿到真正的main函数:
这样就能够接着正常分析了。
先看输入:第一个scanf接受一个%d整数,没有什么溢出点,不过程序限制了v9的大小不能超过10;而下一个read会将读入v9个数据,程序想通过这种方式限制输入量。
事实上,注意v9(unsigned int)和(int)的类型转换,我们可以随便输入负数来绕过检查来几乎无限制地读取数据;
所以第一步:
1 | p.sendlineafter(b'> ', b'-22') |
接下来尝试栈溢出。前文提到过ret2shellcode,我们先寻找一下可读可写可执行的地址:
发现没有合适的段,这时寻找能改变权限的函数mprotect:
该函数的应用在网上查了查,需要三个参数,ROPgadget一下发现pop rdi,rsi,rdx都能找到:
v8在栈上有0x50个空间,接下来开始构造payload的第一部分:
1 | payload = b'a' * 88 |
我们计划向0x400000段写入shellcode,因此选择了修改该地址的权限
接下来拿read函数向该段写入shellcode:
1 | payload += p64(rdxAddr) |
由于能够读入很长的字段,我们直接接着构造payload的第二部分,最后返回地址写成0x400000;
接下来输入shellcode:
1 | shellcode = asm(shellcraft.sh()) |
远程试一下(有时会收到EOFerror,再试一次就行),成功拿到flag:
还有一种做法是通过ROPgadget内置的ropchain来自动生成getshell脚本(一般最终是通过syscall执行execve(“/bin/sh”, 0, 0)),用法就是–ropchain,这里就不再赘述了。
静态编译的弱点就是会一股脑地编译大量函数,各种gadgets非常好寻找,构造rophchain的方法非常灵活,这只是一个例子。
完整exp:
1 | from pwn import * |