From 9d199f0102e28e686cd50462632ea92f7ac150a3 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Mon, 1 Jul 2024 18:28:06 +0100 Subject: [PATCH] Add nmap command --- requirements.txt | 1 + src/cogs/net.py | 130 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/requirements.txt b/requirements.txt index 78aca47..073051b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,3 +20,4 @@ python-magic~=0.4 aiofiles~=23.2 fuzzywuzzy[speedup]~=0.18 tortoise-orm[asyncpg]~=0.21 +superpaste @ git+https://github.com/nexy7574/superpaste.git@e31eca6 diff --git a/src/cogs/net.py b/src/cogs/net.py index 7755d44..cb1ef66 100644 --- a/src/cogs/net.py +++ b/src/cogs/net.py @@ -3,8 +3,12 @@ import io import json import os import re +import shlex +import shutil +import tempfile import time import typing +import warnings from pathlib import Path import discord @@ -14,6 +18,8 @@ from dns import asyncresolver from rich.console import Console from rich.tree import Tree from conf import CONFIG +from superpaste import HstSHBackend +from superpaste.backends import GenericFile class GetFilteredTextView(discord.ui.View): @@ -346,6 +352,130 @@ class NetworkCog(commands.Cog): await ctx.respond(embed=embed) + @commands.slash_command() + @commands.max_concurrency(1, commands.BucketType.user) + async def nmap( + self, + ctx: discord.ApplicationContext, + target: str, + technique: typing.Annotated[ + str, + discord.Option( + str, + choices=[ + discord.OptionChoice(name="TCP SYN", value="S"), + discord.OptionChoice(name="TCP Connect", value="T"), + discord.OptionChoice(name="TCP ACK", value="A"), + discord.OptionChoice(name="TCP Window", value="W"), + discord.OptionChoice(name="TCP Maimon", value="M"), + discord.OptionChoice(name="UDP", value="U"), + discord.OptionChoice(name="TCP Null", value="N"), + discord.OptionChoice(name="TCP FIN", value="F"), + discord.OptionChoice(name="TCP XMAS", value="X"), + ], + default="sT" + ) + ], + treat_all_hosts_online: bool = False, + service_scan: bool = False, + fast_mode: bool = False, + enable_os_detection: bool = False, + timing: typing.Annotated[ + int, + discord.Option( + int, + description="Timing template to use 0 is slowest, 5 is fastest.", + choices=[0, 1, 2, 3, 4, 5], + default=3 + ) + ] = 3, + ports: str = None + ): + """Runs nmap on a target. You cannot specify multiple targets.""" + await ctx.defer() + if len(shlex.split(target)) > 1: + return await ctx.respond("You cannot specify multiple targets.") + if not shutil.which("nmap"): + warnings.warn( + "NMAP is not installed on this system, so the /nmap command is not enabled. " + "If you would like to enable it, install nmap, or mount the binary as a volume in docker." + ) + return await ctx.respond("Nmap is not installed on this system.") + + is_superuser = os.getuid() == 0 + if technique != "T" and not is_superuser: + return await ctx.respond("Only `TCP Connect` can be used on this system.") + if enable_os_detection and not is_superuser: + return await ctx.respond("OS detection is not available on this system.") + + with tempfile.TemporaryDirectory(prefix=f"nmap-{ctx.user.id}-{ctx.message.created_at.timestamp():.0f}") as tmp: + tmp_dir = Path(tmp) + args = [ + "nmap", + "-oA", + str(tmp_dir.resolve() / target), + "-T", + str(timing), + "-s" + technique, + ] + if treat_all_hosts_online: + args.append("-Pn") + if service_scan: + args.append("-sV") + if fast_mode: + args.append("-F") + if ports: + args.extend(("-p", ports)) + if enable_os_detection: + args.append("-O") + args.append(target) + + await ctx.respond( + embed=discord.Embed( + title="Running nmap...", + description="Command:\n" + "```{}```".format(shlex.join(args)), + ) + ) + process = await asyncio.create_subprocess_exec( + *args, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + _, stderr = await process.communicate() + files = [discord.File(x, filename=x.name + ".txt") for x in tmp_dir.iterdir()] + if not files: + if len(stderr) <= 4089: + return await ctx.edit( + embed=discord.Embed( + title="Nmap failed.", + description="```\n" + stderr.decode() + "```", + color=discord.Color.red() + ) + ) + + result = await HstSHBackend().async_create_paste( + GenericFile(stderr.decode()) + ) + return await ctx.edit( + embed=discord.Embed( + title="Nmap failed.", + description=f"Output was too long. [View full output]({result.url})", + color=discord.Color.red() + ) + ) + await ctx.edit( + embed=discord.Embed( + title="Nmap finished!", + description="Result files are attached.\n" + "* `gnmap` is 'greppable'\n" + "* `xml` is XML output\n" + "* `nmap` is normal output", + color=discord.Color.green() + ), + files=files + ) + def setup(bot): bot.add_cog(NetworkCog(bot))