diff --git a/README.md b/README.md index 9a9b0770..dd913310 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,9 @@ Vuls is a tool created to solve the problems listed above. It has the following - [US-CERT](https://www.us-cert.gov/ncas/alerts) - [JPCERT](http://www.jpcert.or.jp/at/2019.html) +- CISA(Cybersecurity & Infrastructure Security Agency) + - [Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) + - Libraries - [Node.js Security Working Group](https://github.com/nodejs/security-wg) - [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db) diff --git a/config/config.go b/config/config.go index 935f37c8..66755cc8 100644 --- a/config/config.go +++ b/config/config.go @@ -41,6 +41,7 @@ type Config struct { Gost GostConf `json:"gost,omitempty"` Exploit ExploitConf `json:"exploit,omitempty"` Metasploit MetasploitConf `json:"metasploit,omitempty"` + KEVuln KEVulnConf `json:"kevuln,omitempty"` Slack SlackConf `json:"-"` EMail SMTPConf `json:"-"` @@ -176,6 +177,7 @@ func (c *Config) ValidateOnReport() bool { &Conf.Gost, &Conf.Exploit, &Conf.Metasploit, + &Conf.KEVuln, } { if err := cnf.Validate(); err != nil { errs = append(errs, xerrors.Errorf("Failed to validate %s: %+v", cnf.GetName(), err)) diff --git a/config/tomlloader.go b/config/tomlloader.go index d3ac85cc..0c90218a 100644 --- a/config/tomlloader.go +++ b/config/tomlloader.go @@ -27,6 +27,7 @@ func (c TOMLLoader) Load(pathToToml, _ string) error { &Conf.Gost, &Conf.Exploit, &Conf.Metasploit, + &Conf.KEVuln, } { cnf.Init() } diff --git a/config/vulnDictConf.go b/config/vulnDictConf.go index 6d7cc023..ce94355a 100644 --- a/config/vulnDictConf.go +++ b/config/vulnDictConf.go @@ -248,7 +248,7 @@ func (cnf *GostConf) Init() { cnf.DebugSQL = Conf.DebugSQL } -// MetasploitConf is gost go-metasploitdb +// MetasploitConf is go-msfdb config type MetasploitConf struct { VulnDict } @@ -274,3 +274,30 @@ func (cnf *MetasploitConf) Init() { cnf.setDefault("go-msfdb.sqlite3") cnf.DebugSQL = Conf.DebugSQL } + +// KEVulnConf is go-kev config +type KEVulnConf struct { + VulnDict +} + +const kevulnDBType = "KEVULN_TYPE" +const kevulnDBURL = "KEVULN_URL" +const kevulnDBPATH = "KEVULN_SQLITE3_PATH" + +// Init set options with the following priority. +// 1. Environment variable +// 2. config.toml +func (cnf *KEVulnConf) Init() { + cnf.Name = "kevuln" + if os.Getenv(kevulnDBType) != "" { + cnf.Type = os.Getenv(kevulnDBType) + } + if os.Getenv(kevulnDBURL) != "" { + cnf.URL = os.Getenv(kevulnDBURL) + } + if os.Getenv(kevulnDBPATH) != "" { + cnf.SQLite3Path = os.Getenv(kevulnDBPATH) + } + cnf.setDefault("go-kev.sqlite3") + cnf.DebugSQL = Conf.DebugSQL +} diff --git a/detector/detector.go b/detector/detector.go index 95352518..908b47b3 100644 --- a/detector/detector.go +++ b/detector/detector.go @@ -111,6 +111,10 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) { } logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve) + if err := FillWithKEVuln(&r, config.Conf.KEVuln); err != nil { + return nil, xerrors.Errorf("Failed to fill with Known Exploited Vulnerabilities: %w", err) + } + FillCweDict(&r) r.ReportedBy, _ = os.Hostname() @@ -361,20 +365,20 @@ func FillCvesWithNvdJvn(r *models.ScanResult, cnf config.GoCveDictConf, logOpts func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) { for _, nvd := range cvedetail.Nvds { for _, cert := range nvd.Certs { - dict.En = append(dict.En, models.Alert{ + dict.USCERT = append(dict.USCERT, models.Alert{ URL: cert.Link, Title: cert.Title, - Team: "us", + Team: "uscert", }) } } for _, jvn := range cvedetail.Jvns { for _, cert := range jvn.Certs { - dict.Ja = append(dict.Ja, models.Alert{ + dict.JPCERT = append(dict.JPCERT, models.Alert{ URL: cert.Link, Title: cert.Title, - Team: "jp", + Team: "jpcert", }) } } diff --git a/detector/kevuln.go b/detector/kevuln.go new file mode 100644 index 00000000..4b324f35 --- /dev/null +++ b/detector/kevuln.go @@ -0,0 +1,214 @@ +//go:build !scanner +// +build !scanner + +package detector + +import ( + "encoding/json" + "net/http" + "time" + + "github.com/cenkalti/backoff" + "github.com/future-architect/vuls/config" + "github.com/future-architect/vuls/logging" + "github.com/future-architect/vuls/models" + "github.com/future-architect/vuls/util" + "github.com/parnurzeal/gorequest" + "golang.org/x/xerrors" + + kevulndb "github.com/vulsio/go-kev/db" + kevulnmodels "github.com/vulsio/go-kev/models" +) + +// FillWithKEVuln : +func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf) error { + if cnf.IsFetchViaHTTP() { + var cveIDs []string + for cveID := range r.ScannedCves { + cveIDs = append(cveIDs, cveID) + } + prefix, err := util.URLPathJoin(cnf.GetURL(), "cves") + if err != nil { + return err + } + responses, err := getKEVulnsViaHTTP(cveIDs, prefix) + if err != nil { + return err + } + for _, res := range responses { + kevulns := []kevulnmodels.KEVuln{} + if err := json.Unmarshal([]byte(res.json), &kevulns); err != nil { + return err + } + + alerts := []models.Alert{} + if len(kevulns) > 0 { + alerts = append(alerts, models.Alert{ + Title: "Known Exploited Vulnerabilities Catalog", + URL: "https://www.cisa.gov/known-exploited-vulnerabilities-catalog", + Team: "cisa", + }) + } + + v, ok := r.ScannedCves[res.request.cveID] + if ok { + v.AlertDict.CISA = alerts + } + r.ScannedCves[res.request.cveID] = v + } + } else { + driver, locked, err := newKEVulnDB(&cnf) + if locked { + return xerrors.Errorf("SQLite3 is locked: %s", cnf.GetSQLite3Path()) + } else if err != nil { + return err + } + defer func() { + if err := driver.CloseDB(); err != nil { + logging.Log.Errorf("Failed to close DB. err: %+v", err) + } + }() + + for cveID, vuln := range r.ScannedCves { + if cveID == "" { + continue + } + kevulns, err := driver.GetKEVulnByCveID(cveID) + if err != nil { + return err + } + if len(kevulns) == 0 { + continue + } + + alerts := []models.Alert{} + if len(kevulns) > 0 { + alerts = append(alerts, models.Alert{ + Title: "Known Exploited Vulnerabilities Catalog", + URL: "https://www.cisa.gov/known-exploited-vulnerabilities-catalog", + Team: "cisa", + }) + } + + vuln.AlertDict.CISA = alerts + r.ScannedCves[cveID] = vuln + } + } + return nil +} + +type kevulnResponse struct { + request kevulnRequest + json string +} + +func getKEVulnsViaHTTP(cveIDs []string, urlPrefix string) ( + responses []kevulnResponse, err error) { + nReq := len(cveIDs) + reqChan := make(chan kevulnRequest, nReq) + resChan := make(chan kevulnResponse, nReq) + errChan := make(chan error, nReq) + defer close(reqChan) + defer close(resChan) + defer close(errChan) + + go func() { + for _, cveID := range cveIDs { + reqChan <- kevulnRequest{ + cveID: cveID, + } + } + }() + + concurrency := 10 + tasks := util.GenWorkers(concurrency) + for i := 0; i < nReq; i++ { + tasks <- func() { + req := <-reqChan + url, err := util.URLPathJoin( + urlPrefix, + req.cveID, + ) + if err != nil { + errChan <- err + } else { + logging.Log.Debugf("HTTP Request to %s", url) + httpGetKEVuln(url, req, resChan, errChan) + } + } + } + + timeout := time.After(2 * 60 * time.Second) + var errs []error + for i := 0; i < nReq; i++ { + select { + case res := <-resChan: + responses = append(responses, res) + case err := <-errChan: + errs = append(errs, err) + case <-timeout: + return nil, xerrors.New("Timeout Fetching KEVuln") + } + } + if len(errs) != 0 { + return nil, xerrors.Errorf("Failed to fetch KEVuln. err: %w", errs) + } + return +} + +type kevulnRequest struct { + cveID string +} + +func httpGetKEVuln(url string, req kevulnRequest, resChan chan<- kevulnResponse, errChan chan<- error) { + var body string + var errs []error + var resp *http.Response + count, retryMax := 0, 3 + f := func() (err error) { + // resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End() + resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End() + if 0 < len(errs) || resp == nil || resp.StatusCode != 200 { + count++ + if count == retryMax { + return nil + } + return xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %+v", url, resp, errs) + } + return nil + } + notify := func(err error, t time.Duration) { + logging.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %+v", t, err) + } + err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify) + if err != nil { + errChan <- xerrors.Errorf("HTTP Error %w", err) + return + } + if count == retryMax { + errChan <- xerrors.New("Retry count exceeded") + return + } + + resChan <- kevulnResponse{ + request: req, + json: body, + } +} + +func newKEVulnDB(cnf config.VulnDictInterface) (driver kevulndb.DB, locked bool, err error) { + if cnf.IsFetchViaHTTP() { + return nil, false, nil + } + path := cnf.GetURL() + if cnf.GetType() == "sqlite3" { + path = cnf.GetSQLite3Path() + } + if driver, locked, err = kevulndb.NewDB(cnf.GetType(), path, cnf.GetDebugSQL(), kevulndb.Option{}); err != nil { + if locked { + return nil, true, xerrors.Errorf("kevulnDB is locked. err: %w", err) + } + return nil, false, err + } + return driver, false, nil +} diff --git a/go.mod b/go.mod index 45cc7b19..9205689e 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( github.com/spf13/cobra v1.2.1 github.com/vulsio/go-cve-dictionary v0.8.2-0.20211028094424-0a854f8e8f85 github.com/vulsio/go-exploitdb v0.4.2-0.20211028071949-1ebf9c4f6c4d + github.com/vulsio/go-kev v0.0.1 github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14 github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8 github.com/vulsio/goval-dictionary v0.6.1-0.20211028072035-e85e14b91ccc @@ -62,10 +63,10 @@ require ( golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 - gopkg.in/ini.v1 v1.63.2 // indirect - gorm.io/driver/mysql v1.1.2 // indirect - gorm.io/driver/postgres v1.1.2 // indirect - gorm.io/driver/sqlite v1.1.6 // indirect + gopkg.in/ini.v1 v1.64.0 // indirect + gorm.io/driver/mysql v1.2.0 // indirect + gorm.io/driver/postgres v1.2.2 // indirect + gorm.io/driver/sqlite v1.2.4 // indirect k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 ) @@ -118,7 +119,7 @@ require ( github.com/jackc/pgtype v1.8.1 // indirect github.com/jackc/pgx/v4 v4.13.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.2 // indirect + github.com/jinzhu/now v1.1.3 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/magiconair/properties v1.8.5 // indirect github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 // indirect @@ -142,13 +143,13 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect - golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect + golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - gorm.io/gorm v1.21.16 // indirect + gorm.io/gorm v1.22.3 // indirect moul.io/http2curl v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index f98b1819..61732d7d 100644 --- a/go.sum +++ b/go.sum @@ -524,8 +524,9 @@ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkg github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20200809112317-0581fc3aee2d/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e h1:/cwV7t2xezilMljIftb7WlFtzGANRCnoOhPjtl2ifcs= github.com/elazarl/goproxy v0.0.0-20210110162100-a92cc753f88e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v0.0.0-20211114080932-d06c3be7c11b h1:1XqENn2YoYZd6w3Awx+7oa+aR87DFIZJFLF2n1IojA0= +github.com/elazarl/goproxy v0.0.0-20211114080932-d06c3be7c11b/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ= @@ -644,6 +645,7 @@ github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2 github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gocarina/gocsv v0.0.0-20201208093247-67c824bc04d4/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= +github.com/gocarina/gocsv v0.0.0-20211020200912-82fc2684cc48/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= github.com/goccy/go-yaml v1.8.1/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y= github.com/goccy/go-yaml v1.8.2 h1:gDYrSN12XK/wQTFjxWIgcIqjNCV/Zb5V09M7cq+dbCs= github.com/goccy/go-yaml v1.8.2/go.mod h1:wS4gNoLalDSJxo/SpngzPQ2BN4uuZVLCmbM4S3vd4+Y= @@ -1018,8 +1020,9 @@ github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:x github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI= +github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -1108,6 +1111,7 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/liamg/clinch v1.5.6/go.mod h1:IXM+nLBuZ5sOQAYYf9+G51nkaA0WY9cszxE5nPXexhE= github.com/liamg/gifwrap v0.0.6/go.mod h1:oW1r2vIWLYyxW+U0io7YbpPSDIJ79FTlZ+hPnXFLW6E= @@ -1178,7 +1182,6 @@ github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lL github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= @@ -1613,6 +1616,8 @@ github.com/vulsio/go-cve-dictionary v0.8.2-0.20211028094424-0a854f8e8f85 h1:nEha github.com/vulsio/go-cve-dictionary v0.8.2-0.20211028094424-0a854f8e8f85/go.mod h1:Ii9TEH35giMSWJM2FwGm1PCPxuBKrbaYhDun2PM7ERo= github.com/vulsio/go-exploitdb v0.4.2-0.20211028071949-1ebf9c4f6c4d h1:iMXVmz2f1Phor1TAmRKx324mDOuXst0GXGEboVRgysg= github.com/vulsio/go-exploitdb v0.4.2-0.20211028071949-1ebf9c4f6c4d/go.mod h1:2R5gwySHHjF3DoEt11xqnIWEJLS93CLfCUDPwYH+VdM= +github.com/vulsio/go-kev v0.0.1 h1:FRAnSdrF4fgqBoeWnctydazLUMBJW9HWzwki+iAu5lw= +github.com/vulsio/go-kev v0.0.1/go.mod h1:NrXTTkGG83ZYl7ypHHLqqzx6HvVkWH37qCizU5UoCS8= github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14 h1:2uYZw2gQ0kymwerTS1FXZbNgptnlye+SB7o3QlLDIBo= github.com/vulsio/go-msfdb v0.2.1-0.20211028071756-4a9759bd9f14/go.mod h1:NGdcwWxCK/ES8vZ/crzREqI69S5gH1MivCpSp1pa2Rc= github.com/vulsio/gost v0.4.1-0.20211028071837-7ad032a6ffa8 h1:jqsECpLRp1EAXGOdhPxHzqYjWP5l980GjJ8s/AUYH/4= @@ -2013,8 +2018,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= @@ -2330,8 +2335,9 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.64.0 h1:Mj2zXEXcNb5joEiSA0zc3HZpTst/iyjNiR4CN8tDzOg= +gopkg.in/ini.v1 v1.64.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -2362,24 +2368,23 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gorm.io/driver/mysql v1.0.6/go.mod h1:KdrTanmfLPPyAOeYGyG+UpDys7/7eeWT1zCq+oekYnU= gorm.io/driver/mysql v1.1.0/go.mod h1:KdrTanmfLPPyAOeYGyG+UpDys7/7eeWT1zCq+oekYnU= gorm.io/driver/mysql v1.1.1/go.mod h1:KdrTanmfLPPyAOeYGyG+UpDys7/7eeWT1zCq+oekYnU= -gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M= -gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM= +gorm.io/driver/mysql v1.2.0 h1:l8+9VwjjyzEkw0PNPBOr2JHhLOGVk7XEnl5hk42bcvs= +gorm.io/driver/mysql v1.2.0/go.mod h1:4RQmTg4okPghdt+kbe6e1bTXIQp7Ny1NnBn/3Z6ghjk= gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= gorm.io/driver/postgres v1.1.0/go.mod h1:hXQIwafeRjJvUm+OMxcFWyswJ/vevcpPLlGocwAwuqw= -gorm.io/driver/postgres v1.1.2 h1:Amy3hCvLqM+/ICzjCnQr8wKFLVJTeOTdlMT7kCP+J1Q= -gorm.io/driver/postgres v1.1.2/go.mod h1:/AGV0zvqF3mt9ZtzLzQmXWQ/5vr+1V1TyHZGZVjzmwI= +gorm.io/driver/postgres v1.2.2 h1:Ka9W6feOU+rPM9m007eYLMD4QoZuYGBnQ3Jp0faGSwg= +gorm.io/driver/postgres v1.2.2/go.mod h1:Ik3tK+a3FMp8ORZl29v4b3M0RsgXsaeMXh9s9eVMXco= gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= -gorm.io/driver/sqlite v1.1.6 h1:p3U8WXkVFTOLPED4JjrZExfndjOtya3db8w9/vEMNyI= -gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= +gorm.io/driver/sqlite v1.2.4 h1:jx16ESo1WzNjgBJNSbhEDoMKJnlhkU8BuBR2C0GC7D8= +gorm.io/driver/sqlite v1.2.4/go.mod h1:n8/CTEIEmo7lKrehQI4pd+rz6O514tMkBeCAR5UTXLs= gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.9/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gorm.io/gorm v1.21.10/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gorm.io/gorm v1.21.11/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= -gorm.io/gorm v1.21.16 h1:YBIQLtP5PLfZQz59qfrq7xbrK7KWQ+JsXXCH/THlMqs= -gorm.io/gorm v1.21.16/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.22.3 h1:/JS6z+GStEQvJNW3t1FTwJwG/gZ+A7crFdRqtvG5ehA= +gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= diff --git a/models/scanresults.go b/models/scanresults.go index 6f93abcd..6500e619 100644 --- a/models/scanresults.go +++ b/models/scanresults.go @@ -105,13 +105,12 @@ func (r *ScanResult) FilterInactiveWordPressLibs(detectInactive bool) { return false }) r.ScannedCves = filtered - return } // ReportFileName returns the filename on localhost without extension func (r ScanResult) ReportFileName() (name string) { if r.Container.ContainerID == "" { - return fmt.Sprintf("%s", r.ServerName) + return r.ServerName } return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName) } @@ -246,17 +245,21 @@ func (r ScanResult) FormatMetasploitCveSummary() string { // FormatAlertSummary returns a summary of CERT alerts func (r ScanResult) FormatAlertSummary() string { - jaCnt := 0 - enCnt := 0 + cisaCnt := 0 + uscertCnt := 0 + jpcertCnt := 0 for _, vuln := range r.ScannedCves { - if len(vuln.AlertDict.En) > 0 { - enCnt += len(vuln.AlertDict.En) + if len(vuln.AlertDict.CISA) > 0 { + cisaCnt += len(vuln.AlertDict.CISA) } - if len(vuln.AlertDict.Ja) > 0 { - jaCnt += len(vuln.AlertDict.Ja) + if len(vuln.AlertDict.USCERT) > 0 { + uscertCnt += len(vuln.AlertDict.USCERT) + } + if len(vuln.AlertDict.JPCERT) > 0 { + jpcertCnt += len(vuln.AlertDict.JPCERT) } } - return fmt.Sprintf("en: %d, ja: %d alerts", enCnt, jaCnt) + return fmt.Sprintf("cisa: %d, uscert: %d, jpcert: %d alerts", cisaCnt, uscertCnt, jpcertCnt) } func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool { @@ -418,11 +421,14 @@ func (r *ScanResult) SortForJSONOutput() { v.CveContents.Sort() - sort.Slice(v.AlertDict.En, func(i, j int) bool { - return v.AlertDict.En[i].Title < v.AlertDict.En[j].Title + sort.Slice(v.AlertDict.USCERT, func(i, j int) bool { + return v.AlertDict.USCERT[i].Title < v.AlertDict.USCERT[j].Title }) - sort.Slice(v.AlertDict.Ja, func(i, j int) bool { - return v.AlertDict.Ja[i].Title < v.AlertDict.Ja[j].Title + sort.Slice(v.AlertDict.JPCERT, func(i, j int) bool { + return v.AlertDict.JPCERT[i].Title < v.AlertDict.JPCERT[j].Title + }) + sort.Slice(v.AlertDict.CISA, func(i, j int) bool { + return v.AlertDict.CISA[i].Title < v.AlertDict.CISA[j].Title }) r.ScannedCves[k] = v } diff --git a/models/scanresults_test.go b/models/scanresults_test.go index e7f8afc3..edf4c88a 100644 --- a/models/scanresults_test.go +++ b/models/scanresults_test.go @@ -212,11 +212,15 @@ func TestScanResult_Sort(t *testing.T) { }, }, AlertDict: AlertDict{ - En: []Alert{ + USCERT: []Alert{ {Title: "a"}, {Title: "b"}, }, - Ja: []Alert{ + JPCERT: []Alert{ + {Title: "a"}, + {Title: "b"}, + }, + CISA: []Alert{ {Title: "a"}, {Title: "b"}, }, @@ -271,11 +275,15 @@ func TestScanResult_Sort(t *testing.T) { }, }, AlertDict: AlertDict{ - En: []Alert{ + USCERT: []Alert{ {Title: "a"}, {Title: "b"}, }, - Ja: []Alert{ + JPCERT: []Alert{ + {Title: "a"}, + {Title: "b"}, + }, + CISA: []Alert{ {Title: "a"}, {Title: "b"}, }, @@ -333,11 +341,15 @@ func TestScanResult_Sort(t *testing.T) { }, }, AlertDict: AlertDict{ - En: []Alert{ + USCERT: []Alert{ {Title: "b"}, {Title: "a"}, }, - Ja: []Alert{ + JPCERT: []Alert{ + {Title: "b"}, + {Title: "a"}, + }, + CISA: []Alert{ {Title: "b"}, {Title: "a"}, }, @@ -392,11 +404,15 @@ func TestScanResult_Sort(t *testing.T) { }, }, AlertDict: AlertDict{ - En: []Alert{ + USCERT: []Alert{ {Title: "a"}, {Title: "b"}, }, - Ja: []Alert{ + JPCERT: []Alert{ + {Title: "a"}, + {Title: "b"}, + }, + CISA: []Alert{ {Title: "a"}, {Title: "b"}, }, diff --git a/models/vulninfos.go b/models/vulninfos.go index 6a940671..a55fd530 100644 --- a/models/vulninfos.go +++ b/models/vulninfos.go @@ -241,7 +241,6 @@ func (ps PackageFixStatuses) Sort() { sort.Slice(ps, func(i, j int) bool { return ps[i].Name < ps[j].Name }) - return } // PackageFixStatus has name and other status about the package @@ -360,7 +359,7 @@ func (v VulnInfo) CveIDDiffFormat() string { if v.DiffStatus != "" { return fmt.Sprintf("%s %s", v.DiffStatus, v.CveID) } - return fmt.Sprintf("%s", v.CveID) + return v.CveID } // Titles returns title (TUI) @@ -814,18 +813,27 @@ type Mitigation struct { URL string `json:"url,omitempty"` } -// AlertDict has target cve JPCERT and USCERT alert data +// AlertDict has target cve JPCERT, USCERT and CISA alert data type AlertDict struct { - Ja []Alert `json:"ja"` - En []Alert `json:"en"` + CISA []Alert `json:"cisa"` + JPCERT []Alert `json:"jpcert"` + USCERT []Alert `json:"uscert"` +} + +func (a AlertDict) IsEmpty() bool { + return len(a.CISA) == 0 && len(a.JPCERT) == 0 && len(a.USCERT) == 0 } // FormatSource returns which source has this alert func (a AlertDict) FormatSource() string { - if len(a.En) != 0 || len(a.Ja) != 0 { - return "CERT" + var s []string + if len(a.CISA) != 0 { + s = append(s, "CISA") } - return "" + if len(a.USCERT) != 0 || len(a.JPCERT) != 0 { + s = append(s, "CERT") + } + return strings.Join(s, "/") } // Confidences is a list of Confidence diff --git a/reporter/util.go b/reporter/util.go index f37891ba..bb50c950 100644 --- a/reporter/util.go +++ b/reporter/util.go @@ -277,7 +277,7 @@ No CVE-IDs are found in updatable packages. // fmt.Sprintf("%4.1f", v2max), // fmt.Sprintf("%4.1f", v3max), exploits, - vinfo.AlertDict.FormatSource(), + fmt.Sprintf("%9s", vinfo.AlertDict.FormatSource()), fmt.Sprintf("%7s", vinfo.PatchStatus(r.Packages)), link, }) @@ -292,7 +292,7 @@ No CVE-IDs are found in updatable packages. // "v3", // "v2", "PoC", - "CERT", + "Alert", "Fixed", "NVD", }) @@ -475,11 +475,15 @@ No CVE-IDs are found in updatable packages. data = append(data, []string{"SANS/CWE Top25", sansTop25URLs[0]}) } - for _, alert := range vuln.AlertDict.Ja { + for _, alert := range vuln.AlertDict.CISA { + data = append(data, []string{"CISA Alert", alert.URL}) + } + + for _, alert := range vuln.AlertDict.JPCERT { data = append(data, []string{"JPCERT Alert", alert.URL}) } - for _, alert := range vuln.AlertDict.En { + for _, alert := range vuln.AlertDict.USCERT { data = append(data, []string{"US-CERT Alert", alert.URL}) } diff --git a/server/server.go b/server/server.go index 180c0573..6be2755b 100644 --- a/server/server.go +++ b/server/server.go @@ -94,6 +94,11 @@ func (h VulsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } logging.Log.Infof("%s: %d exploits are detected", r.FormatServerName(), nMetasploitCve) + if err := detector.FillWithKEVuln(&r, config.Conf.KEVuln); err != nil { + logging.Log.Errorf("Failed to fill with Known Exploited Vulnerabilities: %+v", err) + http.Error(w, err.Error(), http.StatusServiceUnavailable) + } + detector.FillCweDict(&r) // set ReportedAt to current time when it's set to the epoch, ensures that ReportedAt will be set diff --git a/subcmds/discover.go b/subcmds/discover.go index 7138024b..5e6b89d7 100644 --- a/subcmds/discover.go +++ b/subcmds/discover.go @@ -103,6 +103,11 @@ func printConfigToml(ips []string) (err error) { #sqlite3Path = "/path/to/go-msfdb.sqlite3" #url = "" +[kevuln] +#type = ["sqlite3", "mysql", "postgres", "redis", "http" ] +#sqlite3Path = "/path/to/go-kev.sqlite3" +#url = "" + # https://vuls.io/docs/en/config.toml.html#slack-section #[slack] #hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz" diff --git a/tui/tui.go b/tui/tui.go index a59be136..a4e650c9 100644 --- a/tui/tui.go +++ b/tui/tui.go @@ -636,7 +636,7 @@ func summaryLines(r models.ScanResult) string { cvssScore + " |", fmt.Sprintf("%-6s |", av), fmt.Sprintf("%3s |", exploits), - fmt.Sprintf("%6s |", vinfo.AlertDict.FormatSource()), + fmt.Sprintf("%9s |", vinfo.AlertDict.FormatSource()), fmt.Sprintf("%7s |", vinfo.PatchStatus(r.Packages)), strings.Join(pkgNames, ", "), } @@ -808,22 +808,32 @@ func setChangelogLayout(g *gocui.Gui) error { } } - if len(vinfo.AlertDict.En) > 0 { + if len(vinfo.AlertDict.CISA) > 0 { lines = append(lines, "\n", - "USCERT Alert", - "=============", + "CISA Alert", + "===========", ) - for _, alert := range vinfo.AlertDict.En { + for _, alert := range vinfo.AlertDict.CISA { lines = append(lines, fmt.Sprintf("* [%s](%s)", alert.Title, alert.URL)) } } - if len(vinfo.AlertDict.Ja) > 0 { + if len(vinfo.AlertDict.USCERT) > 0 { + lines = append(lines, "\n", + "USCERT Alert", + "=============", + ) + for _, alert := range vinfo.AlertDict.USCERT { + lines = append(lines, fmt.Sprintf("* [%s](%s)", alert.Title, alert.URL)) + } + } + + if len(vinfo.AlertDict.JPCERT) > 0 { lines = append(lines, "\n", "JPCERT Alert", "=============", ) - for _, alert := range vinfo.AlertDict.Ja { + for _, alert := range vinfo.AlertDict.JPCERT { if r.Lang == "ja" { lines = append(lines, fmt.Sprintf("* [%s](%s)", alert.Title, alert.URL)) } else {