漏洞原理---SQL注入
SQL注入
- SQL注入原理:
- 可控变量代入数据库查询,并且变量为存在过滤或过滤不严谨
- schema()可以将当前数据库名称显示
- 靶场1-4关:总结闭合的方式
- ‘
- ‘)
- ‘’
- ‘’)
- 不闭合
secure_file_priv可以设置如下这样进行设置:- 设置为空,那么对所有路径均可进行导入导出。
- 设置为一个目录名字,那么只允许在该路径下导入导出。
- 设置为Null,那么禁止所有导入导出。
联合注入SQL
- SQL注入的判断方式:
- 在参数后加单引号,查看是否存在sql语法报错,如“
**sql syntax**” - 插入简短的payload,查看响应结果是否复合预期;
**and 1=1**页面正常,**and 1=2**页面异常;**and sleep(5)**,确认sql注入点 **order by ? **确定当前网站对接的数据库下的这张表(当前网页使用的这张表)的列数;从1一直往后递增,一直到页面出现报错,或者其他的异常情况,那么最后一次正常回显页面的最大数字就是当前表的列数; order by 3 正常;**order by 4**异常;当前表的列数就是3列;**union select**确定有没有回显的点;**id = -1' union select 1,2,3 --+**,让前面的查不出来值,那么联合查询后面的值就会作为网站输出页面中被取的值;当**id=-1**时,未能在原本的表中查询到对应的信息,那么就会查询到后面的select语句中的值,如果2能够在页面中显示,那么说明2这个位置(一行一列的单元格可以显示内容);确定网站中注入的回显点!- 在存在回显点的地方,插入自定义的攻击代码,比如
**schema()**;可以将当前网站的数据库名称在网页中显示出来。 - 继续利用,将
**schema()**换成其他的查询代码,以获取更多信息;- a. 确定表名;
- b. 确定列名
- c. 根据查询到的表名和列名,查询具体的数据
- 在参数后加单引号,查看是否存在sql语法报错,如“
判断是否存在注入点
1 | |
判断字段数
1 | |
确定回显点
1 | |
探测数据库名称
1 | |

- 在mySQL中5以上, mysql 默认在数据库中存放在一个叫
infomation_schema里 面 ,这个库里面有很多表 重点是这三个表columns 、tables、SCHEMATA表字 段CHEMA_NAME记录着库的信息
判断数据库中的表
1 | |
判断列
1 | |
根据列表名和列名,查具体数据
1 | |

- 总结:
- SQL注入的目的是为了获取数据库中的信息
- 简单测试注入时先在搜索框中使用万能语句进行判断如:
id = 1' or 1 = 1 --+如果显示与当前页面内容一致,则说明存在SQL注入 - 在判断存在注入后,使用
order by 1-99对字段进行探测(order by表示按第几列进行排序)。判断出当前网页中的数据库中的表有几个字段(判断当前表中的列数), - 这里使用
order by的目的在于,order by在判断出存在几个字段后,使用union联合查询,确认回显位置
报错注入(1,5,6)
- xml:可以存放数据,存放http协议之间传输信息,大型框架的配置文件(yml,config,ini….)
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc。(更新哪个xml,更新xml在什么地方,更新成什么值)
第二个参数:XPath_string(Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
报错注入是在第二个参数中发生,原因?
- 因为在执行updatexml()函数时,会先检查XPath语法是否合规,如同写java代码一样,先对语法进行检查
什么是poc,explpit ,payload
- 一段能够利用漏洞并且具有攻击性的代码称之为poc
- 操作系统级别的漏洞为
exloit - 一段利用漏洞的代码,载荷—>
payload
为什么能使用报错注入?
- 因为开发人员在开发时,将
print_r(mysql_error());写入代码当中,如果将这句代码注释掉或者进行替换则前端将不再显示错误信息
“~”–>十六进制:0x7e,它的作用?
- 作用是占位符标记开头和结尾,如果没有这个占位符则报错信息将会显示不完全,但是这里不一定是“~”,还可以用其他符号代替.
- 可以没有后面占位符,但是不能没有前面的占位符
select version() —->显示当前数据库版本号信息
- select updatesml(xml标记位置,Xpath路径,需要修改的内容)
- 备注:如果select version()两端不加括号的话,就会与concat()中前一个占位符和后一个占位符进行一个字符串拼接。
1 | |
floor()报错函数
- floor()函数作用:是不管四舍五入都进行向下取整
总结(有几个参数每个参数代表什么,为什么能发生报错注入漏洞,为什么要在concat前面要拼接字符,不拼接会发生什么,能拼接以哪种形式可以拼接那种不能拼接):

1 | |

1 | |

1 | |

1 | |

1 | |
宽字节注入(冷门注入方式)(32)
- WAF为web应用防火墙,WAF当中存放着各种各样的正则表达式
- %27:为单引号
- ‘':%5c
- %df%5C:運
- 目前SQL中存在逻辑漏洞
- 宽字节是相对于ascII这样单字节而言的;像GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节
- GBK是一种多字符的编码,通常来说,一个gbk编码汉字,占用2个字节。一个utf-8编码的汉字,占用3个字节
- 转义函数:为了过滤用户输入的一些数据,对特殊的字符加上反斜杠“\”进行转义;Mysql中转义的函数addslashes,mysql_real_escape_string,mysql_escape_string等,还有一种是配置magic_quote_gpc,不过PHP高版本已经移除此功能
- 总结:
- 宽字符注入是一种编码转换上存在的漏洞,一般SQL在设置编码是会开启过滤特殊字符,比如 ‘ ‘) ‘’),一般这种漏洞存在于php前端页面编码与数据库编码不一致所导致。 要有宽字节注入漏洞,首先要满足数据库后端使用双/多字节解析SQL语句,其次还要保证在该种字符集范围中包含低字节位是0x5C(01011100) 的字符,初步的测试结果 Big5 和 GBK 字符集都是有的,UTF-8 和GB2312没有这种字符(也就不存在宽字节注入)。 换句话说假如php前端页面数据和后端SQL编码都采用UTF-8 或者GB2312编码就不存在宽字节注入
- 在宽字节注入时,注入逻辑和联合注入逻辑相似,只是在宽字节注入时需要对单独的引号做%df%5C%27用来逃避SQL设置的过滤特殊字符设置 如:
id=-1%df%5C%27 or 1 = 1。SQL中常见的转义设置与配置addslashes、mysql_real_escape_string、mysql_escape_string、php.ini中magic_quote_gpc的配置 - 在联合查询中,需要将联合查询语句当中引号当中的内容转为16进制
1 | |
二次注入(24)
- 使用正常功能插入数据
echo '\'' ---->'- 原理:在一次注入是数据中存在非法字符,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。
- 例如:在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
- 二次注入的目的
- 为了获取数据库中的信息,伪造身份
- 二次注入为什么会生效:
- 用户从前端输入数据库中的一个用户名(用户名必须是数据库当中的)并且在用户名后面跟上转义字符,比如数据库中的用户名Tom 在新的注册页面上输入
Tom'#/Tom'--空格然后在修改密码,如果修改密码成功页面成功,就说明Tom'#修改的密码是数据库中原有Tom的密码,不是Tom’#的密码,之所以会出现这种情况是因为注入点发生在Tom和#/--空格之间的',因为'\''转义后输出为',
- 用户从前端输入数据库中的一个用户名(用户名必须是数据库当中的)并且在用户名后面跟上转义字符,比如数据库中的用户名Tom 在新的注册页面上输入
#/--空格会把后面内容注释,'会和'Tom组成数据库中原有的Tom并且第二次注册的Tom'#依然可以登录
1 | |

过程:
- 账户原有用户

- 添加新用户:

- 以新用户登录

- 修改密码


- 查看数据库,实际修改的密码为数据库中原有Tom的密码

延迟注入(9,10)
有单引号闭合叫:字符型闭合
没有单引号和括号叫数字型注入
- id = -1’ and if((substr(schema()) ,1,1) = ‘a’,条件为真,条件为假 )
- 先猜长度再猜值
- length对字符串的标点也要进行统计
- 在写脚本时为什么先获取长度?
- 因为,在黑盒测试下,攻击者不清楚数库中的信息,所以需要对数据库名称进行一个个探测,写脚本是用循环不清楚要循环多少次才能测试出整个数据库名称,假如先获取当前数据库名车长度然后根据长度在做循环,就可以简化脚本运行时间提高效率。简而言之获取长度是为了知道脚本再什么时候停止运行。
- 再获取表中字段时,为什么tmp = ‘’要放在for循环外面
- 因为如果放在for循环里面,tmp = ‘’都要初始化,每循环一次,就初始化一次,导致循环结果放不到tmp当中
- 延迟注入
- 在测试过程中,攻击方对目标目标数据库任何信息不知道的情况,在搜索框或者抓包之后看不到SQL语句的执行状态,只能从网页页面上的变化来推断出插入的SQL注入是否生效。在这种情况下可以使用盲注的方法进行对目标探测。延迟注入也是盲注的一种。
- 先用正常参数进行探测,发现返回You are in…………..

- 在使用闭合方式+order by 进一步探测是否可以使用延迟注入,发现使用闭合方式+order by于正常输入参数返回结果一致,此时可以判断这里存在延迟注入

- 使用
if(),length(),substr()这三个依次对表的长度->表名,表中列的长度,表中内容表中列的字段,输入列名后列中所有内容长度,最后查询表中信息
1 | |
堆叠注入(38)
- 为什么前面关卡不能用堆叠?
- 这是源代码层面的问题,堆叠注入时源代码使用mysqli_multi_query()函数,这个函数可以执行多个查询,而之前用到是 mysql_qurery()只能执行一条查询语句
- 在堆叠注入时,我们可以在前后闭合的情况下,在每个查询语句后面加分号在加上一条查询语句。
数据外带
- 前提:MySQL所在主机可以出网,mySQL打开了相关权限

防范SQL注入
预编译
- 对前端输入进来的SQL语句进行转义
- PHP中使用PDO进行预编译,PDO是PHP中的一个类
- java中预编译类:创建preparedStatement对象,SQL语句中在需要数据的地方用”?”占位,然后使用set()方法对占位进行填充,最后用executeUpdate()进行执行

- 预编译技术不能百分百防御SQL注入
转码(转义)
- mysql_real_escape_string()——这个方法在PHP中已经过时,这个方法只在UTF-8的编码下生效
正则表达式(简单粗暴,但是效果得看具体情况)
- 正则表达式匹配特殊字符,匹配到特殊字符后将其替换位空,或者拒绝执行SQL语句
- PHP-preg_replace方法
- java-Pattern类
- Go-Regexp类
- Python-re包
补充:
搜索框注入
http://localhost:8080/blog/new.php?search=百度%'union+all+select+1,database(),2,3 and '%'='1
2
3
4
5
6
7
##### Json格式注入
- ```
http://localhost:8080/json.php
使用Post提交,data数据如下:
json={"username":"admin'and 1=2 union select 1,2,3#"}
其他注入方式
- 后台要记录操作访问IP
- IP要进行代码获取,获取到之后,IP有一定概率会记录到数据库当中,如果IP能自定义数据,就可以尝试注入
- 网站要根据用户的访问设备给予显示页面
- 接受访问的UA信息,将各种UA信息进行数据整理,用户访问后对比数据库中UA值来进行判断
- 网站要进行文件上传或者用户登录
- 由于文件上传可大可小,如果采用GET则无法满足传输条件,URl长度限制会触发,所以一般会采用POST
- 用户登录,接收到账号和密码后会带入后端数据库当中进行对比。可以尝试注入
- 在数据包中添加
X-Forwarded-For: 伪造的IP地址如果页面
SQLmap
1 | |
漏洞原理---SQL注入
https://rofgd.github.io/2022/03/16/漏洞原理---SQL注入/








