From 0bb3a125aa382c936b29b4fb7fca11a728c3155a Mon Sep 17 00:00:00 2001 From: pyro Date: Thu, 27 Aug 2026 12:23:18 -0500 Subject: [PATCH] fixed a bug on new project creation where the config file path wasn't being passed correctly. Also started the groundwork for adding more of my rust based tooling to tetanus. --- Cargo.lock | 598 +++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/funcs.rs | 60 +++- src/lib.rs | 844 ++++++++++++++++++++++++++++++++++++++-------- src/rust_tools.rs | 88 +++++ src/server.rs | 45 +++ 6 files changed, 1469 insertions(+), 167 deletions(-) create mode 100644 src/rust_tools.rs diff --git a/Cargo.lock b/Cargo.lock index fdf9228..300ddfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aead" version = "0.6.1" @@ -329,6 +335,20 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto_generate_cdp" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "359220d0b9360b79d17d648d0a3ba1e792ec36bdbc227c8fd0351df3a0415704" +dependencies = [ + "convert_case 0.8.0", + "proc-macro2", + "quote", + "serde", + "serde_json", + "ureq", +] + [[package]] name = "autocfg" version = "1.5.1" @@ -364,6 +384,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bit-set" version = "0.5.3" @@ -670,6 +696,15 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.10.0" @@ -719,6 +754,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -826,14 +870,38 @@ dependencies = [ "cmov", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + [[package]] name = "darling" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", ] [[package]] @@ -849,13 +917,24 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.117", +] + [[package]] name = "darling_macro" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core", + "darling_core 0.23.0", "quote", "syn 2.0.117", ] @@ -895,6 +974,37 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.117", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -910,7 +1020,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case", + "convert_case 0.10.0", "proc-macro2", "quote", "rustc_version", @@ -1088,6 +1198,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1100,6 +1220,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -1232,6 +1361,29 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +[[package]] +name = "headless_chrome" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5754ca220578ad74a5d5174c8ca2ff956fd3b94025f870844de1e910b47dfee5" +dependencies = [ + "anyhow", + "auto_generate_cdp", + "base64 0.22.1", + "derive_builder", + "log", + "rand 0.10.2", + "regex", + "serde", + "serde_json", + "tempfile", + "thiserror 2.0.18", + "tungstenite", + "url", + "which", + "winreg", +] + [[package]] name = "heck" version = "0.5.0" @@ -1268,6 +1420,22 @@ dependencies = [ "digest", ] +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + [[package]] name = "hybrid-array" version = "0.4.14" @@ -1277,12 +1445,116 @@ dependencies = [ "typenum", ] +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -1327,7 +1599,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" dependencies = [ - "darling", + "darling 0.23.0", "indoc", "proc-macro2", "quote", @@ -1448,6 +1720,12 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + [[package]] name = "litrs" version = "1.0.0" @@ -1465,9 +1743,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.31" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lru" @@ -1524,6 +1802,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.1" @@ -1839,10 +2127,16 @@ version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ - "base64", + "base64 0.22.1", "serde_core", ] +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pest" version = "2.8.6" @@ -1992,12 +2286,30 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -2046,6 +2358,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + [[package]] name = "rand" version = "0.10.2" @@ -2057,12 +2379,31 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "rand_core" version = "0.10.1" @@ -2199,9 +2540,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -2211,9 +2552,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -2222,9 +2563,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rhai" @@ -2317,6 +2658,7 @@ dependencies = [ "aws-lc-rs", "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -2482,6 +2824,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + [[package]] name = "sha2" version = "0.10.9" @@ -2530,6 +2883,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + [[package]] name = "siphasher" version = "1.0.3" @@ -2579,12 +2938,29 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2724,7 +3100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "bitflags 2.12.1", "fancy-regex", "filedescriptor", @@ -2769,6 +3145,7 @@ dependencies = [ "clipboard", "crossterm", "fs_extra", + "headless_chrome", "ipnet", "keyring", "rand 0.10.2", @@ -2888,6 +3265,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" version = "1.52.3" @@ -2987,6 +3374,22 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.5", + "sha1", + "thiserror 2.0.18", +] + [[package]] name = "typenum" version = "1.20.1" @@ -3061,6 +3464,60 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d" +dependencies = [ + "base64 0.23.1", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "socks", + "ureq-proto", + "utf8-zero", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613" +dependencies = [ + "base64 0.23.1", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -3175,6 +3632,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "wezterm-bidi" version = "0.2.3" @@ -3247,6 +3713,15 @@ dependencies = [ "wezterm-dynamic", ] +[[package]] +name = "which" +version = "8.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae2f2b2b816647a1cab1acc91f5bd20812d53cb344382635ec2181940c8034f" +dependencies = [ + "libc", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3492,12 +3967,28 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + [[package]] name = "x11-clipboard" version = "0.3.3" @@ -3545,6 +4036,29 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + [[package]] name = "zbus" version = "5.16.0" @@ -3637,12 +4151,66 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + [[package]] name = "zeroize" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index 358a108..df967ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ clap = { version = "4.6.1", features = ["derive"] } clipboard = "0.5.0" crossterm = "0.29.0" fs_extra = "1.3.0" +headless_chrome = "1.0.22" ipnet = "2.12.0" keyring = "4.1.6" rand = "0.10.2" diff --git a/src/funcs.rs b/src/funcs.rs index 2bd78cd..f687373 100644 --- a/src/funcs.rs +++ b/src/funcs.rs @@ -49,6 +49,7 @@ pub fn startup( let response = get_user_input("which config would you like to load?")?; if let Some(path) = selections.get(&response) { app.load_config(PathBuf::from(path), true)?; + println!("entering tui..."); run_tui(app, rx, handles.clone())?; } else { eprintln!("error invalid selection! exiting..."); @@ -56,11 +57,13 @@ pub fn startup( } } else { app.load_config(state.config_file.clone(), true)?; + println!("entering tui..."); run_tui(app, rx, handles.clone())?; } if nested { let (tx, rx) = channel(); state.main_tx = tx; + println!("returning to prevous tui..."); run_tui(state, rx, handles.clone())?; } Ok(()) @@ -475,6 +478,35 @@ pub fn run_tui( ); } "CMD" => { + if let Some((cmd, data)) = + data.split_once("|") + { + match cmd.trim() { + "SET_NAME" => { + locked_server + .name = data + .trim() + .to_string(); + } + "LIST_PROJECTS" => { + let _ = ToolMessage::Input((locked_server.client_id.clone(), "list_projects".to_string())); + } + _ => { + let msg = format!( + "MSG FROM: {}", + data.trim() + ); + let _ = tx_clone.send( + ToolMessage::Output(( + locked_server + .client_id + .clone(), + msg, + )), + ); + } + } + } if data.contains("SET_NAME") { if let Some((_, name)) = data.split_once("|") @@ -485,7 +517,7 @@ pub fn run_tui( } } else { let _ = tx_clone.send( - ToolMessage::Input(( + ToolMessage::Output(( locked_server .client_id .clone(), @@ -680,13 +712,33 @@ pub fn run_tui( } } else { if args == "" { - let _ = state.execute_command(command, None, 0); + match state.execute_command(command, None, 0) { + Ok(_) => {} + Err(e) => { + let _ = state.main_tx.send( + ToolMessage::Output(( + 0, + e.to_string(), + )), + ); + } + } } else { - let _ = state.execute_command( + match state.execute_command( command, Some(args.to_string()), 0, - ); + ) { + Ok(_) => {} + Err(e) => { + let _ = state.main_tx.send( + ToolMessage::Output(( + 0, + e.to_string(), + )), + ); + } + } } } } diff --git a/src/lib.rs b/src/lib.rs index d559766..e4254e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,13 +14,17 @@ use std::io::{self, BufRead, BufReader, Read, Write}; use std::net::IpAddr; use std::net::TcpStream; use std::path::PathBuf; -use std::process::{Command, CommandArgs, Stdio}; +use std::process::{Command, Stdio}; use std::sync::mpsc::Receiver; use std::sync::{Arc, Mutex, mpsc::Sender, mpsc::channel}; use std::time::Duration; use std::time::{self, Instant}; +use walkdir::WalkDir; + +use crate::rust_tools::rustwitness; pub mod funcs; +pub mod rust_tools; pub mod server; pub mod victim; @@ -242,6 +246,10 @@ impl AppState { .to_string(), ); self.help.push("parse_scope\nparse the projects general.md notes file for the scope copied from the workbook\n".to_string()); + self.help.push("parse_portscan | pps\nParse a services.tsv file exported from cobalt stirke in your current project's files directory".to_string()); + self.help + .push("stop_db\nrestart this project's distrobox".to_string()); + self.help.push("rustwitness\nRun a scan on the hosts in the urls.txt file in yoru project files directory that captures screenshots of those urls, optionally through a proxy.".to_string()); self.help.push("exit\nquit the tool\n".to_string()); self.initialize_modules(); return Ok(()); @@ -392,6 +400,10 @@ impl AppState { } else { out_vec.push(format!("status: upcoming")); } + out_vec.push(format!( + "Num hosts: {}", + &self.projects[self.selected_project].hosts.iter().count() + )); for line in out_vec { let _ = self.main_tx.send(ToolMessage::Output((rid, line))); } @@ -438,7 +450,7 @@ impl AppState { let project = self.projects[self.selected_project].clone(); let mut config_file = project.config_folder.clone(); config_file.pop(); - if let Err(e) = remove_dir_all(&project.config_folder) { + if let Err(e) = remove_dir_all(config_file) { let _ = self.main_tx.send(ToolMessage::Output(( rid, format!( @@ -947,6 +959,75 @@ impl AppState { }); } } + "collab_with" => { + if let Some(args) = command_args { + if let Ok(id) = args.trim().parse::() { + if let Some(server) = self.servers.get(self.selected_server) { + if let Ok(mut lock) = server.lock() { + lock.action_que.push(format!("COLLAB|{}", id)); + } + } + } + } + } + "collab" => { + if let Some(args) = command_args { + if let Some(server) = self.servers.get(self.selected_server) { + if let Ok(mut lock) = server.lock() { + lock.action_que.push(format!("CMD|{}", args)); + } + } + } + } + "parse_port_scan" | "pps" => { + self.parse_cs_portscan()?; + } + "stop_db" => { + if let Some(project) = self.projects.get_mut(self.selected_project) { + if let Some(db) = project.db.as_mut() { + db.stop(self.main_tx.clone(), rid)?; + } + } + } + "rustwitness" => { + if let Some(project) = self.projects.get(self.selected_project) { + if let Some(args) = command_args { + if let Some((outfile, proxy)) = args.split_once(" ") { + let mut proxy_string = None; + if let Some((ip, port)) = proxy.split_once(" ") { + proxy_string = Some(format!("socks5://{}:{}", ip, port)); + } + WalkDir::new(&project.files) + .into_iter() + .filter(|res| res.is_ok()) + .for_each(|res| { + if let Ok(entry) = res { + if entry.file_name().to_string_lossy().contains("urls.txt") + { + let input = PathBuf::from(entry.path()); + let mut output = project.files.clone(); + output.push(outfile.trim()); + let proxy_intput = proxy_string.clone(); + let tx = self.main_tx.clone(); + self.workers.spawn(move || { + rustwitness(input, output, proxy_intput, tx) + }); + } + } + }); + } + } else { + let _ = self.main_tx.send(ToolMessage::Output(( + 0, + "[error] incorrect command usage!".to_string(), + ))); + let _ = self.main_tx.send(ToolMessage::Output(( + 0, + "usage: rustwitness output_file_name optional_proxy_ip optional_proxy_port".to_string(), + ))); + } + } + } "exit" => { self.save_all()?; let _ = self.main_tx.send(ToolMessage::AppStateExit); @@ -966,7 +1047,7 @@ impl AppState { .module_loader .asts .get(command_name) - .ok_or_else(|| format!("Command not found: {}", command_name))? + .ok_or_else(|| format!("[error] Command not found: {}", command_name))? .clone(); let engine = Arc::clone(&self.module_loader.engine); let cmd_meta = self @@ -1001,12 +1082,28 @@ impl AppState { } scope.push("config", map); } + "distrobox" => { + if let Some(db) = self.projects[self.selected_project].db.clone() { + scope.push("distrobox", db); + } + } _ if arg_requirement.starts_with("string") => { let input_val = command_args .clone() .unwrap_or_else(|| self.curent_intput.clone()); scope.push("input_string", input_val); } + _ if arg_requirement.starts_with("bool") => { + let mut input_val = false; + if command_args + .clone() + .unwrap_or_else(|| self.curent_intput.clone()) + .contains("true") + { + input_val = true; + } + scope.push("input_bool", input_val); + } _ => {} } } @@ -1053,7 +1150,7 @@ impl AppState { Err(err) => { let _ = tx.send(ToolMessage::Output(( rid, - format!("Script [error] in execution: {}", err), + format!("[error] in script execution: {}", err), ))); } } @@ -1079,32 +1176,58 @@ impl AppState { name: String, rid: usize, ) -> Result<(), Box> { + let mut main_config_file = self.config_file.clone(); + main_config_file.pop(); + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("{} is the main config folder.", main_config_file.display()), + ))); let template_box = self.config.get("template_box").unwrap(); let project_files = PathBuf::from(self.config.get("upcoming_files").unwrap()) .join(format!("{}/{}", org, name)); + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("{} is the project_files folder", project_files.display()), + ))); let project_notes = PathBuf::from(self.config.get("upcoming_notes").unwrap()) .join(format!("{}/{}", org, name)); - let mut template_path = self.config_file.clone(); - let mut project_conf_folder = self.config_file.clone(); + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("{} is the project_notes folder", project_notes.display()), + ))); + let mut template_path = main_config_file.clone(); + let mut project_conf_folder = main_config_file.clone(); let tools = self.config.get("tools").unwrap(); - template_path.pop(); template_path.push("note_templates"); - project_conf_folder.pop(); + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!("{} is the note template folder", template_path.display()), + ))); project_conf_folder.push(format!("projects/{}-{}", org, name)); + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "{} is the project config folder", + project_conf_folder.display() + ), + ))); let mut options = CopyOptions::new(); options.overwrite = true; - match create_dir_all(&project_files) { - Ok(_) => {} - Err(e) => { - let _ = self.main_tx.send(ToolMessage::Output(( - rid, - format!( - "[error] making project files! {e} on {}", - project_files.display() - ), - ))); + if !project_files.exists() { + match create_dir_all(&project_files) { + Ok(_) => {} + Err(e) => { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "[error] making project files! {e} on {}", + project_files.display() + ), + ))); + } } } + if !project_notes.exists() {} match create_dir_all(&project_notes) { Ok(_) => {} Err(e) => { @@ -1117,18 +1240,22 @@ impl AppState { ))); } } - match create_dir_all(&project_conf_folder) { - Ok(_) => {} - Err(e) => { - let _ = self.main_tx.send(ToolMessage::Output(( - rid, - format!( - "[error] making config folder! {e} on {}", - project_conf_folder.display() - ), - ))); + if !project_conf_folder.exists() { + match create_dir_all(&project_conf_folder) { + Ok(_) => {} + Err(e) => { + let _ = self.main_tx.send(ToolMessage::Output(( + rid, + format!( + "[error] making config folder! {e} on {}", + project_conf_folder.display() + ), + ))); + } } } + let mut project_config_file = project_conf_folder.clone(); + project_config_file.push("project.conf"); for entry in read_dir(template_path)? { let entry = entry?; let template_name_os = entry.file_name(); @@ -1179,11 +1306,20 @@ impl AppState { notes: project_notes, files: project_files, hosts: Vec::new(), - config_folder: project_conf_folder, + config_folder: project_config_file, current: false, db: Some(db), scope: Vec::new(), }; + match new_project.save_config() { + Ok(_) => {} + Err(e) => { + let _ = self.main_tx.send(ToolMessage::Output(( + 0, + format!("[error] saving project! {e}"), + ))); + } + } new_project.save_config()?; self.projects.push(new_project); return Ok(()); @@ -1458,6 +1594,381 @@ impl AppState { self.prompt.execute_command.clear(); Ok(()) } + + pub fn parse_cs_portscan(&mut self) -> Result<(), Box> { + if let Some(project) = self.projects.get_mut(self.selected_project) { + let walk = WalkDir::new(project.files.clone()); + walk.into_iter().for_each(|res| { + if let Ok(entry) = res { + if entry.file_name().to_string_lossy().contains("services.tsv") { + if let Ok(scan) = read_to_string(entry.path()) { + let mut url_lines = Vec::new(); + let mut web_host_lines = Vec::new(); + let mut rdp_host_lines = Vec::new(); + let mut ftp_host_lines = Vec::new(); + let mut telnet_host_lines = Vec::new(); + let mut winrm_host_lines = Vec::new(); + let mut mysql_host_lines = Vec::new(); + let mut mssql_host_lines = Vec::new(); + let mut snmp_host_lines = Vec::new(); + let mut kerberos_host_lines = Vec::new(); + let mut ldap_host_lines = Vec::new(); + let mut smb_host_lines = Vec::new(); + let mut unknonw_host_lines = Vec::new(); + let mut ssh_host_lines = Vec::new(); + scan.lines().into_iter().for_each(|line| { + if line.len() > 0 { + let data: Vec<&str> = line.split_whitespace().collect(); + match data[1].trim() { + "80" | "443" | "8080" | "8443" | "8000" | "4433" => { + let http_line = + format!("http://{}:{}", data[0], data[1]); + let https_line = + format!("https://{}:{}", data[0], data[1]); + url_lines.push(http_line); + url_lines.push(https_line); + web_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "3389" => { + rdp_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "21" => { + ftp_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "23" => { + telnet_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "22" => { + ssh_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "5985" | "5986" => { + winrm_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "3306" => { + mysql_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "1433" => { + mssql_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "161" => { + snmp_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "88" => { + kerberos_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "389" | "636" => { + ldap_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + "445" | "139" => { + smb_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + _ => { + unknonw_host_lines.push(data[0].to_string()); + let mut existing = false; + project + .hosts + .iter_mut() + .filter(|h| h.ip == data[0].to_string()) + .for_each(|h| { + h.add_port(data[1]); + existing = true; + }); + if !existing { + let mut new_host = Host::new(); + new_host.ip = data[0].trim().to_string(); + new_host.add_port(data[1]); + project.add_host(new_host); + } + } + } + } + }); + if !url_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("urls.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", url_lines.join("\n")); + } + } + if !web_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("web_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", web_host_lines.join("\n")); + } + } + if !rdp_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("rdp_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", rdp_host_lines.join("\n")); + } + } + if !ftp_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("ftp_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", ftp_host_lines.join("\n")); + } + } + if !telnet_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("telnet_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", telnet_host_lines.join("\n")); + } + } + if !winrm_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("winrm_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", winrm_host_lines.join("\n")); + } + } + if !mysql_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("mysql_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", mysql_host_lines.join("\n")); + } + } + if !mssql_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("mssql_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", mssql_host_lines.join("\n")); + } + } + if !snmp_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("snmp_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", snmp_host_lines.join("\n")); + } + } + if !kerberos_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("kerberos_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", kerberos_host_lines.join("\n")); + } + } + if !ldap_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("ldap_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", ldap_host_lines.join("\n")); + } + } + if !smb_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("smb_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", smb_host_lines.join("\n")); + } + } + if !unknonw_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("unknown_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", unknonw_host_lines.join("\n")); + } + } + if !ssh_host_lines.is_empty() { + let mut file_path = project.files.clone(); + file_path.push("ssh_hosts.txt"); + if let Ok(mut file) = File::create(file_path) { + let _ = write!(file, "{}", ssh_host_lines.join("\n")); + } + } + } + } + } + }); + project.save_config()?; + } + Ok(()) + } } #[derive(Clone, Debug)] @@ -1632,8 +2143,18 @@ impl Project { self.hosts = hosts; } - pub fn add_host(&mut self, host: Host) { - self.hosts.push(host); + pub fn add_host(&mut self, mut host: Host) { + let mut new_id = 0; + self.hosts.iter().for_each(|h| { + if h.id > new_id { + new_id = h.id.clone(); + } + }); + new_id += 1; + if !self.hosts.iter().any(|h| h.ip == host.ip) { + host.id = new_id; + self.hosts.push(host); + } } pub fn config_folder(&mut self, path: PathBuf) { @@ -1681,11 +2202,11 @@ impl Project { println!("{} | {} config loaded!", self.org_name, self.name); println!("loading hosts..."); } - let mut hosts_folder = self.config_folder.clone(); - let mut users_folder = self.config_folder.clone(); - hosts_folder.pop(); + let mut conf_folder = self.config_folder.clone(); + conf_folder.pop(); + let mut hosts_folder = conf_folder.clone(); + let mut users_folder = conf_folder.clone(); hosts_folder.push("hosts"); - users_folder.pop(); users_folder.push("users"); let mut users = HashMap::new(); if users_folder.exists() { @@ -1710,48 +2231,56 @@ impl Project { } } if hosts_folder.exists() { - for entry in read_dir(hosts_folder)? { - let entry = entry?; - let host_path = entry.path(); - let mut new_host = Host::new(); - let host_conf_text = read_to_string(host_path)?; - for line in host_conf_text.lines() { - if line.contains(": ") { - let (setting, data) = line.split_once(": ").unwrap(); - match setting.trim() { - "ip" => new_host.ip = data.trim().to_string(), - "hostname" => new_host.hostname = data.trim().to_string(), - "control_port" => new_host.control_port = data.trim().parse()?, - "pwned" => new_host.pwned = data.trim().parse()?, - "id" => new_host.id = data.trim().parse()?, - "findings" => { - new_host.findings = data - .trim() - .split(",") - .map(|finding| finding.to_string()) - .collect::>() - } - "open_ports" => { - new_host.open_ports = data - .trim() - .split(",") - .into_iter() - .map(|port| port.parse().unwrap()) - .collect::>() - } - "users" => { - let names: Vec<&str> = data.trim().split(",").collect(); - names.into_iter().for_each(|name| { - if let Some(user) = users.get(name) { - new_host.users.push(user.clone()); + read_dir(hosts_folder)?.into_iter().for_each(|res| { + if let Ok(entry) = res { + let host_path = entry.path(); + let mut new_host = Host::new(); + println!("loading {}...", host_path.display()); + if let Ok(host_conf_text) = read_to_string(host_path) { + host_conf_text.lines().into_iter().for_each(|line| { + if line.contains(": ") { + let (setting, data) = line.split_once(": ").unwrap(); + match setting.trim() { + "ip" => new_host.ip = data.trim().to_string(), + "hostname" => new_host.hostname = data.trim().to_string(), + "control_port" => { + new_host.control_port = data.trim().parse().unwrap(); } - }); + "pwned" => { + new_host.pwned = data.trim().parse().unwrap(); + } + "id" => new_host.id = data.trim().parse().unwrap(), + "findings" => { + new_host.findings = data + .trim() + .split(",") + .map(|finding| finding.to_string()) + .collect::>() + } + "open_ports" => { + new_host.open_ports = data + .trim() + .split(",") + .into_iter() + .map(|port| port.parse().unwrap_or_default()) + .collect::>() + } + "users" => { + let names: Vec<&str> = data.trim().split(",").collect(); + names.into_iter().for_each(|name| { + if let Some(user) = users.get(name) { + new_host.users.push(user.clone()); + } + }); + } + _ => {} + } } - _ => {} - } + }); + self.add_host(new_host); } } - } + }); } if display { println!("{} | {} loaded!", self.org_name, self.name); @@ -1760,16 +2289,29 @@ impl Project { } pub fn save_config(&mut self) -> Result<(), Box> { - create_dir_all(&self.config_folder)?; - let mut hosts_folder = self.config_folder.clone(); + let mut conf_folder = self.config_folder.clone(); + conf_folder.pop(); + if !conf_folder.exists() { + match create_dir_all(&conf_folder) { + Ok(_) => {} + Err(e) => { + return Err( + format!("save_config: couldn't create project conf folder! {e}").into(), + ); + } + } + } + let mut hosts_folder = conf_folder.clone(); hosts_folder.push("hosts"); - let mut users_folder = self.config_folder.clone(); + let mut users_folder = conf_folder.clone(); users_folder.push("users"); - create_dir_all(&hosts_folder)?; - create_dir_all(&users_folder)?; - let mut conf_file = self.config_folder.clone(); - conf_file.push("project.conf"); - let mut file = File::create(conf_file)?; + if !hosts_folder.exists() { + create_dir_all(&hosts_folder)?; + } + if !users_folder.exists() { + create_dir_all(&users_folder)?; + } + let mut file = File::create(&self.config_folder)?; let mut out_string = format!( "org_name: {}\nname: {}\nnotes: {}\nfiles: {}\nscope: {}\n", self.org_name, @@ -1784,67 +2326,13 @@ impl Project { out_string.push_str("stage: upcoming"); } file.write_all(out_string.as_bytes())?; - if !self.hosts.is_empty() { - for host in &self.hosts { - let mut host_conf_file = - File::create(hosts_folder.join(format!("{}.conf", host.id)))?; - let mut out_string = format!( - " -ip: {} -hostname: {} -control_port: {} -pwned: {} -id: {}", - host.ip, host.hostname, host.control_port, host.pwned, host.id, - ); - if !host.findings.is_empty() { - out_string.push_str(&format!("\nfindings: ")); - out_string.push_str(&host.findings.join(",")); - } - if !host.open_ports.is_empty() { - out_string.push_str(&format!("\nopen_ports: ")); - out_string.push_str( - &host - .open_ports - .iter() - .map(|port| port.to_string()) - .collect::>() - .join(","), - ); - } - if !host.users.is_empty() { - out_string.push_str(&format!("\nusers: ")); - out_string.push_str( - &host - .users - .iter() - .map(|user| user.name.clone()) - .collect::>() - .join(","), - ); - for user in &host.users { - let mut user_conf_file = - File::create(users_folder.join(format!("{}.conf", user.name)))?; - let mut user_string = format!("username: {}", &user.name); - user_string.push_str(&format!("\ncompromised: {}", user.compromised)); - if user.hash.is_some() { - let hash = user.hash.clone().unwrap(); - user_string.push_str(&format!("hash: {hash}")); - } - if user.password.is_some() { - let password = user.password.clone().unwrap(); - user_string.push_str(&format!("password: {password}")); - } - if user.ticket.is_some() { - let ticket = user.ticket.clone().unwrap().display().to_string(); - user_string.push_str(&format!("ticket: {ticket}")); - } - user_conf_file.write_all(user_string.as_bytes())?; - } - } - host_conf_file.write_all(out_string.as_bytes())?; - } - } + self.hosts + .iter_mut() + .try_for_each(|h| -> Result<(), Box> { + let mut host_path = hosts_folder.clone(); + host_path.push(format!("{}.conf", h.id)); + h.save_config_file(host_path) + })?; return Ok(()); } } @@ -1922,8 +2410,12 @@ impl Host { self.findings = findings; } - pub fn add_port(&mut self, port: usize) { - self.open_ports.push(port); + pub fn add_port(&mut self, port: &str) { + if let Ok(port) = port.trim().parse() { + if !self.open_ports.contains(&port) { + self.open_ports.push(port); + } + } } pub fn add_user(&mut self, user: User) { @@ -1933,6 +2425,37 @@ impl Host { pub fn add_finding(&mut self, finding: String) { self.findings.push(finding); } + + pub fn save_config_file(&mut self, path: PathBuf) -> Result<(), Box> { + let mut host_file = File::create(path)?; + let mut out_string = format!( + " +ip: {} +hostname: {} +control_port: {} +pwned: {} +id: {}", + self.ip, self.hostname, self.control_port, self.pwned, self.id + ); + if !self.findings.is_empty() { + out_string.push_str(&format!("\nfindings: ")); + out_string.push_str(&self.findings.join(",")); + } + if !self.open_ports.is_empty() { + out_string.push_str("\nopen_ports: "); + out_string.push_str( + &self + .open_ports + .iter() + .map(|port| port.to_string()) + .collect::>() + .join(","), + ); + } + write!(host_file, "{}", out_string)?; + host_file.sync_all()?; + Ok(()) + } } #[derive(Clone, Debug)] @@ -2034,12 +2557,12 @@ impl ModuleLoader { engine.register_fn("get_host", |hosts: &mut Vec, index: i64| { hosts.get(index as usize).cloned().unwrap_or_else(Host::new) }); - engine.register_fn("Host", Host::new); + engine.register_fn("new_host", Host::new); engine.register_fn("open_file", |path: &str| { std::fs::read_to_string(path).unwrap_or_else(|_| String::new()) }); engine.register_fn("add_port", |host: &mut Host, port: i64| { - host.add_port(port as usize); + host.add_port(format!("{}", port).as_str()); }); engine.register_fn("expand_target", |s: &str| -> rhai::Array { let mut results = rhai::Array::new(); @@ -2096,6 +2619,13 @@ impl ModuleLoader { } return out_string; }); + engine.register_fn("db_run", |p: &mut Project, command: String| -> String { + let mut out_string = format!("No distrobox configured for selected project."); + if let Some(db) = p.db.clone() { + out_string = db.run_command(&command); + } + return out_string; + }); Self { engine: Arc::new(engine), commands: HashMap::new(), @@ -2317,7 +2847,6 @@ impl DistroBox { let reader = BufReader::new(stdout); for line in reader.lines().flatten() { let _ = tx_out.send(ToolMessage::Output((rid, line.clone()))); - println!("{}", line); } }); } @@ -2327,7 +2856,6 @@ impl DistroBox { let reader = BufReader::new(stderr); for line in reader.lines().flatten() { let _ = tx_out.send(ToolMessage::Output((rid, line.clone()))); - println!("{}", line); } }); } @@ -2382,6 +2910,26 @@ impl DistroBox { let _ = term_cmd.spawn(); } } + + pub fn run_command(&self, cmd: &str) -> String { + let output = Command::new("distrobox") + .arg("enter") + .arg("-e") + .arg(format!("\"{}\"", cmd)) + .output(); + match output { + Ok(output) => { + if output.status.success() { + return String::from_utf8_lossy(&output.stdout).to_string(); + } else { + return format!("[error] {}", String::from_utf8_lossy(&output.stdout)); + } + } + Err(e) => { + return format!("[error] {e}"); + } + } + } } #[derive(Clone)] diff --git a/src/rust_tools.rs b/src/rust_tools.rs new file mode 100644 index 0000000..300da64 --- /dev/null +++ b/src/rust_tools.rs @@ -0,0 +1,88 @@ +use crate::*; +use anyhow::Context; +use headless_chrome::{Browser, LaunchOptions}; +use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; + +pub fn rustwitness( + input: PathBuf, + output: PathBuf, + proxy: Option, + tx: Sender, +) { + fn capture_screenshot( + url: &str, + proxy: Option, + output: PathBuf, + ) -> Result<(), Box> { + if !output.exists() { + create_dir_all(&output)?; + } + let mut launch_options = LaunchOptions::default(); + let file_name = url.split("://").collect::>()[1].to_string(); + if let Some(ref proxy_url) = proxy { + let proxy_arg = format!("--proxy-server={}", proxy_url); + launch_options + .args + .push(std::ffi::OsStr::new(Box::leak(proxy_arg.into_boxed_str()))); + } + let browser = Browser::new(launch_options).context("Browser launch failed")?; + let tab = browser.new_tab().context("Failed to open tab")?; + tab.navigate_to(url).context("Navigation failed")?; + tab.wait_until_navigated() + .context("Waiting for load failed")?; + tab.wait_for_element("body")?; + tab.evaluate("document.readyState === 'complete'", false)?; + let mut file_path = output.clone(); + let mut log_path = output.clone(); + log_path.push("log.txt"); + file_path.push(format!("{}.png", file_name)); + let png_data = tab + .capture_screenshot( + headless_chrome::protocol::cdp::Page::CaptureScreenshotFormatOption::Png, + None, + None, + true, + ) + .context("Screenshot capture failed")?; + + fs::write(&file_path, png_data) + .context("Failed to write file") + .context(format!( + "failed to write png file! {}", + &file_path.display() + ))?; + println!("Successfully captured: {}", url); + fs::write(&log_path, format!("{}\n", url).as_bytes()) + .context("failed to write log file!")?; + Ok(()) + } + match File::open(&input) { + Ok(file) => { + let urls: Vec = BufReader::new(file) + .lines() + .filter_map(|line| line.ok()) + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(); + let _ = tx.send(ToolMessage::Output(( + 0, + format!("Starting capture of {} urls...", urls.len()), + ))); + urls.par_iter().enumerate().for_each(|(_index, url)| { + if let Err(e) = capture_screenshot(url, proxy.clone(), output.clone()) { + let _ = tx.send(ToolMessage::Output(( + 0, + format!("[error] rustwitness: Failed to process {}: {:?}", url, e), + ))); + } + }); + let _ = tx.send(ToolMessage::Output(( + 0, + "[success] all urls scanned for screenshots!".to_string(), + ))); + } + Err(e) => { + let _ = tx.send(ToolMessage::Output((0, format!("[error] rustwitness {e}")))); + } + } +} diff --git a/src/server.rs b/src/server.rs index e1a0293..8fcea99 100644 --- a/src/server.rs +++ b/src/server.rs @@ -22,6 +22,7 @@ use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer}; enum ClientAction { Output(String), Cmd(String), + Collab(String), } pub struct Client { @@ -31,6 +32,7 @@ pub struct Client { actions: Vec, controlling: usize, controlled: usize, + collabing: Vec, output_que: Vec, logged_in: bool, timer: Duration, @@ -201,6 +203,7 @@ where actions: Vec::new(), controlled: 0, controlling: 0, + collabing: Vec::new(), output_que: Vec::new(), logged_in: false, timer: Duration::from_secs(5), @@ -312,6 +315,26 @@ where dest_client.id, cmd ))); } + else{ + let mut colabs = Vec::new(); + lock.clients.iter_mut().filter(|c| c.id == source_id).for_each(|c|{ + if c.collabing.is_empty(){ + c.actions.push(ClientAction::Output("Error no controlled or collabed clients associated with you!".to_string())); + } + else{ + c.collabing.iter().for_each(|id|{ + colabs.push(id.clone()); + }); + } + }); + if !colabs.is_empty(){ + colabs.iter().for_each(|id|{ + lock.clients.iter_mut().filter(|c| &c.id == id).for_each(|c|{ + c.actions.push(ClientAction::Cmd(data.to_string())); + }); + }); + } + } } "CONTROL" => { let new_control_id = data.trim().parse::().unwrap(); @@ -429,6 +452,22 @@ where c.remove = true; }); } + "COLLAB" => { + let dest_id: usize = data.trim().parse().unwrap(); + lock.clients + .iter_mut() + .filter(|c| c.id == source_id) + .for_each(|c| { + c.collabing.push(dest_id.clone()); + }); + lock.clients + .iter_mut() + .filter(|d| d.id == dest_id) + .for_each(|d| { + d.collabing.push(dest_id.clone()); + }); + println!("{} is collabing with {}", source_id, dest_id); + } "SLEEP" => { if let Ok(secs) = data.parse::() { lock.clients @@ -455,6 +494,9 @@ where println!("adding {} to output", text); messages.push(format!("OUTPUT|{}", text)); } + ClientAction::Collab(text) => { + println!("todo"); + } } } let full_message = messages.join("||"); @@ -523,6 +565,9 @@ where println!("adding {} to output", text); messages.push(format!("OUTPUT|{}", text)); } + ClientAction::Collab(text) => { + println!("todo"); + } } } let full_message = messages.join("||");