feat: stream large host scans

This commit is contained in:
ZacharyZcR
2026-06-01 02:55:28 +08:00
parent ac755a9b4a
commit 3e4e2db722
11 changed files with 660 additions and 71 deletions
+485
View File
@@ -0,0 +1,485 @@
package parsers
import (
"bufio"
"context"
"fmt"
"net"
"os"
"strconv"
"strings"
"github.com/shadow1ng/fscan/common/i18n"
)
const DefaultHostBatchSize = 4096
type HostIterator struct {
sources []hostSource
current hostSource
exclude *hostMatcher
}
func NewHostIterator(host string, filename string, nohosts ...string) (*HostIterator, error) {
var sources []hostSource
if filename != "" {
fileSrc, err := newFileHostSource(filename)
if err != nil {
return nil, err
}
sources = append(sources, fileSrc)
}
hostSources, err := newHostSources(host)
if err != nil {
closeHostSources(sources)
return nil, err
}
sources = append(sources, hostSources...)
matcher := newHostMatcher()
if len(nohosts) > 0 && strings.TrimSpace(nohosts[0]) != "" {
if err := matcher.add(nohosts[0]); err != nil {
closeHostSources(sources)
return nil, err
}
}
return &HostIterator{
sources: sources,
exclude: matcher,
}, nil
}
func (it *HostIterator) Close() error {
if it == nil {
return nil
}
var firstErr error
if it.current != nil {
firstErr = it.current.Close()
it.current = nil
}
for _, src := range it.sources {
if err := src.Close(); err != nil && firstErr == nil {
firstErr = err
}
}
it.sources = nil
return firstErr
}
func (it *HostIterator) Next() (string, bool, error) {
for {
if it.current == nil {
if len(it.sources) == 0 {
return "", false, nil
}
it.current = it.sources[0]
it.sources = it.sources[1:]
}
host, ok, err := it.current.Next()
if err != nil {
return "", false, err
}
if !ok {
if err := it.current.Close(); err != nil {
return "", false, err
}
it.current = nil
continue
}
if it.exclude != nil && it.exclude.match(host) {
continue
}
return host, true, nil
}
}
func (it *HostIterator) NextBatch(ctx context.Context, size int) ([]string, error) {
if size <= 0 {
size = DefaultHostBatchSize
}
batch := make([]string, 0, size)
seen := make(map[string]struct{}, size)
for len(batch) < size {
select {
case <-ctx.Done():
return batch, ctx.Err()
default:
}
host, ok, err := it.Next()
if err != nil {
return batch, err
}
if !ok {
return batch, nil
}
if _, exists := seen[host]; exists {
continue
}
seen[host] = struct{}{}
batch = append(batch, host)
}
return batch, nil
}
type hostSource interface {
Next() (string, bool, error)
Close() error
}
type singleHostSource struct {
host string
done bool
}
func (s *singleHostSource) Next() (string, bool, error) {
if s.done {
return "", false, nil
}
s.done = true
return s.host, true, nil
}
func (s *singleHostSource) Close() error { return nil }
type cidrHostSource struct {
current uint32
end uint32
done bool
}
func (s *cidrHostSource) Next() (string, bool, error) {
if s.done || s.current > s.end {
return "", false, nil
}
host := uint32ToIP(s.current)
if s.current == s.end {
s.done = true
} else {
s.current++
}
return host, true, nil
}
func (s *cidrHostSource) Close() error { return nil }
type fileHostSource struct {
file *os.File
scanner *bufio.Scanner
current hostSource
}
func newFileHostSource(filename string) (*fileHostSource, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
return &fileHostSource{
file: file,
scanner: bufio.NewScanner(file),
}, nil
}
func (s *fileHostSource) Next() (string, bool, error) {
for {
if s.current != nil {
host, ok, err := s.current.Next()
if err != nil {
return "", false, err
}
if ok {
return host, true, nil
}
_ = s.current.Close()
s.current = nil
}
if !s.scanner.Scan() {
if err := s.scanner.Err(); err != nil {
return "", false, err
}
return "", false, nil
}
line := strings.TrimSpace(s.scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
sources, err := newHostSources(line)
if err != nil || len(sources) == 0 {
continue
}
if len(sources) == 1 {
s.current = sources[0]
continue
}
s.current = &multiHostSource{sources: sources}
}
}
func (s *fileHostSource) Close() error {
if s.current != nil {
_ = s.current.Close()
s.current = nil
}
if s.file == nil {
return nil
}
err := s.file.Close()
s.file = nil
return err
}
type multiHostSource struct {
sources []hostSource
current hostSource
}
func (s *multiHostSource) Next() (string, bool, error) {
for {
if s.current == nil {
if len(s.sources) == 0 {
return "", false, nil
}
s.current = s.sources[0]
s.sources = s.sources[1:]
}
host, ok, err := s.current.Next()
if err != nil {
return "", false, err
}
if ok {
return host, true, nil
}
_ = s.current.Close()
s.current = nil
}
}
func (s *multiHostSource) Close() error {
if s.current != nil {
_ = s.current.Close()
s.current = nil
}
closeHostSources(s.sources)
s.sources = nil
return nil
}
func newHostSources(host string) ([]hostSource, error) {
var sources []hostSource
for _, h := range strings.Split(host, ",") {
h = strings.TrimSpace(h)
if h == "" {
continue
}
src, err := newHostSource(h)
if err != nil {
closeHostSources(sources)
return nil, err
}
sources = append(sources, src)
}
return sources, nil
}
func newHostSource(host string) (hostSource, error) {
switch {
case host == "192":
return newCIDRHostSource("192.168.0.0/16")
case host == "172":
return newCIDRHostSource("172.16.0.0/12")
case host == "10":
return newCIDRHostSource("10.0.0.0/8")
case strings.Contains(host, "/"):
src, err := newCIDRHostSource(host)
if err != nil {
return nil, fmt.Errorf(i18n.Tr("parser_cidr_failed", host)+": %w", err)
}
return src, nil
case strings.Contains(host, "-") && !strings.Contains(host, ":") && looksLikeIPRange(host):
src, err := newRangeHostSource(host)
if err != nil {
return nil, fmt.Errorf(i18n.Tr("parser_ip_range_failed", host)+": %w", err)
}
return src, nil
default:
return &singleHostSource{host: host}, nil
}
}
func newCIDRHostSource(cidr string) (hostSource, error) {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
}
start, ok := ipToUint32(ipNet.IP)
if !ok {
return nil, fmt.Errorf("%s", i18n.GetText("parser_ipv4_only"))
}
ones, bits := ipNet.Mask.Size()
if bits != 32 {
return nil, fmt.Errorf("%s", i18n.GetText("parser_ipv4_only"))
}
size := uint64(1) << uint(32-ones)
end := start + uint32(size-1)
if size > 2 {
start++
end--
}
return &cidrHostSource{current: start, end: end}, nil
}
func newRangeHostSource(rangeStr string) (hostSource, error) {
parts := strings.Split(rangeStr, "-")
if len(parts) != 2 {
return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_ip_range_fmt", rangeStr))
}
startIPStr := strings.TrimSpace(parts[0])
endIPStr := strings.TrimSpace(parts[1])
startIP := net.ParseIP(startIPStr)
if startIP == nil {
return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_start_ip", startIPStr))
}
if len(endIPStr) < 4 || !strings.Contains(endIPStr, ".") {
endNum, err := strconv.Atoi(endIPStr)
if err != nil || endNum > 255 {
return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_ip_end_val", endIPStr))
}
parts := strings.Split(startIPStr, ".")
if len(parts) != 4 {
return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_ip_fmt", startIPStr))
}
parts[3] = strconv.Itoa(endNum)
endIPStr = strings.Join(parts, ".")
}
start, ok := ipToUint32(startIP)
if !ok {
return nil, fmt.Errorf("%s", i18n.GetText("parser_ipv4_only"))
}
end, ok := ipToUint32(net.ParseIP(endIPStr))
if !ok {
return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_end_ip", endIPStr))
}
if start > end {
return nil, fmt.Errorf("%s", i18n.GetText("parser_start_gt_end"))
}
return &cidrHostSource{current: start, end: end}, nil
}
func closeHostSources(sources []hostSource) {
for _, src := range sources {
_ = src.Close()
}
}
type hostMatcher struct {
exact map[string]struct{}
ranges []ipRange
}
type ipRange struct {
start uint32
end uint32
}
func newHostMatcher() *hostMatcher {
return &hostMatcher{exact: make(map[string]struct{})}
}
func (m *hostMatcher) add(input string) error {
for _, h := range strings.Split(input, ",") {
h = strings.TrimSpace(h)
if h == "" {
continue
}
switch {
case h == "192":
if err := m.addCIDR("192.168.0.0/16"); err != nil {
return err
}
case h == "172":
if err := m.addCIDR("172.16.0.0/12"); err != nil {
return err
}
case h == "10":
if err := m.addCIDR("10.0.0.0/8"); err != nil {
return err
}
case strings.Contains(h, "/"):
if err := m.addCIDR(h); err != nil {
return err
}
case strings.Contains(h, "-") && !strings.Contains(h, ":") && looksLikeIPRange(h):
if err := m.addRange(h); err != nil {
return err
}
default:
m.exact[h] = struct{}{}
}
}
return nil
}
func (m *hostMatcher) addCIDR(cidr string) error {
src, err := newCIDRHostSource(cidr)
if err != nil {
return err
}
rangeSrc, ok := src.(*cidrHostSource)
if !ok {
return fmt.Errorf("%s", i18n.GetText("parser_ipv4_only"))
}
m.ranges = append(m.ranges, ipRange{start: rangeSrc.current, end: rangeSrc.end})
return nil
}
func (m *hostMatcher) addRange(rangeStr string) error {
src, err := newRangeHostSource(rangeStr)
if err != nil {
return err
}
rangeSrc, ok := src.(*cidrHostSource)
if !ok {
return fmt.Errorf("%s", i18n.GetText("parser_ipv4_only"))
}
m.ranges = append(m.ranges, ipRange{start: rangeSrc.current, end: rangeSrc.end})
return nil
}
func (m *hostMatcher) match(host string) bool {
if _, ok := m.exact[host]; ok {
return true
}
ip, ok := ipToUint32(net.ParseIP(host))
if !ok {
return false
}
for _, r := range m.ranges {
if ip >= r.start && ip <= r.end {
return true
}
}
return false
}
func ipToUint32(ip net.IP) (uint32, bool) {
ip4 := ip.To4()
if ip4 == nil {
return 0, false
}
return uint32(ip4[0])<<24 | uint32(ip4[1])<<16 | uint32(ip4[2])<<8 | uint32(ip4[3]), true
}
func uint32ToIP(v uint32) string {
return fmt.Sprintf("%d.%d.%d.%d", byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
+61
View File
@@ -0,0 +1,61 @@
package parsers
import (
"context"
"reflect"
"testing"
)
func TestHostIteratorCIDRBatch(t *testing.T) {
iter, err := NewHostIterator("192.168.1.0/30", "", "")
if err != nil {
t.Fatalf("NewHostIterator error = %v", err)
}
defer iter.Close()
batch, err := iter.NextBatch(context.Background(), 10)
if err != nil {
t.Fatalf("NextBatch error = %v", err)
}
want := []string{"192.168.1.1", "192.168.1.2"}
if !reflect.DeepEqual(batch, want) {
t.Fatalf("batch = %#v, want %#v", batch, want)
}
}
func TestHostIteratorDoesNotExpandWholeRangeAtOnce(t *testing.T) {
iter, err := NewHostIterator("10", "", "")
if err != nil {
t.Fatalf("NewHostIterator error = %v", err)
}
defer iter.Close()
batch, err := iter.NextBatch(context.Background(), 3)
if err != nil {
t.Fatalf("NextBatch error = %v", err)
}
want := []string{"10.0.0.1", "10.0.0.2", "10.0.0.3"}
if !reflect.DeepEqual(batch, want) {
t.Fatalf("batch = %#v, want %#v", batch, want)
}
}
func TestHostIteratorExcludeCIDR(t *testing.T) {
iter, err := NewHostIterator("192.168.1.0/29", "", "192.168.1.2-192.168.1.4")
if err != nil {
t.Fatalf("NewHostIterator error = %v", err)
}
defer iter.Close()
batch, err := iter.NextBatch(context.Background(), 10)
if err != nil {
t.Fatalf("NextBatch error = %v", err)
}
want := []string{"192.168.1.1", "192.168.1.5", "192.168.1.6"}
if !reflect.DeepEqual(batch, want) {
t.Fatalf("batch = %#v, want %#v", batch, want)
}
}
+26 -24
View File
@@ -63,52 +63,54 @@ func (s *AliveScanStrategy) Execute(ctx context.Context, session *common.ScanSes
// 执行存活探测 // 执行存活探测
s.performAliveScan(ctx, info, session) s.performAliveScan(ctx, info, session)
// 输出统计信息
s.outputStats(session)
} }
// performAliveScan 执行存活探测 // performAliveScan 执行存活探测
func (s *AliveScanStrategy) performAliveScan(ctx context.Context, info common.HostInfo, session *common.ScanSession) { func (s *AliveScanStrategy) performAliveScan(ctx context.Context, info common.HostInfo, session *common.ScanSession) {
// 解析目标主机 iter, err := parsers.NewHostIterator(info.Host, session.Params.HostsFile, session.Params.ExcludeHosts)
hosts, err := parsers.ParseIP(info.Host, session.Params.HostsFile, session.Params.ExcludeHosts)
if err != nil { if err != nil {
session.LogError(i18n.Tr("parse_target_failed", err)) session.LogError(i18n.Tr("parse_target_failed", err))
return return
} }
defer func() {
_ = iter.Close()
}()
s.stats.TotalHosts = 0
s.stats.AliveHosts = 0
s.stats.DeadHosts = 0
for {
hosts, err := iter.NextBatch(ctx, targetHostBatchSize(session.Config))
if err != nil {
session.LogError(i18n.Tr("parse_target_failed", err))
return
}
if len(hosts) == 0 { if len(hosts) == 0 {
break
}
s.stats.TotalHosts += len(hosts)
aliveList := CheckLive(ctx, hosts, false, session)
s.stats.AliveHosts += len(aliveList)
for _, host := range aliveList {
session.LogSuccess(fmt.Sprintf("alive %s", host))
}
}
if s.stats.TotalHosts == 0 {
session.LogError(i18n.GetText("parse_error_no_hosts")) session.LogError(i18n.GetText("parse_error_no_hosts"))
return return
} }
// 初始化统计信息
s.stats.TotalHosts = len(hosts)
s.stats.AliveHosts = 0
s.stats.DeadHosts = 0
// 执行存活检测
aliveList := CheckLive(ctx, hosts, false, session) // 使用ICMP探测
// 更新统计信息
s.stats.AliveHosts = len(aliveList)
s.stats.DeadHosts = s.stats.TotalHosts - s.stats.AliveHosts s.stats.DeadHosts = s.stats.TotalHosts - s.stats.AliveHosts
s.stats.ScanDuration = time.Since(s.startTime) s.stats.ScanDuration = time.Since(s.startTime)
s.stats.AliveHostList = aliveList // 存储存活主机列表
if s.stats.TotalHosts > 0 { if s.stats.TotalHosts > 0 {
s.stats.SuccessRate = float64(s.stats.AliveHosts) / float64(s.stats.TotalHosts) * 100 s.stats.SuccessRate = float64(s.stats.AliveHosts) / float64(s.stats.TotalHosts) * 100
} }
} }
// outputStats 输出统计信息(精简版)
func (s *AliveScanStrategy) outputStats(session *common.ScanSession) {
// 只输出存活主机列表,不输出冗余统计
for _, host := range s.stats.AliveHostList {
session.LogSuccess(fmt.Sprintf("alive %s", host))
}
}
// PrepareTargets 存活探测不需要准备扫描目标 // PrepareTargets 存活探测不需要准备扫描目标
func (s *AliveScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo { func (s *AliveScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo {
// 存活探测不需要返回目标列表,因为它不进行后续扫描 // 存活探测不需要返回目标列表,因为它不进行后续扫描
+22
View File
@@ -0,0 +1,22 @@
package core
import (
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/parsers"
)
const maxHostBatchSize = 65536
func targetHostBatchSize(config *common.Config) int {
size := parsers.DefaultHostBatchSize
if config != nil && config.ThreadNum > 0 {
threadWindow := config.ThreadNum * 8
if threadWindow > size {
size = threadWindow
}
}
if size > maxHostBatchSize {
return maxHostBatchSize
}
return size
}
+2 -2
View File
@@ -437,7 +437,7 @@ func buildServiceLogMessage(addr string, serviceInfo *ServiceInfo, isWeb bool) s
info = append(info, fmt.Sprintf("Version:%s", serviceInfo.Version)) info = append(info, fmt.Sprintf("Version:%s", serviceInfo.Version))
} }
if len(info) > 0 { if len(info) > 0 {
msg.WriteString(fmt.Sprintf(" [%s]", strings.Join(info, " ||"))) fmt.Fprintf(&msg, " [%s]", strings.Join(info, " ||"))
} }
// Banner 信息 // Banner 信息
@@ -446,7 +446,7 @@ func buildServiceLogMessage(addr string, serviceInfo *ServiceInfo, isWeb bool) s
if len(banner) > 80 { if len(banner) > 80 {
banner = banner[:80] + "..." banner = banner[:80] + "..."
} }
msg.WriteString(fmt.Sprintf(" Banner:(%s)", banner)) fmt.Fprintf(&msg, " Banner:(%s)", banner)
} }
return msg.String() return msg.String()
+1 -1
View File
@@ -15,7 +15,7 @@ func BytesToRegexSafeString(b []byte) string {
for _, c := range b { for _, c := range b {
if c < 32 || c >= 128 { if c < 32 || c >= 128 {
// 控制字符和高位字节转换为 \x{NN} 格式 // 控制字符和高位字节转换为 \x{NN} 格式
result.WriteString(fmt.Sprintf("\\x{%02x}", c)) fmt.Fprintf(&result, "\\x{%02x}", c)
} else { } else {
result.WriteByte(c) result.WriteByte(c)
} }
+53 -34
View File
@@ -145,46 +145,76 @@ func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *comm
config := session.Config config := session.Config
state := session.State state := session.State
// 解析目标主机 iter, err := parsers.NewHostIterator(info.Host, session.Params.HostsFile, session.Params.ExcludeHosts)
hosts, err := parsers.ParseIP(info.Host, session.Params.HostsFile, session.Params.ExcludeHosts)
if err != nil { if err != nil {
session.LogError(fmt.Sprintf("%s: %v", i18n.GetText("parse_target_failed"), err)) session.LogError(fmt.Sprintf("%s: %v", i18n.GetText("parse_target_failed"), err))
return return
} }
defer func() {
_ = iter.Close()
}()
pluginsToRun, isCustomMode := s.GetPlugins(config)
totalAlive := 0
sawHosts := false
for {
hosts, err := iter.NextBatch(ctx, targetHostBatchSize(config))
if err != nil {
session.LogError(fmt.Sprintf("%s: %v", i18n.GetText("parse_target_failed"), err))
return
}
if len(hosts) == 0 {
break
}
sawHosts = true
// 主机存活检测
if s.shouldPerformLivenessCheck(hosts, config) { if s.shouldPerformLivenessCheck(hosts, config) {
hosts = CheckLive(ctx, hosts, false, session) hosts = CheckLive(ctx, hosts, false, session)
session.LogInfo(i18n.Tr("alive_hosts_count_info", len(hosts))) }
totalAlive += len(hosts)
if len(hosts) == 0 {
continue
} }
if len(hosts) == 0 && len(state.GetHostPorts()) == 0 { s.dispatchUDPPlugins(ctx, session, hosts, info, config, ch, wg)
s.scanHostBatch(ctx, session, hosts, info, pluginsToRun, isCustomMode, ch, wg)
}
if sawHosts && s.shouldReportAliveCount(config) {
session.LogInfo(i18n.Tr("alive_hosts_count_info", totalAlive))
}
if !sawHosts && len(state.GetHostPorts()) == 0 {
return return
} }
// UDP 插件并行分发:直接对存活主机发协议探测包,不走端口扫描 // 合并预设的 host:port
if len(hosts) > 0 { hostPorts := state.GetHostPorts()
s.dispatchUDPPlugins(ctx, session, hosts, info, config, ch, wg) if len(hostPorts) > 0 {
merged := mergeHostPorts(nil, hostPorts)
targets := s.convertToTargetInfos(merged, info)
for _, target := range targets {
for _, pluginName := range pluginsToRun {
if s.IsPluginApplicableByName(pluginName, target.Host, target.Port, isCustomMode, config) {
executeScanTask(ctx, session, pluginName, target, ch, wg)
}
}
}
state.ClearHostPorts()
}
} }
// 流式 channel:端口扫描发现开放端口后立即通知插件执行 func (s *ServiceScanStrategy) scanHostBatch(ctx context.Context, session *common.ScanSession, hosts []string, info common.HostInfo, pluginsToRun []string, isCustomMode bool, ch chan struct{}, wg *sync.WaitGroup) {
config := session.Config
stream := make(chan string, 64) stream := make(chan string, 64)
// 启动端口扫描 goroutine go EnhancedPortScan(ctx, hosts, config.Target.Ports, int64(config.Timeout.Seconds()), session, stream)
go func() {
if len(hosts) > 0 {
EnhancedPortScan(ctx, hosts, config.Target.Ports, int64(config.Timeout.Seconds()), session, stream)
} else {
close(stream)
}
}()
// pipeline 消费:边收开放端口边执行插件
pluginsToRun, isCustomMode := s.GetPlugins(config)
cancelled := false cancelled := false
for addr := range stream { for addr := range stream {
if cancelled { if cancelled {
continue // ctx 已取消,排空 stream 防止写端阻塞 continue
} }
select { select {
case <-ctx.Done(): case <-ctx.Done():
@@ -202,21 +232,10 @@ func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *comm
} }
} }
} }
}
// 合并预设的 host:port func (s *ServiceScanStrategy) shouldReportAliveCount(config *common.Config) bool {
hostPorts := state.GetHostPorts() return !config.DisablePing
if len(hostPorts) > 0 {
merged := mergeHostPorts(nil, hostPorts)
targets := s.convertToTargetInfos(merged, info)
for _, target := range targets {
for _, pluginName := range pluginsToRun {
if s.IsPluginApplicableByName(pluginName, target.Host, target.Port, isCustomMode, config) {
executeScanTask(ctx, session, pluginName, target, ch, wg)
}
}
}
state.ClearHostPorts()
}
} }
// dispatchUDPPlugins 分发UDP协议插件,跳过TCP端口扫描链路 // dispatchUDPPlugins 分发UDP协议插件,跳过TCP端口扫描链路
+3 -3
View File
@@ -57,10 +57,10 @@ func (p *FTPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
// 成功后重新连接获取文件列表 // 成功后重新连接获取文件列表
fileList := p.getFileListAfterAuth(info, result.Username, result.Password, config, state) fileList := p.getFileListAfterAuth(info, result.Username, result.Password, config, state)
var output strings.Builder var output strings.Builder
output.WriteString(fmt.Sprintf("FTP %s %s:%s", target, result.Username, result.Password)) fmt.Fprintf(&output, "FTP %s %s:%s", target, result.Username, result.Password)
if len(fileList) > 0 { if len(fileList) > 0 {
for _, file := range fileList { for _, file := range fileList {
output.WriteString(fmt.Sprintf("\n [->] %s", file)) fmt.Fprintf(&output, "\n [->] %s", file)
} }
} }
session.LogVuln(output.String()) session.LogVuln(output.String())
@@ -205,7 +205,7 @@ func (p *FTPPlugin) testAnonymousAccess(ctx context.Context, info *common.HostIn
output.WriteString(i18n.Tr("ftp_anonymous_access_detail", target, cred.Username, cred.Password)) output.WriteString(i18n.Tr("ftp_anonymous_access_detail", target, cred.Username, cred.Password))
if len(fileList) > 0 { if len(fileList) > 0 {
for _, file := range fileList { for _, file := range fileList {
output.WriteString(fmt.Sprintf("\n [->] %s", file)) fmt.Fprintf(&output, "\n [->] %s", file)
} }
} }
session.LogVuln(output.String()) session.LogVuln(output.String())
+2 -2
View File
@@ -143,11 +143,11 @@ func (p *MS17010Plugin) Exploit(ctx context.Context, info *common.HostInfo, cred
switch config.Shellcode { switch config.Shellcode {
case "bind": case "bind":
output.WriteString("\n" + i18n.GetText("ms17010_exploit_bind_hint") + "\n") output.WriteString("\n" + i18n.GetText("ms17010_exploit_bind_hint") + "\n")
output.WriteString(fmt.Sprintf(" nc %s 64531\n", info.Host)) fmt.Fprintf(&output, " nc %s 64531\n", info.Host)
case "add": case "add":
output.WriteString("\n" + i18n.GetText("ms17010_exploit_add_hint") + "\n") output.WriteString("\n" + i18n.GetText("ms17010_exploit_add_hint") + "\n")
output.WriteString(i18n.GetText("ms17010_exploit_add_credential") + "\n") output.WriteString(i18n.GetText("ms17010_exploit_add_credential") + "\n")
output.WriteString(fmt.Sprintf(" RDP: mstsc /v:%s\n", info.Host)) fmt.Fprintf(&output, " RDP: mstsc /v:%s\n", info.Host)
case "guest": case "guest":
output.WriteString("\n" + i18n.GetText("ms17010_exploit_guest_hint") + "\n") output.WriteString("\n" + i18n.GetText("ms17010_exploit_guest_hint") + "\n")
} }
+1 -1
View File
@@ -145,7 +145,7 @@ func (p *SmbPlugin) testUnauthorizedAccess(ctx context.Context, info *common.Hos
} }
output.WriteString(i18n.Tr("smb_anonymous_access_detail", target, displayUser, cred.Password)) output.WriteString(i18n.Tr("smb_anonymous_access_detail", target, displayUser, cred.Password))
for _, share := range shareInfo { for _, share := range shareInfo {
output.WriteString(fmt.Sprintf("\n%s", share)) fmt.Fprintf(&output, "\n%s", share)
} }
common.LogSuccess(output.String()) common.LogSuccess(output.String())
+1 -1
View File
@@ -899,7 +899,7 @@ func CheckInfoPoc(infostr string) string {
func GetHeader(header map[string]string) string { func GetHeader(header map[string]string) string {
var builder strings.Builder var builder strings.Builder
for name, values := range header { for name, values := range header {
builder.WriteString(fmt.Sprintf("%s: %s\n", name, values)) fmt.Fprintf(&builder, "%s: %s\n", name, values)
} }
builder.WriteString("\r\n") builder.WriteString("\r\n")
return builder.String() return builder.String()