3 Commits
Author SHA1 Message Date
shadow1ng 2c921c375b Merge pull request #608 from NOPTrace/fix/smtp-deadline
发布 / auto-tag (push) Canceled after 0s
测试构建 / 代码检查 (push) Canceled after 0s
发布 / release (push) Canceled after 0s
测试构建 / 单元测试和构建 (push) Canceled after 0s
测试构建 / 构建验证 (push) Canceled after 0s
fix: set conn deadline in smtp testAnonymousAccess/testOpenRelay to avoid hang on silent servers
2026-09-17 12:19:08 +08:00
NOPTrace 6bb8ccd490 fix: set conn deadline in smtp testAnonymousAccess/testOpenRelay to avoid hang on silent servers
testAnonymousAccess and testOpenRelay dial the target and immediately
hand the connection to smtp.NewClient without setting any deadline.
smtp.NewClient reads the 220 greeting as its first operation, so a
server that accepts TCP but never sends data (honeypot/tarpit) blocks
the goroutine forever. The outer select waits on resultChan or
ctx.Done(); with the default -gt 0 the context has no deadline, so
Scan never returns and RunScan's wg.Wait() freezes the whole process.

Set the same ModuleTimeout deadline used by the other functions in
this file (doSMTPAuth, testVRFYCommand, testEXPNCommand, getServerInfo).

Verified against a live accept-but-silent SMTP endpoint:
- unpatched: process hangs (31 goroutines, stack at smtp.go:276)
- patched: full plugin chain completes in ~92s, no regression on
  normally-responding servers (detection output identical)
2026-09-17 09:24:05 +08:00
ZacharyZcR 95cc12e753 Merge pull request #604 from shadow1ng/dev
发布 / auto-tag (push) Canceled after 0s
测试构建 / 代码检查 (push) Canceled after 0s
发布 / release (push) Canceled after 0s
测试构建 / 单元测试和构建 (push) Canceled after 0s
测试构建 / 构建验证 (push) Canceled after 0s
release: v2.2.1
2026-08-26 04:43:42 +08:00
6 changed files with 21 additions and 110 deletions
+6
View File
@@ -243,6 +243,9 @@ func (p *SMTPPlugin) testAnonymousAccess(ctx context.Context, info *common.HostI
}
defer func() { _ = conn.Close() }()
// 修复: 设置读写超时, 防止对只accept不发送banner的服务器(tarpit)永久阻塞
_ = conn.SetDeadline(time.Now().Add(session.Config.ModuleTimeout()))
client, err := smtp.NewClient(conn, info.Host)
if err != nil {
resultChan <- nil
@@ -295,6 +298,9 @@ func (p *SMTPPlugin) testOpenRelay(ctx context.Context, info *common.HostInfo, s
}
defer func() { _ = conn.Close() }()
// 修复: 设置读写超时, 防止对只accept不发送banner的服务器(tarpit)永久阻塞
_ = conn.SetDeadline(time.Now().Add(session.Config.ModuleTimeout()))
client, err := smtp.NewClient(conn, info.Host)
if err != nil {
resultChan <- nil
-10
View File
@@ -21,10 +21,6 @@ func registerMiscDeclarations() []*exprpb.Decl {
decls.NewOverload("tongda_date",
[]*exprpb.Type{},
decls.String)),
decls.NewFunction("timestamp_second",
decls.NewOverload("timestamp_second_zero",
[]*exprpb.Type{},
decls.Int)),
}
}
@@ -51,11 +47,5 @@ func registerMiscImplementations() []*functions.Overload {
return types.String(time.Now().Format("0601"))
},
},
{
Operator: "timestamp_second_zero",
Function: func(value ...ref.Val) ref.Val {
return types.Int(time.Now().Unix())
},
},
}
}
-17
View File
@@ -10,7 +10,6 @@ import (
"net/url"
"strings"
"testing"
"time"
"github.com/google/cel-go/common/types"
)
@@ -1394,19 +1393,3 @@ func TestMakeVarDecl(t *testing.T) {
})
}
}
func TestTimestampSecond(t *testing.T) {
before := time.Now().Unix()
result, err := Evaluate(GetBaseEnv(), "timestamp_second()", map[string]interface{}{})
if err != nil {
t.Fatal(err)
}
got, ok := result.Value().(int64)
if !ok {
t.Fatalf("timestamp_second() type = %T, want int64", result.Value())
}
after := time.Now().Unix()
if got < before || got > after {
t.Fatalf("timestamp_second() = %d, want [%d, %d]", got, before, after)
}
}
-38
View File
@@ -869,10 +869,6 @@ func cloneMap(tags map[string]string) map[string]string {
// evalset 执行CEL表达式并处理特殊类型结果
func evalset(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (string, error) {
if isPlainLiteral(expression, variableMap) {
variableMap[k] = expression
return expression, nil
}
out, err := Evaluate(env, expression, variableMap)
if err != nil {
variableMap[k] = ""
@@ -919,11 +915,6 @@ func isPlainLiteral(expr string, variableMap map[string]interface{}) bool {
if _, exists := variableMap[expr]; exists {
return false
}
// Base64/JWT 常量常包含 +、/、= 或 .,这些字符在 CEL 中也是语法符号。
// 先识别编码值,避免把密钥和令牌误当成表达式编译。
if isEncodedLiteral(expr) {
return true
}
// 含 CEL 语法特征的需要走 CEL 编译
for _, c := range expr {
switch c {
@@ -934,35 +925,6 @@ func isPlainLiteral(expr string, variableMap map[string]interface{}) bool {
return true
}
func isEncodedLiteral(value string) bool {
if strings.Count(value, ".") == 2 {
parts := strings.Split(value, ".")
for _, part := range parts {
if part == "" || strings.IndexFunc(part, func(r rune) bool {
return !isASCIIAlphaNumeric(r) && r != '-' && r != '_'
}) >= 0 {
return false
}
}
return true
}
if len(value) < 4 || len(value)%4 != 0 {
return false
}
padding := strings.TrimRight(value, "=")
if len(value)-len(padding) > 2 {
return false
}
return strings.IndexFunc(padding, func(r rune) bool {
return !isASCIIAlphaNumeric(r) && r != '+' && r != '/'
}) < 0
}
func isASCIIAlphaNumeric(r rune) bool {
return r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9'
}
// CheckInfoPoc 检查POC信息并返回别名
func CheckInfoPoc(infostr string) string {
for _, poc := range fingerprint.PocDatas {
+14 -44
View File
@@ -608,16 +608,16 @@ func stringMatrixEqual(a, b [][]string) bool {
func TestBuildVulnDetails(t *testing.T) {
tests := []struct {
name string
pocDef *Poc
vulName string
params StrMap
wantKeys []string
wantNoKeys []string
wantVulnType string
wantVulnName string
wantParamVal string
wantParamKey string
name string
pocDef *Poc
vulName string
params StrMap
wantKeys []string
wantNoKeys []string
wantVulnType string
wantVulnName string
wantParamVal string
wantParamKey string
}{
{
name: "最小Poc只有Name",
@@ -647,15 +647,15 @@ func TestBuildVulnDetails(t *testing.T) {
wantVulnName: "Full Vuln",
},
{
name: "有params则details含parameters字段",
pocDef: &Poc{Name: "poc-yaml-params"},
name: "有params则details含parameters字段",
pocDef: &Poc{Name: "poc-yaml-params"},
vulName: "Params Vuln",
params: StrMap{
{Key: "user", Value: "admin"},
{Key: "pass", Value: "123456"},
},
wantKeys: []string{"vulnerability_type", "vulnerability_name", "parameters"},
wantNoKeys: []string{"author"},
wantKeys: []string{"vulnerability_type", "vulnerability_name", "parameters"},
wantNoKeys: []string{"author"},
wantParamKey: "user",
wantParamVal: "admin",
},
@@ -865,33 +865,3 @@ func TestCollectVarDeclarations(t *testing.T) {
}
})
}
func TestEvalSetTreatsEncodedValuesAsLiterals(t *testing.T) {
env := GetBaseEnv()
tests := []string{
"fsHspZw/92PrS3XrPW+vxw==",
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyJ9.feetKmWoPnMkAebjkNnyuKo6c21_hzTgu0dfNqbdpZQ",
}
for _, value := range tests {
variables := map[string]interface{}{}
got, err := evalset(env, variables, "token", value)
if err != nil {
t.Fatalf("evalset(%q) error = %v", value, err)
}
if got != value || variables["token"] != value {
t.Fatalf("evalset(%q) = %q, stored %v", value, got, variables["token"])
}
}
}
func TestEvalSetStillEvaluatesExpressions(t *testing.T) {
variables := map[string]interface{}{}
got, err := evalset(GetBaseEnv(), variables, "token", "randomLowercase(6)")
if err != nil {
t.Fatal(err)
}
if len(got) != 6 {
t.Fatalf("randomLowercase result length = %d, want 6", len(got))
}
}
@@ -12,7 +12,7 @@ info:
created: 2025/06/11
set:
randstr: randomLowercase(6)
randstr: randLowercase(6)
rules:
r0:
request: