基于HTTP3的反向代理工具

一、HTTP3的相关知识

HTTP3是第三个主要版本的HTTP协议。与其前任HTTP1.1HTTP2不同,在HTTP3中,将弃用TCP协议,改为使用基于UDP协议的QUIC协议实现。 此变化主要为了解决HTTP/2中存在的队头阻塞问题。修改包括:

  • 在QUIC中,数据流由传输层本身提供,而在HTTP/2中,流在HTTP层完成
  • 由于数据流互相独立,如果仍使用HTTP/2中使用的头部压缩算法,会造成队头阻塞
  • QUIC流与HTTP/2略有不同

HTTP3与HTTP2的比较:

  • 两者都提供数据流
  • 两者都提供服务器推送
  • 两者都有头部压缩,QPACK与HPACK的设计非常类似
  • 两者都通过单一连接上的数据流提供复用
  • 两者都提供数据流的优先度设置
  • 得益于QUIC的0-RTT握手,HTTP/3可以提供更好的早期数据支持,而TCP快速打开和TLS通常只能传输更少的数据,且经常存在问题。
  • 得益于QUIC,HTTP/3的握手速度比TCP+TLS快得多
  • HTTP/3不存在明文的不安全版本。尽管在互联网上很少见,HTTP/2还是可以不配合HTTPS来实现和使用
  • 通过ALPN拓展,HTTP/2可以直接在TLS握手时进行协商。HTTP/3基于QUIC,所以需要凭借响应中的 Alt-Svc: 头部来向客户端宣告

​ QUIC一如既往是安全的,它没有明文版本,想要建立一个QUIC连接,就必须通过TLS 1.3来进行加密保证安全。如上文所说,加密可以避免协议僵化等拦截和特殊处理。这也使QUIC具有了Web用户所期望的所有HTTPS安全特性。

​ QUIC在加密协商前,只有很少的初始握手报文会以明文形式发送。

​ QUIC只需要0-RTT或1-RTT的握手,可以减少协商和建立新连接所需的时间。

​ 当然,上述都是网上搜索到的介绍。对于具体实现,可以先看一下有没有现成的工具。在GitHub找了一下,发现了https://github.com/esrrhs/spp 支持通过 quic 创建反向socks5代理。

1686100653488.png

后面发现frp也支持quic协议,不过frp功能太多了,就不打算动它了。

二、改造

​ 针对现有工具进行改造,避免重复造轮子。此次采用的项目为:https://github.com/esrrhs/spp 。该项目很多代码是位于作者的另外一个项目:https://github.com/esrrhs/gohome 。为了方便改造,建议把这个项目也下载到本地。

目的: 针对攻防场景。减小体积、简化操作。

  1. 分离服务端与客户端,这块其实很好做,因为原项目其实已经分开了。我们只需要分开单独编译即可。

    参考frp创建目录如下:

    .
    ├── cmd
    │   ├── client
    │   │   ├── main.go
    │   │   └── main.go.example
    │   └── server
    │   ├── main.go
    │   └── main.go.example

    其中,*.example 为初始模板,方便后续生成加密后的配置所用。

  2. 去除无用log,特别是客户端的,虽然原项目已经提供了参数可关闭日志,但是在保证程序可以正常运行的情况下,还是建议去除日志输出。

    去除默认的log输出,即能减小客户端的体积,又可以避免在控制台输出大量内容,影响后续的攻击流程(想象一下执行了代理程序后,疯狂在beacon中输出日志的场景)

    服务端日志示例:

    [root@VM-8-2-centos sppq]# ./spp
    [*] Listening on :8443
    [*] New Client: 10.0.8.2:8443<--quic-->1.1.1.1:56751
    [+] processLogin 10.0.8.2:8443<--quic-->1.1.1.1:56751 clienttype:REVERSE_SOCKS5 fromaddr:":27766" name:"windows10_20.0.22.188_10.37.132.102_169.254.241.98__1686293349020817000_0" key:"aOyItVTVJssgMhoo"

    客户端日志示例:

    ➜ ./spp
    [+] Port :13092

    这样,我们只需要知道服务端开启的socks5 端口是多少即可。

  3. 对基础配置进行加密,这一步其实主要是给分析增加一点点难度,因为如果被捕获了客户端,其中的配置很容易被发现的。

    对于配置,可以将需要加密的配置进行转字符串,然后通过动态key加密。参考如下:

    func main() {

    rand.Seed(time.Now().UnixNano())

    encryptData := `lJUrJPxSVsgRb7laXKBp0nQU1aT5wlmuhZtrdW5mHPU31VSBr8HMON/cUZgrk194lHIu4CzSzBcZIsfMKtRO1lgrY/hP7kQ8Vtb67lqTsxtBHEcY/v9MC96idAww3DhTFN7M3ICyGzIbzhyhYMFxnA==`
    decryptData := utils.Decrypt(encryptData, "aiLauyuPZVBDvaWX")
    var c Conf
    err := json.Unmarshal([]byte(decryptData), &c)
    if err != nil {
    fmt.Printf("[-] error: %s \n", err.Error())
    return
    }

    config := proxy.DefaultConfig()
    config.MaxTryCon = c.MaxTry
    config.Key = c.ConnectKey
    config.Encrypt = c.EncryptKey
    name := GetName()

    fromaddr := ":" + randPort()
    fmt.Printf("[+] Port %s\n", fromaddr)
    _, err = proxy.NewClient(config, "quic", c.Server, name, "reverse_socks5", "tcp", fromaddr, "")
    if err != nil {
    fmt.Printf("[-] Connect server error: %s\n", err.Error())
    return
    }
    for {
    time.Sleep(time.Second * 10)
    }
    }
  4. 对于工具的优化,还建议不同使用场景,进行不同的配置。同时结合GitHub,进行版本更新编译。

    # This is an example .goreleaser.yml file with some sensible defaults.
    # Make sure to check the documentation at https://goreleaser.com
    before:
    hooks:
    # You may remove this if you don't use go modules.
    - go mod tidy
    # you may remove this if you don't need go generate
    - go generate ./...
    # .goreleaser.yaml
    builds:
    - main: cmd/client/main.go
    id: "spp"
    binary: sppc
    goos:
    - linux
    - darwin
    - windows
    goarch:
    - amd64
    - 386
    env:
    - CGO_ENABLED=0
    - main: cmd/server/main.go
    id: "spp"
    binary: spps
    goos:
    - linux
    - darwin
    - windows
    goarch:
    - amd64
    - 386
    env:
    - CGO_ENABLED=0
    archives:
    - replacements:
    darwin: Darwin
    linux: Linux
    windows: Windows
    386: i386
    amd64: amd64
    name_template: "spp_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
    checksum:
    name_template: 'checksums.txt'
    snapshot:
    name_template: "{{ .Version }}-next"
    changelog:
    sort: asc
    filters:
    exclude:
    - '^docs:'
    - '^test:'

​ 综上所述,我们可以将工具改成如下使用流程:

  1. 首先运行go run general.go ,进行简单的配置。

1686296829433.png

  1. 通过git 上传到GitHub 并编译。命令参考如下:

    git add .

    git commit -m "提交信息"

    git push origin main

    git tag v0.0.1
  2. 服务端启动服务端程序,客户端直接运行./spp 即可。

参考