0.前言
题目:点击下载ELF文件
保护:未开任何保护
其他:32位ELF,编译环境是Uubuntu14
说实话,这道题对pwn新人不算特别友好,主要难点有两个:一是由于汇编码中调用了call eax导致无法直接查看其伪C代码(这个在当时比赛中确实难住了我,因为以我现在的水平直接审计汇编代码属实头疼,但赛后我找到了解决的方法——很简单,因为错误指发生在call eax,那么把这行代码去掉或者替换就行了),解决后查看伪C代码逻辑就简单了;二是这一题虽然是栈的题目,但不涉及溢出,但只要看懂了伪C代码不难想到使用shellcode,但是根据题目我们最多只能输入19字节,而pwntools自带的shellcode远超这个字节数。所以需要准备小于19字节的shellcode。(因此我认为新人包括我在内要去积累一些较短的shellcode以备不时之需。)
1.问题的产生
之前说了,这道题理论上只要能输入一个小于19字节的shellcode就可以获取shell。那么存在这样的shellcode吗?
答案是存在的,而且只有18字节(i386):
1 | push 0xb |
因此,exp可以这样写:
1 | from pwn import * |
然而,当我信心满满以为打通了的时候……
是的,失败了。
2.探究历程
那么问题就来了,是什么导致了失败?从原理上讲,这个方法没有任何问题才对。于是去尝试debug了一下,发现传入没有任何问题,就是妥妥的18字节。于是我思考了几天这个问题:究竟是什么地方出现了问题?
首先我们要验证一件事:就是这个方法有没有打通的可能。答案是肯定的:可以打通。因为当时比赛时有一个学长就是通过这个方法打通的。通过另一个学长,我了解到了他当时用的是kali。
于是我又连夜去下了一个Kali,然后发现:
通了!它通了!也就是说这种方法是可以打通的。
那么问题又来了:为什么在Kali上能运行,但在Ubuntu上却不能呢?首先考虑的是会不会因为Ubuntu和Kali的shell不同,但很快就被否定掉了:因为实战上是可以打通的,而靶机是Ubuntu系统,因此不是shell的问题。然后考虑的是写入时出现了错误,但这个也因为debug排除了。然后我想的是libc等方面的问题,但当时始终没找到答案。
3.解决
于是我就去群里问了学长他们。在询问的过程中我发现了一点,那就是并不是所有的Ubuntu都没法用这种方法打通,像是18.04的就可以,而我用的是16.04的。后来学长指出,16.04之所以没法成功调用,是因为其参数对应的寄存器的值有问题,如下:(由学长提供)
而18.04的寄存器的值如下图:(同样由学长提供)
可以发现,在18.04的Ubuntu里,ebx、ecx、edx的值都是0,因此不需要控制其他参数,直接传入/bin/sh即可getshell。而Kali的同理。
(这里插一句,说来很惭愧,我最近才知道32系统调用表和64的是不一样的……看来我对函数调用约定理解不深。)
附上系统调用的参考链接。
那么是不是16.04就没有打通的办法了呢?之前说了,16.04无法正常执行shellcode的原因是因为寄存器值异常导致参数异常。因此,我们只要扩大输入的长度,确保我们的shellcode能将参数都置0,即可正常getshell。
具体的exp见学长的博客,因为不是我写出来的,这里不给出。
4.总结
关于栈题目,我之前的惯性思维都是和溢出相关。但这题打破了我对栈题的惯性思维,也让我知道了我之前对ret2shellcode方法的不重视导致我对shellcode无法熟练运用。还有很多存在的问题(例如对函数调用约定的不深入理解、汇编代码的阅读不熟练等),我会在日后的学习中改正解决。