perf: CVE-2026-24061 检测改并发执行,消除硬 sleep 用 deadline 替代

This commit is contained in:
ZacharyZcR
2026-04-28 11:06:22 +08:00
parent b2caf5e118
commit 0157e523ae
+72 -53
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"net"
"strings"
"sync"
"time"
"github.com/shadow1ng/fscan/common"
@@ -84,21 +85,9 @@ func (p *TelnetPlugin) Scan(ctx context.Context, info *common.HostInfo, session
creds[i] = Credential{Username: c.Username, Password: c.Password}
}
// CVE-2026-24061: 使用配置的用户列表检测 Telnetd Authentication Bypass 漏洞
cveUsers := config.Credentials.Userdict["telnet"]
if len(cveUsers) == 0 {
cveUsers = []string{"root", "admin", "administrator"}
}
for _, user := range cveUsers {
if vuln, cveUser, evidence := p.checkCVE202624061(ctx, info, session, user); vuln {
common.LogVuln(i18n.Tr("telnet_cve202624061", target, cveUser, evidence))
return &ScanResult{
Success: true,
Type: plugins.ResultTypeVuln,
Service: "telnet",
Banner: fmt.Sprintf("CVE-2026-24061 Telnetd Authentication Bypass (user: %s)", cveUser),
}
}
// CVE-2026-24061: 并发检测 Telnetd Authentication Bypass 漏洞
if cveResult := p.checkCVE202624061Concurrent(ctx, info, session, config); cveResult != nil {
return cveResult
}
// 使用公共框架进行并发凭据测试
@@ -769,6 +758,57 @@ func (p *TelnetPlugin) drainBuffer(conn net.Conn) {
}
}
// checkCVE202624061Concurrent 并发检测多个用户,首个命中即返回
func (p *TelnetPlugin) checkCVE202624061Concurrent(ctx context.Context, info *common.HostInfo, session *common.ScanSession, config *common.Config) *ScanResult {
cveUsers := config.Credentials.Userdict["telnet"]
if len(cveUsers) == 0 {
cveUsers = []string{"root", "admin", "administrator"}
}
type cveHit struct {
user string
evidence string
}
ctx, cancel := context.WithCancel(ctx)
defer cancel()
ch := make(chan cveHit, 1)
var wg sync.WaitGroup
for _, user := range cveUsers {
wg.Add(1)
go func(u string) {
defer wg.Done()
if vuln, cveUser, evidence := p.checkCVE202624061(ctx, info, session, u); vuln {
select {
case ch <- cveHit{user: cveUser, evidence: evidence}:
cancel() // 通知其他 goroutine 停止
default:
}
}
}(user)
}
// 等待全部完成后关闭 channel
go func() {
wg.Wait()
close(ch)
}()
if hit, ok := <-ch; ok {
target := info.Target()
common.LogVuln(i18n.Tr("telnet_cve202624061", target, hit.user, hit.evidence))
return &ScanResult{
Success: true,
Type: plugins.ResultTypeVuln,
Service: "telnet",
Banner: fmt.Sprintf("CVE-2026-24061 Telnetd Authentication Bypass (user: %s)", hit.user),
}
}
return nil
}
// checkCVE202624061 检测 CVE-2026-24061 Telnetd Authentication Bypass 漏洞
// 利用 NEW-ENVIRON (option 39) 子协商注入恶意环境变量,实现认证绕过
// 返回 (是否漏洞, 触发用户名, 证据)
@@ -895,17 +935,19 @@ func (e *cveChecker) parseIAC(data []byte) []byte {
return output
}
// readAll 读取连接中的所有可用数据
func (e *cveChecker) readAll() []byte {
// readAll 读取连接中的所有可用数据deadline 控制等待上限
func (e *cveChecker) readAll(timeout time.Duration) []byte {
var out []byte
_ = e.conn.SetReadDeadline(time.Now().Add(timeout))
for {
_ = e.conn.SetReadDeadline(time.Now().Add(1 * time.Second))
n, err := e.conn.Read(e.buf)
if err != nil {
break
}
if n > 0 {
out = append(out, e.parseIAC(e.buf[:n])...)
// 收到数据后缩短后续等待,快速收完尾包
_ = e.conn.SetReadDeadline(time.Now().Add(200 * time.Millisecond))
}
if err != nil {
break
}
}
return out
@@ -932,10 +974,10 @@ func (e *cveChecker) extractEvidence(data string, keywords []string) string {
}
// run 执行 CVE-2026-24061 检测流程
// 优先级: id 命令 > echo token > shell prompt 特征
// 优先级: id 命令输出 > echo token 回显
func (e *cveChecker) run() (bool, string, string) {
// 阶段 1: IAC 协商
_ = e.conn.SetReadDeadline(time.Now().Add(5 * time.Second))
// 阶段 1: IAC 协商deadline 控制,不 sleep
_ = e.conn.SetReadDeadline(time.Now().Add(3 * time.Second))
for {
n, err := e.conn.Read(e.buf)
if err != nil {
@@ -950,35 +992,14 @@ func (e *cveChecker) run() (bool, string, string) {
// 协商未触发 exploit 则主动发送
if !e.exploitSent {
e.sendPayload()
time.Sleep(500 * time.Millisecond)
for {
_ = e.conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
n, err := e.conn.Read(e.buf)
if err != nil || n == 0 {
break
}
e.parseIAC(e.buf[:n])
}
e.readAll(500 * time.Millisecond) // 消费协商回包
}
// 阶段 2: id 命令检测 (多次读取确保完整输出)
// 阶段 2: id 命令检测
_, _ = e.conn.Write([]byte("id\n"))
time.Sleep(1500 * time.Millisecond)
idOutput := string(e.readAll(2 * time.Second))
var idOutput string
for i := 0; i < 3; i++ {
part := e.readAll()
if len(part) > 0 {
idOutput += string(part)
}
if len(part) == 0 {
break
}
time.Sleep(500 * time.Millisecond)
}
// 检测 1: id 命令输出 (uid=/gid=/root/nobody)
evidence := e.extractEvidence(idOutput, []string{"uid=", "gid=", "root", "nobody"})
evidence := e.extractEvidence(idOutput, []string{"uid=", "gid="})
if evidence != "" {
return true, e.user, evidence
}
@@ -986,10 +1007,8 @@ func (e *cveChecker) run() (bool, string, string) {
// 阶段 3: echo token 验证
token := e.genToken()
_, _ = e.conn.Write([]byte("echo " + token + "\n"))
time.Sleep(1 * time.Second)
result := string(e.readAll())
stripped := strings.Replace(result, "echo "+token+"\n", "", 1)
result := string(e.readAll(1500 * time.Millisecond))
stripped := strings.Replace(result, "echo "+token, "", 1)
if strings.Contains(stripped, token) {
return true, e.user, "[echo " + token + "]"
}