十分不错的题目 学习到了栈转移的知识点
[栈转移]CISCN_2019_es_2
1 | int vul() |
乍一看是很常规的栈溢出 然而发现这个栈太短了 也只多了0x08(0x30-0x28)个字节 只能覆盖个ebp和ret地址 那么对于没有现成的shell的话 需要使用栈转移的知识点
1 | 如果基础薄弱 可以看一下这篇博客 |
如果基础强的话 往下看即可 在栈销毁时 会执行如下操作 执行如下操作的指令叫leave ; ret
1 | 0x080484b8 : leave ; ret |
要知道 对于一个ebp寄存器 他所存储的地址是当前栈内的ebp 而该地址中存储的值则是上一个栈帧的ebp
1 | +------------------+ |
此时 如果我们将old_ebp覆盖为我们控制的地址 假设为0x08048400 再将ret_addr改为leave ; ret指令 在函数调用结束之后
1 | mov esp,ebp |
此时ebp寄存器内的值为old_ebp esp值不要紧 此时eip指向ret_addr 也是我们所控制的leave ; ret指令 那么会再执行一次上述操作
1 | mov esp,ebp |
那么此时 mov指令会将esp指向我们所控制的地址0x08048400 pop之后eip就会指向+4地址了 也就是说如果我们能够控制0x08048400 + 4地址 那么我们就能getshell 这就是栈转移 但是上述操作和栈转移这三个字关系好像不大呢
1 | int vul() |
以刚刚这题为例 我们已知了有system函数 但是没有/bin/sh字符串 我们可以自己写一个 到时候参数写字符所在地址即可 当然 目前来看binsh的addr我们还不知道 由我们刚刚所得 我们控制的是old_ebp + 4 也就是说我们必须要留出4个字节用来pop掉
1 | 'aaaa' + p32(system_addr) + 'bbbb' + p32(binsh_addr) + '/bin/sh\\\\x00' |
还需要解决几个问题 第一个是字符串s的位置 因为我们需要控制old_ebp到我们所构造的’aaaa’处 不过好在 由于栈结构在编程时就已确定 那么字符串s与ebp的相对位置不会改变
1 | │pwndbg> stack 25 |
注意 是ebp指针内的数值 而不是ebp指针地址 leave的指令可以用ropgadget找 还要注意padding 在ebp和ret之前得写满0x28个字节 所以payload可以修改一下
1 | payload = 'aaaa' + p32(system_addr) + 'bbbb' + p32(binsh_addr) + '/bin/sh\\\\x00' |
到这总算明白栈转移啥意思了 将原来溢出的栈通过减少padding转移至正常的栈空间内 还有个问题就是/bin/sh\x00所在的地址 这个也好办 我们知道了’aaaa’的起始地址 那么一算就行了 每个都是4个字节 4 * 4 = 16 (0x10) 那么-0x38 + 0x10 = 0x28
1 | payload = 'aaaa' + p32(system_addr) + 'bbbb' + p32(old_ebp - 0x28) + '/bin/sh\\\\x00' |
最后我们只需要知道old_ebp的值了 我们可以利用printf的特性 我们之前sendline会在payload末尾打上\n符 如果用send的话不会 那么printf会输出所有的栈空间 于是exp如下