Attack Surface Monitoring: Github Actions & Nessus

Ben Swain
4 min readMay 26, 2023

--

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.

--

--

Ben Swain

Cryptography: OpenID Connect tied with SPIFFE ID, mTLS overlay network design and Passwordless Auth - 20yrs Pentester with CESG Check Team Leader