网易首页 > 网易号 > 正文 申请入驻

捕鱼达人——钓鱼基础设施的应用分析

0
分享至

,通过受害者请求加载资源来判定目标是否打开了邮件:,其位于gophish服务器端,并通过来识别受害者。

0x00 前言

邮件钓鱼攻击是一种常见的网络攻击方法,无论是广撒网式的“大海捞鱼”,还是极具精准化、定制化的鱼叉式钓鱼,都越来越普遍。

在红蓝模拟对抗的场景下,邮件钓鱼攻击也逐渐演变为一种精准、高效的打点手段,或通过恶意链接窃取敏感信息数据,或结合恶意附件夺控目标权限。

本文将从红队基础设施构建的角度出发,结合Cobalt Strike、iRedMail、Gophish等工具,探索邮件钓鱼基本架构的应用。

本文旨在交流技术,严禁从事不正当活动。网络不是法外之地,争做遵纪守法好公民。

0x01 基本设置

基本环境信息:

  • mail vps: xx.xx.xx.52
  • domain: fxxkxxxxxx.xxxxx
  • cobalt strike vps: xx.xx.xx.155
  • 本地局域网出口:xx.xx.xx.35

发送钓鱼邮件的第一步,是先拥有发送者邮箱,常见的如Gmail、Hotmail、163等第三方邮箱。但在真实的钓鱼场景中,一般不会使用此类邮箱。其原因一是容易暴露身份,且邮箱的欺骗性较低,效果不好;二是第三方邮箱通常会有多种限制、严格的过滤规则等,在发送大批量邮件时受限。

因此,常见的模式是基于目标的信息搜集,申请近似的域名,并使用自己的服务器搭建邮箱系统来发送邮件。

如何让申请的域名更具欺骗性、迷惑性,也深有讲究,本文不多赘述,仅申请一无关域名做测试学习使用。

下面简介基于iRedMail的邮箱系统搭建。首先,申请域名,解析信息配置如下:

同时在VPS上配置主机的域名信息:

配置好域名,在服务器上安装iRedMail。

iRedMail是一个开源、免费的邮件服务器项目,其核心组件及其对应的功能主要有:Postfix: SMTP 服务器,Dovecot: POP3/IMAP/Managesieve 服务器,Apache: Web 服务器,MySQL: 用于存储其它程序的数据,也可用于存储邮件帐号。

搭建过程及注意事项,可参考iRedMail官网手册,本文不展开陈述。

注意在安装的过程中,会提示到是否使用由iRedMail提供的防火墙规则,这里根据个人情况选择即可。但从安全的角度出发,还是一定要给服务器上防火墙规则。本文仅做测试学习,暂不启用防火墙策略。

安装完成后,进入邮箱管理员界面,添加两名用户:

登录邮箱界面,即可实现基本邮件发送功能:

注:通过cobalt strike发送邮件,要开启邮箱服务器smtp的sasl身份验证。通过修改邮件服务器postfix配置文件的即可:

smtpd_sasl_auth_enable = yes

测试通过telnet smtp和pop3发送接收邮件:

0x02 钓鱼初试

下面演示基于Cobalt Strike的钓鱼邮件功能

cobalt strike中集成的邮件钓鱼功能模块如图:

要配置的信息包括:

1. Targets : 钓鱼的目标

这里以为钓鱼目标对象

jason1

2. Targets : 钓鱼邮件模板

在真实钓鱼环境中,为使邮件具备以假乱真的欺骗性,通常结合社会工程学手段,精心构造邮件内容。本文以演示为目的,构造一个简单的邮件,并将其内容存储为文件。

mail-template.eml

3. Attachment:邮件附件,一般为恶意文档

可利用cobalt strike 的attack工具包,生成 macro 宏,插入到文档中,制作简易的恶意文档,或采取其他多种方式构造。

4. Embed URL:嵌入的钓鱼链接

可利用cobalt strike的attack工具包,克隆网站,构造钓鱼网站。这里随意找一个网站的登录页面,克隆到自己的服务器上。

在真实环境中,一般是针对目标的关键敏感信息构造钓鱼页面,包括邮箱身份信息、银行卡信息、通讯社交软件信息等,或是在已经突破进入目标内网后,想要获取内网办公系统、企业内网资源管理系统等信息。

5. Mail Server是邮件服务器的地址和身份信息,Bounce TO模仿发件人,正常填写即可

如图所示,目标接收到钓鱼邮件。邮件内记录到,邮件是从cobalt strike服务器发送出去的。且邮件内的连接地址已经替换为克隆后的服务器链接,当用户跳转进入该页面,输入登录信息,cobalt strike控制台就能观察到其输入信息。

还可以结合cobalt strike的listener,采用向邮件中添加恶意附件、替换链接为恶意程序等方式,让目标Beacon上线,此部分非本文演示的重点。

0x03 再上层楼

cobalt strike虽然集成了邮件钓鱼功能,但在某些情况下,显然不是最佳选项。正所谓,用专业的工具做专业的事。针对邮件钓鱼,也有不少专门的工具,例如Gophish,Gophish项目地址。

Gophish是为企业和渗透测试人员设计的开源网络钓鱼工具包。 它提供快速、简易地设置和执行网络钓鱼攻击的能力。

Gophish安装完成后,存在两个页面,一个是钓鱼页面,一个是后台管理控制页面。

本文的实验环境将Gophish直接安装在邮件服务器上(fxxkxxxxxx.xxxxx),但在真实的环境中,应尽量遵循功能分割的原则

钓鱼页面(85端口,可通过配置文件更改):

后台管理控制页面(3333端口,可通过配置文件更改):

下面将介绍如何基于Gophish来实现邮件钓鱼

1-Sending Profile:设置发件策略

其主要内容是,把用来执行发送邮件操作的发送者邮箱配置信息提供给Gophish。

其中几个关键字段:

  • Interface Type:接口类型,默认为smtp且不可更改,因此发送邮件的邮箱需要开启SMTP服务。
  • Host: SMTP服务器地址
  • Username: SMTP服务器认证的用户名
  • Password: SMTP服务器认证的口令
  • Email Headers:自定义邮件头部信息,可按需填写。

填写好相应信息后,可通过发送测试邮件,确认发件邮箱能否正常发送邮件:

Send Test Email

2-Landing Pages:目标钓鱼页面

主要内容是设置钓鱼邮件内,超链接指向的钓鱼网页,比如银行账号登录页面、社交账号登录页面等。

导入超链接,会自动解析html,我们以随便搜到的某一个后台登录页面为例。

Import Site

勾选,即捕获受害者在钓鱼页面登录时的提交数据。

Capture Submitted Data

最下端的,填入提交数据后跳转的页面,为给受害者营造一种输错账号密码的假象,可填入登录页面的连接,并在URL后填入一个报错参数,

Redirect to

https://xxxxx/account/login?errorcode=1

3-Email Templates:邮件模板

主要是发送给受害者的邮件内容模板。通常是在社会工程学的基础上,分析目标特征,发送具有针对性的内容,这里简单构造一个公司发送内部福利的邮件内容。

直接通过导入邮件模板,记得勾选选项,将邮件内容里的超链接替换为钓鱼页面链接。

Import Email

Change Links to Point to Landing Page

在邮件末尾添加一个跟踪图像,可以掌握受害者是否打开了钓鱼邮件。

Add Tracking Image

可添加附件,增强邮件的欺骗性,同时可结合免杀木马使用。

Add Files

4-Users and Groups:目标用户和组

可直接导入csv格式的文件,批量导入用户;也可手工添加。这里以jason1和jason2两名同志为目标。

5-Campaign:执行钓鱼行动

部分是整合上述各个子环节,综合起来执行钓鱼行动。

Campaign

依次填入邮件模板、目标钓鱼页面、发件策略、目标用户信息。

其中,需要注意的是选项。在上文中提到了,gophish启动运行后,我们配置了85端口运行钓鱼页面,3333端口运行后台管理控制页面。当我们启动Campaign事件,85端口会运行我们在部分配置的钓鱼页面,即某后台登录页面。因此,此处的选项填入运行gophish的服务器地址。此外,还需保证此地址对于目标受害者的网络环境来说是可访问的。当填写公网vps的IP地址或域名时,需确保目标内网环境能够访问公网vps,即需要注意防火墙策略等。当填写内网ip地址时,则意味着钓鱼目标页面只能够被目标内网访问,外部网络环境无法访问。当然,也可以输入其他方式搭建的钓鱼服务器地址。

URL

Landing Pages

URL

而后受害者jason1就会接受到钓鱼邮件:

需要注意的是,在邮件内有提示,字面意思很简单,就是为了保护隐私,远程资源被禁掉了。出现提示的原因,是因为我们在部分,启用了选项,会在邮件末尾追加一个远程图像资源

To protect your privacy remote resources have been blocked

Email Template

Add Tracking Image

如图所示,在邮件最后有一个

rid

点击邮件内的超链接,将跳转到我们在部分布局的钓鱼后台登录页面:

Landing Page

输入用户名、口令,登录后,页面将跳转进入真实的后台登录页面,并在url处可看到我们添加的参数,,让受害者以为输错了登录信息,增强欺骗性。

errorcode=1

此时,gophish控制后台已经捕捉记录到受害者的信息:

上图显示,两个目标中,有一个打开了邮件,并点击了钓鱼超链接,输入了登录信息,此人即是jason1:

jason1的结果中,成功记录到了提交的用户名和口令信息。

0x04 写在文末

有几点个人思考和分析,同大家分享:

1. 服务器SMTP出口

无论是采用cobalt strike还是gophish,在真实的环境中,其实都应该遵循功能独立分割的原则。即每台服务器只完成特定的功能,其目的就是在于提高红队基础设施的健壮性、可扩展性、弹性恢复能力。

具体而言,cs server和 gophish server都会通过SMTP协议与mail server连接并发送邮件。即cs server、gophish server、mail server都需要连接到目标的25端口SMTP服务,cs server、gophish server、mail server需放行25出站端口。

部分的VPS或者ISP会默认封禁25出站端口,其目的在于防止大量发送垃圾邮件。例如某厂商的vps,其官方文档中有提到,对于某些服务器实例,将会封禁smtp出站端口,且其屏蔽SMTP的技术不是屏蔽25端口,而是只要是SMTP出站包都会被ban掉。可以向其官方提交工单,申请开放25出站端口,来解决此问题。

2. 钓鱼页面的数据捕获问题

利用gophish来clone制作钓鱼页面,仅仅通过Import难以实现完美克隆,可能无法满足钓鱼的功能需求,通常需要手动修正。

最为常见的问题就是:无法捕获受害者在钓鱼页面提交的数据

首先看一下的源码中,对钓鱼页面部分的处理过程

gophish

在功能部分,通过导入钓鱼页面后,其解析处理html页面代码如下:

Landing Page

Import

// parseHTML parses the page HTML on save to handle the// capturing (or lack thereof!) of credentials and passwordsfunc (p *Page) parseHTML() error { d, err := goquery.NewDocumentFromReader(strings.NewReader(p.HTML)) if err != nil { return err } forms := d.Find("form") forms.Each(func(i int, f *goquery.Selection) { // We always want the submitted events to be // sent to our server f.SetAttr("action", "") if p.CaptureCredentials { // If we don't want to capture passwords, // find all the password fields and remove the "name" attribute. if !p.CapturePasswords { inputs := f.Find("input") inputs.Each(func(j int, input *goquery.Selection) { if t, _ := input.Attr("type"); strings.EqualFold(t, "password") { input.RemoveAttr("name") } }) } else { // If the user chooses to re-enable the capture passwords setting, // we need to re-add the name attribute inputs := f.Find("input") inputs.Each(func(j int, input *goquery.Selection) { if t, _ := input.Attr("type"); strings.EqualFold(t, "password") { input.SetAttr("name", "password") } }) } } else { // Otherwise, remove the name from all // inputs. inputFields := f.Find("input") inputFields.Each(func(j int, input *goquery.Selection) { input.RemoveAttr("name") }) } }) p.HTML, err = d.Html() return err}// Validate ensures that a page contains the appropriate detailsfunc (p *Page) Validate() error { if p.Name == "" { return ErrPageNameNotSpecified } // If the user specifies to capture passwords, // we automatically capture credentials if p.CapturePasswords && !p.CaptureCredentials { p.CaptureCredentials = true } if err := ValidateTemplate(p.HTML); err != nil { return err } if err := ValidateTemplate(p.RedirectURL); err != nil { return err } return p.parseHTML()}// PostPage creates a new page in the database.func PostPage(p *Page) error { err := p.Validate() if err != nil { log.Error(err) return err } // Insert into the DB err = db.Save(p).Error if err != nil { log.Error(err) } return err}

其整体流程是,先验证各个字段的详细情况,然后解析目标页面,并将结果保存到数据库中。

Validate

parseHTML

db.Save(p)

sqlite数据库中的记录如下:

需要注意的是,此时存入数据库的html页面,是经过函数处理的页面。例如代码会将form表单的属性置空

parseHTML()

f.SetAttr("action", "")

action

上图即为处理前后的html源码对比,可以观察到。即表示,当受害者在钓鱼页面提交数据,把数据提交到当前钓鱼页面,而非真实的登录页面,否则钓鱼后台将无法捕获到用户提交的数据。

action=""

post

当受害者在钓鱼页面输入了身份信息并提交后,其后台处理代码主流程如下:

// PhishHandler handles incoming client connections and registers the associated actions performed// (such as clicked link, etc.)func (ps *PhishingServer) PhishHandler(w http.ResponseWriter, r *http.Request) { r, err := setupContext(r) if err != nil { // Log the error if it wasn't something we can safely ignore if err != ErrInvalidRequest && err != ErrCampaignComplete { log.Error(err) } http.NotFound(w, r) return } w.Header().Set("X-Server", config.ServerName) // Useful for checking if this is a GoPhish server (e.g. for campaign reporting plugins) var ptx models.PhishingTemplateContext // Check for a preview if preview, ok := ctx.Get(r, "result").(models.EmailRequest); ok { ptx, err = models.NewPhishingTemplateContext(&preview, preview.BaseRecipient, preview.RId) if err != nil { log.Error(err) http.NotFound(w, r) return } p, err := models.GetPage(preview.PageId, preview.UserId) if err != nil { log.Error(err) http.NotFound(w, r) return } renderPhishResponse(w, r, ptx, p) return } rs := ctx.Get(r, "result").(models.Result) rid := ctx.Get(r, "rid").(string) c := ctx.Get(r, "campaign").(models.Campaign) d := ctx.Get(r, "details").(models.EventDetails) // Check for a transparency request if strings.HasSuffix(rid, TransparencySuffix) { ps.TransparencyHandler(w, r) return } p, err := models.GetPage(c.PageId, c.UserId) if err != nil { log.Error(err) http.NotFound(w, r) return } switch { case r.Method == "GET": err = rs.HandleClickedLink(d) if err != nil { log.Error(err) } case r.Method == "POST": err = rs.HandleFormSubmit(d) if err != nil { log.Error(err) } } ptx, err = models.NewPhishingTemplateContext(&c, rs.BaseRecipient, rs.RId) if err != nil { log.Error(err) http.NotFound(w, r) } renderPhishResponse(w, r, ptx, p)}// renderPhishResponse handles rendering the correct response to the phishing// connection. This usually involves writing out the page HTML or redirecting// the user to the correct URL.func renderPhishResponse(w http.ResponseWriter, r *http.Request, ptx models.PhishingTemplateContext, p models.Page) { // If the request was a form submit and a redirect URL was specified, we // should send the user to that URL if r.Method == "POST" { if p.RedirectURL != "" { redirectURL, err := models.ExecuteTemplate(p.RedirectURL, ptx) if err != nil { log.Error(err) http.NotFound(w, r) return } http.Redirect(w, r, redirectURL, http.StatusFound) return } } // Otherwise, we just need to write out the templated HTML html, err := models.ExecuteTemplate(p.HTML, ptx) if err != nil { log.Error(err) http.NotFound(w, r) return } w.Write([]byte(html))}// HandleFormSubmit updates a Result in the case where the recipient submitted// credentials to the form on a Landing Page.func (r *Result) HandleFormSubmit(details EventDetails) error { event, err := r.createEvent(EventDataSubmit, details) if err != nil { return err } r.Status = EventDataSubmit r.ModifiedDate = event.Time return db.Save(r).Error}func (r *Result) createEvent(status string, details interface{}) (*Event, error) { e := &Event{Email: r.Email, Message: status} if details != nil { dj, err := json.Marshal(details) if err != nil { return nil, err } e.Details = string(dj) } AddEvent(e, r.CampaignId) return e, nil}// AddEvent creates a new campaign event in the databasefunc AddEvent(e *Event, campaignID int64) error { e.CampaignId = campaignID e.Time = time.Now().UTC() whs, err := GetActiveWebhooks() if err == nil { whEndPoints := []webhook.EndPoint{} for _, wh := range whs { whEndPoints = append(whEndPoints, webhook.EndPoint{ URL: wh.URL, Secret: wh.Secret, }) } webhook.SendAll(whEndPoints, e) } else { log.Errorf("error getting active webhooks: %v", err) } return db.Save(e).Error}

其主要流程是是,通过将用户的相关数据存入数据库中的event,然后将重定向页面(通常是真实的登录页面)返回给受害者。

HandleFormSubmit()->createEvent()->AddEvent()

renderPhishResponse()->http.Redirect(w, r, redirectURL, http.StatusFound)

这里想要说明的是,红队人员通过导入钓鱼页面后,函数解析钓鱼页面时,其对目标钓鱼页面有一定的规则、格式上的要求。否则,会导致无法捕获受害者提交的表单数据,或数据不全。

Import

parseHTML()

参考此篇博文中的说明:

结构,即表单(POST方式)— Input标签(具有name属性)Input标签(submit类型)— 表单闭合的结构。
比如:导入的前端源码,必须存在严格存在
···
再如:对于需要被捕获的表单数据,除了input标签需要被包含在
中,还需满足该存在name属性。例如,否则会因为没有字段名而导致value被忽略。

仅以此文作技术交流,恪守职业操守,严禁非法之用。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
巴拉圭“胸神”16年后重返世界杯,靠火辣身材爆红,愿为赢球裸奔

巴拉圭“胸神”16年后重返世界杯,靠火辣身材爆红,愿为赢球裸奔

深析古今
2026-06-14 15:32:00
双标破防反带火,《给阿嫲的情书》新加坡争议票房狂飙

双标破防反带火,《给阿嫲的情书》新加坡争议票房狂飙

喜欢历史的阿繁
2026-06-21 07:43:38
首例5胞胎长大了,父亲已劳累去世,母亲直言:如能重来一个也不要

首例5胞胎长大了,父亲已劳累去世,母亲直言:如能重来一个也不要

柳絮忆史
2025-07-22 07:15:03
柬埔寨对中国免签4个月,这波免签含金量如何?赴柬旅游安全吗?

柬埔寨对中国免签4个月,这波免签含金量如何?赴柬旅游安全吗?

之乎者也小鱼儿
2026-06-21 10:48:14
布达拉宫剖开内部图看懂:为啥有人逛着会觉得阴森压抑

布达拉宫剖开内部图看懂:为啥有人逛着会觉得阴森压抑

西楼知趣杂谈
2026-06-13 19:18:15
范戴克被批转身像波音747,克洛普:范德法特都不值一提

范戴克被批转身像波音747,克洛普:范德法特都不值一提

懂球帝
2026-06-21 11:15:12
给别人当继父是什么感觉?网友:养了20年,下班晚没做饭,喊我滚

给别人当继父是什么感觉?网友:养了20年,下班晚没做饭,喊我滚

夜深爱杂谈
2026-05-28 07:55:43
迷你罗迎来16周岁!身高直逼两米,全家发文送上暖心祝福

迷你罗迎来16周岁!身高直逼两米,全家发文送上暖心祝福

手工制作阿歼
2026-06-21 11:41:01
吳麗珠早知道陳敏兒患重病,揭鍾保羅真正尋死原因:佢太叻

吳麗珠早知道陳敏兒患重病,揭鍾保羅真正尋死原因:佢太叻

粤睇先生
2026-06-21 01:00:05
某鱼惊现“天价笔”:800元一支的中性笔,藏着多少肮脏暗语?

某鱼惊现“天价笔”:800元一支的中性笔,藏着多少肮脏暗语?

番外行
2026-02-26 19:53:05
深度|渐冻人冯锦源的十年:他不再用标签定义自己

深度|渐冻人冯锦源的十年:他不再用标签定义自己

澎湃新闻
2026-06-21 09:54:30
巴西足协主席被爆挪用公款,安排情妇纽约度假,又携妻子抵世界杯

巴西足协主席被爆挪用公款,安排情妇纽约度假,又携妻子抵世界杯

译言
2026-06-21 07:39:15
美媒曝重磅大交易,涉及7人+2首轮,马刺送走福克斯,彻底换血!

美媒曝重磅大交易,涉及7人+2首轮,马刺送走福克斯,彻底换血!

体育见习官
2026-06-20 12:34:55
比圣泉集团还猛?这家10元低价+PCB树脂龙头+光刻胶 游资净抢5亿

比圣泉集团还猛?这家10元低价+PCB树脂龙头+光刻胶 游资净抢5亿

元芳说投资
2026-06-21 06:30:17
原配不知丈夫私生子周岁宴,亲友集体沉默!

原配不知丈夫私生子周岁宴,亲友集体沉默!

尘埃里的看客
2026-06-19 08:37:01
江西一女老板发现男员工神似亡夫,见到员工父母后她愣了

江西一女老板发现男员工神似亡夫,见到员工父母后她愣了

兰姐说故事
2025-05-11 10:00:14
马克龙证实,在阿拉斯加特朗普曾准备将乌克兰“出卖”给普京

马克龙证实,在阿拉斯加特朗普曾准备将乌克兰“出卖”给普京

走进乌克兰2022
2026-06-20 10:09:13
《抓特务》票房倒挂,冯小刚拿出最大的诚意,却生不逢时

《抓特务》票房倒挂,冯小刚拿出最大的诚意,却生不逢时

刘森森
2026-06-21 10:30:54
离谱世界杯!裁判制止冲突手表掉落 进球功臣捡到自己悄悄戴上了

离谱世界杯!裁判制止冲突手表掉落 进球功臣捡到自己悄悄戴上了

狍子歪解体坛
2026-06-20 19:10:30
哈维-西蒙斯:我已经能够重新走路了,肌肉力量也在慢慢恢复

哈维-西蒙斯:我已经能够重新走路了,肌肉力量也在慢慢恢复

懂球帝
2026-06-21 03:55:05
2026-06-21 13:20:49
安全客 incentive-icons
安全客
有思想的安全新媒体
1360文章数 4754关注度
往期回顾 全部

科技要闻

马斯克拿下7800亿元天价薪酬 2028年可兑现

头条要闻

媒体:欧盟"对华特别会议"后 德国对中国立场变化明显

头条要闻

媒体:欧盟"对华特别会议"后 德国对中国立场变化明显

体育要闻

德国的超级替补,10年前还在工厂上班

娱乐要闻

李乃文带妻子法国购物,2人5个孩子!

财经要闻

蔚来的“暗战”时刻

汽车要闻

惊出冷汗!重庆实测奥迪A5L,华为智驾这波操作绝了…

态度原创

时尚
亲子
艺术
手机
游戏

夏天裤子不要总穿黑的,看看这些白色阔腿裤,百搭清爽又显瘦

亲子要闻

从幼儿园小朋友那里听来的八卦有多炸裂?我儿子说他有两个爸爸

艺术要闻

9个中国建筑获“2026 RIBA国际卓越奖”

手机要闻

消息称三星正在S23/24、Z Fold7等机型上测试One UI 9 Beta

台湾评级意外泄露!《暗黑4》Switch 2版已在路上

无障碍浏览 进入关怀版