阅读量:1
文章目录
前言
为什么要写这个?
- fscna被杀的概率太高(哪天二开一下免杀)。
- go还在学习的阶段正是写项目的时候,边写边学习go项目。
- 自己写的项目改起来更加方便。
- 实现功能暂时定为网段扫描和暴力破解和输出文档。
现实现的功能较少后序开发会逐步加入简单漏洞探探测和代理功能。
一、开发过程
项目已经打包上传至github:https://github.com/19xinan/Scanl
1.项目结构
项目结构非常简单,现在开发的基本功能包括主机存活探测、端口扫描、暴力破解功能其他功能后序开发。
scanl/ |-- main.go //程序入口,主函数包括命令格式和网段格式限制 |-- core/ | |-- scanner.go// 扫描器,包括线程控制 | |-- services.go//服务识别 |-- bruteforce/ | |-- bruteforce.go//暴力破解模版 |-- pass.txt//暴力破解使用的密码文件
2.main.go
1.实现网段限制
2.实现网段存活探测
3.实现命令行参数限制
4.实现输出扫描结果文档
package main import ( "flag" "fmt" "net" "os" "sync" "time" "scanl/bruteforce" "scanl/core" ) func main() { fmt.Println(` ██████ ▄████▄ ▄▄▄ ███▄ █ ██▓ ▒██ ▒ ▒██▀ ▀█ ▒████▄ ██ ▀█ █ ▓██▒ ░ ▓██▄ ▒▓█ ▄ ▒██ ▀█▄ ▓██ ▀█ ██▒▒██░ ▒ ██▒▒▓▓▄ ▄██▒░██▄▄▄▄██ ▓██▒ ▐▌██▒▒██░ ▒██████▒▒▒ ▓███▀ ░ ▓█ ▓██▒▒██░ ▓██░░██████▒ ▒ ▒▓▒ ▒ ░░ ░▒ ▒ ░ ▒▒ ▓▒█░░ ▒░ ▒ ▒ ░ ▒░▓ ░ ░ ░▒ ░ ░ ░ ▒ ▒ ▒▒ ░░ ░░ ░ ▒░░ ░ ▒ ░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ `) // 解析命令行参数:-h网段、-all全端口、-t线程数、-pwd指定密码文件、-output指定输出文件名(不指定默认输出) subnet := flag.String("h", "", "Target subnet for scanning (e.g., 192.168.10.0/24)") allPorts := flag.Bool("all", false, "Scan all ports (0-65535)") threads := flag.Int("t", 100, "Number of concurrent threads") passwordFile := flag.String("pwd", "pass.txt", "Password file for bruteforce") outputFile := flag.String("output", "scan_results.txt", "Output file for scan results") flag.Parse() //检查网段 if *subnet == "" { fmt.Println("Usage: ScanL.exe -h <target_subnet> [-all] [-t N] [-pwd pass.txt] [-output scan_results.txt]") os.Exit(1) } // 打开输出文件 outputFileHandle, err := os.OpenFile(*outputFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { fmt.Printf("Error opening output file: %v\n", err) os.Exit(1) } defer outputFileHandle.Close() // 解析网段 ips, err := expandCIDR(*subnet) if err != nil { fmt.Fprintf(outputFileHandle, "Error parsing subnet: %v\n", err) os.Exit(1) } var wg sync.WaitGroup var mutex sync.Mutex var aliveHosts []string // 检测存活主机并输出到终端和文件 for _, ip := range ips { wg.Add(1) go func(ip string) { defer wg.Done() if isHostAlive(ip) { mutex.Lock() aliveHosts = append(aliveHosts, ip) mutex.Unlock() fmt.Printf("Host %s is alive\n", ip) fmt.Fprintf(outputFileHandle, "Host %s is alive\n", ip) } else { fmt.Printf("Host %s is not alive\n", ip) fmt.Fprintf(outputFileHandle, "Host %s is not alive\n", ip) } }(ip) } wg.Wait() // 输出存活主机到文件 fmt.Fprintln(outputFileHandle, "Alive hosts in subnet:") for _, ip := range aliveHosts { fmt.Fprintln(outputFileHandle, ip) } var ports []int if *allPorts { ports = make([]int, 65536) for i := 0; i <= 65535; i++ { ports[i] = i } } else { ports = []int{21, 22, 23, 25, 53, 80, 110, 119, 123, 143, 161, 194, 443, 445, 465, 587, 993, 995, 1433, 1521, 1723, 3306, 3389, 5900, 8080, 8443, 8888, 9090, 7001, 9999, 6379, 9200, 9300, 27017} // 精简端口列表 } // 扫描主机并输出结果到终端和文件 for _, ip := range aliveHosts { fmt.Fprintf(outputFileHandle, "Scanning host: %s\n", ip) fmt.Printf("Scanning host: %s\n", ip) results := core.ScanPorts(ip, ports, *threads) fmt.Fprintf(outputFileHandle, "Open ports on host %s:\n", ip) fmt.Printf("Open ports on host %s:\n", ip) for port, service := range results { if service != "Closed" { fmt.Fprintf(outputFileHandle, "Port %d: %s\n", port, service) fmt.Printf("Port %d: %s\n", port, service) } } // 默认启用暴力破解模块,针对开启了SSH或RDP的端口 if service, found := results[22]; found && service == "SSH" { fmt.Fprintln(outputFileHandle, "Starting bruteforce attack on SSH...") fmt.Println("Starting bruteforce attack on SSH...") bruteforce.Bruteforce(ip, 22, *passwordFile) } //RDP实现有问题暂存 //if service, found := results[3389]; found && service == "RDP" { // fmt.Fprintln(outputFileHandle, "Starting bruteforce attack on RDP...") // fmt.Println("Starting bruteforce attack on RDP...") // bruteforce.Bruteforce(ip, 3389, *passwordFile) //} fmt.Fprintln(outputFileHandle, "---------------------------------------------") fmt.Println("---------------------------------------------") } fmt.Printf("Scan results saved to %s\n", *outputFile) } // expandCIDR 解析网段,生成所有 IP 地址 func expandCIDR(cidr string) ([]string, error) { ip, ipNet, err := net.ParseCIDR(cidr) if err != nil { return nil, err } var ips []string for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); inc(ip) { ips = append(ips, ip.String()) } // 排除网络地址和广播地址 lenIPs := len(ips) switch { case lenIPs < 2: break case lenIPs > 2: ips = ips[1 : len(ips)-1] } return ips, nil } // IP地址递增 func inc(ip net.IP) { for j := len(ip) - 1; j >= 0; j-- { ip[j]++ if ip[j] > 0 { break } } } // isHostAlive 检测主机是否存活 func isHostAlive(ip string) bool { timeout := 2 * time.Second conn, err := net.DialTimeout("ip4:icmp", ip, timeout) if err != nil { return false } defer conn.Close() return true }
3.core模块
3.1 scanner.go
package core import ( "fmt" "net" "sync" "time" ) // ScanPorts 扫描指定主机的指定端口,使用指定数量的并发线程 func ScanPorts(host string, ports []int, threads int) map[int]string { results := make(map[int]string) var mu sync.Mutex var wg sync.WaitGroup portChan := make(chan int, len(ports)) // 启动指定数量的goroutines for i := 0; i < threads; i++ { wg.Add(1) go func() { defer wg.Done() for port := range portChan { service := scanPort(host, port) mu.Lock() results[port] = service mu.Unlock() } }() } // 将所有端口放入通道 for _, port := range ports { portChan <- port } close(portChan) wg.Wait() return results } // scanPort 扫描单个端口 func scanPort(host string, port int) string { address := fmt.Sprintf("%s:%d", host, port) conn, err := net.DialTimeout("tcp", address, 1*time.Second) if err != nil { return "Closed" } defer func(conn net.Conn) { err := conn.Close() if err != nil { } }(conn) return identifyService(port) }
3.2 service.go
package core // identifyService 根据端口号识别服务 func identifyService(port int) string { services := map[int]string{ 21: "FTP", 22: "SSH", 23: "Telnet", 25: "SMTP", 53: "DNS", 80: "HTTP", 110: "POP3", 119: "NNTP", 123: "NTP", 143: "IMAP", 161: "SNMP", 194: "IRC", 443: "HTTPS", 445: "SMB", 465: "SMTPS", 587: "Submission", 993: "IMAPS", 995: "POP3S", 1433: "MSSQL", 1521: "Oracle DB", 1723: "PPTP", 3306: "MySQL", 3389: "RDP", 5900: "VNC", 8080: "HTTP-Proxy", 8443: "HTTPS-Alt", 8888: "HTTP-Alt", 9090: "Weblogic", 7001: "Weblogic-Alt", 9999: "HTTP-Alt2", 6379: "Redis", 9200: "Elasticsearch", 9300: "Elasticsearch-Transport", 27017: "MongoDB", } if service, found := services[port]; found { return service } return "Unknown" }
4.bruteforc
这里少了rdp的爆破
4.1 bruteforce.go
package bruteforce import ( "bufio" "fmt" "os" "sync" "time" "golang.org/x/crypto/ssh" ) // 默认账号列表 var defaultAccounts = []string{"root", "admin", "administrator"} // Bruteforce 执行暴力破解攻击 func Bruteforce(host string, port int, passwordFile string) { passwords, err := readPasswords(passwordFile) if err != nil { fmt.Printf("Error reading password file: %v\n", err) return } var wg sync.WaitGroup wg.Add(len(defaultAccounts) * len(passwords)) // 并发尝试不同的账号和密码组合 for _, account := range defaultAccounts { for _, password := range passwords { go func(host string, port int, account string, password string) { defer wg.Done() fmt.Printf("Trying account: %s, password: %s\n", account, password) if sshLogin(host, port, account, password) { fmt.Printf("SSH login successful: %s:%s@%s\n", account, password, host) } }(host, port, account, password) } } wg.Wait() } // readPasswords 读取密码文件 func readPasswords(filePath string) ([]string, error) { file, err := os.Open(filePath) if err != nil { return nil, err } defer func(file *os.File) { err := file.Close() if err != nil { } }(file) var passwords []string scanner := bufio.NewScanner(file) for scanner.Scan() { passwords = append(passwords, scanner.Text()) } if err := scanner.Err(); err != nil { return nil, err } return passwords, nil } // sshLogin 尝试使用SSH登录 func sshLogin(host string, port int, username, password string) bool { config := &ssh.ClientConfig{ User: username, Auth: []ssh.AuthMethod{ ssh.Password(password), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: 5 * time.Second, } conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), config) if err != nil { return false } defer func(conn *ssh.Client) { err := conn.Close() if err != nil { } }(conn) return true }
二、使用步骤
ScanL.exe -h 192.168.10.1/24 其他参数 ScanL.exe -h <target_subnet> [-all] [-t N] [-pwd pass.txt] [-output scan_results.txt]