白嫖CDN,打造封不尽IP的代理池

2021-03-17 星期三


写在前面


接上次 论如何防溯源连接WebShell 中提到的思路,我们来看看怎么借用云函数实现一个HTTP动态代理池。

提前说明一下:实现代理池的方式有很多,不局限这一种




 先看效果 



挂上代理之后,每次 HTTP 请求,都会以不同的 IP 发送出去,并且出口IP均为 IDC 机房 IP。随便挑选其中一个 IP 去 TI 上查询,可以看到 IP 很干净,专治各种买了威胁情报服务的目标





 原理 


总结下来就是一句话:利用云厂商提供的云函数(函数计算)功能,将客户端的HTTP请求进行转发,由于云函数多出口的特性,让我们也变相拥有了代理池。



云函数


有关云函数的详细介绍可以直接去看各云厂商的产品文档。我们只提几个重点:


1. 云函数不需要服务器,也就是说你不需要去买 VPS。

2. 云函数只是云厂商用自己的服务器帮你运行你上传的代码片段, 执行某个单一的逻辑,可以简单理解为只帮你执行一个函数。

3. 云函数无法长驻,调用的时候创建,执行完之后立即就销毁,所以无法直接保存状态。也正是这一点,让我们无法代理像 SSH 这种需要长连接的服务,只能代理 HTTP(s) 这种无状态的协议。


以腾讯云云函数服务为例,工作原理里面提到了复用原始连接的情况,为代理 TCP 连接提供了可能,但是目前鉴于时间原因没有深入尝试。



设计


云函数不能直接调用,同时还需要创建一个 「触发器」来触发云函数,为了方便我们选择使用 「API 网关触发器」,好处是一个 HTTP 请求就可以触发,也不需要什么别的操作。


我们整个代理池的架构如下:



SCF-Main


 这个是我们云函数,我们需要布署到云上。负责解析结构化的数据,组装 HTTP 请求,发送之后把响应再组装好返回给 API 网关。


SCF-Client


 这是客户端程序,主要做 3 件事:

  • 提供 HTTP/HTTPS 代理服务

  • 把浏览器请求的数据包组装成我们自定义的格式,发送给 API 网关

  • 把 API 网关返回的数据解析后,组装成 HTTP 代理的响应,返回给浏览器



开发——云函数侧


云函数侧要做的事情很简单,只需要接到网关传过来的数据,解开之后自己拼出来一个 HTTP 请求,发送出去,然后把Response 信息再拼好,返回给 API 网关即可。


我们定义两个结构,分别是HTTP请求和响应:


// DefineEvent 请求结构type DefineEvent struct {  URL     string `json:"url"`  // 目标的 URL, eg: http://cip.cc/  Content string `json:"content"` // 最原始的 HTTP 报文, base64}// RespEvent 响应结构type RespEvent struct {  Status bool   `json:"status"` // 请求是否正常  Error  string `json:"error"`  // 错误信息  Data   string `json:"data"` // HTTP 响应原始报文, base64}


利用 http.ReadRequest 直接从原始报文创建 request 对象


req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(string(rawreq))))


接下来就是 dump 出 response 报文,组装成 API 网关返回的格式返回




至此云函数端就已经完成了


开发——SCF-Client 侧


首先按照一个正常的 http 代理服务去开发,处理浏览器和本地代理端口的数据包,这个没啥说的,大篇的文章可以找到,100行就能搞定。


常规的 HTTP 代理是把浏览器发过来的包直接就转发出去了,我们需要把这一块的逻辑重写一下。


把浏览器侧发过来的 HTTP 数据包,组装成 SCF-Main 请求格式,发送给 API 网关



拿到API网关响应包解析并处理后,转换成本地 HTTP 请求正确的响应包,返回给浏览器





 部署&使用步骤 


云函数部分


1. 打开 「函数服务(https://console.cloud.tencent.com/scf/list?rid=1&ns=default) 点击「新建




2. 按照图中步骤填写,不要手欠直接点完成


 基础配置




 高级配置




 触发器配置




3. 点击完成后,自动上传 zip 包并布署,同时会创建 API 网关,直接点「立即访问」




4. 获取云函数的 API 访问路径




Client 侧


scf-client 的参数如下:



➜  scf-client ./scf-client -hUsage of ./scf-client:  -api string // 必选项    API Gateway url, eg: https://service-xxxxx-1257105570.gz.apigw.tencentcs.com/release/helloworld  -l string    Listen Addr (default "0.0.0.0") // 代理监听的地址  -p int    HTTP Listen Port (default 3080) // HTTP 代理监听的端口  -password string    Auth Password, eg: --password 123456  // 如果你要开启认证的话,就请设置 user 和 password  -sp int    HTTPS Listen Port (default 3443)  -timeout int    Request Timeout (second) (default 60)  -user string    Auth User, eg: --user admin


运行 scf-client


./scf-client --api https://service-23e5sr7o-1257105570.gz.apigw.tencentcs.com/release/scf-proxy-main


看到开启 HTTP 代理成功之后,我们使用 curl 进行测试



# http 代理 访问 http 站点curl -x "http://localhost:3080" "http://cip.cc"# http 代理 访问 https 站点curl -x "http://localhost:3080" "https://httpbin.org/post?a=b" -X POST -d "user=hahaha" -k# https 代理 访问 http 站点curl -x "https://localhost:3443" "http://cip.cc" --proxy-insecure# https 代理 访问 https 站点curl -x "https://localhost:3443" "https://httpbin.org/post?a=b" -X POST -d "user=hahaha" -k --proxy-insecure




Debug


开发过程中,难免少不了 Debug,你只需要在云函数侧用 Print 把需要的数据打印出来就行了,然后在云函数的日志里,就可以看到你 Print 的数据。


我们把 response 的数据打印了出来,可以看到响应包的 base64 数据



base64 解开之后就是最终响应的内容了




有关 HTTPS 的实现


我们知道 HTTPS 实际上第一个包是 CONNECT,打开 TCP 通道之后,先互传证书,之后再传 HTTP 报文。在云函数这个场景下,可以简单理解为一次 TCP 会话,TCP 握手之后, Client 只能向 Server 发一个 TCP 包,Server 也只能回一个包,之后就要断开连接。


这个问题也困扰了我很久,最终放弃了透传证书的方式,直接改用中间人(类似 Burpsuite 的 HTTPS 代理形式)


我们采用中间人的方式,在 SCF-Client 里内置了一个根证书,和浏览器交换证书的过程都在本地实现,只把最核心的 HTTP 报文发给 API 网关。




 测试 


HTTP 代理和 HTTPS 代理均可以对 HTTPS 站点代理


HTTP 代理



HTTPS 代理





 最后 


优点


  • 自动切换出口IP,对使用者完全透明,不需要软件去实现代理切换的功能

  • 例如你挂 sqlmap 去注入,可以保证每一个数据包都走不同的IP出去,而命令行参数上只需要填写一个 proxy 地址

  • IP 多如牛毛,封不完的IP,打不死的小强

  • 该方法的 IP 都是白 IP,不在微步在线等威胁情报平台的黑名单里

  • 便宜,便宜,便宜, 每个账号每个月有免费额度,拉上你的同事朋友把多个账号凑一起,然后拿 nginx 做个负载均衡,能把白嫖嫖到底


缺点


  • 仅支持 HTTP 和 HTTPS,这种无状态的协议。不支持其它协议,如果后期改改,勉强能适配一下 redis 协议

  • 速度上来说,可能有时候会慢一些



工具


本文只谈思路,暂时不考虑提供现成工具,有兴趣的同学可以自己参照设计思路去实现,反正没几行代码。





不如关注一波再走?



原文地址:点击此处查看原文