Protostar-栈溢出学习-ret跳转到自定义shellcode

By xia0

0x00 序

经过前面的学习,我们已经了解了简单的栈溢出利用手段,建议看本文章前先看下前面的分析。这次我们来做点有趣的事,我们引入shellcode。

0x01 C语言源代码

Stack5 is a standard buffer overflow, this time introducing shellcode.

This level is at /opt/protostar/bin/stack5

Hints:
At this point in time, it might be easier to use someone elses shellcode
If debugging the shellcode, use \xcc (int3) to stop the program executing and return to the debugger
remove the int3s once your shellcode is done.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}

0x02 思路&分析

和前面的一样,我们可以通过覆盖返回地址然后由ret指令跳转到目标地址,但这次我们没有了win()函数,那我们应该跳转到哪里呢?没错,还是跳到栈上。要达到执行代码的目的,所以我们需要在栈上写入shellcode。Here we go!

0x03 调试&hack

  • 对应汇编,我们在0x080483da <main+22>: ret处下个断点
(gdb) disassemble 
Dump of assembler code for function main:
0x080483c4 <main+0>:    push   %ebp
0x080483c5 <main+1>:    mov    %esp,%ebp
0x080483c7 <main+3>:    and    $0xfffffff0,%esp
0x080483ca <main+6>:    sub    $0x50,%esp
0x080483cd <main+9>:    lea    0x10(%esp),%eax
0x080483d1 <main+13>:    mov    %eax,(%esp)
0x080483d4 <main+16>:    call   0x80482e8 <gets@plt>
0x080483d9 <main+21>:    leave  
0x080483da <main+22>:    ret
  • 同样的方法,Python脚本测试返回地址

    2-0
    2-1
    可以发现0xbffff79c处正是返回地址,对应到TTTT,我需要在TTTT处写上我们要跳转的地址。

    既然题目中说了用int 3 cc指令那我们就先来测试下–[cc指令(见文章末尾)]
    2-2
    2-3
    我们成功执行了cc INT 3指令并触发 SIGTRAP

  • 引入nop slide
    为什么需要nop slide?
    来看看这种情况,我们再启动一个终端去调试。
    2-4
    发现地址里面内容一样,但地址却不同,为什么会这样呢?再看看整个栈的情况
    2-5
    可以发现,在圈中之后的栈地址都相同,但由于工作路径不同,导致前面的栈地址都发生了变化。
    因此,我们需要引入nop slide去解决这种情况。

  • 填写shellcode
    自此,我们可以加入我们的shellcode了。这里我用的这里的shellcode。
    2-6

现在我们重新编写Python脚本,制作shellcode payload

2-7

我们已经跳转的地址已经变化了40字节,并加入100的nopslide偏移,然后再执行shellcode

  • 运行测试
    2-8
    的确执行了dash,但并没有出现运行结果,这里很容易困惑。
    不应该这样的啊?
    经过思考,原来我们执行了/bin/dash后,并没有输入任何数据,所以就退出了。

    那怎么才能让执行后输入数据呢?
    这里有一个小技巧,在执行后用cat指令,将输入转到输出
    2-9

so cool! make it!

0x04 附录

INT 3 CC

详细分分析可以看这边文章int 3中断与软件调试

简单的理解就是:当我们调试程序时,可以在可能有问题的地方插入一条INT 3指令,使CPU执行到这一点时停下来。这便是软件调试中经常用到的断点(breakpoint)功能,因此INT 3指令又被称为断点指令。