1
0
mirror of https://github.com/sairson/Yasso.git synced 2026-02-13 15:26:15 +08:00

Yasso更新大改动,更新扫描方式,去除不常用功能,增加指纹和协议识别,修补bug等

This commit is contained in:
sairson
2022-05-29 09:11:07 +08:00
parent a6cd80f8e8
commit cb72f18edd
129 changed files with 16619 additions and 6278 deletions

447
core/plugin/all.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}