* 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
 | 
						|
}
 |