fix: verify listener ownership before accepting task file uploads

This commit is contained in:
Ed1s0nZ committed 2026-10-06 03:29:20 +08:00
1 parent 062fd30cd7
commit d9a111ff50
4 files changed
+88 -4

No files matched your search

+4
View File
@@ -144,3 +144,7 @@
### 单资产有限核验范围和写回提示(表格第 48 行)
默认单 Agent 系统提示中的持续深挖、穷尽攻击面和赏金门槛要求,与只读连通性/指纹任务完成条件冲突。现以本轮目标限制执行深度,403/认证失败等按核验目标如实报告,达到目标后结束,仍保留明确全面评估任务的验证要求。自定义资产模板此前替换掉默认模板中 complete_asset_scan 指令,现保留用户模板内容并追加完成后的现有记录动作,不增加探测范围;已有写回指令不重复。新增前端测试验证限定核验模板、资产 ID、写回提示及无额外漏洞/弱口令探测提示。Agent 包测试、资产工具和数据库测试通过,后者覆盖实际 complete_asset_scan 回写时间、会话及漏洞数量。未运行真实模型探测,提示修复不能证明所有模型均会及时结束;历史长时间行为仍保留验证限制。
### HTTP 文件上传归属检查(表格第 36 行扩展路径)
HTTP 上传端点此前只验证共享监听器凭据与任务 ID 路径,未校验任务会话归属。现上传前查询任务及其会话,拒绝其他监听器任务。回归测试验证外部监听器不能覆盖已有文件、所属监听器正常上传可用;原路径安全测试增加真实任务/会话夹具。C2 包测试通过。文件下载端点使用独立 file_id,不可直接当 task_id 校验,未以错误映射限制下载;同监听器会话隔离及下载文件归属继续核对。
+67
View File
@@ -0,0 +1,67 @@
package c2
import (
"cyberstrike-ai/internal/database"
"go.uber.org/zap"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
)
func TestHTTPUploadEnforcesListenerOwnership(t *testing.T) {
manager, db := terminalTestManager(t)
task := &database.C2Task{ID: "test-only-file-task", SessionID: "session", TaskType: "download", Status: "sent", CreatedAt: time.Now()}
if err := db.CreateC2Task(task); err != nil {
t.Fatal(err)
}
key, err := GenerateAESKey()
if err != nil {
t.Fatal(err)
}
body, err := EncryptAESGCM(key, []byte("test-only-upload"))
if err != nil {
t.Fatal(err)
}
dir, path, err := uploadPathForTask(manager.StorageDir(), task.ID)
if err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(dir, 0755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte("original"), 0600); err != nil {
t.Fatal(err)
}
listener := &HTTPBeaconListener{rec: &database.C2Listener{ID: "foreign-listener", EncryptionKey: key, ImplantToken: "test-only-token"}, manager: manager, cfg: &ListenerConfig{BeaconFilePath: "/file/"}, logger: zap.NewNop()}
for _, owned := range []bool{false, true} {
if owned {
listener.rec.ID = "listener"
}
upload := httptest.NewRequest(http.MethodPost, "/upload?task_id="+task.ID, strings.NewReader(body))
upload.Header.Set("X-Implant-Token", "test-only-token")
rr := httptest.NewRecorder()
listener.handleUpload(rr, upload)
expected := http.StatusNotFound
if owned {
expected = http.StatusOK
}
if rr.Code != expected {
t.Fatalf("upload owned=%v: status=%d", owned, rr.Code)
}
saved, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
expectedContent := "original"
if owned {
expectedContent = "test-only-upload"
}
if string(saved) != expectedContent {
t.Fatalf("unexpected file mutation owned=%v: %q", owned, saved)
}
}
}
+10 -1
View File
@@ -324,7 +324,7 @@ func (l *HTTPBeaconListener) handleUpload(w http.ResponseWriter, r *http.Request
return
}
taskID := r.URL.Query().Get("task_id")
if taskID == "" {
if taskID == "" || !l.ownsTask(taskID) {
l.disguisedReject(w)
return
}
@@ -397,6 +397,15 @@ func (l *HTTPBeaconListener) handleFileServe(w http.ResponseWriter, r *http.Requ
// 鉴权 / 输出辅助
// ----------------------------------------------------------------------------
func (l *HTTPBeaconListener) ownsTask(taskID string) bool {
task, err := l.manager.DB().GetC2Task(taskID)
if err != nil || task == nil {
return false
}
session, err := l.manager.DB().GetC2Session(task.SessionID)
return err == nil && session != nil && session.ListenerID == l.rec.ID
}
// checkImplantToken 校验 X-Implant-Token header(恒定时间比较防止时序攻击)
func (l *HTTPBeaconListener) checkImplantToken(r *http.Request) bool {
got := r.Header.Get("X-Implant-Token")
+7 -3
View File
@@ -230,8 +230,11 @@ func TestHTTPBeaconListener_HandleFileServe(t *testing.T) {
}
func TestHTTPBeaconListener_HandleUploadConfinesTaskID(t *testing.T) {
tmp := t.TempDir()
store := filepath.Join(tmp, "c2store")
m, db := terminalTestManager(t)
store := m.StorageDir()
if err := db.CreateC2Task(&database.C2Task{ID: "t_safe123", SessionID: "session", TaskType: "download", Status: "sent", CreatedAt: time.Now()}); err != nil {
t.Fatal(err)
}
keyB64, err := GenerateAESKey()
if err != nil {
t.Fatal(err)
@@ -239,10 +242,11 @@ func TestHTTPBeaconListener_HandleUploadConfinesTaskID(t *testing.T) {
token := "test-implant-token-upload"
l := &HTTPBeaconListener{
rec: &database.C2Listener{
ID: "listener",
EncryptionKey: keyB64,
ImplantToken: token,
},
manager: NewManager(nil, zap.NewNop(), store),
manager: m,
logger: zap.NewNop(),
}