二维码-邮件钓鱼历险记

admin 2024年1月24日10:30:00评论18 views字数 6978阅读23分15秒阅读模式

临近年末,公司又要举办“钓鱼演练”,之前钓鱼是以<a>标签链接的方式取诱导点击,想着员工对于“链接”多少有防备心了,故这次想换个花样。恰好朋友Y君不久前用了二维码钓鱼效果不错,索性就换成二维码吧!

钓鱼过程使用到的工具:

  1. gophish

  2. ewomail

有关工具搭建请见此处:

https://mp.weixin.qq.com/s?__biz=Mzg2MDYxOTQ1Mw==&mid=2247483923&idx=1&sn=02224bc45acc7a3d66c5d06d45a845dc&chksm=ce22d5c1f9555cd7ffc76c86476dc060e80e29e5663fbac1a6c8a46c5f1acde05bce207e8cce&token=914578584&lang=zh_CN#rd

下面正文开始:

由于gophish功能中不直接支持使用二维码钓鱼,所以有了下面的折腾:

  1. 首先想到的思路是将gophish生成的钓鱼URL链接,用在线二维码生成地址制作一个二维码,放入到邮件正文内发送

在线二维码生成器地址推荐:

https://www.hlcode.cn/url

但这样存在一个问题:二维码写死了,多人扫描同一二维码,人数少还好,如果是上百人的话,统计钓鱼数据会变得很困难,且数据展示也不方便.

2.故我想是否可以通过前端JS代码的方式,加载钓鱼URL链接后动态的请求在线二维码地址,生成二维码加载进邮件正文

<img id="qrCodeImage" alt="QR Code Image"><!-- 页面加载完成时执行 generateQRCode() -->    <script>        document.addEventListener("DOMContentLoaded", function() {            generateQRCode();        });        async function generateQRCode() {            // 要生成二维码的网址(gophish的URL钓鱼链接)            const contentUrl = "http://example.com?rid=E1MDBFl";            // 构建生成二维码的URL            const apiUrl = `http://qrcode.hlcode.cn/beautify/style/create?bgColor=%23FFFFFF&bodyType=1&content=${encodeURIComponent(contentUrl)}&down=0&embedPosition=0&embedText=&embedTextColor=%23000000&embedTextSize=38&eyeInColor=%23000000&eyeOutColor=%23000000&eyeType=8&eyeUseFore=1&fontFamily=0&foreColor=%23000000&foreColorImage=&foreColorTwo=&foreType=0&frameColor=&gradientWay=0&level=H&logoShadow=0&logoShap=2&logoUrl=&margin=2&rotate=30&size=400&format=1&qrCodeId=0`;            try {                // 发送GET请求                const response = await fetch(apiUrl);                // 获取<img>元素                const qrCodeImage = document.getElementById("qrCodeImage");                // 设置<img>元素的src属性为获取到的二维码图片URL                qrCodeImage.src = await response.blob();                console.log("QR Code generated and displayed.");            } catch (error) {                console.error("An error occurred while processing the request:", error);            }        }</script>

实验很完美,本地成功加载了请求的二维码图片,but。。。

实际测试,邮箱内空无一物?

二维码-邮件钓鱼历险记

百思不得其解后,谷歌告诉了答案:

为了安全性,所有的邮件无法执行JS脚本

3.看来只能从后端下手了,gophish使用的go语言,还好之前写过go的小工具嘿嘿,看了官方文档,模板引用如下:

二维码-邮件钓鱼历险记

这列模板引用的作用,就是可以在<钓鱼模板>或<钓鱼界面>内直接使用,gophish自动渲染解析,举个例子:二维码-邮件钓鱼历险记

邮件发出后,此处的{{.URL}}会被渲染为gophish生成的钓鱼链接

so,既然没有二维码功能,那我们自己加一个吧,在结构体内添加EURL字段(二维码)

搜索URL找到有关模板功能的文件template_context.go:

二维码-邮件钓鱼历险记

可以看到这里定义上述模板引用字段的结构体,下面的

NewPhishingTemplateContext函数返回了该结构体对象

ps:还有一个坑点是outlook默认不加载远程图片,所以干脆在程序内base64编码写入邮件正文了。

修改后该文件代码如下:

package modelsimport (  "bytes"  "strings"  "net/mail"  "net/url"  "fmt"  "path"  "net/http"  "encoding/json"  "text/template"  "encoding/base64"  "io/ioutil")type ApiResponse struct {  Code int    `json:"code"`  Data string `json:"data"`  Msg  string `json:"msg"`}// TemplateContext is an interface that allows both campaigns and email// requests to have a PhishingTemplateContext generated for them.type TemplateContext interface {  getFromAddress() string  getBaseURL() string}// PhishingTemplateContext is the context that is sent to any template, such// as the email or landing page content.type PhishingTemplateContext struct {  From        string  URL         string  Tracker     string  TrackingURL string  EURL  string  RId         string  BaseURL     string  BaseRecipient}// NewPhishingTemplateContext returns a populated PhishingTemplateContext,// parsing the correct fields from the provided TemplateContext and recipient.func NewPhishingTemplateContext(ctx TemplateContext, r BaseRecipient, rid string) (PhishingTemplateContext, error) {  f, err := mail.ParseAddress(ctx.getFromAddress())  if err != nil {    return PhishingTemplateContext{}, err  }  fn := f.Name  if fn == "" {    fn = f.Address  }  templateURL, err := ExecuteTemplate(ctx.getBaseURL(), r)  if err != nil {    return PhishingTemplateContext{}, err  }  // For the base URL, we'll reset the the path and the query  // This will create a URL in the form of http://example.com  baseURL, err := url.Parse(templateURL)  if err != nil {    return PhishingTemplateContext{}, err  }  baseURL.Path = ""  baseURL.RawQuery = ""  phishURL, _ := url.Parse(templateURL)  q := phishURL.Query()  q.Set(RecipientParameter, rid)  phishURL.RawQuery = q.Encode()  trackingURL, _ := url.Parse(templateURL)  trackingURL.Path = path.Join(trackingURL.Path, "/track")  trackingURL.RawQuery = q.Encode()  //这里x.x.x.x换成和钓鱼邮箱域名绑定的ip地址  ipMap := map[string]string{    "x.x.x.x": "www.example.com",  }  // 调用替换函数  newURL := replaceIPWithDomain(phishURL.String(), ipMap)    apiURL := "http://qrcode.hlcode.cn/beautify/style/create?bgColor=%23FFFFFF&bodyType=1&content=" + newURL + "&down=0&embedPosition=0&embedText=&embedTextColor=%23000000&embedTextSize=38&eyeInColor=%23000000&eyeOutColor=%23000000&eyeType=8&eyeUseFore=1&fontFamily=0&foreColor=%23000000&foreColorImage=&foreColorTwo=&foreType=0&frameColor=&gradientWay=0&level=H&logoShadow=0&logoShap=2&logoUrl=&margin=2&rotate=30&size=400&format=1&qrCodeId=0"  imageURL, err := GetQRCodeImageURL(apiURL)  base64DataURL, err := convertImageToBase64(imageURL)  return PhishingTemplateContext{    BaseRecipient: r,    BaseURL:       baseURL.String(),    URL:           phishURL.String(),    TrackingURL:   trackingURL.String(),    EURL:  base64DataURL,    Tracker:       "<img alt='' style='display: none' src='" + trackingURL.String() + "'/>",    From:          fn,    RId:           rid,  }, nil}func convertImageToBase64(url string) (string, error) {  // 获取远程图片的内容  imageContent, err := fetchRemoteImage(url)  if err != nil {    return "", err  }  // 将图片内容转换为Base64编码  base64Image := base64.StdEncoding.EncodeToString(imageContent)  // 构造Base64格式的图片URL  base64DataURL := "data:image/png;base64," + base64Image  return base64DataURL, nil}func fetchRemoteImage(url string) ([]byte, error) {  // 发起HTTP请求获取远程图片内容  response, err := http.Get(url)  if err != nil {    return nil, err  }  defer response.Body.Close()  // 读取图片内容  imageContent, err := ioutil.ReadAll(response.Body)  if err != nil {    return nil, err  }  return imageContent, nil}func replaceIPWithDomain(url string, ipMap map[string]string) string {  for ip, domain := range ipMap {    // 将IP地址替换为域名    url = strings.Replace(url, ip, domain, -1)  }  return url}func GetQRCodeImageURL(apiURL string) (string, error) {  // 发送GET请求  response, err := http.Get(apiURL)  if err != nil {    return "", fmt.Errorf("Error making GET request: %v", err)  }  defer response.Body.Close()  // 检查响应是否成功 (状态码为 200-299)  if response.StatusCode < 200 || response.StatusCode >= 300 {    return "", fmt.Errorf("Error: Unexpected status code %s", response.Status)  }  // 解析JSON响应  var apiResponse ApiResponse  err = json.NewDecoder(response.Body).Decode(&apiResponse)  if err != nil {    return "", fmt.Errorf("Error decoding JSON: %v", err)  }  // 检查API响应中的错误码  if apiResponse.Code != 0 {    return "", fmt.Errorf("API returned an error: %s", apiResponse.Msg)  }  // 返回图片地址  return apiResponse.Data, nil}// ExecuteTemplate creates a templated string based on the provided// template body and data.func ExecuteTemplate(text string, data interface{}) (string, error) {  buff := bytes.Buffer{}  tmpl, err := template.New("template").Parse(text)  if err != nil {    return buff.String(), err  }  err = tmpl.Execute(&buff, data)  return buff.String(), err}// ValidationContext is used for validating templates and pagestype ValidationContext struct {  FromAddress string  BaseURL     string}func (vc ValidationContext) getFromAddress() string {  return vc.FromAddress}func (vc ValidationContext) getBaseURL() string {  return vc.BaseURL}// ValidateTemplate ensures that the provided text in the page or template// uses the supported template variables correctly.func ValidateTemplate(text string) error {  vc := ValidationContext{    FromAddress: "[email protected]",    BaseURL:     "http://example.com",  }  td := Result{    BaseRecipient: BaseRecipient{      Email:     "[email protected]",      FirstName: "Foo",      LastName:  "Bar",      Position:  "Test",    },    RId: "123456",  }  ptx, err := NewPhishingTemplateContext(vc, td.BaseRecipient, td.RId)  if err != nil {    return err  }  _, err = ExecuteTemplate(text, ptx)  if err != nil {    return err  }  return nil

重新打包编译,运行

go build./gophish

完美!测试结果如下:

客户端、网页端均可正常显示二维码,每人一码~

二维码-邮件钓鱼历险记

二维码-邮件钓鱼历险记

原文始发于微信公众号(哈拉少安全小队):二维码-邮件钓鱼历险记

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月24日10:30:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   二维码-邮件钓鱼历险记https://cn-sec.com/archives/2423949.html

发表评论

匿名网友 填写信息