diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b6a0be --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +result.txt \ No newline at end of file diff --git a/Plugins/ftp.go b/Plugins/ftp.go index e434307..5e50a8a 100644 --- a/Plugins/ftp.go +++ b/Plugins/ftp.go @@ -4,53 +4,25 @@ import ( "fmt" "github.com/jlaffaye/ftp" "github.com/shadow1ng/fscan/common" - "strings" "time" ) +type FtpConn struct{} + func FtpScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return } - starttime := time.Now().Unix() - flag, err := FtpConn(info, "anonymous", "") - if flag == true && err == nil { - return err - } else { - errlog := fmt.Sprintf("[-] ftp://%v:%v %v %v", info.Host, info.Ports, "anonymous", err) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - } - - for _, user := range common.Userdict["ftp"] { - for _, pass := range common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - flag, err := FtpConn(info, user, pass) - if flag == true && err == nil { - return err - } else { - errlog := fmt.Sprintf("[-] ftp://%v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["ftp"])*len(common.Passwords)) * common.Timeout) { - return err - } - } - } - } - return tmperr + // 这里把单独的未授权访问测试:anonymous 添加到字典内来测试。 + ftpConn := &FtpConn{} + bt := common.InitBruteThread("ftp", info, common.Timeout, ftpConn) + return bt.Run() } -func FtpConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func (f *FtpConn) Attack(info *common.HostInfo, user string, pass string, timeout int64) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass - conn, err := ftp.DialTimeout(fmt.Sprintf("%v:%v", Host, Port), time.Duration(common.Timeout)*time.Second) + conn, err := ftp.DialTimeout(fmt.Sprintf("%v:%v", Host, Port), time.Duration(timeout)*time.Second) if err == nil { err = conn.Login(Username, Password) if err == nil { diff --git a/Plugins/mssql.go b/Plugins/mssql.go index 6f64c67..0263e2f 100644 --- a/Plugins/mssql.go +++ b/Plugins/mssql.go @@ -5,45 +5,28 @@ import ( "fmt" _ "github.com/denisenkom/go-mssqldb" "github.com/shadow1ng/fscan/common" - "strings" "time" ) +type MssqlConn struct{} + func MssqlScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return } - starttime := time.Now().Unix() - for _, user := range common.Userdict["mssql"] { - for _, pass := range common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - flag, err := MssqlConn(info, user, pass) - if flag == true && err == nil { - return err - } else { - errlog := fmt.Sprintf("[-] mssql %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["mssql"])*len(common.Passwords)) * common.Timeout) { - return err - } - } - } - } - return tmperr + mssqlConn := &MssqlConn{} + bt := common.InitBruteThread("mssql", info, common.Timeout, mssqlConn) + return bt.Run() } -func MssqlConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func (m *MssqlConn) Attack(info *common.HostInfo, user string, pass string, timeout int64) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass dataSourceName := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%v;encrypt=disable;timeout=%v", Host, Username, Password, Port, time.Duration(common.Timeout)*time.Second) db, err := sql.Open("mssql", dataSourceName) if err == nil { - db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second) - db.SetConnMaxIdleTime(time.Duration(common.Timeout) * time.Second) + db.SetConnMaxLifetime(time.Duration(timeout) * time.Second) + db.SetConnMaxIdleTime(time.Duration(timeout) * time.Second) db.SetMaxIdleConns(0) defer db.Close() err = db.Ping() diff --git a/Plugins/mysql.go b/Plugins/mysql.go index 03c6aac..7d8d451 100644 --- a/Plugins/mysql.go +++ b/Plugins/mysql.go @@ -5,45 +5,28 @@ import ( "fmt" _ "github.com/go-sql-driver/mysql" "github.com/shadow1ng/fscan/common" - "strings" "time" ) +type MysqlConn struct{} + func MysqlScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return } - starttime := time.Now().Unix() - for _, user := range common.Userdict["mysql"] { - for _, pass := range common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - flag, err := MysqlConn(info, user, pass) - if flag == true && err == nil { - return err - } else { - errlog := fmt.Sprintf("[-] mysql %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["mysql"])*len(common.Passwords)) * common.Timeout) { - return err - } - } - } - } - return tmperr + mysqlConn := &MysqlConn{} + bt := common.InitBruteThread("mysql", info, common.Timeout, mysqlConn) + return bt.Run() } -func MysqlConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func (m *MysqlConn) Attack(info *common.HostInfo, user string, pass string, timeout int64) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass dataSourceName := fmt.Sprintf("%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v", Username, Password, Host, Port, time.Duration(common.Timeout)*time.Second) db, err := sql.Open("mysql", dataSourceName) if err == nil { - db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second) - db.SetConnMaxIdleTime(time.Duration(common.Timeout) * time.Second) + db.SetConnMaxLifetime(time.Duration(timeout) * time.Second) + db.SetConnMaxIdleTime(time.Duration(timeout) * time.Second) db.SetMaxIdleConns(0) defer db.Close() err = db.Ping() diff --git a/Plugins/oracle.go b/Plugins/oracle.go index 0db4142..f48757f 100644 --- a/Plugins/oracle.go +++ b/Plugins/oracle.go @@ -5,45 +5,28 @@ import ( "fmt" "github.com/shadow1ng/fscan/common" _ "github.com/sijms/go-ora/v2" - "strings" "time" ) +type OracleConn struct{} + func OracleScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return } - starttime := time.Now().Unix() - for _, user := range common.Userdict["oracle"] { - for _, pass := range common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - flag, err := OracleConn(info, user, pass) - if flag == true && err == nil { - return err - } else { - errlog := fmt.Sprintf("[-] oracle %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["oracle"])*len(common.Passwords)) * common.Timeout) { - return err - } - } - } - } - return tmperr + oracleConn := &OracleConn{} + bt := common.InitBruteThread("oracle", info, common.Timeout, oracleConn) + return bt.Run() } -func OracleConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func (o *OracleConn) Attack(info *common.HostInfo, user string, pass string, timeout int64) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass dataSourceName := fmt.Sprintf("oracle://%s:%s@%s:%s/orcl", Username, Password, Host, Port) db, err := sql.Open("oracle", dataSourceName) if err == nil { - db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second) - db.SetConnMaxIdleTime(time.Duration(common.Timeout) * time.Second) + db.SetConnMaxLifetime(time.Duration(timeout) * time.Second) + db.SetConnMaxIdleTime(time.Duration(timeout) * time.Second) db.SetMaxIdleConns(0) defer db.Close() err = db.Ping() diff --git a/Plugins/postgres.go b/Plugins/postgres.go index 36a97ed..e8c676e 100644 --- a/Plugins/postgres.go +++ b/Plugins/postgres.go @@ -5,44 +5,27 @@ import ( "fmt" _ "github.com/lib/pq" "github.com/shadow1ng/fscan/common" - "strings" "time" ) +type PostgresConn struct{} + func PostgresScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return } - starttime := time.Now().Unix() - for _, user := range common.Userdict["postgresql"] { - for _, pass := range common.Passwords { - pass = strings.Replace(pass, "{user}", string(user), -1) - flag, err := PostgresConn(info, user, pass) - if flag == true && err == nil { - return err - } else { - errlog := fmt.Sprintf("[-] psql %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["postgresql"])*len(common.Passwords)) * common.Timeout) { - return err - } - } - } - } - return tmperr + postgresConn := &PostgresConn{} + bt := common.InitBruteThread("postgresql", info, common.Timeout, postgresConn) + return bt.Run() } -func PostgresConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func (p *PostgresConn) Attack(info *common.HostInfo, user string, pass string, timeout int64) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass dataSourceName := fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=%v", Username, Password, Host, Port, "postgres", "disable") db, err := sql.Open("postgres", dataSourceName) if err == nil { - db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second) + db.SetConnMaxLifetime(time.Duration(timeout) * time.Second) defer db.Close() err = db.Ping() if err == nil { diff --git a/Plugins/rdp.go b/Plugins/rdp.go index abfd6d0..ea4fadb 100644 --- a/Plugins/rdp.go +++ b/Plugins/rdp.go @@ -16,88 +16,27 @@ import ( "log" "os" "strconv" - "strings" "sync" "time" ) -type Brutelist struct { - user string - pass string +type RdpConn struct { } func RdpScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return } + rdpConn := &RdpConn{} + bt := common.InitBruteThread("rdp", info, common.Timeout, rdpConn) + return bt.Run() +} - var wg sync.WaitGroup - var signal bool - var num = 0 - var all = len(common.Userdict["rdp"]) * len(common.Passwords) - var mutex sync.Mutex - brlist := make(chan Brutelist, all) +func (r RdpConn) Attack(info *common.HostInfo, user string, pass string, timeout int64) (flag bool, e error) { port, _ := strconv.Atoi(info.Ports) - - for _, user := range common.Userdict["rdp"] { - for _, pass := range common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - brlist <- Brutelist{user, pass} - } - } - - for i := 0; i < common.BruteThread; i++ { - wg.Add(1) - go worker(info.Host, common.Domain, port, &wg, brlist, &signal, &num, all, &mutex, common.Timeout) - } - - close(brlist) - go func() { - wg.Wait() - signal = true - }() - for !signal { - } - - return tmperr -} - -func worker(host, domain string, port int, wg *sync.WaitGroup, brlist chan Brutelist, signal *bool, num *int, all int, mutex *sync.Mutex, timeout int64) { - defer wg.Done() - for one := range brlist { - if *signal == true { - return - } - go incrNum(num, mutex) - user, pass := one.user, one.pass - flag, err := RdpConn(host, domain, user, pass, port, timeout) - if flag == true && err == nil { - var result string - if domain != "" { - result = fmt.Sprintf("[+] RDP:%v:%v:%v\\%v %v", host, port, domain, user, pass) - } else { - result = fmt.Sprintf("[+] RDP:%v:%v:%v %v", host, port, user, pass) - } - common.LogSuccess(result) - *signal = true - return - } else { - errlog := fmt.Sprintf("[-] (%v/%v) rdp %v:%v %v %v %v", *num, all, host, port, user, pass, err) - common.LogError(errlog) - } - } -} - -func incrNum(num *int, mutex *sync.Mutex) { - mutex.Lock() - *num = *num + 1 - mutex.Unlock() -} - -func RdpConn(ip, domain, user, password string, port int, timeout int64) (bool, error) { - target := fmt.Sprintf("%s:%d", ip, port) + target := fmt.Sprintf("%s:%d", info.Host, port) g := NewClient(target, glog.NONE) - err := g.Login(domain, user, password, timeout) + err := g.Login(common.Domain, user, pass, timeout) if err == nil { return true, nil diff --git a/Plugins/smb.go b/Plugins/smb.go index 19911e7..650d83f 100644 --- a/Plugins/smb.go +++ b/Plugins/smb.go @@ -2,46 +2,21 @@ package Plugins import ( "errors" - "fmt" "github.com/shadow1ng/fscan/common" "github.com/stacktitan/smb/smb" - "strings" "time" ) +type SmbConn struct { +} + func SmbScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return nil } - starttime := time.Now().Unix() - for _, user := range common.Userdict["smb"] { - for _, pass := range common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - flag, err := doWithTimeOut(info, user, pass) - if flag == true && err == nil { - var result string - if common.Domain != "" { - result = fmt.Sprintf("[+] SMB:%v:%v:%v\\%v %v", info.Host, info.Ports, common.Domain, user, pass) - } else { - result = fmt.Sprintf("[+] SMB:%v:%v:%v %v", info.Host, info.Ports, user, pass) - } - common.LogSuccess(result) - return err - } else { - errlog := fmt.Sprintf("[-] smb %v:%v %v %v %v", info.Host, 445, user, pass, err) - errlog = strings.Replace(errlog, "\n", "", -1) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["smb"])*len(common.Passwords)) * common.Timeout) { - return err - } - } - } - } - return tmperr + smbConn := &SmbConn{} + bt := common.InitBruteThread("smb", info, common.Timeout, smbConn) + return bt.Run() } func SmblConn(info *common.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) { @@ -67,7 +42,7 @@ func SmblConn(info *common.HostInfo, user string, pass string, signal chan struc return flag, err } -func doWithTimeOut(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func (s *SmbConn) Attack(info *common.HostInfo, user string, pass string, timeout int64) (flag bool, err error) { signal := make(chan struct{}) go func() { flag, err = SmblConn(info, user, pass, signal) @@ -75,7 +50,7 @@ func doWithTimeOut(info *common.HostInfo, user string, pass string) (flag bool, select { case <-signal: return flag, err - case <-time.After(time.Duration(common.Timeout) * time.Second): + case <-time.After(time.Duration(timeout) * time.Second): return false, errors.New("time out") } } diff --git a/Plugins/ssh.go b/Plugins/ssh.go index 7c8276d..f831cfa 100644 --- a/Plugins/ssh.go +++ b/Plugins/ssh.go @@ -7,41 +7,22 @@ import ( "golang.org/x/crypto/ssh" "io/ioutil" "net" - "strings" "time" ) +type SshConn struct { +} + func SshScan(info *common.HostInfo) (tmperr error) { if common.IsBrute { return } - starttime := time.Now().Unix() - for _, user := range common.Userdict["ssh"] { - for _, pass := range common.Passwords { - pass = strings.Replace(pass, "{user}", user, -1) - flag, err := SshConn(info, user, pass) - if flag == true && err == nil { - return err - } else { - errlog := fmt.Sprintf("[-] ssh %v:%v %v %v %v", info.Host, info.Ports, user, pass, err) - common.LogError(errlog) - tmperr = err - if common.CheckErrs(err) { - return err - } - if time.Now().Unix()-starttime > (int64(len(common.Userdict["ssh"])*len(common.Passwords)) * common.Timeout) { - return err - } - } - if common.SshKey != "" { - return err - } - } - } - return tmperr + sshConn := &SshConn{} + bt := common.InitBruteThread("ssh", info, common.Timeout, sshConn) + return bt.Run() } -func SshConn(info *common.HostInfo, user string, pass string) (flag bool, err error) { +func (*SshConn) Attack(info *common.HostInfo, user string, pass string, timeout int64) (flag bool, err error) { flag = false Host, Port, Username, Password := info.Host, info.Ports, user, pass Auth := []ssh.AuthMethod{} @@ -62,7 +43,7 @@ func SshConn(info *common.HostInfo, user string, pass string) (flag bool, err er config := &ssh.ClientConfig{ User: Username, Auth: Auth, - Timeout: time.Duration(common.Timeout) * time.Second, + Timeout: time.Duration(timeout) * time.Second, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }, diff --git a/common/Brute.go b/common/Brute.go new file mode 100644 index 0000000..92e60e8 --- /dev/null +++ b/common/Brute.go @@ -0,0 +1,114 @@ +package common + +import ( + "fmt" + "strings" + "sync" + "time" +) + +// 多线程暴力破解模块 + +type BruteList struct { + user string + pass string +} + +type BruteFunc interface { + Attack(info *HostInfo, user string, pass string, timeout int64) (flag bool, err error) +} + +type BruteThreader struct { + wg sync.WaitGroup + mutex sync.Mutex + signal bool + num int + bruteType string + bruteFunc BruteFunc + info *HostInfo + timeout int64 +} + +func InitBruteThread(bruteType string, info *HostInfo, timeout int64, bruteFunc BruteFunc) (b *BruteThreader) { + + bt := &BruteThreader{ + num: 0, + bruteType: bruteType, + bruteFunc: bruteFunc, + info: info, + timeout: timeout, + } + + return bt +} + +func (t *BruteThreader) Run() (tmperr error) { + + brList, total := t.generateData() + for i := 0; i < BruteThread; i++ { + t.wg.Add(1) + go t.worker(brList, total) + } + close(brList) + go func() { + t.wg.Wait() + t.signal = true + }() + for !t.signal { + } + + return tmperr +} + +func (t *BruteThreader) generateData() (data chan BruteList, total int) { + var all = len(Userdict[t.bruteType]) * len(Passwords) + brList := make(chan BruteList, all) + for _, user := range Userdict[t.bruteType] { + for _, pass := range Passwords { + pass = strings.Replace(pass, "{user}", user, -1) + brList <- BruteList{user, pass} + } + } + return brList, all +} + +func (t *BruteThreader) worker(brList chan BruteList, all int) (tmperr error) { + defer t.wg.Done() + starttime := time.Now().Unix() + for one := range brList { + if t.signal == true { + return + } + go t.incrNum(&t.num, &t.mutex) + user, pass := one.user, one.pass + flag, err := t.bruteFunc.Attack(t.info, user, pass, t.timeout) + if flag == true && err == nil { + var result string + if Domain != "" { + result = fmt.Sprintf("[+] %v:%v:%v:%v\\%v %v", t.bruteType, t.info.Host, t.info.Ports, Domain, user, pass) + } else { + result = fmt.Sprintf("[+] %v:%v:%v:%v %v", t.bruteType, t.info.Host, t.info.Ports, user, pass) + } + LogSuccess(result) + t.signal = true + return + } else { + errlog := fmt.Sprintf("[-] (%v/%v) %v %v:%v %v %v %v", t.num, all, t.bruteType, t.info.Host, t.info.Ports, user, pass, err) + LogError(errlog) + tmperr = err + if CheckErrs(err) { + return err + } + if time.Now().Unix()-starttime > (int64(all) * t.timeout) { + return err + } + } + } + return +} + +func (t *BruteThreader) incrNum(num *int, mutex *sync.Mutex) { + mutex.Lock() + *num = *num + 1 + mutex.Unlock() +} diff --git a/common/config.go b/common/config.go index 605e12e..36e9945 100644 --- a/common/config.go +++ b/common/config.go @@ -2,7 +2,7 @@ package common var version = "1.8.2" var Userdict = map[string][]string{ - "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, + "ftp": {"anonymous", "ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, "mysql": {"root", "mysql"}, "mssql": {"sa", "sql"}, "smb": {"administrator", "admin", "guest"},