栈溢出从复现到挖掘-CVE-2018-16333漏洞复现详解
vlan911 发表于 浙江 IoT安全 206浏览 · 2025-05-06 04:06

漏洞点位分析

漏洞成因是web服务在处理post请求时,对ssid参数直接复制到栈上的一个局部变量中,参数没有进行长度限制,导致栈溢出。根据ssid字符串定位到form_fast_setting_wifi_set函数。

图片加载失败


程序获取ssid参数后,没有经过检查就直接使用strcpy函数复制到栈变量中。其中有个细节:第一次的strcpy如果要溢出到返回地址,会覆盖第二次的strcpy的参数dest。因此,为了将src指针覆盖为有效地址,并且不影响第一次的strcpy, 需要绕过两次strcpy的安全隐患,确保第二次strcpy不崩溃,因此Payload中需包含可读地址

Bash
复制代码
图片加载失败


图片加载失败


利用过程:

1、溢出后跳到第一个gadget1,控制r3寄存器为system函数地址,第一个pc控制为gadget2

2、跳转到gadget2后,控制r0为要执行的命令即可

3、执行system(cmd)

偏移量分析

启动调试,这里需要换成pwndgb,因为pwndgb可以支持更多的指令,特别是计算偏移量

图片加载失败


首先看一下ida对两个strcpy的汇编代码

图片加载失败


上述代码都加载了src的指针,所以如果第一次溢出,第二次不处理就会导致程序异常,接下来看pwndbg调试,首先在第一个strcpy函数前打断点,strcpy函数打断点;并对第二个strcpy函数之前打断点,strcpy函数打断点,而后运行测试脚本

首先可以看寄存器区域,主要看R0寄存器、R11寄存器、SP寄存器、PC寄存器

R0寄存器一般是函数的第一个传参,这里代表的是strcpy函数的第一个参数,目前还没有步入到0x676c,所以值还没有传入

R11寄存器当前的栈帧基址为0x40800264,在ARM架构里,R11寄存器一般代表FP寄存器,他的值指向当前函数的栈帧基址;

SP寄存器当前的栈帧基址为0x407fffe8,SP寄存器代表栈指针,指向当前栈顶(最低地址)

PC寄存器指向下一个即将执行的代码区域

图片加载失败


执行ni指令,步入,后续按回车就行

此时程序已经执行完IDA的汇编代码SUB R2,R11,#-s ,对应的事pwndbg里面的的sub r2, fp, #0x7c

这行代码的意思就是,计算dest的地址(char s),计算方式为R11 - 0x7c=0x40800264-0x7c=0x08001E8 ,对应的是R2寄存器的值,由调试结果可知, 从栈帧基址(FP)向低地址方向偏移 124 字节(0x7C),定位到 char s 缓冲区的起始地址。 ,也就是说char s的偏移量为7C

图片加载失败


下图为执行了 0x67070 ldr r3, [fp, #-0x1c] 指令,该指令为加载src指针到R3寄存器,从这里可以看到,SRC的栈帧指针为 R11 - 0x1c=0x40800264-0x1c=0x0800248

图片加载失败


继续执行一步,0x67074 mov r0, r2 ,此汇编代码市纪委将R2赋值给R0,实际上就是设置目的地址为char s

图片加载失败


继续执行,发现执行了0x67078 mov r1, r3,此代码的是设置源地址(src),而后就是将源地址的字符串赋值到目的地址代表的char s,从而完成strcpy(s,str)

图片加载失败


如果想调试第二个函数,直接按c回车,对第二个函数单独分析(第二次调试打俩断点,第一个是0x67080,第二个是0x67090)

图片加载失败


从调试结果可以看到,第二个strcpy函数也对src进行了加载,dest的地址为0x408001a8

图片加载失败


对比一下ida 中的伪代码可以看到

char s[64]; // [sp+200h] [bp-7Ch] BYREF

char dest[64]; // [sp+1C0h] [bp-BCh] BYREF

char *src; // [sp+260h] [bp-1Ch]

ida反汇编会有一点小瑕疵,比如在arm架构,栈帧基址应该是fp寄存器,但是在ida里显示的事bp,sp+200h与bp-7ch的结果是一样的,这个也可以作为偏移量进行计算对照参考,但是实际结果还是需要看pwndbg的调试,这个结果相对稳定

图片加载失败


由此,我们可以得到大致的栈帧结构图

图片加载失败


根据堆栈图,我们不难发现,src对应栈底的偏移量是0x1C;char s到栈底的偏移量是0x7c;返回地址是根据栈底+4个字节计算得来的,所以src 距离返回地址的距离是0x20;而char s到src的偏移量就是0x60;

上面我们提到,由于两个strcpy函数都对src进行了调用,所以第一次传入src溢出后也会影响到第二个strcpy函数,导致程序溢出崩溃从而无法执行system指令

所以为了能够完成漏洞利用,我们需要对利用链进行切割

(1) 第一次 strcpy(s, src)

目标:覆盖 src 指针,使其指向可控地址(如 libc 中的可读地址)。

偏移量

ssrc 的距离 = (bp - 0x1C) - (bp - 0x7C) = 0x60(96字节)。

Payload 部分

(2) 第二次 strcpy(dest, src)

目标:通过被篡改的 src 指针,向 dest 写入ROP链,覆盖返回地址。

关键点

dest 到返回地址的距离 = (bp + 4) - (bp - 0xBC) = 0xC0(192字节)。

但实际只需覆盖 src 到返回地址的 0x20(32字节),因为 dest 是中间跳板。

p32(readable_addr)4字节, 返回地址本身占4字节(arm架构中,需要4字节填充) , 所以实际填充长度是32-4-4=24字节

(3) payload结构

所以payload应该调整为

图片加载失败


readable_addr可读地址

使用ida pro打开libc.so.0文件,理论上只要是rodata的常量的偏移量,都可以拿来用,但是这里只是偏移量,需要跟上实际的地址,这个地址就是lib基址

图片加载失败


lib基址计算

图片加载失败


由此可见,在内存映射里面的基址地址是0x3fdd1cd4

图片加载失败


使用ida打开libc.so.0,查看puts的相对偏移量为0x35CD4

图片加载失败


由此可知 lib基址为

system基址计算

计算system函数偏移量

图片加载失败


Gadget解析

跳转到R3的gadget1_addr

图片加载失败


0x00018298 : pop {r3, pc}

功能:从栈顶弹出两个值,分别存入 r3pc。( 将 system 地址存入 r3)

用途:控制 r3 寄存器的值,并直接跳转到 pc 指向的地址。( 用于初始化 r3 和跳转 )

找到一个可以控制R0的gadget2_addr

图片加载失败


0x00040cb8 : mov r0, sp ; blx r3

功能:将栈指针 sp 的值赋给 r0,然后跳转到 r3 寄存器指向的地址执行( 此时 r3 已被前一步赋值为 system_addr)。

用途:用于将栈顶数据(如命令字符串)传递给 r0( 用于传递参数并触发 system())。



ARM调用约定在ARM中,函数调用时:

第一个参数通过 r0 传递。

函数地址通常通过 blx r3 跳转(r3 存储目标地址)

关键寄存器作用

r0:ARM架构中用于传递函数第一个参数(如 system("/bin/sh") 中的 "/bin/sh" 地址)。

r3:通用寄存器,此处用于暂存 system 函数地址。

pc:程序计数器,指向下一条要执行的指令地址。通过控制 pc,可以劫持程序流。

由此,完整的payload为:

完整payload

执行脚本,成功实现rce

图片加载失败


程序有时候会抽风,需要点几下ctrl+c

response = requests.post(url, cookies=cookie, data=data) 这个重复两次,是因为如果只发一次包,回来的东西不太对,不知道是啥问题

通过堆栈查看,发现数据已经插入

图片加载失败


若是在0x67080 也就是strcpy传参处打断点,然后查看栈空间,我们可以看见,从r0(函数第一个传参处)0x408001e8 到寄存器0x40800248 都已经被"aaaa"覆盖,并且0x40800248地址也指向libc可读地址,0x3fe0015f = libc_base + 0x6415F = 0x3fd9c000 + 0x6415F = 0x3FE0015F ,与栈内是可以对应的上的

图片加载失败


由于arm架构小端,所以每个寄存器占用4个字节,所以从0x40800248 +4 到0x40800264 进行字节占用补充("bbbb"),为0x20 - 8 = 24 字节 ;

0x40800264 栈底开始进行rog链构造,对应的就是

pop_r3 = libc_base + 0x18298 = 0x3fd9c000 + 0x18298 = 0x3FDB4298

0x40800268返回地址 对应

system基址 = libc_base + 0x5A270 = 0x3fd9c000 + 0x5A270 = 0x3fdf6270

0x4080026c 为后续执行地址,对应

mov_r0_ret_r3 = libc_base + 0x40cb8 = 0x3fd9c000 + 0x40cb8 = 0x3FDDCCB8

0x40800270 栈帧基址开始执行cmd指令,至此,证明思路完全没问题



0 条评论
某人
表情
可输入 255

没有评论