* feat(ms): import gost:MaineK00n/new-windows * chore(discover): add CTI section * feat(ms): fill KB with VulnInfo.DistroAdvisories instead of CveContent.Optional * fix(ms): Change bitSize from 32 to 64 * fix(ms): delete KB prefix * chore(ms): change logger * fix(ms): fill in correct AdvisoryID Co-authored-by: Sadayuki Matsuno <sadayuki.matsuno@gmail.com>
219 lines
5.8 KiB
Go
219 lines
5.8 KiB
Go
//go:build !scanner
|
|
// +build !scanner
|
|
|
|
package gost
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/exp/maps"
|
|
"golang.org/x/exp/slices"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/future-architect/vuls/logging"
|
|
"github.com/future-architect/vuls/models"
|
|
gostmodels "github.com/vulsio/gost/models"
|
|
)
|
|
|
|
// Microsoft is Gost client for windows
|
|
type Microsoft struct {
|
|
Base
|
|
}
|
|
|
|
var kbIDPattern = regexp.MustCompile(`KB(\d{6,7})`)
|
|
|
|
// DetectCVEs fills cve information that has in Gost
|
|
func (ms Microsoft) DetectCVEs(r *models.ScanResult, _ bool) (nCVEs int, err error) {
|
|
if ms.driver == nil {
|
|
return 0, nil
|
|
}
|
|
|
|
var osName string
|
|
osName, ok := r.Optional["OSName"].(string)
|
|
if !ok {
|
|
logging.Log.Warnf("This Windows has wrong type option(OSName). UUID: %s", r.ServerUUID)
|
|
}
|
|
|
|
var products []string
|
|
if _, ok := r.Optional["InstalledProducts"]; ok {
|
|
switch ps := r.Optional["InstalledProducts"].(type) {
|
|
case []interface{}:
|
|
for _, p := range ps {
|
|
pname, ok := p.(string)
|
|
if !ok {
|
|
logging.Log.Warnf("skip products: %v", p)
|
|
continue
|
|
}
|
|
products = append(products, pname)
|
|
}
|
|
case []string:
|
|
for _, p := range ps {
|
|
products = append(products, p)
|
|
}
|
|
case nil:
|
|
logging.Log.Warnf("This Windows has no option(InstalledProducts). UUID: %s", r.ServerUUID)
|
|
}
|
|
}
|
|
|
|
applied, unapplied := map[string]struct{}{}, map[string]struct{}{}
|
|
if _, ok := r.Optional["KBID"]; ok {
|
|
switch kbIDs := r.Optional["KBID"].(type) {
|
|
case []interface{}:
|
|
for _, kbID := range kbIDs {
|
|
s, ok := kbID.(string)
|
|
if !ok {
|
|
logging.Log.Warnf("skip KBID: %v", kbID)
|
|
continue
|
|
}
|
|
unapplied[strings.TrimPrefix(s, "KB")] = struct{}{}
|
|
}
|
|
case []string:
|
|
for _, kbID := range kbIDs {
|
|
unapplied[strings.TrimPrefix(kbID, "KB")] = struct{}{}
|
|
}
|
|
case nil:
|
|
logging.Log.Warnf("This Windows has no option(KBID). UUID: %s", r.ServerUUID)
|
|
}
|
|
|
|
for _, pkg := range r.Packages {
|
|
matches := kbIDPattern.FindAllStringSubmatch(pkg.Name, -1)
|
|
for _, match := range matches {
|
|
applied[match[1]] = struct{}{}
|
|
}
|
|
}
|
|
} else {
|
|
switch kbIDs := r.Optional["AppliedKBID"].(type) {
|
|
case []interface{}:
|
|
for _, kbID := range kbIDs {
|
|
s, ok := kbID.(string)
|
|
if !ok {
|
|
logging.Log.Warnf("skip KBID: %v", kbID)
|
|
continue
|
|
}
|
|
applied[strings.TrimPrefix(s, "KB")] = struct{}{}
|
|
}
|
|
case []string:
|
|
for _, kbID := range kbIDs {
|
|
applied[strings.TrimPrefix(kbID, "KB")] = struct{}{}
|
|
}
|
|
case nil:
|
|
logging.Log.Warnf("This Windows has no option(AppliedKBID). UUID: %s", r.ServerUUID)
|
|
}
|
|
|
|
switch kbIDs := r.Optional["UnappliedKBID"].(type) {
|
|
case []interface{}:
|
|
for _, kbID := range kbIDs {
|
|
s, ok := kbID.(string)
|
|
if !ok {
|
|
logging.Log.Warnf("skip KBID: %v", kbID)
|
|
continue
|
|
}
|
|
unapplied[strings.TrimPrefix(s, "KB")] = struct{}{}
|
|
}
|
|
case []string:
|
|
for _, kbID := range kbIDs {
|
|
unapplied[strings.TrimPrefix(kbID, "KB")] = struct{}{}
|
|
}
|
|
case nil:
|
|
logging.Log.Warnf("This Windows has no option(UnappliedKBID). UUID: %s", r.ServerUUID)
|
|
}
|
|
}
|
|
|
|
logging.Log.Debugf(`GetCvesByMicrosoftKBID query body {"osName": %s, "installedProducts": %q, "applied": %q, "unapplied: %q"}`, osName, products, maps.Keys(applied), maps.Keys(unapplied))
|
|
cves, err := ms.driver.GetCvesByMicrosoftKBID(osName, products, maps.Keys(applied), maps.Keys(unapplied))
|
|
if err != nil {
|
|
return 0, xerrors.Errorf("Failed to detect CVEs. err: %w", err)
|
|
}
|
|
|
|
for cveID, cve := range cves {
|
|
cveCont, mitigations := ms.ConvertToModel(&cve)
|
|
advisories := []models.DistroAdvisory{}
|
|
for _, p := range cve.Products {
|
|
for _, kb := range p.KBs {
|
|
adv := models.DistroAdvisory{
|
|
AdvisoryID: kb.Article,
|
|
Description: "Microsoft Knowledge Base",
|
|
}
|
|
if _, err := strconv.Atoi(kb.Article); err == nil {
|
|
adv.AdvisoryID = fmt.Sprintf("KB%s", kb.Article)
|
|
}
|
|
advisories = append(advisories, adv)
|
|
}
|
|
}
|
|
|
|
r.ScannedCves[cveID] = models.VulnInfo{
|
|
CveID: cveID,
|
|
Confidences: models.Confidences{models.WindowsUpdateSearch},
|
|
DistroAdvisories: advisories,
|
|
CveContents: models.NewCveContents(*cveCont),
|
|
Mitigations: mitigations,
|
|
}
|
|
}
|
|
return len(cves), nil
|
|
}
|
|
|
|
// ConvertToModel converts gost model to vuls model
|
|
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) (*models.CveContent, []models.Mitigation) {
|
|
slices.SortFunc(cve.Products, func(i, j gostmodels.MicrosoftProduct) bool {
|
|
return i.ScoreSet.Vector < j.ScoreSet.Vector
|
|
})
|
|
|
|
v3score := 0.0
|
|
var v3Vector string
|
|
for _, p := range cve.Products {
|
|
v, err := strconv.ParseFloat(p.ScoreSet.BaseScore, 64)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if v3score < v {
|
|
v3score = v
|
|
v3Vector = p.ScoreSet.Vector
|
|
}
|
|
}
|
|
|
|
var v3Severity string
|
|
for _, p := range cve.Products {
|
|
v3Severity = p.Severity
|
|
}
|
|
|
|
option := map[string]string{}
|
|
if 0 < len(cve.ExploitStatus) {
|
|
// TODO: CVE-2020-0739
|
|
// "exploit_status": "Publicly Disclosed:No;Exploited:No;Latest Software Release:Exploitation Less Likely;Older Software Release:Exploitation Less Likely;DOS:N/A",
|
|
option["exploit"] = cve.ExploitStatus
|
|
}
|
|
|
|
mitigations := []models.Mitigation{}
|
|
if cve.Mitigation != "" {
|
|
mitigations = append(mitigations, models.Mitigation{
|
|
CveContentType: models.Microsoft,
|
|
Mitigation: cve.Mitigation,
|
|
URL: cve.URL,
|
|
})
|
|
}
|
|
if cve.Workaround != "" {
|
|
mitigations = append(mitigations, models.Mitigation{
|
|
CveContentType: models.Microsoft,
|
|
Mitigation: cve.Workaround,
|
|
URL: cve.URL,
|
|
})
|
|
}
|
|
|
|
return &models.CveContent{
|
|
Type: models.Microsoft,
|
|
CveID: cve.CveID,
|
|
Title: cve.Title,
|
|
Summary: cve.Description,
|
|
Cvss3Score: v3score,
|
|
Cvss3Vector: v3Vector,
|
|
Cvss3Severity: v3Severity,
|
|
Published: cve.PublishDate,
|
|
LastModified: cve.LastUpdateDate,
|
|
SourceLink: cve.URL,
|
|
Optional: option,
|
|
}, mitigations
|
|
}
|