Pwnable.tm题解
simple pwn
Pwnable.tw题解
最近重建了博客,放了一下之前写的,通过科学上网体验更佳,有些图我用Twitter做的外链
start
这道题目网上有很多的题解,但是基本都没有说的很清楚,所以我决定以非常入门,非常清晰的思路去讲解一下这道Pwn的入门题目:
1 | public _start |
直接来上汇编代码吧,这其中有三个系统调用,并且没有main函数,所以应该是内联汇编写的,大概翻译一下就是:
1 | //read 和 write 系统调用都是采用中断的方式调用的 |
这个时候的栈结构大概就是(至于为什么是这样就不解释了):

write系统调用的作用就是把当前栈顶的数据给打印出来,bin里的调用打印长度为0x14,这也就刚好和我们看到的对上了,但是下面的read系统调用却可以读进0x3c个字节,显然会造成一个栈溢出,所以首先我们要做的就是leak基址
因为write可以打印栈顶0x14的数据,所以应该劫持控制流到这个函数的位置,所以我们第一步构造的payload应该为
1 | # 'A'*20是因为有add esp,0x14,sys_write_addr可以从ida中获取 |
这样当继续执行汇编指令到add esp,0x14是,此时esp就指向原来的exit,此时已经被sys_write_addr覆盖,然后执行ret,将栈顶弹给eip,相当于跳回了:
1 | mov ecx, esp ; addr <-------jmp_addr |
因为ret相当于pop esp,所以此时esp应该指向最早push的那个esp,通过gdb-peda调试我们可以发现最早push进栈的那个esp中存储的是自己下面的那个数的地址,此刻的栈状况应该为:

然后执行sys_write,就会打印出来:
1 | //这里的esp为最早push的esp值,指向0x1 |
接着又会进入到sys_read了,一定要记住一点,sys_read也是从当前栈顶开始读入,因为我在看其他人的题解的时候就发现有人犯了这样的错误,认为接下来的read还是从原来的栈顶开始读入,参考连接:
CSDN上还有一些其他题解,要么就是没讲到这个点上,要么就是说的是错的,所以在这里我着重强调了一下
接下来我们又有一次输入的机会,因为同样有add esp, 0x14,所以同样要构造'A' * 20,接下来应该是shellcode的地址,也就是一个指向自身地址+0x4的值,而esp刚好满足这个条件,但是还要根据offset调整一下,我画张图应该就能理解了:

我自己假设了一些地址,就是为了更方便理解,这样子我相信大家应该都不会有什么疑惑了,顺带提一点,我们的shellcode长度不能超过0x3c-0x14-0x4
然后放一下我的poc吧:
1 | from pwn import * |