钓鱼基础设施的应用分析(钓鱼邮件利用、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官網手冊(https://docs.iredmail.org/index.html),本文不展開陳述。
注意在安裝的過程中,會提示到是否使用由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項目地址(https://github.com/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:目標釣魚頁面
主要內容是設置釣魚郵件內,超鏈接指向的釣魚網頁,比如銀行賬號登錄頁面、社交賬號登錄頁面等。
Import Site導入超鏈接,會自動解析html,我們以隨便搜到的某一個后臺登錄頁面為例。
勾選Capture Submitted Data,即捕獲受害者在釣魚頁面登錄時的提交數據。
最下端的Redirect to,填入提交數據后跳轉的頁面,為給受害者營造一種輸錯賬號密碼的假象,可填入登錄頁面的連接,并在URL后填入一個報錯參數,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部分是整合上述各個子環節,綜合起來執行釣魚行動。
依次填入郵件模板、目標釣魚頁面、發件策略、目標用戶信息。
其中,需要注意的是URL選項。在上文中提到了,gophish啟動運行后,我們配置了85端口運行釣魚頁面,3333端口運行后臺管理控制頁面。當我們啟動Campaign事件,85端口會運行我們在Landing Pages部分配置的釣魚頁面,即某后臺登錄頁面。因此,此處的URL選項填入運行gophish的服務器地址。此外,還需保證此地址對于目標受害者的網絡環境來說是可訪問的。當填寫公網vps的IP地址或域名時,需確保目標內網環境能夠訪問公網vps,即需要注意防火墻策略等。當填寫內網ip地址時,則意味著釣魚目標頁面只能夠被目標內網訪問,外部網絡環境無法訪問。當然,也可以輸入其他方式搭建的釣魚服務器地址。
而后受害者jason1就會接受到釣魚郵件:
需要注意的是,在郵件內有提示To protect your privacy remote resources have been blocked,字面意思很簡單,就是為了保護隱私,遠程資源被禁掉了。出現提示的原因,是因為我們在Email Template部分,啟用了Add Tracking Image選項,會在郵件末尾追加一個遠程圖像資源<img>,通過受害者請求加載資源來判定目標是否打開了郵件:
如圖所示,在郵件最后有一個<img>,其位于gophish服務器端,并通過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的源碼中,對釣魚頁面部分的處理過程
在Landing Page功能部分,通過Import導入釣魚頁面后,其解析處理html頁面代碼如下:
// parseHTML parses the page HTML on save to handle the // capturing (or lack thereof!) of credentials and passwords func (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 serverf.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 attributeinputs := 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 details func (p *Page) Validate() error {if p.Name == "" {return ErrPageNameNotSpecified}// If the user specifies to capture passwords,// we automatically capture credentialsif 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 DBerr = db.Save(p).Errorif err != nil {log.Error(err)}return err }其整體流程是,先Validate驗證各個字段的詳細情況,然后parseHTML解析目標頁面,并將結果db.Save(p)保存到數據庫中。
sqlite數據庫中的記錄如下:
需要注意的是,此時存入數據庫的html頁面,是經過parseHTML()函數處理的頁面。例如f.SetAttr("action", "")代碼會將form表單的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 ignoreif 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 previewif 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 requestif 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 URLif 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 HTMLhtml, 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 = EventDataSubmitr.ModifiedDate = event.Timereturn 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 database func AddEvent(e *Event, campaignID int64) error {e.CampaignId = campaignIDe.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 }其主要流程是是,通過HandleFormSubmit()->createEvent()->AddEvent()將用戶的相關數據存入數據庫中的event,然后renderPhishResponse()->http.Redirect(w, r, redirectURL, http.StatusFound)將重定向頁面(通常是真實的登錄頁面)返回給受害者。
這里想要說明的是,紅隊人員通過Import導入釣魚頁面后,parseHTML()函數解析釣魚頁面時,其對目標釣魚頁面有一定的規則、格式上的要求。否則,會導致無法捕獲受害者提交的表單數據,或數據不全。
參考此篇博文中的說明:
比如:導入的前端源碼,必須存在嚴格存在<form method="post" ···><input name="aaa" ··· /> ··· <input type="submit" ··· /></form>結構,即表單(POST方式)— Input標簽(具有name屬性)Input標簽(submit類型)— 表單閉合的結構。
再如:對于需要被捕獲的表單數據,除了input標簽需要被包含在<form>中,還需滿足該<input>存在name屬性。例如<input name="username">,否則會因為沒有字段名而導致value被忽略。
總結
以上是生活随笔為你收集整理的钓鱼基础设施的应用分析(钓鱼邮件利用、Gophish)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++程序设计中的多态与虚函数知识点
- 下一篇: 面向对象的继承