-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from syn-4ck/develop
v0.1.0 ALPHA
- Loading branch information
Showing
32 changed files
with
2,169 additions
and
0 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* @syn-4ck |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# For most projects, this workflow file will not need changing; you simply need | ||
# to commit it to your repository. | ||
# | ||
# You may wish to alter this file to override the set of languages analyzed, | ||
# or to provide custom queries or build logic. | ||
# | ||
# ******** NOTE ******** | ||
# We have attempted to detect the languages in your repository. Please check | ||
# the `language` matrix defined below to confirm you have the correct set of | ||
# supported CodeQL languages. | ||
# | ||
name: "CodeQL" | ||
|
||
on: | ||
push: | ||
branches: [ main, develop ] | ||
pull_request: | ||
# The branches below must be a subset of the branches above | ||
branches: [ main, develop ] | ||
schedule: | ||
- cron: '32 13 * * 0' | ||
|
||
jobs: | ||
analyze: | ||
name: Analyze | ||
runs-on: ubuntu-latest | ||
|
||
strategy: | ||
fail-fast: false | ||
matrix: | ||
language: [ 'python' ] | ||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] | ||
# Learn more: | ||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed | ||
|
||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v2 | ||
|
||
# Initializes the CodeQL tools for scanning. | ||
- name: Initialize CodeQL | ||
uses: github/codeql-action/init@v1 | ||
with: | ||
languages: ${{ matrix.language }} | ||
# If you wish to specify custom queries, you can do so here or in a config file. | ||
# By default, queries listed here will override any specified in a config file. | ||
# Prefix the list here with "+" to use these queries and those in the config file. | ||
# queries: ./path/to/local/query, your-org/your-repo/queries@main | ||
|
||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). | ||
# If this step fails, then you should remove it and run the build manually (see below) | ||
- name: Autobuild | ||
uses: github/codeql-action/autobuild@v1 | ||
|
||
# ℹ️ Command-line programs to run using the OS shell. | ||
# 📚 https://git.io/JvXDl | ||
|
||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines | ||
# and modify them (or add more) to build your code if your project | ||
# uses a compiled language | ||
|
||
#- run: | | ||
# make bootstrap | ||
# make release | ||
|
||
- name: Perform CodeQL Analysis | ||
uses: github/codeql-action/analyze@v1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Changelog | ||
All notable changes to this project will be documented in this file. | ||
|
||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
|
||
## [0.1.0 ALPHA] - 2021-04-11 | ||
|
||
### Added | ||
- CLI basic scan of Cisco IOS missconfigurations | ||
- First scan modules: SSH & HTTP administration vulnerabilities | ||
- Integration with Cisco API to get IOS vulnerabilities of device version | ||
- JSON & HTML report | ||
|
||
### Changed | ||
|
||
### Deprecated | ||
|
||
### Removed | ||
|
||
### Fixed | ||
|
||
### Security | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from setuptools import find_packages | ||
from setuptools import setup | ||
|
||
with open("README.md", "r") as readme_file: | ||
readme = readme_file.read() | ||
|
||
requirements = [] | ||
|
||
setup( | ||
name="pynipper-ng", | ||
version="0.1.0", | ||
author="Syn-4ck", | ||
author_email="repoJFM@protonmail.com", | ||
description="", | ||
long_description=readme, | ||
long_description_content_type="text/markdown", | ||
url="https://github.com/syn-4ck/pynipper-ng", | ||
packages=find_packages(), | ||
install_requires=requirements, | ||
classifiers=[ | ||
"Programming Language :: Python :: 3.8", | ||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)", | ||
], | ||
) |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
from enum import Enum | ||
import json | ||
import array | ||
import time | ||
import configparser | ||
|
||
from .cisco.cisco_ios_vulns import get_cisco_ios_vulns_data | ||
from .cisco.cisco_vuln import CiscoVuln | ||
from .cisco.parse_config import get_cisco_ios_version | ||
from .cisco.parse_config import get_cisco_ios_hostname | ||
from .cisco.parse_config import get_cisco_ios_passwd_enc | ||
|
||
from .cisco.process_cisco_ios_conf import process_cisco_ios_conf | ||
|
||
from report.report import generate_html_report, generate_json_report | ||
from report.common.types import ReportType | ||
|
||
from devices.common.types import DeviceType | ||
from error.cisco_errors import GenericCiscoError | ||
|
||
def analyze_device(args: dict) -> None: | ||
|
||
#Cisco IOS devices | ||
devices = DeviceType._member_names_[:3] | ||
if args["device_type"] in devices: | ||
|
||
print("[1/4] Initializing pynipper-ng") | ||
hostname_cisco_device = get_cisco_ios_hostname(args["input_file"]) | ||
version_cisco_device = get_cisco_ios_version(args["input_file"]) | ||
|
||
#Get vulns by Cisco API | ||
config_file = configparser.ConfigParser() | ||
config_file.read(args["conf_file"]) | ||
client_id = "" | ||
if config_file.has_option('Cisco', 'CLIENT_ID'): | ||
client_id = config_file['Cisco']['CLIENT_ID'] | ||
client_secret = "" | ||
if config_file.has_option('Cisco', 'CLIENT_SECRET'): | ||
client_secret = config_file['Cisco']['CLIENT_SECRET'] | ||
|
||
vulns = get_cisco_ios_vulns_data(version_cisco_device, client_id, client_secret, args["offline"]) | ||
|
||
vulns_array = _vulns_get_fields(vulns) | ||
vulns_array_sorted = sorted(vulns_array,reverse=True) | ||
|
||
# Get Cisco report missconfigurations | ||
print("[3/4] Checking missconfiguration vulnerabilities") | ||
issues = process_cisco_ios_conf(args["input_file"]) | ||
|
||
#Generate report | ||
print("[4/4] Generating report") | ||
if args["output_type"] == ReportType._member_names_[0]: | ||
generate_html_report(args["output_file"], issues, vulns_array_sorted) | ||
elif args["output_type"] == ReportType._member_names_[1]: | ||
generate_json_report(args["output_file"], issues, vulns_array_sorted) | ||
|
||
def _vulns_get_fields(vulns: str) -> array: | ||
vulns_array = [] | ||
try: | ||
if vulns != '{}': | ||
for vuln in vulns["advisories"]: | ||
v = CiscoVuln( | ||
vuln["advisoryTitle"], | ||
vuln["summary"], | ||
vuln["cves"], | ||
vuln["cvssBaseScore"], | ||
vuln["publicationUrl"] | ||
) | ||
vulns_array.append(v) | ||
except KeyError: | ||
try: | ||
if vulns["errorCode"]: | ||
raise GenericCiscoError(vulns["errorMessage"]) | ||
except KeyError: | ||
raise GenericCiscoError("Unexpected error getting vulns from Cisco API") | ||
except Exception: | ||
raise GenericCiscoError("Unexpected error getting vulns from Cisco API") | ||
|
||
return vulns_array | ||
|
||
def _vuln_pretty_print(vuln: CiscoVuln) -> None: | ||
print(vuln) | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import requests | ||
import json | ||
import os | ||
from tqdm import tqdm | ||
|
||
ACCESS_URI = "https://cloudsso.cisco.com/as/token.oauth2?client_id=" | ||
API_URI = "https://api.cisco.com/security/advisories/ios?version=" | ||
|
||
def _get_access_token(client_id: str, client_secret: str) -> str: | ||
headers = {} | ||
headers["Content-Type"] = "application/x-www-form-urlencoded" | ||
url = ACCESS_URI + client_id + "&client_secret=" + client_secret + "&grant_type=client_credentials" | ||
response = requests.post(url, headers=headers) | ||
data = response.json() | ||
return data['access_token'] | ||
|
||
|
||
def get_cisco_ios_vulns_data(version: str, client_id: str, client_secret: str, disable_api: bool) -> json: | ||
if (disable_api): | ||
print("[2/4] Cisco API disable.") | ||
return json.dumps({}) | ||
if (client_id=="" or client_secret==""): | ||
print("[2/4] No Cisco API Credentials. Skipping get API data...") | ||
return json.dumps({}) | ||
access_token = _get_access_token(client_id, client_secret) | ||
headers = {} | ||
headers["Authorization"] = 'Bearer ' + access_token | ||
url = API_URI + version | ||
response = requests.get(url, headers=headers, stream=True) | ||
|
||
#Implement a progress bar | ||
print("[2/4] Connecting and getting data from Cisco API") | ||
total_size_in_bytes= int(response.headers.get('content-length', 0)) | ||
block_size = 1024 | ||
progress_bar = tqdm(total=total_size_in_bytes, unit_divisor=1024, unit='B', unit_scale=True, leave = True) | ||
f = open('api-data.dat', 'wb') | ||
for data in response.iter_content(block_size): | ||
progress_bar.update(len(data)) | ||
f.write(data) | ||
progress_bar.close() | ||
f.close() | ||
|
||
readable_file = open('api-data.dat', 'r') | ||
file_data = readable_file.read() | ||
readable_file.close() | ||
os.remove('api-data.dat') | ||
return json.loads(file_data) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
class CiscoVuln: | ||
|
||
def __init__(self, title, summary, cves, cvss, url): | ||
self.title = title | ||
self.summary = summary | ||
self.cves = cves | ||
if (cvss=='NA'): | ||
cvss=0.0 | ||
self.cvss = float(cvss) | ||
self.url = url | ||
|
||
def __str__(self): | ||
print("Vulnerability " + self.title + ":") | ||
print("===================================================================================================") | ||
print("Summary: " + self.summary) | ||
print("CVEs: " + str(self.cves)) | ||
print("CVSS: " + self.cvss) | ||
print("Publication URL: " + self.url) | ||
print("\n---------------------------------------------------------------------------------------------------") | ||
return("") | ||
|
||
def __eq__(self, other): | ||
return self.title == other.title | ||
|
||
def __lt__(self, other): | ||
return self.cvss < other.cvss |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
class CiscoIOSIssue: | ||
|
||
def __init__(self, title, observation, impact, ease, recommendation): | ||
self.title = title | ||
self.observation = observation | ||
self.impact = impact | ||
self.ease = ease | ||
self.recommendation = recommendation | ||
|
||
def __str__(self): | ||
print("Issue " + self.title + ":") | ||
print("===================================================================================================") | ||
print("Observation: " + self.observation) | ||
print("Impact: " + self.impact) | ||
print("Ease: " + self.ease) | ||
print("Recommendation: " + self.recommendation) | ||
print("\n---------------------------------------------------------------------------------------------------") | ||
return("") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from analyze.cisco.parse_config import parse_cisco_ios_config_file | ||
from ...ios_process.cisco_ios_issue import CiscoIOSIssue | ||
|
||
# If the device has http configured -> true | ||
def _has_http(filename: str) -> bool: | ||
parser = parse_cisco_ios_config_file(filename) | ||
http_enable = parser.find_objects("ip http server") | ||
http_disable = parser.find_objects("no ip http server") | ||
if (len(http_enable) > 0): | ||
return True | ||
elif (len(http_disable) > 0): | ||
return False | ||
else: | ||
return True #by default IOS Cisco devices has HTTP | ||
|
||
def get_cisco_ios_http(issues: list,filename: str) -> CiscoIOSIssue: | ||
if (_has_http(filename)): | ||
http_issue = CiscoIOSIssue( | ||
"HyperText Transport Protocol Service", | ||
"Recent Cisco IOS-based devices support web-based administration using the HTTP protocol. Cisco web-based administration facilities can sometimes be basic but they do provide a simple method of administering remote devices. However, HTTP is a clear-text protocol and is vulnerable to various packet-capture techniques.", | ||
"An attacker who was able to monitor network traffic could capture authentication credentials.", | ||
"Network packet and password sniffing tools are widely available on the Internet. Once authentication credentials have been captured it is trivial to use the credentials to log in using the captured credentials.", | ||
"It is recommended that, if not required, the HTTP service be disabled. If a remote method of access to the device is required, consider using HTTPS or SSH. The encrypted HTTPS and SSH services may require a firmware or hardware upgrade. The HTTP service can be disabled with the following IOS command: no ip http server. If it is not possible to upgrade the device to use the encrypted HTTPS or SSH services, additional security can be configured." | ||
) | ||
issues.append(http_issue) | ||
|
||
# Number of access list should be used to restrict access to HTTP server | ||
def get_cisco_ios_http_access_list(filename: str) -> str: | ||
parser = parse_cisco_ios_config_file(filename) | ||
access_list = parser.find_objects("ip http access-class") | ||
if (len(access_list) > 0): | ||
num = host[0].re_match_typed(r'^ip http access-class\s+(\S+)', default='') | ||
return num | ||
else: | ||
return "Undefined" | ||
|
||
# Kind of auth in HTTP server | ||
def get_cisco_ios_http_auth(filename: str) -> str: | ||
parser = parse_cisco_ios_config_file(filename) | ||
timeout = parser.find_objects("ip http auth") | ||
if (len(timeout) > 0): | ||
type = host[0].re_match_typed(r'^ip http auth(entication)?\s+(\S+)', default='') | ||
return seconds.split( )[0] | ||
else: | ||
return None | ||
|
||
def get_http_missconfigurations(filename: str) -> list: | ||
issues = [] | ||
get_cisco_ios_http(issues,filename) | ||
#get_cisco_ios_http_access_list(issues,filename) | ||
#get_cisco_ios_http_auth(issues,filename) | ||
return issues |
Oops, something went wrong.