Running a attack surface monitoring with amass, Nessus and GitHub Actions: A Step-by-Step Guide
Introduction
— -
In this blog post, we’ll explore a GitHub Actions-based Continuous Integration/Continuous Deployment (CI/CD) pipeline that uses Python for a security assessment — to domain-based monitoring of the attack surface. The Python script passes the domains listed in the repo file ‘domains.txt’ uses Nessus, a popular vulnerability scanner, to perform this security assessment. To enhance the scan coverage, we’ll utilize OWASP Amass, an open-source tool for network mapping of attack surfaces and asset discovery, to extract the domain details before launching the Nessus scan. Let’s delve into this in more detail.
GitHub Actions Workflow Configuration
— -
Our pipeline is configured with a YAML file that defines several steps:
```yaml
name: Security Assessment
on:
push:
branches:
— main
jobs:
security-assessment:
runs-on: ubuntu-latest
steps:
— name: Checkout Repository
uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.x # Replace ‘x’ with the desired Python version
- name: Install Dependencies
run: |
python -m pip install — upgrade pip
pip install -r requirements.txt
- name: Configure SMTP Settings
run: python script.py smtp_config
- name: Check Nessus Installation
run: python script.py is_nessus_installed
- name: Install Nessus
run: python script.py install_nessus
- name: Wait for Nessus to be Operational
run: python script.py is_nessus_operational
- name: Run Security Assessment
run: python script.py
```
Python Script Overview
— -
In our Python script, we make use of several functions that perform various tasks:
1. **Check if Amass is installed**: Before running the script, we check if the Amass tool is installed on the system.
```python
amass_installed = any(p.name() == “amass” for p in psutil.process_iter())
if not amass_installed:
print(“Amass is not installed.”)
exit(1)
```
2. **Run Amass**: This function uses the subprocess module to execute the Amass command, which retrieves a list of domain hostnames.
```python
def run_amass(domain):
try:
output = subprocess.check_output([amass_program_command, domain])
return output.decode().splitlines()
except subprocess.CalledProcessError as e:
print(f”Error occurred while running Amass: {str(e)}”)
return []
```
3. **Launch Preconfigured Scan**: This function uses the Nessus REST API to launch a pre-configured scan, adding the hostnames discovered by Amass to the scan’s targets dynamically.
```python
def launch_preconfigured_scan(scan_uuid, hostnames):
try:
headers = {
“X-ApiKeys”: f”accessKey={nessus_access_key}; secretKey={nessus_secret_key}”,
“Content-Type”: “application/json”
}
payload = {
“scan_id”: scan_uuid,
“targets”: {
“ip_addresses”: hostnames
}
}
response = requests.post(f”{nessus_api_url}/scans/{scan_uuid}/launch”, json=payload, headers=headers)
response.raise_for_status()
if response.status_code == 200:
print(“Scan launched successfully.”)
else:
print(“Failed to launch the scan.”)
except requests.exceptions.RequestException as e:
print(f”Error occurred while launching the scan: {str(e)}”)
```
4
. **SMTP Configuration**: Here, we configure the SMTP settings of Nessus to enable email notifications.
```python
def smtp_config():
try:
smtp_payload = {
“smtp_host”: “198.51.100.4”,
“smtp_port”: “2025”,
“smtp_from”: “nessus@app-sec.co.uk”,
“smtp_enc”: “No Encryption”,
“smtp_www_host”: “198.51.100.4:8834”,
“smtp_auth”: “NONE”,
“smtp_user”: None,
“smtp_pass”: None
}
response = requests.post(f”{nessus_api_url}/settings/network/mail”, json=smtp_payload, verify=False)
response.raise_for_status()
if response.status_code == 200:
print(“SMTP configuration updated successfully.”)
else:
print(“Failed to update SMTP configuration.”)
except requests.exceptions.RequestException as e:
print(f”Error occurred while updating SMTP configuration: {str(e)}”)
```
5. **Check if Nessus is installed**: This function checks if Nessus is installed on the system.
```python
def is_nessus_installed():
try:
command = “nessuscli version”
result = subprocess.run(command, shell=True, capture_output=True, text=True, check=True)
output = result.stdout.lower()
if “nessuscli” in output:
return True
except (FileNotFoundError, subprocess.CalledProcessError) as e:
print(f”Error occurred while checking Nessus installation: {str(e)}”)
return False
```
6. **Install Nessus**: If Nessus is not already installed, this function installs it.
```python
def install_nessus():
try:
command = “apt-get install nessus -y” # Modify the command according to your package manager
subprocess.run(command, shell=True, check=True)
print(“Nessus installation completed.”)
except subprocess.CalledProcessError as e:
print(f”Error occurred while installing Nessus: {e.stderr}”)
```
7. **Check if Nessus is Operational**: This function checks the operational status of Nessus before proceeding with any scanning activities.
```python
def is_nessus_operational():
try:
headers = {
“X-ApiKeys”: f”accessKey={nessus_access_key}; secretKey={nessus_secret_key}”,
“Content-Type”: “application/json”
}
response = requests.get(f”{nessus_api_url}/server/status”, headers=headers, verify=False)
response.raise_for_status()
data = response.json()
if response.status_code == 200 and data.get(“status”) == “ready”:
return True
except requests.exceptions.RequestException as e:
print(f”Error occurred while checking Nessus status: {str(e)}”)
return False
```
Main Function
— -
In the main function of our Python script, we:
1. Configure SMTP settings.
2. Check if Nessus is installed. If it’s not, we install it.
3. Check if Nessus is operational. If it’s not, we wait until it becomes operational.
4. Read a text file with domain names, run Amass for each domain to find hostnames, and launch the Nessus scans for each of these hostnames.
```python
if __name__ == “__main__”:
# Configure SMTP settings
smtp_config()
# Check if Nessus is installed
if not is_nessus_installed():
install_nessus()
# Check if Nessus is operational
while not is_nessus_operational():
print(“Nessus is not
operational. Waiting for 10 seconds before checking again.”)
time.sleep(10)
# Run Amass and Nessus scans
with open(‘domains.txt’, ‘r’) as file:
for domain in file.readlines():
hostnames = run_amass(domain.strip())
launch_preconfigured_scan(scan_uuid, hostnames)
```
Conclusion
— -
That wraps up our guide to creating a security assessment pipeline using GitHub Actions, Python, Amass, and Nessus. This automated approach saves time, improves the consistency of our scans, and ensures we’re promptly alerted about potential vulnerabilities, making our systems more secure.
Remember to replace placeholder values (like `your_scan_uuid` and `your-nessus-access-key`) with your actual values before running the script. Also, always handle sensitive information securely, avoiding hard-coding them.