FRP 因其功能丰富,红队攻击者常将其用在内网渗透中,但是 FRP 在实际红蓝对抗中存在很多特征,导致被蓝队发现并溯源。
下面通过对 FRP 0.45.0 版本进行分析,提取特征并进行规避。
客户端执行流程分析
客户端入口:cmd/frpc/main.go
命令行参数处理使用的是 cobra 库,初始化,其中cfgFile默认值是./frpc.ini
rootCmd,判断cfgDir,进入runClient(cfgFile)
加载配置文件
解析配置文件,读取配置文件内容给content,UnmarshalClientConfFromIni进行第一次解析赋值给cfg、content备份的buffer给LoadAllProxyConfsFromIni进行第二次解析
UnmarshalClientConfFromIni主要解析ini文件common项
LoadAllProxyConfsFromIni主要解析ini文件所有的proxy代理项
解析完所有的ini文件配置,开始真正启动服务 startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
跟进startService,NewService建立服务,svr.Run开始运行
Run()函数内svr.login()尝试登陆到frps服务端,返回conn和session
login()函数,首先检测TLSEnable是否开启,后面开始建立TCP连接,用建立好的连接,发了个0x17的字符,代表等下要建立tls加密传输
最后发送登录信息给服务端
流量特征修改
基础通信分析
客户端frpc common基础配置
[common]
server_addr = 8.219.248.226
server_port = 2086
[plugin_socks5]
type = tcp
remote_port = 7070
plugin = socks5
plugin_user = admin
plugin_passwd = 123456
frpc启动时,如果客户端[common]
未指定协议protocol
,则默认使用TCP协议
TCP三次握手建立连接后,开始向服务端发送认证信息
o{"version":"0.45.0","os":"darwin","arch":"arm64","privilege_key":"cacaf2489774a4c69abe80dc57ac5486","timestamp":1671416534,"pool_count":1}
服务端frps接收客户端认证信息,并启动代理插件监听
服务端也会通过TCP协议返回一些信息 version、run_id
10{"version":"0.45.0","run_id":"0fa0b38623bbd1bc"}
客户端显示认证成功的信息和代理情况
在其他主机上配置代理测试访问,当frps收到代理转发请求后,frps服务端会将代理信息(代理插件名、地址、端口、认证等)及代理转发请求数据传送给frpc客户端
基础流量特征
上面客户端连接认证及代理转发过程分析,通过在frpc客户端抓包分析,其中流量特征主要包括两点
- 客户端连接认证时,双方三次握手建立TCP通信后传递的一些特定格式的登陆信息
- 客户端代理转发请求时,收到服务端传递的一些特定格式的代理信息
上述特征清理,可开启TLS加密TCP通信内容。
从 v0.25.0 版本开始,frpc 和 frps 之间支持通过 TLS 协议加密传输。通过在 frpc.ini 的 common 中配置 tls_enable = true
来启用此功能。
[common]
server_addr = 8.219.248.226
server_port = 2086
tls_enbale = true
[plugin_socks5]
type = tcp
remote_port = 7070
plugin = socks5
plugin_user = admin
plugin_passwd = 123456
重新启动frpc客户端,此时双方建立连接后的通信TCP数据流内容已加密
测试frpc客户端代理转发服务端代理请求情况,可看到TCP数据流内容已加密
除了使用tls_enable = true
外,还可继续使用use_encryption = true
、use_compression = true
进行二次处理。
TLS特征
当开启TLS加密通信内容时,由于 frp 为了端口复用,建立 TLS 连接的第一个字节默认为 0x17,导致开启TLS流量特征明显。
客户端建立TLS发起第一个数据包Client Hello
之前,默认会发送一个一字节大小的TCP通信数据
修改TLS默认字节,定位源码pkg/util/net/tls.go
、pkg/util/net/dial.go
,修改如下5处
测试效果,默认一字节0x17
变为自定义五字节0x11 0x23 0x66 0x46 0x15
配置文件优化
frpc客户端启动需要指定frpc.ini配置文件,默认frpc配置文件为frpc.ini,同时frpc启动后frpc.ini会保留在目标机子上。
那么,针对frpc配置文件的优化主要包含两点
- 配置文件的文件名及后缀可自定义
- frpc运行自删除配置文件
针对frpc运行自删除配置文件,在cmd/frpc/sub/root.go里面进行修改,第一步,在init()函数中添加frpc启动参数
rootCmd.PersistentFlags().BoolVarP(&delCfgFile, "delete", "d", false, "delete config file of frpc")
第二步,在startService()函数中添加判断delCfgFile参数是否开启,如果开启则删除frpc配置文件
if delCfgFile {
errs := os.Remove(cfgFile)
if errs != nil {
log.Warn("delete config file of frpc fail\n")
} else {
log.Info("delete config file of frpc success\n")
}
}
效果
CDN域前置
frpc域前置CDN一般结合websocket协议使用,自frp0.21.0及之后支持websocket协议(仅支持ws协议,不支持wss协议)。
WebSocket
测试域前置,客户端frpc common配置
[common]
server_addr = 101.42.233.208
server_port = 2086
protocol = websocket
[plugin_socks5]
type = tcp
remote_port = 7070
plugin = socks5
plugin_user = admin
plugin_passwd = 123456
frpc启动时,如果客户端协议为websocket则会通过先发送http请求进行ws协议切换
然后通过websocket协议向服务端发送认证信息
o\000\000\000\000\000\000\000�{"version":"0.45.0","os":"darwin","arch":"arm64","privilege_key":"aa879aa8dbec993816aa42a498c19069","timestamp":1669782153,"pool_count":1}
wireshark字符串搜索Display filter
可以搜索Websocket内的字符串,搜索语句为 data-text-lines contains "darwin"
服务端frps接收客户端认证信息
服务端也会通过ws协议返回一些信息 version、run_id
1\000\000\000\000\000\000\0000{"version":"0.45.0","run_id":"a7bca8b1e7386bdb"}
客户端显示认证成功的信息
上面客户端连接认证过程分析,通过在frpc客户端抓包分析,其中流量特征主要包括两点
- 客户端连接认证服务端时ws协议切换发起的特定http uri请求(/~!frp)
- 双方建立WebsSocket通信后传递的一些特定格式的登陆信息
第一处特征清理,修改frp建立WebSocket时请求的路径
修改常量FrpWebsocketPath,重新编译
第二处特征清理,开启TLS加密WebSocket通信内容
网上有人改版frp websocket适配CDN,新增配置 websocket_domain 为了满足在国内云厂商CDN上抢注的可信域名(server_addr=api.baidu.com.dnsv1cdn.cn、websocket_domain=api.baidu.com,websocket_domain为Http包内的Host头),目前国内云厂商都需要验证域名所属和备案已无法抢注。
WebSocket Secure
Web Socket 使用 TLS 即可实现等同 Web Socket Secure 效果。