getvulns/main.go
2019-09-09 15:14:49 +02:00

265 lines
7.3 KiB
Go

package main
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"flag"
"fmt"
"github.com/fatih/color"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"strings"
"time"
)
const oauth2Url = "https://cloudsso.cisco.com/as/token.oauth2"
type oauthToken struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
}
type ciscoVulns struct {
Advisories []struct {
AdvisoryID string `json:"advisoryId"`
AdvisoryTitle string `json:"advisoryTitle"`
BugIDs []string `json:"bugIDs"`
IpsSignatures []string `json:"ipsSignatures"`
Cves []string `json:"cves"`
CvrfURL string `json:"cvrfUrl"`
OvalURL string `json:"ovalUrl"`
CvssBaseScore string `json:"cvssBaseScore"`
Cwe []string `json:"cwe"`
IosRelease []string `json:"iosRelease"`
FirstFixed []string `json:"firstFixed"`
FirstPublished string `json:"firstPublished"`
LastUpdated string `json:"lastUpdated"`
ProductNames []string `json:"productNames"`
PublicationURL string `json:"publicationUrl"`
Sir string `json:"sir"`
Summary string `json:"summary"`
} `json:"advisories"`
SirCriticalCount int
SirHighCount int
SirMediumCount int
SirLowCount int
}
var verboseFlag = flag.Bool("v", false, "be verbose")
var scriptFlag = flag.Bool("s", false, "scriptble output")
func main() {
var t oauthToken
var c ciscoVulns
var iosXeFlag = flag.String("iosxe", "", "fetch Security Advisories for IOS-XE version")
var iosFlag = flag.String("ios", "", "fetch Security Advisories for IOS version")
var productFlag = flag.String("product", "", "fetch Security Advisories Cisco Products, ex Cisco Adaptive Security Appliance")
flag.Parse()
if *iosXeFlag == "" && *iosFlag == "" && *productFlag == "" {
flag.PrintDefaults()
return
}
/* Colors */
redstr := color.New(color.FgRed).SprintFunc()
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow).SprintFunc()
boldRed := red.Add(color.Bold).SprintFunc()
if *iosXeFlag != "" {
err := t.fetchToken()
if err != nil {
fmt.Println(err)
return
}
c.fetchVulnVersion(t, "iosxe", *iosXeFlag)
c.populateSirCount()
if *scriptFlag == true {
fmt.Printf("Summary Version: %s Critical: %d: High: %d Medium: %d Low: %d\n", *iosXeFlag, c.SirCriticalCount, c.SirHighCount, c.SirMediumCount, c.SirLowCount)
} else {
/* Clear terminal */
fmt.Printf("\033[H\033[2J")
fmt.Printf("--- Vulnerabilities for IOS-XE %s ---\n", *iosXeFlag)
fmt.Printf("Critical: %s High: %s Medium: %s Low: %d\n\n", boldRed(c.SirCriticalCount), redstr(c.SirHighCount), yellow(c.SirMediumCount), c.SirLowCount)
}
}
if *iosFlag != "" {
err := t.fetchToken()
if err != nil {
fmt.Println(err)
return
}
c.fetchVulnVersion(t, "ios", *iosFlag)
c.populateSirCount()
/* Clear terminal */
if *scriptFlag == true {
fmt.Printf("Summary Version: %s Critical: %d: High: %d Medium: %d Low: %d\n", *iosFlag, c.SirCriticalCount, c.SirHighCount, c.SirMediumCount, c.SirLowCount)
} else {
fmt.Printf("\033[H\033[2J")
fmt.Printf("--- Vulnerabilities for IOS %s ---\n", *iosFlag)
fmt.Printf("Critical: %s High: %s Medium: %s Low: %d\n\n", boldRed(c.SirCriticalCount), redstr(c.SirHighCount), yellow(c.SirMediumCount), c.SirLowCount)
}
}
if *productFlag != "" {
fmt.Println("Not yet implemented.")
}
if *scriptFlag == true {
for _, vuln := range c.Advisories {
fmt.Printf("%s:%s:%s:%s\n", vuln.AdvisoryTitle, vuln.CvssBaseScore, vuln.Sir, vuln.PublicationURL)
}
} else {
for _, vuln := range c.Advisories {
switch vuln.Sir {
case "Critical":
fmt.Printf("Title: %s\nCVE Score: %s\nSeverity: %s\nUrl: %s\n\n", vuln.AdvisoryTitle, vuln.CvssBaseScore, boldRed("!!! "+vuln.Sir+" !!!"), vuln.PublicationURL)
case "High":
fmt.Printf("Title: %s\nCVE Score: %s\nSeverity: %s\nUrl: %s\n\n", vuln.AdvisoryTitle, vuln.CvssBaseScore, redstr(vuln.Sir), vuln.PublicationURL)
case "Medium":
fmt.Printf("Title: %s\nCVE Score: %s\nSeverity: %s\nUrl: %s\n\n", vuln.AdvisoryTitle, vuln.CvssBaseScore, yellow(vuln.Sir), vuln.PublicationURL)
default:
fmt.Printf("Title: %s\nCVE Score: %s\nSeverity: %s\nUrl: %s\n\n", vuln.AdvisoryTitle, vuln.CvssBaseScore, vuln.Sir, vuln.PublicationURL)
}
}
}
}
func (c *ciscoVulns) populateSirCount() {
for _, vuln := range c.Advisories {
switch vuln.Sir {
case "Critical":
c.SirCriticalCount++
case "High":
c.SirHighCount++
case "Medium":
c.SirMediumCount++
case "Low":
c.SirLowCount++
}
}
}
func (c *ciscoVulns) fetchVulnVersion(token oauthToken, os string, version string) error {
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
timeout := time.Duration(60 * time.Second)
client := &http.Client{
Timeout: timeout,
Transport: transport,
}
var urlStr bytes.Buffer
switch os {
case "iosxe":
urlStr.WriteString("https://api.cisco.com/security/advisories/iosxe?version=")
case "ios":
urlStr.WriteString("https://api.cisco.com/security/advisories/ios?version=")
}
urlStr.WriteString(version)
url, err := url.Parse(urlStr.String())
request, err := http.NewRequest("GET", url.String(), nil)
if err != nil {
log.Println(err)
}
request.Header.Add("Accept", "application/json")
request.Header.Add("Authorization", "Bearer "+token.AccessToken)
if *verboseFlag == true {
fmt.Printf("Connecting to api.cisco.com\n")
}
response, err := client.Do(request)
if err != nil {
log.Println(err)
return err
}
if response.StatusCode == http.StatusUnauthorized {
log.Println("Invalid credentials")
return errors.New("Invalid credentials")
}
if *verboseFlag == true {
fmt.Printf("Authentication ok.\nReading response\n")
}
data, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Println(err)
return err
}
err = json.Unmarshal(data, &c)
if err != nil {
return err
}
return nil
}
func (t *oauthToken) fetchToken() error {
clientId := os.Getenv("CLIENT_ID")
clientSecret := os.Getenv("CLIENT_SECRET")
if clientId == "" || clientSecret == "" {
return errors.New("You have not set correct environment variables (CLIENT_ID or CLIENT_SECRET).\n\n$ export CLIENT_ID=\"kajsdkljadslkj\"\n$ export CLIENT_SECRET=\"kljadslksjdlj\"")
}
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
timeout := time.Duration(60 * time.Second)
client := &http.Client{
Timeout: timeout,
Transport: transport,
}
form := url.Values{}
form.Add("client_id", clientId)
form.Add("client_secret", clientSecret)
form.Add("grant_type", "client_credentials")
request, err := http.NewRequest("POST", oauth2Url, strings.NewReader(form.Encode()))
if err != nil {
return err
}
request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
if *verboseFlag == true {
fmt.Printf("Connecting to cloudsso.cisco.com.\n")
}
response, err := client.Do(request)
if err != nil {
return err
}
if response.StatusCode == http.StatusUnauthorized {
log.Println("Authentication failed: " + oauth2Url)
return errors.New("Authentication failed: " + oauth2Url)
}
if *verboseFlag == true {
fmt.Printf("Authenticated ok.\nAcquiring token.\n")
}
data, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
err = json.Unmarshal(data, &t)
if err != nil {
return err
}
return nil
}