A fully customizable CLI application to communicate with and send commands to Ninja running behind any NAT, firewall and proxy! Providing secure shell access, file transfer and shell stream (stream shell output from remote to a local file). Jonin has no prerequisites, you can just download the release and use it right away!
Please note that Jonin is the controller (commander). You will need Ninja on the target (remote) computer(s) to host and execute commands.
This CLI application provides a fully customizable tool to manage Ninja instances, connect to them, execute commands, download and upload files, and use the shell stream feature (stream shell output from Ninja to Jonin/local computer)
- Download
- Features
- Demo
- Platforms
- Ninja
- Setup
- Usage
- Configuration File
- Host Setup
- Record Ninja Camera And Mic
- Use As Spyware
- Source Code
You can download the latest release from here
- Secure shell access to remote (Ninja) computer
- File upload/download (multiple files at once) to/from remote (Ninja)
- Shell stream: run a command on remote (Ninja) and stream output to a file on local (Jonin)
demo.mp4
![]() |
![]() |
![]() |
|---|---|---|
| Windows 10 ✔ | macOS 12 Monterey ✔ | Parrot OS 4.11.2 ✔ |
Ninja should be running on the target computer in order to control it with Jonin. PORT configuration should be the same on Ninja and Jonin, and Ninja's HOST should point to Jonin to establish a connection.
Follow these steps to set up Jonin and Ninja:
- Download the Jonin Release and Ninja Release
- Change PORT in
config/constants.jsonfor both Ninja and Jonin; the ports should be the same. Please read this guide about how to set up HOST so you never lose access to Ninja - Expose Jonin to the internet — either with ngrok (easiest) or by forwarding the configured port on Jonin's router (guide from No-IP)
- Done! Now run Ninja on the target/remote computer, run Jonin on the controller/local computer, and wait for a report from your Ninja!
For usage guide and list of commands, check below
Note: NO_LOG in config file SHOULD be set to true when you want to use Ninja as a service, otherwise, the log file might grow larger forever (up to the limit)
If you did everything above and got No Ninjas, then the chances are your ISP is putting you behind a NAT. To check this, you can find your router's WAN IP address (can be found on the router's homepage) and then compare it to the actual IP address that you have on the internet (can be found by searching my ip on Google); if these IP addresses are NOT identical, then your router is behind a NAT. To fix this, you should ask your ISP to change your NAT type to OPEN.
There are few command types available that each of them has their own commands. Here's a full instruction:
(Note that you can customize all these commands in config/constants.json)
-
use this command to change command type (list of all types)
-
clear console
-
close Jonin
-
Ninja management command. using this command type, you can see list of your Ninja s, check their details and connect to them
note: you should list Ninja s first in order to use commands that require index
Usage:
-
listList all Ninjas
-
expand <index>display all details of the Ninja with index < index > from Ninja list
-
connect <index>connect to the Ninja with index < index > from Ninja list
-
disconnectdisconnect from Ninja
Example:
-
listshow list of Ninjas -
expand 1display all details of Ninja Ninja with index 1 from Ninja list
-
connect 1connect to Ninja Ninja with index 1 from Ninja list
-
-
This is a direct shell access to Ninja. You can type any command and see the output
note: you can press ESC at any time to kill current shell and start a new one
Usage:
any valid commandExample:
ping 8.8.8.8diskparttraceroute
-
Same as
cmd, except this one takes 2 local file paths and pipes the command output and error from Ninja to these files respectivelynote: you can press Esc at any time to end transfer
Usage:
@<command>@<output_file>@[<error_file>@]-
<command>any valid command -
<output_file>a valid local file path to pipe process output -
<error_file>a valid local file path to pipe process error
Example:
-
@tracert 8.8.8.8@G:/trace.txt@pipe trace route result from Ninja to G:/trace.txt)
-
@ffmpeg -f gdigrab -framerate 30 -i desktop -f matroska -@G:/desktop.mkv@G:/desktop-err.txt@record Ninja desktop and pipe it to G:/desktop.mkv, also pipe any process error to G:/desktop-err.txt
-
-
Upload one or more files from local to Ninja
note: you can press Esc at any time to end transfer
Usage:
@<local_file_path>|<remote_file_path>@[...]-
<local_file_path>a valid local (source) file path -
<remote_file_path>a valid file path on Ninja (destination) -
[...]add more path pairs to upload multiple files at once
Example:
-
@G:/movie.mkv|C:/movie.mkv@upload movie.mkv from local's G drive to Ninja's C drive
-
@G:/movie1.mkv|C:/movie1.mkv@G:/movie2.mkv|C:/movie2.mkv@upload movie1.mkv and movie2.mkv from local's G drive to Ninja's C drive both together
-
-
Download one or more files from Ninja to local
note: you can press Esc at any time to end transfer
Usage:
@<local_file_path>|<remote_file_path>@[...]-
<local_file_path>a valid local (destination) file path -
<remote_file_path>a valid file path on Ninja (source) -
[...]add more path pairs to download multiple files at once
Example:
-
@G:/movie.mkv|C:/movie.mkv@download movie.mkv from Ninja's C drive to local's G drive
-
@G:/movie1.mkv|C:/movie1.mkv@G:/movie2.mkv|C:/movie2.mkv@download movie1.mkv and movie2.mkv from Ninja's C drive to local's G drive both together
-
-
To have some fun, you can control Ninja's disk tray
Usage:
ejectclosenote: close command is only available for Linux for now
You can find this file in config/constants.json:
When configuring Ninja, you should use a HOST that points to the Jonin computer and will always be available. So you'll have to use one of the following options:
This is the best free option for a permanent setup when you can port-forward. You just need to create an account with one of the DDNS services (like Duck DNS and No-Ip), create a domain name, and set it to point to your dynamic IP address. If your ISP changes your IP, then simply change it on the DDNS website or install a Dynamic Update Client (DUC) to do this for you automatically.
This is the easiest way to get started, especially if you can't port-forward (e.g. CGNAT) or just want to try things quickly. Since Ninja connects outbound to Jonin, run ngrok on the machine where Jonin is listening and tunnel Jonin's port to a public URL. ngrok supports the WebSocket and XHR polling that Socket.io uses, so no extra tunnel configuration is needed.
-
Start Jonin on your controller machine (default port
3707). -
In another terminal, run:
ngrok http 3707
(Replace
3707with yourPORTS.DATAvalue if you changed it.) -
Copy the ngrok hostname (e.g.
abc123.ngrok-free.app) — withouthttps://. -
In Ninja's
config/constants.json:- Set HOST to that ngrok hostname.
- Set PORTS.DATA to 443 (ngrok exposes the tunnel over HTTPS on port 443).
Jonin keeps listening on its local port (3707); only Ninja's config changes to reach it through the tunnel.
Downside: on the free tier, the ngrok URL changes every time you restart the tunnel, so you must update Ninja's HOST after each restart. For a permanent setup, use one of the options below instead. The tunnel must also stay running whenever you want Ninjas to connect.
You can purchase a VPS and use its IP or hostname as HOST in the config file. However, you'll always have to control your Ninjas from this VPS. Another downside is that this is paid.
You can use an IP address for your Jonin and set this IP in Ninja's configuration. This is not recommended since you'll have to spend money while there are easy free ways, unless you already have a static IP.
Just like a static IP, you can use a domain name for your Jonin and set this name in Ninja's configuration. For the same reason as a static IP, this is also not a recommended way.
One way to do this is to use FFmpeg. You'll need to either copy FFmpeg files into the target computer when setting up Ninja, upload it to an already set up Ninja using the upload command, or order Ninja to download it itself using shell commands.
Then, on Windows for example, you can run this:
ffmpeg -list_devices true -f dshow -i dummyto see list of DirectShow devices and then use the following cmd-stream command to record it on Jonin computer:
@ffmpeg -f dshow -i video="DIRECT_SHOW_CAMERA_FROM_LIST":audio="DIRECT_SHOW_MIC_FROM_LIST" -f matroska -@G:/cam.mkv@G:/cam-err.txt@which will save video to G:/cam.mkv and errors to G:/cam-err.txt.
Please note that Ninja can be easily used as spyware, especially when installed as a service; it will open full access to the target computer for the Jonin controlling it. So use it carefully and don't leave the Ninja process running on a computer that is connected to the internet.
Source code will be open soon, after some refactoring and improvements.





{ // connection port "PORTS": { "DATA": 3707 }, // connection config. any valid Socket.io option "CONNECTION": { "RECONNECTION_DELAY_MAX": 5000, "RECONNECTION_DELAY": 1000, "TIMEOUT": 20000, "rejectUnauthorized": false }, // interval in which file receiver party will send ack to sender "FILE_TRANSFER": { "ACK_INTERVAL": 2000 }, // command type names "COMMAND_TYPES": { "CMD": "cmd", "TRAY": "tray", "INSTANCE_MANAGEMENT": "manage", "UPLOAD": "upload", "DOWNLOAD": "download", "CMD_STREAM": "cmd-stream" }, "INSTANCE_MANAGEMENT_COMMANDS": { "LIST": "list", "CONNECT": "connect", "DISCONNECT": "disconnect", "EXPAND": "expand" }, "TRAY_COMMANDS": { "EJECT": "eject", "CLOSE": "close" }, "CONTROL_COMMANDS": { "CHANGE_COMMAND_TYPE": "change", "CLEAR": ["cls", "clear"], "EXIT": ["/exit"], "HELP": "#help" }, "CONTROL_KEYS": { // control key to force end streams, including // upload, download and shell stream "END_STREAM": { // key display name, can be anything "NAME": "Esc", // JavaScript keypress event key name "CODE": "escape" }, // control key to restart remote shell process "RESTART_REMOTE_SHELL": { "NAME": "Esc", "CODE": "escape" } }, // separators used in commands like download and upload "PRIMARY_SEPARATOR": "@", "SECONDARY_SEPARATOR": "|", "PROGRESS_BAR": { // progress bar colors "COLOR_MAP": { "FAILED": ["red", "red"], "INVALID": ["red", "red"], "DONE": ["gray", "green"], "IN_PROGRESS": ["gray", "cyan"] }, // progress bar name/label maximum length "MAX_NAME_LENGTH": 10 } }