渗透基础——选择一个合适的 C2 域名
0x00 前言
在渗透测试中,常常需要选择一个合适的域名作为 c2 服务器,那么什么样的域名才能称之为"合适"呢?
expireddomains.net 也许能够给你一些思路。
通过 expireddomains.net 能够查询到最近过期或删除的域名,更重要的是它提供了关键词搜索功能。
本文将要测试过期域名自动化搜索工具 CatMyFish,分析原理,修正其中的 bug,使用 python 编写一个爬虫,获得所有搜索结果。
0x01 简介
本文将要介绍以下内容:
- 测试过期域名自动化搜索工具 CatMyFish
- 分析原理修正 CatMyFish 中的 bug
- 爬虫开发思路和实现细节
- 开源 python 实现的爬虫代码
0x02 测试过期域名自动化搜索工具 CatMyFish
下载地址: https://github.com/Mr-Un1k0d3r/CatMyFish
主要实现流程
- 用户输入关键词
- 脚本将搜索请求发送到 expireddomains.net 进行查询
- 获得域名列表
- 脚本将域名发送到 Symantec BlueCoat 进行查询
- 获取每个域名的类别
- expireddomains.net 地址: https://www.expireddomains.net/
- Symantec BlueCoat 地址: https://sitereview.bluecoat.com/
实际测试
需要安装 python 库 beautifulsoup4
pip install beautifulsoup4
尝试搜索关键词 microsoft,脚本报错,如下图
脚本对结果的解析出现了问题
于是,按照 CatMyFish 的实现思路自己编写脚本测试一下
访问 expireddomains.net 查询关键词 microsoft
,代码如下:
import urllib
import urllib2
from bs4 import BeautifulSoup
url = "https://www.expireddomains.net/domain-name-search/?q=microsoft"
req = urllib2.Request(url)
res_data = urllib2.urlopen(req)
html = BeautifulSoup(res_data.read(), "html.parser")
tds = html.findAll("td", {"class": "field_domain"})
for td in tds:
for a in td.findAll("a", {"class": "namelinks"}):
print a.text
共获得 15 个结果,如下图
通过浏览器访问,共获得 25 个结果,如下图
经过对比发现通过脚本获得的数目相比浏览器要少,应该是脚本在筛选的时候出现了问题
注:
初学者建议掌握一下 beautifulsoup4 的基本使用技巧,本文暂略
0x03 查找 bug 原因
1、根据 response 查看域名标签,对筛选规则进行判断
需要获取到接收到的 response 数据,通过查看各个域名对应的标签,判断是否在标签筛选的时候出现了问题
查看 response 数据的两种方法:
(1) 使用 Chrome 浏览器查看
F12
-> More tools
-> Network conditions
重新加载网页,选择 ?q=microsoft
-> Resonse
如下图
(2) 使用 python 脚本
代码如下:
import urllib
import urllib2
url = "https://www.expireddomains.net/domain-name-search/?q=microsoft"
req = urllib2.Request(url)
res_data = urllib2.urlopen(req)
print res_data.read()
分析 response 数据,发现出错原因:
使用原测试脚本能够提取出如下数据中的域名:
<td class="field_domain"><a class="namelinks" href="/goto/1/71h90s/59/?tr=search" id="linksdd-domain71h90s" rel="nofollow" target="_blank" title="MicroSoft.msk.ru"><strong>MicroSoft</strong>.msk.ru</a><ul class="kmenucontent" id="links-domain71h90s" style="display:none;"><li class="first"><a class="favicons favgodaddy" href="/goto/16/75wxyx/59/?tr=search" rel="nofollow" target="_blank" title="Register at GoDaddy.com">GoDaddy.com</a></li><li><a class="favicons favdynadot" href="/goto/53/740s95/59/?tr=search" rel="nofollow" target="_blank" title="Register at Dynadot.com">Dynadot.com</a></li><li><a class="favicons favuniregistry" href="/goto/66/7252us/59/?tr=search" rel="nofollow" target="_blank" title="Register at Uniregistry.com">Uniregistry.com</a></li><li><a class="favicons favnamecheap" href="/goto/43/7459ux/59/?tr=search" rel="nofollow" target="_blank" title="Register at Namecheap.com">Namecheap.com</a></li><li><a class="favicons favonecom" href="/goto/57/71gmkr/59/?tr=search" rel="nofollow" target="_blank" title="Register at One.com">One.com</a></li><li><a class="favicons fav123reg" href="/goto/48/7254ap/59/?tr=search" rel="nofollow" target="_blank" title="Register at 123-reg.co.uk">123-reg.co.uk</a></li></ul></td>
但是 response 数据中还包含另一种类型的数据:
<td class="field_domain"><a href="/goto/1/4o47ng/39/?tr=search" rel="nofollow" target="_blank" title="NewMicroSoft.com">New<strong>MicroSoft</strong>.com</a></td>
原测试脚本没有提取该标签中保存的域名信息
0x04 bug 修复
筛选思路:
获得标签 <td class="field_domain">
中第一个 title 的内容
原因:
这样能同时获得两组数据中保存的域名信息,过滤无效信息(如第二个 title 中的域名 GoDaddy.com)
实现代码:
tds = html.findAll("td", {"class": "field_domain"})
for td in tds:
print td.findAll("a")[0]["title"]
因此,获得完整查询结果的测试代码如下:
import urllib
import urllib2
import sys
from bs4 import BeautifulSoup
def SearchExpireddomains(key):
url = "https://www.expireddomains.net/domain-name-search/?q=" + key
req = urllib2.Request(url)
res_data = urllib2.urlopen(req)
html = BeautifulSoup(res_data.read(), "html.parser")
tds = html.findAll("td", {"class": "field_domain"})
for td in tds:
print td.findAll("a")[0]["title"]
if __name__ == "__main__":
SearchExpireddomains(sys.argv[1])
成功获得第一页的所有结果,测试如下图
0x05 获得所有查询结果
expireddomains.net 每页保存 25 个结果,想要获得所有结果,需要发送多个请求,遍历所有查询页面的结果
首先需要获得所有结果的数目,除以 25 获得需要查询的页面个数
1、统计所有结果
查看 Response,找到表示搜索结果数目的位置,内容如下:
<div class="pagescode page_top">
<div class="addoptions left">
<span class="showfilter">Show Filter</span>
<span>(About <strong>20,213 </strong> Domains)</span>
Chrome 浏览器显示如下图
为了简化代码长度,使用 select()
直接传入 CSS 选择器进行筛选,在对标签 strong
进行筛选后,第 1 个标签表示结果数目,对应查询代码为:
print html.select('strong')[0]
输出结果为 <strong>20,213 </strong>
提取其中的数字:
print html.select('strong')[0].text
输出结果为 20,213
去掉中间的",":
print html.select('strong')[0].text.replace(',', '')
输出结果为 20213
除以 25 即可获得需要查询的页面个数,这里需要注意需要将字符串类型的"20213"转换为整型 20213
2、猜测查询规律
第二页查询的 url:
https://www.expireddomains.net/domain-name-search/?start=25&q=microsoft
第三页查询的 url:
https://www.expireddomains.net/domain-name-search/?start=50&q=microsoft
找到查询规律,第 i 页查询的 url:
https://www.expireddomains.net/domain-name-search/?start=<25*(i-1))>&q=microsoft
注:
经测试,expireddomains.net 对未登录用户最多提供 550 个的结果,共 21 页
3、对结果进行判断
在脚本实现上,需要对结果进行判断,如果结果大于 550,只输出 21 页,如果小于 550,输出<结果/25>页
4、模拟浏览器访问(备选)
当我们使用脚本尝试自动查询多个页面时,如果网站使用了反爬虫机制,无法获得真实数据
经测试,expireddomains.net 并未开启反爬虫机制
如果在将来,expireddomains.net 开启了反爬虫机制,脚本需要模拟浏览器发送请求,在头部附加 User-Agent 等信息
查看 Chrome 浏览器获得发送请求的信息,如下图
对照请求,添加头部信息即可绕过
示例代码:
req.add_header("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36")
完整代码实现地址:
https://github.com/3gstudent/GetExpiredDomains
实际测试:
搜索关键词 microsoftoffices
,结果少于 550,如下图
搜索关键词 microsoft
,结果大于 550,只显示 21 页,如图
同 Web 访问的内容对比,结果相同,测试成功
0x06 小结
本文测试了过期域名自动化搜索工具 CatMyFish,分析原理,修正其中的 bug,使用 python 编写爬虫获得所有搜集结果,分享开发思路,开源代码。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
下一篇: Covenant 利用分析
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论