Web安全的最后一道防线:细谈Gobuster的目录/文件/Vhost/DNS子域名暴力破解艺术

2023-09-20 16:29:19 浏览数 (4)

一、前言

Gobuster是一款用go语言编写的对于网站目录/文件DNS子域虚拟主机vhost进行暴力穷举的开源工具,常用于安全领域,其常用的暴力破解模式到目前为止(3.6版本)有如下几种:

模式

含义

dir

最经典的文件路径/目录破解模式。

dns

dns子域名破解模式。

s3

枚举打开S3存储桶并查找存在和存储桶清单(适用于aws)。

gcs

枚举打开的谷歌云存储桶。

vhost

虚拟主机枚举模式(不同于dns子域)。

fuzz

一些基本的模糊处理, 替代 FUZZ 关键字模式。

tftp

暴力破解tftp文件。

近期在某些场景中用到了gobuster,因此不妨趁热打铁写了本文作为沉淀。本文将从上面几种模式中选择最常见最具普遍性适用性的模式:dirdnsvhostfuzz模式中详细讲解其用法,s3gc3适用于aws和谷歌云(gcp)的一些存储桶资源场景,详细用法可以通过gobuster help <mode>来查看。

二、全局参数

首先列举一下全局参数,这些参数在所有模式中都能被使用。

参数

含义

--debug

打开debug模式。

--delay duration

每个线程在请求之间等待的时间(举例: --delay 1500ms)。

--no-color

禁用颜色输出。

--no-error

不显示错误。

-z|--no-progress

不显示进度。

-o|--output string

输出结果到文件。

-p|--pattern string

包含替换模式的文件。

-q|--quiet

安静模式,不打印banner信息和一些无用信息。

-t|--threads int

指定线程数量(默认10)。

-v|--verbose

详细输出日志(404状态码的也会展示)。

-w|--wordlist string

指定字典路径,指定-可以通过标准输入中读取。

--wordlist-offset int

从字典的指定位置继续(默认偏移量为0,从第一个开始)。

三、关于字典

字典是至关重要的一把钥匙,一个足够强大的字典能爆破出更多的可能性,如果要DIY字典则建议使用crunch生成,整理收集业内极具知名的字典如下:

项目名称

链接

Fuzzing-Dicts

https://github.com/3had0w/Fuzzing-Dicts

weakpass

https://weakpass.com/

SecLists

https://github.com/danielmiessler/SecLists

Assetnote Wordlists

https://wordlists.assetnote.io/

fuzzdb

https://github.com/fuzzdb-project/fuzzdb

PayloadsAllTheThings

https://github.com/swisskyrepo/PayloadsAllTheThings

samlists

https://github.com/the-xentropy/samlists

Exploit-Dictionary

https://github.com/epony4c/Exploit-Dictionary

四、目录/文件路径暴力枚举(dir)

1.指定url链接枚举(-u|--url)

-u参数用来指定目标URL地址,此参数必选,同时配合全局参数里的-w来指定字典:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist>

默认使用10个线程,上图通过-t 1只指定了一个线程。

指定64个线程的效果如下:

同时对比下全局参数里的--quiet--debug

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> -q
gobuster dir -u <URL> -w <wordlist> --debug

如上图,不指定-q的情况下默认仍然是debug模式输出,会显示目的URL、HTTP方法、线程数量、字典文件、否定状态码(默认404)、UA信息、超时时间等。

通过抓包可以看到,gobuster根据字典里面的路径内容,组合成完整URL进行枚举:

因此确保字典足够强大,可能结果也会更多。

2.使用cookies(-c|--cookies)

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> -c <cookie>

相当于拿到登录状态信息,再去枚举URL下的目录和文件。

3.打印完整URL(-e|--expanded)

默认只显示文件路径,通过-e参数可以将枚举出来的目录补全后以完整的URL显示:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> -e

4.指定用户名和密码(-U,--username|-P,--pasword)

不想通过cookie的方式来获取,则可以携带用户名密码:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> -e -U 'username' -P 'password'

通过抓包不难发现,这两个参数用的是基础鉴权,如果非基础鉴权则使用无效,会在HTTP头部里面携带基础鉴权的账号密码信息:

5.忽略TLS/SSL证书验证(-k|--no-tls-validation)

curl、wget等七层工具也有一样的参数,忽略证书问题,不进行证书可用性校验:

代码语言:shell复制
gobuster dir -k -u <URL> -H 'Host:<HOST>' -w <wordlist>

6.自定义http头部(-H|--headers)

通过-H参数指定http头部,可以模拟任何想要发送出去的HTTP头部字段。

比如指定特定HOST:

代码语言:shell复制
gobuster dir -k -u <URL> -H 'Host:<HOST>' -w <wordlist> -t 1

指定多个头部,用多个-H分割,比如同时指定HostUser-AgentConnection

代码语言:shell复制
gobuster dir -k -u <URL> -H 'Host:<Host>' -H 'User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36' -H 'Connection:keep-alive' -w <wordlist>

当然User-Agent也可以单独通过-a|----useragent来指定,gobuster提供了此参数,默认为:gobuster/3.6,后面的数字为版本号。

7.指定文件扩展名搜索(-x)

当只想搜索php时,使用-x php,搜索txt时,则指定-x txt,以此类推。

比如只搜索php的文件,显示完整URL(-e),指定64个线程(-t 64):

代码语言:shell复制
gobuster dir -u <URL> -x php -w <wordlist> -t 64 -e

8.读取要从文件搜索的扩展名(-X)

当要搜索的扩展名有多个时,可以通过读取扩展名的文件,不需要一个个手动指定:

代码语言:shell复制
gobuster dir -u <URL> -X <extensions-file> -w <wordlist>

9.指定HTTP请求方法(-m|--method)

不指定默认为GET,如果需要指定其它HTTP方法,则通过-m指定,譬如指定POST请求可以是:

代码语言:shell复制
gobuster dir -u <URL> -m POST -w <wordlist>

10.指定代理服务器(--proxy)

不想暴露真实IP的情况,可以指定代理选项,支持HTTP/HTTPS代理,或者socks5代理。

格式为:http(s)://host:port 或者 socks5://host:port

比如指定socks5代理来扫描对方,可以是:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> --proxy <socks5://host:port>

通过在kali客户端抓包发现,客户端将请求转发给设置的代理伺服器,让它来完成整个扫描:

因为整个过程是加密了,所以不能直接看出请求的内容。

但不难发现整个过程客户端本身并不会去直接请求被扫描的目的服务器:

11.指定客户端证书及私钥(--client-cert-p12|pem)

TLS/SSL双向认证的场景下,客户端请求服务端需要指定客户端证书的,则可以使用此参数指定。

指定p12证书为:

代码语言:shell复制
gobuster dir --client-cert-p12 <certfile> -w <wordlist> -u <URL>

如果p12证书有密码:

代码语言:shell复制
gobuster dir --client-cert-p12 <certfile> --client-cert-p12-password <certpasswordfile> -w <wordlist> -u <URL>

同理,指定pem证书则为:

代码语言:shell复制
gobuster dir --client-cert-pem <certfile> -w <wordlist> -u <URL>

如果有pem证书的私钥:

代码语言:shell复制
gobuster dir --client-cert-pem <certfile> --client-cert-pem-key <keyfile> -w <wordlist> -u <URL>

12.剔除指定内容长度的结果(--exclude-length)

当对返回的结果内容长度有要求时,可以通过此参数指定。

比如,剔除内容长度为0字节的情况:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> --exclude-length 0

再看看不指定的效果:

很明显返回了很多字节为0的页面,基本都为301重定向,并显示了重定向后的URL地址。

如果想剔除在某个大小范围内的结果,通过-字符来指定,比如剔除介于0-100字节大小的结果:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> --exclude-length 0-100

13.指定线程数和延时时间(-t|--delay)

这两个参数都属于全局参数,并非dir模式独有,但组合在一起用值得单独拎出来讲一下。

如果对端服务器有做QPS限频处理,那么触发阈值则可能全部超时或者拿到不符合预期的状态码,可以通过指定线程数和延时时间来将动作放慢,默认不指定的情况,-t每次10个线程,且没有delay时间。

比如每次执行5个线程,然后延时10s再继续下5个线程:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> -t 5 --delay 10s

14.设置黑名单状态码(-b|--status-codes-blacklist)

默认情况下,此参数值为404,即通过字典枚举构造的URL,请求过去如果拿到的是404状态码,则不展示在结果上,因此上面的所有gobuster执行截图,没有看到任何404状态码的结果。

而如果你想看到执行过程,哪怕404页面也要返回的话,全局模式的-v参数可以详细打印日志:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> -v

可以看到404页面会在最前面标记为Missed

-b参数可以设置哪些HTTP状态码不展示到结果上,比如401、403、404、501-504都不展示,可以是:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> -b 401,403,404,501-504
22

15.筛选指定状态码(-s|--status-codes)

同理,有黑名单就有白名单,此参数用来指定符合条件的状态码。

-s-b互斥,不能同时设置(缺省情况下-b为404),因此使用-s时,-b必须要设置为空字符串(-b ""),不然会报错:Error: error on parsing arguments: status-codes ("200") and status-codes-blacklist ("404") are both set - please set only one. status-codes-blacklist is set by default so you might want to disable it by supplying an empty string.

因此,只想要200状态码的结果,可以写成:

代码语言:shell复制
gobuster dir -u <URL> -w <wordlist> -s 200 -b ""

当然也可以同时指定多个或者范围,如:-s 200,300-399,401 -b ""

五、DNS子域名暴力枚举(dns)

1.指定域名枚举(-d|--domain)

-d参数指定目标域名,-w指定字典,以k8s官网为例:

代码语言:shell复制
gobuster -d kubernetes.io -w <wordlist>

通过抓包可以看到,gobuster将字典里面的条目,填充到指定的域名形成子域名,并一次次向DNS服务器发起query请求进行DNS穷举:

2.指定DNS服务器解析(-r|--resolver)

如果不想走系统配置的默认DNS,也不想修改系统的DNS配置,那么-r可以任意指定DNS服务器进行解析:

比如指定腾讯云的公共DNS:119.29.29.29 来枚举CNCF的子域名:

代码语言:shell复制
gobuster dns -d cncf.io -r 119.29.29.29 -w <wordlist>

默认去请求DNS服务器53端口,如果是非默认的其它端口,指定端口即可。

比如请求内网DNS的高端口25533枚举grafana官网的子域名,并且指定每个线程延时时间为1.5s:

代码语言:shell复制
gobuster dns -d grafana.com -r 192.168.1.72:25533 -w <wordlist> --delay 1500ms

3.打印cname记录(-c|--show-cname)

想要输出别名记录时,则可以使用-c参数:

代码语言:shell复制
gobuster dns -c -d <domain> -w <wordlist>

4.打印解析到的IP(-i|--show-ips)

想要知道每个枚举出来的域名对应的解析记录时,可使用-i参数,以枚举gentoo官网为例:

代码语言:shell复制
gobuster dns -d gentoo.org -w <wordlist> -i

5.指定DNS解析超时时间(--timeout)

不设置的情况下默认为1s超时,通过--timeout可以指定,比如指定超时时间为0.5s可以是:

代码语言:shell复制
gobuster dns -d <domain> -w <wordlist> --timeout 0.5s

6.通配符域名的强制处理(--wildcard)

如果目标域名的子域名为通配符域名,形如 *.domain.com,那么字典里面的子域名记录,不管是什么都能返回解析记录,比如github官网:

如上图,gobuster执行后会提示几乎每个域名都返回同样的A记录结果,通过dig也能测试出来。

此时我们指定--wildcard参数,wildcard直译为通配符,让gobuster遇到通配符域名时继续强制执行,但只会返回和通配符域名不一样的解析结果的域名(这个逻辑判断是正确的,不然字典里每个字段都能解析出地址没有任何意义):

代码语言:shell复制
gobuster dns -d <domain> -w <wordlist> --wildcard

六、模糊模式暴力枚举(fuzz)

Uses fuzzing mode. Replaces the keyword FUZZ in the URL, Headers and the request body.

正如帮助文档所述,fuzz模式可以替换FUZZ关键字在URL或者HTTP头部以及请求体里面:

这么说可能比较抽象,可以理解构造的请求URL、HTTP头部、发送的请求体只要掺杂fuzz,就可以带入fuzz变量,fuzz变量的值来源于字典内容,一个个替换到fuzz变量上进行枚举。

比如URL参数枚举,可以写成:

代码语言:shell复制
http://example.com/profile?user=FUZZ

再比如HTTP头部内的某个字段枚举,比如枚举UA信息:

代码语言:shell复制
-H 'User-Agent: FUZZ'

除了以上功能,其他参数都和dir模式完全一致,下面只举例两种最常见的情况,其他任何复杂场景都可以按需配合FUZZ构造请求。

1.指定URL并枚举用户名(-u)

比如枚举一个固定URL,通过入参字典里的字段内容,来进行穷举:

代码语言:shell复制
gobuster fuzz -u domain/?userid=FUZZ -w <wordlist>

gobuster将字典文件内容逐个替换到FUZZ占位符中进行请求。

2.枚举UA信息(-H)

通过UA字典文件的信息,替换到FUZZ占位符:

代码语言:shell复制
gobuster fuzz -H 'User-Agent: FUZZ' -w <wordlist> -u http://192.168.1.72:8080

通过抓包也可以发现,HTTP头部里的UA信息将从字典文件里逐个代入:

七、基于虚拟主机的暴力枚举(vhost)

想知道一个目的IP有哪些对应的服务域名/虚拟主机时,vhost则非常有用,特别是通过DNS模式拿到子域名列表后,可以尝试通过不同的解析IP去枚举上面是否存在这些子域名服务。

其中大部分参数和dir模式是通用的,使用方法也一样,因此不反复赘述,下面将列举较为经典的几个场景。

1.指定URL进行虚拟主机枚举(-u)

URL写成IP形式,主机名存放在字典里,-k不进行TLS/SSL证书校验:

代码语言:shell复制
gobuster vhost -u <URL> -w <wordlist> -k

2.附加主机名子域(--append-domain)

此参数会将字典里的内容,添加到URL的HOST之前,比如URL为:https://domain.com,读取字典内容(如a、b、c),填充后的HOST为:a.domain.com、b.domain.comc.domain.com

如果URL是IP或者IP:PORT形式,那么照样会在前面加,比如:a.192.168.1.72:8080a.192.168.1.1

举个例子,指定的URL已经是主域名形式,指定子域名字典subdomain.txt进行虚拟主机的枚举可以是:

代码语言:shell复制
gobuster vhost -u <URL> -w subdomain.txt -k --append-domain --timeout 1s --retry --retry-attempts 1
  • --timeout 1s:设置超时时间为1s,不指定默认10s。
  • --retry:超时后重试。
  • --retry-attempts 1:设置最大重试次数1次,不指定默认3次。

这里需要和DNS子域枚举区分开来,它并涉及DNS解析请求,vhost模式会对组合出来的所有HOST都默认发送HTTP GET请求,其它HTTP方法,通过-m参数指定即可,比如-m POST

3.指定代理服务器(--proxy)

因为需要向对端发送HTTP/HTTPS请求,并不想暴露自己的情况下,可以使用代理,和dir模式一样,支持HTTP/HTTPS/socks5代理,以socks5代理为例:

代码语言:shell复制
gobuster vhost -u <URL> -k -w <wordlist> --proxy <socks5://host:port>

4.随机User-Agent(--random-agent)

伪造UA信息,提升真实信息的隐蔽性,给对方访问日志也造成一种干扰:

代码语言:shell复制
gobuster vhost -u <URL> -w <wordlist> --random-agent -t 64  # -t指定线程数量

如上图,构造了一个MacOS客户端且浏览器为Chrome的虚假UA信息。

5.指定User-Agent(-a|--useragent)

实际上也可以通过-H参数来从HTTP头部参数里指定,参照dir模式-H

通过此参数来指定,可以伪造我们想伪造的任何UA信息,比如伪造为谷歌bot可以是:

代码语言:shell复制
gobuster vhost -u <URL> -w <wordlist> -a "Googlebot/2.1 ( http://www.googlebot.com/bot.html)"

八、总结

Gobuster作为web安全、渗透领域的案头常备利器之一,其功能全面且强大,支持多线程高并发请求,常用于发现Web应用程序中隐藏的目录和文件,以及对子域名、虚拟主机vhost等进行暴力枚举等场景。

同时也通过抓包分析了不同场景的参数选择上的差异和行为。选择合适的字典文件可以显著影响扫描的效率和成功率,本文也整理了业内极具知名度并且覆盖各类场景的字典,可以作为参考按照实际情况去二次生成更适配需求的字典。

总的来说,Gobuster是渗透测试工具箱中不可或缺的一部分,它可以帮助渗透测试人员识别Web应用程序中的潜在漏洞和安全风险。

附带PDF版本:

Web安全的最后一道防线:细谈Gobuster的目录/文件/Vhost/DNS子域名暴力破解艺术.pdf

我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表

1 人点赞