新站提交
热搜: 目录平台

介绍

在 web 应用程序测试的侦查阶段,测试人员(或攻击者)通常使用已知的子目录列表来暴力破解服务器,以找到隐藏资源。对于这个意图,已知的子目录列表常常被用于 Skipfish 或 wfuzz。如果有额外的上下文信息被获取到,它可能会添加到该列表。最重要的是,一旦测试完成(或者命令执行已被完成),以及一个列表或服务器配置信息被揭露,该列表就会被更新,而不会缺失任何一条。

我最近丢失了我的个人列表,决定从头再建一份。这种列表是不受时间限制的。取决于某种 web 技术的理解力,它需要定期刷新。我打算把我的列表建立在网站公开的最有价值的地方:robots.txt。在这次探索中,我碰到一些有趣的发现。

免责声明:整个练习使用了互联网允许公开访问的资源。从最坏方面说,只是互联网礼仪被破坏了。

robots.txt

这些文件位于网站服务器主目录下,指示站点允许网络爬虫(比如 Google robots)访问哪个部分以进行索引。它们应该被机器分析、但具有人类可读性。例如:

?

123456User-agent: *``Disallow: ``/admin/``Disallow: ``/stats/``Disallow: ``/internaljobs/``Disallow: ``/internaljobsbyorganization/``Disallow: ``/internaljobsearch/你或许看到了,Disallow 指令给攻击者提供了宝贵信息,哪些地方是值得查看的。另外,如果对于一个站点是这种情况,那么它就值得用于检查另一个站点。

抓取

为了搞到一套相关的 robots.txt 文件,有必要从主机名列表着手,如果可能的话,主机名要包含宽泛的互联网、而不要被限制为任意子集(和 主题爬虫 相反)。

大多数商业搜索引擎不再提供免费的解决方案。幸运的是,Common Crawl 项目通过 S3 提供了最近的抓取结果。

该抓取被分隔在 1GB 的文档里(解压缩后有 4GB)。(截止 2015 年 2 月份,它们超过了 33,000 条)。我写了一个脚本,从这些解压缩后的 WARC 抽取出找到的所有主机名:

?

1234567891011121314151617181920212223242526272829require ``'set'``URL_PATTERN``= %r{`` ``(?:(?``:https``?)://)`` ``(?:`` ``(?:(?:[a-z0-``9``][a-z0-``9``-]*)?[a-z0-``9``]+)`` ``(?:.(?:[a-z0-``9``-])*[a-z0-``9``]+)*`` ``(?:.(?:[a-z]{``2``,}))`` ``)`` ``(?::d{``1``,``5``})?``}xin``tlds = ``File``.open(``"tlds-alpha-by-domain.txt"``)

.each_line.collect {|w| w.chomp.downcase}

s = Set.``new``begin````File``.open(``ARGV``[``0``], ``'rb'``).each_line.with_index

do``|line, i|

`s.merge(line.scan(URL_PATTERN).select {|f| # Extract TLD h, m, tail = f.rpartition(":") w = (h.include?"/") ? h : tail head, m, tld = w.rpartition(".") tlds.include? tld.downcase }) endrescueInterruptendputs s.to_a.join(""`)我们得到了一份主机名列表(TLD根据白名单来匹配以避免误报)。然后我借助Burst 使用这个列表来下载 robots.txt:

?

123456789101112131415161718192021222324252627282930313233343536373839import socket``import random``import threading``from burst.all import *``from burst.exception import BurstException``from burst.utils import chunks``nthreads = ``4``slice_sz = ``200``switch_session(``'hosts'``)``hosts = open(``"hosts"``).read().splitlines()``random.shuffle(hosts)``def``worker(s):`` ``for``i, h ``in``enumerate(s):`` ``r = create(h + ``"/robots.txt"``)`` ``# Check if we have visited this host before`` ``for``hr ``in``history:`` ``if``hr.hostname == r.hostname:`` ``break`` ``else``:`` ``try:`` ``r()`` ``except (BurstException, socket.error):`` ``pass``for``ch ``in``chunks(hosts, slice_sz):`` ``slices = chunks(ch, nthreads)`` ``jobs = []`` ``for``s ``in``slices:````t = threading.``Thread``(target=worker,

kwargs={``"s"``: s})

`jobs.append(t) t.start() forj injobs:``whilej.is_alive():``j.join(1) `save()所有东东被分割为 chunks。一次处理一个 chunk,然后会话被保存。这样,在抓取过程中,就能查看结果。

手动使用 Burst 接口,就可以查找特定模式了:

?

123$ burst -rs hosts``hosts >>> ``history``.responded().filter

(lambda x: ``"Disallow: /admin/"``in``x.response.content)

{200:1563 | get.kim, rockville.wusa9.com, mars.nasa.gov, ... }抓取的某些统计数据:59,558 个网站被抓取,59,436 个网站给我们发送了响应。这是 Common Crawl 项目结果的、极具活力的较好体现。其中,37,431 个网站响应的 HTTP 状态码为 200。其中,35,376 个网站返回了貌似合法的 robots.txt(例如,匹配至少一个标准指令)。

意料之外的指令

robots.txt 定义非常少。让我们排除已知指令,看看还有哪些指令:

?

1234567891011121314151617181920212223242526272829303132RE_COMMENT``= re.compile(r``'(^||)s*#'``, re.``I``)``headers = [r``'Disallow'``, ``"Allow"``, ``"User-Agent"``,

"Noindex"``, ``"Crawl-delay"``,

`"Sitemap", "Request-Rate", "Host"]all_reg = [ re.compile(r'(^||)[ t]*'`

+ h + r``'[ t]*:?'``, re.``I``) ``for``h ``in``headers]

switch_session(``'hosts'``)``krs = history.responded().filter(`` ``lambda x: x.response.status == ``"200"``and`` ``len(x.response.content) > ``0``)``words = defaultdict(int)``for``r ``in``krs:`` ``# At least one directive recognised (avoid html answers)`` ``for``reg ``in``all_reg:`` ``if``reg.findall(r.response.content):`` ``break`` ``else``:`` ``continue`` ``# Dump anything not matching an expected regex`` ``for``l ``in``r.response.content.splitlines():`` ``for``reg ``in``all_reg + [``RE_COMMENT``,]:`` ``if``reg.findall(l):`` ``break`` ``else``:`` ``words[l] += ``1``print ``""``.join(`` ``[ str(v) + ``":"``+ k ``for``k,v ``in````sorted(words.items(), key=lambda x: x[``1``],

reverse=True)

`] `)下面是输出结果的摘录:

?

12345678910111235:ACAP-crawler: *``34:ACAP-disallow-crawl: ``/public/search/``32:ACAP-disallow-crawl: ``/article_email/``*``11:Clean-param: filter&ref&sq&sqc&sqm&sst&s&

id``&pm&``do``&``source``&method&size&query&sorting

[...]``12:

1:/* CUSTOMIZATIONS */``[...]``8:Diasllow: authcallback.aspx``1:Disalllow: ``/search``.php``1:Disalow: ``/dnd/``1:Dissalow: ``/catalog/product/gallery/有多个发现。首先,非标准指令的使用(比如,ACAP-*)。其次,某些 HTML 和 CSS 注释也出现了(robots.txt 注释以 # 打头)。最后,我们注意到大量拼写错误的 Disallow,和其它的指令名字。当放宽那些额外的拼写来抽出 Disallow 指令时,这就显示出用处了。

我觉得有意思的、意料之外的一条指令是,来自于 www.hanoverian.co.nz 网站混用了 Apache 配置:

?

12345678User-agent: Googlebot``Disallow: /*.gif$``User-agent: Googlebot``Disallow: /*.jpg$``RewriteEngine on``#Options +FollowSymlinks### 注释

修改上面的脚本,我们再看看下面的注释:

?

123456789241:``# block against duplicate content``241:``# block MSIE from abusing cache request``[...]``26:``#Disallow: /video/index.jsp*``[...]``3:``# --> fuck off. ``2:``# dotbot is wreckless.``2:``# KSCrawler - we don't need help from you``1:``# Huge pig crawlers are eating bandwidth我们观察到一些注释是正常的。它们貌似来自一个模板或拷贝粘贴。一些指令也被注释掉了。当抽出 Disallow 指令时,我们将重新使用它。最后,古老的网怒症爆发了。

隐藏模式

在我最初抽出 Disallow 指令时,我抽出了路径的第一部分,并做了累加,看看这样的路径被找到多少次。虽然这个列表看起来符合我的预期,但是第一条引起了我的注意:

?

1234569995:plenum``4603:search``4038:user``3398:wp-admin``3135:en``2774:admin其它条目看起来肯定熟悉,然而我还没有听过哪个应用程序或服务器子路径中有“plenum”。查询我们的 hosts.txt 集合:

?

12hosts >>> history.responded().filter(lambda x: ``"plenum"``in``x.response.content)``{``200``:``1``| www.knesset.gov.il}只有一个命中,它是以色列国会网站。为什么这个网站有这么多的 Disallow 指令呢?下面是它们的 robots.txt 摘录:

?

1234567891011User-agent: *``Disallow: ``/plenum/data/5510903``.doc``Disallow: ``/plenum/data/5697203``.doc``Disallow: ``/plenum/data/07822203``.doc``Disallow: ``/plenum/data/07822803``.doc``Disallow: ``/plenum/data/7111903``.doc``Disallow: ``/plenum/data/7714403``.doc``Disallow: ``/plenum/data/7714903``.doc``Disallow: ``/plenum/data/7715303``.doc``Disallow: ``/plenum/data/7118203``.doc``Disallow: ``/plenum/data/7118303``.doc大约 10,000 份这样的文档明确要求不要被索引。当然,这肯定有着某种厉害关系。这些文档大部分仍然可以在线访问。使用 Google 翻译服务,我们或许找到了会议记录。我这里就不引用这些文档了,不过你可以去看看。

按照同样思维,如果一个习惯在明确阻止访问某些文档上犯了错误,那么一定有其它习惯也会犯错误。另一个查询根据类似的模式被运行了,看看发生了什么。我关注于至少包含一位数字(引用次数?)的所有 Disallow 指令,并按照出现次数排序:

?

123427016:[``'www.yourdictionary.com'``]``12651:[``'download.oracle.com'``, ``'docs.oracle.com'``]``9995:[``'www.knesset.gov.il'``]``9381:[``'state.gov'``]忽略前两条,重点看第四条。恩,state.gov 有着类似的 robots.txt。

?

123456789101112131415User-agent: *``Disallow: ``/www/``Disallow: ``/waterfall/``[...]``Disallow: organization``/193959``.pdf``Disallow: organization``/193960``.pdf``Disallow: organization``/193543``.pdf``Disallow: organization``/193546``.pdf``Disallow: organization``/193567``.pdf``Disallow: organization``/193719``.pdf``Disallow: organization``/193723``.pdf``Disallow: organization``/193738``.pdf``Disallow: organization``/193775``.pdf``Disallow: organization``/193779``.pdf``Disallow: organization``/193791``.pdf刚才的一幕又发生了,大约 9,000 个文档明确要求不要被索引。然而这一次,它们不能再从服务器上找到了。幸运的是,互联网从来不会忘掉。

涉及的大部分文档已经被存档了。但是仔细看下时间线,虽然它们通常是在不同的日期添加的,但是所有文档都在同一个日期消失了,2013 年 4 月。Edward Snowden 当时所做的某些工作,或许是这次大规模删除背后的原因。重申,我自己不会深入这些文档细节,但是你可以自便。

回到 robots.txt 列表里次数较多的条目,我们发现了 www.createspace.com。Create Space 是一家 Amazon 公司,它帮助不知名的作者出版他们的书:

?

12345Disallow: ``/en/community/thread/12819``Disallow: ``/en/community/message/91211``Disallow: ``/en/community/message/92213``Disallow: ``/en/community/message/92216``Disallow: ``/en/community/message/92222大约 100 条这样的信息对于网络爬虫是隐藏的。如果我们专门看它们的内容:

我不打算深入该讨论的细节,但请记住,Create Space 明确要求这些信息不要被查询。

Disallow

现在,我们回头关注下最初目标,抽出每个路径的第一部分。每次匹配将被累加,一个文件算一次。这类似于文件频率。下面是前十个:

?

123456789105806:wp-admin``5398:search``3793:admin``3206:includes``2805:cgi-bin``2663:modules``2389:xmlrpc``2384:scripts``2333:user``2153:``cron这份额外结果取决于在你的正则表达式和目标(比如仅抽出字典里的名字)里包含了哪些字符。

列表质量

同样的实验使用 Common Crawl 的另一个子集实现。抽出主机名之后,对其它子集的对比就完成了。这两份列表的相似度用 Jaccard index测量。系数值介于 0 和 1 之间,其中,1 代表高相似度(相同),0 代表不共享任何元素(不相交)。对于这两个主机名文件列表,0.27 的系数值被计算出来了。这是合理的,它们有着大量不同的网站,但是一些大型网址是共享的(正则表达式捕捉了所有被找到的 URL,包括响应内容中的 URL,而不仅仅只是被抓取的 URL)。

同样的相似度被测量了,这次是根据从这两个集合中抽出的 Disallow 词语清单。下面是结果图表:

两份列表中的前 N 个词语根据 Jaccard index 被标示出来了。刚开始,前 100 个词语有着高相似度(~0.88)。随着子集规模的增大,相似度开始减少。三条竖线被标出来了,它们代表了第一个集合内的出现次数的门槛。当至少出现 10 次的一个词被找到时,两个列表仍然有着相对较高的相似度。

保留多少词?

根据上面的结果,只有一定数量的、排名靠前的词语被保留到了我们的最终列表。实际上,在能发送多少请求(时间、网络限制)和找到隐藏资源所得好处之间,有一个权衡。

个人看来,我宁愿在所有时间里运行差不多没有限制的列表,也不愿意错失一个资源(如果你的 SSH keys 可以访问到 ~huang 目录,那么我不想掠过它们)。

限制

下面是这次练习中值得注意的一些限制。首先,用到的算法根本没有优化。明显的改进是存在的,但是本文的目标是,这种实验需要的脚本少于一百行,主要复用了已有工具。

其次,被频繁访问的主机名取决于 Common Crawl 的抓取策略。既然基于开源项目 Nutch,那么已知的偏见就不要有了。我没有阅读 Nutch 的源代码来证实这个假设。

第三,一些不相关的重复或许出现了,它们人为地增强了某些词语的分数。这对于有着多个子域名的域名,是尤为正确的。在此,我只是基于最大的域名(*.blogspot.*,*.wordpress.com 等)实现了一份黑名单。

最后,只有 Disallow 指令被分析了。其它有用的信息或许产生于其它指令的聚合中。

结论

正如我们所见,robots.txt 的使用不是没有影响。在最简单的情况下,它将泄露受限制的路径和你的服务器所使用的技术。不过,经过进一步的调查,有人或许发现,一些内容不应该出现在那里。

从防卫者的角度看,两个常见谬误仍然存在。第一,robots.txt 或多或少扮演着访问控制的机制。第二,它们的内容只会被搜索引擎看到、而不会被人看到。我希望这篇文章向你展示了为什么这两点是错误的,以及这种假设可能产生什么影响。

汇智网

用互动的方式,学习最前沿的编程技术

微信号:ihubwiz

长按二维码点击“识别图中二维码”即可关注

微博: @汇智网

分享到朋友圈丨点右上角···分享

汇智网课程内容已经覆盖:

Node.js、Mongodb、javascript、c、c#、php、python、angularjs、ionic、react、uml、redis、mysql、nginx、css、html、bootstrap、flask、gulp、mocha、git、meteor、canvas、typescript、Material Design Lite、ECMAScript、zebra、Elasticsearch、d3.js、express、Mongoose、jquery。