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