FRP改造计划
AI 摘要(由 ChatGPT 总结生成):
本文介绍了对FRP(Fast Reverse Proxy)的改造计划,包括远程加载配置文件、配置文件自动删除、修改客户端使用Proxy的流量特征、TLS默认字节修改以及命令方式开启socks5代理等。改进使FRP更适用于红队代理场景,提高安全性和灵活性。详细改造步骤包括对源码的多处修改,如在`cmd/frpc/sub/root.go`中添加参数以实现配置文件自动删除,在`pkg/config/value.go`中实现远程加载配置文件功能,在`server/proxy/proxy.go`中修改客户端使用Proxy的流量特征,以及在`pkg/util/net/tls.go`中修改TLS默认字节。最后,通过命令方式开启socks5代理以避免配置文件加载请求。
前言该 IDEA 源于一次外网打入内网后做代理时的灵感,原版 FRP 只能本地加载配置文件,故联想到可不可以“魔改” FRP 使其远程加载配置文件以及实现其它操作,遂事后去搜索借鉴其它大佬的思路以及教程,对 FRP 进行改造,使其更加契合红队做代理时的场景。 本文是基于最新版 FRP 0.48.0 版本的改造。话不多说,下面教程开始!
改造教程基于项目源码修改,最好拥有简单的代码能力。
配置文件自动删除原版 FRPC 只能基于本地文件加载运行,若后续不手动删除配置文件,则配置文件会遗留在目标机器上,后续可能被别人拿来分析并获取你的公网 IP 等,更甚者直接溯源到你本人,故运行 FRPC 时顺手删除配置文件是必要的。 功能点在 cmd/frpc/sub/root.go 文件中修改。原理是在 init 中注册参数,然后判断参数是否开启,开启就删除。代码如下: - package sub
- import (
- ……
- )
- const (
- ……
- )
- var (
- cfgFile string
- ……
- bindPort int
- tlsEnable bool
-
- // 添加该行
- delEnable bool
- // END
- )
- func init() {
- rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")
- rootCmd.PersistentFlags().StringVarP(&cfgDir, "config_dir", "", "", "config directory, run one frpc service for each file in config directory")
- rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
- // 添加该行
- rootCmd.PersistentFlags().BoolVarP(&delEnable, "delini", "d", false, "enable auto delete frpc.ini (only local files)")
- // END
- }
- func RegisterCommonFlags(cmd *cobra.Command) {
- ……
- }
- var rootCmd = &cobra.Command{
- ……
- }
- func Execute() {
- ……
- }
- func handleSignal(svr *client.Service, doneCh chan struct{}) {
- ……
- }
- func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
- ……
- }
- func runClient(cfgFilePath string) error {
- ……
- }
- func startService(
- ……
- ) (err error) {
- log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
- ……
- svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
- if errRet != nil {
- err = errRet
- return
- }
- // 添加该行
- if delEnable == true {
- err := os.Remove(cfgFile)
- if err != nil {
- return err
- }
- }
- // END
- closedDoneCh := make(chan struct{})
- ……
- return
- }
复制代码使用方法:指定本地配置文件时多带一个 -d 或 --delini 参数就行,如下图:
远程加载配置文件使得配置文件可以从网络加载,此处可以将配置文件直接写入到 FRPC 源码中,但这样不利于后期修改,故舍弃该方式,加入从网络加载方式。 功能点在 pkg/config/value.go 文件中修改。指定配置文件的参数若是 http 请求,则对其进行请求加载,否则,从对应指定路径加载。此处只需将 GetRenderedConfFromFile 函数功能修改即可,修改为如下:
- func GetRenderedConfFromFile(path string) (out []byte, err error) {
- var b []byte
- rawUrl := path
- if strings.Contains(rawUrl, "http") {
- log.Info("Remote load ini file")
- response, _err1 := http.Get(path)
- if _err1 != nil {
- return
- }
- defer response.Body.Close()
- body, _err := io.ReadAll(response.Body)
- if _err != nil {
- return
- }
- httpContent := string(body)
- var content = []byte(httpContent)
- out, err = RenderContent(content)
- return
- } else {
- log.Info("Local load ini file")
- b, err = os.ReadFile(path)
- if err != nil {
- return
- }
- localContent := string(b)
- var content = []byte(localContent)
- out, err = RenderContent(content)
- return
- }
- }
复制代码 功能演示如下图:
修改客户端使用Proxy的流量特征正常情况下建议 FRP 服务端开启 tls_only = true 选项,同时 FRP 客户端配置文件中指定 tls_enable = true 选项以加密传输的流量,这样可以避免暴露你的服务器 IP 或本地 IP ,如果不想开启 TLS 选项的话,又不想暴露相关 IP ,那么可以对其进行如下修改以隐藏流量特征。 功能点在 server/proxy/proxy.go 文件中修改,将 GetWorkConnFromPool 函数内代码手动指定 IP 地址(IP 可随意修改),代码如下: - ……
- if src != nil {
- srcAddr, srcPortStr, _ = net.SplitHostPort(src.String())
- // 追加如下
- srcAddr = "127.0.0.1"
- srcPort, _ = strconv.Atoi(srcPortStr)
- }
- if dst != nil {
- dstAddr, dstPortStr, _ = net.SplitHostPort(dst.String())
- // 追加如下
- dstAddr = "127.0.0.1"
- dstPort, _ = strconv.Atoi(dstPortStr)
- }
- ……
复制代码上述问题建议在配置文件中启用 tls_enable 参数以规避。
TLS默认字节修改从 v0.25.0 版本开始 FRPC 和 FRPS 之间支持通过 TLS 协议加密传输,为了端口复用,FRP 建立 TLS 连接的第一个字节为 0x17 ,有些流量识别程序识别到特定流量,可能会阻拦,故我们可以对其进行修改。 功能点在 pkg/util/net/tls.go 文件中修改,如下: - ……
- // var FRPTLSHeadByte = 0x17
- // 修改为下方,可自定义其它
- var FRPTLSHeadByte = 0x88
- func CheckAndEnableTLSServerConnWithTimeout(
- c net.Conn, tlsConfig *tls.Config, tlsOnly bool, timeout time.Duration,
- ) (out net.Conn, isTLS bool, custom bool, err error) {
- sc, r := gnet.NewSharedConnSize(c, 2)
- buf := make([]byte, 1)
- var n int
- _ = c.SetReadDeadline(time.Now().Add(timeout))
- n, err = r.Read(buf)
- _ = c.SetReadDeadline(time.Time{})
- if err != nil {
- return
- }
- ……
复制代码该方式在新版本中可在客户端配置中加入 disable_custom_tls_first_byte = true 参数使得 TLS 不发送 0x17 。
命令方式开启socks5代理下述方法使用命令方式开启 socks5 代理,以避免配置文件加载请求。 修改源码文件 cmd/frpc/sub/tcp.go 并添加相关参数,如下: - package sub
- import (
- ……
- )
- func init() {
- ……
- //添加这几行
- tcpCmd.PersistentFlags().StringVarP(&pluginName, "plugin", "", "", "plugins")
- tcpCmd.PersistentFlags().StringVarP(&pluginUser, "plugin_user", "", "", "plugins user name")
- tcpCmd.PersistentFlags().StringVarP(&pluginPass, "plugin_pass", "", "", "plguins user password")
- //END
- rootCmd.AddCommand(tcpCmd)
- }
- var tcpCmd = &cobra.Command{
- ……
- cfg.UseEncryption = useEncryption
- cfg.UseCompression = useCompression
- //添加这几行
- cfg.Plugin = pluginName
- if cfg.Plugin != "" {
- cfg.PluginParams = make(map[string]string)
- cfg.PluginParams["plugin_user"] = pluginUser
- cfg.PluginParams["plugin_passwd"] = pluginPass
- }
- //END
- ……
- }
复制代码再修改 cmd/frpc/sub/root.go 文件,增加如下参数:
- var (
- cfgFile string
- ……
- bindPort int
- tlsEnable bool
- //添加这几行
- pluginName string
- pluginUser string
- pluginPass string
- //END
- )
复制代码最后使用方式如下:
- ./frpc tcp -s [IP]:[PORT] -r [远端端口] -t [Token] --plugin socks5 --plugin_user [鉴权用户] --plugin_pass [鉴权密码] -n [代理名称]
复制代码如图所示:
此方法建议在 Windows 下使用,Linux 使用可以看到完整命令参数。
参考
恢复正常了……FRP改造计划 - Is Yang's Blog (isisy.com)
|