├── .flake8 ├── .gitignore ├── Pipfile ├── README.md ├── contributing.md ├── env_lab.py ├── env_user.template ├── intro-amp ├── amp_custom_detect.py ├── amp_get_events.py ├── amp_malware_count.py ├── amp_query_param.py └── mission │ └── amp_mission.py ├── intro-firepower ├── fdm │ ├── fdm_auth.py │ ├── fdm_create_network.py │ └── fdm_list_networks.py ├── fmc │ ├── fmc_clean_access_policy.py │ ├── fmc_create_access_policy.py │ └── fmc_requests.py └── mission │ └── firepower_mission.py ├── intro-ise ├── ise_list_anc_policies.py └── mission │ └── ise_mission.py ├── intro-python ├── git-basics │ └── change_me.txt ├── parsing-json │ ├── interface-config.json │ ├── interfaces.json │ ├── nested_data.py │ ├── parsing_json.py │ └── pep20.txt ├── part1 │ ├── hands_on_exercise.py │ └── hello.py └── part2 │ ├── fortune_cookie.py │ ├── structure.py │ └── variable_scope.py ├── intro-rest-apis ├── postman │ ├── deckofcards.postman_collection.json │ └── deckofcardsapi.postman_environment.json └── python │ └── deck_of_cards.py ├── intro-threatgrid ├── maldomain.py ├── mission │ └── thgrid_mission.py ├── threatgrid_ioc_query.py └── threatgrid_search_submissions.py ├── intro-umbrella ├── EnforcementDeleteRequest.py ├── EnforcementGetRequest.py ├── EnforcementPostRequest.py ├── InvestigateGetRequest.py ├── InvestigateGetRequestThreatGrid.py ├── InvestigatePostRequest.py └── mission │ └── umbrellamission.py ├── mission-data ├── 078a122a9401dd47a61369ac769d9e707d9e86bdf7ad91708510b9a4584e8d49.json ├── 1eb15091d4605809a0a78e9c150e764c9253f9249a7babe4484c27d822d59900.json ├── 3372c1edab46837f1e973164fa2d726c5c5e17bcb888828ccd7c4dfcc234a370.json ├── 5a0d64cc41bb8455f38b4b31c6e69af9e7fd022b0ea9ea0c32c371def24d67fb.json ├── 7c9d5724064693dfeef76fd4da8d6f159ef0e6707e67c4a692a03e94f4a6e27a.json ├── 7e54dceecd3d3a23a896e971ae4bb9e71a64a5c1c3b77ac1c64241c55c1b95bb.json ├── 7fd72a36f7e0e6e0a8bc777fc9ed41e0a6d5526c98bc95a09e189531cf7e70d5.json ├── 87715c2487765488d72919a3720f11806592fe1018aa5c95aaf9fd13fb041f20.json ├── 8db0d7f3a27291f197173a1e3a3a7242fc49deb2d06f90598475c919417a1c7a.json ├── README.md ├── b75fd580c29736abd11327eef949e449f6d466a05fb6fd343d3957684c8036e5.json ├── domainlist.json ├── f52bfac9637aea189ec918d05113c36f5bcf580f3c0de8a934fe3438107d3f0c.json ├── iplist.json ├── mac-addresses.json ├── riskydomains.json └── sha256-list.json ├── my_file.py ├── postman └── umbrella │ └── Umbrella Devnet Express.postman_collection.json ├── pyproject.toml ├── requirements.txt ├── script ├── bootstrap ├── clean ├── python_template.py ├── setup └── update_dependencies └── verify ├── backend ├── ampforendpoints.py ├── fdm.py ├── fmc.py ├── ise.py ├── threatgrid.py ├── umbrellaInvestigate.py ├── umbrellaenforcement.py └── webexteams.py ├── requirements.txt ├── testEnv.py └── verify.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = 3 | .git, 4 | .venv, 5 | venv, 6 | postman, 7 | max-complexity = 10 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User Environments 2 | env_user.py 3 | 4 | # Lab Data 5 | intro-threatgrid/*.json 6 | 7 | # Mission Data 8 | mission-data/ 9 | !mission-data/README.md 10 | 11 | # Virtual Environments 12 | .venv 13 | venv/ 14 | 15 | # Version and Dependency Management 16 | .python-version 17 | Pipfile.lock 18 | 19 | # IDE / Text-Editor Artifacts 20 | .vscode 21 | .idea 22 | 23 | # Python Artifacts 24 | __pycache__/ 25 | *.py[cod] 26 | *$py.class 27 | *.so 28 | 29 | # OS Artifacts 30 | .DS_Store 31 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | ipython = "*" 8 | flake8 = "*" 9 | black = "==19.3b0" 10 | 11 | [packages] 12 | requests = "*" 13 | urllib3 = "*" 14 | bravado = "*" 15 | webexteamssdk = "*" 16 | crayons = "*" 17 | 18 | [requires] 19 | python_full_version = "3.6.5" 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Cisco DevNet DevNet Express Security Track Code 2 | 3 | This repository contains the sample code to go along with [Cisco DevNet Learning Labs](https://developer.cisco.com/learning) covering security topics. During the setup steps of the labs, you'll be asked to clone this repository down to your workstation to get started. 4 | 5 | ## Getting started 6 | 1. Go to and login or register if you're not registered. 7 | 2. Follow the instructions in the lab. The lab environment offers a step-by-step path to configure and run the examples of this repository. 8 | 9 | ## Technical requirements 10 | You may either do the labs in the virtual environment offered by Cisco or configure your own environment on your desktop. 11 | 12 | ## Accessing Cisco virtual lab environment 13 | You need a PC or MAC with the following software: 14 | - Anyconnect VPN client : either the package provided by Cisco for Windows environment or the free client available on Linux platform, openconnect . 15 | - RDP client to connect to windows, either the RDP client embarked in any windows OS or a free alternative such as Remmina . 16 | The connection parameters are provided in the lab environment. 17 | 18 | ## Working on your own desktop 19 | ### Python 3.6 20 | Python is required to run the sample scripts. 21 | 22 | You have to install Python 3.6. We suggest that you work in a virtual environment to work with Python. If you want to know more about virtual env, this document is an excellent introduction . The lab explains how to install virtual-env. You can work with Miniconda as an alternative. To install Miniconda, follow the official instructions here: . 23 | 24 | ### Postman 25 | You use Postman to test REST API calls in a graphical environment. To install Postman, follow the official instructions here: . 26 | 27 | ### Creating virtual env with Conda 28 | You should run the following commands on Linux to create a virtual environment with Conda: 29 | ``` 30 | $conda create --name cisco python=3.6 31 | $source activate cisco 32 | ``` 33 | and you're done! 34 | 35 | ## Contributing 36 | Contributions are welcome, and we are glad to review changes through pull requests. See [contributing.md](contributing.md) for details. 37 | 38 | The goal of these learning labs is to ensure a 'hands-on' learning approach rather than just theory or instructions. 39 | 40 | ## About this Sample Code 41 | 42 | Contributions are welcome, and we are glad to review changes through pull requests. See [contributing.md](contributing.md) for details. 43 | 44 | Within this repository are several files and folders covering different topics and labs. This table provides details on what each is used for, and which labs they correspond to. 45 | 46 | | File / Folder | Description | 47 | |:-----------------------------------------------|:----------------------------| 48 | | [`env_lab.py`](env_lab.py) | A Python file containing lab infrastructure details for routers, switches and appliances leveraged in the different labs. This file provides a centralized Python `import` that is used in other code samples to retrieve IPs, usernames, and passwords for connections | 49 | | [`env_user.template`](env_user.template) | Similar to `env_lab.py`, this is a template for end users to copy within their own code repo as `env_user.py` where they can provide unique details for their own accounts. For example, their Webex Teams (formerly Cisco Spark) authentication token. Not all labs require this file, if one does it will be specified in setup. | 50 | | [`requirements.txt`](requirements.txt) | Global Python requirements file containing the requirements for **all** labs within this repository. Each folder also contains a local `requirements.txt` file. | 51 | | [`intro-python-code/`](intro-python-code/) | Sample code and exercises for the [Python Fundamentals Learning Labs](https://developer.cisco.com/learning/modules/programming-fundamentals/parsing-json-python/step/1) Pulled in through a file copy in November 2018. Note that the submodule tracks with the `master` branch, but solutions are on the `solution` branch in the [original CiscoDevNet/intro-python-code repository](https://github.com/CiscoDevNet/intro-python-code).
| 52 | | [`intro-rest-api/`](intro-rest-api/) | Sample code and exercises for the [REST API Fundamentals Learning Labs](https://developer.cisco.com/learning/modules/rest-api-fundamentals/hands-on-postman/step/1) Pulled in through a file copy in November 2018. | 53 | | [`intro-umbrella/`](intro-umbrella/) | Sample code and exercises for the [Introduction to Cisco Umbrella Learning Labs]()
(_Publishing Soon_) | 54 | | [`verify/`](verify/) | A series of verification scripts primarily used during DevNet Express events to ensure the workshop environment is fully operational. | 55 | | [`dev/`](dev/) | Resources and information for building code samples and labs. | 56 | | [`requirements-dev.txt`](requirements-dev.txt) | Python requirements file containing requirements only needed if developing new code samples. | 57 | 58 | > Note: These code samples are also leveraged during DevNet Express events. If you are one of these events, your event proctors and hosts will walk you through event setup and verification steps as part of agenda. 59 | 60 | > Note: the [`mission-data`](https://github.com/CiscoDevNet/dne-security-code/tree/master/mission-data) directory contains the answsers in JSON files. This is as last resort only when the attendee is not able to solve it. Make sure that the attendees don't use this to "cheat". 61 | 62 | ## Contributing 63 | 64 | These learning modules are for public consumption, so you must ensure that you have the rights to any content that you contribute. 65 | 66 | ## Getting Involved 67 | 68 | * If you’re a Cisco employee and would like to have access to make changes yourself, please add your GitHub ID and we’ll get in touch. 69 | * If you'd like to contribute to an existing lab, refer to [contributing.md](contributing.md). 70 | * If you're interested in creating a new Cisco DevNet Learning Lab, please contact a DevNet administrator for guidance. 71 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | For sample code, you can help in these primary ways: 4 | - Testing the Learning Lab and then reporting issues in the repo or in the common issues tracking repo 5 | - Using the Issue tracker to report issues or comment that you will work on an issue 6 | - Updating the sample code 7 | - If needed, updating the content in the Learning Lab repo 8 | - If needed, requesting or creating a release for the Learning Lab 9 | - If needed, contacting DevNet to publish new or updated Learning Labs 10 | 11 | ## Using the issue tracker 12 | 13 | For DevNet Express events, use these issue tracker repos based on the content track: 14 | * https://github.com/CiscoDevNet/devnet-express-dna-issues 15 | * https://github.com/CiscoDevNet/devnet-express-cc-issues 16 | * https://github.com/CiscoDevNet/devnet-express-dci-issues 17 | * https://github.com/CiscoDevNet/devnet-express-security-issues 18 | 19 | Use the issue tracker to suggest additions, report bugs, and ask questions. 20 | This is also a great way to connect with the developers of the project and find others interested in this solution. 21 | 22 | Also use the issue tracker to find ways to contribute. Test a lab, find a bug, 23 | log an issue, or offer an update, comment on the issue that you will take on 24 | that effort, then follow the _Changing the samples_ guidance below. 25 | 26 | ## Changing the examples 27 | 28 | Generally speaking, you should clone the repository, make changes locally, and then submit a pull request (PR). We expect you have validated that all code samples work as expected. Plus, the code should follow pep8 guidelines for Python. 29 | -------------------------------------------------------------------------------- /env_lab.py: -------------------------------------------------------------------------------- 1 | """Set the Environment Information Needed to Access Your Lab! 2 | 3 | The provided sample code in this repository will reference this file to get the 4 | information needed to connect to your lab backend. You provide this info here 5 | once and the scripts in this repository will access it as needed by the lab. 6 | 7 | 8 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | """ 28 | 29 | from crayons import blue, green, red 30 | from inspect import currentframe 31 | 32 | # User Input 33 | 34 | # Please select the lab environment that you will be using today 35 | # sandbox - Cisco DevNet Always-On / Reserved Sandboxes 36 | # express - Cisco DevNet Express Lab Backend 37 | # custom - Your Own "Custom" Lab Backend 38 | ENVIRONMENT_IN_USE = "express" 39 | 40 | # End User Input 41 | 42 | 43 | if ENVIRONMENT_IN_USE == "sandbox": 44 | # Values for the Reservable FMC Sandbox 45 | FMC = { 46 | "host": "fmcrestapisandbox.cisco.com", 47 | "port": 443, 48 | "username": "", # Generated when reserved 49 | "password": "", # Generated when reserved 50 | } 51 | 52 | elif ENVIRONMENT_IN_USE == "express": 53 | # FMC in the DevNet Express Security dCloud Pod 54 | FMC = { 55 | "host": "198.18.133.9", 56 | "port": 443, 57 | "username": "apiuser", 58 | "password": "C1sco12345", 59 | } 60 | 61 | # FDM in the DevNet Express Security dCloud Pod 62 | FDM = { 63 | "host": "198.18.133.8", 64 | "port": 443, 65 | "username": "admin", 66 | "password": "C1sco12345", 67 | "api_version" : 3, 68 | } 69 | 70 | # ISE in the DevNet Express Security dCloud Pod 71 | ISE = { 72 | "host": "198.18.133.27", 73 | "port": 9060, 74 | "username": "ersadmin", 75 | "password": "C1sco12345", 76 | } 77 | 78 | # AMP for endpoints in the DevNet Express Security dCloud Pod, 79 | # If you are using your own cloud AMP account change this to api.amp.cisco.com 80 | AMP = {"host": "amp.dcloud.cisco.com"} 81 | 82 | # ThreatGrid Cloud 83 | THREATGRID = {"host": "panacea.threatgrid.com"} 84 | 85 | UMBRELLA = {"en_url": "https://s-platform.api.opendns.com/1.0/events", 86 | "inv_url": "https://investigate.api.umbrella.com", } 87 | 88 | 89 | elif ENVIRONMENT_IN_USE == "custom": 90 | # Your FMC 91 | FMC = {"host": "", "port": 443, "username": "", "password": ""} 92 | 93 | # Your FDM 94 | FDM = {"host": "", "port": 443, "username": "", "password": ""} 95 | 96 | # Your ISE 97 | ISE = {"host": "", "port": 9060, "username": "", "password": ""} 98 | 99 | # Your AMP 100 | AMP = {"host": ""} 101 | 102 | # Your ThreatGrid 103 | THREATGRID = {"host": ""} 104 | 105 | 106 | """Helper functions for the missions""" 107 | 108 | 109 | def print_missing_mission_warn(lineerror) : 110 | print(blue(f"\nPlease replace this fucntion (print_missing_mission_warn(...)) with correct required mission statements!\n")) 111 | print(green(f"At hosted DNE Event; Please ask for help from procter or your neighbour attendee; if you are not making progress...\n")) 112 | print(red(f"Check and complete the #TODO at Line number ---> {lineerror}")) 113 | return exit() 114 | 115 | def get_line(): 116 | currentfram=currentframe() 117 | return currentfram.f_back.f_lineno -------------------------------------------------------------------------------- /env_user.template: -------------------------------------------------------------------------------- 1 | """Set your Environment Information once, not many times. 2 | 3 | The provided sample code in this repository will reference this file to get the 4 | needed information about you and your context to complete the labs. You 5 | provide this info here once and the scripts in this repository will access it 6 | as needed by the lab. 7 | 8 | TODO: To setup your `env_user.py` copy this file then edit and save your info 9 | 10 | $ cp env_user.template env_user.py 11 | 12 | 13 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | """ 33 | 34 | 35 | # User Input 36 | 37 | # Webex Teams 38 | WEBEX_TEAMS_ACCESS_TOKEN = "" 39 | WEBEX_TEAMS_ROOM_ID = "" 40 | 41 | 42 | # Cisco Umbrella 43 | UMBRELLA_ENFORCEMENT_KEY = "" 44 | UMBRELLA_INVESTIGATE_KEY = "" 45 | 46 | 47 | # Cisco AMP 48 | AMP_CLIENT_ID = "" 49 | AMP_API_KEY = "" 50 | 51 | 52 | # Cisco ThreatGrid 53 | THREATGRID_API_KEY = "" 54 | 55 | # End User Input 56 | -------------------------------------------------------------------------------- /intro-amp/amp_custom_detect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Intro to Cisco AMP Step 3 4 | Copyright (c) 2018-2020 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import json 25 | import sys 26 | from pathlib import Path 27 | 28 | import requests 29 | import webexteamssdk 30 | from crayons import blue, green, red 31 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 32 | 33 | 34 | # Locate the directory containing this file and the repository root. 35 | # Temporarily add these directories to the system path so that we can import 36 | # local files. 37 | here = Path(__file__).parent.absolute() 38 | repository_root = (here / ".." ).resolve() 39 | 40 | sys.path.insert(0, str(repository_root)) 41 | 42 | import env_lab # noqa 43 | import env_user # noqa 44 | 45 | SAMPLE_SHA256="3372c1edab46837f1e973164fa2d726c5c7f17dcb888828ccd7c4dfcc234a375" 46 | 47 | # Disable insecure request warnings 48 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 49 | 50 | 51 | # Functions 52 | def get_amp_detections( 53 | host=env_lab.AMP.get("host"), 54 | client_id=env_user.AMP_CLIENT_ID, 55 | api_key=env_user.AMP_API_KEY 56 | ): 57 | """Get a list of recent custom detections from Cisco AMP.""" 58 | print("\n==> Getting recent custom detections from AMP") 59 | 60 | url = f"https://{client_id}:{api_key}@{host}/v1/file_lists/simple_custom_detections" 61 | 62 | response = requests.get(url, verify=False) 63 | response.raise_for_status() 64 | 65 | events_list = response.json() 66 | 67 | print(green(f"Retrieved {len(events_list)} data from AMP")) 68 | 69 | return events_list 70 | 71 | def parseResponse(file_lists): 72 | print(file_lists) 73 | for item in file_lists["data"]: 74 | if item["name"] == "File Blacklist": 75 | list_id = item["guid"] 76 | return list_id 77 | 78 | def post_to_amp( 79 | list_id, 80 | payload, 81 | host=env_lab.AMP.get("host"), 82 | client_id=env_user.AMP_CLIENT_ID, 83 | api_key=env_user.AMP_API_KEY): 84 | print("\n==> Adding SHA to AMP custom detections list") 85 | 86 | url = f"https://{client_id}:{api_key}@{host}/v1/file_lists/{list_id}/files/{SAMPLE_SHA256}" 87 | 88 | response = requests.post(url, post_this, verify=False) 89 | response.raise_for_status() 90 | 91 | rdata = response.json()["data"] 92 | return rdata 93 | 94 | # If this script is the "main" script, run... 95 | if __name__ == "__main__": 96 | 97 | #TODO: Call the function to get detections 98 | dects = 99 | #TODO: Call the function to parse the custom detections list 100 | list_id = 101 | post_this = {'description':'created by DNE user using API'} 102 | #TODO: Call post function to add a problem sha to your custom detection list 103 | response = post_to_amp(TODO, TODO) 104 | 105 | print(f"\n==> Successfully added {SAMPLE_SHA256} to AMP custom detections list") 106 | 107 | -------------------------------------------------------------------------------- /intro-amp/amp_get_events.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Cisco Intro to AMP Hands on Exercisce Step2 4 | 5 | Copyright (c) 2018-2020 Cisco and/or its affiliates. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | 27 | import json 28 | import sys 29 | from pathlib import Path 30 | import requests 31 | from crayons import green, yellow 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / ".." ).resolve() 40 | sys.path.insert(0, str(repository_root)) 41 | 42 | import env_lab # noqa 43 | import env_user # noqa 44 | 45 | 46 | # Disable insecure request warnings 47 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 48 | 49 | 50 | # Functions 51 | def get_amp_events( 52 | host=env_lab.AMP.get("host"), 53 | client_id=env_user.AMP_CLIENT_ID, 54 | api_key=env_user.AMP_API_KEY, 55 | ): 56 | """Get a list of recent events from Cisco AMP.""" 57 | print("\n==> Getting recent events from AMP") 58 | # TODO: Construct the URL 59 | url = f"https://{client_id}:{api_key}@{host}/v1/events" 60 | 61 | response = requests.get(url, verify=False) 62 | response.raise_for_status() 63 | 64 | events_list = response.json()["data"] 65 | 66 | return events_list 67 | 68 | 69 | def write_events_to_file(filepath, ampevents): 70 | with open(events_path, "w") as file: 71 | json.dump(ampevents, file, indent=2) 72 | 73 | 74 | # If this script is the "main" script, run... 75 | if __name__ == "__main__": 76 | # TODO: Get the list of events from AMP 77 | # Hint: Call the function to get AMP events and assign it to variable amp_events 78 | 79 | # TODO: Print the total number of events observed. 80 | print(yellow(f"Received total {len(amp_events)} malware events")) 81 | # Get the current file directory root 82 | repository_root = (here).resolve() 83 | # create a newfile "events.json" 84 | events_path = repository_root / "events.json" 85 | # TODO: Store the events observed in a file. 86 | # Hint: 1 set up path (uncomment next line) 87 | # events_path = repository_root / "events.json" 88 | 89 | print(green(f"\n==> Saving events to: {events_path}")) 90 | #TODO: Hint: 2 you can all the write_events_to_file function 91 | 92 | -------------------------------------------------------------------------------- /intro-amp/amp_malware_count.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Intro to Cisco AMP Step 3 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import json 27 | import sys 28 | from pathlib import Path 29 | 30 | import requests 31 | import webexteamssdk 32 | from crayons import blue, green, red 33 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 34 | 35 | 36 | # Locate the directory containing this file and the repository root. 37 | # Temporarily add these directories to the system path so that we can import 38 | # local files. 39 | here = Path(__file__).parent.absolute() 40 | repository_root = (here / ".." ).resolve() 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | import env_lab # noqa 45 | import env_user # noqa 46 | 47 | 48 | # Disable insecure request warnings 49 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 50 | 51 | 52 | # Functions 53 | def get_amp_events( 54 | host=env_lab.AMP.get("host"), 55 | client_id=env_user.AMP_CLIENT_ID, 56 | api_key=env_user.AMP_API_KEY, 57 | ): 58 | """Get a list of recent events from Cisco AMP.""" 59 | print("\n==> Getting recent events from AMP") 60 | 61 | url = f"https://{client_id}:{api_key}@{host}/v1/events" 62 | 63 | response = requests.get(url, verify=False) 64 | response.raise_for_status() 65 | 66 | events_list = response.json()["data"] 67 | 68 | print(green(f"Retrieved {len(events_list)} events from AMP")) 69 | 70 | return events_list 71 | 72 | 73 | # If this script is the "main" script, run... 74 | if __name__ == "__main__": 75 | #TODO Get the list of events from AMP; Please call the right function 76 | # Hint: Call the function and assign it to variable "amp_events" 77 | amp_events = 78 | #TODO: Enter the specific event you are interested in to find from the result Hint: malware execute event id is 1107296272 79 | # Hint: Create a variable "malware_event_id" assign the malware execute event id 80 | count = 0 81 | for event in amp_events: 82 | if event["event_type_id"] == malware_event_id: 83 | count=count+1 84 | 85 | print(red(f"Found {count} malware events!!!")) -------------------------------------------------------------------------------- /intro-amp/amp_query_param.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Intro to Cisco AMP Step 3 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import json 27 | import sys 28 | from pathlib import Path 29 | 30 | import requests 31 | import webexteamssdk 32 | from crayons import blue, green, red 33 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 34 | 35 | 36 | # Locate the directory containing this file and the repository root. 37 | # Temporarily add these directories to the system path so that we can import 38 | # local files. 39 | here = Path(__file__).parent.absolute() 40 | repository_root = (here / ".." ).resolve() 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | import env_lab # noqa 45 | import env_user # noqa 46 | 47 | 48 | # Disable insecure request warnings 49 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 50 | 51 | 52 | # Functions 53 | def get_amp_events( 54 | qparams, 55 | host=env_lab.AMP.get("host"), 56 | client_id=env_user.AMP_CLIENT_ID, 57 | api_key=env_user.AMP_API_KEY 58 | ): 59 | """Get a list of recent events from Cisco AMP.""" 60 | print("\n==> Getting recent events from AMP") 61 | 62 | url = f"https://{client_id}:{api_key}@{host}/v1/events" 63 | 64 | response = requests.get(url,params=qparams, verify=False) 65 | response.raise_for_status() 66 | 67 | events_list = response.json()["data"] 68 | 69 | print(green(f"Retrieved {len(events_list)} events from AMP")) 70 | 71 | return events_list 72 | 73 | 74 | # If this script is the "main" script, run... 75 | if __name__ == "__main__": 76 | #TODO: Create the query for AMP here 77 | #Hint: create a variable qparams and assign "event_type[]=1090519054&limit=10" to it 78 | qparams = 79 | #TODO: Call the function to get events 80 | #Hint : Call the correct function and assign return value to a variable "amp_events" 81 | amp_events = 82 | #TODO: Print the events (Think if you can make a colored print like previous examples) 83 | print(json.dumps(MISSION, indent=2)) 84 | 85 | -------------------------------------------------------------------------------- /intro-amp/mission/amp_mission.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Mission - Cisco AMP 3 | 4 | This is your starting point for the Zero-day Workflow. 5 | 6 | 7 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | """ 27 | 28 | 29 | import json 30 | import sys 31 | from pathlib import Path 32 | 33 | import requests 34 | import webexteamssdk 35 | from crayons import blue, green, red 36 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 37 | from inspect import currentframe 38 | 39 | # Locate the directory containing this file and the repository root. 40 | # Temporarily add these directories to the system path so that we can import 41 | # local files. 42 | here = Path(__file__).parent.absolute() 43 | repository_root = (here / ".." / "..").resolve() 44 | 45 | sys.path.insert(0, str(repository_root)) 46 | 47 | import env_lab # noqa 48 | import env_user # noqa 49 | 50 | 51 | # Disable insecure request warnings 52 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 53 | 54 | 55 | # Functions 56 | def get_amp_events( 57 | host=env_lab.AMP.get("host"), 58 | client_id=env_user.AMP_CLIENT_ID, 59 | api_key=env_user.AMP_API_KEY, 60 | ): 61 | """Get a list of recent events from Cisco AMP.""" 62 | print(blue("\n==> Getting recent events from AMP")) 63 | 64 | url = f"https://{client_id}:{api_key}@{host}/v1/events" 65 | 66 | response = requests.get(url, verify=False) 67 | response.raise_for_status() 68 | 69 | events_list = response.json()["data"] 70 | 71 | print(green(f"Retrieved {len(events_list)} events from AMP")) 72 | 73 | return events_list 74 | 75 | 76 | def get_amp_computer_details( url, 77 | client_id=env_user.AMP_CLIENT_ID, 78 | api_key=env_user.AMP_API_KEY, 79 | ): 80 | 81 | """Get details of infected computer from Cisco AMP.""" 82 | url = f"https://{client_id}:{api_key}@{url}" 83 | 84 | #TODO: do a GET request to retrieve infected computer details (remmeber to NOT do SSL verification!) 85 | # Hint: Remember to assign it varibale "response" 86 | env_lab.print_missing_mission_warn(env_lab.get_line()) 87 | 88 | response = MISSION 89 | response.raise_for_status() 90 | events_list = response.json()["data"] 91 | return events_list 92 | 93 | 94 | def extract_observables(amp_events): 95 | """Extract hash, IP, and MAC address observables from Malware events.""" 96 | print(blue("\n==> Extracting observables from the AMP events")) 97 | 98 | # Initialize data structures for collecting observables 99 | observables = [] 100 | 101 | # Standard AMP event ID for Malware events 102 | malware_event_id = 1107296272 103 | value = "" 104 | ip = "" 105 | mac = "" 106 | sha256= "" 107 | for event in amp_events: 108 | if event["event_type_id"] == malware_event_id: 109 | try: 110 | hostname = event["computer"]["hostname"] 111 | """ get the links URL to get details of infected computer""" 112 | url = event["computer"]["links"]["computer"] 113 | malU=url.partition("https://")[2] 114 | 115 | """ get the links URL to get details of infected computer""" 116 | events_list = get_amp_computer_details(malU) 117 | ip = events_list["network_addresses"][0]["ip"] 118 | mac = events_list["network_addresses"][0]["mac"] 119 | sha256 = event["file"]["identity"]["sha256"] 120 | """ Handle missing dict Key errors""" 121 | except KeyError as ke: 122 | value = "None" 123 | observables.append({ 124 | "hostname": hostname, 125 | "ip_address": ip, 126 | "mac_address": mac, 127 | "sha256": sha256, 128 | }) 129 | 130 | if observables: 131 | print(green(f"Extracted observables from " 132 | f"{len(observables)} malware events")) 133 | else: 134 | print(red("No malware events found.")) 135 | sys.exit(1) 136 | 137 | return observables 138 | 139 | 140 | 141 | # If this script is the "main" script, run... 142 | if __name__ == "__main__": 143 | #TODO: Use the right function to fill the amp_events variable with the AMP events 144 | env_lab.print_missing_mission_warn(env_lab.get_line()) 145 | amp_events = MISSION 146 | 147 | #TODO: Use the right function to fill the amp_observables variable with extracted observables from the AMP events 148 | env_lab.print_missing_mission_warn(env_lab.get_line()) 149 | amp_observables = MISSION 150 | 151 | # Save the MAC addresses of the endpoints where malware executed to a JSON 152 | # file. In the ISE Mission we will read this file and quarantine these 153 | # endpoints. 154 | mac_addresses_path = repository_root / "mission-data/mac-addresses.json" 155 | print(blue(f"\n==> Saving MAC addresses to: {mac_addresses_path}")) 156 | 157 | with open(mac_addresses_path, "w") as file: 158 | mac_addresses = [o["mac_address"] for o in amp_observables] 159 | json.dump(mac_addresses, file, indent=2) 160 | 161 | # Save the malware SHA256 hashes to a JSON file. We will use these in the 162 | # ThreatGrid Mission. 163 | sha256_list_path = repository_root / "mission-data/sha256-list.json" 164 | print(blue(f"\n==> Saving SHA256 hashes to: {sha256_list_path}")) 165 | 166 | #TODO: open a file and write to it, similar to the code in lines 154-156. However, this time use the sha256_list_path, and instead of mac addresses. use the sha256 hashes. (Tip: try to print amp_observables, so that you know what to search for.) 167 | env_lab.print_missing_mission_warn(env_lab.get_line()) 168 | with open(MISSION, "w") as file: 169 | MISSION = [o["MISSION"] for o in amp_observables] 170 | json.dump(MISSION, file, indent=2) 171 | 172 | # Finally, post a message to the Webex Teams Room to brag!!! 173 | print(blue("\n==> Posting message to Webex Teams")) 174 | 175 | teams = webexteamssdk.WebexTeamsAPI(env_user.WEBEX_TEAMS_ACCESS_TOKEN) 176 | teams.messages.create( 177 | roomId=env_user.WEBEX_TEAMS_ROOM_ID, 178 | markdown=f"**AMP Mission completed!!!** \n\n" 179 | f"I extracted observables from {len(amp_observables)} AMP " 180 | f"malware events." 181 | ) 182 | 183 | print(green("AMP Mission Completed!!!")) 184 | -------------------------------------------------------------------------------- /intro-firepower/fdm/fdm_auth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Authenticate with FDM and obtain an API access token. 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import sys 27 | from pathlib import Path 28 | 29 | import requests 30 | from crayons import blue, green 31 | from requests import HTTPError 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / ".." / "..").resolve() 40 | 41 | sys.path.insert(0, str(repository_root)) 42 | 43 | from env_lab import FDM # noqa 44 | 45 | 46 | # Disable insecure request warnings 47 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 48 | 49 | 50 | def fdm_login( 51 | host=FDM.get("host"), 52 | port=FDM.get("port"), 53 | username=FDM.get("username"), 54 | password=FDM.get("password"), 55 | api_version=FDM.get("api_version"), 56 | ): 57 | """Login to FDM and return an access token that may be used for API calls. 58 | 59 | This login will give you an access token that is valid for ~30 minutes 60 | with no refresh. Using this token should be fine for short running scripts. 61 | 62 | Do not use for services that need to last longer than 30 minutes. 63 | """ 64 | print(blue("\n==> Logging into FDM and requesting an access token")) 65 | 66 | headers = { 67 | "Content-Type": "application/json", 68 | "Accept": "application/json", 69 | } 70 | 71 | payload = { 72 | "grant_type": "password", 73 | "username": username, 74 | "password": password, 75 | } 76 | 77 | response = requests.post( 78 | f"https://{host}:{port}/api/fdm/v{api_version}/fdm/token", 79 | headers=headers, 80 | json=payload, 81 | verify=False, 82 | ) 83 | 84 | try: 85 | response.raise_for_status() 86 | access_token = response.json()["access_token"] 87 | 88 | except HTTPError: 89 | if response.status_code == 400: 90 | raise HTTPError(f"Error logging in to FDM: {response.text}") 91 | else: 92 | raise 93 | 94 | except ValueError: 95 | raise ValueError("Error parsing the response from FDM") 96 | 97 | print( 98 | green("Login was successful!"), 99 | f"Access Token: {access_token}", 100 | sep="\n" 101 | ) 102 | 103 | return access_token 104 | 105 | 106 | # If this script is the "main" script, run... 107 | if __name__ == "__main__": 108 | fdm_login() 109 | -------------------------------------------------------------------------------- /intro-firepower/fdm/fdm_create_network.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Get the list of all networks in FDM. 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import sys 27 | from pathlib import Path 28 | from pprint import pformat 29 | 30 | import requests 31 | from crayons import blue, green, white 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / ".." / "..").resolve() 40 | 41 | sys.path.insert(0, str(repository_root)) 42 | sys.path.insert(0, str(here)) 43 | 44 | from env_lab import FDM # noqa 45 | from fdm_auth import fdm_login # noqa 46 | 47 | 48 | # Data for the network object to be created 49 | NETWORK_OBJECT = { 50 | "name": "PUT_NAME_HERE", 51 | "description": "DevNet Security", 52 | "subType": "HOST", 53 | "value": "5.5.5.5", 54 | "type": "networkobject" 55 | } 56 | 57 | 58 | # Disable insecure request warnings 59 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 60 | 61 | 62 | def fdm_create_network( 63 | network_object, 64 | access_token, 65 | host=FDM.get("host"), 66 | port=FDM.get("port"), 67 | api_version=FDM.get("api_version"), 68 | ): 69 | """Create a new network in FDM.""" 70 | print(blue("\n==> Creating a new network in FDM")) 71 | 72 | headers = { 73 | "Content-Type": "application/json", 74 | "Accept": "application/json", 75 | "Authorization": f"Bearer {access_token}", 76 | } 77 | 78 | payload = network_object 79 | 80 | response = requests.post( 81 | f"https://{host}:{port}/api/fdm/v{api_version}/object/networks", 82 | headers=headers, 83 | json=payload, 84 | verify=False, 85 | ) 86 | response.raise_for_status() 87 | 88 | print(green("Successfully created the new network!")) 89 | return response.json() 90 | 91 | 92 | # If this script is the "main" script, run... 93 | if __name__ == "__main__": 94 | token = fdm_login() 95 | new_network_details = fdm_create_network(NETWORK_OBJECT, token) 96 | 97 | print( 98 | white("Network Details:", bold=True), 99 | pformat(new_network_details), 100 | sep="\n" 101 | ) 102 | -------------------------------------------------------------------------------- /intro-firepower/fdm/fdm_list_networks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Get the list of all networks in FDM. 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import sys 27 | from pathlib import Path 28 | from pprint import pformat 29 | 30 | import requests 31 | from crayons import blue, green, white 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / ".." / "..").resolve() 40 | 41 | sys.path.insert(0, str(repository_root)) 42 | sys.path.insert(0, str(here)) 43 | 44 | from env_lab import FDM # noqa 45 | from fdm_auth import fdm_login # noqa 46 | 47 | 48 | # Disable insecure request warnings 49 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 50 | 51 | 52 | def fdm_get_networks(access_token, host=FDM.get("host"), port=FDM.get("port"), api_version=FDM.get("api_version"),): 53 | """Get the list of all Networks in FDM.""" 54 | print(blue("\n==> Getting a list of all Networks in FDM")) 55 | 56 | headers = { 57 | "Content-Type": "application/json", 58 | "Accept": "application/json", 59 | "Authorization": f"Bearer {access_token}", 60 | } 61 | 62 | response = requests.get( 63 | f"https://{host}:{port}/api/fdm/v{api_version}/object/networks", 64 | headers=headers, 65 | verify=False, 66 | ) 67 | response.raise_for_status() 68 | 69 | print(green("Successfully retrieved the list of Networks")) 70 | return response.json() 71 | 72 | 73 | # If this script is the "main" script, run... 74 | if __name__ == "__main__": 75 | token = fdm_login() 76 | networks = fdm_get_networks(token) 77 | 78 | print( 79 | white('Network(s):', bold=True), 80 | pformat(networks), 81 | sep="\n", 82 | ) 83 | -------------------------------------------------------------------------------- /intro-firepower/fmc/fmc_clean_access_policy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Check for a 'DNE Security Access Control Policy' and remove it if found. 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | import sys 26 | from pathlib import Path 27 | 28 | from crayons import blue, green 29 | 30 | 31 | # Locate the directory containing this file and the repository root. 32 | # Temporarily add these directories to the system path so that we can import 33 | # local files. 34 | here = Path(__file__).parent.absolute() 35 | repository_root = (here / ".." / "..").resolve() 36 | 37 | sys.path.insert(0, str(repository_root)) 38 | sys.path.insert(0, str(here)) 39 | 40 | 41 | from fmc_requests import fmc_authenticate, fmc_get, fmc_delete # noqa 42 | 43 | 44 | # Authenticate with FMC 45 | fmc_authenticate() 46 | 47 | 48 | # Get the configured access policies 49 | print(blue("\n==> Retrieving the configured access policies")) 50 | 51 | configured_policies = fmc_get("policy/accesspolicies") 52 | 53 | print( 54 | green("Successfully retrieved the list of configured access policies"), 55 | f"Retrieved {len(configured_policies['items'])} policies", 56 | sep="\n" 57 | ) 58 | 59 | 60 | # Look for a policy named "DNE Security Access Control Policy" 61 | print(blue("\n==> Looking for the 'DNE Security Access Control Policy'")) 62 | 63 | for policy in configured_policies["items"]: 64 | if policy["name"] == "DNE Security Access Control Policy": 65 | print("Policy found") 66 | 67 | print(blue("\n==> Deleting the `DNE Security Access Control Policy`")) 68 | 69 | fmc_delete(f"policy/accesspolicies/{policy['id']}") 70 | 71 | print(green("Policy deleted")) 72 | 73 | break 74 | 75 | else: 76 | print(green( 77 | "The `DNE Security Access Control Policy` doesn't exist; " 78 | "you are good to go!" 79 | )) 80 | -------------------------------------------------------------------------------- /intro-firepower/fmc/fmc_create_access_policy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Create an FMC Access Policy and Rules. 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import sys 27 | from pathlib import Path 28 | from pprint import pformat 29 | 30 | from crayons import blue, green 31 | 32 | 33 | # Locate the directory containing this file and the repository root. 34 | # Temporarily add these directories to the system path so that we can import 35 | # local files. 36 | here = Path(__file__).parent.absolute() 37 | repository_root = (here / ".." / "..").resolve() 38 | 39 | sys.path.insert(0, str(repository_root)) 40 | sys.path.insert(0, str(here)) 41 | 42 | from fmc_requests import fmc_authenticate, fmc_post # noqa 43 | 44 | 45 | # Authenticate with FMC 46 | fmc_authenticate() 47 | 48 | 49 | # Create an Access Policy 50 | print(blue("\n==> Creating a new Access Policy on FMC")) 51 | 52 | access_policy = { 53 | "type": "AccessPolicy", 54 | "name": "DNE Security Access Control Policy", 55 | "description": "Basic AC Policy", 56 | "defaultAction": {"action": "BLOCK"}, 57 | } 58 | 59 | created_policy = fmc_post("policy/accesspolicies", access_policy) 60 | 61 | print( 62 | green('Policy Created:'), 63 | pformat(access_policy), 64 | sep="\n", 65 | ) 66 | 67 | 68 | # Create an Access Rule in the new policy 69 | print(blue("\n==> Creating an Access Rule in the new policy")) 70 | 71 | access_rule = { 72 | "action": "ALLOW", 73 | "enabled": True, 74 | "type": "AccessRule", 75 | "name": "Rule1", 76 | "sourceNetworks": { 77 | "objects": [ 78 | { 79 | "type": "Network", 80 | "overridable": False, 81 | "id": "005056B8-1C9D-0ed3-0000-085899345923", 82 | "name": "DNE_Security_Internal_Network", 83 | } 84 | ] 85 | }, 86 | "sendEventsToFMC": False, 87 | "logFiles": False, 88 | "logBegin": False, 89 | "logEnd": False, 90 | } 91 | 92 | created_rule = fmc_post( 93 | f"policy/accesspolicies/{created_policy['id']}/accessrules", 94 | access_rule, 95 | ) 96 | 97 | print( 98 | green("Access Rule Created:"), 99 | pformat(created_rule), 100 | sep="\n", 101 | ) 102 | -------------------------------------------------------------------------------- /intro-firepower/fmc/fmc_requests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Helper functions to authenticate with FMC and send basic CRUD requests. 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import sys 27 | from pathlib import Path 28 | 29 | import requests 30 | from crayons import blue, green 31 | from requests.auth import HTTPBasicAuth 32 | from urllib3.exceptions import InsecureRequestWarning 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / ".." / "..").resolve() 40 | 41 | sys.path.insert(0, str(repository_root)) 42 | sys.path.insert(0, str(here)) 43 | 44 | from env_lab import FMC # noqa 45 | 46 | 47 | # Global Variables 48 | domain_uuid = "" 49 | headers = { 50 | "Content-Type": "application/json", 51 | "Accept": "application/json", 52 | } 53 | 54 | 55 | # Disable insecure request warnings 56 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 57 | 58 | 59 | # Helper Functions 60 | def fmc_authenticate( 61 | host=FMC.get("host"), 62 | port=FMC.get("port"), 63 | username=FMC.get("username"), 64 | password=FMC.get("password"), 65 | ): 66 | """Authenticate with FMC; get and store the auth token and domain UUID.""" 67 | print(blue("\n==> Authenticating with FMC and requesting an access token")) 68 | 69 | global domain_uuid 70 | 71 | authentication = HTTPBasicAuth(username, password) 72 | 73 | response = requests.post( 74 | f"https://{host}:{port}/api/fmc_platform/v1/auth/generatetoken", 75 | headers=headers, 76 | auth=authentication, 77 | verify=False 78 | ) 79 | response.raise_for_status() 80 | 81 | # Get the authentication token and domain UUID from the response 82 | access_token = response.headers.get("X-auth-access-token") 83 | domain_uuid = response.headers.get("DOMAIN_UUID") 84 | 85 | # Update the headers used for subsequent requests to FMC 86 | headers["DOMAIN_UUID"] = domain_uuid 87 | headers["X-auth-access-token"] = access_token 88 | 89 | print( 90 | green('Successfully authenticated to FMC!'), 91 | f"Domain UUID: {domain_uuid}", 92 | f"Access Token: {access_token}", 93 | sep="\n" 94 | ) 95 | 96 | return access_token, domain_uuid 97 | 98 | 99 | def create_url(endpoint_path, host=FMC.get("host"), port=FMC.get("port")): 100 | """Create an FMC configuration API endpoint URL.""" 101 | return f"https://{host}:{port}/api/fmc_config/v1" \ 102 | f"/domain/{domain_uuid}/{endpoint_path}" 103 | 104 | 105 | def fmc_post(endpoint_path, data): 106 | """Send a POST request to FMC and return the parsed JSON response.""" 107 | url = create_url(endpoint_path) 108 | 109 | print("Sending POST request to", url) 110 | response = requests.post(url, headers=headers, json=data, verify=False) 111 | response.raise_for_status() 112 | 113 | return response.json() 114 | 115 | 116 | def fmc_get(endpoint_path): 117 | """Send a GET request to FMC and return the parsed JSON response.""" 118 | url = create_url(endpoint_path) 119 | 120 | print("Sending GET request to", url) 121 | response = requests.get(url, headers=headers, verify=False) 122 | response.raise_for_status() 123 | 124 | return response.json() 125 | 126 | 127 | def fmc_delete(endpoint_path): 128 | """Send a DELETE request to FMC and return the parsed JSON response.""" 129 | url = create_url(endpoint_path) 130 | 131 | print("Sending DELETE request to", url) 132 | response = requests.delete(url, headers=headers, verify=False) 133 | response.raise_for_status() 134 | 135 | return response.json() 136 | 137 | 138 | # If this script is the "main" script, run... 139 | if __name__ == "__main__": 140 | fmc_authenticate() 141 | -------------------------------------------------------------------------------- /intro-firepower/mission/firepower_mission.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | FDM Mission --- Now you will apply the URL filtering on the NGFW. 4 | 5 | Copyright (c) 2018-2020 Cisco and/or its affiliates. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | import json 26 | import sys 27 | from pathlib import Path 28 | 29 | import requests 30 | import webexteamssdk 31 | from crayons import blue, green, red 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | from bravado.client import SwaggerClient 34 | from bravado.requests_client import RequestsClient 35 | 36 | # Locate the directory containing this file and the repository root. 37 | # Temporarily add these directories to the system path so that we can import 38 | # local files. 39 | here = Path(__file__).parent.absolute() 40 | repository_root = (here / ".." / "..").resolve() 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | 45 | from env_lab import FDM 46 | from env_user import WEBEX_TEAMS_ACCESS_TOKEN 47 | from env_user import WEBEX_TEAMS_ROOM_ID 48 | pathf = Path(__file__).parent.absolute() 49 | fdmfolder = (pathf / ".." / "fdm").resolve() 50 | 51 | 52 | # Disable insecure request warnings 53 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 54 | 55 | 56 | headers = { 57 | "Content-Type": "application/json", 58 | "Accept": "application/json", 59 | "Authorization": "Bearer " 60 | } 61 | 62 | api_version = FDM.get("api_version") 63 | 64 | 65 | 66 | 67 | def login(host=FDM.get("host"), 68 | port=FDM.get("port"), 69 | username=FDM.get("username"), 70 | password=FDM.get("password"), ): 71 | 72 | payload = { 73 | "grant_type": "password", 74 | "username": username, 75 | "password": password, 76 | } 77 | #mission TODO: Complete the URL to get FDM oAuth token Here is starting string "https://{host}:{port}/api/fdm/v{api_version}" 78 | env_lab.print_missing_mission_warn(env_lab.get_line()) 79 | url = MISSION 80 | 81 | r = requests.post(url, json=payload, verify=False, headers=headers) 82 | access_token = "Bearer %s" % r.json()['access_token'] 83 | headers['Authorization'] = access_token 84 | 85 | def get_spec_json(host=FDM.get("host"), 86 | port=FDM.get("port"), 87 | username=FDM.get("username"), 88 | password=FDM.get("password"),): 89 | http_client = RequestsClient() 90 | http_client.session.verify = False 91 | http_client.session.headers = headers 92 | url = f"https://{host}:{port}/apispec/ngfw.json" 93 | client = SwaggerClient.from_url(url, http_client=http_client, config={'validate_responses':False}) 94 | return client 95 | 96 | # ---------------- 97 | def create_reference_model(client, model): 98 | """ 99 | Creates a ReferenceModel object referring to the passed in model object. 100 | This is used when one object refers to another object. 101 | 102 | client -- Bravado client object 103 | model -- destination model object 104 | """ 105 | ReferenceModel = client.get_model('ReferenceModel') 106 | reference_model = ReferenceModel(id=model['id'], type=model['type']) 107 | if hasattr(model, 'name'): 108 | reference_model['name'] = model['name'] 109 | if hasattr(model, 'version'): 110 | reference_model['version'] = model['version'] 111 | return reference_model 112 | 113 | 114 | def create_url_object(client, domain): 115 | """ 116 | Creates a single URL object 117 | 118 | client -- Bravado client object 119 | domain -- A single domain to create into a URL object 120 | 121 | Return created URL object 122 | """ 123 | url_object = client.get_model("URLObject")(type="urlobject") 124 | url_object.name = domain 125 | url_object.url = domain 126 | temp = client.URLObject.addURLObject(body=url_object).result() 127 | print(f"Created URL Object : {domain}\n\n") 128 | return temp 129 | 130 | def create_url_object_group(client, name, url_objects): 131 | """ 132 | Creates a single URL object group 133 | 134 | client -- Bravado client object 135 | name -- Name of the url object group 136 | url_objects -- List of URL objects to add to the group 137 | 138 | Returns single URL object group 139 | """ 140 | url_object_group = client.get_model("URLObjectGroup")(type="urlobjectgroup") 141 | url_object_group.name = name 142 | url_object_group.objects = [create_reference_model(client, x) for x in url_objects] 143 | temp = client.URLObject.addURLObjectGroup(body=url_object_group).result() 144 | print(f"Created URL Group Object : {name}\n\n") 145 | return temp 146 | 147 | 148 | def create_access_rule(client, url_object_group): 149 | """ 150 | Creates a single access rule denying the url object group 151 | 152 | client -- Bravado client object 153 | url_object_group -- A single URL object group 154 | 155 | Returns created access rule 156 | """ 157 | # get access policy first 158 | access_policy = client.AccessPolicy.getAccessPolicyList().result()['items'][0] 159 | 160 | # create embedded app filter 161 | embedded_url_filter = client.get_model("EmbeddedURLFilter")(type="embeddedurlfilter") 162 | embedded_url_filter.urlObjects = [create_reference_model(client, url_object_group)] 163 | 164 | # Access Rule model 165 | access_rule = client.get_model("AccessRule")(type="accessrule") 166 | access_rule.name = 'block bad domains' 167 | access_rule.urlFilter = embedded_url_filter 168 | access_rule.ruleAction = "DENY" 169 | temp = client.AccessPolicy.addAccessRule(body=access_rule, parentId=access_policy.id).result() 170 | print(f"Created Access Policy to block URL Object : {access_rule.name}\n\n") 171 | return temp 172 | 173 | def dedupe_list(mylist) : 174 | """ 175 | Creates a list without duplicates 176 | 177 | mylist -- The input list 178 | 179 | Returns List without the duplicates 180 | """ 181 | deduped_items = [] 182 | duplicates = [] 183 | for i in mylist: 184 | if i not in deduped_items: 185 | deduped_items.append(i) 186 | else: 187 | duplicates.append(i) 188 | print(f"We found dulicates and pruned the list : {duplicates}\n") 189 | return deduped_items 190 | 191 | 192 | def readdomains_file(filename) : 193 | with open (filename, 'r') as fp: 194 | maclist = json.loads(fp.read()) 195 | return maclist 196 | 197 | 198 | 199 | if __name__ == '__main__': 200 | #TODO Mission login for API access 201 | MISSON() 202 | client = get_spec_json() 203 | domain_list = [] 204 | clean_domains = [] 205 | #Read the domain file created by ThreatGrid 206 | domainlist_path = repository_root / "mission-data/riskydomains.json" 207 | domain_list = readdomains_file(domainlist_path) 208 | #TODO Mission make sure there no duplicate domains 209 | clean_domains = MISSION 210 | env_lab.print_missing_mission_warn(env_lab.get_line()) 211 | 212 | url_objects = [] 213 | for doms in clean_domains: 214 | url_objects.append(create_url_object(client, doms)) 215 | 216 | #TODO Mission Create a url group using the url_objects created in the above steps: 217 | #Pass these 3 values to the proper function client, "your_picked_name_for_URL_Object", url objects create in above for loop 218 | env_lab.print_missing_mission_warn(env_lab.get_line()) 219 | url_object_group = MISSION 220 | 221 | #TODO Mission Create Access Rule (Hint: Look for the relevant function above) to block the URL object created above ... which will block all the risky domains 222 | env_lab.print_missing_mission_warn(env_lab.get_line()) 223 | 224 | #post Message to WebEx Teams! 225 | print(blue("\n==> Posting message to Webex Teams")) 226 | teams = webexteamssdk.WebexTeamsAPI(WEBEX_TEAMS_ACCESS_TOKEN) 227 | teams.messages.create( 228 | roomId=WEBEX_TEAMS_ROOM_ID, 229 | markdown=f"**Firepower - FDM Mission completed!!!** \n\n" 230 | f"I was able to block domains from the file" 231 | 232 | ) 233 | print(green("Firepower - FDM: Mission Completed!!!")) 234 | -------------------------------------------------------------------------------- /intro-ise/ise_list_anc_policies.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """List ISE Adaptive Network Control (ANC) policies. 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import sys 27 | from pathlib import Path 28 | from pprint import pformat 29 | 30 | import requests 31 | from crayons import blue, green, white 32 | from requests.auth import HTTPBasicAuth 33 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 34 | 35 | 36 | # Locate the directory containing this file and the repository root. 37 | # Temporarily add these directories to the system path so that we can import 38 | # local files. 39 | here = Path(__file__).parent.absolute() 40 | repository_root = (here / "..").resolve() 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | from env_lab import ISE # noqa 45 | 46 | 47 | # Disable insecure request warnings 48 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 49 | 50 | 51 | def get_ise_anc_policies( 52 | host=ISE.get("host"), 53 | port=ISE.get("port"), 54 | username=ISE.get("username"), 55 | password=ISE.get("password"), 56 | ): 57 | """Get a list of configured ISE Adaptive Network Control (ANC) policies.""" 58 | print(blue("\n==> Getting ISE ANC policies")) 59 | 60 | headers = { 61 | 'Content-Type': "application/json", 62 | 'Accept': "application/json" 63 | } 64 | 65 | authentication = HTTPBasicAuth(username, password) 66 | 67 | response = requests.get( 68 | f"https://{host}:{port}/ers/config/ancpolicy", 69 | headers=headers, 70 | auth=authentication, 71 | verify=False, 72 | ) 73 | response.raise_for_status() 74 | 75 | print(green("Successfully retrieved the ANC policies!")) 76 | 77 | return response.json()["SearchResult"]["resources"] 78 | 79 | 80 | def get_ise_anc_policy_details( 81 | policy_id, 82 | host=ISE.get("host"), 83 | port=ISE.get("port"), 84 | username=ISE.get("username"), 85 | password=ISE.get("password"), 86 | ): 87 | """Retrieve the details of a configured ANC policy.""" 88 | print(blue(f"\n==> Getting the details for the `{policy_id}` ANC policy")) 89 | 90 | headers = { 91 | 'Content-Type': "application/json", 92 | 'Accept': "application/json" 93 | } 94 | 95 | authentication = HTTPBasicAuth(username, password) 96 | 97 | response = requests.get( 98 | f"https://{host}:{port}/ers/config/ancpolicy/{policy_id}", 99 | headers=headers, 100 | auth=authentication, 101 | verify=False, 102 | ) 103 | response.raise_for_status() 104 | 105 | print(green(f"Successfully retrieved the `{policy_id}`` ANC policy")) 106 | 107 | return response.json() 108 | 109 | 110 | # If this script is the "main" script, run... 111 | if __name__ == "__main__": 112 | #TODO : Call the fucntion to get ISE ANC policies assign the value returned by function to "policies" variable 113 | policies = MISSION 114 | print( 115 | white("\nAdaptive Network Control (ANC) Policies:", bold=True), 116 | pformat(policies), 117 | sep="\n" 118 | ) 119 | #TODO: Use the policy/polices you have received from ISE to get the details on the policy. 120 | devnet_anc_policy = MISSION 121 | 122 | #TODO: Print the policy details you have received from ISE 123 | print( 124 | white("\nANC_Devnet Adaptive Network Control Policy:", bold=True), 125 | pformat(MISSION), 126 | sep="\n" 127 | ) 128 | -------------------------------------------------------------------------------- /intro-ise/mission/ise_mission.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Mission - Cisco ISE 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import json 25 | import sys 26 | from pathlib import Path 27 | 28 | import requests 29 | import webexteamssdk 30 | from crayons import blue, green, red 31 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 32 | 33 | 34 | # Locate the directory containing this file and the repository root. 35 | # Temporarily add these directories to the system path so that we can import 36 | # local files. 37 | here = Path(__file__).parent.absolute() 38 | repository_root = (here / ".." / "..").resolve() 39 | 40 | sys.path.insert(0, str(repository_root)) 41 | 42 | import env_lab # noqa 43 | import env_user # noqa 44 | 45 | 46 | # Disable insecure request warnings 47 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 48 | 49 | 50 | # Functions 51 | 52 | def createPayload(maclist, policy): 53 | data_to_send = { 54 | 'OperationAdditionalData': { 55 | 'additionalData' : [{ 56 | 'name': 'macAddress', 57 | f'value': maclist 58 | }, 59 | { 60 | 'name': 'policyName', 61 | f'value': policy 62 | }] 63 | } 64 | } 65 | return data_to_send 66 | 67 | 68 | def readmacaddr_file(filename) : 69 | with open (filename, 'r') as fp: 70 | maclist = json.loads(fp.read()) 71 | return maclist 72 | 73 | headers = { 74 | 'content-type': "application/json", 75 | 'accept': "application/json" 76 | } 77 | username = env_lab.ISE.get("username") 78 | password = env_lab.ISE.get("password") 79 | host = env_lab.ISE.get("host") 80 | port = env_lab.ISE.get("port") 81 | 82 | 83 | def get_policy_ise(): 84 | 85 | #TODO: Create the URL for the GET request to get the ANC policy from ISE. Hint: Make sure you pass the Auth paramenters for the API call 86 | env_lab.print_missing_mission_warn(env_lab.get_line()) 87 | url = MISSION 88 | 89 | #Create GET Request 90 | req = requests.get(url, verify=False, headers=headers) 91 | #req = requests.request("GET", url, verify=False, headers=headers) 92 | namelist = " " 93 | if(req.status_code == 200): 94 | resp_json = req.json() 95 | policies = resp_json["SearchResult"]["resources"] 96 | for policy in policies: 97 | namelist = policy["name"] 98 | print("\nI've Found the Quarantine Policy {0} to Nuke the Rogue computers from the corp network... \n".format(namelist) ) 99 | else: 100 | print("An error has ocurred with the following code %(error)s" % {'error': req.status_code}) 101 | return namelist 102 | 103 | def post_to_ise(maclist, namelist): 104 | #TODO: Create the URL for the PUT request to apply the ANC policy! Hint: Make sure you pass the Auth paramenters for the API call 105 | url = MISSION 106 | env_lab.print_missing_mission_warn(env_lab.get_line()) 107 | 108 | for items in maclist: 109 | payload = "{\r\n \"OperationAdditionalData\": {\r\n \"additionalData\": [{\r\n \"name\": \"macAddress\",\r\n \"value\": \""+ items + "\"\r\n },\r\n {\r\n \"name\": \"policyName\",\r\n \"value\": \"" + namelist + '"' + "\r\n }]\r\n }\r\n}" 110 | print(json.dumps(payload,sort_keys=True,indent=3)) 111 | response = requests.request("PUT", url, data=payload, verify=False, headers=headers) 112 | if(response.status_code == 204): 113 | print("Done!..Applied Quarantine policy to the rogue endpoint...MAC: {0} Threat is now contained....".format(items)) 114 | else: 115 | print("An error has ocurred with the following code %(error)s" % {'error': response.status_code}) 116 | 117 | if __name__ == "__main__": 118 | maclist_path = repository_root / "mission-data/mac-addresses.json" 119 | maclist = readmacaddr_file(maclist_path) 120 | 121 | #TODO Call the function for getting ANC policy and store it in the policylist variable 122 | env_lab.print_missing_mission_warn(env_lab.get_line()) 123 | policylist = MISSION 124 | 125 | #TODO call the function for applying policy to the endpoints 126 | env_lab.print_missing_mission_warn(env_lab.get_line()) 127 | 128 | # # Finally, post a message to the Webex Teams Room to brag!!! 129 | print(blue("\n==> Posting message to Webex Teams")) 130 | teams = webexteamssdk.WebexTeamsAPI(env_user.WEBEX_TEAMS_ACCESS_TOKEN) 131 | teams.messages.create( 132 | roomId=env_user.WEBEX_TEAMS_ROOM_ID, 133 | markdown=f"**ISE Mission completed!!!** \n\n" 134 | f"I have applied quarantine policy to the rogue endpoints! \n\n" 135 | 136 | ) 137 | 138 | print(green("ISE Mission Completed!!!")) 139 | -------------------------------------------------------------------------------- /intro-python/git-basics/change_me.txt: -------------------------------------------------------------------------------- 1 | If you change even one character in this file... Git will know. 2 | -------------------------------------------------------------------------------- /intro-python/parsing-json/interface-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ietf-interfaces:interface": { 3 | "name": "GigabitEthernet2", 4 | "description": "Wide Area Network", 5 | "enabled": true, 6 | "ietf-ip:ipv4": { 7 | "address": [ 8 | { 9 | "ip": "172.16.0.2", 10 | "netmask": "255.255.255.0" 11 | } 12 | ] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /intro-python/parsing-json/interfaces.json: -------------------------------------------------------------------------------- 1 | { 2 | "ietf-interfaces:interfaces": { 3 | "interface": [ 4 | { 5 | "name": "GigabitEthernet1", 6 | "type": "iana-if-type:ethernetCsmacd", 7 | "enabled": true, 8 | "ietf-ip:ipv4": { 9 | "address": [ 10 | { 11 | "ip": "198.18.134.11", 12 | "netmask": "255.255.192.0" 13 | } 14 | ] 15 | }, 16 | "ietf-ip:ipv6": {} 17 | }, 18 | { 19 | "name": "GigabitEthernet2", 20 | "type": "iana-if-type:ethernetCsmacd", 21 | "enabled": true, 22 | "ietf-ip:ipv4": { 23 | "address": [ 24 | { 25 | "ip": "172.16.255.1", 26 | "netmask": "255.255.255.0" 27 | } 28 | ] 29 | }, 30 | "ietf-ip:ipv6": {} 31 | }, 32 | { 33 | "name": "Loopback0", 34 | "description": "loop 0", 35 | "type": "iana-if-type:softwareLoopback", 36 | "enabled": true, 37 | "ietf-ip:ipv4": { 38 | "address": [ 39 | { 40 | "ip": "10.0.0.1", 41 | "netmask": "255.255.255.255" 42 | } 43 | ] 44 | }, 45 | "ietf-ip:ipv6": {} 46 | } 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /intro-python/parsing-json/nested_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Working with nested data hands-on exercise / coding challenge.""" 3 | 4 | 5 | import json 6 | import os 7 | 8 | 9 | # Get the absolute path for the directory where this file is located "here" 10 | here = os.path.abspath(os.path.dirname(__file__)) 11 | 12 | 13 | with open(os.path.join(here, "interfaces.json")) as file: 14 | # TODO: Parse the contents of the JSON file into a variable 15 | 16 | 17 | # TODO: Loop through the interfaces in the JSON data and print out each 18 | # interface's name, ip, and netmask. 19 | -------------------------------------------------------------------------------- /intro-python/parsing-json/parsing_json.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Parsing structured JSON text into native Python data structures... 3 | 4 | ...and how to access and work with nested data. 5 | """ 6 | 7 | 8 | import json 9 | import os 10 | from pprint import pprint 11 | 12 | 13 | # Get the absolute path for the directory where this file is located "here" 14 | here = os.path.abspath(os.path.dirname(__file__)) 15 | 16 | 17 | # Read in the JSON text 18 | with open(os.path.join(here, "interface-config.json")) as file: 19 | json_text = file.read() 20 | 21 | 22 | # Display the type and contents of the json_text variable 23 | print("json_text is a", type(json_text)) 24 | print(json_text) 25 | 26 | 27 | # Use the json module to parse the JSON string into native Python data 28 | json_data = json.loads(json_text) 29 | 30 | 31 | # Display the type and contents of the json_data variable 32 | print("json_data is a", type(json_data)) 33 | pprint(json_data) 34 | -------------------------------------------------------------------------------- /intro-python/parsing-json/pep20.txt: -------------------------------------------------------------------------------- 1 | Beautiful is better than ugly. 2 | Explicit is better than implicit. 3 | Simple is better than complex. 4 | Complex is better than complicated. 5 | Flat is better than nested. 6 | Sparse is better than dense. 7 | Readability counts. 8 | Special cases aren't special enough to break the rules. 9 | Although practicality beats purity. 10 | Errors should never pass silently. 11 | Unless explicitly silenced. 12 | In the face of ambiguity, refuse the temptation to guess. 13 | There should be one-- and preferably only one --obvious way to do it. 14 | Although that way may not be obvious at first unless you're Dutch. 15 | Now is better than never. 16 | Although never is often better than *right* now. 17 | If the implementation is hard to explain, it's a bad idea. 18 | If the implementation is easy to explain, it may be a good idea. 19 | Namespaces are one honking great idea -- let's do more of those! 20 | -------------------------------------------------------------------------------- /intro-python/part1/hands_on_exercise.py: -------------------------------------------------------------------------------- 1 | """Intro to Python - Part 1 - Hands-On Exercise.""" 2 | 3 | 4 | import math 5 | import random 6 | 7 | 8 | # TODO: Write a print statement that displays both the type and value of `pi` 9 | pi = math.pi 10 | 11 | 12 | # TODO: Write a conditional to print out if `i` is less than or greater than 50 13 | i = random.randint(0, 100) 14 | 15 | 16 | # TODO: Write a conditional that prints the color of the picked fruit 17 | picked_fruit = random.choice(['orange', 'strawberry', 'banana']) 18 | 19 | 20 | # TODO: Write a function that multiplies two numbers and returns the result 21 | # Define the function here. 22 | 23 | 24 | # TODO: Now call the function a few times to calculate the following answers 25 | 26 | print("12 x 96 =", ) 27 | 28 | print("48 x 17 =", ) 29 | 30 | print("196523 x 87323 =", ) 31 | -------------------------------------------------------------------------------- /intro-python/part1/hello.py: -------------------------------------------------------------------------------- 1 | """Run a Python script from the Terminal by passing it to the Interpreter.""" 2 | 3 | print("Hello Python!! ...at least I didn't say World.") 4 | -------------------------------------------------------------------------------- /intro-python/part2/fortune_cookie.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """Script flow and debugging. Print your own fortune cookie!""" 4 | 5 | 6 | import random 7 | 8 | 9 | FORTUNES = [ 10 | "There is a good chance your code will work, eventually.", 11 | "The weather will be hot, cold or just right today.", 12 | "I see Network DevOps in your future.", 13 | ] 14 | 15 | 16 | def generate_fortune() -> str: 17 | """Use mystical forces (a random selection) to get a user's fortune.""" 18 | return random.choice(FORTUNES) 19 | 20 | 21 | def generate_lucky_numbers(how_many: int) -> list: 22 | """Returns a list of (random) 'lucky' numbers.""" 23 | lucky_numbers = [] 24 | for _ in range(how_many): 25 | lucky_numbers.append(random.randint(0, 99)) 26 | return lucky_numbers 27 | 28 | 29 | def create_fortune_cookie_message(how_many_lucky_numbers: int) -> str: 30 | """Create and return a fortune cookie message. 31 | 32 | The message should include the user's fortune and lucky numbers. 33 | """ 34 | # TODO: Create a fortune cookie message by calling generate_fortune() and 35 | # generate_lucky_numbers() and then composing and returning the fortune 36 | # cookie's message. 37 | 38 | raise NotImplementedError() 39 | 40 | 41 | def main(): 42 | """Create and print a fortune cookie.""" 43 | print("Get your fortune cookie!") 44 | 45 | # Prompt the user for how many lucky numbers they would like 46 | qty_lucky_numbers = input("How many lucky numbers would you like? ") 47 | qty_lucky_numbers = int(qty_lucky_numbers.strip()) 48 | 49 | # Create and display their Fortune 50 | fortune_cookie_message = create_fortune_cookie_message(qty_lucky_numbers) 51 | print("\nHere is your fortune:\n") 52 | print(fortune_cookie_message) 53 | 54 | 55 | if __name__ == '__main__': 56 | main() 57 | -------------------------------------------------------------------------------- /intro-python/part2/structure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # """Module docstring.""" 3 | 4 | 5 | # Imports 6 | import os 7 | import sys 8 | 9 | 10 | # Module Constants 11 | START_MESSAGE = "CLI Inspection Script" 12 | 13 | 14 | # Module "Global" Variables 15 | location = os.path.abspath(__file__) 16 | 17 | 18 | # Module Functions and Classes 19 | def main(*args): 20 | """My main script function. 21 | 22 | Displays the full patch to this script, and a list of the arguments passed 23 | to the script. 24 | """ 25 | print(START_MESSAGE) 26 | print("Script Location:", location) 27 | print("Arguments Passed:", args) 28 | 29 | 30 | # Check to see if this file is the "__main__" script being executed 31 | if __name__ == '__main__': 32 | _, *script_args = sys.argv 33 | main(*script_args) 34 | -------------------------------------------------------------------------------- /intro-python/part2/variable_scope.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Demonstrate module vs. locally scoped variables.""" 3 | 4 | 5 | # Create a module variable 6 | module_variable = "I am a module variable." 7 | 8 | 9 | # Define a function that expects to receive a value for an argument variable 10 | def my_function(argument_variable): 11 | """Showing how module, argument, and local variables are used.""" 12 | # Create a local variable 13 | local_variable = "I am a local variable." 14 | 15 | print(module_variable, "...and I can be accessed inside a function.") 16 | print(argument_variable, "...and I can be passed to a function.") 17 | print(local_variable, "...and I can ONLY be accessed inside a function.") 18 | 19 | 20 | # Call the function; supplying the value for the argument variable 21 | my_function(argument_variable="I am a argument variable.") 22 | 23 | 24 | # Let's try accessing that local variable here at module scope 25 | print("\nTrying to access local_variable outside of its function...") 26 | try: 27 | print(local_variable) 28 | except NameError as error: 29 | print(error) 30 | -------------------------------------------------------------------------------- /intro-rest-apis/postman/deckofcards.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "e15f88fe-57c8-ff09-d177-678f6254a145", 4 | "name": "Deckofcards", 5 | "description": "The Deck of Cards API provides a programmatic way to make requests for cards in a standard deck or to shuffle cards in a deck. Multiple decks can be shuffled or drawn from. See https://deckofcardsapi.com for more information. No authorization is needed.", 6 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 7 | }, 8 | "item": [ 9 | { 10 | "name": "Draw three cards", 11 | "request": { 12 | "method": "GET", 13 | "header": [], 14 | "body": {}, 15 | "url": { 16 | "raw": "https://deckofcardsapi.com/api/deck/{{deck_id}}/draw/?count=3", 17 | "protocol": "https", 18 | "host": [ 19 | "deckofcardsapi", 20 | "com" 21 | ], 22 | "path": [ 23 | "api", 24 | "deck", 25 | "{{deck_id}}", 26 | "draw", 27 | "" 28 | ], 29 | "query": [ 30 | { 31 | "key": "count", 32 | "value": "3" 33 | } 34 | ] 35 | }, 36 | "description": "deckofcardsapi.com " 37 | }, 38 | "response": [] 39 | }, 40 | { 41 | "name": "Shuffle single deck", 42 | "request": { 43 | "method": "GET", 44 | "header": [], 45 | "body": {}, 46 | "url": { 47 | "raw": "https://deckofcardsapi.com/api/deck/new/shuffle/?deck_count=1", 48 | "protocol": "https", 49 | "host": [ 50 | "deckofcardsapi", 51 | "com" 52 | ], 53 | "path": [ 54 | "api", 55 | "deck", 56 | "new", 57 | "shuffle", 58 | "" 59 | ], 60 | "query": [ 61 | { 62 | "key": "deck_count", 63 | "value": "1" 64 | } 65 | ] 66 | }, 67 | "description": "Shuffle the cards in a single deck" 68 | }, 69 | "response": [] 70 | } 71 | ], 72 | "event": [ 73 | { 74 | "listen": "prerequest", 75 | "script": { 76 | "id": "8357713b-c0fc-4ff4-94cd-691c1dbb2d16", 77 | "type": "text/javascript", 78 | "exec": [ 79 | "" 80 | ] 81 | } 82 | }, 83 | { 84 | "listen": "test", 85 | "script": { 86 | "id": "63700483-f301-47b3-97c3-0a794eb389be", 87 | "type": "text/javascript", 88 | "exec": [ 89 | "" 90 | ] 91 | } 92 | } 93 | ] 94 | } -------------------------------------------------------------------------------- /intro-rest-apis/postman/deckofcardsapi.postman_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "c0f39bb9-81de-51d5-2eeb-c6ab08856f4a", 3 | "name": "deckOfCardsApi", 4 | "values": [ 5 | { 6 | "enabled": true, 7 | "key": "deck_id", 8 | "value": "ghndyy3ygexv", 9 | "type": "text" 10 | }, 11 | { 12 | "enabled": true, 13 | "key": "deck_id_blackjack", 14 | "value": "kumcct6f4gte", 15 | "type": "text" 16 | } 17 | ], 18 | "_postman_variable_scope": "environment", 19 | "_postman_exported_at": "2018-04-03T22:10:06.836Z", 20 | "_postman_exported_using": "Postman/6.0.10" 21 | } -------------------------------------------------------------------------------- /intro-rest-apis/python/deck_of_cards.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Shuffle six decks of cards using the Deck of Cards API 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import requests 27 | 28 | 29 | url = "https://deckofcardsapi.com/api/deck/new/shuffle/" 30 | querystring = {"deck_count": "6"} 31 | headers = { 32 | 'Cache-Control': "no-cache", 33 | 'Postman-Token': "dd1d8ca5-7000-21b2-2230-39969d585419" 34 | } 35 | response = requests.request("GET", url, headers=headers, params=querystring) 36 | 37 | print(response.text) 38 | deck = response.json() 39 | deck_id = deck['deck_id'] 40 | print(deck_id) 41 | -------------------------------------------------------------------------------- /intro-threatgrid/maldomain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Search ThreatGrid Submissions. 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import json 27 | import sys 28 | from datetime import datetime, timedelta 29 | from pathlib import Path 30 | from pprint import pprint 31 | 32 | import requests 33 | from crayons import blue, green, white 34 | 35 | 36 | # Locate the directory containing this file and the repository root. 37 | # Temporarily add these directories to the system path so that we can import 38 | # local files. 39 | here = Path(__file__).parent.absolute() 40 | repository_root = (here / "..").resolve() 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | from env_lab import THREATGRID # noqa 45 | from env_user import THREATGRID_API_KEY # noqa 46 | api_key = THREATGRID_API_KEY 47 | 48 | url = 'https://panacea.threatgrid.com/api/v2/iocs/feeds/domains?after=2018-07-18T21:39:13Z&before=2019-07-18T22:39:13Z&domain=lamp.troublerifle.bid&api_key={}'.format(api_key) 49 | r = requests.get(url) 50 | pprint (r.json()) 51 | -------------------------------------------------------------------------------- /intro-threatgrid/mission/thgrid_mission.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Mission - Cisco ThreatGrid 3 | 4 | This is your research step in the Zero-day Workflow. 5 | 6 | 7 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | """ 27 | 28 | 29 | import json 30 | import sys 31 | from pathlib import Path 32 | 33 | import requests 34 | import webexteamssdk 35 | from crayons import blue, green, red 36 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 37 | 38 | # Locate the directory containing this file and the repository root. 39 | # Temporarily add these directories to the system path so that we can import 40 | # local files. 41 | here = Path(__file__).parent.absolute() 42 | repository_root = (here / ".." / "..").resolve() 43 | 44 | sys.path.insert(0, str(repository_root)) 45 | 46 | from env_lab import THREATGRID # noqa 47 | from env_user import THREATGRID_API_KEY # noqa 48 | from env_user import WEBEX_TEAMS_ACCESS_TOKEN 49 | from env_user import WEBEX_TEAMS_ROOM_ID 50 | # Disable insecure request warnings 51 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 52 | th_headers = { 53 | 'Content-Type': 'application/json' 54 | } 55 | #Containers for the domains 56 | domain_list = [] 57 | #containers for the Ips 58 | ip_list = [] 59 | observables=[] 60 | def readIocsFile(filename): 61 | with open (filename, 'r') as fp: 62 | shalist = json.loads(fp.read()) 63 | return shalist 64 | 65 | # step one query threatgrid for the sha_256 and extract relevant information 66 | # important info is sample id 67 | # import info is threat_score 68 | 69 | 70 | def get_FromThreatGrid(path, 71 | host=THREATGRID.get("host"), 72 | key=THREATGRID_API_KEY,): 73 | """GET method for Threatgrid.""" 74 | url = f"{host}/api/v2" 75 | response = requests.get( 76 | "https://{}{}&api_key={}".format(url, path, key), 77 | headers=th_headers 78 | ) 79 | #print (response.json()) 80 | # Consider any status other than 2xx an error 81 | response.raise_for_status() 82 | return response.json() 83 | 84 | 85 | def find_Obervables(sha_256_1): 86 | print(f"Picking up the next sha from the list: {sha_256_1} ") 87 | samples = get_FromThreatGrid("/search/submissions?q={}".format(sha_256_1)) 88 | #print (samples) 89 | if(samples == None): 90 | return 91 | if (samples == "Response [408]"): 92 | return 93 | sample_ids = {} 94 | behaviors = [] 95 | flist_path = repository_root / "mission-data" / f"{sha_256_1}.json" 96 | for sample in samples['data']['items']: 97 | sample_ids[sample["item"]["sample"] 98 | ] = sample["item"]["analysis"]["threat_score"] 99 | for behavior in sample["item"]["analysis"]["behaviors"]: 100 | behaviors.append(behavior["title"]) 101 | # Prepare TG report to screen with average score after number of runs and behavior 102 | behaviors = set(behaviors) 103 | num_of_runs = len(sample_ids) 104 | total = 0 105 | sample_string = "" 106 | domains = "" 107 | writeme=[] 108 | for sample, score in sample_ids.items(): 109 | total = total + score 110 | sample_string = "{}{},".format(sample_string, sample) 111 | if(num_of_runs>0): 112 | print(f"Threat Score of sample: {total/num_of_runs}\n") 113 | else: 114 | print(f"Sample not found in the time window provided\n") 115 | for value in behaviors: 116 | if len(value)== 0: 117 | writeme.append(f"Sample for {sha_256_1} not found in the ThreatGrid.. Try increasing the time window or upload the sample") 118 | else: 119 | writeme.append(value) 120 | sample_string = sample_string[:-1] 121 | writer_file(flist_path, writeme, None) 122 | #print (sample_string) 123 | if len(sample_string) != 0: 124 | domains = get_FromThreatGrid( 125 | "/samples/feeds/domains?sample={}&after=2018-2-2".format(sample_string)) # if no samples returned, increase range, e.g. check out after 2010-07-18T21:39:13Z 126 | if (domains == "Response [408]"): 127 | return 128 | else: 129 | for domain in domains["data"]["items"]: 130 | if domain["relation"] == "dns-lookup": 131 | for item in domain["data"]["answers"]: 132 | observables.append({ 133 | "domains": domain["domain"], 134 | "ip_address": item, 135 | }) 136 | 137 | def writer_file(filename, glist, ioc): 138 | with open(filename, "w") as file: 139 | if ioc==None: 140 | json.dump(glist, file, indent=2) 141 | else: 142 | jsondata = [o[ioc] for o in glist] 143 | json.dump(jsondata, file, indent=2) 144 | file.close() 145 | 146 | 147 | 148 | if __name__ == "__main__": 149 | # Save the MAC addresses of the endpoints where malware executed to a JSON 150 | # file. In the ISE Mission we will read this file and quarantine these 151 | # endpoints.sha256-list.json 152 | shalist_path = repository_root / "mission-data/sha256-list.json" 153 | shalist = readIocsFile(shalist_path) 154 | #TODO: iterate through the shalist and find the observables per sha! hint: for ... in ...: 155 | env_lab.print_missing_mission_warn(env_lab.get_line()) 156 | 157 | 158 | #Create data files for the Umbrella Mission. 159 | domainlist_path = repository_root / "mission-data/domainlist.json" 160 | iplist_path = repository_root / "mission-data/iplist.json" 161 | 162 | writer_file(domainlist_path, observables, "domains") 163 | 164 | #TODO: Write the ipaddress from observables to a file: Hint look above how we did the domains 165 | env_lab.print_missing_mission_warn(env_lab.get_line()) 166 | writer_file(TODO) 167 | 168 | # Finally, post a message to the Webex Teams Room to brag!!! 169 | print(blue("\n==> Posting message to Webex Teams")) 170 | 171 | teams = webexteamssdk.WebexTeamsAPI(WEBEX_TEAMS_ACCESS_TOKEN) 172 | teams.messages.create( 173 | roomId=WEBEX_TEAMS_ROOM_ID, 174 | markdown=f"**ThreatGrid Mission completed!!!** \n\n" 175 | f"I extracted domains & IP associated with SHAs {len(observables)} using ThreatGrid " 176 | f"APIs Sample Search." 177 | ) 178 | -------------------------------------------------------------------------------- /intro-threatgrid/threatgrid_ioc_query.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Query the ThreatGrid Indication of Compromise (IoC) feed. 3 | 4 | Copyright (c) 2018-2020 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import json 27 | import sys 28 | from datetime import datetime, timedelta 29 | from pathlib import Path 30 | 31 | import requests 32 | from crayons import blue, green, white 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / "..").resolve() 40 | 41 | sys.path.insert(0, str(repository_root)) 42 | 43 | from env_lab import THREATGRID # noqa 44 | from env_user import THREATGRID_API_KEY # noqa 45 | 46 | 47 | # Constants 48 | TIME_PERIOD = 500 # Search the IoC feed for the past TIME_PERIOD days 49 | 50 | 51 | def query_threatgrid_ioc_feed( 52 | domain, 53 | after=None, 54 | before=None, 55 | host=THREATGRID.get("host"), 56 | api_key=THREATGRID_API_KEY, 57 | ): 58 | """Query the IoC feed by domain and return the list of malware samples. 59 | 60 | Args: 61 | domain(str): Lookup this domain name in the ThreatGrid IoC feed. 62 | after(str): Query for events that occurred after this datetime. 63 | before(str): Query for events that occurred before this datetime. 64 | host(str): The ThreatGrid host. 65 | api_key(str): Your ThreatGrid API key. 66 | """ 67 | print(blue(f"\n==> Querying the ThreatGrid IoC feed for domain: {domain}")) 68 | 69 | query_parameters = { 70 | "domain": domain, 71 | "after": after, 72 | "before": before, 73 | "api_key": api_key, 74 | } 75 | 76 | response = requests.get( 77 | f"https://{host}/api/v2/iocs/feeds/domains", 78 | params=query_parameters, 79 | ) 80 | response.raise_for_status() 81 | 82 | samples = response.json()["data"]["items"] 83 | 84 | print(green(f"Successfully retrieved data on " 85 | f"{len(samples)} malware samples")) 86 | 87 | return samples 88 | 89 | 90 | # If this script is the "main" script, run... 91 | if __name__ == "__main__": 92 | if len(sys.argv) == 2: 93 | _, query_domain = sys.argv 94 | else: 95 | print(f"{white('Usage:', bold=True)} {Path(__file__).name} DOMAIN") 96 | sys.exit(1) 97 | 98 | query_start = datetime.utcnow() - timedelta(days=TIME_PERIOD) 99 | query_end = datetime.utcnow() 100 | #TODO Call the function for query feed. 101 | malware_samples = TODO( 102 | query_domain, 103 | after=f"{query_start.isoformat()}Z", 104 | before=f"{query_end.isoformat()}Z", 105 | ) 106 | 107 | malware_samples_path = here / f"{query_domain}-malware-samples-data.json" 108 | print(blue(f"\n==> Saving samples data to: {malware_samples_path}")) 109 | #TODO: Pass the proper path to the open function 110 | with open(TODO, "w") as file: 111 | json.dump(malware_samples, file, indent=2) 112 | -------------------------------------------------------------------------------- /intro-threatgrid/threatgrid_search_submissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Search ThreatGrid Submissions. 3 | 4 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | import json 27 | import sys 28 | from pathlib import Path 29 | 30 | import requests 31 | from crayons import blue, green, red, white 32 | 33 | 34 | # Locate the directory containing this file and the repository root. 35 | # Temporarily add these directories to the system path so that we can import 36 | # local files. 37 | here = Path(__file__).parent.absolute() 38 | repository_root = (here / "..").resolve() 39 | 40 | sys.path.insert(0, str(repository_root)) 41 | 42 | from env_lab import THREATGRID # noqa 43 | from env_user import THREATGRID_API_KEY # noqa 44 | 45 | 46 | def search_threatgrid_submissions( 47 | sha256, 48 | host=THREATGRID.get("host"), 49 | api_key=THREATGRID_API_KEY, 50 | ): 51 | """Search TreatGrid Submissions, by sha256. 52 | 53 | Args: 54 | sha256(str): Lookup this hash in ThreatGrid Submissions. 55 | host(str): The ThreatGrid host. 56 | api_key(str): Your ThreatGrid API key. 57 | """ 58 | print(blue(f"\n==> Searching the ThreatGrid Submissions for: {sha256}")) 59 | 60 | query_parameters = { 61 | "q": sha256, 62 | "api_key": api_key, 63 | } 64 | 65 | response = requests.get( 66 | f"https://{host}/api/v2/search/submissions", 67 | params=query_parameters, 68 | ) 69 | response.raise_for_status() 70 | 71 | submission_info = response.json()["data"]["items"] 72 | 73 | if submission_info: 74 | print(green("Successfully retrieved data on the sha256 submission")) 75 | else: 76 | print(red("Unable to retrieve data on the sha256 submission")) 77 | sys.exit(1) 78 | 79 | return submission_info 80 | 81 | 82 | # If this script is the "main" script, run... 83 | if __name__ == "__main__": 84 | if len(sys.argv) == 2: 85 | _, sha256 = sys.argv 86 | else: 87 | print(f"{white('Usage:', bold=True)} {Path(__file__).name} SHA256") 88 | sys.exit(1) 89 | """ #TODO Call the proper function """ 90 | submission_info = 91 | 92 | submission_info_path = here / f"{sha256}-submission-info.json" 93 | print(blue(f"\n==> Saving submission info to: {submission_info_path}")) 94 | """ #TODO Pass the file path to open function to write the file """ 95 | with open(#TODO:) as file: 96 | json.dump(submission_info, file, indent=2) 97 | -------------------------------------------------------------------------------- /intro-umbrella/EnforcementDeleteRequest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | 5 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from datetime import datetime 27 | import requests 28 | import socket 29 | import configparser 30 | import json 31 | import sys 32 | from pathlib import Path 33 | import webexteamssdk 34 | from crayons import blue, green, red 35 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 36 | 37 | 38 | # Locate the directory containing this file and the repository root. 39 | # Temporarily add these directories to the system path so that we can import 40 | # local files. 41 | here = Path(__file__).parent.absolute() 42 | repository_root = (here / "..").resolve() 43 | 44 | sys.path.insert(0, str(repository_root)) 45 | 46 | sys.path.insert(0, str(repository_root)) 47 | 48 | from env_lab import UMBRELLA # noqa 49 | from env_user import UMBRELLA_ENFORCEMENT_KEY 50 | from env_user import UMBRELLA_INVESTIGATE_KEY # noqa 51 | from env_user import WEBEX_TEAMS_ACCESS_TOKEN 52 | from env_user import WEBEX_TEAMS_ROOM_ID 53 | # Disable insecure request warnings 54 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 55 | 56 | enforcement_api_key = UMBRELLA_ENFORCEMENT_KEY 57 | 58 | time = datetime.now().isoformat() 59 | 60 | 61 | # URL needed to do POST requests 62 | domain_url = "https://s-platform.api.opendns.com/1.0/domains" 63 | 64 | delete_domain = "internetbadguys.com" 65 | 66 | url_delete = domain_url + '?customerKey=' + enforcement_api_key + '&where[name]='+ delete_domain 67 | 68 | req = requests.delete(url_delete) 69 | 70 | # error handling if true then the request was HTTP 204, so successful 71 | if(req.status_code == 204): 72 | print("SUCCESS: the following domain is deleted from your current custom Block List: %(domain)s" %{'domain': delete_domain}) 73 | else: 74 | print("An error has ocurred with the following code %(error)s, please consult the following link: https://enforcement-api.readme.io/" % {'error': req.status_code}) 75 | -------------------------------------------------------------------------------- /intro-umbrella/EnforcementGetRequest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | 5 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from datetime import datetime 27 | import requests 28 | import configparser 29 | import json 30 | import sys 31 | from pathlib import Path 32 | from crayons import blue, green, red 33 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 34 | 35 | 36 | # Locate the directory containing this file and the repository root. 37 | # Temporarily add these directories to the system path so that we can import 38 | # local files. 39 | here = Path(__file__).parent.absolute() 40 | repository_root = (here / "..").resolve() 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | sys.path.insert(0, str(repository_root)) 45 | 46 | from env_lab import UMBRELLA # noqa 47 | from env_user import UMBRELLA_ENFORCEMENT_KEY 48 | 49 | # Disable insecure request warnings 50 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 51 | 52 | enforcement_api_key = UMBRELLA_ENFORCEMENT_KEY 53 | 54 | time = datetime.now().isoformat() 55 | 56 | 57 | # URL needed to do POST requests 58 | domain_url = "https://s-platform.api.opendns.com/1.0/domains" 59 | 60 | # URL needed for POST request 61 | url_get = domain_url + '?customerKey=' + enforcement_api_key 62 | 63 | # create empty list to contain all domains already in Umbrella 64 | domain_list = [] 65 | 66 | # keep doing GET requests, until looped through all domains 67 | while True: 68 | req = requests.get(url_get) 69 | json_file = req.json() 70 | for row in json_file["data"]: 71 | domain_list.append(row["name"]) 72 | # GET requests will only list 200 domains, if more than that, it will request next bulk of 200 domains 73 | if bool(json_file["meta"]["next"]): 74 | Url = json_file["meta"]["next"] 75 | # break out of loop when finished 76 | else: 77 | break 78 | 79 | # error handling if true then the request was HTTP 200, so successful 80 | if(req.status_code == 200): 81 | print("SUCCESS: the following domain(s) are in your current custom Block List:") 82 | print(domain_list) 83 | else: 84 | print("An error has ocurred with the following code %(error)s, please consult the following link: https://enforcement-api.readme.io/" % {'error': req.status_code}) -------------------------------------------------------------------------------- /intro-umbrella/EnforcementPostRequest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | 4 | 5 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | from datetime import datetime 27 | import requests 28 | import socket 29 | import configparser 30 | import json 31 | import sys 32 | from pathlib import Path 33 | import webexteamssdk 34 | from crayons import blue, green, red 35 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 36 | 37 | 38 | # Locate the directory containing this file and the repository root. 39 | # Temporarily add these directories to the system path so that we can import 40 | # local files. 41 | here = Path(__file__).parent.absolute() 42 | repository_root = (here / "..").resolve() 43 | 44 | sys.path.insert(0, str(repository_root)) 45 | 46 | sys.path.insert(0, str(repository_root)) 47 | 48 | from env_lab import UMBRELLA # noqa 49 | from env_user import UMBRELLA_ENFORCEMENT_KEY 50 | from env_user import UMBRELLA_INVESTIGATE_KEY # noqa 51 | from env_user import WEBEX_TEAMS_ACCESS_TOKEN 52 | from env_user import WEBEX_TEAMS_ROOM_ID 53 | # Disable insecure request warnings 54 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 55 | 56 | enforcement_api_key = UMBRELLA_ENFORCEMENT_KEY 57 | 58 | time = datetime.now().isoformat() 59 | 60 | 61 | # URL needed to do POST requests 62 | event_url = "https://s-platform.api.opendns.com/1.0/events" 63 | 64 | # time for AlertTime and EventTime when domains are added to Umbrella 65 | time = datetime.now().isoformat() 66 | 67 | # domain that will be uploaded 68 | domain = "internetbadguys.com" 69 | 70 | # URL needed for POST request 71 | url_post = event_url+'?customerKey='+ enforcement_api_key 72 | 73 | # NOTE: Although this information MUST be provided when using the API, not all of it is utilized in the destination lists within Umbrella 74 | data = { 75 | "alertTime": time + "Z", 76 | "deviceId": "ba6a59f4-e692-4724-ba36-c28132c761de", 77 | "deviceVersion": "13.7a", 78 | "dstDomain": domain, 79 | "dstUrl": "http://" + domain + "/", 80 | "eventTime": time + "Z", 81 | "protocolVersion": "1.0a", 82 | "providerName": "Security Platform" 83 | } 84 | 85 | # POST REQUEST: post request ensembly 86 | req = requests.post(url_post, data=json.dumps(data), headers={'Content-type': 'application/json', 'Accept': 'application/json'}) 87 | 88 | # error handling if true then the request was HTTP 202, so successful 89 | if(req.status_code == 202): 90 | print("SUCCESS: domain (%(domain)s) was accepted, HTTP response: 202, timestamp: %(time)s" % {'domain': domain, 'time': time}) 91 | else: 92 | print("An error has ocurred with the following code %(error)s, please consult the following link: https://enforcement-api.readme.io/" % {'error': req.status_code}) 93 | -------------------------------------------------------------------------------- /intro-umbrella/InvestigateGetRequest.py: -------------------------------------------------------------------------------- 1 | ##!/usr/bin/env python 2 | """ 3 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | """ 23 | from datetime import datetime 24 | import requests 25 | import configparser 26 | import json 27 | import sys 28 | from pathlib import Path 29 | 30 | from crayons import blue, green, red 31 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 32 | 33 | 34 | # Locate the directory containing this file and the repository root. 35 | # Temporarily add these directories to the system path so that we can import 36 | # local files. 37 | here = Path(__file__).parent.absolute() 38 | repository_root = (here / ".." ).resolve() 39 | 40 | sys.path.insert(0, str(repository_root)) 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | from env_lab import UMBRELLA # noqa 45 | from env_user import UMBRELLA_INVESTIGATE_KEY # noqa 46 | # Disable insecure request warnings 47 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 48 | 49 | 50 | # SOLUTION SECTION #2 POST REQUEST LAB 5-HandsOn-Investigate-API-Hunting 51 | 52 | # import necessary libraries / modules 53 | import requests 54 | import json 55 | from datetime import datetime 56 | 57 | # API key from evn_user.py 58 | investigate_api_key = UMBRELLA_INVESTIGATE_KEY 59 | 60 | # URL needed for the domain status and category 61 | # investigate_url = "https://investigate.api.umbrella.com/domains/categorization/" 62 | inv_u = UMBRELLA.get("inv_url") 63 | investigate_url = f"{inv_u}/domains/categorization/" 64 | 65 | 66 | # domain that will be checked 67 | domain = "internetbadguys.com" 68 | 69 | #create header for authentication 70 | headers = { 71 | 'Authorization': 'Bearer ' + investigate_api_key 72 | } 73 | 74 | # assemble the URI, show labels give readable output 75 | get_url = investigate_url + domain + "?showLabels" 76 | print(get_url) 77 | # do GET request for the domain status and category 78 | req = requests.get(get_url, headers=headers) 79 | 80 | # time for timestamp of verdict domain 81 | time = datetime.now().isoformat() 82 | 83 | # error handling if true then the request was HTTP 200, so successful 84 | if(req.status_code == 200): 85 | # retrieve status for domain 86 | output = req.json() 87 | domain_output = output[domain] 88 | domain_status = domain_output["status"] 89 | # walk through different options of status 90 | if(domain_status == -1): 91 | print("SUCCESS: The domain %(domain)s is found MALICIOUS at %(time)s" % {'domain': domain, 'time': time}) 92 | elif(domain_status == 1): 93 | print("SUCCESS: The domain %(domain)s is found CLEAN at %(time)s" % {'domain': domain, 'time': time}) 94 | else: 95 | print("SUCCESS: The domain %(domain)s is found UNDEFINED / RISKY at %(time)s" % {'domain': domain, 'time': time}) 96 | else: 97 | print("An error has ocurred with the following code %(error)s, please consult the following link: https://docs.umbrella.com/investigate-api/" % {'error': req.status_code}) 98 | -------------------------------------------------------------------------------- /intro-umbrella/InvestigateGetRequestThreatGrid.py: -------------------------------------------------------------------------------- 1 | ##!/usr/bin/env python 2 | """ 3 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | """ 23 | from datetime import datetime 24 | import requests 25 | import socket 26 | import configparser 27 | import json 28 | import sys 29 | from pathlib import Path 30 | 31 | from crayons import blue, green, red 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / ".." ).resolve() 40 | 41 | sys.path.insert(0, str(repository_root)) 42 | 43 | sys.path.insert(0, str(repository_root)) 44 | 45 | from env_lab import UMBRELLA # noqa 46 | from env_user import UMBRELLA_INVESTIGATE_KEY # noqa 47 | # Disable insecure request warnings 48 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 49 | 50 | 51 | # SOLUTION SECTION #2 POST REQUEST LAB 5-HandsOn-Investigate-API-Hunting 52 | 53 | # import necessary libraries / modules 54 | import requests 55 | import json 56 | from datetime import datetime 57 | 58 | investigate_api_key = UMBRELLA_INVESTIGATE_KEY 59 | 60 | # URL needed for the domain status and category 61 | investigate_url = "https://investigate.api.umbrella.com/samples/" 62 | 63 | # domain that will be checked 64 | domain = "internetbadguys.com" 65 | 66 | #create header for authentication and set limit of sample return to 1 67 | headers = { 68 | 'Authorization': 'Bearer ' + investigate_api_key, 69 | 'limit': '1' 70 | } 71 | 72 | # assemble the URI, show labels give readable output 73 | get_url = investigate_url + domain 74 | 75 | # do GET request for the domain status and category 76 | req = requests.get(get_url, headers=headers) 77 | 78 | # time for timestamp of verdict domain 79 | time = datetime.now().isoformat() 80 | 81 | # error handling if true then the request was HTTP 200, so successful 82 | if(req.status_code == 200): 83 | # store json output in variable 84 | output = req.json() 85 | # check if there are associated samples for domain 86 | if(output["samples"] == []): 87 | print("No associated samples for %(domain)s at %(time)s" % {'domain': domain, 'time': time}) 88 | else: 89 | # go through json and store hash and score in variable 90 | sample = output["samples"][0] 91 | hash_sample = sample["sha256"] 92 | score_sample = sample["threatScore"] 93 | 94 | # print hash and score of domain 95 | print("SUCCESS: The domain %(domain)s has an associated sample with Hash: %(hash)s and Threat Score: %(score)i at %(time)s" % {'domain': domain, 'hash': hash_sample, 'score': score_sample, 'time': time}) 96 | else: 97 | # error handling 98 | print("An error has ocurred with the following code %(error)s, please consult the following link: https://docs.umbrella.com/investigate-api/" % {'error': req.status_code}) 99 | -------------------------------------------------------------------------------- /intro-umbrella/InvestigatePostRequest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | """ 23 | from datetime import datetime 24 | import requests 25 | import socket 26 | import configparser 27 | import json 28 | import sys 29 | from pathlib import Path 30 | 31 | from crayons import blue, green, red 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / ".." ).resolve() 40 | 41 | sys.path.insert(0, str(repository_root)) 42 | 43 | sys.path.insert(0, str(repository_root)) 44 | 45 | from env_lab import UMBRELLA # noqa 46 | from env_user import UMBRELLA_INVESTIGATE_KEY # noqa 47 | # Disable insecure request warnings 48 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 49 | 50 | 51 | # SOLUTION SECTION #2 POST REQUEST LAB 5-HandsOn-Investigate-API-Hunting 52 | 53 | # import necessary libraries / modules 54 | import requests 55 | import json 56 | from datetime import datetime 57 | 58 | # API key from evn_user.py 59 | investigate_api_key = UMBRELLA_INVESTIGATE_KEY 60 | 61 | # URL needed for the domain status and category 62 | # investigate_url = "https://investigate.api.umbrella.com/domains/categorization/" 63 | inv_u = UMBRELLA.get("inv_url") 64 | investigate_url = f"{inv_u}/domains/categorization/" 65 | 66 | # domains that will be checked 67 | 68 | 69 | # put in right format to pass as argument in POST request 70 | 71 | 72 | #create header for authentication 73 | headers = { 74 | 'Authorization': 'Bearer ' + investigate_api_key 75 | } 76 | 77 | def get_umbrella_dispos(domains): 78 | # put in right format to pass as argument in POST request 79 | values = str(json.dumps(domains)) 80 | req = requests.post(investigate_url, data=values, headers=headers) 81 | # time for timestamp of verdict domain 82 | time = datetime.now().isoformat() 83 | # error handling if true then the request was HTTP 200, so successful 84 | if(req.status_code == 200): 85 | print("SUCCESS: request has the following code: 200\n") 86 | output = req.json() 87 | for domain in domains: 88 | domain_output = output[domain] 89 | domain_status = domain_output["status"] 90 | if(domain_status == -1): 91 | print("The domain %(domain)s is found MALICIOUS at %(time)s\n" % {'domain': domain, 'time': time}) 92 | elif(domain_status == 1): 93 | print("The domain %(domain)s is found CLEAN at %(time)s\n" % 94 | {'domain': domain, 'time': time}) 95 | else: 96 | print("The domain %(domain)s is found UNDEFINED / RISKY at %(time)s\n" % 97 | {'domain': domain, 'time': time}) 98 | else: 99 | print("An error has ocurred with the following code %(error)s, please consult the following link: https://docs.umbrella.com/investigate-api/" % 100 | {'error': req.status_code}) 101 | 102 | if __name__ == "__main__": 103 | #TODO Call the function to get dispostion on these domains 104 | domains = ["internetbadguys.com", "cnn.com", "cisco.com"] 105 | get_umbrella_dispos(domains) 106 | -------------------------------------------------------------------------------- /intro-umbrella/mission/umbrellamission.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Mission - Cisco Umbrella 3 | 4 | This is your research step in the Zero-day Workflow. 5 | 6 | 7 | Copyright (c) 2018-2020 Cisco and/or its affiliates. 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | """ 27 | 28 | from datetime import datetime 29 | import requests 30 | import socket 31 | import configparser 32 | import json 33 | import sys 34 | from pathlib import Path 35 | import webexteamssdk 36 | from crayons import blue, green, red 37 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 38 | 39 | 40 | # Locate the directory containing this file and the repository root. 41 | # Temporarily add these directories to the system path so that we can import 42 | # local files. 43 | here = Path(__file__).parent.absolute() 44 | repository_root = (here / ".." / "..").resolve() 45 | 46 | sys.path.insert(0, str(repository_root)) 47 | 48 | import env_lab 49 | from env_lab import UMBRELLA # noqa 50 | from env_user import UMBRELLA_ENFORCEMENT_KEY 51 | from env_user import UMBRELLA_INVESTIGATE_KEY # noqa 52 | from env_user import WEBEX_TEAMS_ACCESS_TOKEN 53 | from env_user import WEBEX_TEAMS_ROOM_ID 54 | # Disable insecure request warnings 55 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 56 | 57 | 58 | 59 | def is_valid_ipv4_address(address): 60 | try: 61 | socket.inet_pton(socket.AF_INET, address) 62 | except AttributeError: # no inet_pton here, sorry 63 | try: 64 | #print(address) 65 | socket.inet_aton(address) 66 | except socket.error: 67 | return False 68 | return address.count('.') == 3 69 | except socket.error: # not a valid address 70 | return False 71 | 72 | return True 73 | 74 | # Read the config file to get settings 75 | 76 | enforcement_api_key = UMBRELLA_ENFORCEMENT_KEY 77 | 78 | time = datetime.now().isoformat() 79 | 80 | investigate_api_key = UMBRELLA_INVESTIGATE_KEY 81 | 82 | 83 | 84 | 85 | # URL needed to do POST requests 86 | event_url = UMBRELLA.get("en_url") 87 | 88 | # URL needed for POST request 89 | url_post = event_url + '?customerKey=' + enforcement_api_key 90 | 91 | inv_u = UMBRELLA.get("inv_url") 92 | 93 | #TODO: finish the URL to get the Status and Category of a domain! 94 | env_lab.print_missing_mission_warn(env_lab.get_line()) 95 | investigate_url = MISSION 96 | 97 | #create header for authentication and set limit of sample return to 1 98 | headers = { 99 | 'Authorization': 'Bearer ' + investigate_api_key, 100 | 'limit': '1' 101 | } 102 | #print(url_post) 103 | 104 | def get_DomainStatus(getUrl, domain): 105 | #print(getUrl) 106 | req = requests.get(getUrl, headers=headers) 107 | if(req.status_code == 200): 108 | output = req.json() 109 | domainOutput = output[domain] 110 | domainStatus = domainOutput["status"] 111 | if(domainStatus == -1): 112 | print("SUCCESS: The domain %(domain)s is found MALICIOUS at %(time)s" % 113 | {'domain': domain, 'time': time}) 114 | return "bad" 115 | elif(domainStatus == 1): 116 | #print("SUCCESS: The domain %(domain)s is found CLEAN at %(time)s" % 117 | #{'domain': domain, 'time': time}) 118 | return "clean" 119 | #TODO: check if else the domain status is risky 120 | elif(MISSION): 121 | print(MISSION) 122 | return "MISSION" 123 | else: 124 | print("An error has ocurred with the following code %(error)s, please consult the following link: https://docs.umbrella.com/investigate-api/" % 125 | {'error': req.status_code}) 126 | return "error" 127 | 128 | def readIocsFile(filename): 129 | with open (filename, 'r') as fp: 130 | shalist = json.loads(fp.read()) 131 | return shalist 132 | 133 | def write_risky_domains_for_firewall(filenamed, domainlist): 134 | with open(filenamed, "w") as file: 135 | json.dump(domainlist, file, indent=2) 136 | 137 | def removeDups(list): 138 | domain_list_r = [] 139 | domin_filter_ip = [] 140 | domain_final = [] 141 | for i in list: 142 | if i not in domain_list_r: 143 | domain_list_r.append(i) 144 | domain_filter_ip = domain_list_r 145 | print("We found dulicates and pruned the list :\n") 146 | return domain_filter_ip 147 | 148 | def handleDomains(filename): 149 | try: 150 | domain_list = readIocsFile(filename) 151 | time = datetime.now().isoformat() 152 | domain_list_f = [] 153 | 154 | #TODO: call the correct function to remove duplicate domains from the domain list 155 | domain_list = MISSION 156 | env_lab.print_missing_mission_warn(env_lab.get_line()) 157 | #TODO: loop through every domain in the domain list HINT: for ... in ...: 158 | for MISSION in MISSION: 159 | print(f"Working on {MISSION} .....") 160 | get_url = investigate_url + MISSION + "?showLabels" 161 | status = get_DomainStatus(get_url, MISSION) 162 | if(status != "error"): 163 | if ((status == "bad") or (status == "risky")): 164 | post_Enforcement(MISSION) 165 | domain_list_f.append(MISSION) 166 | else: 167 | print(f"Found clean domain, ignoring enforcement on {MISSION}") 168 | else: 169 | print("got error from Umbrella investigate") 170 | #Let's save another file with Umbrella Disposition on Domains 171 | # so that we block only bad & risky domains on firewalls 172 | filenamed = repository_root / "mission-data/riskydomains.json" 173 | write_risky_domains_for_firewall(filenamed, domain_list_f) 174 | except KeyboardInterrupt: 175 | print("\nExiting...\n") 176 | 177 | def post_Enforcement(domdata): 178 | data={ 179 | "alertTime": time + "Z", 180 | "deviceId": "ba6a59f4-e692-4724-ba36-c28132c761de", 181 | "deviceVersion": "13.7a", 182 | "dstDomain": domdata, 183 | "dstUrl": "http://" + domdata + "/", 184 | "eventTime": time + "Z", 185 | "protocolVersion": "1.0a", 186 | "providerName": "Security Platform" 187 | } 188 | request_post = requests.post(url_post, data=json.dumps(data), headers={ 189 | 'Content-type': 'application/json', 'Accept': 'application/json'}) 190 | if(request_post.status_code == 202): 191 | print("\n") 192 | print(f"SUCCESS: {domdata} BLOCKED!!") 193 | print("\n") 194 | # error handling 195 | else: 196 | print("An error has ocurred with the following code %(error)s, please consult the following link: https://docs.umbrella.com/investigate-api/" % 197 | {'error': request_post.status_code}) 198 | 199 | 200 | if __name__ == "__main__": 201 | # Save the MAC addresses of the endpoints where malware executed to a JSON 202 | # file. In the ISE Mission we will read this file and quarantine these 203 | # endpoints.sha256-list.json 204 | domainlist_path = repository_root / "mission-data/domainlist.json" 205 | 206 | # TODO: Mission call the function to handle domains 207 | MISSION(domainlist_path) 208 | 209 | #TODO: initialize the teams object with the webexteamssdk using your access token and the ROOM_ID 210 | 211 | teams.MISSION( 212 | roomId=MISSION 213 | markdown=f"**Umbrella Mission completed!!!** \n\n" 214 | f"I checked the status of the domains generated by ThreatGrid using Umbrella Investigate! \n\n" 215 | f"I also blocked DNS lookup using Umbrella Enforcement API for Malicious and Risky Domains" 216 | 217 | ) 218 | 219 | print(green("Umbrella Mission Completed!!!")) 220 | -------------------------------------------------------------------------------- /mission-data/078a122a9401dd47a61369ac769d9e707d9e86bdf7ad91708510b9a4584e8d49.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /mission-data/1eb15091d4605809a0a78e9c150e764c9253f9249a7babe4484c27d822d59900.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /mission-data/3372c1edab46837f1e973164fa2d726c5c5e17bcb888828ccd7c4dfcc234a370.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Process Attempts to Terminate Another Process", 3 | "Artifact Flagged by Antivirus Has Assigned CVE Number", 4 | "Cisco Umbrella Categorized Domain As A Parked Domain", 5 | "Artifact With Virtual Environment Enumeration Detected", 6 | "Process Modified a File in the Program Files Directory", 7 | "Ransomware Queried Domain", 8 | "PDF Contains \"URI\" Action", 9 | "Artifact Packed with RAR", 10 | "Alternate Data Stream File Creation Detected", 11 | "Sample flagged by antivirus service contacted domain", 12 | "Command Exe File Deletion Detected", 13 | "Executable Signed With Stolen Digital Certificate", 14 | "JigSaw Ransomware Detected", 15 | "VBA Macro Reads Environment Variables", 16 | "Shamoon Malware Detected", 17 | "JavaScript Obfuscation Using \"eval()\" Function", 18 | "Executable Packed with UPX", 19 | "PE Contains Section with Blank or No Name", 20 | "Artifact Flagged as Known Adware by Antivirus", 21 | "Dynamic DNS Domain Detected", 22 | "A Possible Phishing HTML Page Was Found", 23 | "Possible Double Flux Nameserver Detected [Beta]", 24 | "Pending File Deletions", 25 | "Submitted Sample Alteration and Umbrella Flagged Domain", 26 | "Upatre Detected", 27 | "Process Modified an Executable File", 28 | "Sample Launched Copy Of Itself", 29 | "Wanacryptor Ransomware Detected", 30 | "Process Modified File in a User Directory", 31 | "Office Document Contains a VBA Macro", 32 | "JAR Contains File with Executable Extension", 33 | "Static Analysis Flagged Artifact As Anti-Analysis", 34 | "Outbound HTTP GET Request From URL Submission", 35 | "RAT Queried Domain", 36 | "VBA Macro May Call Shell", 37 | "Sample With Known Stolen Certificates Queried Domain", 38 | "Nullsoft Installer Detected", 39 | "Executable Has Embedded Executable in Resources", 40 | "CDF File Used as an Archive", 41 | "Ransomware UIWIX Detected", 42 | "Static Analysis Flagged Artifact As Sandbox Aware", 43 | "JavaScript Calls ActiveXObject", 44 | "JAR Contains a Windows Executable File", 45 | "Static Analysis Flagged Artifact As Malicious", 46 | "Command Exe File Execution Detected", 47 | "Ransomware Backup Deletion Detected", 48 | "Office Document Requires Password", 49 | "Executable Artifact Imports Tool Help Functions", 50 | "Static Analysis Flagged Artifact As Anomalous", 51 | "CVE-2017-0199 RTF Vulnerability Detected", 52 | "PE Header Overlaps the DOS Header", 53 | "PDF Document Requires Password", 54 | "Banking Trojan Queried Domain", 55 | "Signed Artifact Flagged as Known Trojan by Antivirus", 56 | "Artifact Flagged Malicious by Antivirus Service", 57 | "PE Resource Indicates Chinese Origin", 58 | "Process Created an Executable in a User Directory", 59 | "Registry Persistence Mechanism Refers to an Executable in a User Data Directory", 60 | "Static Analysis Flagged Artifact As VM Aware", 61 | "DNS Response Contains Low Time to Live (TTL) Value", 62 | "File Downloaded to Disk", 63 | "Hook Procedure Detected in Executable", 64 | "RTF File Contains a Large Amount of Appended Data", 65 | "Executable Artifact Imports An EXE file as a DLL", 66 | "VBA Macro Contains URL", 67 | "Process Modified a File in a System Directory", 68 | "JavaScript Obfuscation Using \"fromCharCode()\" Function", 69 | "VBA Macro Imports Function From External Library", 70 | "Artifact Flagged by Antivirus", 71 | "Process Deleted the Submitted File", 72 | "Office Document Contains ActiveX Controls", 73 | "Domain in Cisco Umbrella Block List", 74 | "Potential TOR Connection", 75 | "Sample Created A Batch File", 76 | "Artifact Flagged as Known Trojan by Antivirus", 77 | "Process Registered File as a File Handler", 78 | "Executable Artifact Uses .NET", 79 | "Executable Has Embedded Document in Resources", 80 | "File Name of Executable on Disk Does Not Match Original File Name", 81 | "Suspicious Launch of explorer.exe Detected", 82 | "Executable Artifact Imports Process Status DLL", 83 | "Process Modified Desktop Wallpaper", 84 | "JAR Uses Crypto Package", 85 | "VBA Macro References Base64", 86 | "Shadow Copy Deletion Detected", 87 | "Service DLL Detected", 88 | "COM Object Detected", 89 | "Process Read INI File", 90 | "Document Flagged by Antivirus", 91 | "Spectre Vulnerability Detected", 92 | "Generic Ransomware Detected", 93 | "Possible ZeuS Variant Detected by Antivirus", 94 | "Decoy Document Detected", 95 | "DNS Query Returned Non-Existent Domain", 96 | "Network Stream Marked by Snort as Protocol Vulnerability", 97 | "Executable Packed with ASProtect", 98 | "Excessive File Modification by Process", 99 | "Static Analysis Flagged Artifact As Potentially Obfuscated", 100 | "Process Uses Very Large Command-Line", 101 | "Processes Have A Circular Parent-Child Relationship", 102 | "Executable Signed With Digital Certificate", 103 | "Unsigned File In Roaming Directory Executed", 104 | "PE Resource Indicates Russian Origin", 105 | "Process Modified Autorun Registry Key Value", 106 | "Sample Contacts Only Benign Domains", 107 | "Process Created a File in a Recycle Bin Folder", 108 | "VBA Macro Has Action on Open", 109 | "Script Contains URL", 110 | "Executable with Encrypted Sections", 111 | "PE Resource Indicates Arabic Origin", 112 | "JAR Uses Reflection Package", 113 | "Cisco Umbrella Flagged Domain As A Proxy Or Anonymizer", 114 | "PE Resource Indicates Romanian Origin", 115 | "Process Modified INI File", 116 | "PE Resource Indicates Korean Origin", 117 | "Large Amount of High Entropy Artifacts Written", 118 | "Javascript Contains an Excessively Long String", 119 | "Executable Uses Armadillo", 120 | "Submitted URL Downloaded a Malicious File", 121 | "PDF Contains URIs", 122 | "Potential Code Injection Detected", 123 | "Executable Imported the IsDebuggerPresent Symbol", 124 | "A PE Artifact has an Invalid Certificate Signature", 125 | "Submitted Sample Launched Copy and Contacted Flagged Domain" 126 | ] -------------------------------------------------------------------------------- /mission-data/5a0d64cc41bb8455f38b4b31c6e69af9e7fd022b0ea9ea0c32c371def24d67fb.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Process Attempts to Terminate Another Process", 3 | "Artifact Flagged by Antivirus Has Assigned CVE Number", 4 | "Cisco Umbrella Categorized Domain As A Parked Domain", 5 | "Artifact With Virtual Environment Enumeration Detected", 6 | "PDF Contains \"URI\" Action", 7 | "Artifact Packed with RAR", 8 | "Ransomware Queried Domain", 9 | "Alternate Data Stream File Creation Detected", 10 | "Sample flagged by antivirus service contacted domain", 11 | "Executable Signed With Stolen Digital Certificate", 12 | "JigSaw Ransomware Detected", 13 | "VBA Macro Reads Environment Variables", 14 | "Shamoon Malware Detected", 15 | "JavaScript Obfuscation Using \"eval()\" Function", 16 | "Executable Packed with UPX", 17 | "PE Contains Section with Blank or No Name", 18 | "Artifact Flagged as Known Adware by Antivirus", 19 | "A Possible Phishing HTML Page Was Found", 20 | "Pending File Deletions", 21 | "Upatre Detected", 22 | "Process Modified an Executable File", 23 | "Wanacryptor Ransomware Detected", 24 | "Office Document Contains a VBA Macro", 25 | "JAR Contains File with Executable Extension", 26 | "Process Modified File in a User Directory", 27 | "Static Analysis Flagged Artifact As Anti-Analysis", 28 | "Outbound HTTP GET Request From URL Submission", 29 | "RAT Queried Domain", 30 | "VBA Macro May Call Shell", 31 | "Sample With Known Stolen Certificates Queried Domain", 32 | "Nullsoft Installer Detected", 33 | "Executable Has Embedded Executable in Resources", 34 | "CDF File Used as an Archive", 35 | "Ransomware UIWIX Detected", 36 | "Static Analysis Flagged Artifact As Sandbox Aware", 37 | "JavaScript Calls ActiveXObject", 38 | "JAR Contains a Windows Executable File", 39 | "Static Analysis Flagged Artifact As Malicious", 40 | "Office Document Requires Password", 41 | "Executable Artifact Imports Tool Help Functions", 42 | "Static Analysis Flagged Artifact As Anomalous", 43 | "CVE-2017-0199 RTF Vulnerability Detected", 44 | "PE Header Overlaps the DOS Header", 45 | "PDF Document Requires Password", 46 | "Banking Trojan Queried Domain", 47 | "Signed Artifact Flagged as Known Trojan by Antivirus", 48 | "Artifact Flagged Malicious by Antivirus Service", 49 | "PE Resource Indicates Chinese Origin", 50 | "Process Created an Executable in a User Directory", 51 | "Static Analysis Flagged Artifact As VM Aware", 52 | "Registry Persistence Mechanism Refers to an Executable in a User Data Directory", 53 | "DNS Response Contains Low Time to Live (TTL) Value", 54 | "File Downloaded to Disk", 55 | "Hook Procedure Detected in Executable", 56 | "RTF File Contains a Large Amount of Appended Data", 57 | "Executable Artifact Imports An EXE file as a DLL", 58 | "VBA Macro Contains URL", 59 | "JavaScript Obfuscation Using \"fromCharCode()\" Function", 60 | "VBA Macro Imports Function From External Library", 61 | "Artifact Flagged by Antivirus", 62 | "Office Document Contains ActiveX Controls", 63 | "Domain in Cisco Umbrella Block List", 64 | "Sample Created A Batch File", 65 | "Artifact Flagged as Known Trojan by Antivirus", 66 | "Process Registered File as a File Handler", 67 | "Executable Artifact Uses .NET", 68 | "Executable Has Embedded Document in Resources", 69 | "File Name of Executable on Disk Does Not Match Original File Name", 70 | "Suspicious Launch of explorer.exe Detected", 71 | "Executable Artifact Imports Process Status DLL", 72 | "JAR Uses Crypto Package", 73 | "VBA Macro References Base64", 74 | "Service DLL Detected", 75 | "COM Object Detected", 76 | "Process Read INI File", 77 | "Document Flagged by Antivirus", 78 | "Spectre Vulnerability Detected", 79 | "Possible ZeuS Variant Detected by Antivirus", 80 | "DNS Query Returned Non-Existent Domain", 81 | "Executable Packed with ASProtect", 82 | "Static Analysis Flagged Artifact As Potentially Obfuscated", 83 | "Process Uses Very Large Command-Line", 84 | "Processes Have A Circular Parent-Child Relationship", 85 | "Executable Signed With Digital Certificate", 86 | "Unsigned File In Roaming Directory Executed", 87 | "PE Resource Indicates Russian Origin", 88 | "Process Modified Autorun Registry Key Value", 89 | "VBA Macro Has Action on Open", 90 | "Script Contains URL", 91 | "Executable with Encrypted Sections", 92 | "PE Resource Indicates Arabic Origin", 93 | "JAR Uses Reflection Package", 94 | "PE Resource Indicates Romanian Origin", 95 | "Process Modified INI File", 96 | "PE Resource Indicates Korean Origin", 97 | "Javascript Contains an Excessively Long String", 98 | "Executable Uses Armadillo", 99 | "Submitted URL Downloaded a Malicious File", 100 | "PDF Contains URIs", 101 | "Potential Code Injection Detected", 102 | "Executable Imported the IsDebuggerPresent Symbol", 103 | "A PE Artifact has an Invalid Certificate Signature" 104 | ] -------------------------------------------------------------------------------- /mission-data/7c9d5724064693dfeef76fd4da8d6f159ef0e6707e67c4a692a03e94f4a6e27a.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Remote IP Address Contacted", 3 | "PE COFF File Header Had Unexpected NumberOfSymbols", 4 | "HTTP Client Error Response", 5 | "Artifact Flagged by Antivirus", 6 | "Outbound HTTP GET Request", 7 | "PE Resource Indicates Russian Origin", 8 | "Artifact Flagged as Known Trojan by Antivirus", 9 | "Process Opens a Listening Port", 10 | "Script Contains URL", 11 | "Executable with Encrypted Sections", 12 | "PE Has Sections Marked Executable and Writable", 13 | "PE COFF Header Timestamp is Set to Date in the Future", 14 | "Process Modified INI File", 15 | "HTTP Redirection Response", 16 | "Javascript Contains an Excessively Long String", 17 | "URL Resulted in 404 or Empty File", 18 | "PE Checksum is Invalid", 19 | "Artifact Flagged Malicious by Antivirus Service", 20 | "Process Read INI File", 21 | "Process Modified File in a User Directory", 22 | "Potential Code Injection Detected", 23 | "DNS Response Contains Low Time to Live (TTL) Value", 24 | "Process Uses Localhost for Network Traffic" 25 | ] -------------------------------------------------------------------------------- /mission-data/7e54dceecd3d3a23a896e971ae4bb9e71a64a5c1c3b77ac1c64241c55c1b95bb.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /mission-data/7fd72a36f7e0e6e0a8bc777fc9ed41e0a6d5526c98bc95a09e189531cf7e70d5.json: -------------------------------------------------------------------------------- 1 | [ 2 | "RAT Queried Domain", 3 | "Process Attempts to Terminate Another Process", 4 | "JavaScript Obfuscation Using \"fromCharCode()\" Function", 5 | "Cozycar Detected", 6 | "Outbound HTTP GET Request From URL Submission", 7 | "PDF Contains \"URI\" Action", 8 | "Artifact Packed with RAR", 9 | "Artifact Flagged by Antivirus", 10 | "Executable Artifact has Misleading File Extension", 11 | "Static Analysis Flagged Artifact As Potentially Obfuscated", 12 | "Executable Signed With Digital Certificate", 13 | "Domain in Cisco Umbrella Block List", 14 | "Unsigned File In Roaming Directory Executed", 15 | "Sample flagged by antivirus service contacted domain", 16 | "Javascript in HTML Uses Location.Replace Function", 17 | "JavaScript Calls ActiveXObject", 18 | "HTML Contains JavaScript Using 'eval()' Function", 19 | "Process Modified Autorun Registry Key Value", 20 | "JavaScript Obfuscation Using \"eval()\" Function", 21 | "Javascript References Executable", 22 | "File Name of Executable on Disk Does Not Match Original File Name", 23 | "Suspicious Launch of explorer.exe Detected", 24 | "Script Contains URL", 25 | "Cisco Umbrella Categorized Domain As File Storage", 26 | "Executable Artifact Imports Process Status DLL", 27 | "Executable with Encrypted Sections", 28 | "Possible Double Flux Nameserver Detected [Beta]", 29 | "A Process Launched Several Children", 30 | "Executable Artifact Imports Tool Help Functions", 31 | "Process Modified an Executable File", 32 | "PE Resource Indicates Spanish Origin", 33 | "Static Analysis Flagged Artifact As Anomalous", 34 | "PE Contains an Invalid Certificate Signature", 35 | "PE Resource Indicates Korean Origin", 36 | "Process Attempts to Forcefully Terminate Another Process", 37 | "Javascript Contains an Excessively Long String", 38 | "PE Checksum is Invalid", 39 | "Sample Launched Copy Of Itself", 40 | "COM Object Detected", 41 | "Signed Artifact Flagged as Known Trojan by Antivirus", 42 | "Artifact Flagged Malicious by Antivirus Service", 43 | "PDF Contains URIs", 44 | "Process Created an Executable in a User Directory", 45 | "Process Modified File in a User Directory", 46 | "Potential Code Injection Detected", 47 | "Decoy Document Detected", 48 | "Registry Persistence Mechanism Refers to an Executable in a User Data Directory", 49 | "Executable Imported the IsDebuggerPresent Symbol", 50 | "DNS Query Returned Non-Existent Domain", 51 | "DNS Response Contains Low Time to Live (TTL) Value", 52 | "A PE Artifact has an Invalid Certificate Signature", 53 | "Process Registered COM Server DLL" 54 | ] -------------------------------------------------------------------------------- /mission-data/87715c2487765488d72919a3720f11806592fe1018aa5c95aaf9fd13fb041f20.json: -------------------------------------------------------------------------------- 1 | [ 2 | "RAT Queried Domain", 3 | "Process Modified a File in a System Directory", 4 | "Nullsoft Installer Detected", 5 | "Process Modified Trusted Root Certificates", 6 | "Process Modified a File in the Program Files Directory", 7 | "Artifact Flagged by Antivirus", 8 | "Excessive Number of DNS Queries", 9 | "Executable Artifact has Misleading File Extension", 10 | "Excessive Remote Process Code Injection Detected", 11 | "Outbound HTTP GET Request", 12 | "Artifact With Antivirus Enumeration Detected", 13 | "Process Uses Very Large Command-Line", 14 | "Protocol Mismatch over Standard DNS Port", 15 | "Executable Signed With Digital Certificate", 16 | "Sample flagged by antivirus service contacted domain", 17 | "Check for GeoIP Location Detected", 18 | "Process Created a File in the Windows Start Menu Folder", 19 | "Hook Procedure Detected in Executable", 20 | "PE Resource Indicates Russian Origin", 21 | "Process Modified Autorun Registry Key Value", 22 | "Possible ZeroAccess Rootkit V2 Variant Detected", 23 | "Artifact Flagged as Known Trojan by Antivirus", 24 | "Anti Virus String Detected in Process Command Line", 25 | "File Name of Executable on Disk Does Not Match Original File Name", 26 | "Executable with Encrypted Sections", 27 | "Executable Artifact Imports Process Status DLL", 28 | "PE Resource Indicates Vietnamese Origin", 29 | "PE Has Sections Marked Executable and Writable", 30 | "Public DNS Server Contacted", 31 | "Possible Double Flux Nameserver Detected [Beta]", 32 | "Very Large Registry Data", 33 | "Executable Artifact Imports Tool Help Functions", 34 | "PE Resource Indicates Romanian Origin", 35 | "Process Modified an Executable File", 36 | "PE Resource Indicates Spanish Origin", 37 | "Process Modified INI File", 38 | "Static Analysis Flagged Artifact As Anomalous", 39 | "PE Resource Indicates Korean Origin", 40 | "Artifact Flagged Malicious by Antivirus Service", 41 | "Process Read INI File", 42 | "Process Added a Service to the ControlSet Registry Key", 43 | "Process Registered COM Server DLL", 44 | "Process Created an Executable in a User Directory", 45 | "Process Modified File in a User Directory", 46 | "Potential Code Injection Detected", 47 | "Decoy Document Detected", 48 | "Static Analysis Flagged Artifact As VM Aware", 49 | "Network Stream Marked as Malware by Snort", 50 | "Executable Imported the IsDebuggerPresent Symbol", 51 | "DNS Response Contains Low Time to Live (TTL) Value", 52 | "Domain Resolves to Localhost", 53 | "File Downloaded to Disk", 54 | "Static Analysis Flagged Artifact As Anti-Analysis", 55 | "Process Registered a Service DLL", 56 | "Excessive UDP Connections Detected" 57 | ] -------------------------------------------------------------------------------- /mission-data/8db0d7f3a27291f197173a1e3a3a7242fc49deb2d06f90598475c919417a1c7a.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /mission-data/README.md: -------------------------------------------------------------------------------- 1 | # This is a working folder for the mission scripts. 2 | 3 | ## All the mission data will be written in this folder by the scripts. 4 | 5 | ### AMP mission will create 2 files. 6 | ### ISE Mission will consume the Mac Address file created by AMP mission 7 | ### ThreatGrid Mission will consume the IOCs (SHAs) file created by the AMP mission 8 | ### ThreatGrid Mission will create 1 file for Umbrella and FDM Missions 9 | ### Umbrella will consume one file. 10 | ### FDM will consume one file. 11 | -------------------------------------------------------------------------------- /mission-data/b75fd580c29736abd11327eef949e449f6d466a05fb6fd343d3957684c8036e5.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /mission-data/domainlist.json: -------------------------------------------------------------------------------- 1 | [ 2 | "7tno4hib47vlep5o.tor2web.org", 3 | "7tno4hib47vlep5o.tor2web.fi", 4 | "7tno4hib47vlep5o.tor2web.org", 5 | "7tno4hib47vlep5o.tor2web.fi", 6 | "7tno4hib47vlep5o.tor2web.org", 7 | "7tno4hib47vlep5o.tor2web.fi", 8 | "github.com", 9 | "github.com", 10 | "time.windows.com", 11 | "www.bing.com", 12 | "www.bing.com", 13 | "codeload.github.com", 14 | "codeload.github.com", 15 | "7tno4hib47vlep5o.tor2web.org", 16 | "7tno4hib47vlep5o.tor2web.fi", 17 | "github.com", 18 | "github.com", 19 | "www.bing.com", 20 | "www.bing.com", 21 | "codeload.github.com", 22 | "codeload.github.com", 23 | "github.com", 24 | "github.com", 25 | "www.bing.com", 26 | "www.bing.com", 27 | "codeload.github.com", 28 | "codeload.github.com", 29 | "github.com", 30 | "github.com", 31 | "www.bing.com", 32 | "codeload.github.com", 33 | "codeload.github.com", 34 | "codeload.github.com", 35 | "codeload.github.com", 36 | "www.bing.com", 37 | "www.bing.com", 38 | "github.com", 39 | "github.com", 40 | "ieonline.microsoft.com", 41 | "ieonline.microsoft.com", 42 | "www.bing.com", 43 | "www.bing.com", 44 | "codeload.github.com", 45 | "codeload.github.com", 46 | "github.com", 47 | "github.com", 48 | "www.bing.com", 49 | "www.bing.com", 50 | "fonts.gstatic.com", 51 | "cfl.dropboxstatic.com", 52 | "cfl.dropboxstatic.com", 53 | "beacon.dropbox.com", 54 | "ieonline.microsoft.com", 55 | "bolt.dropbox.com", 56 | "dropbox.com", 57 | "ucaf095e758f8aa29305914193ff.previews.dropboxusercontent.com", 58 | "dropboxcaptcha.com", 59 | "www.gstatic.com", 60 | "ucaf98418afe971746236f8c91ac.dl.dropboxusercontent.com", 61 | "www.dropbox.com", 62 | "www.google.com", 63 | "beacon.dropbox.com", 64 | "ieonline.microsoft.com", 65 | "dropbox.com", 66 | "ucaf095e758f8aa29305914193ff.previews.dropboxusercontent.com", 67 | "dropboxcaptcha.com", 68 | "ucaf98418afe971746236f8c91ac.dl.dropboxusercontent.com", 69 | "www.dropbox.com", 70 | "www.gstatic.com", 71 | "www.google.com", 72 | "cfl.dropboxstatic.com", 73 | "cfl.dropboxstatic.com", 74 | "www.bing.com", 75 | "www.bing.com", 76 | "fonts.gstatic.com", 77 | "time.windows.com", 78 | "www.bing.com", 79 | "www.bing.com", 80 | "codeload.github.com", 81 | "codeload.github.com", 82 | "github.com", 83 | "github.com", 84 | "github.com", 85 | "github.com", 86 | "www.bing.com", 87 | "www.bing.com", 88 | "codeload.github.com", 89 | "codeload.github.com", 90 | "www.bing.com", 91 | "www.bing.com", 92 | "codeload.github.com", 93 | "codeload.github.com", 94 | "github.com", 95 | "github.com" 96 | ] -------------------------------------------------------------------------------- /mission-data/f52bfac9637aea189ec918d05113c36f5bcf580f3c0de8a934fe3438107d3f0c.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /mission-data/iplist.json: -------------------------------------------------------------------------------- 1 | [ 2 | "185.100.85.150", 3 | "194.150.168.74", 4 | "185.100.85.150", 5 | "194.150.168.74", 6 | "185.100.85.150", 7 | "194.150.168.74", 8 | "192.30.253.113", 9 | "192.30.253.112", 10 | "52.168.138.145", 11 | "204.79.197.200", 12 | "13.107.21.200", 13 | "192.30.253.121", 14 | "192.30.253.120", 15 | "185.100.85.150", 16 | "194.150.168.74", 17 | "192.30.253.113", 18 | "192.30.253.112", 19 | "204.79.197.200", 20 | "13.107.21.200", 21 | "192.30.253.121", 22 | "192.30.253.120", 23 | "192.30.253.112", 24 | "192.30.253.113", 25 | "13.107.21.200", 26 | "204.79.197.200", 27 | "192.30.253.121", 28 | "192.30.253.120", 29 | "192.30.255.112", 30 | "192.30.255.113", 31 | "204.79.197.229", 32 | "192.30.255.120", 33 | "192.30.255.121", 34 | "192.30.253.121", 35 | "192.30.253.120", 36 | "13.107.21.200", 37 | "204.79.197.200", 38 | "192.30.253.112", 39 | "192.30.253.113", 40 | "204.79.197.200", 41 | "204.79.197.200", 42 | "204.79.197.200", 43 | "13.107.21.200", 44 | "192.30.253.121", 45 | "192.30.253.120", 46 | "192.30.253.113", 47 | "192.30.253.112", 48 | "13.107.21.200", 49 | "204.79.197.200", 50 | "172.217.9.227", 51 | "104.16.99.29", 52 | "104.16.100.29", 53 | "162.125.34.129", 54 | "204.79.197.200", 55 | "162.125.18.133", 56 | "162.125.248.1", 57 | "162.125.8.5", 58 | "54.192.55.234", 59 | "172.217.11.35", 60 | "162.125.8.6", 61 | "162.125.8.1", 62 | "172.217.6.196", 63 | "162.125.34.129", 64 | "204.79.197.200", 65 | "162.125.248.1", 66 | "162.125.8.5", 67 | "54.192.55.234", 68 | "162.125.8.6", 69 | "162.125.8.1", 70 | "172.217.11.3", 71 | "172.217.6.196", 72 | "104.16.99.29", 73 | "104.16.100.29", 74 | "13.107.21.200", 75 | "204.79.197.200", 76 | "172.217.10.3", 77 | "52.168.138.145", 78 | "204.79.197.200", 79 | "13.107.21.200", 80 | "192.30.253.121", 81 | "192.30.253.120", 82 | "192.30.253.113", 83 | "192.30.253.112", 84 | "192.30.253.113", 85 | "192.30.253.112", 86 | "204.79.197.200", 87 | "13.107.21.200", 88 | "192.30.253.121", 89 | "192.30.253.120", 90 | "13.107.21.200", 91 | "204.79.197.200", 92 | "192.30.253.121", 93 | "192.30.253.120", 94 | "192.30.253.112", 95 | "192.30.253.113" 96 | ] -------------------------------------------------------------------------------- /mission-data/mac-addresses.json: -------------------------------------------------------------------------------- 1 | [ 2 | "0a:96:ce:46:01:99", 3 | "7b:d0:84:fe:f0:60", 4 | "39:45:52:16:bc:49", 5 | "ed:74:7d:93:d2:e3", 6 | "00:fc:a6:78:15:b1", 7 | "06:36:05:17:e9:e4", 8 | "3a:24:52:cc:9e:fc", 9 | "84:9a:a4:a1:e6:50", 10 | "ff:1b:ad:dc:fb:87", 11 | "43:0e:ba:fc:e8:48", 12 | "c5:71:2f:65:13:fc" 13 | ] -------------------------------------------------------------------------------- /mission-data/riskydomains.json: -------------------------------------------------------------------------------- 1 | [ 2 | "7tno4hib47vlep5o.tor2web.fi", 3 | "cfl.dropboxstatic.com", 4 | "dropboxcaptcha.com" 5 | ] -------------------------------------------------------------------------------- /mission-data/sha256-list.json: -------------------------------------------------------------------------------- 1 | [ 2 | "3372c1edab46837f1e973164fa2d726c5c5e17bcb888828ccd7c4dfcc234a370", 3 | "b75fd580c29736abd11327eef949e449f6d466a05fb6fd343d3957684c8036e5", 4 | "078a122a9401dd47a61369ac769d9e707d9e86bdf7ad91708510b9a4584e8d49", 5 | "7e54dceecd3d3a23a896e971ae4bb9e71a64a5c1c3b77ac1c64241c55c1b95bb", 6 | "87715c2487765488d72919a3720f11806592fe1018aa5c95aaf9fd13fb041f20", 7 | "1eb15091d4605809a0a78e9c150e764c9253f9249a7babe4484c27d822d59900", 8 | "8db0d7f3a27291f197173a1e3a3a7242fc49deb2d06f90598475c919417a1c7a", 9 | "7fd72a36f7e0e6e0a8bc777fc9ed41e0a6d5526c98bc95a09e189531cf7e70d5", 10 | "f52bfac9637aea189ec918d05113c36f5bcf580f3c0de8a934fe3438107d3f0c", 11 | "7c9d5724064693dfeef76fd4da8d6f159ef0e6707e67c4a692a03e94f4a6e27a", 12 | "5a0d64cc41bb8455f38b4b31c6e69af9e7fd022b0ea9ea0c32c371def24d67fb" 13 | ] -------------------------------------------------------------------------------- /my_file.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CiscoDevNet/dne-security-code/9d46b5742b3707aeace1e3e287193749bd200284/my_file.py -------------------------------------------------------------------------------- /postman/umbrella/Umbrella Devnet Express.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "ddbb6b1b-be9c-4fef-9983-d4efa89ae43c", 4 | "name": "Umbrella Devnet Express", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Post Request Enforcement", 10 | "request": { 11 | "auth": { 12 | "type": "noauth" 13 | }, 14 | "method": "POST", 15 | "header": [ 16 | { 17 | "key": "Content-Type", 18 | "value": "application/json" 19 | }, 20 | { 21 | "key": "Accept", 22 | "value": "application/json" 23 | } 24 | ], 25 | "body": { 26 | "mode": "raw", 27 | "raw": " [{\n\n \"alertTime\": \"2018-07-17T16:27:12.152962Z\",\n\n \"deviceId\": \"ba6a59f4-e692-4724-ba36-c28132c761de\",\n\n \"deviceVersion\": \"13.7a\",\n\n \"dstDomain\": \"internetbadguys.com\",\n\n \"dstUrl\": \"http://internetbadguys.com/a-bad-url\",\n\n \"eventTime\": \"2018-07-17T16:27:12.152962Z\",\n\n \"protocolVersion\": \"1.0a\",\n\n \"providerName\": \"Security Platform\"\n\n },{\n\n \"alertTime\": \"2018-07-17T16:27:12.152962Z\",\n\n \"deviceId\": \"ba6a59f4-e692-4724-ba36-c28132c761de\",\n\n \"deviceVersion\": \"13.7a\",\n\n \"dstDomain\": \"moarinternetbadguys.com\",\n\n \"dstUrl\": \"http://moarinternetbadguys.com/a-bad-url-again\",\n\n \"eventTime\": \"2018-07-17T16:27:12.152962Z\",\n\n \"protocolVersion\": \"1.0a\",\n\n \"providerName\": \"Security Platform\"\n\n }]" 28 | }, 29 | "url": { 30 | "raw": "https://s-platform.api.opendns.com/1.0/events?customerKey=", 31 | "protocol": "https", 32 | "host": [ 33 | "s-platform", 34 | "api", 35 | "opendns", 36 | "com" 37 | ], 38 | "path": [ 39 | "1.0", 40 | "events" 41 | ], 42 | "query": [ 43 | { 44 | "key": "customerKey", 45 | "value": "" 46 | } 47 | ] 48 | } 49 | }, 50 | "response": [] 51 | }, 52 | { 53 | "name": "Get Request Enforcement", 54 | "request": { 55 | "auth": { 56 | "type": "noauth" 57 | }, 58 | "method": "GET", 59 | "header": [ 60 | { 61 | "key": "", 62 | "value": "" 63 | }, 64 | { 65 | "key": "", 66 | "value": "" 67 | } 68 | ], 69 | "body": {}, 70 | "url": { 71 | "raw": "https://s-platform.api.opendns.com/1.0/domains?customerKey=", 72 | "protocol": "https", 73 | "host": [ 74 | "s-platform", 75 | "api", 76 | "opendns", 77 | "com" 78 | ], 79 | "path": [ 80 | "1.0", 81 | "domains" 82 | ], 83 | "query": [ 84 | { 85 | "key": "customerKey", 86 | "value": "" 87 | } 88 | ] 89 | } 90 | }, 91 | "response": [] 92 | }, 93 | { 94 | "name": "Get Request Investigate", 95 | "request": { 96 | "auth": { 97 | "type": "bearer", 98 | "bearer": [ 99 | { 100 | "key": "token", 101 | "value": "", 102 | "type": "string" 103 | } 104 | ] 105 | }, 106 | "method": "GET", 107 | "header": [], 108 | "body": {}, 109 | "url": { 110 | "raw": "https://investigate.api.umbrella.com/domains/categorization/internetbadguys.com?showLabels", 111 | "protocol": "https", 112 | "host": [ 113 | "investigate", 114 | "api", 115 | "umbrella", 116 | "com" 117 | ], 118 | "path": [ 119 | "domains", 120 | "categorization", 121 | "internetbadguys.com" 122 | ], 123 | "query": [ 124 | { 125 | "key": "showLabels", 126 | "value": null 127 | } 128 | ] 129 | } 130 | }, 131 | "response": [] 132 | }, 133 | { 134 | "name": "Post Request Investigate", 135 | "request": { 136 | "auth": { 137 | "type": "bearer", 138 | "bearer": [ 139 | { 140 | "key": "token", 141 | "value": "", 142 | "type": "string" 143 | } 144 | ] 145 | }, 146 | "method": "POST", 147 | "header": [ 148 | { 149 | "key": "Content-Type", 150 | "value": "application/json" 151 | } 152 | ], 153 | "body": { 154 | "mode": "raw", 155 | "raw": "[\"internetbadguys.com\", \"cnn.com\", \"cisco.com\"]" 156 | }, 157 | "url": { 158 | "raw": "https://investigate.api.umbrella.com/domains/categorization/", 159 | "protocol": "https", 160 | "host": [ 161 | "investigate", 162 | "api", 163 | "umbrella", 164 | "com" 165 | ], 166 | "path": [ 167 | "domains", 168 | "categorization", 169 | "" 170 | ] 171 | } 172 | }, 173 | "response": [] 174 | }, 175 | { 176 | "name": "Delete Request Enforcement", 177 | "request": { 178 | "auth": { 179 | "type": "noauth" 180 | }, 181 | "method": "DELETE", 182 | "header": [], 183 | "body": {}, 184 | "url": { 185 | "raw": "https://s-platform.api.opendns.com/1.0/domains/moarinternetbadguys.com?customerKey=", 186 | "protocol": "https", 187 | "host": [ 188 | "s-platform", 189 | "api", 190 | "opendns", 191 | "com" 192 | ], 193 | "path": [ 194 | "1.0", 195 | "domains", 196 | "moarinternetbadguys.com" 197 | ], 198 | "query": [ 199 | { 200 | "key": "customerKey", 201 | "value": "" 202 | } 203 | ] 204 | } 205 | }, 206 | "response": [] 207 | }, 208 | { 209 | "name": "Post Request Investigate Threatgrid samples", 210 | "request": { 211 | "auth": { 212 | "type": "bearer", 213 | "bearer": [ 214 | { 215 | "key": "token", 216 | "value": "", 217 | "type": "string" 218 | } 219 | ] 220 | }, 221 | "method": "GET", 222 | "header": [ 223 | { 224 | "key": "limit", 225 | "value": "1" 226 | } 227 | ], 228 | "body": {}, 229 | "url": { 230 | "raw": "https://investigate.api.umbrella.com/samples/snibl.org", 231 | "protocol": "https", 232 | "host": [ 233 | "investigate", 234 | "api", 235 | "umbrella", 236 | "com" 237 | ], 238 | "path": [ 239 | "samples", 240 | "snibl.org" 241 | ] 242 | } 243 | }, 244 | "response": [] 245 | } 246 | ] 247 | } -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 79 3 | target-version = ['py36'] 4 | include = '\.pyi?$' 5 | exclude = ''' 6 | 7 | ( 8 | /( 9 | | \.git 10 | | \.venv 11 | | postman 12 | )/ 13 | ) 14 | ''' 15 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -i https://pypi.org/simple 2 | attrs==19.1.0 3 | bravado-core==5.11.0 4 | bravado==10.3.2 5 | certifi==2019.3.9 6 | chardet==3.0.4 7 | colorama==0.4.1 8 | crayons==0.2.0 9 | future==0.17.1 10 | idna==2.8 11 | jsonpointer==2.0 12 | jsonref==0.2 13 | jsonschema[format]==3.0.1 14 | monotonic==1.5 15 | msgpack-python==0.5.6 16 | pyrsistent==0.14.11 17 | python-dateutil==2.8.0 18 | pytz==2018.9 19 | pyyaml==5.1 20 | requests-toolbelt==0.9.1 21 | requests==2.21.0 22 | rfc3987==1.3.8 23 | simplejson==3.16.0 24 | six==1.12.0 25 | strict-rfc3339==0.7 26 | swagger-spec-validator==2.4.0 27 | typing-extensions==3.7.2 28 | urllib3==1.24.1 29 | webcolors==1.8.1 30 | webexteamssdk==1.1.1 31 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Bootstrap the project by installing development dependencies. 3 | 4 | set -e 5 | cd "$(dirname "$0")/.." 6 | 7 | 8 | # Ensure that packages are only installed in an active virtual environment 9 | export PIP_REQUIRE_VIRTUALENV=true 10 | 11 | 12 | if [[ -n "$(pipenv --version 2>/dev/null)" ]]; then 13 | echo "==> Installing pipenv environment Pipfile development dependencies" 14 | pipenv install --dev 15 | 16 | else 17 | echo "This project uses pipenv to manage development dependencies. Please install pipenv and run the script again." 18 | echo "See https://pipenv.readthedocs.io for more information." 19 | exit 1 20 | 21 | fi 22 | -------------------------------------------------------------------------------- /script/clean: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Clean the project directory. 3 | 4 | set -e 5 | cd "$(dirname "$0")/.." 6 | 7 | 8 | # Script Arguments 9 | for i in ${@}; do 10 | case ${i} in 11 | --all) 12 | deep_clean=true 13 | ;; 14 | 15 | *) 16 | echo "Unknown argument: $i" 17 | exit 1 18 | ;; 19 | esac 20 | done 21 | 22 | 23 | echo "==> DevNet Express Lab and Mission data..." 24 | rm -f mission-data/*.json 25 | rm -f intro-threatgrid/*.json 26 | 27 | echo "==> Removing OS artifacts..." 28 | find . -name '.DS_Store' -exec rm -f {} + 29 | 30 | echo "==> Removing Python build artifacts..." 31 | find . -name '*.pyc' -exec rm -f {} + 32 | find . -name '*.pyo' -exec rm -f {} + 33 | find . -name '*~' -exec rm -f {} + 34 | find . -name '__pycache__' -exec rm -fr {} + 35 | find . -name '*.egg-info' -exec rm -fr {} + 36 | find . -name '*.egg' -exec rm -f {} + 37 | rm -rf build/ 38 | rm -rf dist/ 39 | rm -rf .eggs/ 40 | 41 | echo "==> Removing test and coverage artifacts..." 42 | rm -rf .cache/ 43 | rm -f .coverage 44 | rm -rf .tox/ 45 | rm -rf htmlcov/ 46 | 47 | echo "==> Cleaning docs directories..." 48 | rm -rf docs/_build/* 49 | 50 | 51 | if [[ $deep_clean == true ]]; then 52 | echo "==> Deep cleaning..." 53 | 54 | if [ -f "Pipfile" ] && [ -n "$(pipenv --version 2>/dev/null)" ]; then 55 | echo "==> Removing pipenv environment..." 56 | pipenv --rm || true 57 | fi 58 | 59 | if [ -d ".venv" ]; then 60 | echo "==> Removing project virtual environment '.venv'..." 61 | rm -rf .venv/ 62 | fi 63 | 64 | if [ -d "venv" ]; then 65 | echo "==> Removing project virtual environment 'venv'..." 66 | rm -rf venv/ 67 | fi 68 | fi 69 | -------------------------------------------------------------------------------- /script/python_template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """One-line summary of your script. 3 | 4 | Multi-line description of your script (make sure you include the copyright and 5 | license notice). 6 | 7 | 8 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | """ 28 | 29 | 30 | import sys 31 | from pathlib import Path 32 | 33 | import webexteamssdk 34 | from crayons import green 35 | 36 | 37 | # Get the absolute path for the directory where this file is located 38 | here = Path(__file__).parent.absolute() 39 | 40 | # Get the path for the project / repository root 41 | repository_root = (here / "..").resolve() 42 | 43 | 44 | # Extend the system path to include the project root and import the env files 45 | sys.path.insert(0, str(repository_root)) 46 | import env_lab # noqa 47 | import env_user # noqa 48 | 49 | 50 | # TODO: Insert your code here. What follows is just an example. 51 | 52 | 53 | teams = webexteamssdk.WebexTeamsAPI(env_user.WEBEX_TEAMS_ACCESS_TOKEN) 54 | 55 | 56 | print(f""" 57 | {green("My FMC Lab Environment:")} 58 | FMC Host: {env_lab.FMC["host"]} 59 | FMC Port: {env_lab.FMC["port"]} 60 | FMC Username: {env_lab.FMC["username"]} 61 | FMC Password: {env_lab.FMC["password"]} 62 | """) 63 | 64 | 65 | print(f""" 66 | {green("I am connected to Webex Teams as:")} 67 | {teams.people.me()} 68 | """) 69 | 70 | print(f""" 71 | {green("I am posting things to the following Teams Room:")} 72 | {teams.rooms.get(env_user.WEBEX_TEAMS_ROOM_ID)} 73 | """) 74 | -------------------------------------------------------------------------------- /script/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Setup the project for initial use (after cloning) or reset it back to the initial (unused) state. 3 | 4 | set -e 5 | cd "$(dirname "$0")/.." 6 | 7 | 8 | echo "==> Setting up / resetting project for initial use..." 9 | 10 | script/clean --all 11 | 12 | script/bootstrap 13 | -------------------------------------------------------------------------------- /script/update_dependencies: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Update the project's dependencies and create a new requirements.txt file. 3 | # 4 | # Use pipenv to manage the user vs. dev requirements and to create complete locked user requirements.txt file in the 5 | # repository root. 6 | 7 | 8 | 9 | set -e 10 | cd "$(dirname "$0")/.." 11 | 12 | 13 | # Ensure that packages are only installed in an active virtual environment 14 | export PIP_REQUIRE_VIRTUALENV=true 15 | 16 | 17 | echo "==> Updating the project dependencies" 18 | pipenv update 19 | 20 | echo "==> Updating the repository requirements.txt file" 21 | pipenv lock -r > requirements.txt 22 | -------------------------------------------------------------------------------- /verify/backend/ampforendpoints.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Verify the AMP for Endpoints APIs are accessible and responding. 3 | 4 | Verify that DNA Center Sanbox Northbound APIs are accessible and 5 | responding with data. 6 | 7 | 8 | Copyright (c) 2019-2020 Cisco and/or its affiliates. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | """ 28 | 29 | import sys 30 | from pathlib import Path 31 | 32 | import requests 33 | from crayons import blue, green, red 34 | from requests import HTTPError 35 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 36 | 37 | 38 | # Locate the directory containing this file and the repository root. 39 | # Temporarily add these directories to the system path so that we can import 40 | # local files. 41 | here = Path(__file__).parent.absolute() 42 | repository_root = (here / ".." / "..").resolve() 43 | 44 | sys.path.insert(0, str(repository_root)) 45 | 46 | import env_lab # noqa 47 | import env_user 48 | 49 | # Disable insecure request warnings 50 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 51 | 52 | 53 | def get_amp_events( 54 | host=env_lab.AMP.get("host"), 55 | client_id=env_user.AMP_CLIENT_ID, 56 | api_key=env_user.AMP_API_KEY, 57 | ): 58 | """Get a list of recent events from Cisco AMP.""" 59 | print("\n==> Getting recent events from AMP") 60 | 61 | url = f"https://{client_id}:{api_key}@{host}/v1/events" 62 | 63 | response = requests.get(url, verify=False) 64 | response.raise_for_status() 65 | 66 | events_list = response.json() 67 | 68 | print(green(f"Retrieved events from AMP...")) 69 | 70 | return events_list 71 | 72 | 73 | def verify() -> bool: 74 | """Verify access to the AMP for Endpoints APIs.""" 75 | print(blue('\n\n==> Verifying access to the AMP for Endpoints APIs')) 76 | # Verify the Spark Room exists and is accessible via the access token 77 | try: 78 | if(len(get_amp_events())): 79 | print(green(f"AMP for endpoints API is accessible!\n")) 80 | else: 81 | print(red(f"AMP for endpoints API is accessible, API credentials might be wrong")) 82 | except: 83 | print(red("Unable to contact AMP for Endpoints")) 84 | return False 85 | 86 | return True 87 | 88 | 89 | if __name__ == '__main__': 90 | verify() 91 | -------------------------------------------------------------------------------- /verify/backend/fdm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Verify the FDM APIs are accessible and responding. 3 | 4 | 5 | 6 | Copyright (c) 2019-2020 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | import sys 28 | from pathlib import Path 29 | 30 | import requests 31 | from crayons import blue, green, red 32 | from requests import HTTPError 33 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 34 | 35 | 36 | # Locate the directory containing this file and the repository root. 37 | # Temporarily add these directories to the system path so that we can import 38 | # local files. 39 | here = Path(__file__).parent.absolute() 40 | repository_root = (here / ".." / "..").resolve() 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | from env_lab import FDM # noqa 45 | 46 | 47 | # Disable insecure request warnings 48 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 49 | 50 | 51 | def fdm_login( 52 | host=FDM.get("host"), 53 | port=FDM.get("port"), 54 | username=FDM.get("username"), 55 | password=FDM.get("password"), 56 | api_version=FDM.get("api_version") 57 | ): 58 | """Login to FDM and return an access token that may be used for API calls. 59 | """ 60 | print("\n==> Logging into FDM and requesting an access token") 61 | 62 | headers = { 63 | "Content-Type": "application/json", 64 | "Accept": "application/json", 65 | } 66 | 67 | payload = { 68 | "grant_type": "password", 69 | "username": username, 70 | "password": password, 71 | } 72 | 73 | response = requests.post( 74 | f"https://{host}:{port}/api/fdm/v{api_version}/fdm/token", 75 | headers=headers, 76 | json=payload, 77 | verify=False, 78 | ) 79 | 80 | try: 81 | response.raise_for_status() 82 | access_token = response.json()["access_token"] 83 | 84 | except HTTPError: 85 | if response.status_code == 400: 86 | raise HTTPError(f"Error logging in to FDM: {response.text}") 87 | else: 88 | raise 89 | 90 | except ValueError: 91 | raise ValueError("Error parsing the response from FDM") 92 | 93 | return access_token 94 | 95 | 96 | def verify() -> bool: 97 | """FDM APIs""" 98 | print(blue("\n==> Verifying access to the FDM APIs")) 99 | 100 | try: 101 | if(len(fdm_login())): 102 | print(green(f"Firepower FDM API is accessible...Note: FDM 90 day Eval License needs to enabled for full functionality..!!!\n")) 103 | else: 104 | print(red(f"Firepower FDM API is accessible, API credentials might be wrong")) 105 | except: 106 | print(red("Unable to contact FDM")) 107 | return False 108 | 109 | return True 110 | 111 | 112 | if __name__ == '__main__': 113 | verify() 114 | -------------------------------------------------------------------------------- /verify/backend/fmc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Verify the Cisco (FMC) Firepower Management Center APIs are accessible and responding. 3 | 4 | 5 | Copyright (c) 2019-2020 Cisco and/or its affiliates. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | import sys 26 | from pathlib import Path 27 | 28 | import requests 29 | from crayons import blue, green, red 30 | from requests import HTTPError 31 | from requests.auth import HTTPBasicAuth 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | 34 | # Locate the directory containing this file and the repository root. 35 | # Temporarily add these directories to the system path so that we can import 36 | # local files. 37 | here = Path(__file__).parent.absolute() 38 | repository_root = (here / ".." / "..").resolve() 39 | 40 | sys.path.insert(0, str(repository_root)) 41 | 42 | from env_lab import FMC # noqa 43 | 44 | 45 | # Disable insecure request warnings 46 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 47 | 48 | domain_uuid = "" 49 | headers = { 50 | "Content-Type": "application/json", 51 | "Accept": "application/json", 52 | } 53 | 54 | def fmc_authenticate( 55 | host=FMC.get("host"), 56 | port=FMC.get("port"), 57 | username=FMC.get("username"), 58 | password=FMC.get("password"), 59 | ): 60 | """Authenticate with FMC; get and store the auth token and domain UUID.""" 61 | print("\n==> Authenticating with FMC and requesting an access token") 62 | 63 | global domain_uuid 64 | 65 | authentication = HTTPBasicAuth(username, password) 66 | 67 | response = requests.post( 68 | f"https://{host}:{port}/api/fmc_platform/v1/auth/generatetoken", 69 | headers=headers, 70 | auth=authentication, 71 | verify=False 72 | ) 73 | response.raise_for_status() 74 | 75 | # Get the authentication token and domain UUID from the response 76 | access_token = response.headers.get("X-auth-access-token") 77 | domain_uuid = response.headers.get("DOMAIN_UUID") 78 | 79 | # Update the headers used for subsequent requests to FMC 80 | headers["DOMAIN_UUID"] = domain_uuid 81 | headers["X-auth-access-token"] = access_token 82 | 83 | return access_token, domain_uuid 84 | 85 | def verify() -> bool: 86 | """FMC APIs""" 87 | print(blue("\n==> Verifying access to the FMC APIs")) 88 | 89 | try: 90 | if(len(fmc_authenticate())): 91 | print(green(f"Firepower FMC API is accessible\n")) 92 | else: 93 | print(red(f"Firepower FMC API is accessible, but something went wrong... Check Credentials?")) 94 | except: 95 | print(red("Unable to contact FMC")) 96 | return False 97 | 98 | return True 99 | 100 | 101 | if __name__ == '__main__': 102 | verify() 103 | -------------------------------------------------------------------------------- /verify/backend/ise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Verify the ISE APIs are accessible and responding. 3 | 4 | Copyright (c) 2019-2020 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | import sys 26 | from pathlib import Path 27 | 28 | import requests 29 | from crayons import blue, green, red 30 | from requests import HTTPError 31 | from requests.auth import HTTPBasicAuth 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / ".." / "..").resolve() 40 | 41 | sys.path.insert(0, str(repository_root)) 42 | 43 | from env_lab import ISE # noqa 44 | 45 | 46 | # Disable insecure request warnings 47 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 48 | 49 | def get_ise_anc_policies( 50 | host=ISE.get("host"), 51 | port=ISE.get("port"), 52 | username=ISE.get("username"), 53 | password=ISE.get("password"), 54 | ): 55 | """Get a list of configured ISE Adaptive Network Control (ANC) policies.""" 56 | print("\n==> Getting ISE ANC policies") 57 | 58 | headers = { 59 | 'Content-Type': "application/json", 60 | 'Accept': "application/json" 61 | } 62 | 63 | authentication = HTTPBasicAuth(username, password) 64 | 65 | response = requests.get( 66 | f"https://{host}:{port}/ers/config/ancpolicy", 67 | headers=headers, 68 | auth=authentication, 69 | verify=False, 70 | ) 71 | response.raise_for_status() 72 | 73 | return response.json()["SearchResult"]["resources"] 74 | 75 | def verify() -> bool: 76 | """Verify access to the ISE ERS REST APIs.""" 77 | print(blue("\n==> Verifying access to the ISE ERS REST APIs for Environment.")) 78 | try: 79 | if(len(get_ise_anc_policies())): 80 | print(green(f"ISE ERS REST API is accessible!\n")) 81 | else: 82 | print(red(f"ISE ERS REST API is accessible, API credentials might be wrong")) 83 | except: 84 | print(red("Unable to contact ISE ERS REST API")) 85 | return False 86 | 87 | return True 88 | 89 | 90 | if __name__ == "__main__": 91 | verify() 92 | -------------------------------------------------------------------------------- /verify/backend/threatgrid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Verify the ThreatGrid APIs are accessible and responding. 3 | 4 | 5 | 6 | Copyright (c) 2019-2020 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | import sys 28 | from pathlib import Path 29 | 30 | import requests 31 | from crayons import blue, green, red 32 | from requests import HTTPError 33 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 34 | 35 | 36 | # Locate the directory containing this file and the repository root. 37 | # Temporarily add these directories to the system path so that we can import 38 | # local files. 39 | here = Path(__file__).parent.absolute() 40 | repository_root = (here / ".." / "..").resolve() 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | from env_lab import THREATGRID # noqa 45 | from env_user import THREATGRID_API_KEY # noqa 46 | 47 | # Disable insecure request warnings 48 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 49 | 50 | def ThreatGrid_get(): 51 | api_key = THREATGRID_API_KEY 52 | print("\n==> Getting recent events from ThreatGrid") 53 | url = 'https://panacea.threatgrid.com/api/v2/iocs/feeds/domains?after=2018-07-18T21:39:13Z&before=2019-07-18T22:39:13Z&domain=lamp.troublerifle.bid&api_key={}'.format(api_key) 54 | r = requests.get(url) 55 | r.raise_for_status() 56 | print(green(f"Retrieved events from ThreatGrid...")) 57 | 58 | return (r.json()) 59 | 60 | 61 | def verify() -> bool: 62 | """ThreatGrid APIs""" 63 | print(blue("\n==> Verifying access to the ThreatGrid APIs")) 64 | 65 | try: 66 | if(len(ThreatGrid_get())): 67 | print(green(f" ThreatGrid API is accessible!\n")) 68 | else: 69 | print(red(f" ThreatGrid API is accessible, API credentials might be wrong")) 70 | except: 71 | print(red("Unable to contact ThreatGrid Cloud")) 72 | return False 73 | 74 | return True 75 | 76 | 77 | if __name__ == '__main__': 78 | verify() 79 | -------------------------------------------------------------------------------- /verify/backend/umbrellaInvestigate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Verify the Umbrella Investigate APIs are accessible and responding. 3 | 4 | 5 | 6 | Copyright (c) 2019-2020 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | import requests 27 | import configparser 28 | import json 29 | import sys 30 | from pathlib import Path 31 | 32 | from crayons import blue, green, red 33 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 34 | # Locate the directory containing this file and the repository root. 35 | # Temporarily add these directories to the system path so that we can import 36 | # local files. 37 | here = Path(__file__).parent.absolute() 38 | repository_root = (here / ".." / ".." ).resolve() 39 | 40 | sys.path.insert(0, str(repository_root)) 41 | 42 | sys.path.insert(0, str(repository_root)) 43 | 44 | from env_lab import UMBRELLA # noqa 45 | from env_user import UMBRELLA_INVESTIGATE_KEY # noqa 46 | # Disable insecure request warnings 47 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 48 | # API key from evn_user.py 49 | 50 | def doUmbrellaGet() : 51 | investigate_api_key = UMBRELLA_INVESTIGATE_KEY 52 | # URL needed for the domain status and category 53 | #create header for authentication 54 | headers = { 55 | 'Authorization': 'Bearer ' + investigate_api_key 56 | } 57 | # # investigate_url = "https://investigate.api.umbrella.com/domains/categorization/" 58 | inv_u = UMBRELLA.get("inv_url") 59 | investigate_url = f"{inv_u}/domains/categorization/" 60 | # domain that will be checked 61 | domain = "internetbadguys.com" 62 | #create header for authentication 63 | headers = { 64 | 'Authorization': 'Bearer ' + investigate_api_key 65 | } 66 | # assemble the URI, show labels give readable output 67 | get_url = investigate_url + domain + "?showLabels" 68 | 69 | #do GET request for the domain status and category 70 | try: 71 | req = requests.get(get_url, headers=headers) 72 | 73 | # time for timestamp of verdict domain 74 | if(req.status_code == 200): 75 | return "Green" 76 | elif(req.status_code == 401): 77 | return "Yellow" 78 | else: 79 | return "Orange" 80 | except: 81 | return "Red" 82 | 83 | 84 | def verify() -> bool: 85 | print(blue("\n==> Verifying access to the Umbrella Investigate APIs")) 86 | 87 | """ Verify the umbrella investigate accessible via the provided token """ 88 | 89 | result = doUmbrellaGet() 90 | 91 | if (result == "Green"): 92 | print(green(f"Umbrella Investigate is accessible and token is good!\n")) 93 | return True 94 | elif (result == "Yellow"): 95 | print(red(f"Umbrella Investigate is accessible But token is NO good!\n")) 96 | return False 97 | elif (result == "Orange"): 98 | print(red(f"Umbrella Investigate is accessible But something went very wrong!\n")) 99 | return False 100 | else: 101 | print(red("Unable to access Umbrella Investigate Cloud\n")) 102 | return False 103 | 104 | 105 | 106 | if __name__ == '__main__': 107 | verify() 108 | -------------------------------------------------------------------------------- /verify/backend/umbrellaenforcement.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Verify the Umbrella Plaform Enforcement API is accessible and responding. 3 | 4 | Copyright (c) 2019-2020 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | from datetime import datetime 26 | import requests 27 | import configparser 28 | import json 29 | import sys 30 | from pathlib import Path 31 | from crayons import blue, green, red 32 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 33 | 34 | 35 | # Locate the directory containing this file and the repository root. 36 | # Temporarily add these directories to the system path so that we can import 37 | # local files. 38 | here = Path(__file__).parent.absolute() 39 | repository_root = (here / ".."/"..").resolve() 40 | 41 | sys.path.insert(0, str(repository_root)) 42 | 43 | sys.path.insert(0, str(repository_root)) 44 | 45 | from env_lab import UMBRELLA # noqa 46 | from env_user import UMBRELLA_ENFORCEMENT_KEY 47 | 48 | # Disable insecure request warnings 49 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 50 | 51 | enforcement_api_key = UMBRELLA_ENFORCEMENT_KEY 52 | 53 | # URL needed to do POST requests 54 | domain_url = "https://s-platform.api.opendns.com/1.0/domains" 55 | 56 | # URL needed for POST request 57 | url_get = domain_url + '?customerKey=' + enforcement_api_key 58 | 59 | # keep doing GET requests, until looped through all domains 60 | 61 | def verify() -> bool: 62 | """Verify access to the NFVIS Device.""" 63 | print(blue("\n==> Verifying access to the Umbrella Platform Enforecment APIs in Cloud.")) 64 | try: 65 | req = requests.get(url_get) 66 | if(req.status_code == 200): 67 | print(green(f"Umbrella Platform Enforcement API is accessible and API key is good!\n")) 68 | return True 69 | elif(req.status_code == 401): 70 | print(red(f"Umbrella Platform Enforcement API is accessible and API key is NOT Working!\n")) 71 | return False 72 | else: 73 | print(red(f"Umbrella Platform Enforcement API is pingable but something went very wrong!\n")) 74 | return False 75 | except: 76 | print(red("Unable to access Umbrella Investigate Cloud\n")) 77 | return False 78 | 79 | 80 | if __name__ == "__main__": 81 | verify() 82 | -------------------------------------------------------------------------------- /verify/backend/webexteams.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Verify the Webex Teams APIs are accessible and responding. 3 | 4 | Verify that user's provide SPARK_ACCESS_TOKEN is valid and that calls to the 5 | Webex Teams APIs complete successfully. 6 | 7 | 8 | Copyright (c) 2018 Cisco and/or its affiliates. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | """ 28 | import json 29 | import sys 30 | from pathlib import Path 31 | 32 | import requests 33 | import webexteamssdk 34 | from crayons import blue, green, red 35 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 36 | 37 | 38 | # Locate the directory containing this file and the repository root. 39 | # Temporarily add these directories to the system path so that we can import 40 | # local files. 41 | here = Path(__file__).parent.absolute() 42 | repository_root = (here / ".." / "..").resolve() 43 | 44 | sys.path.insert(0, str(repository_root)) 45 | 46 | import env_lab # noqa 47 | import env_user # noqa 48 | 49 | 50 | # Disable insecure request warnings 51 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 52 | 53 | 54 | def verify() -> bool: 55 | """Verify access to the Webex Teams APIs.""" 56 | print(blue("\n==> Verifying access to the Webex Teams APIs")) 57 | 58 | # Check to ensure the user has provided their Spark Access Token 59 | if not env_user.WEBEX_TEAMS_ACCESS_TOKEN: 60 | print( 61 | red( "\nFAILED: You must provide your WEBEX_TEAMS_ACCESS_TOKEN in the " 62 | "env_user.py file.\n") 63 | ) 64 | return False 65 | try: 66 | teams = webexteamssdk.WebexTeamsAPI(env_user.WEBEX_TEAMS_ACCESS_TOKEN) 67 | me = teams.people.me() 68 | except webexteamssdk.ApiError as e: 69 | print( 70 | red(f"\nFAILED: The API call to Webex Teams returned the following " 71 | "error:\n{e}\n")) 72 | return False 73 | else: 74 | print( 75 | green(f"\nYou are connected to Webex Teams as: {me.emails[0]}\n") 76 | ) 77 | 78 | 79 | # Check to ensure the user has provided a WebEx TEAMS Room ID 80 | if not env_user.WEBEX_TEAMS_ROOM_ID: 81 | print( 82 | red("\nFAILED: You must provide the WEBEX_TEAMS_ROOM_ID of the room you " 83 | "want to work with in the env_user.py file.\n") 84 | ) 85 | return False 86 | 87 | # Verify the Spark Room exists and is accessible via the access token 88 | try: 89 | room = teams.rooms.get(env_user.WEBEX_TEAMS_ROOM_ID) 90 | except webexteamssdk.ApiError as e: 91 | print( 92 | red(f"\nFAILED: There was an error accessing the Spark Room using the WEBEX_TEAMS_ROOM_ID you provided; error details:\n{e}\n")) 93 | return False 94 | else: 95 | print( 96 | green(f"You will be posting messages to the following room: {room.title}\n")) 97 | 98 | return True 99 | 100 | 101 | if __name__ == '__main__': 102 | verify() 103 | -------------------------------------------------------------------------------- /verify/requirements.txt: -------------------------------------------------------------------------------- 1 | -i https://pypi.org/simple 2 | attrs==19.1.0 3 | bravado-core==5.11.0 4 | bravado==10.3.2 5 | certifi==2019.3.9 6 | chardet==3.0.4 7 | colorama==0.4.1 8 | crayons==0.2.0 9 | future==0.17.1 10 | idna==2.8 11 | jsonpointer==2.0 12 | jsonref==0.2 13 | jsonschema[format]==3.0.1 14 | monotonic==1.5 15 | msgpack-python==0.5.6 16 | pyrsistent==0.14.11 17 | python-dateutil==2.8.0 18 | pytz==2018.9 19 | pyyaml==5.1 20 | requests-toolbelt==0.9.1 21 | requests==2.21.0 22 | rfc3987==1.3.8 23 | simplejson==3.16.0 24 | six==1.12.0 25 | strict-rfc3339==0.7 26 | swagger-spec-validator==2.4.3 27 | typing-extensions==3.7.2 28 | urllib3==1.24.1 29 | webcolors==1.8.1 30 | webexteamssdk==1.1.1 31 | -------------------------------------------------------------------------------- /verify/testEnv.py: -------------------------------------------------------------------------------- 1 | """Set the Environment Information Needed to Access Your Lab! 2 | 3 | The provided sample code in this repository will reference this file to get the 4 | information needed to connect to your lab backend. You provide this info here 5 | once and the scripts in this repository will access it as needed by the lab. 6 | 7 | 8 | Copyright (c) 2018-2019 Cisco and/or its affiliates. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | """ 28 | import os 29 | import sys 30 | import time 31 | import requests 32 | import requests.auth 33 | from urllib3.exceptions import InsecureRequestWarning 34 | # Locate the directory containing this file and the repository root. 35 | # Temporarily add these directories to the system path so that we can import 36 | # local files. 37 | here = os.path.abspath(os.path.dirname(__file__)) 38 | repository_root = os.path.abspath(os.path.join(here, "..")) 39 | sys.path.insert(0, repository_root) 40 | sys.path.insert(0, here) 41 | 42 | print ("Reading env.py....") 43 | syms = ['◐', '◓', '◑', '◒'] 44 | time.sleep(4) 45 | 46 | print ("\nDone Reading... Preparing to Print your config ...") 47 | 48 | from env_lab import FMC # 49 | from env_lab import FDM 50 | from env_lab import ISE 51 | from env_lab import ENFORCEMENT 52 | from env_lab import INVESTIGATE 53 | from env_lab import AMP 54 | from env_lab import THREATGRID 55 | from env_lab import WEBEXTEAMS 56 | time.sleep(2) 57 | host=FMC["host"] 58 | port=FMC["port"] 59 | username=FMC["username"] 60 | password=FMC["password"] 61 | print("\n\nYour FMC Configuration:\n") 62 | print (f"FMC HOST: {host}") 63 | print (f"FMC PORT: {port}") 64 | print (f"FMC UserName: {username}") 65 | print (f"FMC Password: {password}") 66 | host=FDM["host"] 67 | port=FDM["port"] 68 | username=FDM["username"] 69 | password=FDM["password"] 70 | time.sleep(2) 71 | print("\n\nYour FDM Configuration:\n") 72 | print (f"FDM HOST: {host}") 73 | print (f"FDM PORT: {port}") 74 | print (f"FDM UserName: {username}") 75 | print (f"FDM Password: {password}") 76 | host=ISE["host"] 77 | port=ISE["port"] 78 | username=ISE["username"] 79 | password=ISE["password"] 80 | time.sleep(2) 81 | print("\n\nYour ISE Configuration:\n") 82 | print (f"ISE HOST: {host}") 83 | print (f"ISE PORT: {port}") 84 | print (f"ISE UserName: {username}") 85 | print (f"ISE Password: {password}") 86 | time.sleep(2) 87 | print("\n\nYour Umbrella Configuration:\n") 88 | apikey=ENFORCEMENT["apiKey"] 89 | print (f"Umbrella enforcement Key: {apikey}") 90 | token=INVESTIGATE["token"] 91 | print (f"Umbrella Investigate Key: {token}") 92 | custid = AMP["clientId"] 93 | apikey = AMP["apiKey"] 94 | host = AMP["host"] 95 | time.sleep(2) 96 | print("\n\nYour AMP Configuration:\n") 97 | print (f"AMP Customer ID: {custid}") 98 | print (f"AMP API Key: {apikey}") 99 | print (f"AMP HOSTNAME: {host}") 100 | time.sleep(2) 101 | print("\n\nYour THREATGRID Configuration:\n") 102 | host = THREATGRID["host"] 103 | apikey = THREATGRID["apiKey"] 104 | print (f"THREATGRID HOST: {host}") 105 | print (f"THREATGRID API KEY: {apikey}\n\n") 106 | time.sleep(2) 107 | print("\n\nYour WEBEX TEAMS Configuration:\n") 108 | roomID = WEBEXTEAMS["roomID"] 109 | apikey = WEBEXTEAMS["apiKey"] 110 | print (f"WEBEX TEAMS ROOM ID : {roomID}") 111 | print (f"WEBEX TEAMS API KEY: {apikey}\n\n") 112 | -------------------------------------------------------------------------------- /verify/verify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Verify the backend lab environment. 3 | 4 | Verify that the `env_lab.py` and `env_user.py` Python environment files exist 5 | and that they import successfully, and then verify that the backend lab 6 | environments are accessible and responding to API calls. 7 | 8 | 9 | Copyright (c) 2018 Cisco and/or its affiliates. 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | """ 29 | 30 | from importlib.util import module_from_spec, spec_from_file_location 31 | from glob import glob 32 | import os 33 | import sys 34 | 35 | 36 | # Get the absolute path for the directory where this file is located "here" 37 | here = os.path.abspath(os.path.dirname(__file__)) 38 | 39 | # Get the absolute path for the project / repository root 40 | project_root = os.path.abspath(os.path.join(here, "..")) 41 | 42 | # Extend the system path to include the project root 43 | if project_root not in sys.path: 44 | sys.path.insert(0, project_root) 45 | 46 | 47 | # Verify env_lab.py 48 | try: 49 | print("==> Importing env_lab") 50 | import env_lab # noqa 51 | except Exception as e: 52 | print("FAILED: Error importing env_lab.py; error details:\n{}\n".format(e)) 53 | sys.exit(1) 54 | 55 | 56 | # Verify env_user.py 57 | try: 58 | print("==> Importing env_user") 59 | import env_user # noqa 60 | except ModuleNotFoundError: 61 | print( 62 | "\nFAILED: Error importing env_user.py; file not found. Did you " 63 | "create one? Copy and edit env_lab.template in the repository root.\n" 64 | ) 65 | sys.exit(1) 66 | except Exception as e: 67 | print( 68 | "\nFAILED: Error importing `env_user.py`. Error Details:\n" 69 | "{}\n".format(e) 70 | ) 71 | sys.exit(1) 72 | 73 | 74 | # Verify backend lab environments 75 | backend = os.path.abspath(os.path.join(here, "backend")) 76 | verified = [] 77 | for python_file in glob(os.path.join(backend, "*.py")): 78 | name = os.path.splitext(os.path.basename(python_file))[0] 79 | spec = spec_from_file_location(name, python_file) 80 | module = module_from_spec(spec) 81 | spec.loader.exec_module(module) 82 | verified.append(module.verify()) 83 | 84 | if all(verified): 85 | print("\nAll lab backend systems are responding. You're good to go!") 86 | else: 87 | print( 88 | "\nSome of the backend systems didn't respond or reported errors; " 89 | "please check the above output for details." 90 | ) 91 | --------------------------------------------------------------------------------