1
0
mirror of https://github.com/sairson/Yasso.git synced 2026-06-16 15:18:12 +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

120
core/brute/brute.go Normal file
View File

@@ -0,0 +1,120 @@
package brute
import (
"Yasso/config"
"Yasso/core/logger"
"fmt"
"math"
"reflect"
"strings"
"sync"
)
type Brute struct {
user []string // 被枚举的用户名
pass []string // 被枚举的密码
bruteMethod interface{} // 枚举方法
service string // 服务命令
serviceConn config.ServiceConn // 服务连接
thread int // 执行爆破的线程数
output string // 结果输出路径
noBrute bool // 是否执行爆破
}
func NewBrute(user, pass []string, method interface{}, service string, serviceConn config.ServiceConn, thread int, noBrute bool, output string) *Brute {
return &Brute{
user: user,
pass: pass,
bruteMethod: method,
output: output,
service: service,
thread: thread,
serviceConn: serviceConn,
noBrute: noBrute,
}
}
// RunEnumeration 开始蛮力枚举
func (b *Brute) RunEnumeration() {
if b.noBrute == false {
var wg sync.WaitGroup
if len(b.user) == 0 {
b.user = config.UserDict[b.service] // 获取对应端口的user列表
}
if len(b.pass) == 0 {
b.pass = config.PassDict
}
var t int
if len(b.pass) <= b.thread {
t = len(b.pass)
} else {
t = b.thread
}
// 分割密码
num := int(math.Ceil(float64(len(b.pass)) / float64(b.thread))) // 每个协程的user数量
// 分割用户名
all := map[int][]string{}
for i := 1; i <= t; i++ {
for j := 0; j < num; j++ {
tmp := (i-1)*num + j
if tmp < len(b.pass) {
all[i] = append(all[i], b.pass[tmp])
}
}
}
for i := 1; i <= t; i++ {
wg.Add(1)
tmp := all[i]
go func(tmp []string) {
defer wg.Done()
for _, p := range tmp {
for _, u := range b.user {
// 开始爆破,带有用户名密码的服务
if strings.Contains(p, "{user}") {
p = strings.ReplaceAll(p, "{user}", u)
}
if b.export(b.call(b.serviceConn, u, p), b.serviceConn.Hostname, b.serviceConn.Port, b.service, u, p, b.output) {
return
}
}
}
}(tmp)
}
wg.Wait()
}
}
// call 函数调用,爆破将会调用该模块去执行操作操作
func (b *Brute) call(params ...interface{}) []reflect.Value {
f := reflect.ValueOf(b.bruteMethod)
if len(params) != f.Type().NumIn() {
logger.Fatal(fmt.Sprintf("call func %v has an error", b.bruteMethod))
return nil
}
args := make([]reflect.Value, len(params))
for k, param := range params {
if param == "" || param == 0 {
continue
}
args[k] = reflect.ValueOf(param)
}
return f.Call(args)
}
// 结果验证
func (b *Brute) export(v []reflect.Value, host string, port int, service, user, pass string, output string) bool {
var mutex sync.Mutex
for _, value := range v {
switch value.Kind() {
case reflect.Bool:
if value.Bool() == true {
mutex.Lock()
logger.Success(fmt.Sprintf("brute %v:%v success [%v:%v][%v]", host, port, user, pass, service))
logger.JSONSave(host, logger.WeakPassSave, service, map[string]string{user: pass})
mutex.Unlock()
return true
}
}
}
return false
}

114
core/flag/flag.go Normal file
View File

@@ -0,0 +1,114 @@
package flag
import (
"Yasso/core/logger"
"Yasso/core/plugin"
"Yasso/pkg/exploit"
"github.com/spf13/cobra"
"os"
"time"
)
type allFlags struct {
Hosts string // 全局变量 标识ip列表或文件路径
Ports string // 全局变量 标识扫描的端口
Timeout int // 全局变量 标识超时时间
NoCrack bool // 全局变量 标识all模块是否开启爆破
NoAlive bool // 全局变量 是否采用ping来判断存活主机
User string // 全局变量 标识all模块爆破使用用户名字典
Pass string // 全局变量 标识all模块爆破使用密码字典
Thread int // 全局变量 标识all模块扫描时的线程数
NoService bool // 全局变量 标识all模块是否探测服务
NoVulcan bool // 全局变量 标识all模块是否进行主机层漏扫
}
type BurpFlags struct {
Hosts string // 全局变量标识ip列表或文件路径
Method string // 爆破的服务名称
User string // 爆破时采用的用户字典
Pass string // 爆破时采用的密码字典
Thread int // 爆破时采用的线程数
Timeout int // 爆破的超时时间
IsAlive bool // 爆破前是否检测存活
}
var burp BurpFlags
var all allFlags
var rootCmd = &cobra.Command{
Use: "Yasso",
Short: "\n_____.___. ____ ___\n\\__ | |____ ______ __________\\ \\/ /\n / | \\__ \\ / ___// ___/ _ \\\\ / \n \\____ |/ __ \\_\\___ \\ \\___ ( <_> ) \\ \n / ______(____ /____ >____ >____/___/\\ \\\n \\/ \\/ \\/ \\/ \\_/\n",
}
var allCmd = &cobra.Command{
Use: "all",
Short: "Use all scanner module (.attention) Traffic is very big",
Run: func(cmd *cobra.Command, args []string) {
if all.Hosts == "" {
_ = cmd.Help()
return
}
scanner := plugin.NewAllScanner(all.Hosts, all.Ports, all.NoAlive, all.NoCrack, all.User, all.Pass, all.Thread, time.Duration(all.Timeout)*1000*time.Millisecond, all.NoService, all.NoVulcan)
scanner.RunEnumeration()
},
}
var serviceCmd = &cobra.Command{
Use: "service",
Short: "Detection or blasting services by module",
Run: func(cmd *cobra.Command, args []string) {
if burp.Hosts == "" {
_ = cmd.Help()
return
}
plugin.BruteService(burp.User, burp.Pass, burp.Hosts, burp.Method, burp.Thread, time.Duration(burp.Timeout)*1000*time.Millisecond, burp.IsAlive)
},
}
var ExpCmd = &cobra.Command{
Use: "exploit",
Short: "Exploits to attack the service",
Run: func(cmd *cobra.Command, args []string) {
if cmd.HasSubCommands() {
_ = cmd.Help()
}
},
}
func init() {
rootCmd.PersistentFlags().StringVar(&logger.LogFile, "output", "result.txt", "set logger file")
allCmd.Flags().StringVar(&logger.LogJson, "json", "", "设置json格式输出文件")
allCmd.Flags().StringVarP(&all.Hosts, "hosts", "H", "", "设置扫描的目标参数(.eg) \n[192.168.248.1/24]\n[192.168.248.1-255]\n[example.txt]")
allCmd.Flags().StringVar(&all.Ports, "ports", "", "设置扫描的端口参数(.eg) null将采用默认端口号 top 1000")
allCmd.Flags().IntVar(&all.Timeout, "timeout", 1, "设置扫描的超时时间 默认1秒")
allCmd.Flags().BoolVar(&all.NoCrack, "no-crack", false, "设置扫描时是否爆破脆弱服务")
allCmd.Flags().BoolVar(&all.NoAlive, "no-alive", false, "设置扫描时是否先检测主机存活")
allCmd.Flags().StringVar(&all.User, "user-dic", "", "设置扫描时爆破采用的用户名字典 (.eg) null将采用默认用户名字典")
allCmd.Flags().StringVar(&all.Pass, "pass-dic", "", "设置扫描时爆破采用的密码字典 (.eg) null将采用默认密码字典")
allCmd.Flags().IntVar(&all.Thread, "thread", 500, "设置扫描时的扫描线程 (.eg) 默认500 线程")
allCmd.Flags().BoolVar(&all.NoService, "no-service", false, "设置扫描时是否探测服务")
allCmd.Flags().BoolVar(&all.NoVulcan, "no-vuln", false, "设置扫描时是否检测主机层漏洞")
rootCmd.AddCommand(allCmd)
serviceCmd.Flags().StringVarP(&burp.Hosts, "hosts", "H", "", "设置扫描的目标参数(.eg) \n[192.168.248.1/24]\n[192.168.248.1-255]\n[example.txt]")
serviceCmd.Flags().StringVar(&burp.Method, "module", "", "指定要爆破的服务名称(.eg) \n[mssql,ftp,ssh,mysql,rdp,postgres,redis,winrm,smb,mongo]\n以逗号分割,可同时爆破多个服务(--module ssh:22,mysql:3306,rdp:3389)")
serviceCmd.Flags().IntVar(&burp.Thread, "thread", 500, "设置扫描时的扫描线程 (.eg) 默认500 线程")
serviceCmd.Flags().StringVar(&burp.User, "user-dic", "", "设置扫描时爆破采用的用户名字典 (.eg) null将采用默认用户名字典")
serviceCmd.Flags().StringVar(&burp.Pass, "pass-dic", "", "设置扫描时爆破采用的密码字典 (.eg) null将采用默认密码字典")
serviceCmd.Flags().IntVar(&burp.Timeout, "timeout", 1, "设置爆破的超时时间 默认1秒")
serviceCmd.Flags().BoolVar(&burp.IsAlive, "is-alive", true, "爆破前是否进行ping检测存活")
rootCmd.AddCommand(serviceCmd)
rootCmd.AddCommand(ExpCmd)
// 利用模块命令
ExpCmd.AddCommand(exploit.MssqlCmd)
ExpCmd.AddCommand(exploit.SshCmd)
ExpCmd.AddCommand(exploit.WinRmCmd)
ExpCmd.AddCommand(exploit.RedisCmd)
ExpCmd.AddCommand(exploit.SunLoginCmd)
ExpCmd.AddCommand(exploit.LdapReaperCmd)
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(0)
}
}

199
core/logger/logger.go Normal file
View File

@@ -0,0 +1,199 @@
package logger
import (
"Yasso/config"
"encoding/json"
"fmt"
"github.com/gookit/color"
"os"
"sync"
)
var (
Cyan = color.Cyan.Render
Red = color.Red.Render
LightGreen = color.Style{color.Green, color.OpBold}.Render
LightRed = color.Style{color.Red, color.OpBold}.Render
)
const (
PortSave = 1
HostSave = 2
WeakPassSave = 3
InformationSave = 4
VulnerabilitySave = 5
)
var LogFile string
var LogJson string
var mutex sync.Mutex
func Info(in ...interface{}) {
mutex.Lock()
var all []interface{}
for k, v := range in {
if k == len(in)-1 {
all = append(all, fmt.Sprintf("%v", v))
} else {
all = append(all, fmt.Sprintf("%v ", v))
}
}
fmt.Println(fmt.Sprintf("[%s] ", Cyan("*")) + fmt.Sprint(all...))
file, err := os.OpenFile(LogFile, os.O_APPEND|os.O_CREATE|os.O_SYNC, 0666)
if err != nil {
Fatal("open file has an error", err.Error())
return
}
defer file.Close()
_, _ = file.WriteString(fmt.Sprintf("[*] " + fmt.Sprint(all...) + "\n"))
mutex.Unlock()
}
func Success(in ...interface{}) {
mutex.Lock()
var all []interface{}
for k, v := range in {
if k == len(in)-1 {
all = append(all, fmt.Sprintf("%v", v))
} else {
all = append(all, fmt.Sprintf("%v ", v))
}
}
fmt.Println(fmt.Sprintf("[%s] ", LightGreen("+")) + fmt.Sprint(all...))
file, err := os.OpenFile(LogFile, os.O_APPEND|os.O_CREATE|os.O_SYNC, 0666)
if err != nil {
Fatal("open file has an error", err.Error())
return
}
defer file.Close()
_, err = file.WriteString(fmt.Sprintf("[+] " + fmt.Sprint(all...) + "\n"))
mutex.Unlock()
}
func Fatal(in ...interface{}) {
var all []interface{}
for k, v := range in {
if k == len(in)-1 {
all = append(all, fmt.Sprintf("%v", v))
} else {
all = append(all, fmt.Sprintf("%v ", v))
}
}
fmt.Println(fmt.Sprintf("[%s] ", Red("#")) + fmt.Sprint(all...))
}
// JSONSave 保存json格式数据
func JSONSave(host string, t int, in ...interface{}) {
if LogJson != "" {
switch t {
case VulnerabilitySave:
for _, v := range config.JSONSave {
// 服务存在
if v.Host == host {
v.Vulnerability = append(v.Vulnerability, in[0].(string))
}
}
case PortSave:
// 端口存储
var flag = false
for _, v := range config.JSONSave {
// 服务存在
if v.Host == host {
v.Port = in[0].([]int) // 将端口存储
flag = true
}
}
if flag == false {
config.JSONSave = append(config.JSONSave, &config.Format{
Host: host,
})
for _, v := range config.JSONSave {
// 服务存在
if v.Host == host {
v.Port = in[0].([]int) // 将端口存储
flag = true
}
}
}
case HostSave:
// 主机存储
config.JSONSave = append(config.JSONSave, &config.Format{
Host: host,
})
case WeakPassSave:
// 这里存储json的服务弱口令
for _, v := range config.JSONSave {
// 服务名称已经有了,那么将口令加到它的WeakPass种
// 如果主机之前也是存活的
if v.Host == host {
// 遍历主机的服务列表
var flag = false
for _, value := range v.Service {
if value.Name == in[0].(string) { // 服务名
value.WeakPass = append(value.WeakPass, in[1].(map[string]string))
flag = true // 证明服务存在
}
}
// 证明host存在
if flag == false {
v.Service = append(v.Service, &config.Service{
Name: in[0].(string), //服务名
WeakPass: []map[string]string{in[1].(map[string]string)},
})
}
// 证明host存在
if flag == false {
v.Service = append(v.Service, &config.Service{
Name: in[0].(string), //服务名
WeakPass: []map[string]string{in[1].(map[string]string)},
})
}
}
}
case 4:
// 这里information字段
for _, v := range config.JSONSave {
// 服务名称已经有了,那么将口令加到它的WeakPass种
// 如果主机之前也是存活的
if v.Host == host {
// 遍历主机的服务列表
var flag = false
for _, value := range v.Service {
if value.Name == in[0].(string) { // 服务名
value.Information = append(value.Information, in[1].(string))
flag = true // 证明服务存在
}
}
// 证明host存在
if flag == false {
v.Service = append(v.Service, &config.Service{
Name: in[0].(string), //服务名
Information: []string{in[1].(string)},
})
}
}
}
}
// 将以json格式保存,文件将会保存全局变量存储的结果集
}
}
func LoggerSave() {
if LogJson != "" {
body, err := json.Marshal(config.JSONSave)
if err != nil {
Fatal("save json marshal failed", err.Error())
return
}
filePtr, err := os.Create(LogJson)
if err != nil {
fmt.Println("文件创建失败", err.Error())
return
}
defer filePtr.Close()
// 创建Json编码器
_, _ = filePtr.Write(body)
}
}

301
core/parse/parse_ip.go Normal file
View File

@@ -0,0 +1,301 @@
package parse
import (
"Yasso/core/logger"
"bufio"
"errors"
"fmt"
"github.com/projectdiscovery/cdncheck"
"net"
"os"
"path"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"sync"
)
// ReadFile 从文件中读取数据
func ReadFile(filename string) ([]string, error) {
file, err := os.Open(filename)
if err != nil {
logger.Fatal("open file has an error", err.Error())
return nil, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
var re []string
for scanner.Scan() {
text := strings.TrimSpace(scanner.Text())
if text != "" {
re = append(re, text)
}
}
re = Duplicate(re) // 去重
return re, nil
}
// ConvertDomainToIpAddress 将域名转换成ip地址
func ConvertDomainToIpAddress(domains []string, thread int) ([]string, error) {
checkChan := make(chan string, 100)
var wg sync.WaitGroup
var re []string
for i := 0; i < thread; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for host := range checkChan {
if strings.Count(host, ".") == 3 && len(strings.Split(host, ":")) == 2 {
// 这种是带有端口的ip地址
re = append(re, host)
continue
}
ip, err := net.LookupHost(host)
if err != nil {
continue
}
if ip != nil {
// 证明存在cdn直接丢掉即可(不跑带有cdn的域名)
if len(ip) >= 2 {
logger.Info(fmt.Sprintf("%s has cdn %v", host, ip[:]))
continue
} else {
for _, i := range ip {
re = append(re, i)
}
}
}
}
}()
}
for _, domain := range domains {
if strings.Contains(domain, "http://") {
domain = strings.TrimPrefix(domain, "http://")
}
if strings.Contains(domain, "https://") {
domain = strings.TrimPrefix(domain, "https://")
}
checkChan <- domain
}
close(checkChan)
wg.Wait()
re = Duplicate(re) // 去重
return re, nil
}
// cdnFilter cdn过滤器
func cdnFilter(ip string, client *cdncheck.Client) string {
if found, _, err := client.Check(net.ParseIP(ip)); found && err == nil {
return ip
}
return ""
}
// Duplicate 去重
func Duplicate(slc []string) []string {
var re []string
temp := map[string]byte{}
for _, v := range slc {
l := len(temp)
temp[v] = 0
if len(temp) != l {
re = append(re, v)
}
}
return re
}
// RegIpv4Address 匹配ipv4
func RegIpv4Address(context string) string {
matched, err := regexp.MatchString("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}", context)
if err != nil {
return ""
}
if matched {
return context
}
return ""
}
func HandleIps(ip string) ([]string, error) {
var Unprocessed []string
var err error
var basic []string
if strings.Contains(ip, ".txt") {
if strings.ToLower(path.Ext(filepath.Base(ip))) == ".txt" {
// 文件后缀为.txt的话,我们按照文件解析并获取数据结果
Unprocessed, err = ReadFile(ip)
if err != nil {
return []string{}, err
}
}
/*
这里获取到的数据格式可能为
192.168.248.1/24
192.168.248.1-155
www.baidu.com
192.168.248.1:3389
https://www.baidu.com
*/
// 第一波解析开始解析ip地址格式
for _, i := range Unprocessed {
switch {
case RegIpv4Address(i) != "" && (strings.Count(i, "/24") == 1 || strings.Count(i, "/16") == 1):
temp, err := ConvertIpFormatA(i)
if err != nil {
logger.Fatal("parse ip address has an error", err.Error())
return []string{}, err
}
basic = append(basic, temp...)
case RegIpv4Address(i) != "" && strings.Count(i, "-") == 1 && !strings.Contains(i, "/"):
fmt.Println(i)
temp, err := ConvertIpFormatB(i)
if err != nil {
logger.Fatal("parse ip address has an error", err.Error())
return []string{}, err
}
basic = append(basic, temp...)
case strings.Contains(i, "https://") || strings.Contains(i, "http://"):
if strings.Contains(i, "https://") {
basic = append(basic, strings.ReplaceAll(i, "https://", ""))
}
if strings.Contains(i, "http://") {
basic = append(basic, strings.ReplaceAll(i, "https", ""))
}
default:
basic = append(basic, i)
}
}
// 第一波解析完成,开始第二波解析,解析域名
basic = Duplicate(basic) // 第一次去重
basic, err = ConvertDomainToIpAddress(basic, 100)
if err != nil {
logger.Fatal("parse domain has an error", err.Error())
return nil, err
}
basic = Duplicate(basic) // 第二次去重
} else {
basic, err = ConvertIpFormatAll(ip)
if err != nil {
logger.Fatal("parse ip address has an error", err.Error())
return []string{}, err
}
}
// 二次筛选
var newBasic []string
for _, ip := range basic {
if strings.Contains(ip, "/") {
newBasic = append(newBasic, strings.Split(ip, "/")[0])
} else {
newBasic = append(newBasic, ip)
}
}
// 对获取到的ip地址进行排序进行后续操作
sort.Strings(newBasic)
return newBasic, err
}
// ConvertIpFormatA 不解析192.168.248.1/8格式目前
func ConvertIpFormatA(ip string) ([]string, error) {
var ip4 = net.ParseIP(strings.Split(ip, "/")[0])
if ip4 == nil {
return []string{}, errors.New("not an ipv4 address")
}
var mark = strings.Split(ip, "/")[1]
var temp []string
var err error
switch mark {
case "24":
var ip3 = strings.Join(strings.Split(ip[:], ".")[0:3], ".")
for i := 0; i <= 255; i++ {
temp = append(temp, ip3+"."+strconv.Itoa(i))
}
err = nil
case "16":
var ip2 = strings.Join(strings.Split(ip[:], ".")[0:2], ".")
for i := 0; i <= 255; i++ {
for j := 0; j <= 255; j++ {
temp = append(temp, ip2+"."+strconv.Itoa(i)+"."+strconv.Itoa(j))
}
}
err = nil
default:
temp = []string{}
err = errors.New("not currently supported")
}
return temp, err
}
func ConvertIpFormatB(ip string) ([]string, error) {
var ip4 = strings.Split(ip, "-")
var ipA = net.ParseIP(ip4[0])
if ip4 == nil {
return []string{}, errors.New("not an ipv4 address")
}
var temp []string
if len(ip4[1]) < 4 {
iprange, err := strconv.Atoi(ip4[1])
if ipA == nil || iprange > 255 || err != nil {
return []string{}, errors.New("input format is not ccorrect")
}
var splitip = strings.Split(ip4[0], ".")
ip1, err1 := strconv.Atoi(splitip[3])
ip2, err2 := strconv.Atoi(ip4[1])
prefixip := strings.Join(splitip[0:3], ".")
if ip1 > ip2 || err1 != nil || err2 != nil {
return []string{}, errors.New("input format is not ccorrect")
}
for i := ip1; i <= ip2; i++ {
temp = append(temp, prefixip+"."+strconv.Itoa(i))
}
} else {
var splitip1 = strings.Split(ip4[0], ".")
var splitip2 = strings.Split(ip4[1], ".")
if len(splitip1) != 4 || len(splitip2) != 4 {
return []string{}, errors.New("input format is not ccorrect")
}
start, end := [4]int{}, [4]int{}
for i := 0; i < 4; i++ {
ip1, err1 := strconv.Atoi(splitip1[i])
ip2, err2 := strconv.Atoi(splitip2[i])
if ip1 > ip2 || err1 != nil || err2 != nil {
return []string{}, errors.New("input format is not ccorrect")
}
start[i], end[i] = ip1, ip2
}
startNum := start[0]<<24 | start[1]<<16 | start[2]<<8 | start[3]
endNum := end[0]<<24 | end[1]<<16 | end[2]<<8 | end[3]
for num := startNum; num <= endNum; num++ {
ip := strconv.Itoa((num>>24)&0xff) + "." + strconv.Itoa((num>>16)&0xff) + "." + strconv.Itoa((num>>8)&0xff) + "." + strconv.Itoa((num)&0xff)
temp = append(temp, ip)
}
}
return temp, nil
}
func ConvertIpFormatAll(ip string) ([]string, error) {
reg := regexp.MustCompile(`[a-zA-Z]+`)
switch {
case strings.Count(ip, "/") == 1:
return ConvertIpFormatA(ip)
case strings.Count(ip, "-") == 1:
return ConvertIpFormatB(ip)
case reg.MatchString(ip):
_, err := net.LookupHost(ip)
if err != nil {
return []string{}, err
}
return []string{ip}, nil
default:
var isip = net.ParseIP(ip)
if isip == nil {
return []string{}, errors.New("input format is not ccorrect")
}
return []string{ip}, nil
}
}

50
core/parse/parse_port.go Normal file
View File

@@ -0,0 +1,50 @@
package parse
import (
"strconv"
"strings"
)
// HandlePorts 解析端口格式
func HandlePorts(ports string) ([]int, error) {
var scanPorts []int
slices := strings.Split(ports, ",")
for _, port := range slices {
port = strings.Trim(port, " ")
upper := port
if strings.Contains(port, "-") {
ranges := strings.Split(port, "-")
if len(ranges) < 2 {
continue
}
startPort, _ := strconv.Atoi(ranges[0])
endPort, _ := strconv.Atoi(ranges[1])
if startPort < endPort {
port = ranges[0]
upper = ranges[1]
} else {
port = ranges[1]
upper = ranges[0]
}
}
start, _ := strconv.Atoi(port)
end, _ := strconv.Atoi(upper)
for i := start; i <= end; i++ {
scanPorts = append(scanPorts, i)
}
}
scanPorts = RemoveDuplicate(scanPorts)
return scanPorts, nil
}
func RemoveDuplicate(old []int) []int {
result := make([]int, 0, len(old))
temp := map[int]struct{}{}
for _, item := range old {
if _, ok := temp[item]; !ok {
temp[item] = struct{}{}
result = append(result, item)
}
}
return result
}

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
}

26
core/utils/utils.go Normal file
View File

@@ -0,0 +1,26 @@
package utils
import (
"encoding/hex"
"fmt"
"strings"
)
func ByteToStringParse(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
}