fix: validate callback hosts before generating payloads

This commit is contained in:
Ed1s0nZ committed 2026-10-06 02:57:09 +08:00
1 parent 7d6d3e77dc
commit fa4e5ef4cb
8 files changed
+119 -9

No files matched your search

+6 -1
View File
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"path/filepath"
@@ -748,11 +749,15 @@ func (h *C2Handler) PayloadOneliner(c *gin.Context) {
return
}
scheme := "http"
if listener.Type == "https_beacon" {
scheme = "https"
}
input := c2.OnelinerInput{
Kind: kind,
Host: host,
Port: listener.BindPort,
HTTPBaseURL: fmt.Sprintf("http://%s:%d", host, listener.BindPort),
HTTPBaseURL: fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(host, strconv.Itoa(listener.BindPort))),
ImplantToken: listener.ImplantToken,
}
@@ -11,6 +11,7 @@ import (
"cyberstrike-ai/internal/c2"
"cyberstrike-ai/internal/database"
"cyberstrike-ai/internal/security"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
@@ -53,3 +54,43 @@ func TestC2ListenerEditRejectsInvalidFieldsWithoutSaving(t *testing.T) {
}
}
}
func TestC2OnelinerRejectsInvalidCallbackAndUsesListenerScheme(t *testing.T) {
db, err := database.NewDB(filepath.Join(t.TempDir(), "callback.db"), zap.NewNop())
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = db.Close() })
listener := &database.C2Listener{ID: "https", Name: "test", Type: "https_beacon", BindHost: "127.0.0.1", BindPort: 8443, ImplantToken: "test-only-token", CreatedAt: time.Now()}
if err := db.CreateC2Listener(listener); err != nil {
t.Fatal(err)
}
h := NewC2Handler(c2.NewManager(db, zap.NewNop(), t.TempDir()), zap.NewNop())
r := gin.New()
r.Use(func(c *gin.Context) {
c.Set(security.ContextSessionKey, security.Session{UserID: "test-owner", Scope: database.RBACScopeAll})
})
r.POST("/oneliner", h.PayloadOneliner)
for _, host := range []string{"bad host/abc", "https://example.com", "example.com:8443", "example.com", "::1"} {
req := httptest.NewRequest(http.MethodPost, "/oneliner", bytes.NewBufferString(fmt.Sprintf(`{"listener_id":"https","kind":"curl_beacon","host":%q}`, host)))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
valid := host == "example.com" || host == "::1"
if !valid && w.Code != 400 {
t.Fatalf("invalid callback status: %d", w.Code)
}
if valid {
if w.Code != 200 {
t.Fatalf("valid callback status: %d", w.Code)
}
expected := "https://example.com:8443"
if host == "::1" {
expected = "https://[::1]:8443"
}
if !bytes.Contains(w.Body.Bytes(), []byte(expected)) {
t.Fatalf("scheme or IPv6 formatting lost")
}
}
}
}