mirror of
https://github.com/sairson/Yasso.git
synced 2026-02-13 15:26:15 +08:00
Yasso更新大改动,更新扫描方式,去除不常用功能,增加指纹和协议识别,修补bug等
This commit is contained in:
447
core/plugin/all.go
Normal file
447
core/plugin/all.go
Normal file
@@ -0,0 +1,447 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/config/banner"
|
||||
"Yasso/core/brute"
|
||||
"Yasso/core/logger"
|
||||
"Yasso/core/parse"
|
||||
"Yasso/pkg/webscan"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type scannerAll struct {
|
||||
ip string // 需要解析的ip列表或者文件
|
||||
port string // 需要解析的端口列表
|
||||
noAlive bool // 是否探测存活
|
||||
noBrute bool // 是否进行爆破
|
||||
userPath string // 爆破所需的user字典路径
|
||||
passPath string // 爆破所需的pass字典路径
|
||||
thread int // 扫描所需线程数
|
||||
timeout time.Duration // 爆破的超时数
|
||||
noService bool // 是否进行服务的探测(包括web)
|
||||
noVulcan bool // 是否进行主机层漏洞扫描
|
||||
}
|
||||
|
||||
func NewAllScanner(ip, port string, isAlive, isBrute bool, user, pass string, thread int, timeout time.Duration, noService bool, noVulcan bool) *scannerAll {
|
||||
return &scannerAll{
|
||||
ip: ip,
|
||||
port: port,
|
||||
noAlive: isAlive,
|
||||
noBrute: isBrute,
|
||||
userPath: user,
|
||||
passPath: pass,
|
||||
thread: thread,
|
||||
timeout: timeout,
|
||||
noService: noService,
|
||||
noVulcan: noVulcan,
|
||||
}
|
||||
}
|
||||
|
||||
// RunEnumeration 执行程序
|
||||
func (s *scannerAll) RunEnumeration() {
|
||||
banner.Banner()
|
||||
defer func() {
|
||||
logger.Info("Yasso scan complete")
|
||||
}()
|
||||
if s.ip == "" {
|
||||
logger.Fatal("need ips to parse")
|
||||
return
|
||||
}
|
||||
// 1. 解析用户的ip列表
|
||||
ips, err := parse.HandleIps(s.ip)
|
||||
if err != nil {
|
||||
logger.Fatal("parse ips has an error", err.Error())
|
||||
return
|
||||
}
|
||||
// 2.解析用户的port列表
|
||||
var ports []int
|
||||
if s.port == "" {
|
||||
ports = config.DefaultScannerPort
|
||||
} else {
|
||||
ports, err = parse.HandlePorts(s.port)
|
||||
if err != nil {
|
||||
logger.Fatal("parse ports has an error", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
var user []string
|
||||
var pass []string
|
||||
// 3.解析用户的字典,没有字典的话,就采用默认的字典
|
||||
if s.userPath != "" {
|
||||
user, err = parse.ReadFile(s.userPath)
|
||||
if err != nil {
|
||||
logger.Fatal("parse user dict file has an error")
|
||||
return
|
||||
}
|
||||
}
|
||||
if s.passPath != "" {
|
||||
pass, err = parse.ReadFile(s.passPath)
|
||||
if err != nil {
|
||||
logger.Fatal("parse user dict file has an error")
|
||||
return
|
||||
}
|
||||
return
|
||||
} else {
|
||||
pass = config.PassDict
|
||||
}
|
||||
|
||||
// 4. 解析完成后,通过isAlive判断存活,这里采用并发方式
|
||||
var wg sync.WaitGroup
|
||||
var mutex sync.Mutex
|
||||
var ipChannel = make(chan string, 1000)
|
||||
var port7 []int = []int{139, 445, 135, 22, 23, 21, 3389}
|
||||
var ipAlive []string
|
||||
if s.noAlive == false {
|
||||
for i := 0; i < s.thread; i++ {
|
||||
wg.Add(1)
|
||||
go func(ctx context.Context) {
|
||||
defer wg.Done()
|
||||
for ip := range ipChannel {
|
||||
if ping(ip) == true {
|
||||
logger.Info(fmt.Sprintf("%v is alive (ping)", ip))
|
||||
logger.JSONSave(ip, logger.HostSave) // json存储
|
||||
ipAlive = append(ipAlive, ip)
|
||||
} else {
|
||||
// 这里尝试探测7个常用端口,如果有一个开放,则证明ip也是存活网段
|
||||
for _, p := range port7 {
|
||||
if tcpConn(ip, p) == true {
|
||||
logger.Info(fmt.Sprintf("%v is alive (tcp)", ip))
|
||||
logger.JSONSave(ip, logger.HostSave) // json存储
|
||||
ipAlive = append(ipAlive, ip)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}(context.Background())
|
||||
}
|
||||
for _, ip := range ips {
|
||||
// 带有端口的不进行扫描,直接加入
|
||||
if strings.Contains(ip, ":") {
|
||||
ipAlive = append(ipAlive, ip)
|
||||
continue
|
||||
} else {
|
||||
ipChannel <- ip
|
||||
}
|
||||
}
|
||||
close(ipChannel) // 防止死锁
|
||||
wg.Wait()
|
||||
} else {
|
||||
ipAlive = ips
|
||||
}
|
||||
// 5.扫描完成后,做端口扫描,同样是高并发
|
||||
ipChannel = make(chan string, 1000) // 二次复用
|
||||
var portAlive = make(map[string][]int)
|
||||
for i := 0; i < s.thread; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for ip := range ipChannel {
|
||||
// 做端口扫描
|
||||
mutex.Lock()
|
||||
p := NewRunner(ports, ip, s.thread, tcpConn).RunEnumeration()
|
||||
portAlive[ip] = append(portAlive[ip], p...)
|
||||
logger.JSONSave(ip, logger.PortSave, p) // 存储可用端口
|
||||
mutex.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
for _, ip := range ipAlive {
|
||||
// 带有端口的不进行扫描,直接加入
|
||||
if strings.Count(ip, ":") == 1 {
|
||||
t := strings.Split(ip, ":")
|
||||
p, err := strconv.Atoi(t[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
portAlive[t[0]] = append(portAlive[t[0]], p)
|
||||
continue
|
||||
} else {
|
||||
ipChannel <- ip
|
||||
}
|
||||
}
|
||||
close(ipChannel) // 防止死锁
|
||||
wg.Wait()
|
||||
// 6. 端口扫描结束,根据用户指示,判断是否进行爆破
|
||||
for k, v := range portAlive {
|
||||
// 遍历每一个ip的每一个端口看看属于哪一个服务
|
||||
v = parse.RemoveDuplicate(v) // 去个重
|
||||
sort.Ints(v) // 排序
|
||||
for _, p := range v {
|
||||
switch p {
|
||||
case 22:
|
||||
if s.noService == false {
|
||||
information := VersionSSH(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
})
|
||||
logger.JSONSave(k, logger.InformationSave, "ssh", information)
|
||||
}
|
||||
brute.NewBrute(user, pass, SshConnByUser, "ssh", config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, s.thread, s.noBrute, "").RunEnumeration()
|
||||
case 21:
|
||||
// 未授权
|
||||
if ok, _ := FtpConn(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, "", ""); ok {
|
||||
continue
|
||||
}
|
||||
// 爆破ftp
|
||||
brute.NewBrute(user, pass, FtpConn, "ftp", config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, s.thread, s.noBrute, "").RunEnumeration()
|
||||
case 445:
|
||||
// 未授权
|
||||
if ok, _ := SmbConn(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, "administrator", ""); ok {
|
||||
logger.Info(fmt.Sprintf("smb %s unauthorized", k))
|
||||
// 未授权,用户名密码均为null
|
||||
logger.JSONSave(k, logger.WeakPassSave, "smb", map[string]string{"null": "null"})
|
||||
continue
|
||||
}
|
||||
brute.NewBrute(user, pass, SmbConn, "smb", config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, s.thread, s.noBrute, "").RunEnumeration()
|
||||
case 1433:
|
||||
if s.noService == false {
|
||||
ok, information := VersionMssql(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
})
|
||||
// 存在ok
|
||||
if ok {
|
||||
logger.JSONSave(k, logger.InformationSave, "mssql", information)
|
||||
}
|
||||
}
|
||||
brute.NewBrute(user, pass, MssqlConn, "mssql", config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, s.thread, s.noBrute, "").RunEnumeration()
|
||||
case 2181:
|
||||
if s.noService == false {
|
||||
if ok, _ := ZookeeperConn(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, "", ""); ok {
|
||||
// 未授权
|
||||
logger.JSONSave(k, logger.WeakPassSave, "zookeeper", map[string]string{"null": "null"})
|
||||
continue
|
||||
}
|
||||
}
|
||||
case 3306:
|
||||
// 未授权
|
||||
if _, ok, _ := MySQLConn(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, "", ""); ok {
|
||||
logger.Info(fmt.Sprintf("mysql %s unauthorized", k))
|
||||
// 未授权,用户名密码均为null
|
||||
logger.JSONSave(k, logger.WeakPassSave, "mysql", map[string]string{"null": "null"})
|
||||
continue
|
||||
} else {
|
||||
brute.NewBrute(user, pass, MySQLConn, "mysql", config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, s.thread, s.noBrute, "").RunEnumeration()
|
||||
}
|
||||
case 3389:
|
||||
// 仅探测主机版本
|
||||
if s.noService == false {
|
||||
if ok, information := VersionRdp(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, "", ""); ok {
|
||||
// 版本
|
||||
logger.JSONSave(k, logger.InformationSave, "rdp", information)
|
||||
continue
|
||||
}
|
||||
}
|
||||
case 6379:
|
||||
if s.noService == false {
|
||||
if _, ok, _ := RedisUnAuthConn(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, "", ""); ok {
|
||||
logger.JSONSave(k, logger.WeakPassSave, "redis", map[string]string{"null": "null"})
|
||||
continue
|
||||
}
|
||||
}
|
||||
brute.NewBrute(user, pass, RedisAuthConn, "redis", config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, s.thread, s.noBrute, "").RunEnumeration()
|
||||
|
||||
case 5432:
|
||||
brute.NewBrute(user, pass, PostgreConn, "postgres", config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, s.thread, s.noBrute, "").RunEnumeration()
|
||||
case 5985:
|
||||
brute.NewBrute(user, pass, WinRMAuth, "winrm", config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, s.thread, s.noBrute, "").RunEnumeration()
|
||||
case 11211:
|
||||
if s.noService == false {
|
||||
if ok, _ := MemcacheConn(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, "", ""); ok {
|
||||
logger.JSONSave(k, logger.WeakPassSave, "memcache", map[string]string{"null": "null"})
|
||||
continue
|
||||
}
|
||||
}
|
||||
case 27017:
|
||||
if s.noService == false {
|
||||
if ok, _ := MongoUnAuth(config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, "", ""); ok {
|
||||
logger.JSONSave(k, logger.WeakPassSave, "mongodb", map[string]string{"null": "null"})
|
||||
break
|
||||
}
|
||||
}
|
||||
brute.NewBrute(user, pass, MongoAuth, "mongodb", config.ServiceConn{
|
||||
Hostname: k,
|
||||
Port: p,
|
||||
Timeout: s.timeout,
|
||||
}, s.thread, s.noBrute, "").RunEnumeration()
|
||||
default:
|
||||
if s.noService == false {
|
||||
webscan.DisMapConn(k, p, s.timeout)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.noService == false {
|
||||
// 8. 进行win服务扫描扫描
|
||||
ipChannel = make(chan string, 1000) // 第四次复用
|
||||
for i := 0; i < s.thread; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for ip := range ipChannel {
|
||||
mutex.Lock()
|
||||
func(ip string) {
|
||||
ok, information := NbnsScanConn(ip, 137, s.timeout)
|
||||
if ok {
|
||||
logger.JSONSave(ip, logger.InformationSave, "netbios", information)
|
||||
}
|
||||
}(ip)
|
||||
func(ip string) {
|
||||
ok, information := SmbScanConn(ip, 445, s.timeout)
|
||||
if ok {
|
||||
logger.JSONSave(ip, logger.InformationSave, "smb", information)
|
||||
}
|
||||
}(ip)
|
||||
func(ip string) {
|
||||
ok, information := OxidScanConn(ip, 135, s.timeout)
|
||||
if ok {
|
||||
logger.JSONSave(ip, logger.InformationSave, "oxid", information)
|
||||
}
|
||||
}(ip)
|
||||
mutex.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
for _, ip := range ipAlive {
|
||||
// 带有端口的不进行扫描,直接加入
|
||||
if strings.Count(ip, ":") == 1 && (strings.Split(ip, ":")[0] != strconv.Itoa(139) || strings.Split(ip, ":")[0] != strconv.Itoa(135) || strings.Split(ip, ":")[0] != strconv.Itoa(445)) {
|
||||
continue
|
||||
} else if strings.Split(ip, ":")[0] == strconv.Itoa(139) || strings.Split(ip, ":")[0] == strconv.Itoa(135) || strings.Split(ip, ":")[0] == strconv.Itoa(445) {
|
||||
ipChannel <- strings.Split(ip, ":")[0]
|
||||
} else {
|
||||
ipChannel <- ip
|
||||
}
|
||||
}
|
||||
close(ipChannel) // 防止死锁
|
||||
wg.Wait() // 等待结束
|
||||
|
||||
}
|
||||
// 7. 进行主机漏洞扫描
|
||||
if s.noVulcan == false {
|
||||
ipChannel = make(chan string, 1000) // 第四次复用
|
||||
for i := 0; i < s.thread; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for ip := range ipChannel {
|
||||
// 做端口扫描
|
||||
mutex.Lock()
|
||||
func() {
|
||||
ok := Ms17010Conn(config.ServiceConn{
|
||||
Hostname: ip,
|
||||
Port: 445,
|
||||
Domain: "",
|
||||
Timeout: s.timeout,
|
||||
PublicKey: "",
|
||||
})
|
||||
if ok {
|
||||
logger.JSONSave(ip, logger.VulnerabilitySave, "MS17010")
|
||||
}
|
||||
}()
|
||||
func() {
|
||||
ok := SmbGhostConn(config.ServiceConn{
|
||||
Hostname: ip,
|
||||
Port: 445,
|
||||
Domain: "",
|
||||
Timeout: s.timeout,
|
||||
PublicKey: "",
|
||||
})
|
||||
if ok {
|
||||
logger.JSONSave(ip, logger.VulnerabilitySave, "CVE-2020-0796")
|
||||
}
|
||||
}()
|
||||
mutex.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
for _, ip := range ipAlive {
|
||||
// 带有端口的不进行扫描,直接加入
|
||||
if strings.Count(ip, ":") == 1 && strings.Split(ip, ":")[0] != strconv.Itoa(445) {
|
||||
continue
|
||||
} else if strings.Split(ip, ":")[0] == strconv.Itoa(445) {
|
||||
ipChannel <- strings.Split(ip, ":")[0]
|
||||
} else {
|
||||
ipChannel <- ip
|
||||
}
|
||||
}
|
||||
|
||||
close(ipChannel) // 防止死锁
|
||||
wg.Wait() // 等待结束
|
||||
}
|
||||
logger.LoggerSave()
|
||||
}
|
||||
278
core/plugin/brute.go
Normal file
278
core/plugin/brute.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/config/banner"
|
||||
"Yasso/core/brute"
|
||||
"Yasso/core/logger"
|
||||
"Yasso/core/parse"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var BurpMap = map[string]interface{}{
|
||||
"ssh": SshConnByUser,
|
||||
"mongodb": MongoAuth,
|
||||
"mysql": MySQLConn,
|
||||
"mssql": MssqlConn,
|
||||
"rdp": RdpConn,
|
||||
"redis": RedisAuthConn,
|
||||
"ftp": FtpConn,
|
||||
"smb": SmbConn,
|
||||
"winrm": WinRMAuth,
|
||||
"postgres": PostgreConn,
|
||||
}
|
||||
|
||||
func BruteService(user, pass string, ipd string, module string, thread int, timeout time.Duration, isAlive bool) {
|
||||
banner.Banner()
|
||||
defer func() {
|
||||
logger.Info("brute service complete")
|
||||
}()
|
||||
// 先解析传过来的ips列表
|
||||
if ipd == "" {
|
||||
logger.Fatal("need ips to parse")
|
||||
return
|
||||
}
|
||||
ips, err := parse.HandleIps(ipd)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var userDic, passDic []string
|
||||
if user != "" {
|
||||
userDic, err = parse.ReadFile(user)
|
||||
}
|
||||
if pass != "" {
|
||||
passDic, err = parse.ReadFile(pass)
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("dic file is not found")
|
||||
return
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
var ipChannel = make(chan string, 1000)
|
||||
var ipAlive []string
|
||||
if isAlive == true {
|
||||
for i := 0; i < thread; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for ip := range ipChannel {
|
||||
if ping(ip) == true {
|
||||
logger.Info(fmt.Sprintf("%v is alive (ping)", ip))
|
||||
ipAlive = append(ipAlive, ip)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
for _, ip := range ips {
|
||||
// 带有端口的不进行扫描,直接加入
|
||||
if strings.Contains(ip, ":") {
|
||||
ipAlive = append(ipAlive, ip)
|
||||
continue
|
||||
} else {
|
||||
ipChannel <- ip
|
||||
}
|
||||
}
|
||||
close(ipChannel) // 防止死锁
|
||||
wg.Wait()
|
||||
} else {
|
||||
ipAlive = ips
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("start brute service %v", strings.Split(module, ",")))
|
||||
// 这里获取到了ip列表,格式各种各样 www.baidu.com:80 192.168.248.1 192.168.248.1:445
|
||||
for _, each := range strings.Split(module, ",") { // 遍历每一个服务
|
||||
// 这里获取到对应的服务和端口
|
||||
service := strings.Split(each, ":")
|
||||
if len(service) >= 3 || len(service) <= 0 {
|
||||
logger.Fatal("brute service format is error")
|
||||
break
|
||||
}
|
||||
switch service[0] {
|
||||
case "ssh":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 22
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "ssh", BurpMap["ssh"])
|
||||
case "mongo":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 27017
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "mongodb", BurpMap["mongodb"])
|
||||
case "mysql":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 3306
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "mysql", BurpMap["mysql"])
|
||||
case "rdp":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 3389
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "rdp", BurpMap["rdp"])
|
||||
case "redis":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 6379
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "redis", BurpMap["redis"])
|
||||
case "smb":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 445
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "smb", BurpMap["smb"])
|
||||
case "winrm":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 5985
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "winrm", BurpMap["winrm"])
|
||||
case "postgres":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 5432
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "postgres", BurpMap["postgres"])
|
||||
case "mssql":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 1433
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "mssql", BurpMap["mssql"])
|
||||
case "ftp":
|
||||
var p int
|
||||
if len(service) == 2 {
|
||||
// 带端口,采用用户自带端口
|
||||
p, err = strconv.Atoi(service[1])
|
||||
} else {
|
||||
// 不带端口,采用默认
|
||||
p = 21
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal("port number useless")
|
||||
break
|
||||
}
|
||||
run(ipAlive, p, userDic, passDic, timeout, thread, "ftp", BurpMap["ftp"])
|
||||
default:
|
||||
logger.Fatal(fmt.Sprintf("not found service %s", service[0]))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 执行爆破的函数
|
||||
func run(ips []string, port int, user, pass []string, timeout time.Duration, thread int, service string, method interface{}) {
|
||||
var ipChannel = make(chan string, 1000) // 二次复用
|
||||
var mutex sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < thread; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for ip := range ipChannel {
|
||||
// 这里获取到每一个ip
|
||||
mutex.Lock()
|
||||
brute.NewBrute(user, pass, method, service, config.ServiceConn{
|
||||
Hostname: ip,
|
||||
Port: port,
|
||||
Timeout: time.Duration(timeout),
|
||||
}, thread, false, "").RunEnumeration()
|
||||
mutex.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
for _, ip := range ips {
|
||||
// 带有端口的不进行扫描,直接直接跳过
|
||||
if strings.Count(ip, ":") == 1 {
|
||||
if strings.Split(ip, ":")[1] == strconv.Itoa(port) { // 带端口,且端口和需要爆破的端口号相同
|
||||
ipChannel <- strings.Split(ip, ":")[0]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
ipChannel <- ip
|
||||
}
|
||||
}
|
||||
close(ipChannel) // 防止死锁
|
||||
wg.Wait()
|
||||
}
|
||||
140
core/plugin/eternalblue.go
Normal file
140
core/plugin/eternalblue.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
//TODO:MS17010
|
||||
|
||||
var (
|
||||
negotiateProtocolRequest, _ = hex.DecodeString("00000085ff534d4272000000001853c00000000000000000000000000000fffe00004000006200025043204e4554574f524b2050524f4752414d20312e3000024c414e4d414e312e30000257696e646f777320666f7220576f726b67726f75707320332e316100024c4d312e325830303200024c414e4d414e322e3100024e54204c4d20302e313200")
|
||||
sessionSetupRequest, _ = hex.DecodeString("00000088ff534d4273000000001807c00000000000000000000000000000fffe000040000dff00880004110a000000000000000100000000000000d40000004b000000000000570069006e0064006f007700730020003200300030003000200032003100390035000000570069006e0064006f007700730020003200300030003000200035002e0030000000")
|
||||
treeConnectRequest, _ = hex.DecodeString("00000060ff534d4275000000001807c00000000000000000000000000000fffe0008400004ff006000080001003500005c005c003100390032002e003100360038002e003100370035002e003100320038005c00490050004300240000003f3f3f3f3f00")
|
||||
transNamedPipeRequest, _ = hex.DecodeString("0000004aff534d42250000000018012800000000000000000000000000088ea3010852981000000000ffffffff0000000000000000000000004a0000004a0002002300000007005c504950455c00")
|
||||
trans2SessionSetupRequest, _ = hex.DecodeString("0000004eff534d4232000000001807c00000000000000000000000000008fffe000841000f0c0000000100000000000000a6d9a40000000c00420000004e0001000e000d0000000000000000000000000000")
|
||||
)
|
||||
|
||||
func Ms17010Conn(info config.ServiceConn) bool {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
status, err := RequestMs17010(conn, info.Hostname)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if status == true {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func RequestMs17010(conn net.Conn, ip string) (bool, error) {
|
||||
defer conn.Close()
|
||||
err := conn.SetDeadline(time.Now().Add(500 * time.Millisecond))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, err = conn.Write(negotiateProtocolRequest)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
reply := make([]byte, 1024)
|
||||
if n, err := conn.Read(reply); err != nil || n < 36 {
|
||||
return false, err
|
||||
}
|
||||
if binary.LittleEndian.Uint32(reply[9:13]) != 0 {
|
||||
return false, err
|
||||
}
|
||||
_, err = conn.Write(sessionSetupRequest)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
n, err := conn.Read(reply)
|
||||
if err != nil || n < 36 {
|
||||
return false, err
|
||||
}
|
||||
if binary.LittleEndian.Uint32(reply[9:13]) != 0 {
|
||||
// status != 0
|
||||
//fmt.Printf("can't determine whether %s is vulnerable or not\n", ip)
|
||||
return false, fmt.Errorf("can't determine whether %s is vulnerable or not\n", ip)
|
||||
}
|
||||
|
||||
// extract OS info
|
||||
var os string
|
||||
sessionSetupResponse := reply[36:n]
|
||||
if wordCount := sessionSetupResponse[0]; wordCount != 0 {
|
||||
// find byte count
|
||||
byteCount := binary.LittleEndian.Uint16(sessionSetupResponse[7:9])
|
||||
if n != int(byteCount)+45 {
|
||||
logger.Fatal("invalid session setup AndX response")
|
||||
} else {
|
||||
// two continous null bytes indicates end of a unicode string
|
||||
for i := 10; i < len(sessionSetupResponse)-1; i++ {
|
||||
if sessionSetupResponse[i] == 0 && sessionSetupResponse[i+1] == 0 {
|
||||
os = string(sessionSetupResponse[10:i])
|
||||
os = strings.Replace(os, string([]byte{0x00}), "", -1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
userID := reply[32:34]
|
||||
treeConnectRequest[32] = userID[0]
|
||||
treeConnectRequest[33] = userID[1]
|
||||
// TODO change the ip in tree path though it doesn't matter
|
||||
_, err = conn.Write(treeConnectRequest)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if n, err := conn.Read(reply); err != nil || n < 36 {
|
||||
return false, err
|
||||
}
|
||||
|
||||
treeID := reply[28:30]
|
||||
transNamedPipeRequest[28] = treeID[0]
|
||||
transNamedPipeRequest[29] = treeID[1]
|
||||
transNamedPipeRequest[32] = userID[0]
|
||||
transNamedPipeRequest[33] = userID[1]
|
||||
|
||||
conn.Write(transNamedPipeRequest)
|
||||
if n, err := conn.Read(reply); err != nil || n < 36 {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if reply[9] == 0x05 && reply[10] == 0x02 && reply[11] == 0x00 && reply[12] == 0xc0 {
|
||||
//fmt.Printf("%s\tMS17-010\t(%s)\n", ip, os)
|
||||
//if runtime.GOOS=="windows" {fmt.Printf("%s\tMS17-010\t(%s)\n", ip, os)
|
||||
//} else{fmt.Printf("\033[33m%s\tMS17-010\t(%s)\033[0m\n", ip, os)}
|
||||
//color.Magenta("%s\tMS17-010\t(%s)\n", ip, os)
|
||||
logger.Info(fmt.Sprintf("%v Find MS17010 (%s)", ip, os))
|
||||
// detect present of DOUBLEPULSAR SMB implant
|
||||
trans2SessionSetupRequest[28] = treeID[0]
|
||||
trans2SessionSetupRequest[29] = treeID[1]
|
||||
trans2SessionSetupRequest[32] = userID[0]
|
||||
trans2SessionSetupRequest[33] = userID[1]
|
||||
|
||||
conn.Write(trans2SessionSetupRequest)
|
||||
|
||||
if n, err := conn.Read(reply); err != nil || n < 36 {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if reply[34] == 0x51 {
|
||||
fmt.Printf("DOUBLEPULSAR SMB IMPLANT in %s\n", ip)
|
||||
}
|
||||
return true, nil
|
||||
} else {
|
||||
fmt.Printf("%s\t \t(%s)\n", ip, os)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
27
core/plugin/ftp.go
Normal file
27
core/plugin/ftp.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"fmt"
|
||||
"github.com/jlaffaye/ftp"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func FtpConn(info config.ServiceConn, user, pass string) (bool, error) {
|
||||
var flag = false
|
||||
c, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), time.Duration(info.Timeout))
|
||||
|
||||
conn, err := ftp.Dial(fmt.Sprintf("%v:%v", info.Hostname, info.Port), ftp.DialWithNetConn(c))
|
||||
if err == nil {
|
||||
err = conn.Login(user, pass)
|
||||
if err == nil {
|
||||
if pass == "" {
|
||||
logger.Success(fmt.Sprintf("ftp %v unauthorized", fmt.Sprintf("%v:%v", info.Hostname, info.Port)))
|
||||
}
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
return flag, err
|
||||
}
|
||||
78
core/plugin/grdp.go
Normal file
78
core/plugin/grdp.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"Yasso/pkg/grdp"
|
||||
"Yasso/pkg/grdp/glog"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func RdpConn(info config.ServiceConn, user, pass string) (bool, error) {
|
||||
var err error
|
||||
var host = fmt.Sprintf("%v:%v", info.Hostname, info.Port)
|
||||
g := grdp.NewClient(host, glog.NONE)
|
||||
//SSL协议登录测试
|
||||
err = g.LoginForSSL(info.Domain, user, pass)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if err.Error() != "PROTOCOL_RDP" {
|
||||
return false, err
|
||||
}
|
||||
//RDP协议登录测试
|
||||
err = g.LoginForRDP(info.Domain, user, pass)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func VersionRdp(info config.ServiceConn, user, pass string) (bool, string) {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
msg := "\x03\x00\x00\x2b\x26\xe0\x00\x00\x00\x00\x00\x43\x6f\x6f\x6b\x69\x65\x3a\x20\x6d\x73\x74\x73\x68\x61\x73\x68\x3d\x75\x73\x65\x72\x30\x0d\x0a\x01\x00\x08\x00\x00\x00\x00\x00"
|
||||
_, err = conn.Write([]byte(msg))
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
reply := make([]byte, 256)
|
||||
_, _ = conn.Read(reply)
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
var buffer [256]byte
|
||||
if bytes.Equal(reply[:], buffer[:]) {
|
||||
return false, ""
|
||||
} else if hex.EncodeToString(reply[0:8]) != "030000130ed00000" {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
os := map[string]string{}
|
||||
os["030000130ed000001234000209080000000000"] = "Windows 7/Windows Server 2008 R2"
|
||||
os["030000130ed000001234000200080000000000"] = "Windows 7/Windows Server 2008"
|
||||
os["030000130ed000001234000201080000000000"] = "Windows Server 2008 R2"
|
||||
os["030000130ed000001234000207080000000000"] = "Windows 8/Windows server 2012"
|
||||
os["030000130ed00000123400020f080000000000"] = "Windows 8.1/Windows Server 2012 R2"
|
||||
os["030000130ed000001234000300080001000000"] = "Windows 10/Windows Server 2016"
|
||||
os["030000130ed000001234000300080005000000"] = "Windows 10/Windows 11/Windows Server 2019"
|
||||
var banner string
|
||||
for k, v := range os {
|
||||
if k == hex.EncodeToString(reply[0:19]) {
|
||||
banner = v
|
||||
logger.Info(fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner))
|
||||
return true, fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner)
|
||||
}
|
||||
}
|
||||
banner = hex.EncodeToString(reply[0:19])
|
||||
_ = reply
|
||||
logger.Info(fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner))
|
||||
return true, fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner)
|
||||
}
|
||||
88
core/plugin/icmp.go
Normal file
88
core/plugin/icmp.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ping(ip string) bool {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = exec.Command("cmd", "/c", "ping -n 1 -w 1 "+ip+" && echo true || echo false")
|
||||
case "linux":
|
||||
cmd = exec.Command("/bin/bash", "-c", "ping -c 1 -w 1 "+ip+" >/dev/null && echo true || echo false")
|
||||
case "darwin":
|
||||
cmd = exec.Command("/bin/bash", "-c", "ping -c 1 "+ip+" >/dev/null && echo true || echo false")
|
||||
default:
|
||||
cmd = exec.Command("/bin/bash", "-c", "ping -c 1"+ip+" >/dev/null && echo true || echo false")
|
||||
}
|
||||
info := bytes.Buffer{}
|
||||
cmd.Stdout = &info
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if err = cmd.Wait(); err != nil {
|
||||
return false
|
||||
} else {
|
||||
if strings.Contains(info.String(), "true") {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func icmp(host string) bool {
|
||||
conn, err := net.DialTimeout("ip4:icmp", host, 1*time.Second)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
if err := conn.SetDeadline(time.Now().Add(1 * time.Second)); err != nil {
|
||||
return false
|
||||
}
|
||||
msg := packet(host)
|
||||
if _, err := conn.Write(msg); err != nil {
|
||||
return false
|
||||
}
|
||||
var receive = make([]byte, 60)
|
||||
if _, err := conn.Read(receive); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func packet(host string) []byte {
|
||||
var msg = make([]byte, 40)
|
||||
msg[0] = 8
|
||||
msg[1] = 0
|
||||
msg[2] = 0
|
||||
msg[3] = 0
|
||||
msg[4], msg[5] = host[0], host[1]
|
||||
msg[6], msg[7] = byte(1>>8), byte(1&255)
|
||||
msg[2] = byte(checksum(msg[0:40]) >> 8)
|
||||
msg[3] = byte(checksum(msg[0:40]) & 255)
|
||||
return msg
|
||||
}
|
||||
|
||||
func checksum(msg []byte) uint16 {
|
||||
var sum = 0
|
||||
var length = len(msg)
|
||||
for i := 0; i < length-1; i += 2 {
|
||||
sum += int(msg[i])*256 + int(msg[i+1])
|
||||
}
|
||||
if length%2 == 1 {
|
||||
sum += int(msg[length-1]) * 256
|
||||
}
|
||||
sum = (sum >> 16) + (sum & 0xffff)
|
||||
sum = sum + (sum >> 16)
|
||||
return uint16(^sum)
|
||||
}
|
||||
39
core/plugin/memcache.go
Normal file
39
core/plugin/memcache.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func MemcacheConn(info config.ServiceConn, user, pass string) (bool, error) {
|
||||
client, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer func() {
|
||||
if client != nil {
|
||||
client.Close()
|
||||
}
|
||||
}()
|
||||
if err == nil {
|
||||
err = client.SetDeadline(time.Now().Add(time.Duration(info.Timeout)))
|
||||
if err == nil {
|
||||
_, err = client.Write([]byte("stats\n")) //Set the key randomly to prevent the key on the server from being overwritten
|
||||
if err == nil {
|
||||
reply := make([]byte, 1024)
|
||||
n, err := client.Read(reply)
|
||||
if err == nil {
|
||||
if bytes.Contains(reply[:n], []byte("STAT")) {
|
||||
logger.Success(fmt.Sprintf("Memcached %s unauthorized", fmt.Sprintf("%s:%v", info.Hostname, info.Port)))
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
83
core/plugin/mongo.go
Normal file
83
core/plugin/mongo.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"fmt"
|
||||
"gopkg.in/mgo.v2"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func MongoAuth(info config.ServiceConn, user, pass string) (*mgo.Session, bool, error) {
|
||||
|
||||
conf := &mgo.DialInfo{
|
||||
Dial: func(addr net.Addr) (net.Conn, error) {
|
||||
return net.DialTimeout("tcp", addr.String(), info.Timeout)
|
||||
},
|
||||
Addrs: []string{fmt.Sprintf("%s:%d", info.Hostname, info.Port)},
|
||||
Timeout: info.Timeout,
|
||||
Database: "test",
|
||||
Source: "admin",
|
||||
Username: user,
|
||||
Password: pass,
|
||||
PoolLimit: 4096,
|
||||
Direct: false,
|
||||
}
|
||||
db, err := mgo.DialWithInfo(conf)
|
||||
if err == nil {
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return db, true, nil
|
||||
}
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
func MongoUnAuth(info config.ServiceConn, user, pass string) (bool, error) {
|
||||
var flag = false
|
||||
data1 := []byte{58, 0, 0, 0, 167, 65, 0, 0, 0, 0, 0, 0, 212, 7, 0, 0, 0, 0, 0, 0, 97, 100, 109, 105, 110, 46, 36, 99, 109, 100, 0, 0, 0, 0, 0, 255, 255, 255, 255, 19, 0, 0, 0, 16, 105, 115, 109, 97, 115, 116, 101, 114, 0, 1, 0, 0, 0, 0}
|
||||
data2 := []byte{72, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 212, 7, 0, 0, 0, 0, 0, 0, 97, 100, 109, 105, 110, 46, 36, 99, 109, 100, 0, 0, 0, 0, 0, 1, 0, 0, 0, 33, 0, 0, 0, 2, 103, 101, 116, 76, 111, 103, 0, 16, 0, 0, 0, 115, 116, 97, 114, 116, 117, 112, 87, 97, 114, 110, 105, 110, 103, 115, 0, 0}
|
||||
connString := fmt.Sprintf("%s:%v", info.Hostname, info.Port)
|
||||
conn, err := net.DialTimeout("tcp", connString, info.Timeout)
|
||||
defer func() {
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = conn.SetReadDeadline(time.Now().Add(time.Duration(info.Timeout)))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, err = conn.Write(data1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
reply := make([]byte, 1024)
|
||||
count, err := conn.Read(reply)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
text := string(reply[0:count])
|
||||
if strings.Contains(text, "ismaster") {
|
||||
_, err = conn.Write(data2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
count, err := conn.Read(reply)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
text := string(reply[0:count])
|
||||
if strings.Contains(text, "totalLinesWritten") {
|
||||
flag = true
|
||||
logger.Success(fmt.Sprintf("Mongodb %v unauthorized", info.Hostname))
|
||||
}
|
||||
}
|
||||
return flag, nil
|
||||
}
|
||||
78
core/plugin/mssql.go
Normal file
78
core/plugin/mssql.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
_ "github.com/denisenkom/go-mssqldb"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func MssqlConn(info config.ServiceConn, user, pass string) (*sql.DB, bool, error) {
|
||||
var flag = false
|
||||
db, err := sql.Open("mssql", fmt.Sprintf("sqlserver://%v:%v@%v:%v/?connection&timeout=%v&encrypt=disable", user, pass, info.Hostname, info.Port, info.Timeout))
|
||||
if err == nil {
|
||||
db.SetConnMaxLifetime(time.Duration(info.Timeout))
|
||||
db.SetConnMaxIdleTime(time.Duration(info.Timeout))
|
||||
db.SetMaxIdleConns(0)
|
||||
err = db.Ping()
|
||||
if err == nil {
|
||||
flag = true
|
||||
return db, flag, nil
|
||||
}
|
||||
}
|
||||
return db, flag, err
|
||||
}
|
||||
|
||||
func VersionMssql(info config.ServiceConn) (bool, string) {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
msg := "\x12\x01\x00\x34\x00\x00\x00\x00\x00\x00\x15\x00\x06\x01\x00\x1b\x00\x01\x02\x00\x1c\x00\x0c\x03\x00\x28\x00\x04\xff\x08\x00\x01\x55\x00\x00\x02\x4d\x53\x53\x51\x4c\x53\x65\x72\x76\x65\x72\x00\x00\x00\x31\x32"
|
||||
_, err = conn.Write([]byte(msg))
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
reply := make([]byte, 256)
|
||||
_, _ = conn.Read(reply)
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
var buffer [256]byte
|
||||
if bytes.Equal(reply[:], buffer[:]) {
|
||||
return false, ""
|
||||
} else if hex.EncodeToString(reply[0:4]) != "04010025" {
|
||||
return false, ""
|
||||
}
|
||||
v, status := getVersion(reply)
|
||||
if status {
|
||||
logger.Info(fmt.Sprintf("%s:%v [version:%v][mssql]", info.Hostname, info.Port, v))
|
||||
return true, fmt.Sprintf("%s:%v [version:%v]", info.Hostname, info.Port, v)
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func getVersion(reply []byte) (string, bool) {
|
||||
m, err := strconv.ParseUint(hex.EncodeToString(reply[29:30]), 16, 32)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
s, err := strconv.ParseUint(hex.EncodeToString(reply[30:31]), 16, 32)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
r, err := strconv.ParseUint(hex.EncodeToString(reply[31:33]), 16, 32)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
v := fmt.Sprintf("%d.%d.%d", m, s, r)
|
||||
return v, true
|
||||
}
|
||||
25
core/plugin/mysql.go
Normal file
25
core/plugin/mysql.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"time"
|
||||
)
|
||||
|
||||
func MySQLConn(info config.ServiceConn, user, pass string) (*sql.DB, bool, error) {
|
||||
var flag = false
|
||||
address := fmt.Sprintf("%v:%v@tcp(%v:%v)/mysql?charset=utf8&timeout=%v", user, pass, info.Hostname, info.Port, info.Timeout)
|
||||
db, err := sql.Open("mysql", address)
|
||||
if err == nil {
|
||||
db.SetConnMaxLifetime(time.Duration(info.Timeout))
|
||||
db.SetConnMaxIdleTime(time.Duration(info.Timeout))
|
||||
//defer db.Close()
|
||||
err = db.Ping()
|
||||
if err == nil {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
return db, flag, err
|
||||
}
|
||||
134
core/plugin/nbnsscan.go
Normal file
134
core/plugin/nbnsscan.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/core/logger"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NbnsScanConn(host string, port int, timeout time.Duration) (bool, string) {
|
||||
conn, err := net.DialTimeout("udp", fmt.Sprintf("%v:%v", host, port), timeout)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
msg := []byte{
|
||||
0x0, 0x00, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x43, 0x4b, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x0, 0x0,
|
||||
0x21, 0x0, 0x1,
|
||||
}
|
||||
_, err = conn.Write(msg)
|
||||
if err != nil {
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
reply := make([]byte, 256)
|
||||
err = conn.SetDeadline(time.Now().Add(time.Duration(timeout)))
|
||||
if err != nil {
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
_, _ = conn.Read(reply)
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
var buffer [256]byte
|
||||
if bytes.Equal(reply[:], buffer[:]) {
|
||||
return false, ""
|
||||
}
|
||||
/*
|
||||
Re: https://en.wikipedia.org/wiki/NetBIOS#NetBIOS_Suffixes
|
||||
For unique names:
|
||||
00: Workstation Service (workstation name)
|
||||
03: Windows Messenger service
|
||||
06: Remote Access Service
|
||||
20: File Service (also called Host Record)
|
||||
21: Remote Access Service client
|
||||
1B: Domain Master Browser – Primary Domain Controller for a domain
|
||||
1D: Master Browser
|
||||
For group names:
|
||||
00: Workstation Service (workgroup/domain name)
|
||||
1C: Domain Controllers for a domain (group record with up to 25 IP addresses)
|
||||
1E: Browser Service Elections
|
||||
*/
|
||||
var n int
|
||||
NumberFoNames, _ := strconv.Atoi(convert([]byte{reply[56:57][0]}[:]))
|
||||
var flagGroup string
|
||||
var flagUnique string
|
||||
var flagDC string
|
||||
|
||||
for i := 0; i < NumberFoNames; i++ {
|
||||
data := reply[n+57+18*i : n+57+18*i+18]
|
||||
if string(data[16:17]) == "\x84" || string(data[16:17]) == "\xC4" {
|
||||
if string(data[15:16]) == "\x1C" {
|
||||
flagDC = "Domain Controllers"
|
||||
}
|
||||
if string(data[15:16]) == "\x00" {
|
||||
flagGroup = nbnsByteToStringParse(data[0:16])
|
||||
}
|
||||
if string(data[14:16]) == "\x02\x01" {
|
||||
flagGroup = nbnsByteToStringParse(data[0:16])
|
||||
}
|
||||
} else if string(data[16:17]) == "\x04" || string(data[16:17]) == "\x44" || string(data[16:17]) == "\x64" {
|
||||
if string(data[15:16]) == "\x1C" {
|
||||
flagDC = "Domain Controllers"
|
||||
}
|
||||
if string(data[15:16]) == "\x00" {
|
||||
flagUnique = nbnsByteToStringParse(data[0:16])
|
||||
}
|
||||
if string(data[15:16]) == "\x20" {
|
||||
flagUnique = nbnsByteToStringParse(data[0:16])
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if flagGroup == "" && flagUnique == "" {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
result := make(map[string]interface{})
|
||||
result["banner.string"] = flagGroup + "\\" + flagUnique
|
||||
result["identify.string"] = fmt.Sprintf("[%s]", logger.LightRed(flagDC))
|
||||
if len(flagDC) != 0 {
|
||||
result["identify.bool"] = true
|
||||
} else {
|
||||
result["identify.bool"] = false
|
||||
}
|
||||
if result["identify.bool"] == true {
|
||||
logger.Success(fmt.Sprintf("[%s] %v %v", fmt.Sprintf("%v:%v", host, port), result["banner.string"], result["identify.string"]))
|
||||
} else {
|
||||
logger.Success(fmt.Sprintf("[%s] %v", fmt.Sprintf("%v:%v", host, port), result["banner.string"]))
|
||||
}
|
||||
|
||||
return true, fmt.Sprintf("%v", result["banner.string"])
|
||||
}
|
||||
|
||||
func convert(b []byte) string {
|
||||
s := make([]string, len(b))
|
||||
for i := range b {
|
||||
s[i] = strconv.Itoa(int(b[i]))
|
||||
}
|
||||
return strings.Join(s, "")
|
||||
}
|
||||
|
||||
func nbnsByteToStringParse(p []byte) string {
|
||||
var w []string
|
||||
var res string
|
||||
for i := 0; i < len(p); i++ {
|
||||
if p[i] > 32 && p[i] < 127 {
|
||||
w = append(w, string(p[i]))
|
||||
continue
|
||||
}
|
||||
}
|
||||
res = strings.Join(w, "")
|
||||
return res
|
||||
}
|
||||
77
core/plugin/oxidscan.go
Normal file
77
core/plugin/oxidscan.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/core/logger"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func OxidScanConn(host string, port int, timeout time.Duration) (bool, string) {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", host, port), timeout)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
msg1 := "\x05\x00\x0b\x03\x10\x00\x00\x00\x48\x00\x00\x00\x01\x00\x00\x00\xf8\x0f\xf8\x0f\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\xc4\xfe\xfc\x99\x60\x52\x1b\x10\xbb\xcb\x00\xaa\x00\x21\x34\x7a\x00\x00\x00\x00\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60\x02\x00\x00\x00"
|
||||
msg2 := "\x05\x00\x00\x03\x10\x00\x00\x00\x18\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00"
|
||||
_, err = conn.Write([]byte(msg1))
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
reply1 := make([]byte, 256)
|
||||
_, _ = conn.Read(reply1)
|
||||
|
||||
if hex.EncodeToString(reply1[0:8]) != "05000c0310000000" {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
_, err = conn.Write([]byte(msg2))
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
reply2 := make([]byte, 512)
|
||||
_, _ = conn.Read(reply2)
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
c := 0
|
||||
zero := make([]byte, 1)
|
||||
var buffer bytes.Buffer
|
||||
result := make(map[string]string)
|
||||
for i := 0; i < len(reply2[42:]); {
|
||||
b := reply2[42:][i : i+2]
|
||||
i += 2
|
||||
if 42+i == len(reply2[42:]) {
|
||||
break
|
||||
}
|
||||
if string(b) == "\x09\x00" {
|
||||
break
|
||||
}
|
||||
if string(b) == "\x07\x00" {
|
||||
c += 1
|
||||
if c == 6 {
|
||||
break
|
||||
}
|
||||
buffer.Write([]byte("\x7C\x7C"))
|
||||
result["banner.string"] = strings.Join([]string{string(buffer.Bytes())}, ",")
|
||||
continue
|
||||
}
|
||||
if bytes.Equal(b[0:1], zero[0:1]) {
|
||||
continue
|
||||
}
|
||||
buffer.Write(b[0:1])
|
||||
result["banner.string"] = strings.Join([]string{string(buffer.Bytes())}, ",")
|
||||
if c == 6 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(strings.Split(result["banner.string"], "||")) > 0 {
|
||||
logger.Success(strings.Split(result["banner.string"], "||"))
|
||||
}
|
||||
return true, fmt.Sprintf("%v", strings.Split(result["banner.string"], "||"))
|
||||
}
|
||||
22
core/plugin/postgres.go
Normal file
22
core/plugin/postgres.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func PostgreConn(info config.ServiceConn, user, pass string) (bool, error) {
|
||||
var flag = false
|
||||
db, err := sql.Open("postgres", fmt.Sprintf("postgres://%v:%v@%v:%v/%v?sslmode=%v", user, pass, info.Hostname, info.Port, "postgres", "disable"))
|
||||
if err == nil {
|
||||
db.SetConnMaxLifetime(time.Duration(info.Timeout))
|
||||
defer db.Close()
|
||||
err = db.Ping()
|
||||
if err == nil {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
return flag, err
|
||||
}
|
||||
64
core/plugin/ps.go
Normal file
64
core/plugin/ps.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/core/logger"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Scanner struct {
|
||||
ports []int
|
||||
ip string
|
||||
scanChannel chan int
|
||||
thread int
|
||||
scan func(ip string, port int) bool
|
||||
}
|
||||
|
||||
func NewRunner(ports []int, ip string, thread int, scan func(ip string, port int) bool) *Scanner {
|
||||
return &Scanner{
|
||||
ports: ports,
|
||||
ip: ip,
|
||||
scanChannel: make(chan int, 1000),
|
||||
thread: thread,
|
||||
scan: scan,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scanner) RunEnumeration() []int {
|
||||
var wg sync.WaitGroup
|
||||
var re []int
|
||||
for i := 0; i < s.thread; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for p := range s.scanChannel {
|
||||
if s.scan(s.ip, p) {
|
||||
logger.Info(fmt.Sprintf("%v:%v is open", s.ip, p))
|
||||
re = append(re, p)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
for _, p := range s.ports {
|
||||
s.scanChannel <- p
|
||||
}
|
||||
close(s.scanChannel)
|
||||
wg.Wait()
|
||||
return re
|
||||
}
|
||||
|
||||
func tcpConn(ip string, port int) bool {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", ip, port), 100*time.Millisecond)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
conn.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
//TODO: 暂定,目前还没有syn扫描方式
|
||||
func synConn(ip string, port int) bool {
|
||||
return true
|
||||
}
|
||||
105
core/plugin/redis.go
Normal file
105
core/plugin/redis.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func RedisAuthConn(info config.ServiceConn, user, pass string) (net.Conn, bool, error) {
|
||||
var flag = false
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return conn, false, err
|
||||
}
|
||||
err = conn.SetReadDeadline(time.Now().Add(time.Duration(info.Timeout)))
|
||||
if err != nil {
|
||||
return conn, false, err
|
||||
}
|
||||
// 认证
|
||||
_, err = conn.Write([]byte(fmt.Sprintf("auth %s\r\n", pass)))
|
||||
if err != nil {
|
||||
return conn, false, err
|
||||
}
|
||||
reply, err := RedisReply(conn)
|
||||
if err != nil {
|
||||
return conn, false, err
|
||||
}
|
||||
if strings.Contains(reply, "+OK") {
|
||||
flag = true
|
||||
dbfilename := redisInfo(conn, reply)
|
||||
logger.Info(fmt.Sprintf("Redis %s:%v Login Success dbfilename:[%v]", info.Hostname, info.Port, dbfilename))
|
||||
}
|
||||
return conn, flag, nil
|
||||
}
|
||||
|
||||
func RedisUnAuthConn(info config.ServiceConn, user, pass string) (net.Conn, bool, error) {
|
||||
_, _ = user, pass
|
||||
var flag = false
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return conn, false, err
|
||||
}
|
||||
err = conn.SetReadDeadline(time.Now().Add(time.Duration(info.Timeout)))
|
||||
if err != nil {
|
||||
return conn, false, err
|
||||
}
|
||||
_, err = conn.Write([]byte("info\r\n"))
|
||||
if err != nil {
|
||||
return conn, false, err
|
||||
}
|
||||
reply, err := RedisReply(conn)
|
||||
if err != nil {
|
||||
return conn, false, err
|
||||
}
|
||||
if strings.Contains(reply, "redis_version") {
|
||||
flag = true
|
||||
dbfilename := redisInfo(conn, reply)
|
||||
logger.Success(fmt.Sprintf("Redis %s:%v unauthorized dbfilename:[%v] ", info.Hostname, info.Port, dbfilename))
|
||||
}
|
||||
return conn, flag, nil
|
||||
}
|
||||
|
||||
func RedisReply(conn net.Conn) (string, error) {
|
||||
var (
|
||||
r string
|
||||
err error
|
||||
)
|
||||
buf := make([]byte, 5*1024)
|
||||
for {
|
||||
count, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
r += string(buf[0:count])
|
||||
if count < 5*1024 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
|
||||
func redisInfo(conn net.Conn, reply string) string {
|
||||
var (
|
||||
dbfilename string
|
||||
)
|
||||
// 读取filename
|
||||
_, err := conn.Write([]byte(fmt.Sprintf("CONFIG GET dbfilename\r\n")))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
text, err := RedisReply(conn)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
text1 := strings.Split(text, "\r\n")
|
||||
if len(text1) > 2 {
|
||||
dbfilename = text1[len(text1)-2]
|
||||
} else {
|
||||
dbfilename = text1[0]
|
||||
}
|
||||
return dbfilename
|
||||
}
|
||||
58
core/plugin/rmi.go
Normal file
58
core/plugin/rmi.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RMIConn 识别rmi服务方式
|
||||
func RMIConn(info config.ServiceConn, user, pass string) bool {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
msg := "\x4a\x52\x4d\x49\x00\x02\x4b"
|
||||
_, err = conn.Write([]byte(msg))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
reply := make([]byte, 256)
|
||||
_, _ = conn.Read(reply)
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
var buffer [256]byte
|
||||
if bytes.Equal(reply[:], buffer[:]) {
|
||||
return false
|
||||
} else if hex.EncodeToString(reply[0:1]) != "4e" {
|
||||
return false
|
||||
}
|
||||
// 这里解析出字符串
|
||||
banner := byteToString(reply)
|
||||
logger.Success(fmt.Sprintf("%v [%v]", fmt.Sprintf("%v:%v", info.Hostname, info.Port), banner))
|
||||
return true
|
||||
}
|
||||
|
||||
func byteToString(p []byte) string {
|
||||
var w []string
|
||||
var res string
|
||||
for i := 0; i < len(p); i++ {
|
||||
if p[i] > 32 && p[i] < 127 {
|
||||
w = append(w, string(p[i]))
|
||||
continue
|
||||
}
|
||||
asciiTo16 := fmt.Sprintf("\\x%s", hex.EncodeToString(p[i:i+1]))
|
||||
w = append(w, asciiTo16)
|
||||
}
|
||||
res = strings.Join(w, "")
|
||||
if strings.Contains(res, "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00") {
|
||||
s := strings.Split(res, "\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00")
|
||||
return s[0]
|
||||
}
|
||||
return res
|
||||
}
|
||||
46
core/plugin/smb.go
Normal file
46
core/plugin/smb.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"errors"
|
||||
"github.com/stacktitan/smb/smb"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SmbConn(info config.ServiceConn, user, pass string) (bool, error) {
|
||||
signal := make(chan struct{})
|
||||
var (
|
||||
flag bool
|
||||
err error
|
||||
)
|
||||
go func() {
|
||||
flag, err = dialSmbTimeOut(info, user, pass, signal)
|
||||
}()
|
||||
select {
|
||||
case <-signal:
|
||||
return flag, err
|
||||
case <-time.After(1 * time.Second):
|
||||
return false, errors.New("smb conn time out")
|
||||
}
|
||||
}
|
||||
|
||||
func dialSmbTimeOut(info config.ServiceConn, user, pass string, signal chan struct{}) (bool, error) {
|
||||
var flag = false
|
||||
options := smb.Options{
|
||||
Host: info.Hostname,
|
||||
Port: 445,
|
||||
User: user,
|
||||
Password: pass,
|
||||
Domain: info.Domain,
|
||||
Workstation: "",
|
||||
}
|
||||
session, err := smb.NewSession(options, false)
|
||||
if err == nil {
|
||||
session.Close()
|
||||
if session.IsAuthenticated {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
signal <- struct{}{}
|
||||
return flag, err
|
||||
}
|
||||
120
core/plugin/smbghost.go
Normal file
120
core/plugin/smbghost.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
pkt = "\x00" + // session
|
||||
"\x00\x00\xc0" + // legth
|
||||
|
||||
"\xfeSMB@\x00" + // protocol
|
||||
|
||||
//[MS-SMB2]: SMB2 NEGOTIATE Request
|
||||
//https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/e14db7ff-763a-4263-8b10-0c3944f52fc5
|
||||
|
||||
"\x00\x00" +
|
||||
"\x00\x00" +
|
||||
"\x00\x00" +
|
||||
"\x00\x00" +
|
||||
"\x1f\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
|
||||
// [MS-SMB2]: SMB2 NEGOTIATE_CONTEXT
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/15332256-522e-4a53-8cd7-0bd17678a2f7
|
||||
|
||||
"$\x00" +
|
||||
"\x08\x00" +
|
||||
"\x01\x00" +
|
||||
"\x00\x00" +
|
||||
"\x7f\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"x\x00" +
|
||||
"\x00\x00" +
|
||||
"\x02\x00" +
|
||||
"\x00\x00" +
|
||||
"\x02\x02" +
|
||||
"\x10\x02" +
|
||||
"\x22\x02" +
|
||||
"$\x02" +
|
||||
"\x00\x03" +
|
||||
"\x02\x03" +
|
||||
"\x10\x03" +
|
||||
"\x11\x03" +
|
||||
"\x00\x00\x00\x00" +
|
||||
|
||||
// [MS-SMB2]: SMB2_PREAUTH_INTEGRITY_CAPABILITIES
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5a07bd66-4734-4af8-abcf-5a44ff7ee0e5
|
||||
|
||||
"\x01\x00" +
|
||||
"&\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x01\x00" +
|
||||
"\x20\x00" +
|
||||
"\x01\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x00\x00" +
|
||||
|
||||
// [MS-SMB2]: SMB2_COMPRESSION_CAPABILITIES
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/78e0c942-ab41-472b-b117-4a95ebe88271
|
||||
|
||||
"\x03\x00" +
|
||||
"\x0e\x00" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\x01\x00" + //CompressionAlgorithmCount
|
||||
"\x00\x00" +
|
||||
"\x01\x00\x00\x00" +
|
||||
"\x01\x00" + //LZNT1
|
||||
"\x00\x00" +
|
||||
"\x00\x00\x00\x00"
|
||||
)
|
||||
|
||||
func SmbGhostConn(info config.ServiceConn) bool {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return false
|
||||
} else {
|
||||
defer conn.Close()
|
||||
conn.Write([]byte(pkt))
|
||||
buff := make([]byte, 1024)
|
||||
err = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||
n, err := conn.Read(buff)
|
||||
if err != nil {
|
||||
//Println(err.Error()) // Profound analysis
|
||||
}
|
||||
|
||||
if bytes.Contains([]byte(buff[:n]), []byte("Public")) == true {
|
||||
logger.Success(fmt.Sprintf("%s Find CVE-2020-0796", info.Hostname))
|
||||
return true
|
||||
} else {
|
||||
//Println(ip + " Not Vulnerable")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
596
core/plugin/smbscan.go
Normal file
596
core/plugin/smbscan.go
Normal file
@@ -0,0 +1,596 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/core/logger"
|
||||
"bytes"
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SmbScanConn(host string, port int, timeout time.Duration) (bool, string) {
|
||||
res, _, err := smb2(host, port, timeout)
|
||||
if err != nil || res["ntlmssp.Version"] == "" {
|
||||
return false, ""
|
||||
} else {
|
||||
banner := fmt.Sprintf("[%s]\n[%s (version) || %s (FQDN Name) ||%s (Domain Name) ||%s (Netbios Name)]",
|
||||
fmt.Sprintf("%s:%v", host, port),
|
||||
res["ntlmssp.Version"],
|
||||
res["ntlmssp.DNSComputer"],
|
||||
res["ntlmssp.TargetName"],
|
||||
res["ntlmssp.NetbiosComputer"],
|
||||
)
|
||||
logger.Success(banner)
|
||||
return true, banner
|
||||
}
|
||||
}
|
||||
|
||||
// -------------smb--------------------------------
|
||||
// smb2 from https://github.com/RumbleDiscovery/rumble-tools/blob/main/cmd/rumble-smb2-sessions/main.go
|
||||
func smb2(host string, port int, timeout time.Duration) (map[string]string, []byte, error) {
|
||||
// SMB1NegotiateProtocolRequest is a SMB1 request that advertises support for SMB2
|
||||
var smb1NegotiateProtocolRequest = []byte{
|
||||
0x00, 0x00, 0x00, 0xd4, 0xff, 0x53, 0x4d, 0x42,
|
||||
0x72, 0x00, 0x00, 0x00, 0x00, 0x18, 0x43, 0xc8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x02,
|
||||
0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
|
||||
0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52,
|
||||
0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02,
|
||||
0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f, 0x46,
|
||||
0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52,
|
||||
0x4b, 0x53, 0x20, 0x31, 0x2e, 0x30, 0x33, 0x00,
|
||||
0x02, 0x4d, 0x49, 0x43, 0x52, 0x4f, 0x53, 0x4f,
|
||||
0x46, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f,
|
||||
0x52, 0x4b, 0x53, 0x20, 0x33, 0x2e, 0x30, 0x00,
|
||||
0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31,
|
||||
0x2e, 0x30, 0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e,
|
||||
0x32, 0x58, 0x30, 0x30, 0x32, 0x00, 0x02, 0x44,
|
||||
0x4f, 0x53, 0x20, 0x4c, 0x41, 0x4e, 0x4d, 0x41,
|
||||
0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4c, 0x41,
|
||||
0x4e, 0x4d, 0x41, 0x4e, 0x32, 0x2e, 0x31, 0x00,
|
||||
0x02, 0x53, 0x61, 0x6d, 0x62, 0x61, 0x00, 0x02,
|
||||
0x4e, 0x54, 0x20, 0x4c, 0x41, 0x4e, 0x4d, 0x41,
|
||||
0x4e, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02, 0x4e,
|
||||
0x54, 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31,
|
||||
0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32,
|
||||
0x2e, 0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x4d,
|
||||
0x42, 0x20, 0x32, 0x2e, 0x3f, 0x3f, 0x3f, 0x00,
|
||||
}
|
||||
|
||||
info := make(map[string]string)
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", host, port), timeout)
|
||||
if err != nil {
|
||||
return info, []byte{}, err
|
||||
}
|
||||
err = SMBSendData(conn, smb1NegotiateProtocolRequest, timeout)
|
||||
if err != nil {
|
||||
return info, []byte{}, err
|
||||
}
|
||||
|
||||
data, err := SMBReadFrame(conn, timeout)
|
||||
if err != nil {
|
||||
return info, []byte{}, err
|
||||
}
|
||||
|
||||
err = SMBSendData(conn, SMB2NegotiateProtocolRequest(host), timeout)
|
||||
if err != nil {
|
||||
return info, []byte{}, err
|
||||
}
|
||||
|
||||
data, _ = SMBReadFrame(conn, timeout)
|
||||
SMB2ExtractFieldsFromNegotiateReply(data, info)
|
||||
|
||||
// SMB2SessionSetupNTLMSSP is a SMB2 SessionSetup NTLMSSP request
|
||||
var smb2SessionSetupNTLMSSP = []byte{
|
||||
0x00, 0x00, 0x00, 0xa2, 0xfe, 0x53, 0x4d, 0x42,
|
||||
0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x01,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x58, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x48, 0x06, 0x06,
|
||||
0x2b, 0x06, 0x01, 0x05, 0x05, 0x02, 0xa0, 0x3e,
|
||||
0x30, 0x3c, 0xa0, 0x0e, 0x30, 0x0c, 0x06, 0x0a,
|
||||
0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02,
|
||||
0x02, 0x0a, 0xa2, 0x2a, 0x04, 0x28, 0x4e, 0x54,
|
||||
0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00,
|
||||
0x00, 0x00, 0x97, 0x82, 0x08, 0xe2, 0x00, 0x00,
|
||||
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0a, 0x00,
|
||||
0xba, 0x47, 0x00, 0x00, 0x00, 0x0f,
|
||||
}
|
||||
|
||||
setup := make([]byte, len(smb2SessionSetupNTLMSSP))
|
||||
copy(setup, smb2SessionSetupNTLMSSP)
|
||||
|
||||
// Set the ProcessID
|
||||
binary.LittleEndian.PutUint16(setup[4+32:], 0xfeff)
|
||||
|
||||
err = SMBSendData(conn, setup, timeout)
|
||||
if err != nil {
|
||||
return info, []byte{}, err
|
||||
}
|
||||
|
||||
data, err = SMBReadFrame(conn, timeout)
|
||||
SMB2ExtractSIDFromSessionSetupReply(data, info)
|
||||
SMBExtractFieldsFromSecurityBlob(data, info)
|
||||
|
||||
return info, data, err
|
||||
}
|
||||
|
||||
// RandomBytes generates a random byte sequence of the requested length
|
||||
func RandomBytes(numBytes int) []byte {
|
||||
randBytes := make([]byte, numBytes)
|
||||
err := binary.Read(crand.Reader, binary.BigEndian, &randBytes)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return randBytes
|
||||
}
|
||||
|
||||
// SMBReadFrame reads the netBios header then the full response
|
||||
func SMBReadFrame(conn net.Conn, t time.Duration) ([]byte, error) {
|
||||
timeout := time.Now().Add(time.Duration(t) * time.Second)
|
||||
res := []byte{}
|
||||
nbh := make([]byte, 4)
|
||||
|
||||
err := conn.SetReadDeadline(timeout)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Read the NetBIOS header
|
||||
n, err := conn.Read(nbh[:])
|
||||
if err != nil {
|
||||
// Return if EOF is reached
|
||||
if err == io.EOF {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Return if timeout is reached
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// If we have data and received an error, it was probably a reset
|
||||
if len(res) > 0 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
if n != 4 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
res = append(res[:], nbh[:n]...)
|
||||
dlen := binary.BigEndian.Uint32(nbh[:]) & 0x00ffffff
|
||||
buf := make([]byte, dlen)
|
||||
n, err = conn.Read(buf[:])
|
||||
if err != nil {
|
||||
// Return if EOF is reached
|
||||
if err == io.EOF {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Return if timeout is reached
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// If we have data and received an error, it was probably a reset
|
||||
if len(res) > 0 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
res = append(res[:], buf[:n]...)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// SMB2ExtractFieldsFromNegotiateReply extracts useful fields from the SMB2 negotiate response
|
||||
func SMB2ExtractFieldsFromNegotiateReply(blob []byte, info map[string]string) {
|
||||
|
||||
smbOffset := bytes.Index(blob, []byte{0xfe, 'S', 'M', 'B'})
|
||||
if smbOffset < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
data := blob[smbOffset:]
|
||||
|
||||
// Basic sanity check
|
||||
if len(data) < (64 + 8 + 16 + 36) {
|
||||
return
|
||||
}
|
||||
|
||||
switch binary.LittleEndian.Uint16(data[64+2:]) {
|
||||
case 0:
|
||||
info["smb.Signing"] = "disabled"
|
||||
case 1:
|
||||
info["smb.Signing"] = "enabled"
|
||||
case 2, 3:
|
||||
info["smb.Signing"] = "required"
|
||||
}
|
||||
|
||||
info["smb.Dialect"] = fmt.Sprintf("0x%.4x", binary.LittleEndian.Uint16(data[64+4:]))
|
||||
//info["smb.GUID"] = uuid.FromBytesOrNil(data[64+8 : 64+8+16]).String()
|
||||
info["smb.Capabilities"] = fmt.Sprintf("0x%.8x", binary.LittleEndian.Uint32(data[64+8+16:]))
|
||||
|
||||
negCtxCount := int(binary.LittleEndian.Uint16(data[64+6:]))
|
||||
negCtxOffset := int(binary.LittleEndian.Uint32(data[64+8+16+36:]))
|
||||
if negCtxCount == 0 || negCtxOffset == 0 || negCtxOffset+(negCtxCount*8) > len(data) {
|
||||
return
|
||||
}
|
||||
|
||||
negCtxData := data[negCtxOffset:]
|
||||
idx := 0
|
||||
for {
|
||||
if idx+8 > len(negCtxData) {
|
||||
break
|
||||
}
|
||||
negType := int(binary.LittleEndian.Uint16(negCtxData[idx:]))
|
||||
negLen := int(binary.LittleEndian.Uint16(negCtxData[idx+2:]))
|
||||
idx += 8
|
||||
|
||||
if idx+negLen > len(negCtxData) {
|
||||
break
|
||||
}
|
||||
negData := negCtxData[idx : idx+negLen]
|
||||
|
||||
SMB2ParseNegotiateContext(negType, negData, info)
|
||||
|
||||
// Move the index to the next context
|
||||
idx += negLen
|
||||
// Negotiate Contexts are aligned on 64-bit boundaries
|
||||
for idx%8 != 0 {
|
||||
idx++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SMB2ParseNegotiateContext decodes fields from the SMB2 Negotiate Context values
|
||||
func SMB2ParseNegotiateContext(t int, data []byte, info map[string]string) {
|
||||
switch t {
|
||||
case 1:
|
||||
// SMB2_PREAUTH_INTEGRITY_CAPABILITIES
|
||||
if len(data) < 6 {
|
||||
return
|
||||
}
|
||||
hashCount := int(binary.LittleEndian.Uint16(data[:]))
|
||||
// MUST only be one in responses
|
||||
if hashCount != 1 {
|
||||
return
|
||||
}
|
||||
hashSaltLen := int(binary.LittleEndian.Uint16(data[2:]))
|
||||
hashType := int(binary.LittleEndian.Uint16(data[4:]))
|
||||
hashName := "sha512"
|
||||
if hashType != 1 {
|
||||
hashName = fmt.Sprintf("unknown-%d", hashType)
|
||||
}
|
||||
info["smb.HashAlg"] = hashName
|
||||
info["smb.HashSaltLen"] = fmt.Sprintf("%d", hashSaltLen)
|
||||
|
||||
case 2:
|
||||
// SMB2_ENCRYPTION_CAPABILITIES
|
||||
if len(data) < 4 {
|
||||
return
|
||||
}
|
||||
cipherCount := int(binary.LittleEndian.Uint16(data[:]))
|
||||
if len(data) < 2+(2*cipherCount) {
|
||||
return
|
||||
}
|
||||
// MUST only be one in responses
|
||||
if cipherCount != 1 {
|
||||
return
|
||||
}
|
||||
cipherList := []string{}
|
||||
for i := 0; i < cipherCount; i++ {
|
||||
cipherID := int(binary.LittleEndian.Uint16(data[2+(i*2):]))
|
||||
cipherName := ""
|
||||
switch cipherID {
|
||||
case 1:
|
||||
cipherName = "aes-128-ccm"
|
||||
case 2:
|
||||
cipherName = "aes-128-gcm"
|
||||
default:
|
||||
cipherName = fmt.Sprintf("unknown-%d", cipherID)
|
||||
}
|
||||
cipherList = append(cipherList, cipherName)
|
||||
}
|
||||
|
||||
info["smb.CipherAlg"] = strings.Join(cipherList, "\t")
|
||||
|
||||
case 3:
|
||||
// SMB2_COMPRESSION_CAPABILITIES
|
||||
if len(data) < 10 {
|
||||
return
|
||||
}
|
||||
compCount := int(binary.LittleEndian.Uint16(data[:]))
|
||||
if len(data) < 2+2+4+(2*compCount) {
|
||||
return
|
||||
}
|
||||
// MUST only be one in responses
|
||||
if compCount != 1 {
|
||||
return
|
||||
}
|
||||
compList := []string{}
|
||||
for i := 0; i < compCount; i++ {
|
||||
compID := int(binary.LittleEndian.Uint16(data[8+(i*2):]))
|
||||
compName := ""
|
||||
switch compID {
|
||||
case 0:
|
||||
compName = "none"
|
||||
case 1:
|
||||
compName = "lznt1"
|
||||
case 2:
|
||||
compName = "lz77"
|
||||
case 3:
|
||||
compName = "lz77+huff"
|
||||
case 4:
|
||||
compName = "patternv1"
|
||||
default:
|
||||
compName = fmt.Sprintf("unknown-%d", compID)
|
||||
}
|
||||
compList = append(compList, compName)
|
||||
}
|
||||
info["smb.CompressionFlags"] = fmt.Sprintf("0x%.4x", binary.LittleEndian.Uint32(data[4:]))
|
||||
info["smb.CompressionAlg"] = strings.Join(compList, "\t")
|
||||
}
|
||||
}
|
||||
|
||||
// SMBSendData writes a SMB request to a socket
|
||||
func SMBSendData(conn net.Conn, data []byte, timeout time.Duration) error {
|
||||
err := conn.SetWriteDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, err := conn.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = n
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SMB2ExtractSIDFromSessionSetupReply tries to extract the SessionID and Signature from a SMB2 reply
|
||||
func SMB2ExtractSIDFromSessionSetupReply(blob []byte, info map[string]string) {
|
||||
smbOffset := bytes.Index(blob, []byte{0xfe, 'S', 'M', 'B'})
|
||||
if smbOffset < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
smbData := blob[smbOffset:]
|
||||
if len(smbData) < 48 {
|
||||
return
|
||||
}
|
||||
|
||||
status := binary.LittleEndian.Uint32(smbData[8:])
|
||||
info["smb.Status"] = fmt.Sprintf("0x%.8x", status)
|
||||
|
||||
sessID := binary.LittleEndian.Uint64(smbData[40:])
|
||||
info["smb.SessionID"] = fmt.Sprintf("0x%.16x", sessID)
|
||||
|
||||
if len(smbData) >= 64 {
|
||||
sigData := hex.EncodeToString(smbData[48:64])
|
||||
if sigData != "00000000000000000000000000000000" {
|
||||
info["smb.Signature"] = sigData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SMBExtractValueFromOffset peels a field out of a SMB buffer
|
||||
func SMBExtractValueFromOffset(blob []byte, idx int) ([]byte, int, error) {
|
||||
res := []byte{}
|
||||
|
||||
if len(blob) < (idx + 6) {
|
||||
return res, idx, fmt.Errorf("data truncated")
|
||||
}
|
||||
|
||||
len1 := binary.LittleEndian.Uint16(blob[idx:])
|
||||
idx += 2
|
||||
|
||||
// len2 := binary.LittleEndian.Uint16(blob[idx:])
|
||||
idx += 2
|
||||
|
||||
off := binary.LittleEndian.Uint32(blob[idx:])
|
||||
idx += 4
|
||||
|
||||
// Allow zero length values
|
||||
if len1 == 0 {
|
||||
return res, idx, nil
|
||||
}
|
||||
|
||||
if len(blob) < int(off+uint32(len1)) {
|
||||
return res, idx, fmt.Errorf("data value truncated")
|
||||
}
|
||||
|
||||
res = append(res, blob[off:off+uint32(len1)]...)
|
||||
return res, idx, nil
|
||||
}
|
||||
|
||||
// SMBExtractFieldsFromSecurityBlob extracts fields from the NTLMSSP response
|
||||
func SMBExtractFieldsFromSecurityBlob(blob []byte, info map[string]string) {
|
||||
var err error
|
||||
|
||||
ntlmsspOffset := bytes.Index(blob, []byte{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0x00, 0x02, 0x00, 0x00, 0x00})
|
||||
if ntlmsspOffset < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
data := blob[ntlmsspOffset:]
|
||||
|
||||
// Basic sanity check
|
||||
if len(data) < (12 + 6 + 12 + 8 + 6 + 8) {
|
||||
return
|
||||
}
|
||||
|
||||
idx := 12
|
||||
|
||||
targetName, idx, err := SMBExtractValueFromOffset(data, idx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Negotiate Flags
|
||||
negotiateFlags := binary.LittleEndian.Uint32(data[idx:])
|
||||
info["ntlmssp.NegotiationFlags"] = fmt.Sprintf("0x%.8x", negotiateFlags)
|
||||
idx += 4
|
||||
|
||||
// NTLM Server Challenge
|
||||
idx += 8
|
||||
|
||||
// Reserved
|
||||
idx += 8
|
||||
|
||||
// Target Info
|
||||
targetInfo, idx, err := SMBExtractValueFromOffset(data, idx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Version
|
||||
versionMajor := uint8(data[idx])
|
||||
idx++
|
||||
|
||||
versionMinor := uint8(data[idx])
|
||||
idx++
|
||||
|
||||
versionBuild := binary.LittleEndian.Uint16(data[idx:])
|
||||
idx += 2
|
||||
|
||||
ntlmRevision := binary.BigEndian.Uint32(data[idx:])
|
||||
|
||||
// macOS reverses the endian order of this field for some reason
|
||||
if ntlmRevision == 251658240 {
|
||||
ntlmRevision = binary.LittleEndian.Uint32(data[idx:])
|
||||
}
|
||||
|
||||
info["ntlmssp.Version"] = fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionBuild)
|
||||
info["ntlmssp.NTLMRevision"] = fmt.Sprintf("%d", ntlmRevision)
|
||||
info["ntlmssp.TargetName"] = TrimName(string(targetName))
|
||||
|
||||
idx = 0
|
||||
for {
|
||||
if idx+4 > len(targetInfo) {
|
||||
break
|
||||
}
|
||||
|
||||
attrType := binary.LittleEndian.Uint16(targetInfo[idx:])
|
||||
idx += 2
|
||||
|
||||
// End of List
|
||||
if attrType == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
attrLen := binary.LittleEndian.Uint16(targetInfo[idx:])
|
||||
idx += 2
|
||||
|
||||
if idx+int(attrLen) > len(targetInfo) {
|
||||
// log.Printf("too short: %d/%d", idx+int(attrLen), len(targetInfo))
|
||||
break
|
||||
}
|
||||
|
||||
attrVal := targetInfo[idx : idx+int(attrLen)]
|
||||
idx += int(attrLen)
|
||||
|
||||
switch attrType {
|
||||
case 1:
|
||||
info["ntlmssp.NetbiosComputer"] = TrimName(string(attrVal))
|
||||
case 2:
|
||||
info["ntlmssp.NetbiosDomain"] = TrimName(string(attrVal))
|
||||
case 3:
|
||||
info["ntlmssp.DNSComputer"] = TrimName(string(attrVal))
|
||||
case 4:
|
||||
info["ntlmssp.DNSDomain"] = TrimName(string(attrVal))
|
||||
case 7:
|
||||
ts := binary.LittleEndian.Uint64(attrVal[:])
|
||||
info["ntlmssp.Timestamp"] = fmt.Sprintf("0x%.16x", ts)
|
||||
|
||||
}
|
||||
|
||||
// End of List
|
||||
if attrType == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TrimName removes null bytes and trims leading and trailing spaces from a string
|
||||
func TrimName(name string) string {
|
||||
return strings.TrimSpace(strings.Replace(name, "\x00", "", -1))
|
||||
}
|
||||
|
||||
// SMB2NegotiateProtocolRequest generates a new Negotiate request with the specified target name
|
||||
func SMB2NegotiateProtocolRequest(dst string) []byte {
|
||||
|
||||
base := []byte{
|
||||
0xfe, 0x53, 0x4d, 0x42,
|
||||
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x05, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Client GUID (16)
|
||||
base = append(base[:], RandomBytes(16)...)
|
||||
|
||||
base = append(base[:], []byte{
|
||||
0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x02, 0x02, 0x10, 0x02, 0x00, 0x03, 0x02, 0x03,
|
||||
0x11, 0x03, 0x00, 0x00, 0x01, 0x00, 0x26, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00,
|
||||
0x01, 0x00,
|
||||
}...)
|
||||
|
||||
// SHA-512 Salt (32)
|
||||
base = append(base[:], RandomBytes(32)...)
|
||||
|
||||
base = append(base[:], []byte{
|
||||
0x00, 0x00, 0x02, 0x00, 0x06, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
}...)
|
||||
|
||||
encodedDst := make([]byte, len(dst)*2)
|
||||
for i, b := range []byte(dst) {
|
||||
encodedDst[i*2] = b
|
||||
}
|
||||
|
||||
netname := []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||
binary.LittleEndian.PutUint16(netname[2:], uint16(len(encodedDst)))
|
||||
netname = append(netname, encodedDst...)
|
||||
|
||||
base = append(base, netname...)
|
||||
|
||||
nbhd := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(nbhd, uint32(len(base)))
|
||||
nbhd = append(nbhd, base...)
|
||||
return nbhd
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
126
core/plugin/ssh.go
Normal file
126
core/plugin/ssh.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"Yasso/core/utils"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SshConnByUser(info config.ServiceConn, user, pass string) (*ssh.Client, bool, error) {
|
||||
sshConfig := &ssh.ClientConfig{User: user, Auth: []ssh.AuthMethod{ssh.Password(pass)}, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: info.Timeout}
|
||||
con, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
c, ch, re, err := ssh.NewClientConn(con, fmt.Sprintf("%v:%v", info.Hostname, info.Port), sshConfig)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return ssh.NewClient(c, ch, re), true, err
|
||||
}
|
||||
|
||||
func SshConnByKey(info config.ServiceConn, user string) (*ssh.Client, bool, error) {
|
||||
var (
|
||||
err error
|
||||
HomePath string
|
||||
key []byte
|
||||
)
|
||||
switch {
|
||||
case info.PublicKey == "":
|
||||
HomePath, err = os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
key, err = ioutil.ReadFile(path.Join(HomePath, ".ssh", "id_rsa"))
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
case info.PublicKey != "":
|
||||
key, err = ioutil.ReadFile(info.PublicKey)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
signer, err := ssh.ParsePrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
sshConfig := &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
Timeout: info.Timeout,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
con, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
c, ch, re, err := ssh.NewClientConn(con, fmt.Sprintf("%v:%v", info.Hostname, info.Port), sshConfig)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return ssh.NewClient(c, ch, re), true, err
|
||||
}
|
||||
|
||||
func VersionSSH(info config.ServiceConn) string {
|
||||
buff, err := sshConn(info)
|
||||
if err != nil {
|
||||
logger.Fatal(fmt.Sprintf("%s ssh conn has an error", info.Hostname))
|
||||
return ""
|
||||
}
|
||||
ok, _ := regexp.Match(`^SSH.\d`, buff)
|
||||
str := utils.ByteToStringParse(buff)
|
||||
if ok {
|
||||
logger.Info(fmt.Sprintf("%s:%v [%v]", info.Hostname, info.Port, strings.Split(str, "\\x0d\\x0a")[0]))
|
||||
return fmt.Sprintf("%s:%v [%v]", info.Hostname, info.Port, strings.Split(str, "\\x0d\\x0a")[0])
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// sshConn 连接到tcp
|
||||
func sshConn(info config.ServiceConn) ([]byte, error) {
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), time.Duration(info.Timeout))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = conn.SetDeadline(time.Now().Add(time.Duration(2) * time.Second))
|
||||
reply := make([]byte, 256)
|
||||
_, err = conn.Read(reply)
|
||||
|
||||
var buffer [256]byte
|
||||
if err == nil && bytes.Equal(reply[:], buffer[:]) == false {
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
return reply, nil
|
||||
}
|
||||
conn, err = net.DialTimeout("tcp", fmt.Sprintf("%v:%v", info.Hostname, info.Port), time.Duration(info.Timeout))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg := "GET /test HTTP/1.1\r\n\r\n"
|
||||
_, err = conn.Write([]byte(msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = conn.SetDeadline(time.Now().Add(time.Duration(2) * time.Second))
|
||||
reply = make([]byte, 256)
|
||||
_, _ = conn.Read(reply)
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
return reply, nil
|
||||
}
|
||||
30
core/plugin/winrm.go
Normal file
30
core/plugin/winrm.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"fmt"
|
||||
"github.com/masterzen/winrm"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
func WinRMAuth(info config.ServiceConn, user, pass string) (*winrm.Client, bool, error) {
|
||||
var err error
|
||||
params := winrm.DefaultParameters
|
||||
// 设置代理认证
|
||||
params.Dial = func(network, addr string) (net.Conn, error) {
|
||||
return net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout)
|
||||
}
|
||||
// 设置输入
|
||||
endpoint := winrm.NewEndpoint("other-host", 5985, false, false, nil, nil, nil, 0)
|
||||
client, err := winrm.NewClientWithParameters(endpoint, user, pass, params)
|
||||
stdout := os.Stdout
|
||||
res, err := client.Run("echo ISOK > nul", stdout, os.Stderr)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if res == 0 && err == nil {
|
||||
return client, true, nil
|
||||
}
|
||||
return nil, false, err
|
||||
}
|
||||
29
core/plugin/zookeeper.go
Normal file
29
core/plugin/zookeeper.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"Yasso/config"
|
||||
"Yasso/core/logger"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func ZookeeperConn(info config.ServiceConn, user, pass string) (bool, error) {
|
||||
payload := []byte("envidddfdsfsafafaerwrwerqwe")
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%v", info.Hostname, info.Port), info.Timeout)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, err = conn.Write(payload)
|
||||
if err == nil {
|
||||
reply := make([]byte, 1024)
|
||||
n, err := conn.Read(reply)
|
||||
if err == nil {
|
||||
if bytes.Contains(reply[:n], []byte("Environment")) {
|
||||
logger.Success(fmt.Sprintf("zookeeper %s unauthorized", fmt.Sprintf("%v:%v", info.Hostname, info.Port)))
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
Reference in New Issue
Block a user