Compare commits

...

12 Commits

Author SHA1 Message Date
Kota Kanbe
51b8e169d2 fix(scan): warning if lsof command not found (#1167) 2021-02-07 07:28:45 +09:00
Kota Kanbe
b4611ae9b7 fix(scan): fix yum-ps warning Failed to exec which -bash (#1166) 2021-02-07 07:23:12 +09:00
Kota Kanbe
cd6722017b fix(scan): yum-ps err Failed to find the package (#1165) 2021-02-06 08:42:06 +09:00
Kota Kanbe
290edffccf fix(log): output version to log for debugging purpose (#1163) 2021-02-04 07:47:56 +09:00
Kota Kanbe
64a6222bf9 fix(report): set created_at and updated_at of trivy to json (#1162) 2021-02-03 17:52:44 +09:00
Kota Kanbe
adb686b7c9 fix(report): set created_at and updated_at of wpscan.com to json (#1161) 2021-02-03 16:41:44 +09:00
Kota Kanbe
d4af341b0f fix(report): remove duplicated refreshing logic when report with -diff (#1160) 2021-02-03 07:37:19 +09:00
Kota Kanbe
fea7e93c8d chore: fix comment (#1158) 2021-02-02 06:06:49 +09:00
sadayuki-matsuno
8b6b8d0f2e feat(wordpress): define API limit exceed error for wpscan.com (#1155)
* feat(wordpress) specify wp err

* fix typo, chagne const name

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2021-01-30 09:53:41 +09:00
Kota Kanbe
4dcbd865cc fix(report): set http timeout 10 sec (#1154)
* fix(report): set http timeout 10 sec

* fix: add an error handling
2021-01-30 09:40:33 +09:00
Kota Kanbe
39b19444fe Merge branch 'master' of github.com:future-architect/vuls 2021-01-28 16:24:14 +09:00
Kota Kanbe
644d5a5462 fix(report): remove retry logic for wpscan.com (#1151)
* fix(saas) change saas upload s3 key (#1116)

* fix(report): remove retry logic for wpscan.com

Co-authored-by: sadayuki-matsuno <sadayuki.matsuno@gmail.com>
2021-01-28 16:21:33 +09:00
33 changed files with 219 additions and 167 deletions

View File

@@ -29,7 +29,7 @@ func main() {
flag.Parse()
if *v {
fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
fmt.Printf("vuls-%s-%s\n", config.Version, config.Revision)
os.Exit(int(subcommands.ExitSuccess))
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
@@ -63,7 +64,7 @@ func (cnf *ExploitConf) CheckHTTPHealth() error {
}
url := fmt.Sprintf("%s/health", cnf.URL)
resp, _, errs := gorequest.New().Get(url).End()
resp, _, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
@@ -63,7 +64,7 @@ func (cnf *GoCveDictConf) CheckHTTPHealth() error {
}
url := fmt.Sprintf("%s/health", cnf.URL)
resp, _, errs := gorequest.New().SetDebug(Conf.Debug).Get(url).End()
resp, _, errs := gorequest.New().Timeout(10 * time.Second).SetDebug(Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to request to CVE server. url: %s, errs: %s",

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
@@ -63,7 +64,7 @@ func (cnf *GostConf) CheckHTTPHealth() error {
}
url := fmt.Sprintf("%s/health", cnf.URL)
resp, _, errs := gorequest.New().Get(url).End()
resp, _, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/parnurzeal/gorequest"
"golang.org/x/xerrors"
@@ -64,7 +65,7 @@ func (cnf *GovalDictConf) CheckHTTPHealth() error {
}
url := fmt.Sprintf("%s/health", cnf.URL)
resp, _, errs := gorequest.New().Get(url).End()
resp, _, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {

View File

@@ -57,12 +57,24 @@ func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanR
return references[i].Link < references[j].Link
})
var published time.Time
if vuln.PublishedDate != nil {
published = *vuln.PublishedDate
}
var lastModified time.Time
if vuln.LastModifiedDate != nil {
lastModified = *vuln.LastModifiedDate
}
vulnInfo.CveContents = models.CveContents{
models.Trivy: models.CveContent{
Cvss3Severity: vuln.Severity,
References: references,
Title: vuln.Title,
Summary: vuln.Description,
Published: published,
LastModified: lastModified,
},
}
// do only if image type is Vuln

View File

@@ -19,6 +19,9 @@ var (
// ErrFailedToAccessWpScan is error of wpscan.com api access
ErrFailedToAccessWpScan ErrorCode = "ErrFailedToAccessWpScan"
// ErrWpScanAPILimitExceeded is error of wpscan.com api limit exceeded
ErrWpScanAPILimitExceeded ErrorCode = "ErrWpScanAPILimitExceeded"
)
// New :

View File

@@ -85,7 +85,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
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().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 {

View File

@@ -22,6 +22,7 @@ func DetectGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string)
src := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
//TODO Proxy
httpClient := oauth2.NewClient(context.Background(), src)
// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
@@ -32,10 +33,12 @@ func DetectGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string)
for {
jsonStr := fmt.Sprintf(jsonfmt, owner, repo, 100, after)
req, err := http.NewRequest("POST",
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
"https://api.github.com/graphql",
bytes.NewBuffer([]byte(jsonStr)),
)
defer cancel()
if err != nil {
return 0, err
}

View File

@@ -154,7 +154,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
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().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 {

View File

@@ -63,13 +63,13 @@ func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
}
// FindByFQPN search a package by Fully-Qualified-Package-Name
func (ps Packages) FindByFQPN(nameVerRelArc string) (*Package, error) {
func (ps Packages) FindByFQPN(nameVerRel string) (*Package, error) {
for _, p := range ps {
if nameVerRelArc == p.FQPN() {
if nameVerRel == p.FQPN() {
return &p, nil
}
}
return nil, xerrors.Errorf("Failed to find the package: %s", nameVerRelArc)
return nil, xerrors.Errorf("Failed to find the package: %s", nameVerRel)
}
// Package has installed binary packages.
@@ -96,9 +96,6 @@ func (p Package) FQPN() string {
if p.Release != "" {
fqpn += fmt.Sprintf("-%s", p.Release)
}
if p.Arch != "" {
fqpn += fmt.Sprintf(".%s", p.Arch)
}
return fqpn
}

View File

@@ -530,6 +530,7 @@ func (c Cvss) Format() string {
return fmt.Sprintf("%3.1f/%s %s", c.Score, c.Vector, c.Severity)
}
// SeverityToCvssScoreRange returns CVSS score range
func (c Cvss) SeverityToCvssScoreRange() string {
return severityToCvssScoreRange(c.Severity)
}

View File

@@ -39,7 +39,7 @@ func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetche
}
url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "count", osFamily, release)
resp, body, errs := gorequest.New().Get(url).End()
resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %s", url, resp, errs)
}
@@ -57,7 +57,7 @@ func (b Base) CheckIfOvalFresh(driver db.DB, osFamily, release string) (ok bool,
lastModified = driver.GetLastModified(osFamily, release)
} else {
url, _ := util.URLPathJoin(cnf.Conf.OvalDict.URL, "lastmodified", osFamily, release)
resp, body, errs := gorequest.New().Get(url).End()
resp, body, errs := gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, xerrors.Errorf("HTTP GET error, url: %s, resp: %v, err: %s", url, resp, errs)
}

View File

@@ -189,7 +189,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
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().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 {

View File

@@ -1,14 +1,17 @@
package report
import (
"context"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
)
// ChatWorkWriter send report to ChatWork
@@ -48,26 +51,25 @@ func (w ChatWorkWriter) Write(rs ...models.ScanResult) (err error) {
func chatWorkpostMessage(room, token, message string) error {
uri := fmt.Sprintf("https://api.chatwork.com/v2/rooms/%s/messages=%s", room, token)
payload := url.Values{"body": {message}}
payload := url.Values{
"body": {message},
}
reqs, err := http.NewRequest("POST", uri, strings.NewReader(payload.Encode()))
reqs.Header.Add("X-ChatWorkToken", token)
reqs.Header.Add("Content-Type", "application/x-www-form-urlencoded")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, strings.NewReader(payload.Encode()))
defer cancel()
if err != nil {
return err
}
client := &http.Client{}
req.Header.Add("X-ChatWorkToken", token)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(reqs)
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@@ -114,7 +114,7 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
var resp *http.Response
f := func() (err error) {
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
resp, body, errs = gorequest.New().Timeout(10 * time.Second).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("HTTP GET Error, url: %s, resp: %v, err: %s",
url, resp, errs)
@@ -162,7 +162,7 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
var resp *http.Response
f := func() (err error) {
// req := gorequest.New().SetDebug(config.Conf.Debug).Post(url)
req := gorequest.New().Post(url)
req := gorequest.New().Timeout(10 * time.Second).Post(url)
for key := range query {
req = req.Send(fmt.Sprintf("%s=%s", key, query[key])).Type("json")
}

View File

@@ -127,16 +127,10 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
return nil, err
}
diff, err := diff(rs, prevs)
rs, err = diff(rs, prevs)
if err != nil {
return nil, err
}
for i, r := range diff {
if err := fillCvesWithNvdJvn(dbclient.CveDB, &r); err != nil {
return nil, err
}
rs[i] = r
}
}
for i, r := range rs {

View File

@@ -136,7 +136,7 @@ func send(msg message) error {
jsonBody := string(bytes)
f := func() (err error) {
resp, body, errs := gorequest.New().Proxy(config.Conf.HTTPProxy).Post(conf.HookURL).Send(string(jsonBody)).End()
resp, body, errs := gorequest.New().Timeout(10 * time.Second).Proxy(config.Conf.HTTPProxy).Post(conf.HookURL).Send(string(jsonBody)).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {

View File

@@ -2,13 +2,16 @@ package report
import (
"bytes"
"context"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
)
@@ -55,15 +58,21 @@ func (w TelegramWriter) Write(rs ...models.ScanResult) (err error) {
func sendMessage(chatID, token, message string) error {
uri := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", token)
payload := `{"text": "` + strings.Replace(message, `"`, `\"`, -1) + `", "chat_id": "` + chatID + `", "parse_mode": "Markdown" }`
req, err := http.NewRequest("POST", uri, bytes.NewBuffer([]byte(payload)))
req.Header.Add("Content-Type", "application/json")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, bytes.NewBuffer([]byte(payload)))
defer cancel()
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/json")
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
if err != nil {
return err
}
client := &http.Client{}
resp, err := client.Do(req)
if checkResponse(resp) != nil && err != nil {
fmt.Println(err)
return err
}
defer resp.Body.Close()

View File

@@ -504,7 +504,7 @@ func loadPrevious(currs models.ScanResults) (prevs models.ScanResults, err error
path := filepath.Join(dir, filename)
r, err := loadOneServerScanResult(path)
if err != nil {
util.Log.Errorf("%+v", err)
util.Log.Debugf("%+v", err)
continue
}
if r.Family == result.Family && r.Release == result.Release {

View File

@@ -2,20 +2,22 @@ package saas
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
@@ -41,8 +43,7 @@ type payload struct {
}
// UploadSaas : UploadSaas
func (w Writer) Write(rs ...models.ScanResult) (err error) {
// dir string, configPath string, config *c.Config
func (w Writer) Write(rs ...models.ScanResult) error {
if len(rs) == 0 {
return nil
}
@@ -60,34 +61,25 @@ func (w Writer) Write(rs ...models.ScanResult) (err error) {
ScannedIPv4s: strings.Join(ipv4s, ", "),
ScannedIPv6s: strings.Join(ipv6s, ", "),
}
var body []byte
if body, err = json.Marshal(payload); err != nil {
body, err := json.Marshal(payload)
if err != nil {
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
var req *http.Request
if req, err = http.NewRequest("POST", c.Conf.Saas.URL, bytes.NewBuffer(body)); err != nil {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.Conf.Saas.URL, bytes.NewBuffer(body))
defer cancel()
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
proxy := c.Conf.HTTPProxy
var client http.Client
if proxy != "" {
proxyURL, _ := url.Parse(proxy)
client = http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}
} else {
client = http.Client{}
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
if err != nil {
return err
}
var resp *http.Response
if resp, err = client.Do(req); err != nil {
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
@@ -95,44 +87,40 @@ func (w Writer) Write(rs ...models.ScanResult) (err error) {
return xerrors.Errorf("Failed to get Credential. Request JSON : %s,", string(body))
}
var t []byte
if t, err = ioutil.ReadAll(resp.Body); err != nil {
t, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var tempCredential TempCredential
if err = json.Unmarshal(t, &tempCredential); err != nil {
if err := json.Unmarshal(t, &tempCredential); err != nil {
return xerrors.Errorf("Failed to unmarshal saas credential file. err : %s", err)
}
credential := credentials.NewStaticCredentialsFromCreds(credentials.Value{
AccessKeyID: *tempCredential.Credential.AccessKeyId,
SecretAccessKey: *tempCredential.Credential.SecretAccessKey,
SessionToken: *tempCredential.Credential.SessionToken,
sess, err := session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentialsFromCreds(credentials.Value{
AccessKeyID: *tempCredential.Credential.AccessKeyId,
SecretAccessKey: *tempCredential.Credential.SecretAccessKey,
SessionToken: *tempCredential.Credential.SessionToken,
}),
Region: aws.String("ap-northeast-1"),
})
var sess *session.Session
if sess, err = session.NewSession(&aws.Config{
Credentials: credential,
Region: aws.String("ap-northeast-1"),
}); err != nil {
if err != nil {
return xerrors.Errorf("Failed to new aws session. err: %w", err)
}
svc := s3.New(sess)
for _, r := range rs {
s3Key := renameKeyName(r.ServerUUID, r.Container)
var b []byte
if b, err = json.Marshal(r); err != nil {
b, err := json.Marshal(r)
if err != nil {
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
util.Log.Infof("Uploading...: ServerName: %s, ", r.ServerName)
s3Key := renameKeyName(r.ServerUUID, r.Container)
putObjectInput := &s3.PutObjectInput{
Bucket: aws.String(tempCredential.S3Bucket),
Key: aws.String(path.Join(tempCredential.S3ResultsDir, s3Key)),
Body: bytes.NewReader(b),
}
if _, err := svc.PutObject(putObjectInput); err != nil {
return xerrors.Errorf("Failed to upload data to %s/%s, err: %w",
tempCredential.S3Bucket, s3Key, err)

View File

@@ -32,19 +32,16 @@ func newAlpine(c config.ServerInfo) *alpine {
// Alpine
// https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/alpine.rb
func detectAlpine(c config.ServerInfo) (itsMe bool, os osTypeInterface) {
os = newAlpine(c)
func detectAlpine(c config.ServerInfo) (bool, osTypeInterface) {
if r := exec(c, "ls /etc/alpine-release", noSudo); !r.isSuccess() {
return false, os
return false, nil
}
if r := exec(c, "cat /etc/alpine-release", noSudo); r.isSuccess() {
os := newAlpine(c)
os.setDistro(config.Alpine, strings.TrimSpace(r.Stdout))
return true, os
}
return false, os
return false, nil
}
func (o *alpine) checkScanMode() error {

View File

@@ -322,7 +322,7 @@ func (l *base) detectPlatform() {
var dsFingerPrintPrefix = "AgentStatus.agentCertHash: "
func (l *base) detectDeepSecurity() (fingerprint string, err error) {
func (l *base) detectDeepSecurity() (string, error) {
// only work root user
if l.getServerInfo().Mode.IsFastRoot() {
if r := l.exec("test -f /opt/ds_agent/dsa_query", sudo); r.isSuccess() {
@@ -621,7 +621,7 @@ func (d *DummyFileInfo) IsDir() bool { return false }
//Sys is
func (d *DummyFileInfo) Sys() interface{} { return nil }
func (l *base) scanWordPress() (err error) {
func (l *base) scanWordPress() error {
if l.ServerInfo.WordPress.IsZero() || l.ServerInfo.Type == config.ServerTypePseudo {
return nil
}
@@ -835,7 +835,7 @@ func (l *base) findPortTestSuccessOn(listenIPPorts []string, searchListenPort mo
return addrs
}
func (l *base) ps() (stdout string, err error) {
func (l *base) ps() (string, error) {
cmd := `LANGUAGE=en_US.UTF-8 ps --no-headers --ppid 2 -p 2 --deselect -o pid,comm`
r := l.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess() {
@@ -858,7 +858,7 @@ func (l *base) parsePs(stdout string) map[string]string {
return pidNames
}
func (l *base) lsProcExe(pid string) (stdout string, err error) {
func (l *base) lsProcExe(pid string) (string, error) {
cmd := fmt.Sprintf("ls -l /proc/%s/exe", pid)
r := l.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
@@ -875,7 +875,7 @@ func (l *base) parseLsProcExe(stdout string) (string, error) {
return ss[10], nil
}
func (l *base) grepProcMap(pid string) (stdout string, err error) {
func (l *base) grepProcMap(pid string) (string, error) {
cmd := fmt.Sprintf(`cat /proc/%s/maps 2>/dev/null | grep -v " 00:00 " | awk '{print $6}' | sort -n | uniq`, pid)
r := l.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
@@ -888,15 +888,16 @@ func (l *base) parseGrepProcMap(stdout string) (soPaths []string) {
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
line = strings.Split(line, ";")[0]
soPaths = append(soPaths, line)
}
return soPaths
}
func (l *base) lsOfListen() (stdout string, err error) {
cmd := `lsof -i -P -n | grep LISTEN`
func (l *base) lsOfListen() (string, error) {
cmd := `lsof -i -P -n`
r := l.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 1) {
if !r.isSuccess() {
return "", xerrors.Errorf("Failed to lsof: %s", r)
}
return r.Stdout, nil
@@ -906,7 +907,11 @@ func (l *base) parseLsOf(stdout string) map[string][]string {
portPids := map[string][]string{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
ss := strings.Fields(scanner.Text())
line := scanner.Text()
if !strings.Contains(line, "LISTEN") {
continue
}
ss := strings.Fields(line)
if len(ss) < 10 {
continue
}

View File

@@ -218,12 +218,14 @@ func Test_base_parseGrepProcMap(t *testing.T) {
args: args{
`/etc/selinux/targeted/contexts/files/file_contexts.bin
/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin
/usr/lib64/libdl-2.28.so`,
/usr/lib64/libdl-2.28.so
/usr/lib64/libnss_files-2.17.so;601ccbf3`,
},
wantSoPaths: []string{
"/etc/selinux/targeted/contexts/files/file_contexts.bin",
"/etc/selinux/targeted/contexts/files/file_contexts.homedirs.bin",
"/usr/lib64/libdl-2.28.so",
`/usr/lib64/libnss_files-2.17.so`,
},
},
}
@@ -255,6 +257,7 @@ sshd 644 root 4u IPv6 16716 0t0 TCP *:22 (LISTEN)
squid 959 proxy 11u IPv6 16351 0t0 TCP *:3128 (LISTEN)
node 1498 ubuntu 21u IPv6 20132 0t0 TCP *:35401 (LISTEN)
node 1498 ubuntu 22u IPv6 20133 0t0 TCP *:44801 (LISTEN)
rpcbind 568 rpc 7u IPv6 15149 0t0 UDP *:111
docker-pr 9135 root 4u IPv6 297133 0t0 TCP *:6379 (LISTEN)`,
},
wantPortPid: map[string][]string{

View File

@@ -40,18 +40,16 @@ func newDebian(c config.ServerInfo) *debian {
// Ubuntu, Debian, Raspbian
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/debian.rb
func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err error) {
deb = newDebian(c)
func detectDebian(c config.ServerInfo) (bool, osTypeInterface, error) {
if r := exec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
if r.Error != nil {
return false, deb, nil
return false, nil, nil
}
if r.ExitStatus == 255 {
return false, deb, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
return false, nil, xerrors.Errorf("Unable to connect via SSH. Scan with -vvv option to print SSH debugging messages and check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
}
util.Log.Debugf("Not Debian like Linux. %s", r)
return false, deb, nil
return false, nil, nil
}
// Raspbian
@@ -62,8 +60,8 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// Raspbian GNU/Linux 7 \n \l
result := strings.Fields(r.Stdout)
if len(result) > 2 && result[0] == config.Raspbian {
distro := strings.ToLower(trim(result[0]))
deb.setDistro(distro, trim(result[2]))
deb := newDebian(c)
deb.setDistro(strings.ToLower(trim(result[0])), trim(result[2]))
return true, deb, nil
}
}
@@ -76,10 +74,10 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
re := regexp.MustCompile(`(?s)^Distributor ID:\s*(.+?)\n*Release:\s*(.+?)$`)
result := re.FindStringSubmatch(trim(r.Stdout))
deb := newDebian(c)
if len(result) == 0 {
deb.setDistro("debian/ubuntu", "unknown")
util.Log.Warnf(
"Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
util.Log.Warnf("Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
} else {
distro := strings.ToLower(trim(result[1]))
deb.setDistro(distro, trim(result[2]))
@@ -95,6 +93,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"
re := regexp.MustCompile(`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`)
result := re.FindStringSubmatch(trim(r.Stdout))
deb := newDebian(c)
if len(result) == 0 {
util.Log.Warnf(
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
@@ -109,12 +108,13 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// Debian
cmd := "cat /etc/debian_version"
if r := exec(c, cmd, noSudo); r.isSuccess() {
deb := newDebian(c)
deb.setDistro(config.Debian, trim(r.Stdout))
return true, deb, nil
}
util.Log.Debugf("Not Debian like Linux: %s", c.ServerName)
return false, deb, nil
return false, nil, nil
}
func trim(str string) string {
@@ -1297,7 +1297,8 @@ func (o *debian) dpkgPs() error {
pidListenPorts := map[string][]models.PortStat{}
stdout, err = o.lsOfListen()
if err != nil {
return xerrors.Errorf("Failed to ls of: %w", err)
// warning only, continue scanning
o.log.Warnf("Failed to lsof: %+v", err)
}
portPids := o.parseLsOf(stdout)
for ipPort, pids := range portPids {
@@ -1332,7 +1333,8 @@ func (o *debian) dpkgPs() error {
for _, n := range pkgNames {
p, ok := o.Packages[n]
if !ok {
return xerrors.Errorf("pkg not found %s", n)
o.log.Warnf("Failed to FindByFQPN: %+v", err)
continue
}
p.AffectedProcs = append(p.AffectedProcs, proc)
o.Packages[p.Name] = p

View File

@@ -31,15 +31,14 @@ func newBsd(c config.ServerInfo) *bsd {
}
//https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/freebsd.rb
func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
bsd = newBsd(c)
func detectFreebsd(c config.ServerInfo) (bool, osTypeInterface) {
// Prevent from adding `set -o pipefail` option
c.Distro = config.Distro{Family: config.FreeBSD}
if r := exec(c, "uname", noSudo); r.isSuccess() {
if strings.Contains(strings.ToLower(r.Stdout), config.FreeBSD) == true {
if b := exec(c, "freebsd-version", noSudo); b.isSuccess() {
bsd := newBsd(c)
rel := strings.TrimSpace(b.Stdout)
bsd.setDistro(config.FreeBSD, rel)
return true, bsd
@@ -47,7 +46,7 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
}
}
util.Log.Debugf("Not FreeBSD. servernam: %s", c.ServerName)
return false, bsd
return false, nil
}
func (o *bsd) checkScanMode() error {

View File

@@ -12,9 +12,12 @@ type pseudo struct {
}
func detectPseudo(c config.ServerInfo) (itsMe bool, pseudo osTypeInterface, err error) {
p := newPseudo(c)
p.setDistro(config.ServerTypePseudo, "")
return c.Type == config.ServerTypePseudo, p, nil
if c.Type == config.ServerTypePseudo {
p := newPseudo(c)
p.setDistro(config.ServerTypePseudo, "")
return true, p, nil
}
return false, nil, nil
}
func newPseudo(c config.ServerInfo) *pseudo {

View File

@@ -492,7 +492,8 @@ func (o *redhatBase) yumPs() error {
pidListenPorts := map[string][]models.PortStat{}
stdout, err = o.lsOfListen()
if err != nil {
return xerrors.Errorf("Failed to lsof: %w", err)
// warning only, continue scanning
o.log.Warnf("Failed to lsof: %+v", err)
}
portPids := o.parseLsOf(stdout)
for ipPort, pids := range portPids {
@@ -508,14 +509,14 @@ func (o *redhatBase) yumPs() error {
for pid, loadedFiles := range pidLoadedFiles {
o.log.Debugf("rpm -qf: %#v", loadedFiles)
pkgNames, err := o.getPkgName(loadedFiles)
pkgNameVerRels, err := o.getPkgNameVerRels(loadedFiles)
if err != nil {
o.log.Debugf("Failed to get package name by file path: %s, err: %s", pkgNames, err)
o.log.Debugf("Failed to get package name by file path: %s, err: %s", pkgNameVerRels, err)
continue
}
uniq := map[string]struct{}{}
for _, name := range pkgNames {
for _, name := range pkgNameVerRels {
uniq[name] = struct{}{}
}
@@ -529,10 +530,11 @@ func (o *redhatBase) yumPs() error {
ListenPortStats: pidListenPorts[pid],
}
for fqpn := range uniq {
p, err := o.Packages.FindByFQPN(fqpn)
for pkgNameVerRel := range uniq {
p, err := o.Packages.FindByFQPN(pkgNameVerRel)
if err != nil {
return err
o.log.Warnf("Failed to FindByFQPN: %+v", err)
continue
}
p.AffectedProcs = append(p.AffectedProcs, proc)
o.Packages[p.Name] = *p
@@ -604,7 +606,7 @@ func (o *redhatBase) parseNeedsRestarting(stdout string) (procs []models.NeedRes
cmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 which %s", path)
r := o.exec(cmd, sudo)
if !r.isSuccess() {
o.log.Warnf("Failed to exec which %s: %s", path, r)
o.log.Debugf("Failed to exec which %s: %s", path, r)
continue
}
path = strings.TrimSpace(r.Stdout)
@@ -623,7 +625,7 @@ func (o *redhatBase) parseNeedsRestarting(stdout string) (procs []models.NeedRes
func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
execCommand = strings.Replace(execCommand, "\x00", " ", -1) // for CentOS6.9
path := strings.Fields(execCommand)[0]
cmd := `LANGUAGE=en_US.UTF-8 rpm -qf --queryformat "%{NAME}-%{EPOCH}:%{VERSION}-%{RELEASE}.%{ARCH}\n" ` + path
cmd := `LANGUAGE=en_US.UTF-8 rpm -qf --queryformat "%{NAME}-%{EPOCH}:%{VERSION}-%{RELEASE}\n" ` + path
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return "", xerrors.Errorf("Failed to SSH: %s", r)
@@ -632,7 +634,7 @@ func (o *redhatBase) procPathToFQPN(execCommand string) (string, error) {
return strings.Replace(fqpn, "-(none):", "-", -1), nil
}
func (o *redhatBase) getPkgName(paths []string) (pkgNames []string, err error) {
func (o *redhatBase) getPkgNameVerRels(paths []string) (pkgNameVerRels []string, err error) {
cmd := o.rpmQf() + strings.Join(paths, " ")
r := o.exec(util.PrependProxyEnv(cmd), noSudo)
if !r.isSuccess(0, 2, 4, 8) {
@@ -647,9 +649,9 @@ func (o *redhatBase) getPkgName(paths []string) (pkgNames []string, err error) {
o.log.Debugf("Failed to parse rpm -qf line: %s, r: %s. continue", line, r)
continue
}
pkgNames = append(pkgNames, pack.FQPN())
pkgNameVerRels = append(pkgNameVerRels, pack.FQPN())
}
return pkgNames, nil
return pkgNameVerRels, nil
}
func (o *redhatBase) rpmQa() string {

View File

@@ -36,10 +36,10 @@ func newSUSE(c config.ServerInfo) *suse {
// https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/suse.rb
func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
s := newSUSE(c)
if r := exec(c, "ls /etc/os-release", noSudo); r.isSuccess() {
if r := exec(c, "zypper -V", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/os-release", noSudo); r.isSuccess() {
s := newSUSE(c)
name, ver := s.parseOSRelease(r.Stdout)
s.setDistro(name, ver)
return true, s
@@ -52,6 +52,7 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) == 2 {
//TODO check opensuse or opensuse.leap
s := newSUSE(c)
s.setDistro(config.OpenSUSE, result[1])
return true, s
}
@@ -63,18 +64,19 @@ func detectSUSE(c config.ServerInfo) (bool, osTypeInterface) {
re = regexp.MustCompile(`PATCHLEVEL = (\d+)`)
result = re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) == 2 {
s := newSUSE(c)
s.setDistro(config.SUSEEnterpriseServer,
fmt.Sprintf("%s.%s", version, result[1]))
return true, s
}
}
util.Log.Warnf("Failed to parse SUSE Linux version: %s", r)
return true, s
return true, newSUSE(c)
}
}
}
util.Log.Debugf("Not SUSE Linux. servername: %s", c.ServerName)
return false, s
return false, nil
}
func (o *suse) parseOSRelease(content string) (name string, ver string) {

View File

@@ -96,7 +96,7 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
f.BoolVar(&c.Conf.Diff, "diff", false,
"Difference between previous result and current result ")
"Difference between previous result and current result")
f.BoolVar(&c.Conf.IgnoreUnscoredCves, "ignore-unscored-cves", false,
"Don't report the unscored CVEs")

View File

@@ -86,8 +86,9 @@ func NewCustomLogger(server config.ServerInfo) *logrus.Entry {
}
}
fields := logrus.Fields{"prefix": whereami}
return log.WithFields(fields)
entry := log.WithFields(logrus.Fields{"prefix": whereami})
entry.Infof("vuls-%s-%s", config.Version, config.Revision)
return entry
}
// GetDefaultLogDir returns default log directory

View File

@@ -3,6 +3,7 @@ package util
import (
"fmt"
"net"
"net/http"
"net/url"
"strings"
@@ -178,3 +179,19 @@ func Major(version string) string {
}
return ver[0:strings.Index(ver, ".")]
}
// GetHTTPClient return http.Client
func GetHTTPClient(proxy string) (*http.Client, error) {
if proxy != "" {
proxyURL, err := url.Parse(proxy)
if err != nil {
return nil, err
}
return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}, nil
}
return &http.Client{}, nil
}

View File

@@ -1,6 +1,7 @@
package wordpress
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
@@ -8,6 +9,7 @@ import (
"strings"
"time"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/errof"
"github.com/future-architect/vuls/models"
@@ -30,11 +32,10 @@ type WpCveInfos struct {
//WpCveInfo is for wpscan json
type WpCveInfo struct {
ID string `json:"id"`
Title string `json:"title"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
// PublishedDate string `json:"published_date"`
ID string `json:"id"`
Title string `json:"title"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
VulnType string `json:"vuln_type"`
References References `json:"references"`
FixedIn string `json:"fixed_in"`
@@ -57,7 +58,8 @@ func DetectWordPressCves(r *models.ScanResult, cnf *c.WpScanConf) (int, error) {
// Core
ver := strings.Replace(r.WordPressPackages.CoreVersion(), ".", "", -1)
if ver == "" {
return 0, xerrors.New("Failed to get WordPress core version")
return 0, errof.New(errof.ErrFailedToAccessWpScan,
fmt.Sprintf("Failed to get WordPress core version."))
}
url := fmt.Sprintf("https://wpscan.com/api/v3/wordpresses/%s", ver)
wpVinfos, err := wpscan(url, ver, cnf.Token)
@@ -112,8 +114,7 @@ func DetectWordPressCves(r *models.ScanResult, cnf *c.WpScanConf) (int, error) {
func wpscan(url, name, token string) (vinfos []models.VulnInfo, err error) {
body, err := httpRequest(url, token)
if err != nil {
return nil, errof.New(errof.ErrFailedToAccessWpScan,
fmt.Sprintf("Failed to access to wpscan.comm. body: %s, err: %s", string(body), err))
return nil, err
}
if body == "" {
util.Log.Debugf("wpscan.com response body is empty. URL: %s", url)
@@ -196,10 +197,12 @@ func extractToVulnInfos(pkgName string, cves []WpCveInfo) (vinfos []models.VulnI
CveID: cveID,
CveContents: models.NewCveContents(
models.CveContent{
Type: models.WpScan,
CveID: cveID,
Title: vulnerability.Title,
References: refs,
Type: models.WpScan,
CveID: cveID,
Title: vulnerability.Title,
References: refs,
Published: vulnerability.CreatedAt,
LastModified: vulnerability.UpdatedAt,
},
),
VulnType: vulnerability.VulnType,
@@ -217,21 +220,27 @@ func extractToVulnInfos(pkgName string, cves []WpCveInfo) (vinfos []models.VulnI
}
func httpRequest(url, token string) (string, error) {
retry := 1
util.Log.Debugf("%s", url)
req, err := http.NewRequest("GET", url, nil)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
defer cancel()
if err != nil {
return "", errof.New(errof.ErrFailedToAccessWpScan,
fmt.Sprintf("Failed to access to wpscan.com. err: %s", err))
}
req.Header.Set("Authorization", fmt.Sprintf("Token token=%s", token))
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
if err != nil {
return "", err
}
req.Header.Set("Authorization", fmt.Sprintf("Token token=%s", token))
loop:
resp, err := new(http.Client).Do(req)
resp, err := client.Do(req)
if err != nil {
return "", err
return "", errof.New(errof.ErrFailedToAccessWpScan,
fmt.Sprintf("Failed to access to wpscan.com. err: %s", err))
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
return "", errof.New(errof.ErrFailedToAccessWpScan,
fmt.Sprintf("Failed to access to wpscan.com. err: %s", err))
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
@@ -239,14 +248,13 @@ loop:
} else if resp.StatusCode == 404 {
// This package is not in wpscan
return "", nil
} else if resp.StatusCode == 429 && retry <= 3 {
// 429 Too Many Requests
util.Log.Debugf("sleep %d min(s): %s", retry, resp.Status)
time.Sleep(time.Duration(retry) * time.Minute)
retry++
goto loop
} else if resp.StatusCode == 429 {
return "", errof.New(errof.ErrWpScanAPILimitExceeded,
fmt.Sprintf("wpscan.com API limit exceeded: %+v", resp.Status))
} else {
util.Log.Warnf("wpscan.com unknown status code: %+v", resp.Status)
return "", nil
}
return "", err
}
func removeInactives(pkgs models.WordPressPackages) (removed models.WordPressPackages) {