Initial commit.
This commit is contained in:
commit
f75d57b53a
4 changed files with 303 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
getvulns
|
37
README.md
Normal file
37
README.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# getvulns 🚨
|
||||||
|
Utility to fetch and list current security advisories of network vendor products and os versions.
|
||||||
|
|
||||||
|
To be able to use this utility you need to go to
|
||||||
|
1. Access the Cisco API console at: [https://apiconsole.cisco.com](https://apiconsole.cisco.com)
|
||||||
|
2. Login with your CCO credentials
|
||||||
|
3. Register your application and obtain your client credentials.
|
||||||
|
4. Once you register your application and obtain your client ID and client secret you need to add these to your environment variables like so:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ export CLIENT_ID="0lkajsdklsajdlkj"
|
||||||
|
$ export CLIENT_SECRET="2lk3rlncxk5l6l"
|
||||||
|
```
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ ./getvulns -h
|
||||||
|
Usage of ./getvulns:
|
||||||
|
-ios string
|
||||||
|
fetch Security Advisories for IOS version
|
||||||
|
-iosxe string
|
||||||
|
fetch Security Advisories for IOS-XE version
|
||||||
|
-product string
|
||||||
|
fetch Security Advisories Cisco Products, ex Cisco Adaptive Security Appliance
|
||||||
|
-v be verbose
|
||||||
|
$
|
||||||
|
```
|
||||||
|
|
||||||
|
# Building
|
||||||
|
```bash
|
||||||
|
$ go get -u -v
|
||||||
|
$ go build || go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
# Screenshot
|
||||||
|
![1](assets/img/screenshot.png)
|
BIN
assets/img/screenshot.png
Normal file
BIN
assets/img/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 627 KiB |
265
main.go
Normal file
265
main.go
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
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
|
||||||
|
}
|
Loading…
Reference in a new issue