Compare commits
1024 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5255ae700 | |||
| 7c7c33c718 | |||
| 186532e12b | |||
| 2b1a041e7f | |||
| bfd8736640 | |||
| 566e6e224c | |||
| cefa100ba6 | |||
| 202f0c3aed | |||
| 4d1e0610b1 | |||
| bb5d5fc83e | |||
| 4febfaee47 | |||
| 9ecfda2291 | |||
| 3e3bd6f3cb | |||
| b5de6062ba | |||
| 81f09afd0d | |||
| 33f67cdf7b | |||
| 4a1b40be68 | |||
| 719ee27d6b | |||
| 8e1f8e5f5e | |||
| 7f27bc26a2 | |||
| f8faabac01 | |||
| 70c02d4dd2 | |||
| 6b620d7dac | |||
| 5da2d19089 | |||
| 9b0f804a08 | |||
| b32825d2c5 | |||
| 45fea7c79b | |||
| 3311d4a6ea | |||
| a816f81cd1 | |||
| 141836832d | |||
| 96c31c5b97 | |||
| 5ba05231a9 | |||
| fcc0e0e662 | |||
| 5724bb7350 | |||
| 025f6b2984 | |||
| 1622afc503 | |||
| a4a8998592 | |||
| cd77ed50eb | |||
| 5278c2f3e1 | |||
| 131ec681e7 | |||
| cc5582cada | |||
| d04c433715 | |||
| a7c2b95ac1 | |||
| e7c2771539 | |||
| 291414c34f | |||
| 5aae215bca | |||
| afcb6a9a2d | |||
| 318ecdc65f | |||
| 6bb0d94235 | |||
| 834005d2be | |||
| 409e2a6812 | |||
| 682b26120f | |||
| d1a98fd7de | |||
| 20cdf09aec | |||
| bf447b17ea | |||
| 5eb8cd5bb9 | |||
| 8b19841ce9 | |||
| 7162e40d2e | |||
| 714b4120f1 | |||
| beb1c7d20e | |||
| 5ad26a99cd | |||
| 7528d4e774 | |||
| d090041d17 | |||
| 5746f5374b | |||
| ae9b9bcc67 | |||
| 0ce8b5c65a | |||
| 35641b0612 | |||
| bc3f0626f5 | |||
| 416474da66 | |||
| 9bb9c22e65 | |||
| 1f47c4854d | |||
| ac216919e0 | |||
| 5c03326c39 | |||
| f6fa96d2de | |||
| dafaddac51 | |||
| 5d12c15b67 | |||
| 0e4f237496 | |||
| 5c522b3a2f | |||
| 48591142a6 | |||
| 84fb31ed2d | |||
| 9143b2c020 | |||
| a42086d9cb | |||
| 76791222bb | |||
| 08e6cfd89f | |||
| 510e729ffa | |||
| d811d754f0 | |||
| 94d0c13134 | |||
| a5dfba2554 | |||
| 549ce6b67e | |||
| ddb5c5a738 | |||
| caa9d029da | |||
| 53dcbcf423 | |||
| 4a76407edb | |||
| 9af50e91b8 | |||
| 269bdd547a | |||
| 52f3329739 | |||
| f6b4e83b12 | |||
| 98e4b0dcca | |||
| 9b239db040 | |||
| d5271a947e | |||
| 49a4fe0b3f | |||
| a052d2cb8a | |||
| e127658a69 | |||
| 88ca3f9e7b | |||
| 13485d6d9e | |||
| bb8a72903f | |||
| 0cb1897fe2 | |||
| 54b9ad88ad | |||
| 593f5c0548 | |||
| 3ab143df99 | |||
| d34a2b2091 | |||
| a1e47b80a3 | |||
| 56010d9712 | |||
| bf63268e5e | |||
| 209090123c | |||
| ea4be2e106 | |||
| 3d02910124 | |||
| e3779b3692 | |||
| c766465036 | |||
| 1f8d520de6 | |||
| 79c61e76f6 | |||
| 221879f4e4 | |||
| 1f62caa4f4 | |||
| 9ee1203aab | |||
| c381c909cd | |||
| 20501a88c7 | |||
| 03db33cd49 | |||
| 239cd39a11 | |||
| 2cd04d57ce | |||
| b535736c9c | |||
| ef55a70a9b | |||
| f20bfdda9b | |||
| 1fad5a3bef | |||
| 401588b1c1 | |||
| 542bd2e048 | |||
| d86fdb283a | |||
| 4fe646030d | |||
| 23bcd76bc3 | |||
| a3ca55c7a5 | |||
| 0d366a872d | |||
| ae6f8d4ec5 | |||
| c5c3d7a70d | |||
| 94e729dd6c | |||
| 023a1209c9 | |||
| 6e58e7792b | |||
| 6ded309a8c | |||
| 5d59cb378d | |||
| ec0e808ab5 | |||
| ad01fa1278 | |||
| dcb18f0b19 | |||
| b6efc20140 | |||
| 6678455f32 | |||
| 82e0180a17 | |||
| 0421a51460 | |||
| ea3a7f2ad1 | |||
| 7f72557a4f | |||
| e75aa25d20 | |||
| 22214f979d | |||
| faf56a7ff8 | |||
| 6e9c5b4f74 | |||
| 8849b9cf15 | |||
| 17d46ee153 | |||
| 68d8987eea | |||
| bcb59beb72 | |||
| d0a5f3d08e | |||
| 112d06f748 | |||
| 8f988a441c | |||
| 926a3955f6 | |||
| 504a9c88e8 | |||
| ef054085cd | |||
| 5f14d492ca | |||
| fec72d2082 | |||
| 0f2b365a25 | |||
| 4ef874e4ae | |||
| 158fc04d9f | |||
| 1bd78c2469 | |||
| 3fbbd1dc25 | |||
| de3ed19541 | |||
| 2428017d29 | |||
| b10370e23b | |||
| f9f8133009 | |||
| 904a2d13d7 | |||
| c6a83497be | |||
| 2ff74b14de | |||
| 26ce32c6b3 | |||
| 9132876eab | |||
| e05b05a591 | |||
| a8c9686ccb | |||
| 10cdfdffb8 | |||
| 7b9dee6608 | |||
| 90477b336f | |||
| 332e5ce98b | |||
| 14f9fe1bfd | |||
| 6f77b44235 | |||
| 70a1758597 | |||
| a34dab708b | |||
| 19724eb42f | |||
| e26faeee75 | |||
| 275ca92533 | |||
| 4ec679fbfe | |||
| 40d6f9394d | |||
| 67b8a42b93 | |||
| e9f6256e3f | |||
| ee9ce8f0d5 | |||
| d387329fff | |||
| 5e2da897d5 | |||
| 887a2fbd29 | |||
| c56937caf2 | |||
| bc3084b016 | |||
| 1a110bc707 | |||
| 3b69bc741f | |||
| 8f7ea692b2 | |||
| 6a831c07ab | |||
| b1e1a107f3 | |||
| a53e1aea16 | |||
| 5ae6f7f673 | |||
| 21a33b183b | |||
| 04b9488f09 | |||
| e4750ecb9d | |||
| 204150c0ea | |||
| b12691e86a | |||
| d39423ef6a | |||
| 760fad82af | |||
| bdf329713c | |||
| f01f642aaf | |||
| b1a42dcf16 | |||
| 53ce7591f3 | |||
| 7d99424e50 | |||
| 171757de64 | |||
| a9828f265b | |||
| 56f50697d6 | |||
| 414fe0614c | |||
| ba48f0c733 | |||
| 0191b25923 | |||
| 78ec725862 | |||
| 8ad4b03072 | |||
| 9813a0be6e | |||
| bd41bba92d | |||
| 30423e7bf5 | |||
| 685d9ff5d2 | |||
| 78a1f14c91 | |||
| aee53f572f | |||
| c1ce4cbf3c | |||
| a1631bf6fc | |||
| 899e243a3a | |||
| add9a864b2 | |||
| 9d639008ea | |||
| aa7cec7fbb | |||
| aa34cb87d3 | |||
| 306f9ce6c2 | |||
| 15983edfc5 | |||
| caa0de01e9 | |||
| 235e459313 | |||
| 9958674d79 | |||
| d207496718 | |||
| 4e57c1b428 | |||
| 92e0833a79 | |||
| badaa3c595 | |||
| bc78e5950e | |||
| d6e2c4c725 | |||
| 8de9f0aa6e | |||
| 499826bb72 | |||
| 634eb9adf2 | |||
| e91f61d2f9 | |||
| 05ac53b8d5 | |||
| c98b96b9a0 | |||
| 476364d2ca | |||
| c8603728e3 | |||
| b2d7ac7fc5 | |||
| b9acd6bed9 | |||
| 967587f3c6 | |||
| 8be01aa2e6 | |||
| c2985352b1 | |||
| 63a81d3c0c | |||
| 3407a84b8d | |||
| d0d6492147 | |||
| e91f57bc0d | |||
| af472aa27c | |||
| b529bf25f4 | |||
| 7ba73dfb9f | |||
| 6510a09c63 | |||
| fa5529f0ce | |||
| acac99d13c | |||
| fabfd9ec37 | |||
| d88523104d | |||
| d3e22611fd | |||
| bdbc0e0941 | |||
| 772e29544a | |||
| 866ab420dc | |||
| 9c85745a3a | |||
| b35e882227 | |||
| 33b4c88422 | |||
| f587435ab1 | |||
| cbca6349e4 | |||
| d59d826f01 | |||
| 4f90548d8a | |||
| ada147e8cd | |||
| 2e5f5f6fe9 | |||
| f752aa496b | |||
| 4bf37ca5d5 | |||
| a2dcc6dde5 | |||
| f4ee39b7c2 | |||
| 93991fb66c | |||
| 4bb613353d | |||
| 3f87bc21a2 | |||
| b311480e2a | |||
| 160d6e8af3 | |||
| 6c79a8167e | |||
| e703227261 | |||
| 0b8ae0659d | |||
| 9df44674bb | |||
| 0a2c8bf647 | |||
| 6062eb22dc | |||
| eefa1ecd5b | |||
| 2189af8805 | |||
| 3b74f932a2 | |||
| 9b4615e324 | |||
| 575879c88c | |||
| ea9249a939 | |||
| 173b9fc72c | |||
| 01a93d44a9 | |||
| dc1ca6cffc | |||
| a4d6872921 | |||
| 20019fc9f7 | |||
| 6ba4e2ed60 | |||
| 18d242772d | |||
| e2384a1f5c | |||
| 2ae7c0ef05 | |||
| d2c0d467ea | |||
| 119eef93d7 | |||
| 463c3d9173 | |||
| d4d6b93d54 | |||
| 48bfd80268 | |||
| d159ece6a4 | |||
| 6b8a609a9c | |||
| b52552698a | |||
| 6172238ebf | |||
| b3d9a66646 | |||
| 6042d0d21f | |||
| f90066002f | |||
| 39352ffcb7 | |||
| 28917bc4f6 | |||
| e7a4cda136 | |||
| e53a77d81a | |||
| 786df87bdc | |||
| 6af40c553e | |||
| b4ea31561f | |||
| 9847ca6ecc | |||
| 86dd6192a7 | |||
| d9c629d49e | |||
| f344d7abcc | |||
| 3970d07030 | |||
| 3e035da2a6 | |||
| 754417d8a9 | |||
| 3f5e416c0a | |||
| 1f795f93c1 | |||
| 7ed462b1bb | |||
| 9b27df4fc3 | |||
| f27eead476 | |||
| 42b1bc441d | |||
| 8834a87f01 | |||
| ea5a9bf674 | |||
| 9f72a3af32 | |||
| 0a770916cf | |||
| e056834f1d | |||
| f8f3ad0352 | |||
| 55826df728 | |||
| 4752a10ac6 | |||
| 2afb2b1782 | |||
| 681608a25a | |||
| cc5c97e4ce | |||
| 2d7dba361c | |||
| 6a0df57dd7 | |||
| 53abb0fc90 | |||
| 5f418b5e56 | |||
| ecc1698372 | |||
| 113c41f4ae | |||
| 97f13b3851 | |||
| 0fb8f96272 | |||
| 0ba89de2c3 | |||
| f24e34034c | |||
| 17aeb02ce2 | |||
| 15e96800fa | |||
| 6bec2152b8 | |||
| 8e2d787036 | |||
| daba572a6a | |||
| 482c7d6ba2 | |||
| 4b7bd3dcbc | |||
| 2a990ce3c6 | |||
| 65683db837 | |||
| 78eb6ae5fe | |||
| 56793e38ee | |||
| a6faf5262f | |||
| ef652aa3fa | |||
| 240fd51083 | |||
| c645a58263 | |||
| bb80ae357d | |||
| 5d7bdbbcea | |||
| d9714c039b | |||
| cbe3da1d66 | |||
| 5e75502c48 | |||
| a688c93e21 | |||
| 1e6945713c | |||
| fb49298c0b | |||
| cda94eaea8 | |||
| 814062b908 | |||
| 694444ea04 | |||
| b8a9e0cc82 | |||
| ef74ef0e07 | |||
| 9d4e58db8c | |||
| fc087647ca | |||
| e16b7dada0 | |||
| 1b61060141 | |||
| fedf0daf76 | |||
| 15027ef82e | |||
| 091167acb2 | |||
| 75ac27b07a | |||
| 8c9342ba54 | |||
| 93834c8bf1 | |||
| 83935fd804 | |||
| 14a9ed49ce | |||
| 835fe530fe | |||
| 78cf95e173 | |||
| 6db0150c37 | |||
| 5eba97ebce | |||
| 0ed89ef595 | |||
| f27ace02a1 | |||
| 014ca2b788 | |||
| d571288fc0 | |||
| 649ad736c5 | |||
| 4c14166a1d | |||
| 3a44fa926c | |||
| e668ebfbd5 | |||
| b7e9c7e781 | |||
| b7de3f8ea6 | |||
| 8a035c76ca | |||
| 4f462fcd60 | |||
| 69ce692ff1 | |||
| d7f7c54b1a | |||
| ecc91f99f7 | |||
| e2729848d9 | |||
| 00cf8e1f71 | |||
| 0c312391cf | |||
| 365a2ec7f7 | |||
| f5e88e0b05 | |||
| 08db52e946 | |||
| ded438f750 | |||
| 368969ccc7 | |||
| 6da4bfa0c0 | |||
| 573a0ba47b | |||
| 876e2fe39d | |||
| 3714cacce6 | |||
| 7e4a8a3f23 | |||
| a7199adcb8 | |||
| 7aa27dbec8 | |||
| e9cffc9e7b | |||
| 0f5deac1ac | |||
| fe06fd5c42 | |||
| 07f436929e | |||
| 5bb70c37f8 | |||
| 91f272f4b6 | |||
| ca8d34ee8d | |||
| 62d8b76c21 | |||
| 3a935c201d | |||
| e5b0783d34 | |||
| e0b1e0193a | |||
| b003bcf90f | |||
| 0901bb1308 | |||
| 8e14563982 | |||
| 5f6ba7f24f | |||
| c1f0e60066 | |||
| 6e18de8922 | |||
| 37ee399af1 | |||
| 1d09a4f6c5 | |||
| ccb105b87b | |||
| a63134245b | |||
| 9a67e4932f | |||
| 34ec6f0887 | |||
| 0485c04224 | |||
| bfb7c4aeff | |||
| 0093cfdd90 | |||
| d31ade7a40 | |||
| 8880f13157 | |||
| 9f0ada9ef3 | |||
| dda116b9e4 | |||
| 85f9770c97 | |||
| 6de0776c0b | |||
| f42e02daa0 | |||
| 584323677d | |||
| b9e3ba334a | |||
| e90cf5da01 | |||
| 3cd9ce1eaa | |||
| f99f4a62c5 | |||
| 502bca446d | |||
| 3047417a0d | |||
| 171440ae59 | |||
| 771502cc2c | |||
| a7bd4e2360 | |||
| 5cd85726d5 | |||
| d09459bea7 | |||
| c6fb8802c7 | |||
| ef063d8778 | |||
| 52f6a35dbc | |||
| 5c4222f939 | |||
| 515cf5e432 | |||
| f8ca3743ae | |||
| ef2edff0b7 | |||
| 561411288a | |||
| 6384e56280 | |||
| e8de02df35 | |||
| 6b21072ba2 | |||
| bab3f77555 | |||
| 7bb007f44f | |||
| 6f505b2fe0 | |||
| 750d39e6b6 | |||
| 0ac9cac1bb | |||
| 04c91dcacf | |||
| 1c44c1b55e | |||
| d8546cbe1f | |||
| 8b7e323ce9 | |||
| 9a0c4733dc | |||
| 68e7d6d76d | |||
| 5f2371bc55 | |||
| abbad040a3 | |||
| 3497bbdb68 | |||
| 3242149c13 | |||
| 6df4e8df40 | |||
| 9b81453d40 | |||
| 314b391c52 | |||
| 0258636827 | |||
| f23bb9b42e | |||
| 274edba91f | |||
| d6aee35a8d | |||
| a7530b6cbe | |||
| ec4a81c812 | |||
| 654b006a5b | |||
| ebf60407f8 | |||
| f08f60d9b7 | |||
| 26fe3e4e4d | |||
| 2b7eae3a0a | |||
| fc7a5353d9 | |||
| 74d5354ead | |||
| ebd55faa9e | |||
| 3de588385b | |||
| 05c2b42499 | |||
| a03395cda2 | |||
| 8da083794f | |||
| 8acae6c5d6 | |||
| 823b50aa63 | |||
| e3d60063dc | |||
| 038b91b52f | |||
| 5854c7ffa8 | |||
| f0e3a343bf | |||
| 6d1e8d8fb1 | |||
| ddd8f3dbbb | |||
| 8e0daa48a1 | |||
| 4f45c72d03 | |||
| 7dfa8874ba | |||
| c12d70b56a | |||
| 88e825a123 | |||
| 69744cf7cb | |||
| 739518eac9 | |||
| 9033b82896 | |||
| 6728c3f545 | |||
| c8a8cd3f0f | |||
| bf1fb2e5c9 | |||
| 9a6c2492f1 | |||
| 0e3584d906 | |||
| 2535b62dbe | |||
| 9710d2bf8d | |||
| d824433885 | |||
| d2c90c8c52 | |||
| 5fc6c8faad | |||
| 0af745f948 | |||
| 98d6e6d515 | |||
| 8d2af5d1e5 | |||
| baf6f8e20b | |||
| 117d4eff85 | |||
| d8989f7a86 | |||
| 83b4f1a865 | |||
| db5664c383 | |||
| e3037671d4 | |||
| ca218d78ee | |||
| 3bde010133 | |||
| 8920a52a0f | |||
| 5d64f69348 | |||
| e09c8b9dfc | |||
| fa5d25b8fd | |||
| 9fd678e1bd | |||
| ba09528944 | |||
| f211c9c2e3 | |||
| 82073d4f84 | |||
| 8d3c39335d | |||
| f09576ea26 | |||
| 2486714b7b | |||
| 21f57aaf07 | |||
| 588d397cf7 | |||
| 2fd767fb6f | |||
| 99af838fcb | |||
| e16916dc94 | |||
| 06c4f141d5 | |||
| 2c883fad50 | |||
| 1a65897d81 | |||
| 0d0f14d8e3 | |||
| b49d2242aa | |||
| 2d9b7924ab | |||
| 040b478ae7 | |||
| 611717ff3c | |||
| 0f954c79a7 | |||
| e76b2d039c | |||
| 433ad720a2 | |||
| 55326f01db | |||
| 190ab6049c | |||
| 58e926a149 | |||
| 2b98d841d4 | |||
| dbc6581de7 | |||
| 7fcf135456 | |||
| 656b646fc1 | |||
| d91e8f429d | |||
| edebcc6b92 | |||
| 428f9ca83d | |||
| 3ec24630db | |||
| e4a23fc28c | |||
| 84a7530268 | |||
| a9e2ceaf50 | |||
| 27d6710a21 | |||
| a1171b1655 | |||
| febfdc732a | |||
| 13e14c4f76 | |||
| 8d03f29503 | |||
| efa9a175ca | |||
| 366d79bab5 | |||
| a77bfde23b | |||
| 23e9a1316b | |||
| 358d2bc672 | |||
| 30ae7ff21b | |||
| f91f151234 | |||
| 1e234c9e67 | |||
| 240422631e | |||
| 9c71ea54ba | |||
| ba97a70970 | |||
| 6c37ecaff7 | |||
| 2e1e56774f | |||
| b445ce29ed | |||
| a6390d59cc | |||
| 269237dd8e | |||
| aa4021b7d5 | |||
| 4a7fab3822 | |||
| 3b3e5551e2 | |||
| 1e44b46b17 | |||
| 686325b386 | |||
| e736f6b0b0 | |||
| dd7935b69c | |||
| 74f7077c51 | |||
| 04722df95a | |||
| 8bafdeffe1 | |||
| 69e7efa02c | |||
| 29b12196c4 | |||
| 60c34f6532 | |||
| 7b6fb65d93 | |||
| 7d39ebb014 | |||
| 30547ebc64 | |||
| 8a058ad524 | |||
| 6dd35cdfc7 | |||
| b36a9842ce | |||
| f8af4bdc7d | |||
| aee7ce727d | |||
| 1c956f5998 | |||
| 4b1aafa719 | |||
| 58d831001f | |||
| 991befb4c3 | |||
| f8b6f5ece2 | |||
| b51236ac51 | |||
| 6868e32a84 | |||
| 6431ede868 | |||
| 598f91c6f7 | |||
| 57dd7d8c1e | |||
| 046680f5f0 | |||
| f6342dce4f | |||
| 8df6d2f26d | |||
| ff7a351d78 | |||
| baa09f64b8 | |||
| 115f9f6558 | |||
| 5b8ae6b74a | |||
| ecdcad9290 | |||
| 3bff500927 | |||
| 4abdc9424d | |||
| 7a545b7d3e | |||
| 4dc58f8d33 | |||
| 3e1fe7723f | |||
| 53106bc17e | |||
| b39be9afd8 | |||
| 5529737988 | |||
| 8ba5b4d747 | |||
| 6f3cdb6ee4 | |||
| 71e5c89689 | |||
| 6180b7d2ce | |||
| 13762db3c4 | |||
| 3feb21204e | |||
| 67eff11737 | |||
| ceb051dc3b | |||
| bf94afff20 | |||
| bf0a9e92ce | |||
| f213a93a32 | |||
| 98762cbfa2 | |||
| f6eb395e4a | |||
| 453a3d049c | |||
| 0679e96cd0 | |||
| dcd2a77d06 | |||
| 1164f08f56 | |||
| f1f85f9e8c | |||
| 3269a9f724 | |||
| fd18639259 | |||
| fc9f51ec17 | |||
| 664278009f | |||
| 269d2bab2b | |||
| cc12b2d8b8 | |||
| 38e80587a9 | |||
| 15ca27421b | |||
| 67d83f6220 | |||
| c66043336f | |||
| e647fc1107 | |||
| 14e157ea3c | |||
| e9eb13edcb | |||
| 2f5e4118c1 | |||
| 41677ed1b8 | |||
| 647148b123 | |||
| 885d0124c9 | |||
| 8da256262c | |||
| 6e4ce73785 | |||
| 094fedc22a | |||
| 36bfa0b7a0 | |||
| 7ccab37f47 | |||
| 5ff1598d82 | |||
| 57ddd501b4 | |||
| 46ebb3479c | |||
| 488e9cfce7 | |||
| f75836d58d | |||
| 4c3e4bb203 | |||
| 56f6a35cd6 | |||
| 247cb65212 | |||
| 51307ae19c | |||
| 6b831c376a | |||
| 8b2c13b22b | |||
| c638acdfc7 | |||
| ece8001f00 | |||
| 8353acb404 | |||
| 8c2f10c7d7 | |||
| b60838ebee | |||
| e43c510a5f | |||
| f9b706db4e | |||
| f2ff9c166a | |||
| 6c902544b5 | |||
| 246cad2511 | |||
| 581122ac47 | |||
| 80cae7c2d2 | |||
| 41166ce1ff | |||
| 38afa8807b | |||
| e8bc8e2941 | |||
| d06f8c2a40 | |||
| cd88e34dd9 | |||
| 29e849ef1a | |||
| 5695b36c77 | |||
| 2f6e8206d8 | |||
| a7b0def3c2 | |||
| d527536192 | |||
| 8ba01b4527 | |||
| 58301560d0 | |||
| 135fce0993 | |||
| 38c4de6896 | |||
| 3f8aa8b74d | |||
| 463325e694 | |||
| 46521a8170 | |||
| ab2932642f | |||
| 1cd2082e71 | |||
| a5f51aaefc | |||
| c4eb6cdcfa | |||
| e0ca9dbea0 | |||
| 02ab78d835 | |||
| e66edb9717 | |||
| e3cb914901 | |||
| d2e350b851 | |||
| 6cc6341e3a | |||
| 2c7a7fa663 | |||
| 562dcefc7a | |||
| a21947fc5c | |||
| 65a64e3929 | |||
| 67937a6313 | |||
| 6b3636ba3b | |||
| bc4d4853ec | |||
| 2ff7d45348 | |||
| d99cf64ae8 | |||
| e1d224f8c7 | |||
| 501159f6eb | |||
| 5553bd10f4 | |||
| 2e003137de | |||
| 9386643c5d | |||
| 3220b363bf | |||
| ae8a7a65d0 | |||
| c16bcada89 | |||
| 55b9188a13 | |||
| d6a85ccd53 | |||
| fefdeef55a | |||
| 8408d538de | |||
| 4cb3c05b15 | |||
| 175e8a47f0 | |||
| b8ea1eb226 | |||
| 16f2bf4eda | |||
| eda4aa13f1 | |||
| 5cd71ca6e7 | |||
| aab9bcd3b4 | |||
| e47df26436 | |||
| 212e548486 | |||
| 9daefc9eee | |||
| d5f478654b | |||
| 7c8bd44c37 | |||
| dd952759e6 | |||
| 93c31fd826 | |||
| d013fc2042 | |||
| ac15eb567b | |||
| 9752e49e2a | |||
| d03429900b | |||
| 6f4fd16cdb | |||
| 05141046bd | |||
| 6a22a3aed9 | |||
| 05d281521d | |||
| ce43bd75de | |||
| 30bc74a9a9 | |||
| 70d3267803 | |||
| 1fb30647df | |||
| 6ff55b3693 | |||
| 207064e975 | |||
| d8aa7487d2 | |||
| 00928c367a | |||
| f5d02d74ef | |||
| 0cafe696a6 | |||
| 28c7305d52 | |||
| 0093cd0522 | |||
| 7e22e2c7cd | |||
| abb10159b9 | |||
| 8c9e69ab9d | |||
| b6c4724dcb | |||
| 9421e16826 | |||
| a94001e7e2 | |||
| 81520c43db | |||
| 59b2e4e59b | |||
| c4f77f16a9 | |||
| 85b6c28d5d | |||
| 971b5fef1e | |||
| 2840583bc1 | |||
| f64a9712f5 | |||
| a6a9c6d3fa | |||
| f323e1364c | |||
| 3e19062670 | |||
| a987b9d96c | |||
| d8a0c55fe5 | |||
| 9119e051b7 | |||
| 9f23e580e4 | |||
| 3b24db2d89 | |||
| 3f56cda716 | |||
| 1690683f41 | |||
| e662f28407 | |||
| 26d32d3cd9 | |||
| 55c4cf734a | |||
| c568f73b51 | |||
| 9c25b77aa6 | |||
| 9fbaf19f35 | |||
| e3c3965020 | |||
| 40000472a5 | |||
| cb8ac909d7 | |||
| 7627b53650 | |||
| f30f48aea2 | |||
| e66c6d7e21 | |||
| 8ede7d9943 | |||
| 0d72aefb62 | |||
| 893e6628e6 | |||
| baf394fc0b | |||
| 035963758d | |||
| d760e02289 | |||
| e6b145d563 | |||
| 7afb359918 | |||
| 707a5eda93 | |||
| 6d3c0ae873 | |||
| 476e75869c | |||
| e7110c24c5 | |||
| 0e4c7961fe | |||
| 6f0cb70e02 | |||
| 4dec25d187 | |||
| e5e24804f6 | |||
| f1b24e5674 | |||
| d0edcb9a0b | |||
| 2dbb5a1097 | |||
| 451992f07c | |||
| 5cfbf885ca | |||
| ecc1cb53dc | |||
| 39d9adb6bc | |||
| 8675dc0202 | |||
| de81a73097 | |||
| 9d6f122254 | |||
| be11d5c3a2 | |||
| 786be39398 | |||
| c92794263b | |||
| 953b7a4e9b | |||
| 88b7089d1b | |||
| d4745fe7cf | |||
| 94423d2d12 | |||
| f5476ab381 | |||
| 1cd52ce2d6 | |||
| 071c115d70 | |||
| 20eea3518a | |||
| 0a049e991d | |||
| 6ef7c96a93 | |||
| edcdaa2f05 | |||
| f7c03656bf | |||
| a9ff58061e | |||
| 5a269fc601 | |||
| 57b581c85b | |||
| dcb1d99b26 | |||
| 372a2de406 | |||
| c8fb3a2faf | |||
| c9208b61dd | |||
| e74a738565 | |||
| 5d6b7c1b7b | |||
| bc9b5b0afe | |||
| a332d3bdc4 | |||
| c3f6f0b08d | |||
| 515bd34c7e | |||
| 32330fa8b2 | |||
| 16d3530ac3 | |||
| cc10ce78d3 | |||
| f35239de40 | |||
| daaf7891d6 | |||
| 46d447392c | |||
| b4432dfd47 | |||
| 1b9b9cb11d | |||
| f4b4f06333 | |||
| fca97c7d5c | |||
| 818af18355 | |||
| 69f94193f6 | |||
| 403a3fefcb | |||
| 8ce39a9127 | |||
| f3a1b2bada | |||
| 41422f4826 | |||
| 6189bc73ac | |||
| 45d9ae2f2d | |||
| c329f6c77e | |||
| 9fa77a16b6 | |||
| 2c581c31aa | |||
| 76fc12c365 | |||
| 3413869bf1 | |||
| 84aea67aa0 | |||
| 73d92a92e1 | |||
| 9c8d7e937e | |||
| b165670ed3 | |||
| 4e6dd23129 | |||
| f0a3b44c53 | |||
| 472da35a36 | |||
| b60d86fdc3 | |||
| 9b99b9fd35 | |||
| dde3da84fd | |||
| 147f1030c3 | |||
| 43d4b90782 | |||
| dee7ee2d2c | |||
| bf8df3bc02 | |||
| 82973c295c | |||
| ac654f9bab | |||
| 310c40d7aa | |||
| b3b528966c | |||
| e31ff755f4 | |||
| 101742de3c | |||
| 25e8541964 | |||
| 1251efd752 | |||
| 61075f40ac | |||
| 39b92b79d9 | |||
| 0e88591a3b | |||
| 32b06362bd | |||
| 298ef2072c | |||
| 7390e941a7 | |||
| 1fad2a413e | |||
| de96822c0d | |||
| 4c6af1a925 | |||
| 7ccddb93f7 | |||
| 3883a14365 | |||
| 837b2f4d51 | |||
| 03126872bb | |||
| 8eaf1bd60b | |||
| 7f9fbac68b | |||
| 335f6b6c0c | |||
| f52768632a | |||
| 8d46ce0862 | |||
| 11cf46a006 | |||
| f1e2b739e1 | |||
| 3e4a067c6b | |||
| d2dc83ebb2 | |||
| c040501ee4 | |||
| 70b1ddfda2 | |||
| cf2e91bfcc | |||
| daed4d5a7c | |||
| e99497fdc7 | |||
| cb7df51209 | |||
| a3c8834f05 | |||
| 3f4336cca9 | |||
| da4e3bbd2a | |||
| 20e582135d | |||
| bfb069509e | |||
| d9c1cb3937 | |||
| f3540ee07b | |||
| 794fec26db | |||
| 46282e37a4 | |||
| 87749f1452 | |||
| 4428790766 | |||
| 51c42f2958 | |||
| aaf5863d9a | |||
| 88e084e695 | |||
| 96487aec2e | |||
| 1179f5f118 | |||
| ef3a1f5245 | |||
| f223d9cf1b | |||
| 78832b4790 | |||
| 3c065f56ae | |||
| a65840b013 | |||
| 49e4278647 | |||
| 5a324971ed | |||
| f3e76f7ee5 |
@@ -0,0 +1 @@
|
|||||||
|
.git/
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
---
|
|
||||||
name: 提交Bug
|
|
||||||
about: "请务必按照模板提交Bug\U0001F64F"
|
|
||||||
title: 请填写BUG标题
|
|
||||||
labels: bug
|
|
||||||
assignees: shmilylty
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**是否使用了最新代码**
|
|
||||||
是或否(如果不是的话尝试克隆最新的代码再跑一下)
|
|
||||||
|
|
||||||
**Bug描述**
|
|
||||||
清晰而简洁的Bug描述
|
|
||||||
|
|
||||||
**如何复现**
|
|
||||||
复现步骤(可不写)
|
|
||||||
复现命令
|
|
||||||
|
|
||||||
**预期结果**
|
|
||||||
清晰而简洁的预期结果描述(可不写)
|
|
||||||
|
|
||||||
**实际结果**
|
|
||||||
清晰而简洁的实际结果描述(如出现什么错误)
|
|
||||||
|
|
||||||
**屏幕截图**
|
|
||||||
|
|
||||||
|
|
||||||
**运行环境**
|
|
||||||
- 系统:[例如Windows 10 x64]
|
|
||||||
- Python版本:[例如3.7.1]
|
|
||||||
|
|
||||||
|
|
||||||
**报错文本**
|
|
||||||
复制完整的报错文本
|
|
||||||
@@ -1,38 +1,42 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: Please use this English template to submit Bug
|
||||||
about: Create a report to help us improve
|
about: "Be sure to submit the Bug according to the template\U0001F64F"
|
||||||
title: ''
|
title: Please fill in the BUG title
|
||||||
labels: ''
|
labels: bug
|
||||||
assignees: ''
|
assignees: shmilylty
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Whether the latest code is used**
|
||||||
A clear and concise description of what the bug is.
|
Yes or no (if not, try to clone the latest code and run again!)
|
||||||
|
|
||||||
**To Reproduce**
|
**Bug description**
|
||||||
Steps to reproduce the behavior:
|
Clear and concise Bug description(required)
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
**Operation environment**
|
||||||
A clear and concise description of what you expected to happen.
|
- System information: [e.g. Windows 10 x64] (required)
|
||||||
|
- Python version: [e.g. 3.7.1] (required)
|
||||||
|
- OneForAll version: [e.g. 0.3.0] (required)
|
||||||
|
|
||||||
|
**How to reproduce**
|
||||||
|
1. Step (optional)
|
||||||
|
|
||||||
**Screenshots**
|
2. Command (required)
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Error text**
|
||||||
- OS: [e.g. iOS]
|
Copy the complete error text (required)
|
||||||
- Browser [e.g. chrome, safari]
|
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Smartphone (please complete the following information):**
|
**Expected results**
|
||||||
- Device: [e.g. iPhone6]
|
A clear and concise description of the expected results (optional, such as what a normal situation should look like)
|
||||||
- OS: [e.g. iOS8.1]
|
|
||||||
- Browser [e.g. stock browser, safari]
|
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Additional context**
|
**Actual results**
|
||||||
Add any other context about the problem here.
|
A clear and concise description of the actual results (optional, such as any errors)
|
||||||
|
|
||||||
|
**Screenshot**
|
||||||
|
Screenshot of complete OneForAll execution process (recommended upload)
|
||||||
|
|
||||||
|
**Log upload**
|
||||||
|
Upload oneforall.log files (it is recommended to upload logs in case of complex problems)
|
||||||
|
|
||||||
|
**Supplementary information**
|
||||||
|
Some other supplementary notes about bug
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
name: 请使用这个中文模板提交Bug
|
||||||
|
about: "请务必按照模板提交Bug\U0001F64F"
|
||||||
|
title: 请填写BUG标题
|
||||||
|
labels: bug
|
||||||
|
assignees: shmilylty
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**是否使用了最新代码**
|
||||||
|
是或否(如果不是的话尝试克隆最新的代码再跑一下!)
|
||||||
|
|
||||||
|
**Bug描述**
|
||||||
|
清晰而简洁的Bug描述(必写)
|
||||||
|
|
||||||
|
**运行环境**
|
||||||
|
- 系统:[例如Windows 10 x64](必写)
|
||||||
|
- Python版本:[例如3.7.1](必写)
|
||||||
|
- OneForAll版本:[例如0.3.0](必写)
|
||||||
|
|
||||||
|
**如何复现**
|
||||||
|
复现步骤(选写)
|
||||||
|
|
||||||
|
复现命令(必写)
|
||||||
|
|
||||||
|
**报错文本**
|
||||||
|
复制完整的报错文本(必写)
|
||||||
|
|
||||||
|
**预期结果**
|
||||||
|
清晰而简洁的预期结果描述(选写,如正常情况应该是怎么样的)
|
||||||
|
|
||||||
|
**实际结果**
|
||||||
|
清晰而简洁的实际结果描述(选写,如出现什么错误)
|
||||||
|
|
||||||
|
**屏幕截图**
|
||||||
|
完整OneForAll执行流程截图(建议上传)
|
||||||
|
|
||||||
|
**日志上传**
|
||||||
|
上传oneforall.log日志文件(复杂问题建议上传)
|
||||||
|
|
||||||
|
**其他补充**
|
||||||
|
关于bug的其他一些补充说明
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
name: Custom issue template
|
|
||||||
about: Describe this issue template's purpose here.
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
name: Release Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: build and push
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out the repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Setup Docker buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_PASS }}
|
||||||
|
|
||||||
|
- name: Build the Docker image
|
||||||
|
run: docker buildx build --push --platform linux/amd64,linux/arm64 -t shmilylty/oneforall:latest .
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
name: OneForAll test
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Set up Python 3.8
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install uvloop
|
||||||
|
pip install -r requirements.txt
|
||||||
|
- name: Lint with flake8
|
||||||
|
run: |
|
||||||
|
pip install flake8
|
||||||
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
|
# - name: Test with pytest
|
||||||
|
# run: |
|
||||||
|
# pip install pytest
|
||||||
|
# pytest
|
||||||
|
- name: Test with example
|
||||||
|
run: |
|
||||||
|
pip install coverage
|
||||||
|
coverage run test.py
|
||||||
@@ -235,4 +235,8 @@ $RECYCLE.BIN/
|
|||||||
# Windows shortcuts
|
# Windows shortcuts
|
||||||
*.lnk
|
*.lnk
|
||||||
|
|
||||||
# End of https://www.gitignore.io/api/python,windows,pycharm
|
# End of https://www.gitignore.io/api/python,windows,pycharm
|
||||||
|
|
||||||
|
# User-defined settings
|
||||||
|
config/setting.py
|
||||||
|
config/api.py
|
||||||
|
|||||||
@@ -1,39 +1,102 @@
|
|||||||
sudo: true
|
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|
||||||
language: python
|
language: python
|
||||||
|
|
||||||
matrix:
|
jobs:
|
||||||
include:
|
include:
|
||||||
- name: "Python 3.6 on Linux"
|
- name: "Python 3.6 on Linux"
|
||||||
|
os: linux
|
||||||
|
dist: xenial
|
||||||
python: 3.6 # this works for Linux but is ignored on macOS or Windows
|
python: 3.6 # this works for Linux but is ignored on macOS or Windows
|
||||||
|
before_install:
|
||||||
|
- pip3 install -U pip
|
||||||
- name: "Python 3.7 on Xenial Linux"
|
- name: "Python 3.7 on Xenial Linux"
|
||||||
|
os: linux
|
||||||
|
dist: xenial # required for Python >= 3.7
|
||||||
python: 3.7 # this works for Linux but is ignored on macOS or Windows
|
python: 3.7 # this works for Linux but is ignored on macOS or Windows
|
||||||
dist: xenial # required for Python >= 3.7
|
before_install:
|
||||||
|
- pip3 install -U pip
|
||||||
- name: "Python 3.8 on Xenial Linux"
|
- name: "Python 3.8 on Xenial Linux"
|
||||||
python: 3.8-dev # this works for Linux but is ignored on macOS or Windows
|
os: linux
|
||||||
dist: xenial # required for Python >= 3.7
|
dist: xenial # required for Python >= 3.7
|
||||||
- name: "Python 3.7 on macOS"
|
python: 3.8 # this works for Linux but is ignored on macOS or Windows
|
||||||
|
before_install:
|
||||||
|
- pip3 install -U pip
|
||||||
|
- name: "Python 3.9 on Xenial Linux"
|
||||||
|
os: linux
|
||||||
|
dist: xenial # required for Python >= 3.7
|
||||||
|
python: 3.9-dev # this works for Linux but is ignored on macOS or Windows
|
||||||
|
before_install:
|
||||||
|
- pip3 install -U pip
|
||||||
|
- name: "Python 3.6 on MacOS"
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode10.2 # Python 3.7 running on macOS 10.14.3
|
osx_image: xcode9.4 # Python 3.6 running on macOS 10.13
|
||||||
language: shell # 'language: python' is an error on Travis CI macOS
|
language: shell # 'language: python' is an error on Travis CI macOS
|
||||||
|
before_install:
|
||||||
|
- python3 --version
|
||||||
|
- pip3 install -U pip
|
||||||
|
- name: "Python 3.7 on MacOS"
|
||||||
|
os: osx
|
||||||
|
osx_image: xcode10.2 # Python 3.7 running on macOS 10.14
|
||||||
|
language: shell # 'language: python' is an error on Travis CI macOS
|
||||||
|
before_install:
|
||||||
|
- python3 --version
|
||||||
|
- pip3 install -U pip
|
||||||
|
- name: "Python 3.8 on MacOS"
|
||||||
|
os: osx
|
||||||
|
osx_image: xcode12.2 # Python 3.7 running on macOS 10.15
|
||||||
|
language: shell # 'language: python' is an error on Travis CI macOS
|
||||||
|
before_install:
|
||||||
|
- python3 --version
|
||||||
|
- pip3 install -U pip
|
||||||
|
- name: "Python 3.6 on Windows"
|
||||||
|
os: windows
|
||||||
|
language: shell
|
||||||
|
before_install:
|
||||||
|
- choco install python --version 3.6.1
|
||||||
|
- python -m pip install --upgrade pip
|
||||||
|
- chcp.com 65001
|
||||||
|
env:
|
||||||
|
- PATH=/c/Python36:/c/Python36/Scripts:$PATH
|
||||||
|
- PYTHONIOENCODING=UTF-8
|
||||||
- name: "Python 3.7 on Windows"
|
- name: "Python 3.7 on Windows"
|
||||||
|
os: windows
|
||||||
|
language: shell
|
||||||
|
before_install:
|
||||||
|
- choco install python --version 3.7.0
|
||||||
|
- python -m pip install --upgrade pip
|
||||||
|
- chcp.com 65001
|
||||||
|
env:
|
||||||
|
- PATH=/c/Python37:/c/Python37/Scripts:$PATH
|
||||||
|
- PYTHONIOENCODING=UTF-8
|
||||||
|
- name: "Python 3.8 on Windows"
|
||||||
os: windows # Windows 10.0.17134 N/A Build 17134
|
os: windows # Windows 10.0.17134 N/A Build 17134
|
||||||
language: shell
|
language: shell
|
||||||
before_install:
|
before_install:
|
||||||
- choco install python
|
- choco install python --version 3.8.0
|
||||||
- python -m pip install --upgrade pip
|
- python -m pip install --upgrade pip
|
||||||
env: PATH=/c/Python37:/c/Python37/Scripts:$PATH
|
- chcp.com 65001
|
||||||
|
env:
|
||||||
|
- PATH=/c/Python38:/c/Python38/Scripts:$PATH
|
||||||
|
- PYTHONIOENCODING=UTF-8
|
||||||
|
- name: "Python 3.9 on Windows"
|
||||||
|
os: windows # Windows 10.0.17134 N/A Build 17134
|
||||||
|
language: shell
|
||||||
|
before_install:
|
||||||
|
- choco install python --version 3.9.0
|
||||||
|
- python -m pip install --upgrade pip
|
||||||
|
- chcp.com 65001
|
||||||
|
env:
|
||||||
|
- PATH=/c/Python39:/c/Python39/Scripts:$PATH
|
||||||
|
- PYTHONIOENCODING=UTF-8
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- pip3 install -U pip
|
|
||||||
- pip3 install codecov
|
- pip3 install codecov
|
||||||
- pip3 install -r requirements.txt
|
- pip3 install -r requirements.txt
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- coverage run oneforall/example.py
|
- coverage run test.py
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- codecov
|
- codecov
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
# 更新日志
|
|
||||||
OneForAll的所有值得注意的更改都将记录在此文件中。
|
|
||||||
|
|
||||||
OneForAll的更新日志格式基于[Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
|
||||||
|
|
||||||
OneForAll遵守[语义化版本格式](https://semver.org/)。
|
|
||||||
|
|
||||||
## Unreleased
|
|
||||||
## [0.0.4](https://github.com/shmilylty/oneforall/releases/tag/v0.0.4) - 2019-08-11
|
|
||||||
### 修复
|
|
||||||
- 修复一些已知Bugs
|
|
||||||
|
|
||||||
## [0.0.3](https://github.com/shmilylty/oneforall/releases/tag/v0.0.3) - 2019-08-08
|
|
||||||
### 修改
|
|
||||||
- 代码PEP8格式化
|
|
||||||
### 修改
|
|
||||||
- 修改一些已知Bugs
|
|
||||||
|
|
||||||
## [0.0.2](https://github.com/shmilylty/oneforall/releases/tag/v0.0.2) - 2019-08-04
|
|
||||||
### 新增
|
|
||||||
- 新增有关文档
|
|
||||||
### 修改
|
|
||||||
- 修改有关日志输出格式和信息
|
|
||||||
### 修复
|
|
||||||
- 升级fire库版本解决运行报错问题
|
|
||||||
### 移除
|
|
||||||
- 移除brotlipy依赖
|
|
||||||
|
|
||||||
|
|
||||||
## [0.0.1](https://github.com/shmilylty/oneforall/releases/tag/v0.0.1) - 2019-08-02
|
|
||||||
### 新增
|
|
||||||
- 新增检查crossdomain.xml收集子域功能
|
|
||||||
- 新增检查域名证书收集子域功能
|
|
||||||
- 新增检查内容安全策略头收集子域功能
|
|
||||||
- 新增域传送利用功能
|
|
||||||
- 新增子域收集功能(搜索引擎,DNS数据集,证书透明度,网上爬虫档案)
|
|
||||||
- 新增子域爆破功能
|
|
||||||
- 新增数据库导出功能
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# 参与者公约
|
|
||||||
|
|
||||||
## 我们的承诺
|
|
||||||
|
|
||||||
为建设开放友好的环境,我们贡献者和维护者承诺:不论年龄、体型、身体健全与否、民族、性征、性别认同与表征、经验水平、教育程度、社会地位、国籍、相貌、种族、信仰、性取向,我们项目和社区的参与者皆免于骚扰。
|
|
||||||
|
|
||||||
## 我们的准则
|
|
||||||
|
|
||||||
有助于创造积极环境的行为包括但不限于:
|
|
||||||
|
|
||||||
* 措辞友好且包容
|
|
||||||
* 尊重不同的观点和经验
|
|
||||||
* 耐心接受有益批评
|
|
||||||
* 关注对社区最有利的事情
|
|
||||||
* 与社区其他成员友善相处
|
|
||||||
|
|
||||||
参与者不应采取的行为包括但不限于:
|
|
||||||
|
|
||||||
* 发布与性有关的言论或图像、不受欢迎地献殷勤
|
|
||||||
* 捣乱/煽动/造谣行为、侮辱/贬损的评论、人身及政治攻击
|
|
||||||
* 公开或私下骚扰
|
|
||||||
* 未经明确授权便发布他人的资料,如住址、电子邮箱等
|
|
||||||
* 其他有理由认定为违反职业操守的不当行为
|
|
||||||
|
|
||||||
## 我们的义务
|
|
||||||
|
|
||||||
项目维护者有义务诠释何谓“妥当行为”,并妥善公正地纠正已发生的不当行为。
|
|
||||||
|
|
||||||
项目维护者有权利和义务去删除、编辑、拒绝违背本行为标准的评论(comments)、提交(commits)、代码、wiki 编辑、问题(issues)等贡献;项目维护者可暂时或永久地封禁任何他们认为行为不当、威胁、冒犯、有害的参与者。
|
|
||||||
|
|
||||||
## 适用范围
|
|
||||||
|
|
||||||
本行为标准适用于本项目。当有人代表本项目或本社区时,本标准亦适用于此人所处的公共平台。
|
|
||||||
|
|
||||||
代表本项目或本社区的情形包括但不限于:使用项目的官方电子邮件、通过官方媒体账号发布消息、作为指定代表参与在线或线下活动等。
|
|
||||||
|
|
||||||
代表本项目的行为可由项目维护者进一步定义及解释。
|
|
||||||
|
|
||||||
## 贯彻落实
|
|
||||||
|
|
||||||
可以致信[admin@hackfun.org],向项目团队举报滥用、骚扰及不当行为。
|
|
||||||
|
|
||||||
维护团队将审议并调查全部投诉,妥善地予以必要的回应。项目团队有义务保密举报者信息。具体执行方针或将另行发布。
|
|
||||||
|
|
||||||
未切实遵守或执行本行为标准的项目维护人员,经项目负责人或其他成员决议,可能被暂时或永久地剥夺参与本项目的资格。
|
|
||||||
|
|
||||||
## 来源
|
|
||||||
|
|
||||||
本行为标准改编自[参与者公约](https://www.contributor-covenant.org)的1.4版本,可[在此](https://www.contributor-covenant.org/zh-cn/version/1/4/code-of-conduct.html)查阅。
|
|
||||||
|
|
||||||
You can view the Contributor Covenant Translations [here](https://www.contributor-covenant.org/translations).
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Contribution Guidelines
|
|
||||||
|
|
||||||
Before opening any issues or proposing any pull requests and to get the greatest chance of helpful responses, please observe the following notes.
|
|
||||||
|
|
||||||
## Good Bug Reports
|
|
||||||
|
|
||||||
Please be aware of the following things when filing bug reports:
|
|
||||||
|
|
||||||
1. Avoid raising duplicate issues. *Please* use the GitHub issue search feature
|
|
||||||
to check whether your bug report or feature request has been mentioned in
|
|
||||||
the past. Duplicate bug reports and feature requests are a huge maintenance
|
|
||||||
burden on the limited resources of the project. If it is clear from your
|
|
||||||
report that you would have struggled to find the original, that's ok, but
|
|
||||||
if searching for a selection of words in your issue title would have found
|
|
||||||
the duplicate then the issue will likely be closed extremely abruptly.
|
|
||||||
2. When filing bug reports about exceptions or tracebacks, please include the
|
|
||||||
*complete* traceback. Partial tracebacks, or just the exception text, are
|
|
||||||
not helpful. Issues that do not contain complete tracebacks may be closed
|
|
||||||
without warning.
|
|
||||||
3. Make sure you provide a suitable amount of information to work with. This
|
|
||||||
means you should provide:
|
|
||||||
|
|
||||||
- Guidance on **how to reproduce the issue**. Ideally, this should be a
|
|
||||||
*small* code sample that can be run immediately by the maintainers.
|
|
||||||
Failing that, let us know what you're doing, how often it happens, what
|
|
||||||
environment you're using, etc. Be thorough: it prevents us needing to ask
|
|
||||||
further questions.
|
|
||||||
- Tell us **what you expected to happen**. When we run your example code,
|
|
||||||
what are we expecting to happen? What does "success" look like for your
|
|
||||||
code?
|
|
||||||
- Tell us **what actually happens**. It's not helpful for you to say "it
|
|
||||||
doesn't work" or "it fails". Tell us *how* it fails: do you get an
|
|
||||||
exception? A hang? A non-200 status code? How was the actual result
|
|
||||||
different from your expected result?
|
|
||||||
- Tell us **what version of OneForAll you're using**, and
|
|
||||||
**how you installed it**. Different versions of Requests behave
|
|
||||||
differently and have different bugs, and some distributors of Requests
|
|
||||||
ship patches on top of the code we supply.
|
|
||||||
|
|
||||||
If you do not provide all of these things, it will take us much longer to
|
|
||||||
fix your problem. If we ask you to clarify these and you never respond, we
|
|
||||||
will close your issue without fixing it.
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# OneForAll贡献者
|
|
||||||
|
|
||||||
* **[Jing Ling](https://github.com/shmilylty)**
|
|
||||||
* 核心开发
|
|
||||||
|
|
||||||
* **[Black Star](https://github.com/blackstar24)**
|
|
||||||
* 模块贡献
|
|
||||||
|
|
||||||
* [**iceMatcha**](https://github.com/iceMatcha)
|
|
||||||
* bug调试
|
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
FROM --platform=$TARGETPLATFORM python:3.8-alpine3.10
|
||||||
|
LABEL maintainer="milktea@vmoe.info"
|
||||||
|
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||||
|
RUN apk update && apk --no-cache add git build-base libffi-dev libxml2-dev libxslt-dev libressl-dev
|
||||||
|
ADD requirements.txt /requirements.txt
|
||||||
|
RUN pip install uvloop -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
RUN pip install -r /requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
RUN git clone https://github.com/blechschmidt/massdns
|
||||||
|
WORKDIR /massdns
|
||||||
|
RUN make
|
||||||
|
ADD . /OneForAll/
|
||||||
|
RUN mv /massdns/bin/massdns /OneForAll/thirdparty/massdns/massdns_linux_$(uname -m)
|
||||||
|
RUN mkdir /OneForAll/results
|
||||||
|
WORKDIR /OneForAll/
|
||||||
|
|
||||||
|
ENTRYPOINT ["python", "oneforall.py"]
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -6,21 +6,17 @@ verify_ssl = true
|
|||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
aiodns = "*"
|
|
||||||
tqdm = "*"
|
tqdm = "*"
|
||||||
aiomultiprocess = "*"
|
|
||||||
loguru = "*"
|
loguru = "*"
|
||||||
dnspython = "*"
|
dnspython = "*"
|
||||||
requests = "*"
|
|
||||||
records = "*"
|
|
||||||
tldextract = "*"
|
|
||||||
exrex = "*"
|
exrex = "*"
|
||||||
aiohttp = "*"
|
fire = "*"
|
||||||
fire = "==0.2.1"
|
|
||||||
bs4 = "*"
|
bs4 = "*"
|
||||||
cchardet = "*"
|
tenacity = "*"
|
||||||
lxml = "*"
|
treelib = "*"
|
||||||
|
sqlalchemy = "*"
|
||||||
|
requests = "*"
|
||||||
pysocks = "*"
|
pysocks = "*"
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.7"
|
python_version = "3.8"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "d95e37615ca48fe39a66e8c24eff76910c66b01b0739c0ab00ffa0f4df022107"
|
"sha256": "b593ca54685d3e36cb21706fd3ef3ee747c0d87fa7043fa483fcc30d316960eb"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
"python_version": "3.7"
|
"python_version": "3.8"
|
||||||
},
|
},
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
@@ -16,77 +16,13 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"default": {
|
"default": {
|
||||||
"aiodns": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:815fdef4607474295d68da46978a54481dd1e7be153c7d60f9e72773cd38d77d",
|
|
||||||
"sha256:aaa5ac584f40fe778013df0aa6544bf157799bd3f608364b451840ed2c8688de"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.0.0"
|
|
||||||
},
|
|
||||||
"aiohttp": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:00d198585474299c9c3b4f1d5de1a576cc230d562abc5e4a0e81d71a20a6ca55",
|
|
||||||
"sha256:0155af66de8c21b8dba4992aaeeabf55503caefae00067a3b1139f86d0ec50ed",
|
|
||||||
"sha256:09654a9eca62d1bd6d64aa44db2498f60a5c1e0ac4750953fdd79d5c88955e10",
|
|
||||||
"sha256:199f1d106e2b44b6dacdf6f9245493c7d716b01d0b7fbe1959318ba4dc64d1f5",
|
|
||||||
"sha256:296f30dedc9f4b9e7a301e5cc963012264112d78a1d3094cd83ef148fdf33ca1",
|
|
||||||
"sha256:368ed312550bd663ce84dc4b032a962fcb3c7cae099dbbd48663afc305e3b939",
|
|
||||||
"sha256:40d7ea570b88db017c51392349cf99b7aefaaddd19d2c78368aeb0bddde9d390",
|
|
||||||
"sha256:629102a193162e37102c50713e2e31dc9a2fe7ac5e481da83e5bb3c0cee700aa",
|
|
||||||
"sha256:6d5ec9b8948c3d957e75ea14d41e9330e1ac3fed24ec53766c780f82805140dc",
|
|
||||||
"sha256:87331d1d6810214085a50749160196391a712a13336cd02ce1c3ea3d05bcf8d5",
|
|
||||||
"sha256:9a02a04bbe581c8605ac423ba3a74999ec9d8bce7ae37977a3d38680f5780b6d",
|
|
||||||
"sha256:9c4c83f4fa1938377da32bc2d59379025ceeee8e24b89f72fcbccd8ca22dc9bf",
|
|
||||||
"sha256:9cddaff94c0135ee627213ac6ca6d05724bfe6e7a356e5e09ec57bd3249510f6",
|
|
||||||
"sha256:a25237abf327530d9561ef751eef9511ab56fd9431023ca6f4803f1994104d72",
|
|
||||||
"sha256:a5cbd7157b0e383738b8e29d6e556fde8726823dae0e348952a61742b21aeb12",
|
|
||||||
"sha256:a97a516e02b726e089cffcde2eea0d3258450389bbac48cbe89e0f0b6e7b0366",
|
|
||||||
"sha256:acc89b29b5f4e2332d65cd1b7d10c609a75b88ef8925d487a611ca788432dfa4",
|
|
||||||
"sha256:b05bd85cc99b06740aad3629c2585bda7b83bd86e080b44ba47faf905fdf1300",
|
|
||||||
"sha256:c2bec436a2b5dafe5eaeb297c03711074d46b6eb236d002c13c42f25c4a8ce9d",
|
|
||||||
"sha256:cc619d974c8c11fe84527e4b5e1c07238799a8c29ea1c1285149170524ba9303",
|
|
||||||
"sha256:d4392defd4648badaa42b3e101080ae3313e8f4787cb517efd3f5b8157eaefd6",
|
|
||||||
"sha256:e1c3c582ee11af7f63a34a46f0448fca58e59889396ffdae1f482085061a2889"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==3.5.4"
|
|
||||||
},
|
|
||||||
"aiomultiprocess": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:fd9b616d5145ac2b01f315725277231ffad7e56eb6675885598f6fe861ef9fa3"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.6.0"
|
|
||||||
},
|
|
||||||
"async-timeout": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
|
|
||||||
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
|
|
||||||
],
|
|
||||||
"version": "==3.0.1"
|
|
||||||
},
|
|
||||||
"attrs": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
|
|
||||||
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
|
|
||||||
],
|
|
||||||
"version": "==19.1.0"
|
|
||||||
},
|
|
||||||
"backports.csv": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1277dfff73130b2e106bf3dd347adb3c5f6c4340882289d88f31240da92cbd6d",
|
|
||||||
"sha256:21f6e09bab589e6c1f877edbc40277b65e626262a86e69a70137db714eaac5ce"
|
|
||||||
],
|
|
||||||
"version": "==1.0.7"
|
|
||||||
},
|
|
||||||
"beautifulsoup4": {
|
"beautifulsoup4": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:05668158c7b85b791c5abde53e50265e16f98ad601c402ba44d70f96c4159612",
|
"sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35",
|
||||||
"sha256:25288c9e176f354bf277c0a10aa96c782a6a18a17122dba2e8cec4a97e03343b",
|
"sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25",
|
||||||
"sha256:f040590be10520f2ea4c2ae8c3dae441c7cfff5308ec9d58a0ec0c1b8f81d469"
|
"sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"
|
||||||
],
|
],
|
||||||
"version": "==4.8.0"
|
"version": "==4.9.3"
|
||||||
},
|
},
|
||||||
"bs4": {
|
"bs4": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -95,116 +31,35 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.0.1"
|
"version": "==0.0.1"
|
||||||
},
|
},
|
||||||
"cchardet": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:079aa02a14072874d943a671ba778a9def5b0e3cedc2ac9f59308526cfb31472",
|
|
||||||
"sha256:3e048a21688dcb4c797f40c8deb3600887bcaf435620256fd8becd4252012750",
|
|
||||||
"sha256:41fced7a6f05ef859fe3eac89fc2120aca3cbbfd2b6c803bed3ee4bf02956903",
|
|
||||||
"sha256:440903d5dca3d326f4b841e7fa760b6af1be4f950ead1a6ff77b76eaa46f0cd3",
|
|
||||||
"sha256:50170f346527c5df4d3cb94648ca187c666e61c0db6e510b984e867c44709d8b",
|
|
||||||
"sha256:6c55a6e7bc7337671c9f1ad90746c0efb2b2979ff4305c7ca1d7d381f05174c1",
|
|
||||||
"sha256:7f581ea172b252034f745dfd49733966b73b73907bdef0b47ad5f2008b797d54",
|
|
||||||
"sha256:80f7b087198827e60c81574c321b12f89188eae626ae1567d66808928be42f88",
|
|
||||||
"sha256:8ba753ff73ca2f3554999a0e027eab9450f6ffdb7e92e1b4e13b52be89995349",
|
|
||||||
"sha256:9ad8f61d6d1ca37bd4b954ad92d461ea4f58d0dc413b0790a5abed7c09e54996",
|
|
||||||
"sha256:a35bd23cedbaa87cc9300af1dd10bb03fda41894045fbca7bfdf1d350b813f25",
|
|
||||||
"sha256:a8feb9a7def2310e18c27e485a21a38669abe8c2e36b93c6ce1a1363495d4cdf",
|
|
||||||
"sha256:aa9dd4cee8a5210a6d0a7b263b98dc50637e00401fc4a5ad3ce2dbef54fdfa02",
|
|
||||||
"sha256:ab9858a0673262e467619df91f425cfef0590dcf5deef5c0c7945e9dc4dbd7d8",
|
|
||||||
"sha256:b09a488bbb35be95f82845e3c4312be9025e8377975b027eee67e0b39445e070",
|
|
||||||
"sha256:b2893d558761b3534cddf5a49ba8d77df3d8f964d7b14680b925f4a85fc13476",
|
|
||||||
"sha256:b5a8f9b229a30cd2432572d15e169483bc47c24418772ff58d0585050631c2fd",
|
|
||||||
"sha256:bded54eeccd5f810bc69e076b3d9a35819a92e5e0559ad274b9ae9061b1b881d",
|
|
||||||
"sha256:cbc206061e69561af6e4cba11f99abd928346c6b5bcdc83eb32ae40e9fc23a5f",
|
|
||||||
"sha256:cc9745e0400da4cfb49f075e7819f22473b66443f953427058fee2c7b9547cc0",
|
|
||||||
"sha256:db30bf3825702c07fc55a290d41663fd8151f870642a15667bbabf81fff21e0b",
|
|
||||||
"sha256:eeeb1b95bb5851dda93ee522860a0e6066d47921cb1d540cb778346e37e5a524",
|
|
||||||
"sha256:f1c3919fb71ac5da3aeee42c5b731c99dcd2beed71db7fdc28ca993c173f0402"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.1.4"
|
|
||||||
},
|
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
|
"sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
|
||||||
"sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"
|
"sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
|
||||||
],
|
],
|
||||||
"version": "==2019.6.16"
|
"version": "==2020.12.5"
|
||||||
},
|
|
||||||
"cffi": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774",
|
|
||||||
"sha256:046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d",
|
|
||||||
"sha256:066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90",
|
|
||||||
"sha256:066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b",
|
|
||||||
"sha256:2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63",
|
|
||||||
"sha256:300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45",
|
|
||||||
"sha256:34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25",
|
|
||||||
"sha256:46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3",
|
|
||||||
"sha256:4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b",
|
|
||||||
"sha256:4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647",
|
|
||||||
"sha256:4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016",
|
|
||||||
"sha256:50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4",
|
|
||||||
"sha256:55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb",
|
|
||||||
"sha256:5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753",
|
|
||||||
"sha256:59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7",
|
|
||||||
"sha256:73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9",
|
|
||||||
"sha256:a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f",
|
|
||||||
"sha256:a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8",
|
|
||||||
"sha256:a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f",
|
|
||||||
"sha256:a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc",
|
|
||||||
"sha256:ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42",
|
|
||||||
"sha256:b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3",
|
|
||||||
"sha256:d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909",
|
|
||||||
"sha256:d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45",
|
|
||||||
"sha256:dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d",
|
|
||||||
"sha256:e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512",
|
|
||||||
"sha256:e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff",
|
|
||||||
"sha256:ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201"
|
|
||||||
],
|
|
||||||
"version": "==1.12.3"
|
|
||||||
},
|
},
|
||||||
"chardet": {
|
"chardet": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
|
||||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
|
||||||
],
|
],
|
||||||
"version": "==3.0.4"
|
"version": "==4.0.0"
|
||||||
},
|
},
|
||||||
"colorama": {
|
"colorama": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d",
|
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
|
||||||
"sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"
|
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
|
||||||
],
|
],
|
||||||
"markers": "sys_platform == 'win32'",
|
"markers": "sys_platform == 'win32'",
|
||||||
"version": "==0.4.1"
|
"version": "==0.4.4"
|
||||||
},
|
|
||||||
"defusedxml": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93",
|
|
||||||
"sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"
|
|
||||||
],
|
|
||||||
"version": "==0.6.0"
|
|
||||||
},
|
},
|
||||||
"dnspython": {
|
"dnspython": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01",
|
"sha256:95d12f6ef0317118d2a1a6fc49aac65ffec7eb8087474158f42f26a639135216",
|
||||||
"sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d"
|
"sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.16.0"
|
"version": "==2.1.0"
|
||||||
},
|
|
||||||
"docopt": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"
|
|
||||||
],
|
|
||||||
"version": "==0.6.2"
|
|
||||||
},
|
|
||||||
"et-xmlfile": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:614d9722d572f6246302c4491846d2c393c199cfa4edc9af593437691683335b"
|
|
||||||
],
|
|
||||||
"version": "==1.0.1"
|
|
||||||
},
|
},
|
||||||
"exrex": {
|
"exrex": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -215,208 +70,160 @@
|
|||||||
},
|
},
|
||||||
"fire": {
|
"fire": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:6865fefc6981a713d2ce56a2a2c92c56c729269f74a6cddd6f4b94d16ae084c9"
|
"sha256:c5e2b8763699d1142393a46d0e3e790c5eb2f0706082df8f647878842c216a62"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.2.1"
|
"version": "==0.4.0"
|
||||||
|
},
|
||||||
|
"future": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
|
||||||
|
],
|
||||||
|
"version": "==0.18.2"
|
||||||
|
},
|
||||||
|
"greenlet": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0a77691f0080c9da8dfc81e23f4e3cffa5accf0f5b56478951016d7cfead9196",
|
||||||
|
"sha256:0ddd77586553e3daf439aa88b6642c5f252f7ef79a39271c25b1d4bf1b7cbb85",
|
||||||
|
"sha256:111cfd92d78f2af0bc7317452bd93a477128af6327332ebf3c2be7df99566683",
|
||||||
|
"sha256:122c63ba795fdba4fc19c744df6277d9cfd913ed53d1a286f12189a0265316dd",
|
||||||
|
"sha256:181300f826625b7fd1182205b830642926f52bd8cdb08b34574c9d5b2b1813f7",
|
||||||
|
"sha256:1a1ada42a1fd2607d232ae11a7b3195735edaa49ea787a6d9e6a53afaf6f3476",
|
||||||
|
"sha256:1bb80c71de788b36cefb0c3bb6bfab306ba75073dbde2829c858dc3ad70f867c",
|
||||||
|
"sha256:1d1d4473ecb1c1d31ce8fd8d91e4da1b1f64d425c1dc965edc4ed2a63cfa67b2",
|
||||||
|
"sha256:292e801fcb3a0b3a12d8c603c7cf340659ea27fd73c98683e75800d9fd8f704c",
|
||||||
|
"sha256:2c65320774a8cd5fdb6e117c13afa91c4707548282464a18cf80243cf976b3e6",
|
||||||
|
"sha256:4365eccd68e72564c776418c53ce3c5af402bc526fe0653722bc89efd85bf12d",
|
||||||
|
"sha256:5352c15c1d91d22902582e891f27728d8dac3bd5e0ee565b6a9f575355e6d92f",
|
||||||
|
"sha256:58ca0f078d1c135ecf1879d50711f925ee238fe773dfe44e206d7d126f5bc664",
|
||||||
|
"sha256:5d4030b04061fdf4cbc446008e238e44936d77a04b2b32f804688ad64197953c",
|
||||||
|
"sha256:5d69bbd9547d3bc49f8a545db7a0bd69f407badd2ff0f6e1a163680b5841d2b0",
|
||||||
|
"sha256:5f297cb343114b33a13755032ecf7109b07b9a0020e841d1c3cedff6602cc139",
|
||||||
|
"sha256:62afad6e5fd70f34d773ffcbb7c22657e1d46d7fd7c95a43361de979f0a45aef",
|
||||||
|
"sha256:647ba1df86d025f5a34043451d7c4a9f05f240bee06277a524daad11f997d1e7",
|
||||||
|
"sha256:719e169c79255816cdcf6dccd9ed2d089a72a9f6c42273aae12d55e8d35bdcf8",
|
||||||
|
"sha256:7cd5a237f241f2764324396e06298b5dee0df580cf06ef4ada0ff9bff851286c",
|
||||||
|
"sha256:875d4c60a6299f55df1c3bb870ebe6dcb7db28c165ab9ea6cdc5d5af36bb33ce",
|
||||||
|
"sha256:90b6a25841488cf2cb1c8623a53e6879573010a669455046df5f029d93db51b7",
|
||||||
|
"sha256:94620ed996a7632723a424bccb84b07e7b861ab7bb06a5aeb041c111dd723d36",
|
||||||
|
"sha256:b5f1b333015d53d4b381745f5de842f19fe59728b65f0fbb662dafbe2018c3a5",
|
||||||
|
"sha256:c5b22b31c947ad8b6964d4ed66776bcae986f73669ba50620162ba7c832a6b6a",
|
||||||
|
"sha256:c93d1a71c3fe222308939b2e516c07f35a849c5047f0197442a4d6fbcb4128ee",
|
||||||
|
"sha256:cdb90267650c1edb54459cdb51dab865f6c6594c3a47ebd441bc493360c7af70",
|
||||||
|
"sha256:cfd06e0f0cc8db2a854137bd79154b61ecd940dce96fad0cba23fe31de0b793c",
|
||||||
|
"sha256:d3789c1c394944084b5e57c192889985a9f23bd985f6d15728c745d380318128",
|
||||||
|
"sha256:da7d09ad0f24270b20f77d56934e196e982af0d0a2446120cb772be4e060e1a2",
|
||||||
|
"sha256:df3e83323268594fa9755480a442cabfe8d82b21aba815a71acf1bb6c1776218",
|
||||||
|
"sha256:df8053867c831b2643b2c489fe1d62049a98566b1646b194cc815f13e27b90df",
|
||||||
|
"sha256:e1128e022d8dce375362e063754e129750323b67454cac5600008aad9f54139e",
|
||||||
|
"sha256:e6e9fdaf6c90d02b95e6b0709aeb1aba5affbbb9ccaea5502f8638e4323206be",
|
||||||
|
"sha256:eac8803c9ad1817ce3d8d15d1bb82c2da3feda6bee1153eec5c58fa6e5d3f770",
|
||||||
|
"sha256:eb333b90036358a0e2c57373f72e7648d7207b76ef0bd00a4f7daad1f79f5203",
|
||||||
|
"sha256:ed1d1351f05e795a527abc04a0d82e9aecd3bdf9f46662c36ff47b0b00ecaf06",
|
||||||
|
"sha256:f3dc68272990849132d6698f7dc6df2ab62a88b0d36e54702a8fd16c0490e44f",
|
||||||
|
"sha256:f59eded163d9752fd49978e0bab7a1ff21b1b8d25c05f0995d140cc08ac83379",
|
||||||
|
"sha256:f5e2d36c86c7b03c94b8459c3bd2c9fe2c7dab4b258b8885617d44a22e453fb7",
|
||||||
|
"sha256:f6f65bf54215e4ebf6b01e4bb94c49180a589573df643735107056f7a910275b",
|
||||||
|
"sha256:f8450d5ef759dbe59f84f2c9f77491bb3d3c44bc1a573746daf086e70b14c243",
|
||||||
|
"sha256:f97d83049715fd9dec7911860ecf0e17b48d8725de01e45de07d8ac0bd5bc378"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3'",
|
||||||
|
"version": "==1.0.0"
|
||||||
},
|
},
|
||||||
"idna": {
|
"idna": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
|
||||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
|
||||||
],
|
],
|
||||||
"version": "==2.8"
|
"version": "==2.10"
|
||||||
},
|
|
||||||
"jdcal": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1abf1305fce18b4e8aa248cf8fe0c56ce2032392bc64bbd61b5dff2a19ec8bba",
|
|
||||||
"sha256:472872e096eb8df219c23f2689fc336668bdb43d194094b5cc1707e1640acfc8"
|
|
||||||
],
|
|
||||||
"version": "==1.4.1"
|
|
||||||
},
|
},
|
||||||
"loguru": {
|
"loguru": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b6fad0d7aed357b5c147edcc6982606b933754338950b72d8123f48c150c5a4f",
|
"sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319",
|
||||||
"sha256:e3138bfdee5f57481a2a6e078714be20f8c71ab1ff3f07f8fb1cfa25191fed2a"
|
"sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.3.2"
|
|
||||||
},
|
|
||||||
"lxml": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:06e5599b9c54f797a3c0f384c67705a0d621031007aa2400a6c7d17300fdb995",
|
|
||||||
"sha256:092237cfe4ece074401b75001a2e525fa6e1fb9d40fee8b7b132b1947d3bd2f8",
|
|
||||||
"sha256:0b6d49d0a26fe8207df8dd27c40b75be4deb2277173903aa76ec3e82df77cbe7",
|
|
||||||
"sha256:0f77061c20b4f32b1cf39e8f661c74e966344084c996e7b23c3a94e472461df0",
|
|
||||||
"sha256:0fef86edfa2f146b4b0ae2c6c05c3e4a8f3388b3655eafbc4aab3247f4dabb24",
|
|
||||||
"sha256:2f163c8844db4ed06a230ef092e2461ad01830972a896b8f3cf8b5bac70ae85d",
|
|
||||||
"sha256:350333190052bbfbc3222b1805b59b7979d7276e57af2257367e15a2db27082d",
|
|
||||||
"sha256:3b57dc5ed7b6a7d852c961f2389ca99404c2b59fd2088baec6fbaca02f688be4",
|
|
||||||
"sha256:3e86e5df4a8edd6f725f3c76f1d45e046d4f3aa40478092e4f5f373ad1f526e2",
|
|
||||||
"sha256:43dac60d10341d3e56be089cd0798b70e70d45ce32279f4c3190d8cbd71350e4",
|
|
||||||
"sha256:4665ee84ac8ba11d58f1ed517e29ea8536b4ae4e0c6fb6c7d3dce70abcd279f0",
|
|
||||||
"sha256:5033cf606a7cb559db967689b1b2e743994000f783607ba4c484e90917395ad7",
|
|
||||||
"sha256:75d731af05bf40f808d7716e0d26b4b02913402f861c032ce8c36efca350ae72",
|
|
||||||
"sha256:7720174604c7647e357566ac9e4d135c137caed5e7b01223551a4c81c8dc8b9a",
|
|
||||||
"sha256:b33ec641309bcea40c76c1b105f988e4e8f9a2f1ee1486aa5c0eeef33956c9bb",
|
|
||||||
"sha256:d1135dc0ac197242028ede085b693ba1f2bff7f0f9b91080e2540348312bfa53",
|
|
||||||
"sha256:d5a61e9c2322b45f259909a02b76bc98c4641214e22a37191d00c151aa9cdb9a",
|
|
||||||
"sha256:da22c4b17bc17dad9c8faf6d94c8fe568ac71c867a56631ab874da418fc7f8f7",
|
|
||||||
"sha256:da5c48ec9f8d8b5df42d328b6d1fb8d9413cd664a2367ef4f6f7cc48ee5b82c0",
|
|
||||||
"sha256:db2794bad21b7b30b6849b4e1537171cae8a7087711d958d69c233470dc612e7",
|
|
||||||
"sha256:f1c2f67df727034f94ccb590142d1d110f3dd38f638a4f1567fdd9f39892ba05",
|
|
||||||
"sha256:f840dddded8b046edc774c88ed8d2442cdb231a68894c42c74e3a809450fae76"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==4.4.0"
|
|
||||||
},
|
|
||||||
"multidict": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f",
|
|
||||||
"sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3",
|
|
||||||
"sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef",
|
|
||||||
"sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b",
|
|
||||||
"sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73",
|
|
||||||
"sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc",
|
|
||||||
"sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3",
|
|
||||||
"sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd",
|
|
||||||
"sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351",
|
|
||||||
"sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941",
|
|
||||||
"sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d",
|
|
||||||
"sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1",
|
|
||||||
"sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b",
|
|
||||||
"sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a",
|
|
||||||
"sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3",
|
|
||||||
"sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7",
|
|
||||||
"sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0",
|
|
||||||
"sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0",
|
|
||||||
"sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014",
|
|
||||||
"sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5",
|
|
||||||
"sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036",
|
|
||||||
"sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d",
|
|
||||||
"sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a",
|
|
||||||
"sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce",
|
|
||||||
"sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1",
|
|
||||||
"sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a",
|
|
||||||
"sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9",
|
|
||||||
"sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7",
|
|
||||||
"sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b"
|
|
||||||
],
|
|
||||||
"version": "==4.5.2"
|
|
||||||
},
|
|
||||||
"odfpy": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:596021f0519623ca8717331951c95e3b8d7b21e86edc7efe8cb650a0d0f59a2b"
|
|
||||||
],
|
|
||||||
"version": "==1.4.0"
|
|
||||||
},
|
|
||||||
"openpyxl": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:626d38647c063d55803ef4971c4d43226538d4e95cb6260c094e363ee33e10c7"
|
|
||||||
],
|
|
||||||
"version": "==2.4.11"
|
|
||||||
},
|
|
||||||
"pycares": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2ca080db265ea238dc45f997f94effb62b979a617569889e265c26a839ed6305",
|
|
||||||
"sha256:6f79c6afb6ce603009db2042fddc2e348ad093ece9784cbe2daa809499871a23",
|
|
||||||
"sha256:70918d06eb0603016d37092a5f2c0228509eb4e6c5a3faacb4184f6ab7be7650",
|
|
||||||
"sha256:755187d28d24a9ea63aa2b4c0638be31d65fbf7f0ce16d41261b9f8cb55a1b99",
|
|
||||||
"sha256:7baa4b1f2146eb8423ff8303ebde3a20fb444a60db761fba0430d104fe35ddbf",
|
|
||||||
"sha256:90b27d4df86395f465a171386bc341098d6d47b65944df46518814ae298f6cc6",
|
|
||||||
"sha256:9e090dd6b2afa65cb51c133883b2bf2240fd0f717b130b0048714b33fb0f47ce",
|
|
||||||
"sha256:a11b7d63c3718775f6e805d6464cb10943780395ab042c7e5a0a7a9f612735dd",
|
|
||||||
"sha256:b253f5dcaa0ac7076b79388a3ac80dd8f3bd979108f813baade40d3a9b8bf0bd",
|
|
||||||
"sha256:c7f4f65e44ba35e35ad3febc844270665bba21cfb0fb7d749434e705b556e087",
|
|
||||||
"sha256:cdb342e6a254f035bd976d95807a2184038fc088d957a5104dcaab8be602c093",
|
|
||||||
"sha256:cf08e164f8bfb83b9fe633feb56f2754fae6baefcea663593794fa0518f8f98c",
|
|
||||||
"sha256:df9bc694cf03673878ea8ce674082c5acd134991d64d6c306d4bd61c0c1df98f"
|
|
||||||
],
|
|
||||||
"version": "==3.0.0"
|
|
||||||
},
|
|
||||||
"pycparser": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
|
|
||||||
],
|
|
||||||
"version": "==2.19"
|
|
||||||
},
|
|
||||||
"pysocks": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:15d38914b60dbcb231d276f64882a20435c049450160e953ca7d313d1405f16f",
|
|
||||||
"sha256:32238918ac0f19e9fd870a8692ac9bd14f5e8752b3c62624cda5851424642210",
|
|
||||||
"sha256:d9031ea45fdfacbe59a99273e9f0448ddb33c1580fe3831c1b09557c5718977c"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.7.0"
|
|
||||||
},
|
|
||||||
"pyyaml": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
|
|
||||||
"sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
|
|
||||||
"sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
|
|
||||||
"sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
|
|
||||||
"sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
|
|
||||||
"sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
|
|
||||||
"sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
|
|
||||||
"sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
|
|
||||||
"sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
|
|
||||||
"sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
|
|
||||||
"sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
|
|
||||||
"sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
|
|
||||||
"sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
|
|
||||||
],
|
|
||||||
"version": "==5.1.2"
|
|
||||||
},
|
|
||||||
"records": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:47e4874096f4a8f4b5bcad8c7c7cf512be36186e6e263ff3dfd750b05ff0d3c4",
|
|
||||||
"sha256:cdbacf52c61b4a3bc10fef1286a24a63ae95255a2e7b4e8ccb1e1f96737231ed"
|
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.5.3"
|
"version": "==0.5.3"
|
||||||
},
|
},
|
||||||
"requests": {
|
"pysocks": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
"sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299",
|
||||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
"sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5",
|
||||||
|
"sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.22.0"
|
"version": "==1.7.1"
|
||||||
},
|
},
|
||||||
"requests-file": {
|
"requests": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:75c175eed739270aec3c5279ffd74e6527dada275c5c0d76b5817e9c86bb7dea",
|
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
|
||||||
"sha256:8f04aa6201bacda0567e7ac7f677f1499b0fc76b22140c54bc06edf1ba92e2fa"
|
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
|
||||||
],
|
],
|
||||||
"version": "==1.4.3"
|
"index": "pypi",
|
||||||
|
"version": "==2.25.1"
|
||||||
},
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
|
||||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
|
||||||
],
|
],
|
||||||
"version": "==1.12.0"
|
"version": "==1.15.0"
|
||||||
},
|
},
|
||||||
"soupsieve": {
|
"soupsieve": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:72b5f1aea9101cf720a36bb2327ede866fd6f1a07b1e87c92a1cc18113cbc946",
|
"sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc",
|
||||||
"sha256:e4e9c053d59795e440163733a7fec6c5972210e1790c507e4c7b051d6c5259de"
|
"sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"
|
||||||
],
|
],
|
||||||
"version": "==1.9.2"
|
"markers": "python_version >= '3.0'",
|
||||||
|
"version": "==2.2.1"
|
||||||
},
|
},
|
||||||
"sqlalchemy": {
|
"sqlalchemy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:217e7fc52199a05851eee9b6a0883190743c4fb9c8ac4313ccfceaffd852b0ff"
|
"sha256:02b039e0e7e6de2f15ea2d2de3995e31a170e700ec0b37b4eded662171711d19",
|
||||||
|
"sha256:08943201a1e3c6238e48f4d5d56c27ea1e1b39d3d9f36a9d81fc3cfb0e1b83bd",
|
||||||
|
"sha256:0ee0054d4a598d2920cae14bcbd33e200e02c5e3b47b902627f8cf5d4c9a2a4b",
|
||||||
|
"sha256:11e7a86209f69273e75d2dd64b06c0c2660e39cd942fce2170515c404ed7358a",
|
||||||
|
"sha256:1294f05916c044631fd626a4866326bbfbd17f62bd37510d000afaef4b35bd74",
|
||||||
|
"sha256:2f11b5783933bff55291ca06496124347627d211ff2e509e846af1c35de0a3fb",
|
||||||
|
"sha256:301d0cd6ef1dc73b607748183da857e712d6f743de8d92b1e1f8facfb0ba2aa2",
|
||||||
|
"sha256:344b58b4b4193b72e8b768a51ef6eb5a4c948ce313a0f23e2ea081e71ce8ac0e",
|
||||||
|
"sha256:44e11a06168782b6d485daef197783366ce7ab0d5eea0066c899ae06cef47bbc",
|
||||||
|
"sha256:45b091ccbf94374ed14abde17e9a04522b0493a17282eaaf4383efdd413f5243",
|
||||||
|
"sha256:48540072f43b3c080159ec1f24a4b014c0ee83d3b73795399974aa358a8cf71b",
|
||||||
|
"sha256:4df07161897191ed8d4a0cfc92425c81296160e5c5f76c9256716d3085172883",
|
||||||
|
"sha256:4f7ce3bfdab6520554af4a5b1df4513d45388624d015ba4d921daf48ce1d6503",
|
||||||
|
"sha256:5361e25181b9872d6906c8c9be7dc05cb0a0951d71ee59ee5a71c1deb301b8a8",
|
||||||
|
"sha256:6f8fdad2f335d2f3ca2f3ee3b01404f7abcf519b03de2c510f1f42d16e39ffb4",
|
||||||
|
"sha256:70a1387396ea5b3022539b560c287daf79403d8b4b365f89b56d660e625a4457",
|
||||||
|
"sha256:7481f9c2c832a3bf37c80bee44d91ac9938b815cc06f7e795b976e300914aab9",
|
||||||
|
"sha256:7c0c7bb49167ac738ca6ee6e7f94a9988a7e4e261d8da335341e8c8c8f3b2e9b",
|
||||||
|
"sha256:7de84feb31af3d8fdf819cac2042928d0b60d3cb16f49c4b2f48d88db46e79f6",
|
||||||
|
"sha256:7f5087104c3c5af11ea59e49ae66c33ca98b14a47d3796ae97498fca53f84aef",
|
||||||
|
"sha256:81badd7d3e0e6aba70a5d1b50fabe8112e9835a6fdb0684054c3fe5378ce0d01",
|
||||||
|
"sha256:82f11b679df91275788be6734dd4a9dfa29bac67b85326992609f62b05bdab37",
|
||||||
|
"sha256:8301ecf3e819eb5dbc171e84654ff60872807775301a55fe35b0ab2ba3742031",
|
||||||
|
"sha256:8d6a9feb5efd2fdab25c6d5a0a5589fed9d789f5ec57ec12263fd0e60ce1dea6",
|
||||||
|
"sha256:915d4fa08776c0252dc5a34fa15c6490f66f411ea1ac9492022f98875d6baf20",
|
||||||
|
"sha256:94040a92b6676f9ffdab6c6b479b3554b927a635c90698c761960b266b04fc88",
|
||||||
|
"sha256:a08027ae84efc563f0f2f341dda572eadebeca38c0ae028a009988f27e9e6230",
|
||||||
|
"sha256:a103294583383660d9e06dbd82037dc8e94c184bdcb27b2be44ae4457dafc6b4",
|
||||||
|
"sha256:c22bfac8d3b955cdb13f0fcd6343156bf56d925196cf7d9ab9ce9f61d3f1e11c",
|
||||||
|
"sha256:c3810ebcf1d42c532c8f5c3f442c705d94442a27a32f2df5344f0857306ab321",
|
||||||
|
"sha256:ee4ddc904fb6414b5118af5b8d45e428aac2ccda01326b2ba2fe4354b0d8d1ae",
|
||||||
|
"sha256:f16801795f1ffe9472360589a04301018c79e4582a85e68067275bb4f765e4e2",
|
||||||
|
"sha256:f62c57ceadedeb8e7b98b48ac4d684bf2b0f73b9d882fed3ca260d9aedf6403f",
|
||||||
|
"sha256:fbb0fda1c574975807aceb0e2332e0ecfe9e5656c191ed482c1a5eafe7a33823"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.0'",
|
"index": "pypi",
|
||||||
"version": "==1.3.6"
|
"version": "==1.4.5"
|
||||||
},
|
},
|
||||||
"tablib": {
|
"tenacity": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0f88a9cebdaa1a2cc29ae57387082ee81015d1149ecd34e48a8c8d3b4dd21670",
|
"sha256:5bd16ef5d3b985647fe28dfa6f695d343aa26479a04e8792b9d3c8f49e361ae1",
|
||||||
"sha256:5f33c079b07eb10cf9c4b4696add2ecf32c89db7729240546ecdcd5c92f67e13"
|
"sha256:a0ce48587271515db7d3a5e700df9ae69cce98c4b57c23a4886da15243603dd8"
|
||||||
],
|
],
|
||||||
"version": "==0.13.0"
|
"index": "pypi",
|
||||||
|
"version": "==7.0.0"
|
||||||
},
|
},
|
||||||
"termcolor": {
|
"termcolor": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -424,66 +231,35 @@
|
|||||||
],
|
],
|
||||||
"version": "==1.1.0"
|
"version": "==1.1.0"
|
||||||
},
|
},
|
||||||
"tldextract": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2c1c5d9d454f79734b4f3da0d603856dd9f820753410a3e9abf0a0c9fde33e97",
|
|
||||||
"sha256:b72bef6013de67c7fa181250bc2c2e089a994d259c09ca95a9771f2f97e29ed1"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.2.1"
|
|
||||||
},
|
|
||||||
"tqdm": {
|
"tqdm": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:14a285392c32b6f8222ecfbcd217838f88e11630affe9006cd0e94c7eff3cb61",
|
"sha256:9fdf349068d047d4cfbe24862c425883af1db29bcddf4b0eeb2524f6fbdb23c7",
|
||||||
"sha256:25d4c0ea02a305a688e7e9c2cdc8f862f989ef2a4701ab28ee963295f5b109ab"
|
"sha256:d666ae29164da3e517fcf125e41d4fe96e5bb375cd87ff9763f6b38b5592fe33"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==4.32.2"
|
"version": "==4.59.0"
|
||||||
|
},
|
||||||
|
"treelib": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1cbfffb2d2b75ccac27d0200cee0507b6fbb0726e0afb9fae017ade5d2ce8788"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.6.1"
|
||||||
},
|
},
|
||||||
"urllib3": {
|
"urllib3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
|
"sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
|
||||||
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
|
"sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"
|
||||||
],
|
],
|
||||||
"version": "==1.25.3"
|
"version": "==1.26.4"
|
||||||
},
|
},
|
||||||
"win32-setctime": {
|
"win32-setctime": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:568fd636c68350bcc54755213fe01966fe0a6c90b386c0776425944a0382abef",
|
"sha256:4e88556c32fdf47f64165a2180ba4552f8bb32c1103a2fafd05723a0bd42bd4b",
|
||||||
"sha256:b47e5023ec7f0b4962950902b15bc56464a380d869f59d27dbf9ab423b23e8f9"
|
"sha256:dc925662de0a6eb987f0b01f599c01a8236cb8c62831c22d9cada09ad958243e"
|
||||||
],
|
],
|
||||||
"markers": "sys_platform == 'win32'",
|
"markers": "sys_platform == 'win32'",
|
||||||
"version": "==1.0.1"
|
"version": "==1.0.3"
|
||||||
},
|
|
||||||
"xlrd": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:546eb36cee8db40c3eaa46c351e67ffee6eeb5fa2650b71bc4c758a29a1b29b2",
|
|
||||||
"sha256:e551fb498759fa3a5384a94ccd4c3c02eb7c00ea424426e212ac0c57be9dfbde"
|
|
||||||
],
|
|
||||||
"version": "==1.2.0"
|
|
||||||
},
|
|
||||||
"xlwt": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:a082260524678ba48a297d922cc385f58278b8aa68741596a87de01a9c628b2e",
|
|
||||||
"sha256:c59912717a9b28f1a3c2a98fd60741014b06b043936dcecbc113eaaada156c88"
|
|
||||||
],
|
|
||||||
"version": "==1.3.0"
|
|
||||||
},
|
|
||||||
"yarl": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9",
|
|
||||||
"sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f",
|
|
||||||
"sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb",
|
|
||||||
"sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320",
|
|
||||||
"sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842",
|
|
||||||
"sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0",
|
|
||||||
"sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829",
|
|
||||||
"sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310",
|
|
||||||
"sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4",
|
|
||||||
"sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8",
|
|
||||||
"sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1"
|
|
||||||
],
|
|
||||||
"version": "==1.3.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"develop": {}
|
"develop": {}
|
||||||
|
|||||||
@@ -3,344 +3,298 @@
|
|||||||
[](https://travis-ci.org/shmilylty/OneForAll)
|
[](https://travis-ci.org/shmilylty/OneForAll)
|
||||||
[](https://codecov.io/gh/shmilylty/OneForAll)
|
[](https://codecov.io/gh/shmilylty/OneForAll)
|
||||||
[](https://codeclimate.com/github/shmilylty/OneForAll/maintainability)
|
[](https://codeclimate.com/github/shmilylty/OneForAll/maintainability)
|
||||||
[](./LICENSE)
|
[](https://github.com/shmilylty/OneForAll/tree/master/LICENSE)
|
||||||
[](./)
|
[](https://github.com/shmilylty/OneForAll/tree/master/)
|
||||||
[](https://github.com/shmilylty/OneForAll/releases)
|
[](https://github.com/shmilylty/OneForAll/releases)
|
||||||
|
|
||||||
👊**OneForAll是一款功能强大的子域收集工具** 📝[English Document](./README.en.md)
|
👊**OneForAll是一款功能强大的子域收集工具** 📝[English Document](https://github.com/shmilylty/OneForAll/tree/master/docs/en-us/README.md)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## 🚀上手指南
|
||||||
|
|
||||||
|
📢 请务必花一点时间阅读此文档,有助于你快速熟悉OneForAll!
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>🐍安装要求</b></summary>
|
||||||
|
|
||||||
|
OneForAll基于[Python 3.6.0]( https://www.python.org/downloads/release/python-360/ )开发和测试,OneForAll需要高于Python 3.6.0的版本才能运行。
|
||||||
|
安装Python环境可以参考[Python 3 安装指南](https://pythonguidecn.readthedocs.io/zh/latest/starting/installation.html#python-3)。运行以下命令检查Python和pip3版本:
|
||||||
|
```bash
|
||||||
|
python -V
|
||||||
|
pip3 -V
|
||||||
|
```
|
||||||
|
如果你看到类似以下的输出便说明Python环境没有问题:
|
||||||
|
```bash
|
||||||
|
Python 3.6.0
|
||||||
|
pip 19.2.2 from C:\Users\shmilylty\AppData\Roaming\Python\Python36\site-packages\pip (python 3.6)
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>✔安装步骤(git 版)</b></summary>
|
||||||
|
|
||||||
|
1. **下载**
|
||||||
|
|
||||||
|
由于该项目**处于开发中**,会不断进行更新迭代,下载时请使用`git clone`**克隆**最新代码仓库,也方便后续的更新,不推荐从Releases下载,因为Releases里版本更新缓慢,也不方便更新,
|
||||||
|
本项目已经在[码云](https://gitee.com/shmilylty/OneForAll.git)(Gitee)镜像了一份,国内推荐使用码云进行克隆比较快:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://gitee.com/shmilylty/OneForAll.git
|
||||||
|
```
|
||||||
|
或者:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/shmilylty/OneForAll.git
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **安装**
|
||||||
|
|
||||||
|
你可以通过pip3安装OneForAll的依赖,以下为**Windows系统**下使用**pip3**安装依赖的示例:注意:如果你的Python3安装在系统Program Files目录下,如:`C:\Program Files\Python36`,那么请以管理员身份运行命令提示符cmd执行以下命令!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd OneForAll/
|
||||||
|
python3 -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
python3 oneforall.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
其他系统平台的请参考[依赖安装](https://github.com/shmilylty/OneForAll/tree/master/docs/installation_dependency.md),如果在安装依赖过程中发现编译某个依赖库失败时可以参考[常见问题与回答.md](https://github.com/shmilylty/OneForAll/tree/master/docs/troubleshooting.md)文档中解决方法,如果依然不能解决欢迎加群反馈问题。
|
||||||
|
|
||||||
|
3. **更新**
|
||||||
|
|
||||||
|
执行以下命令**更新**项目(可保存对`/config/setting.py`和`/config/api.py`的修改):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git stash # 暂存本地的修改
|
||||||
|
git fetch --all # 拉取项目更新
|
||||||
|
git pull # 下载覆盖
|
||||||
|
git stash pop # 释放本地修改
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>✔安装步骤(docker 版)</b></summary>
|
||||||
|
|
||||||
|
首先下载并编辑配置文件,添加自己的`api`和个性化设置,并保留原始文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
config
|
||||||
|
├── api.py
|
||||||
|
├── default.py
|
||||||
|
├── __init__.py
|
||||||
|
├── log.py
|
||||||
|
└── setting.py
|
||||||
|
```
|
||||||
|
|
||||||
|
拉取镜像并执行,其中`~/.config`替换为你自己配置文件所在文件夹的路径
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker pull shmilylty/oneforall
|
||||||
|
docker run -it --rm -v ~/results:/OneForAll/results -v ~/.config:/OneForAll/config shmilylty/oneforall --target example.com run
|
||||||
|
```
|
||||||
|
参数直接加在指令末尾,结果会输出在本地目录`~/results`,如需保存到其他位置,可以自行修改
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>✨使用演示</b></summary>
|
||||||
|
|
||||||
|
如果你是通过pip3安装的依赖则使用以下命令运行示例:
|
||||||
|
```bash
|
||||||
|
python3 oneforall.py --target example.com run
|
||||||
|
python3 oneforall.py --targets ./example.txt run
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>🧐结果说明</b></summary>
|
||||||
|
|
||||||
|
我们以`python3 oneforall.py --target example.com run`命令为例,OneForAll在默认参数正常执行完毕会在results目录生成相应结果:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`example.com.csv`是每个主域下的子域收集结果。
|
||||||
|
|
||||||
|
`all_subdomain_result_1583034493.csv`是每次运行OneForAll收集到子域的汇总结果,包含`example.com.csv`,方便在批量收集场景中获取全部结果。
|
||||||
|
|
||||||
|
`result.sqlite3`是存放每次运行OneForAll收集到子域的SQLite3结果数据库,其数据库结构如下图:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
其中类似`example_com_origin_result`表存放每个模块最初子域收集结果。
|
||||||
|
|
||||||
|
其中类似`example_com_resolve_result`表存放对子域进行解析后的结果。
|
||||||
|
|
||||||
|
其中类似`example_com_last_result`表存放上一次子域收集结果(需要收集两次以上才会生成)。
|
||||||
|
|
||||||
|
其中类似`example_com_now_result`表存放现在子域收集结果,一般情况关注这张表就可以了。
|
||||||
|
|
||||||
|
更多信息请参阅[字段解释说明](./docs/field.md)。
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>🤔使用帮助</b></summary>
|
||||||
|
|
||||||
|
命令行参数只提供了一些常用参数,更多详细的参数配置请见[setting.py](https://github.com/shmilylty/OneForAll/tree/master/config/setting.py),如果你认为有些参数是命令界面经常使用到的或缺少了什么参数等问题非常欢迎反馈。由于众所周知的原因,如果要使用一些被墙的收集接口请先到[setting.py](https://github.com/shmilylty/OneForAll/tree/master/config/setting.py)配置代理,有些收集模块需要提供API(大多都是可以注册账号免费获取),如果需要使用请到[api.py](https://github.com/shmilylty/OneForAll/tree/master/config/api.py)配置API信息,如果不使用请忽略有关报错提示。(详细模块请阅读[收集模块说明](https://github.com/shmilylty/OneForAll/tree/master/docs/collection_modules.md))
|
||||||
|
|
||||||
|
OneForAll命令行界面基于[Fire](https://github.com/google/python-fire/)实现,有关Fire更高级使用方法请参阅[使用Fire CLI](https://github.com/google/python-fire/blob/master/docs/using-cli.md)。
|
||||||
|
|
||||||
|
[oneforall.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall.py)是主程序入口,oneforall.py可以调用[brute.py](https://github.com/shmilylty/OneForAll/tree/master/brute.py),[takerover.py](https://github.com/shmilylty/OneForAll/tree/master/takerover.py)及[dbexport.py](https://github.com/shmilylty/OneForAll/tree/master/dbexport.py)等模块,为了方便进行子域爆破独立出了brute.py,为了方便进行子域接管风险检查独立出了takerover.py,为了方便数据库导出独立出了dbexport.py,这些模块都可以单独运行,并且所接受参数要更丰富一点,如果要单独使用这些模块请参考[使用帮助](https://github.com/shmilylty/OneForAll/tree/master/docs/usage_help.md)
|
||||||
|
|
||||||
|
❗注意:当你在使用过程中遇到一些问题或者疑惑时,请先到[Issues](https://github.com/shmilylty/OneForAll/issues)里使用搜索找找答案,还可以参阅[常见问题与回答](https://github.com/shmilylty/OneForAll/tree/master/docs/troubleshooting.md)。
|
||||||
|
|
||||||
|
**oneforall.py使用帮助**
|
||||||
|
|
||||||
|
以下帮助信息可能不是最新的,你可以使用`python oneforall.py --help`获取最新的帮助信息。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python oneforall.py --help
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
oneforall.py - OneForAll帮助信息
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
oneforall.py COMMAND | --target=TARGET <flags>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
OneForAll是一款功能强大的子域收集工具
|
||||||
|
|
||||||
|
Example:
|
||||||
|
python3 oneforall.py version
|
||||||
|
python3 oneforall.py --target example.com run
|
||||||
|
python3 oneforall.py --targets ./domains.txt run
|
||||||
|
python3 oneforall.py --target example.com --valid None run
|
||||||
|
python3 oneforall.py --target example.com --brute True run
|
||||||
|
python3 oneforall.py --target example.com --port small run
|
||||||
|
python3 oneforall.py --target example.com --fmt csv run
|
||||||
|
python3 oneforall.py --target example.com --dns False run
|
||||||
|
python3 oneforall.py --target example.com --req False run
|
||||||
|
python3 oneforall.py --target example.com --takeover False run
|
||||||
|
python3 oneforall.py --target example.com --show True run
|
||||||
|
|
||||||
|
Note:
|
||||||
|
参数alive可选值True,False分别表示导出存活,全部子域结果
|
||||||
|
参数port可选值有'default', 'small', 'large', 详见config.py配置
|
||||||
|
参数fmt可选格式有 'csv','json'
|
||||||
|
参数path默认None使用OneForAll结果目录生成路径
|
||||||
|
|
||||||
|
ARGUMENTS
|
||||||
|
TARGET
|
||||||
|
单个域名(二选一必需参数)
|
||||||
|
TARGETS
|
||||||
|
每行一个域名的文件路径(二选一必需参数)
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--brute=BRUTE
|
||||||
|
s
|
||||||
|
--dns=DNS
|
||||||
|
DNS解析子域(默认True)
|
||||||
|
--req=REQ
|
||||||
|
HTTP请求子域(默认True)
|
||||||
|
--port=PORT
|
||||||
|
请求验证子域的端口范围(默认只探测80端口)
|
||||||
|
--valid=VALID
|
||||||
|
只导出存活的子域结果(默认False)
|
||||||
|
--fmt=FMT
|
||||||
|
结果保存格式(默认csv)
|
||||||
|
--path=PATH
|
||||||
|
结果保存路径(默认None)
|
||||||
|
--takeover=TAKEOVER
|
||||||
|
检查子域接管(默认False)
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
## 🎉项目简介
|
## 🎉项目简介
|
||||||
|
|
||||||
|
项目地址:[https://github.com/shmilylty/OneForAll](https://github.com/shmilylty/OneForAll)
|
||||||
|
|
||||||
在渗透测试中信息收集的重要性不言而喻,子域收集是信息收集中必不可少且非常重要的一环,目前网上也开源了许多子域收集的工具,但是总是存在以下部分问题:
|
在渗透测试中信息收集的重要性不言而喻,子域收集是信息收集中必不可少且非常重要的一环,目前网上也开源了许多子域收集的工具,但是总是存在以下部分问题:
|
||||||
|
|
||||||
* **不够强大**,子域收集的接口不够多,不能做到对批量子域自动收集,没有自动子域解析,验证,FUZZ以及信息拓展等功能。
|
* **不够强大**,子域收集的接口不够多,不能做到对批量子域自动收集,没有自动子域解析,验证,FUZZ以及信息拓展等功能。
|
||||||
* **不够友好**,固然命令行模块比较方便,但是当可选的参数很多,要实现的操作复杂,用命令行模式就有点不够友好,如果有交互良好,高可操作的前端那么使用体验就会好很多。
|
* **不够友好**,固然命令行模块比较方便,但是当可选的参数很多,要实现的操作复杂,用命令行模式就有点不够友好,如果有交互良好,高可操作的前端那么使用体验就会好很多。
|
||||||
|
|
||||||
* **缺少维护**,很多工具几年没有更新过一次,issues和PR是啥,不存在的。
|
* **缺少维护**,很多工具几年没有更新过一次,issues和PR是啥,不存在的。
|
||||||
|
|
||||||
* **效率问题**,没有利用多进程,多线程以及异步协程技术,速度较慢。
|
* **效率问题**,没有利用多进程,多线程以及异步协程技术,速度较慢。
|
||||||
|
|
||||||
为了解决以上痛点,此项目应用而生,OneForAll一词是来自我喜欢的一部日漫《[我的英雄学院](https://manhua.fzdm.com/131/)》,它是一种通过一代代的传承不断变强的潜力无穷的顶级个性,目前[番剧](https://www.bilibili.com/bangumi/media/md7452/)也更新到了第三季了,欢迎大佬们入坑😄。正如其名,我希望OneForAll是一款集百家之长,功能强大的全面快速子域收集终极神器🔨。
|
为了解决以上痛点,此项目应用而生,正如其名,我希望OneForAll是一款集百家之长,功能强大的全面快速子域收集终极神器🔨。
|
||||||
|
|
||||||
目前OneForAll还在开发中,肯定有不少问题和需要改进的地方,欢迎大佬们提交[Issues](https://github.com/shmilylty/OneForAll/issues)和[PR](https://github.com/shmilylty/OneForAll/pulls),用着还行给个小星星✨吧,目前有一个专门用于OneForAll交流和反馈QQ群👨👨👦👦::[**824414244**](//shang.qq.com/wpa/qunwpa?idkey=125d3689b60445cdbb11e4ddff38036b7f6f2abbf4f7957df5dddba81aa90771),也可以给我发邮件📧[admin@hackfun.org]。
|
目前OneForAll还在开发中,肯定有不少问题和需要改进的地方,欢迎大佬们提交[Issues](https://github.com/shmilylty/OneForAll/issues)和[PR](https://github.com/shmilylty/OneForAll/pulls),用着还行给个小星星✨吧,目前有一个专门用于OneForAll交流和反馈QQ群👨👨👦👦::[**824414244**](//shang.qq.com/wpa/qunwpa?idkey=125d3689b60445cdbb11e4ddff38036b7f6f2abbf4f7957df5dddba81aa90771)(加群验证:信息收集)。
|
||||||
|
|
||||||
## 👍功能特性
|
## 👍功能特性
|
||||||
|
|
||||||
* **收集能力强大**,详细模块请阅读[收集模块说明](./docs/collection_modules.md)。
|
* **收集能力强大**,详细模块请阅读[收集模块说明](https://github.com/shmilylty/OneForAll/tree/master/docs/collection_modules.md)。
|
||||||
1. 利用证书透明度收集子域(目前有6个模块:`censys_api`,`certdb_api`,`certspotter`,`crtsh`,`entrust`,`google`)
|
1. 利用证书透明度收集子域(目前有6个模块:`censys_api`,`certspotter`,`crtsh`,`entrust`,`google`,`spyse_api`)
|
||||||
|
2. 常规检查收集子域(目前有4个模块:域传送漏洞利用`axfr`,检查跨域策略文件`cdx`,检查HTTPS证书`cert`,检查内容安全策略`csp`,检查robots文件`robots`,检查sitemap文件`sitemap`,利用NSEC记录遍历DNS域`dnssec`,后续会添加NSEC3记录等模块)
|
||||||
2. 常规检查收集子域(目前有4个模块:域传送漏洞利用`axfr`,检查跨域策略文件`cdx`,检查HTTPS证书`cert`,检查内容安全策略`csp`,检查robots文件`robots`,检查sitemap文件`sitemap`,后续会添加检查NSEC记录,NSEC3记录等模块)
|
|
||||||
|
|
||||||
3. 利用网上爬虫档案收集子域(目前有2个模块:`archivecrawl`,`commoncrawl`,此模块还在调试,该模块还有待添加和完善)
|
3. 利用网上爬虫档案收集子域(目前有2个模块:`archivecrawl`,`commoncrawl`,此模块还在调试,该模块还有待添加和完善)
|
||||||
|
4. 利用DNS数据集收集子域(目前有24个模块:`bevigil_api`, `binaryedge_api`, `bufferover`, `cebaidu`, `chinaz`, `chinaz_api`, `circl_api`, `cloudflare`, `dnsdb_api`, `dnsdumpster`, `hackertarget`, `ip138`, `ipv4info_api`, `netcraft`, `passivedns_api`, `ptrarchive`, `qianxun`, `rapiddns`, `riddler`, `robtex`, `securitytrails_api`, `sitedossier`, `threatcrowd`, `wzpc`, `ximcx`)
|
||||||
|
5. 利用DNS查询收集子域(目前有5个模块:通过枚举常见的SRV记录并做查询来收集子域`srv`,以及通过查询域名的DNS记录中的MX,NS,SOA,TXT记录来收集子域)
|
||||||
|
6. 利用威胁情报平台数据收集子域(目前有6个模块:`alienvault`, `riskiq_api`,`threatbook_api`,`threatminer`,`virustotal`,`virustotal_api`该模块还有待添加和完善)
|
||||||
|
7. 利用搜索引擎发现子域(目前有18个模块:`ask`, `baidu`, `bing`, `bing_api`, `duckduckgo`, `exalead`, `fofa_api`, `gitee`, `github`, `github_api`, `google`, `google_api`, `shodan_api`, `so`, `sogou`, `yahoo`, `yandex`, `zoomeye_api`),在搜索模块中除特殊搜索引擎,通用的搜索引擎都支持自动排除搜索,全量搜索,递归搜索。
|
||||||
|
* **支持子域爆破**,该模块有常规的字典爆破,也有自定义的fuzz模式,支持批量爆破和递归爆破,自动判断泛解析并处理。
|
||||||
|
* **支持子域验证**,默认开启子域验证,自动解析子域DNS,自动请求子域获取title和banner,并综合判断子域存活情况。
|
||||||
|
* **支持子域爬取**,根据已有的子域,请求子域响应体以及响应体里的JS,从中再次发现新的子域。
|
||||||
|
* **支持子域置换**,根据已有的子域,使用子域替换技术再次发现新的子域。
|
||||||
|
* **支持子域接管**,默认开启子域接管风险检查,支持子域自动接管(目前只有Github,有待完善),支持批量检查。
|
||||||
|
* **处理功能强大**,发现的子域结果支持自动去除,自动DNS解析,HTTP请求探测,自动筛选出有效子域,拓展子域的Banner信息,最终支持的导出格式有`txt`, `csv`, `json`。
|
||||||
|
* **速度极快**,[收集模块](https://github.com/shmilylty/OneForAll/tree/master/collect.py)使用多线程调用,[爆破模块](https://github.com/shmilylty/OneForAll/tree/master/brute.py)使用[massdns](https://github.com/blechschmidt/massdns),DNS解析速度每秒可解析350000以上个域名,子域验证中DNS解析和HTTP请求使用异步多协程,多线程检查[子域接管](https://github.com/shmilylty/OneForAll/tree/master/takeover.py)风险。
|
||||||
|
* **体验良好**,各模块都有进度条,异步保存各模块结果。
|
||||||
|
|
||||||
4. 利用DNS数据集收集子域(目前有16个模块:`binaryedge_api`, `circl_api`, `hackertarget`, `riddler`, `bufferover`, `dnsdb`, `ipv4info`, `robtex`, `chinaz`, `dnsdb_api`, `netcraft`, `securitytrails_api`, `chinaz_api`, `dnsdumpster`, `ptrarchive`, `sitedossier`)
|
如果你有其他很棒的想法请务必告诉我!😎
|
||||||
|
|
||||||
5. 利用DNS查询收集子域(目前有1个模块:通过枚举常见的SRV记录并做查询来收集子域`srv`,该模块还有待添加和完善)
|
## 🌲目录结构
|
||||||
|
|
||||||
6. 利用威胁情报平台数据收集子域(目前有5个模块:`riskiq_api`,`threatbook_api`,`threatminer`,`virustotal`,`virustotal_api`该模块还有待添加和完善)
|
更多信息请参阅[目录结构说明](https://github.com/shmilylty/OneForAll/tree/master/docs/directory_structure.md)。
|
||||||
|
|
||||||
7. 利用搜索引擎发现子域(目前有15个模块:`ask`, `bing_api`, `fofa_api`, `shodan_api`, `yahoo`, `baidu`, `duckduckgo`, `google`, `so`, `yandex`, `bing`, `exalead`, `google_api`, `sogou`, `zoomeye_api`),在搜索模块中除特殊搜索引擎,通用的搜索引擎都支持自动排除搜索,全量搜索,递归搜索。
|
本项目[docs](https://github.com/shmilylty/OneForAll/tree/master/docs/)目录下还提供了一些帮助与说明,如[子域字典来源说明](https://github.com/shmilylty/OneForAll/tree/master/docs/dictionary_source.md)、[泛解析判断流程](https://github.com/shmilylty/OneForAll/tree/master/docs/wildcard_judgment.png)。
|
||||||
|
|
||||||
* **处理功能强大**,发现的子域结果支持自动去除,自动DNS解析,HTTP请求探测,自动移除无效子域,拓展子域的Banner信息,最终支持的导出格式有`csv`, `tsv`, `json`, `yaml`, `html`, `xls`, `xlsx`, `dbf`, `latex`, `ods`。
|
|
||||||
|
|
||||||
* **速度极速**,[收集模块](./oneforall/collect.py)使用多线程调用,[爆破模块](./oneforall/aiobrute.py)使用异步多进程多协程,DNS解析和HTTP请求使用异步多协程。
|
|
||||||
|
|
||||||
## 🚀上手指南
|
|
||||||
|
|
||||||
由于该项目**处于开发中**,会不断进行更新迭代,下载使用最好**克隆**最新项目。
|
|
||||||
|
|
||||||
**🐍安装要求**
|
|
||||||
|
|
||||||
1. Python 3.6-3.7
|
|
||||||
|
|
||||||
**✔安装步骤**
|
|
||||||
|
|
||||||
1. 下载更新
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/shmilylty/OneForAll.git
|
|
||||||
```
|
|
||||||
|
|
||||||
❗如果你之前已经克隆了项目运行之前请**备份**自己修改过的文件到项目外的地方(如**config.py**),然后执行以下命令**更新**项目:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git fetch --all
|
|
||||||
git reset --hard origin/master
|
|
||||||
git pull
|
|
||||||
```
|
|
||||||
|
|
||||||
2. 安装依赖
|
|
||||||
|
|
||||||
* 使用pipenv
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip3 install pipenv
|
|
||||||
cd OneForAll/
|
|
||||||
pipenv install --python 3.7
|
|
||||||
cd oneforall
|
|
||||||
pipenv run python3 oneforall.py --help
|
|
||||||
```
|
|
||||||
|
|
||||||
* 使用pip3
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd OneForAll/
|
|
||||||
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
|
||||||
cd oneforall/
|
|
||||||
python3 oneforall.py --help
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
如果在安装依赖过程中发现编译某个依赖库失败时可以参考[编译失败解决方法](./docs/building_fail_solution.md),如果还没有解决欢迎加群反馈。
|
## 👏用到框架
|
||||||
|
|
||||||
**✨使用演示**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 oneforall.py --target example.com run
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
**🤔使用帮助**
|
|
||||||
|
|
||||||
命令行参数只提供了一些常用参数,更多详细的参数配置请见[config.py](./oneforall/config.py),如果你认为有些参数是命令界面经常使用到的或缺少了什么参数等问题非常欢迎反馈。由于众所周知的原因,如果要使用一些被墙的收集接口请先到[config.py](./oneforall/config.py)配置代理,有些收集模块需要提供API(大多都是可以注册账号免费获取),如果需要使用请到[config.py](./oneforall/config.py)配置API信息,如果不使用请忽略有关报错提示。(详细模块请阅读[收集模块说明](./docs/collection_modules.md))
|
|
||||||
|
|
||||||
OneForAll命令行界面基于[Fire](https://github.com/google/python-fire/)实现,有关Fire更高级使用方法请参阅[使用Fire CLI](https://github.com/google/python-fire/blob/master/docs/using-cli.md),有任何使用疑惑欢迎加群交流。
|
|
||||||
|
|
||||||
oneforall.py是主程序入口,oneforall.py里有调用aiobrute.py和dbexport.py,为了方便进行子域爆破和数据库导出独立出了aiobrute.py和dbexport.py,这两个文件可以单独运行,并且所接受参数要更丰富一点。
|
|
||||||
|
|
||||||
1. oneforall.py使用帮助
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pipenv run python oneforall.py --help
|
|
||||||
```
|
|
||||||
```bash
|
|
||||||
NAME
|
|
||||||
oneforall.py - OneForAll是一款功能强大的子域收集工具
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
oneforall.py --target=TARGET <flags>
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
Version: 0.0.2
|
|
||||||
Project: https://github.com/shmilylty/OneForAll/
|
|
||||||
|
|
||||||
Example:
|
|
||||||
python oneforall.py --target example.com run
|
|
||||||
python oneforall.py --target example.com --brute True --port medium --valid 1 run
|
|
||||||
python oneforall.py --target ./domains.txt --format csv --path= ./result.csv --output True run
|
|
||||||
|
|
||||||
Note:
|
|
||||||
参数valid可选值有1,0,None,分别表示导出有效,无效,全部子域
|
|
||||||
参数port可选值有'small', 'medium', 'large', 'xlarge',详见config.py配置
|
|
||||||
参数format可选格式有'csv','tsv','json','yaml','html','xls','xlsx','dbf','latex','ods'
|
|
||||||
参数path为None会根据format参数和域名名称在项目结果目录生成相应文件
|
|
||||||
|
|
||||||
ARGUMENTS
|
|
||||||
TARGET
|
|
||||||
单个域名或者每行一个域名的文件路径
|
|
||||||
|
|
||||||
FLAGS
|
|
||||||
--brute=BRUTE
|
|
||||||
是否使用爆破模块(默认禁用)
|
|
||||||
--port=PORT
|
|
||||||
HTTP请求验证的端口范围(默认medium)
|
|
||||||
--valid=VALID
|
|
||||||
导出子域的有效性(默认1)
|
|
||||||
--path=PATH
|
|
||||||
导出路径(默认None)
|
|
||||||
--format=FORMAT
|
|
||||||
导出格式(默认xlsx)
|
|
||||||
--output=OUTPUT
|
|
||||||
是否将导出数据输出到终端(默认False)
|
|
||||||
```
|
|
||||||
|
|
||||||
2. aiobrute.py使用帮助
|
|
||||||
|
|
||||||
关于泛解析问题处理程序首先会访问一个随机的子域判断是否泛解析,如果使用了泛解析则是通过以下判断处理:
|
|
||||||
- 一是主要是与泛解析的IP集合和TTL值做对比,可以参考[这篇文章](http://sh3ll.me/archives/201704041222.txt)。
|
|
||||||
- 二是多次解析到同一IP集合次数(默认设置为10,可以在config.py设置大小)
|
|
||||||
- 考虑爆破效率问题目前还没有加上HTTP响应体相似度对比和响应体内容判断
|
|
||||||
经过测试在16核心的CPU,使用16进程64协程,100M带宽的环境下,设置任务分割为50000,跑两百万字典大概10分钟左右跑完,大概3333个子域每秒。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pipenv run python aiobrute.py --help
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
NAME
|
|
||||||
aiobrute.py - OneForAll多进程多协程异步子域爆破模块
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
aiobrute.py COMMAND | --target=TARGET <flags>
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
Example:
|
|
||||||
python aiobrute.py --target example.com run
|
|
||||||
python aiobrute.py --target ./domains.txt run
|
|
||||||
python aiobrute.py --target example.com --processes 4 --coroutine 64 --wordlist data/subdomains.txt run
|
|
||||||
python aiobrute.py --target example.com --recursive True --depth 2 --namelist data/next_subdomains.txt run
|
|
||||||
python aiobrute.py --target www.{fuzz}.example.com --fuzz True --rule [a-z][0-9] run
|
|
||||||
|
|
||||||
Note:
|
|
||||||
参数segment的设置受CPU性能,网络带宽,运营商限制等问题影响,默认设置500个子域为一任务组,
|
|
||||||
当你觉得你的环境不受以上因素影响,当前爆破速度较慢,那么强烈建议根据字典大小调整大小:
|
|
||||||
十万字典建议设置为5000,百万字典设置为50000
|
|
||||||
|
|
||||||
ARGUMENTS
|
|
||||||
TARGET
|
|
||||||
单个域名或者每行一个域名的文件路径
|
|
||||||
|
|
||||||
FLAGS
|
|
||||||
--processes=PROCESSES
|
|
||||||
爆破的进程数(默认CPU核心数)
|
|
||||||
--coroutine=COROUTINE
|
|
||||||
每个爆破进程下的协程数(默认16)
|
|
||||||
--wordlist=WORDLIST
|
|
||||||
指定爆破所使用的字典路径(默认使用config.py配置)
|
|
||||||
--segment=SEGMENT
|
|
||||||
爆破任务分割(默认500)
|
|
||||||
--recursive=RECURSIVE
|
|
||||||
是否使用递归爆破(默认False)
|
|
||||||
--depth=DEPTH
|
|
||||||
递归爆破的深度(默认2)
|
|
||||||
--namelist=NAMELIST
|
|
||||||
指定递归爆破所使用的字典路径(默认使用config.py配置)
|
|
||||||
--fuzz=FUZZ
|
|
||||||
是否使用fuzz模式进行爆破(默认False,开启必须指定fuzz正则规则)
|
|
||||||
--rule=RULE
|
|
||||||
fuzz模式使用的正则规则(默认使用config.py配置)
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
3. dbexport.py使用帮助
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pipenv run python dbexport.py --help
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
NAME
|
|
||||||
dbexport.py - OneForAll数据库导出模块
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
dbexport.py TABLE <flags>
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
Example:
|
|
||||||
python dbexport.py --db result.db --table name --format csv --output False
|
|
||||||
python dbexport.py --db result.db --table name --format csv --path= ./result.csv
|
|
||||||
|
|
||||||
Note:
|
|
||||||
参数valid可选值1,0,None,分别表示导出有效,无效,全部子域
|
|
||||||
参数format可选格式:'csv', 'tsv', 'json', 'yaml', 'html', 'xls', 'xlsx', 'dbf', 'latex', 'ods'
|
|
||||||
参数path为None会根据format参数和域名名称在项目结果目录生成相应文件
|
|
||||||
|
|
||||||
POSITIONAL ARGUMENTS
|
|
||||||
TABLE
|
|
||||||
要导出的表
|
|
||||||
|
|
||||||
FLAGS
|
|
||||||
--db=DB
|
|
||||||
要导出的数据库路径(默认为results/result.sqlite3)
|
|
||||||
--valid=VALID
|
|
||||||
导出子域的有效性(默认None)
|
|
||||||
--path=PATH
|
|
||||||
导出路径(默认None)
|
|
||||||
--format=FORMAT
|
|
||||||
导出格式(默认xlsx)
|
|
||||||
--output=OUTPUT
|
|
||||||
是否将导出数据输出到终端(默认False)
|
|
||||||
|
|
||||||
NOTES
|
|
||||||
You can also use flags syntax for POSITIONAL ARGUMENTS
|
|
||||||
```
|
|
||||||
|
|
||||||
## 👏主要框架
|
|
||||||
|
|
||||||
* [aiodns](https://github.com/saghul/aiodns) - 简单DNS异步解析库。
|
|
||||||
* [aiohttp](https://github.com/aio-libs/aiohttp) - 异步http客户端/服务器框架
|
* [aiohttp](https://github.com/aio-libs/aiohttp) - 异步http客户端/服务器框架
|
||||||
* [aiomultiprocess](https://github.com/jreese/aiomultiprocess) - 将Python代码提升到更高的性能水平(multiprocessing和asyncio结合,实现异步多进程多协程)
|
|
||||||
* [beautifulsoup4](https://pypi.org/project/beautifulsoup4/) - 可以轻松从HTML或XML文件中提取数据的Python库
|
* [beautifulsoup4](https://pypi.org/project/beautifulsoup4/) - 可以轻松从HTML或XML文件中提取数据的Python库
|
||||||
* [fire](https://github.com/google/python-fire) - Python Fire是一个纯粹根据任何Python对象自动生成命令行界面(CLI)的库
|
* [fire](https://github.com/google/python-fire) - Python Fire是一个纯粹根据任何Python对象自动生成命令行界面(CLI)的库
|
||||||
* [loguru](https://github.com/Delgan/loguru) - 旨在带来愉快的日志记录Python库
|
* [loguru](https://github.com/Delgan/loguru) - 旨在带来愉快的日志记录Python库
|
||||||
|
* [massdns](https://github.com/blechschmidt/massdns) - 高性能的DNS解析器
|
||||||
* [records](https://github.com/kennethreitz/records) - Records是一个非常简单但功能强大的库,用于对大多数关系数据库进行最原始SQL查询。
|
* [records](https://github.com/kennethreitz/records) - Records是一个非常简单但功能强大的库,用于对大多数关系数据库进行最原始SQL查询。
|
||||||
* [requests](https://github.com/psf/requests) - Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用。
|
* [requests](https://github.com/psf/requests) - Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用。
|
||||||
* [tqdm](https://github.com/tqdm/tqdm) - 适用于Python和CLI的快速,可扩展的进度条库
|
* [tqdm](https://github.com/tqdm/tqdm) - 适用于Python和CLI的快速,可扩展的进度条库
|
||||||
|
|
||||||
感谢这些伟大优秀的Python库!
|
感谢这些伟大优秀的Python库!
|
||||||
|
|
||||||
## 🌲目录结构
|
## 🔖版本控制
|
||||||
|
|
||||||
```bash
|
该项目使用[SemVer](https://semver.org/)语言化版本格式进行版本管理,你可以参阅[变更记录说明](https://github.com/shmilylty/OneForAll/tree/master/docs/changes.md)了解历史变更情况。
|
||||||
D:.
|
|
||||||
|
|
|
||||||
+---.github
|
|
||||||
+---docs
|
|
||||||
| collection_modules.md 收集模块说明
|
|
||||||
+---images
|
|
||||||
\---oneforall
|
|
||||||
| aiobrute.py 异步多进程多协程子域爆破模块,可以单独运行
|
|
||||||
| collect.py 各个收集模块上层调用
|
|
||||||
| config.py 配置文件
|
|
||||||
| dbexport.py 数据库导出模块,可以单独运行
|
|
||||||
| domains.txt 要批量爆破的域名列表
|
|
||||||
| oneforall.py OneForAll主入口,可以单独运行
|
|
||||||
| __init__.py
|
|
||||||
|
|
|
||||||
+---common 公共调用模块
|
|
||||||
+---data 存放一些所需数据
|
|
||||||
| next_subdomains.txt 下一层子域字典
|
|
||||||
| public_suffix_list.dat 顶级域名后缀
|
|
||||||
| srv_names.json 常见SRV记录前缀名
|
|
||||||
| subdomains.txt 子域爆破常见字典
|
|
||||||
|
|
|
||||||
\---modules
|
|
||||||
+---certificates 利用证书透明度收集子域模块
|
|
||||||
+---check 常规检查收集子域模块
|
|
||||||
+---crawl 利用网上爬虫档案收集子域模块
|
|
||||||
+---datasets 利用DNS数据集收集子域模块
|
|
||||||
+---dnsquery 利用DNS查询收集子域模块
|
|
||||||
+---intelligence 利用威胁情报平台数据收集子域模块
|
|
||||||
\---search 利用搜索引擎发现子域模块
|
|
||||||
|
|
||||||
```
|
## ⌛后续计划
|
||||||
|
|
||||||
|
- [ ] 各模块持续优化和完善
|
||||||
|
- [ ] 操作强大交互人性的前端界面实现
|
||||||
|
|
||||||
|
更多信息请参阅[后续开发计划](https://github.com/shmilylty/OneForAll/tree/master/docs/todo.md)。
|
||||||
|
|
||||||
## 🙏贡献
|
## 🙏贡献
|
||||||
|
|
||||||
非常热烈欢迎各位大佬一起完善本项目!
|
非常热烈欢迎各位大佬一起完善本项目!
|
||||||
|
|
||||||
## ⌛后续计划
|
## 👨💻贡献者
|
||||||
|
|
||||||
- [ ] 子域收集模块优化
|
|
||||||
- [ ] 子域接管功能实现
|
|
||||||
- [ ] 子域收集爬虫实现
|
|
||||||
- [ ] 操作强大交互人性的前端界面实现
|
|
||||||
|
|
||||||
更多详细信息请阅读[TODO.md](./TODO.md)。
|
|
||||||
|
|
||||||
## 🔖版本控制
|
|
||||||
|
|
||||||
该项目使用[SemVer](https://semver.org/)语言化版本格式进行版本管理,你可以在[Releases](https://github.com/shmilylty/OneForAll/releases)查看可用版本。
|
|
||||||
|
|
||||||
## 👨💻作者
|
|
||||||
|
|
||||||
* **[Jing Ling](https://github.com/shmilylty)**
|
* **[Jing Ling](https://github.com/shmilylty)**
|
||||||
* 核心开发
|
* 核心开发
|
||||||
|
|
||||||
* **[Black Star](https://github.com/blackstar24)**
|
你可以在[贡献者文档](https://github.com/shmilylty/OneForAll/tree/master/docs/contributors.md)中查看所有贡献者以及他们所做出的贡献,感谢他们让OneForAll变得更强大好用。
|
||||||
* 模块贡献
|
|
||||||
|
|
||||||
* [**iceMatcha**](https://github.com/iceMatcha)
|
## ☕赞赏
|
||||||
* bug调试
|
|
||||||
|
|
||||||
*你也可以在[CONTRIBUTORS.md](./CONTRIBUTORS.md)中参看所有参与该项目的开发者。*
|
如果你觉得这个项目帮助到了你,你可以打赏一杯咖啡以资鼓励:)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 📄版权
|
## 📄版权
|
||||||
|
|
||||||
该项目签署了GPL-3.0授权许可,详情请参阅[LICENSE.md](./LICENSE.md)。
|
该项目签署了GPL-3.0授权许可,详情请参阅[LICENSE](https://github.com/shmilylty/OneForAll/blob/master/LICENSE)。
|
||||||
|
|
||||||
## 😘鸣谢
|
## 😘鸣谢
|
||||||
|
|
||||||
@@ -348,6 +302,13 @@ D:.
|
|||||||
|
|
||||||
感谢[A-Team](https://github.com/QAX-A-Team)大哥们热情无私的问题解答!
|
感谢[A-Team](https://github.com/QAX-A-Team)大哥们热情无私的问题解答!
|
||||||
|
|
||||||
## 📜免责声明 ##
|
## 📜免责声明
|
||||||
|
|
||||||
本工具仅限于安全研究与教学使用,用户使用本工具所造成的所有后果,由用户承担全部法律及连带责任,本项目所有作者和贡献者不承担任何法律及连带责任。
|
本工具仅能在取得足够合法授权的企业安全建设中使用,在使用本工具过程中,您应确保自己所有行为符合当地的法律法规。
|
||||||
|
如您在使用本工具的过程中存在任何非法行为,您将自行承担所有后果,本工具所有开发者和所有贡献者不承担任何法律及连带责任。
|
||||||
|
除非您已充分阅读、完全理解并接受本协议所有条款,否则,请您不要安装并使用本工具。
|
||||||
|
您的使用行为或者您以其他任何明示或者默示方式表示接受本协议的,即视为您已阅读并同意本协议的约束。
|
||||||
|
|
||||||
|
## 💖Star趋势
|
||||||
|
|
||||||
|
[](https://starchart.cc/shmilylty/OneForAll)
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
# OneForAll后续开发计划
|
|
||||||
|
|
||||||
## 下一步计划
|
|
||||||
|
|
||||||
- [ ] 子域收集模块优化
|
|
||||||
- [ ] 子域接管功能实现
|
|
||||||
- [ ] 子域收集爬虫实现
|
|
||||||
- [ ] 操作强大交互人性的前端界面实现
|
|
||||||
|
|
||||||
@@ -0,0 +1,503 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
OneForAll subdomain brute module
|
||||||
|
|
||||||
|
:copyright: Copyright (c) 2019, Jing Ling. All rights reserved.
|
||||||
|
:license: GNU General Public License v3.0, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
import gc
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
import exrex
|
||||||
|
import fire
|
||||||
|
|
||||||
|
import export
|
||||||
|
from common import utils
|
||||||
|
from config import settings
|
||||||
|
from common.module import Module
|
||||||
|
from modules import wildcard
|
||||||
|
from config.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
def gen_subdomains(expression, path):
|
||||||
|
"""
|
||||||
|
Generate subdomains
|
||||||
|
|
||||||
|
:param str expression: generate subdomains expression
|
||||||
|
:param str path: path of wordlist
|
||||||
|
:return set subdomains: list of subdomains
|
||||||
|
"""
|
||||||
|
subdomains = set()
|
||||||
|
with open(path, encoding='utf-8', errors='ignore') as fd:
|
||||||
|
for line in fd:
|
||||||
|
word = line.strip().lower()
|
||||||
|
if len(word) == 0:
|
||||||
|
continue
|
||||||
|
if not utils.is_subname(word):
|
||||||
|
continue
|
||||||
|
if word.startswith('.'):
|
||||||
|
word = word[1:]
|
||||||
|
if word.endswith('.'):
|
||||||
|
word = word[:-1]
|
||||||
|
subdomain = expression.replace('*', word)
|
||||||
|
subdomains.add(subdomain)
|
||||||
|
size = len(subdomains)
|
||||||
|
logger.log('DEBUG', f'The size of the dictionary generated by {path} is {size}')
|
||||||
|
if size == 0:
|
||||||
|
logger.log('ALERT', 'Please check the dictionary content!')
|
||||||
|
else:
|
||||||
|
utils.check_random_subdomain(subdomains)
|
||||||
|
return subdomains
|
||||||
|
|
||||||
|
|
||||||
|
def gen_fuzz_subdomains(expression, rule, fuzzlist):
|
||||||
|
"""
|
||||||
|
Generate subdomains based on fuzz mode
|
||||||
|
|
||||||
|
:param str expression: generate subdomains expression
|
||||||
|
:param str rule: regexp rule
|
||||||
|
:param str fuzzlist: fuzz dictionary
|
||||||
|
:return set subdomains: list of subdomains
|
||||||
|
"""
|
||||||
|
subdomains = set()
|
||||||
|
if fuzzlist:
|
||||||
|
fuzz_domain = gen_subdomains(expression, fuzzlist)
|
||||||
|
subdomains.update(fuzz_domain)
|
||||||
|
if rule:
|
||||||
|
fuzz_count = exrex.count(rule)
|
||||||
|
if fuzz_count > 10000000:
|
||||||
|
logger.log('ALERT', f'The dictionary generated by this rule is too large: '
|
||||||
|
f'{fuzz_count} > 10000000')
|
||||||
|
for fuzz_string in exrex.generate(rule):
|
||||||
|
fuzz_string = fuzz_string.lower()
|
||||||
|
if not fuzz_string.isalnum():
|
||||||
|
continue
|
||||||
|
fuzz_domain = expression.replace('*', fuzz_string)
|
||||||
|
subdomains.add(fuzz_domain)
|
||||||
|
utils.check_random_subdomain(subdomains)
|
||||||
|
logger.log('DEBUG', f'Dictionary size based on fuzz mode: {len(subdomains)}')
|
||||||
|
return subdomains
|
||||||
|
|
||||||
|
|
||||||
|
def gen_word_subdomains(expression, path):
|
||||||
|
"""
|
||||||
|
Generate subdomains based on word mode
|
||||||
|
|
||||||
|
:param str expression: generate subdomains expression
|
||||||
|
:param str path: path of wordlist
|
||||||
|
:return set subdomains: list of subdomains
|
||||||
|
"""
|
||||||
|
subdomains = gen_subdomains(expression, path)
|
||||||
|
logger.log('DEBUG', f'Dictionary based on word mode size: {len(subdomains)}')
|
||||||
|
return subdomains
|
||||||
|
|
||||||
|
|
||||||
|
def query_domain_ns_a(ns_list):
|
||||||
|
logger.log('INFOR', f'Querying A record from authoritative name server: {ns_list} ')
|
||||||
|
if not isinstance(ns_list, list):
|
||||||
|
return list()
|
||||||
|
ns_ip_list = []
|
||||||
|
resolver = utils.dns_resolver()
|
||||||
|
for ns in ns_list:
|
||||||
|
try:
|
||||||
|
answer = resolver.query(ns, 'A')
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
logger.log('ERROR', f'Query authoritative name server {ns} A record error')
|
||||||
|
continue
|
||||||
|
if answer:
|
||||||
|
for item in answer:
|
||||||
|
ns_ip_list.append(item.address)
|
||||||
|
logger.log('INFOR', f'Authoritative name server A record result: {ns_ip_list}')
|
||||||
|
return ns_ip_list
|
||||||
|
|
||||||
|
|
||||||
|
def query_domain_ns(domain):
|
||||||
|
logger.log('INFOR', f'Querying NS records of {domain}')
|
||||||
|
domain = utils.get_main_domain(domain)
|
||||||
|
resolver = utils.dns_resolver()
|
||||||
|
try:
|
||||||
|
answer = resolver.query(domain, 'NS')
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
logger.log('ERROR', f'Querying NS records of {domain} error')
|
||||||
|
return list()
|
||||||
|
ns = [item.to_text() for item in answer]
|
||||||
|
logger.log('INFOR', f'{domain}\'s authoritative name server is {ns}')
|
||||||
|
return ns
|
||||||
|
|
||||||
|
|
||||||
|
def check_dict():
|
||||||
|
if not settings.enable_check_dict:
|
||||||
|
return
|
||||||
|
sec = settings.check_time
|
||||||
|
logger.log('ALERT', f'You have {sec} seconds to check '
|
||||||
|
f'whether the configuration is correct or not')
|
||||||
|
logger.log('ALERT', f'If you want to exit, please use `Ctrl + C`')
|
||||||
|
try:
|
||||||
|
time.sleep(sec)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logger.log('INFOR', 'Due to configuration incorrect, exited')
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_result_infos(items, infos, subdomains, appear_times, wc_ips, wc_ttl):
|
||||||
|
qname = items.get('name')[:-1] # 去除最右边的`.`点号
|
||||||
|
reason = items.get('status')
|
||||||
|
resolver = items.get('resolver')
|
||||||
|
data = items.get('data')
|
||||||
|
answers = data.get('answers')
|
||||||
|
info = dict()
|
||||||
|
cnames = list()
|
||||||
|
ips = list()
|
||||||
|
ip_times = list()
|
||||||
|
cname_times = list()
|
||||||
|
ttls = list()
|
||||||
|
is_valid_flags = list()
|
||||||
|
have_a_record = False
|
||||||
|
for answer in answers:
|
||||||
|
if answer.get('type') != 'A':
|
||||||
|
continue
|
||||||
|
have_a_record = True
|
||||||
|
ttl = answer.get('ttl')
|
||||||
|
ttls.append(ttl)
|
||||||
|
name = answer.get('name') # 去除最右边的`.`点号
|
||||||
|
cname = name[:-1].lower() # 去除最右边的`.`点号
|
||||||
|
cnames.append(cname)
|
||||||
|
cname_num = appear_times.get(cname)
|
||||||
|
cname_times.append(cname_num)
|
||||||
|
ip = answer.get('data')
|
||||||
|
ips.append(ip)
|
||||||
|
ip_num = appear_times.get(ip)
|
||||||
|
ip_times.append(ip_num)
|
||||||
|
isvalid, reason = wildcard.is_valid_subdomain(ip, ip_num, cname, cname_num, ttl, wc_ttl, wc_ips)
|
||||||
|
logger.log('TRACE', f'{ip} effective: {isvalid} reason: {reason}')
|
||||||
|
is_valid_flags.append(isvalid)
|
||||||
|
if not have_a_record:
|
||||||
|
logger.log('TRACE', f'All query result of {qname} no A record{answers}')
|
||||||
|
# 为了优化内存 只添加有A记录且通过判断的子域到记录中
|
||||||
|
if have_a_record and all(is_valid_flags):
|
||||||
|
info['resolve'] = 1
|
||||||
|
info['reason'] = reason
|
||||||
|
info['ttl'] = ttls
|
||||||
|
info['cname'] = cnames
|
||||||
|
info['ip'] = ips
|
||||||
|
info['ip_times'] = ip_times
|
||||||
|
info['cname_times'] = cname_times
|
||||||
|
info['resolver'] = resolver
|
||||||
|
infos[qname] = info
|
||||||
|
subdomains.append(qname)
|
||||||
|
return infos, subdomains
|
||||||
|
|
||||||
|
|
||||||
|
def stat_appear_times(result_path):
|
||||||
|
logger.log('INFOR', f'Counting IP cname appear times')
|
||||||
|
times = dict()
|
||||||
|
logger.log('DEBUG', f'Reading {result_path}')
|
||||||
|
with open(result_path) as fd:
|
||||||
|
for line in fd:
|
||||||
|
line = line.strip()
|
||||||
|
try:
|
||||||
|
items = json.loads(line)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
logger.log('ERROR', f'Error parsing {result_path} '
|
||||||
|
f'line {line} Skip this line')
|
||||||
|
continue
|
||||||
|
status = items.get('status')
|
||||||
|
if status != 'NOERROR':
|
||||||
|
continue
|
||||||
|
data = items.get('data')
|
||||||
|
if 'answers' not in data:
|
||||||
|
continue
|
||||||
|
answers = data.get('answers')
|
||||||
|
for answer in answers:
|
||||||
|
if answer.get('type') == 'A':
|
||||||
|
ip = answer.get('data')
|
||||||
|
# 取值 如果是首次出现的IP集合 出现次数先赋值0
|
||||||
|
value_one = times.setdefault(ip, 0)
|
||||||
|
times[ip] = value_one + 1
|
||||||
|
name = answer.get('data')
|
||||||
|
cname = name[:-1].lower() # 去除最右边的`.`点号
|
||||||
|
# 取值 如果是首次出现的IP集合 出现次数先赋值0
|
||||||
|
value_two = times.setdefault(cname, 0)
|
||||||
|
times[cname] = value_two + 1
|
||||||
|
if answer.get('type') == 'CNAME':
|
||||||
|
name = answer.get('data')
|
||||||
|
cname = name[:-1].lower() # 去除最右边的`.`点号
|
||||||
|
# 取值 如果是首次出现的IP集合 出现次数先赋值0
|
||||||
|
value_three = times.setdefault(cname, 0)
|
||||||
|
times[cname] = value_three + 1
|
||||||
|
return times
|
||||||
|
|
||||||
|
|
||||||
|
def deal_output(output_path, appear_times, wildcard_ips, wildcard_ttl):
|
||||||
|
logger.log('INFOR', f'Processing result')
|
||||||
|
infos = dict() # 用来记录所有域名有关信息
|
||||||
|
subdomains = list() # 用来保存所有通过有效性检查的子域
|
||||||
|
logger.log('DEBUG', f'Processing {output_path}')
|
||||||
|
with open(output_path) as fd:
|
||||||
|
for line in fd:
|
||||||
|
line = line.strip()
|
||||||
|
try:
|
||||||
|
items = json.loads(line)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
logger.log('ERROR', f'Error parsing {line} Skip this line')
|
||||||
|
continue
|
||||||
|
qname = items.get('name')[:-1] # 去除最右边的`.`点号
|
||||||
|
status = items.get('status')
|
||||||
|
if status != 'NOERROR':
|
||||||
|
logger.log('TRACE', f'Found {qname}\'s result {status} '
|
||||||
|
f'while processing {line}')
|
||||||
|
continue
|
||||||
|
data = items.get('data')
|
||||||
|
if 'answers' not in data:
|
||||||
|
logger.log('TRACE', f'Processing {line}, {qname} no response')
|
||||||
|
continue
|
||||||
|
infos, subdomains = gen_result_infos(items, infos, subdomains,
|
||||||
|
appear_times, wildcard_ips,
|
||||||
|
wildcard_ttl)
|
||||||
|
return infos, subdomains
|
||||||
|
|
||||||
|
|
||||||
|
def save_brute_dict(dict_path, dict_set):
|
||||||
|
dict_data = '\n'.join(dict_set)
|
||||||
|
if not utils.save_to_file(dict_path, dict_data):
|
||||||
|
logger.log('FATAL', 'Saving dictionary error')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_file(dict_path, output_path):
|
||||||
|
if settings.delete_generated_dict:
|
||||||
|
dict_path.unlink()
|
||||||
|
if settings.delete_massdns_result:
|
||||||
|
output_path.unlink()
|
||||||
|
|
||||||
|
|
||||||
|
class Brute(Module):
|
||||||
|
"""
|
||||||
|
OneForAll subdomain brute module
|
||||||
|
|
||||||
|
Example:
|
||||||
|
brute.py --target domain.com --word True run
|
||||||
|
brute.py --targets ./domains.txt --word True run
|
||||||
|
brute.py --target domain.com --word True --concurrent 2000 run
|
||||||
|
brute.py --target domain.com --word True --wordlist subnames.txt run
|
||||||
|
brute.py --target domain.com --word True --recursive True --depth 2 run
|
||||||
|
brute.py --target d.com --fuzz True --place m.*.d.com --rule '[a-z]' run
|
||||||
|
brute.py --target d.com --fuzz True --place m.*.d.com --fuzzlist subnames.txt run
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--fmt csv/json (result format)
|
||||||
|
--path Result path (default None, automatically generated)
|
||||||
|
|
||||||
|
|
||||||
|
:param str target: One domain (target or targets must be provided)
|
||||||
|
:param str targets: File path of one domain per line
|
||||||
|
:param int concurrent: Number of concurrent (default 2000)
|
||||||
|
:param bool word: Use word mode generate dictionary (default False)
|
||||||
|
:param str wordlist: Dictionary path used in word mode (default use ./config/default.py)
|
||||||
|
:param bool recursive: Use recursion (default False)
|
||||||
|
:param int depth: Recursive depth (default 2)
|
||||||
|
:param str nextlist: Dictionary file path used by recursive (default use ./config/default.py)
|
||||||
|
:param bool fuzz: Use fuzz mode generate dictionary (default False)
|
||||||
|
:param bool alive: Only export alive subdomains (default False)
|
||||||
|
:param str place: Designated fuzz position (required if use fuzz mode)
|
||||||
|
:param str rule: Specify the regexp rules used in fuzz mode (required if use fuzz mode)
|
||||||
|
:param str fuzzlist: Dictionary path used in fuzz mode (default use ./config/default.py)
|
||||||
|
:param bool export: Export the results (default True)
|
||||||
|
:param str fmt: Result format (default csv)
|
||||||
|
:param str path: Result directory (default None)
|
||||||
|
"""
|
||||||
|
def __init__(self, target=None, targets=None, concurrent=None,
|
||||||
|
word=False, wordlist=None, recursive=False, depth=None,
|
||||||
|
nextlist=None, fuzz=False, place=None, rule=None, fuzzlist=None,
|
||||||
|
export=True, alive=True, fmt='csv', path=None):
|
||||||
|
Module.__init__(self)
|
||||||
|
self.module = 'Brute'
|
||||||
|
self.source = 'Brute'
|
||||||
|
self.target = target
|
||||||
|
self.targets = targets
|
||||||
|
self.concurrent_num = concurrent or settings.brute_concurrent_num
|
||||||
|
self.word = word
|
||||||
|
self.wordlist = wordlist or settings.brute_wordlist_path
|
||||||
|
self.recursive_brute = recursive or settings.enable_recursive_brute
|
||||||
|
self.recursive_depth = depth or settings.brute_recursive_depth
|
||||||
|
self.recursive_nextlist = nextlist or settings.recursive_nextlist_path
|
||||||
|
self.fuzz = fuzz or settings.enable_fuzz
|
||||||
|
self.place = place or settings.fuzz_place
|
||||||
|
self.rule = rule or settings.fuzz_rule
|
||||||
|
self.fuzzlist = fuzzlist or settings.fuzz_list
|
||||||
|
self.export = export
|
||||||
|
self.alive = alive
|
||||||
|
self.fmt = fmt
|
||||||
|
self.path = path
|
||||||
|
self.bulk = False # 是否是批量爆破场景
|
||||||
|
self.domains = list() # 待爆破的所有域名集合
|
||||||
|
self.domain = str() # 当前正在进行爆破的域名
|
||||||
|
self.ips_times = dict() # IP集合出现次数
|
||||||
|
self.enable_wildcard = None # 当前域名是否使用泛解析
|
||||||
|
self.quite = False
|
||||||
|
|
||||||
|
def gen_brute_dict(self, domain):
|
||||||
|
logger.log('INFOR', f'Generating dictionary for {domain}')
|
||||||
|
dict_set = set()
|
||||||
|
# 如果domain不是self.subdomain 而是self.domain的子域则生成递归爆破字典
|
||||||
|
if self.word:
|
||||||
|
self.place = ''
|
||||||
|
if not self.place:
|
||||||
|
self.place = '*.' + domain
|
||||||
|
wordlist = self.wordlist
|
||||||
|
main_domain = utils.get_main_domain(domain)
|
||||||
|
if domain != main_domain:
|
||||||
|
wordlist = self.recursive_nextlist
|
||||||
|
if self.word:
|
||||||
|
word_subdomains = gen_word_subdomains(self.place, wordlist)
|
||||||
|
dict_set.update(word_subdomains)
|
||||||
|
if self.fuzz:
|
||||||
|
fuzz_subdomains = gen_fuzz_subdomains(self.place, self.rule, self.fuzzlist)
|
||||||
|
dict_set.update(fuzz_subdomains)
|
||||||
|
count = len(dict_set)
|
||||||
|
logger.log('INFOR', f'Dictionary size: {count}')
|
||||||
|
if count > 10000000:
|
||||||
|
logger.log('ALERT', f'The generated dictionary is '
|
||||||
|
f'too large {count} > 10000000')
|
||||||
|
return dict_set
|
||||||
|
|
||||||
|
def check_brute_params(self):
|
||||||
|
if not (self.word or self.fuzz):
|
||||||
|
logger.log('FATAL', f'Please specify at least one brute mode')
|
||||||
|
exit(1)
|
||||||
|
if len(self.domains) > 1:
|
||||||
|
self.bulk = True
|
||||||
|
if self.fuzz:
|
||||||
|
if self.place is None:
|
||||||
|
logger.log('FATAL', f'No fuzz position specified')
|
||||||
|
exit(1)
|
||||||
|
if self.rule is None and self.fuzzlist is None:
|
||||||
|
logger.log('FATAL', f'No fuzz rules or fuzz dictionary specified')
|
||||||
|
exit(1)
|
||||||
|
if self.bulk:
|
||||||
|
logger.log('FATAL', f'Cannot use fuzz mode in the bulk brute')
|
||||||
|
exit(1)
|
||||||
|
if self.recursive_brute:
|
||||||
|
logger.log('FATAL', f'Cannot use recursive brute in fuzz mode')
|
||||||
|
exit(1)
|
||||||
|
fuzz_count = self.place.count('*')
|
||||||
|
if fuzz_count < 1:
|
||||||
|
logger.log('FATAL', f'No fuzz position specified')
|
||||||
|
exit(1)
|
||||||
|
if fuzz_count > 1:
|
||||||
|
logger.log('FATAL', f'Only one fuzz position can be specified')
|
||||||
|
exit(1)
|
||||||
|
if self.domain not in self.place:
|
||||||
|
logger.log('FATAL', f'Incorrect domain for fuzz')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
def init_dict_path(self):
|
||||||
|
data_dir = settings.data_storage_dir
|
||||||
|
if self.wordlist is None:
|
||||||
|
self.wordlist = settings.brute_wordlist_path or data_dir.joinpath('subnames.txt')
|
||||||
|
if self.recursive_nextlist is None:
|
||||||
|
self.recursive_nextlist = settings.recursive_nextlist_path or data_dir.joinpath('subnames_next.txt')
|
||||||
|
|
||||||
|
def main(self, domain):
|
||||||
|
start = time.time()
|
||||||
|
logger.log('INFOR', f'Blasting {domain} ')
|
||||||
|
massdns_dir = settings.third_party_dir.joinpath('massdns')
|
||||||
|
result_dir = settings.result_save_dir
|
||||||
|
temp_dir = result_dir.joinpath('temp')
|
||||||
|
utils.check_dir(temp_dir)
|
||||||
|
massdns_path = utils.get_massdns_path(massdns_dir)
|
||||||
|
timestring = utils.get_timestring()
|
||||||
|
|
||||||
|
wildcard_ips = list() # 泛解析IP列表
|
||||||
|
wildcard_ttl = int() # 泛解析TTL整型值
|
||||||
|
ns_list = query_domain_ns(self.domain)
|
||||||
|
ns_ip_list = query_domain_ns_a(ns_list) # DNS权威名称服务器对应A记录列表
|
||||||
|
if self.enable_wildcard is None:
|
||||||
|
self.enable_wildcard = wildcard.detect_wildcard(domain)
|
||||||
|
|
||||||
|
if self.enable_wildcard:
|
||||||
|
wildcard_ips, wildcard_ttl = wildcard.collect_wildcard_record(domain, ns_ip_list)
|
||||||
|
ns_path = utils.get_ns_path(settings.use_china_nameservers, self.enable_wildcard, ns_ip_list)
|
||||||
|
|
||||||
|
dict_set = self.gen_brute_dict(domain)
|
||||||
|
|
||||||
|
dict_name = f'generated_subdomains_{domain}_{timestring}.txt'
|
||||||
|
dict_path = temp_dir.joinpath(dict_name)
|
||||||
|
save_brute_dict(dict_path, dict_set)
|
||||||
|
del dict_set
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
output_name = f'resolved_result_{domain}_{timestring}.json'
|
||||||
|
output_path = temp_dir.joinpath(output_name)
|
||||||
|
log_path = result_dir.joinpath('massdns.log')
|
||||||
|
check_dict()
|
||||||
|
logger.log('INFOR', f'Running massdns to brute subdomains')
|
||||||
|
utils.call_massdns(massdns_path, dict_path, ns_path, output_path,
|
||||||
|
log_path, quiet_mode=self.quite,
|
||||||
|
concurrent_num=self.concurrent_num)
|
||||||
|
appear_times = stat_appear_times(output_path)
|
||||||
|
self.infos, self.subdomains = deal_output(output_path, appear_times,
|
||||||
|
wildcard_ips, wildcard_ttl)
|
||||||
|
delete_file(dict_path, output_path)
|
||||||
|
end = time.time()
|
||||||
|
self.elapse = round(end - start, 1)
|
||||||
|
logger.log('ALERT', f'{self.source} module takes {self.elapse} seconds, '
|
||||||
|
f'found {len(self.subdomains)} subdomains of {domain}')
|
||||||
|
logger.log('DEBUG', f'{self.source} module found subdomains of {domain}: '
|
||||||
|
f'{self.subdomains}')
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
return self.subdomains
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
logger.log('INFOR', f'Start running {self.source} module')
|
||||||
|
self.domains = utils.get_domains(self.target, self.targets)
|
||||||
|
for self.domain in self.domains:
|
||||||
|
self.results = list() # 置空
|
||||||
|
all_subdomains = list()
|
||||||
|
self.init_dict_path()
|
||||||
|
self.check_brute_params()
|
||||||
|
if self.recursive_brute:
|
||||||
|
logger.log('INFOR', f'Start recursively brute the 1 layer subdomain'
|
||||||
|
f' of {self.domain}')
|
||||||
|
valid_subdomains = self.main(self.domain)
|
||||||
|
|
||||||
|
all_subdomains.extend(valid_subdomains)
|
||||||
|
|
||||||
|
# 递归爆破下一层的子域
|
||||||
|
# fuzz模式不使用递归爆破
|
||||||
|
if self.recursive_brute:
|
||||||
|
for layer_num in range(1, self.recursive_depth):
|
||||||
|
# 之前已经做过1层子域爆破 当前实际递归层数是layer+1
|
||||||
|
logger.log('INFOR', f'Start recursively brute the {layer_num + 1} '
|
||||||
|
f'layer subdomain of {self.domain}')
|
||||||
|
for subdomain in all_subdomains:
|
||||||
|
self.place = '*.' + subdomain
|
||||||
|
# 进行下一层子域爆破的限制条件
|
||||||
|
num = subdomain.count('.') - self.domain.count('.')
|
||||||
|
if num == layer_num:
|
||||||
|
valid_subdomains = self.main(subdomain)
|
||||||
|
all_subdomains.extend(valid_subdomains)
|
||||||
|
|
||||||
|
logger.log('INFOR', f'Finished {self.source} module to brute {self.domain}')
|
||||||
|
if not self.path:
|
||||||
|
name = f'{self.domain}_brute_result.{self.fmt}'
|
||||||
|
self.path = settings.result_save_dir.joinpath(name)
|
||||||
|
# 数据库导出
|
||||||
|
if self.export:
|
||||||
|
export.export_data(self.domain,
|
||||||
|
alive=self.alive,
|
||||||
|
limit='resolve',
|
||||||
|
path=self.path,
|
||||||
|
fmt=self.fmt)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
fire.Fire(Brute)
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import requests
|
||||||
|
from config.log import logger
|
||||||
|
from common.module import Module
|
||||||
|
|
||||||
|
|
||||||
|
class Check(Module):
|
||||||
|
"""
|
||||||
|
Check base class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
Module.__init__(self)
|
||||||
|
self.request_status = 1
|
||||||
|
|
||||||
|
def to_check(self, filenames):
|
||||||
|
urls = set()
|
||||||
|
urls_www = set()
|
||||||
|
for filename in filenames:
|
||||||
|
urls.update((
|
||||||
|
f'http://{self.domain}/{filename}',
|
||||||
|
f'https://{self.domain}/{filename}',
|
||||||
|
))
|
||||||
|
urls_www.update((
|
||||||
|
f'http://www.{self.domain}/{filename}',
|
||||||
|
f'https://www.{self.domain}/{filename}'
|
||||||
|
))
|
||||||
|
self.check_loop(urls)
|
||||||
|
self.check_loop(urls_www)
|
||||||
|
|
||||||
|
def check_loop(self, urls):
|
||||||
|
for url in urls:
|
||||||
|
self.header = self.get_header()
|
||||||
|
self.proxy = self.get_proxy(self.source)
|
||||||
|
try:
|
||||||
|
resp = self.get(url, check=False, ignore=True, raise_error=True)
|
||||||
|
except requests.exceptions.ConnectTimeout:
|
||||||
|
logger.log('DEBUG', f'Connection to {url} timed out, so break check')
|
||||||
|
break
|
||||||
|
self.subdomains = self.collect_subdomains(resp)
|
||||||
|
if self.subdomains:
|
||||||
|
break
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# coding=utf-8
|
from common.module import Module
|
||||||
from .module import Module
|
|
||||||
|
|
||||||
|
class Crawl(Module):
|
||||||
class Crawl(Module):
|
"""
|
||||||
"""
|
Crawl base class
|
||||||
|
"""
|
||||||
"""
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Module.__init__(self)
|
Module.__init__(self)
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
"""
|
||||||
|
SQLite database initialization and operation
|
||||||
|
"""
|
||||||
|
|
||||||
|
from common import records
|
||||||
|
|
||||||
|
from common.records import Connection
|
||||||
|
from config.log import logger
|
||||||
|
from config import settings
|
||||||
|
|
||||||
|
|
||||||
|
class Database(object):
|
||||||
|
def __init__(self, db_path=None):
|
||||||
|
self.conn = self.get_conn(db_path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_conn(db_path):
|
||||||
|
"""
|
||||||
|
Get database connection
|
||||||
|
|
||||||
|
:param db_path: Database path
|
||||||
|
:return: db_conn: SQLite database connection
|
||||||
|
"""
|
||||||
|
logger.log('TRACE', f'Establishing database connection')
|
||||||
|
if isinstance(db_path, Connection):
|
||||||
|
return db_path
|
||||||
|
protocol = 'sqlite:///'
|
||||||
|
if not db_path: # 数据库路径为空连接默认数据库
|
||||||
|
db_path = f'{protocol}{settings.result_save_dir}/result.sqlite3'
|
||||||
|
else:
|
||||||
|
db_path = f'{protocol}{db_path}'
|
||||||
|
db = records.Database(db_path) # 不存在数据库时会新建一个数据库
|
||||||
|
logger.log('TRACE', f'Use the database: {db_path}')
|
||||||
|
return db.get_connection()
|
||||||
|
|
||||||
|
def query(self, sql):
|
||||||
|
try:
|
||||||
|
results = self.conn.query(sql)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
return None
|
||||||
|
return results
|
||||||
|
|
||||||
|
def create_table(self, table_name):
|
||||||
|
"""
|
||||||
|
Create table
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
if self.exist_table(table_name):
|
||||||
|
logger.log('TRACE', f'{table_name} table already exists')
|
||||||
|
return
|
||||||
|
logger.log('TRACE', f'Creating {table_name} table')
|
||||||
|
self.query(f'create table "{table_name}" ('
|
||||||
|
f'id integer primary key,'
|
||||||
|
f'alive int,'
|
||||||
|
f'request int,'
|
||||||
|
f'resolve int,'
|
||||||
|
f'url text,'
|
||||||
|
f'subdomain text,'
|
||||||
|
f'port int,'
|
||||||
|
f'level int,'
|
||||||
|
f'cname text,'
|
||||||
|
f'ip text,'
|
||||||
|
f'public int,'
|
||||||
|
f'cdn int,'
|
||||||
|
f'status int,'
|
||||||
|
f'reason text,'
|
||||||
|
f'title text,'
|
||||||
|
f'banner text,'
|
||||||
|
f'header text,'
|
||||||
|
f'history text,'
|
||||||
|
f'response text,'
|
||||||
|
f'ip_times text,'
|
||||||
|
f'cname_times text,'
|
||||||
|
f'ttl text,'
|
||||||
|
f'cidr text,'
|
||||||
|
f'asn text,'
|
||||||
|
f'org text,'
|
||||||
|
f'addr text,'
|
||||||
|
f'isp text,'
|
||||||
|
f'resolver text,'
|
||||||
|
f'module text,'
|
||||||
|
f'source text,'
|
||||||
|
f'elapse float,'
|
||||||
|
f'find int)')
|
||||||
|
|
||||||
|
def insert_table(self, table_name, result):
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
self.conn.query(
|
||||||
|
f'insert into "{table_name}" '
|
||||||
|
f'(id, alive, resolve, request, url, subdomain, port, level,'
|
||||||
|
f'cname, ip, public, cdn, status, reason, title, banner, header,'
|
||||||
|
f'history, response, ip_times, cname_times, ttl, cidr, asn, org,'
|
||||||
|
f'addr, isp, resolver, module, source, elapse, find) '
|
||||||
|
f'values (:id, :alive, :resolve, :request, :url,'
|
||||||
|
f':subdomain, :port, :level, :cname, :ip, :public, :cdn,'
|
||||||
|
f':status, :reason, :title, :banner, :header, :history, :response,'
|
||||||
|
f':ip_times, :cname_times, :ttl, :cidr, :asn, :org, :addr, :isp,'
|
||||||
|
f':resolver, :module, :source, :elapse, :find)', **result)
|
||||||
|
|
||||||
|
def save_db(self, table_name, results, module_name=None):
|
||||||
|
"""
|
||||||
|
Save the results of each module in the database
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
:param list results: results list
|
||||||
|
:param str module_name: module
|
||||||
|
"""
|
||||||
|
logger.log('TRACE', f'Saving the subdomain results of {table_name} '
|
||||||
|
f'found by module {module_name} into database')
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
if results:
|
||||||
|
try:
|
||||||
|
self.conn.bulk_query(
|
||||||
|
f'insert into "{table_name}" '
|
||||||
|
f'(id, alive, resolve, request, url, subdomain, port, level, '
|
||||||
|
f'cname, ip, public, cdn, status, reason, title, banner, header, '
|
||||||
|
f'history, response, ip_times, cname_times, ttl, cidr, asn, org, '
|
||||||
|
f'addr, isp, resolver, module, source, elapse, find) '
|
||||||
|
f'values (:id, :alive, :resolve, :request, :url, '
|
||||||
|
f':subdomain, :port, :level, :cname, :ip, :public, :cdn,'
|
||||||
|
f':status, :reason, :title, :banner, :header, :history, :response, '
|
||||||
|
f':ip_times, :cname_times, :ttl, :cidr, :asn, :org, :addr, :isp, '
|
||||||
|
f':resolver, :module, :source, :elapse, :find)', results)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e)
|
||||||
|
|
||||||
|
def exist_table(self, table_name):
|
||||||
|
"""
|
||||||
|
Determine table exists
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
:return bool: Whether table exists
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
logger.log('TRACE', f'Determining whether the {table_name} table exists')
|
||||||
|
results = self.query(f'select count() from sqlite_master where type = "table" and'
|
||||||
|
f' name = "{table_name}"')
|
||||||
|
if results.scalar() == 0:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def copy_table(self, table_name, bak_table_name):
|
||||||
|
"""
|
||||||
|
Copy table to create backup
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
:param str bak_table_name: new table name
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
bak_table_name = bak_table_name.replace('.', '_')
|
||||||
|
logger.log('TRACE', f'Copying {table_name} table to {bak_table_name} new table')
|
||||||
|
self.query(f'drop table if exists "{bak_table_name}"')
|
||||||
|
self.query(f'create table "{bak_table_name}" '
|
||||||
|
f'as select * from "{table_name}"')
|
||||||
|
|
||||||
|
def clear_table(self, table_name):
|
||||||
|
"""
|
||||||
|
Clear the table
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
logger.log('TRACE', f'Clearing data in table {table_name}')
|
||||||
|
self.query(f'delete from "{table_name}"')
|
||||||
|
|
||||||
|
def drop_table(self, table_name):
|
||||||
|
"""
|
||||||
|
Delete table
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
logger.log('TRACE', f'Deleting {table_name} table')
|
||||||
|
self.query(f'drop table if exists "{table_name}"')
|
||||||
|
|
||||||
|
def rename_table(self, table_name, new_table_name):
|
||||||
|
"""
|
||||||
|
Rename table name
|
||||||
|
|
||||||
|
:param str table_name: old table name
|
||||||
|
:param str new_table_name: new table name
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
new_table_name = new_table_name.replace('.', '_')
|
||||||
|
logger.log('TRACE', f'Renaming {table_name} table to {new_table_name} table')
|
||||||
|
self.query(f'alter table "{table_name}" '
|
||||||
|
f'rename to "{new_table_name}"')
|
||||||
|
|
||||||
|
def deduplicate_subdomain(self, table_name):
|
||||||
|
"""
|
||||||
|
Deduplicate subdomains in the table
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
logger.log('TRACE', f'Deduplicating subdomains in {table_name} table')
|
||||||
|
self.query(f'delete from "{table_name}" where '
|
||||||
|
f'id not in (select min(id) '
|
||||||
|
f'from "{table_name}" group by subdomain)')
|
||||||
|
|
||||||
|
def remove_invalid(self, table_name):
|
||||||
|
"""
|
||||||
|
Remove nulls or invalid subdomains in the table
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
logger.log('TRACE', f'Removing invalid subdomains in {table_name} table')
|
||||||
|
self.query(f'delete from "{table_name}" where '
|
||||||
|
f'subdomain is null or resolve == 0')
|
||||||
|
|
||||||
|
def get_data(self, table_name):
|
||||||
|
"""
|
||||||
|
Get all the data in the table
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
logger.log('TRACE', f'Get all the data from {table_name} table')
|
||||||
|
return self.query(f'select * from "{table_name}"')
|
||||||
|
|
||||||
|
def export_data(self, table_name, alive, limit):
|
||||||
|
"""
|
||||||
|
Get part of the data in the table
|
||||||
|
|
||||||
|
:param str table_name: table name
|
||||||
|
:param any alive: alive flag
|
||||||
|
:param str limit: limit value
|
||||||
|
"""
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
sql = f'select id, alive, request, resolve, url, subdomain, level,' \
|
||||||
|
f'cname, ip, public, cdn, port, status, reason, title, banner,' \
|
||||||
|
f'cidr, asn, org, addr, isp, source from "{table_name}" '
|
||||||
|
if alive and limit:
|
||||||
|
if limit in ['resolve', 'request']:
|
||||||
|
where = f' where {limit} = 1'
|
||||||
|
sql += where
|
||||||
|
elif alive:
|
||||||
|
where = f' where alive = 1'
|
||||||
|
sql += where
|
||||||
|
sql += ' order by subdomain'
|
||||||
|
logger.log('TRACE', f'Get the data from {table_name} table')
|
||||||
|
return self.query(sql)
|
||||||
|
|
||||||
|
def count_alive(self, table_name):
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
sql = f'select count() from "{table_name}" where alive = 1'
|
||||||
|
return self.query(sql)
|
||||||
|
|
||||||
|
def get_resp_by_url(self, table_name, url):
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
sql = f'select response from "{table_name}" where url = "{url}"'
|
||||||
|
logger.log('TRACE', f'Get response data from {url}')
|
||||||
|
return self.query(sql).scalar()
|
||||||
|
|
||||||
|
def get_data_by_fields(self, table_name, fields):
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
field_str = ', '.join(fields)
|
||||||
|
sql = f'select {field_str} from "{table_name}"'
|
||||||
|
logger.log('TRACE', f'Get specified field data {fields} from {table_name} table')
|
||||||
|
return self.query(sql)
|
||||||
|
|
||||||
|
def update_data_by_url(self, table_name, info, url):
|
||||||
|
table_name = table_name.replace('.', '_')
|
||||||
|
field_str = ', '.join(map(lambda kv: f'{kv[0]} = "{kv[1]}"', info.items()))
|
||||||
|
sql = f'update "{table_name}" set {field_str} where url = "{url}"'
|
||||||
|
return self.query(sql)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""
|
||||||
|
Close the database connection
|
||||||
|
"""
|
||||||
|
self.conn.close()
|
||||||
@@ -1,64 +1,63 @@
|
|||||||
# coding=utf-8
|
import re
|
||||||
import re
|
from common import tldextract
|
||||||
import tldextract
|
from config import settings
|
||||||
import config
|
|
||||||
|
|
||||||
|
class Domain(object):
|
||||||
class Domain(object):
|
"""
|
||||||
"""
|
Processing domain class
|
||||||
域名处理类
|
|
||||||
|
:param str string: input string
|
||||||
:param str string: 传入的字符串
|
"""
|
||||||
"""
|
def __init__(self, string):
|
||||||
def __init__(self, string):
|
self.string = str(string)
|
||||||
self.string = str(string)
|
self.regexp = r'\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b'
|
||||||
self.regexp = r'\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b'
|
self.domain = None
|
||||||
self.domain = None
|
|
||||||
|
def match(self):
|
||||||
def match(self):
|
"""
|
||||||
"""
|
match domain
|
||||||
域名匹配
|
|
||||||
|
:return : result
|
||||||
:return: 匹配结果
|
"""
|
||||||
"""
|
result = re.search(self.regexp, self.string, re.I)
|
||||||
result = re.search(self.regexp, self.string, re.I)
|
if result:
|
||||||
if result:
|
return result.group()
|
||||||
return result.group()
|
return None
|
||||||
else:
|
|
||||||
return None
|
def extract(self):
|
||||||
|
"""
|
||||||
def extract(self):
|
extract domain
|
||||||
"""
|
|
||||||
域名导出
|
>>> d = Domain('www.example.com')
|
||||||
|
<domain.Domain object>
|
||||||
>>>d = Domain('www.example.com')
|
>>> d.extract()
|
||||||
<domain.Domain object>
|
ExtractResult(subdomain='www', domain='example', suffix='com')
|
||||||
>>>d.extract()
|
|
||||||
ExtractResult(subdomain='www', domain='example', suffix='com')
|
:return: extracted domain results
|
||||||
|
"""
|
||||||
:return: 导出结果
|
data_storage_dir = settings.data_storage_dir
|
||||||
"""
|
extract_cache_file = data_storage_dir.joinpath('public_suffix_list.dat')
|
||||||
extract_cache_file = config.data_storage_path.joinpath('public_suffix_list.dat')
|
ext = tldextract.TLDExtract(extract_cache_file)
|
||||||
tldext = tldextract.TLDExtract(extract_cache_file)
|
result = self.match()
|
||||||
result = self.match()
|
if result:
|
||||||
if result:
|
return ext(result)
|
||||||
return tldext(result)
|
return None
|
||||||
else:
|
|
||||||
return None
|
def registered(self):
|
||||||
|
"""
|
||||||
def registered(self):
|
registered domain
|
||||||
"""
|
|
||||||
获取注册域名
|
>>> d = Domain('www.example.com')
|
||||||
|
<domain.Domain object>
|
||||||
>>>d = Domain('www.example.com')
|
>>> d.registered()
|
||||||
<domain.Domain object>
|
example.com
|
||||||
>>>d.registered()
|
|
||||||
example.com
|
:return: registered domain result
|
||||||
|
"""
|
||||||
:return: 注册域名
|
if not settings.use_tld_extract:
|
||||||
"""
|
return self.string
|
||||||
result = self.extract()
|
result = self.extract()
|
||||||
if result:
|
if result:
|
||||||
return result.registered_domain
|
return result.registered_domain
|
||||||
else:
|
return None
|
||||||
return None
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import zipfile
|
||||||
|
|
||||||
|
from common.utils import ip_to_int
|
||||||
|
from config.setting import data_storage_dir
|
||||||
|
from common.database import Database
|
||||||
|
|
||||||
|
|
||||||
|
def get_db_path():
|
||||||
|
zip_path = data_storage_dir.joinpath('ip2location.zip')
|
||||||
|
db_path = data_storage_dir.joinpath('ip2location.db')
|
||||||
|
if db_path.exists():
|
||||||
|
return db_path
|
||||||
|
zf = zipfile.ZipFile(str(zip_path))
|
||||||
|
zf.extract('ip2location.db', data_storage_dir)
|
||||||
|
return db_path
|
||||||
|
|
||||||
|
|
||||||
|
class IPAsnInfo(Database):
|
||||||
|
def __init__(self):
|
||||||
|
path = get_db_path()
|
||||||
|
Database.__init__(self, path)
|
||||||
|
|
||||||
|
def find(self, ip):
|
||||||
|
info = {'cidr': '', 'asn': '', 'org': ''}
|
||||||
|
if isinstance(ip, (int, str)):
|
||||||
|
ip = ip_to_int(ip)
|
||||||
|
else:
|
||||||
|
return info
|
||||||
|
sql = f'SELECT * FROM asn WHERE ip_from <= {ip} AND ip_to >= {ip} LIMIT 1;'
|
||||||
|
result = self.query(sql)
|
||||||
|
if not hasattr(result, 'dataset'):
|
||||||
|
return info
|
||||||
|
asn = result.as_dict()
|
||||||
|
info['cidr'] = asn[0]['cidr']
|
||||||
|
info['asn'] = f"AS{asn[0]['asn']}"
|
||||||
|
info['org'] = asn[0]['as']
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asn_info = IPAsnInfo()
|
||||||
|
print(asn_info.find("188.81.94.77"))
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
"""
|
||||||
|
" ip2region python searcher client module
|
||||||
|
"
|
||||||
|
" Author: koma<komazhang@foxmail.com>
|
||||||
|
" Date : 2015-11-06
|
||||||
|
"""
|
||||||
|
import io
|
||||||
|
import sys
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
|
||||||
|
from config import settings
|
||||||
|
|
||||||
|
|
||||||
|
class IpRegInfo(object):
|
||||||
|
__INDEX_BLOCK_LENGTH = 12
|
||||||
|
__TOTAL_HEADER_LENGTH = 8192
|
||||||
|
|
||||||
|
__f = None
|
||||||
|
__headerSip = []
|
||||||
|
__headerPtr = []
|
||||||
|
__headerLen = 0
|
||||||
|
__indexSPtr = 0
|
||||||
|
__indexLPtr = 0
|
||||||
|
__indexCount = 0
|
||||||
|
__dbBinStr = ''
|
||||||
|
|
||||||
|
def __init__(self, db_file):
|
||||||
|
self.init_database(db_file)
|
||||||
|
|
||||||
|
def memory_search(self, ip):
|
||||||
|
"""
|
||||||
|
" memory search method
|
||||||
|
" param: ip
|
||||||
|
"""
|
||||||
|
if not ip.isdigit():
|
||||||
|
ip = self.ip2long(ip)
|
||||||
|
|
||||||
|
if self.__dbBinStr == '':
|
||||||
|
self.__dbBinStr = self.__f.read() # read all the contents in file
|
||||||
|
self.__indexSPtr = self.get_long(self.__dbBinStr, 0)
|
||||||
|
self.__indexLPtr = self.get_long(self.__dbBinStr, 4)
|
||||||
|
self.__indexCount = int((self.__indexLPtr - self.__indexSPtr) /
|
||||||
|
self.__INDEX_BLOCK_LENGTH) + 1
|
||||||
|
|
||||||
|
l, h, data_ptr = (0, self.__indexCount, 0)
|
||||||
|
while l <= h:
|
||||||
|
m = int((l + h) >> 1)
|
||||||
|
p = self.__indexSPtr + m * self.__INDEX_BLOCK_LENGTH
|
||||||
|
sip = self.get_long(self.__dbBinStr, p)
|
||||||
|
|
||||||
|
if ip < sip:
|
||||||
|
h = m - 1
|
||||||
|
else:
|
||||||
|
eip = self.get_long(self.__dbBinStr, p + 4)
|
||||||
|
if ip > eip:
|
||||||
|
l = m + 1
|
||||||
|
else:
|
||||||
|
data_ptr = self.get_long(self.__dbBinStr, p + 8)
|
||||||
|
break
|
||||||
|
|
||||||
|
if data_ptr == 0:
|
||||||
|
raise Exception("Data pointer not found")
|
||||||
|
|
||||||
|
return self.return_data(data_ptr)
|
||||||
|
|
||||||
|
def init_database(self, db_file):
|
||||||
|
"""
|
||||||
|
" initialize the database for search
|
||||||
|
" param: dbFile
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.__f = io.open(db_file, "rb")
|
||||||
|
except IOError as e:
|
||||||
|
print("[Error]: %s" % e)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def return_data(self, data_ptr):
|
||||||
|
"""
|
||||||
|
" get ip data from db file by data start ptr
|
||||||
|
" param: data ptr
|
||||||
|
"""
|
||||||
|
data_len = (data_ptr >> 24) & 0xFF
|
||||||
|
data_ptr = data_ptr & 0x00FFFFFF
|
||||||
|
|
||||||
|
self.__f.seek(data_ptr)
|
||||||
|
data = self.__f.read(data_len)
|
||||||
|
|
||||||
|
info = {"city_id": self.get_long(data, 0),
|
||||||
|
"region": data[4:].decode('utf-8')}
|
||||||
|
return info
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ip2long(ip):
|
||||||
|
_ip = socket.inet_aton(ip)
|
||||||
|
return struct.unpack("!L", _ip)[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_ip(ip):
|
||||||
|
p = ip.split(".")
|
||||||
|
if len(p) != 4:
|
||||||
|
return False
|
||||||
|
for pp in p:
|
||||||
|
if not pp.isdigit():
|
||||||
|
return False
|
||||||
|
if len(pp) > 3:
|
||||||
|
return False
|
||||||
|
if int(pp) > 255:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_long(b, offset):
|
||||||
|
if len(b[offset:offset + 4]) == 4:
|
||||||
|
return struct.unpack('I', b[offset:offset + 4])[0]
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.__f is not None:
|
||||||
|
self.__f.close()
|
||||||
|
self.__dbBinStr = None
|
||||||
|
self.__headerPtr = None
|
||||||
|
self.__headerSip = None
|
||||||
|
|
||||||
|
|
||||||
|
class IpRegData(IpRegInfo):
|
||||||
|
def __init__(self):
|
||||||
|
path = settings.data_storage_dir.joinpath('ip2region.db')
|
||||||
|
IpRegInfo.__init__(self, path)
|
||||||
|
|
||||||
|
def query(self, ip):
|
||||||
|
result = self.memory_search(ip)
|
||||||
|
addr_list = result.get('region').split('|')
|
||||||
|
addr = ''.join(filter(lambda x: x != '0', addr_list[:-1]))
|
||||||
|
isp = addr_list[-1]
|
||||||
|
if isp == '0':
|
||||||
|
isp = '未知'
|
||||||
|
info = {'addr': addr, 'isp': isp}
|
||||||
|
return info
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
from common.module import Module
|
||||||
|
from common import utils
|
||||||
|
from config.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
class Lookup(Module):
|
||||||
|
"""
|
||||||
|
DNS query base class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
Module.__init__(self)
|
||||||
|
self.qtype = ''
|
||||||
|
|
||||||
|
def query(self):
|
||||||
|
"""
|
||||||
|
Query the TXT record of domain
|
||||||
|
:return: query result
|
||||||
|
"""
|
||||||
|
answer = utils.dns_query(self.domain, self.qtype)
|
||||||
|
if answer is None:
|
||||||
|
return None
|
||||||
|
for item in answer:
|
||||||
|
record = item.to_text()
|
||||||
|
subdomains = self.match_subdomains(record)
|
||||||
|
self.subdomains.update(subdomains)
|
||||||
|
logger.log('DEBUG', record)
|
||||||
|
return self.subdomains
|
||||||
@@ -0,0 +1,369 @@
|
|||||||
|
"""
|
||||||
|
Module base class
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from config.log import logger
|
||||||
|
from config import settings
|
||||||
|
from common import utils
|
||||||
|
from common.database import Database
|
||||||
|
|
||||||
|
lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
|
class Module(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.module = 'Module'
|
||||||
|
self.source = 'BaseModule'
|
||||||
|
self.cookie = None
|
||||||
|
self.header = dict()
|
||||||
|
self.proxy = None
|
||||||
|
self.delay = 1 # 请求睡眠时延
|
||||||
|
self.timeout = settings.request_timeout_second # 请求超时时间
|
||||||
|
self.verify = settings.request_ssl_verify # 请求SSL验证
|
||||||
|
self.domain = str() # 当前进行子域名收集的主域
|
||||||
|
self.subdomains = set() # 存放发现的子域
|
||||||
|
self.infos = dict() # 存放子域有关信息
|
||||||
|
self.results = list() # 存放模块结果
|
||||||
|
self.start = time.time() # 模块开始执行时间
|
||||||
|
self.end = None # 模块结束执行时间
|
||||||
|
self.elapse = None # 模块执行耗时
|
||||||
|
|
||||||
|
def have_api(self, *apis):
|
||||||
|
"""
|
||||||
|
Simply check whether the api information configure or not
|
||||||
|
|
||||||
|
:param apis: apis set
|
||||||
|
:return bool: check result
|
||||||
|
"""
|
||||||
|
if not all(apis):
|
||||||
|
logger.log('DEBUG', f'{self.source} module is not configured')
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def begin(self):
|
||||||
|
"""
|
||||||
|
begin log
|
||||||
|
"""
|
||||||
|
logger.log('DEBUG', f'Start {self.source} module to '
|
||||||
|
f'collect subdomains of {self.domain}')
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
"""
|
||||||
|
finish log
|
||||||
|
"""
|
||||||
|
self.end = time.time()
|
||||||
|
self.elapse = round(self.end - self.start, 1)
|
||||||
|
logger.log('DEBUG', f'Finished {self.source} module to '
|
||||||
|
f'collect {self.domain}\'s subdomains')
|
||||||
|
logger.log('INFOR', f'{self.source} module took {self.elapse} seconds '
|
||||||
|
f'found {len(self.subdomains)} subdomains')
|
||||||
|
logger.log('DEBUG', f'{self.source} module found subdomains of {self.domain}\n'
|
||||||
|
f'{self.subdomains}')
|
||||||
|
|
||||||
|
def head(self, url, params=None, check=True, **kwargs):
|
||||||
|
"""
|
||||||
|
Custom head request
|
||||||
|
|
||||||
|
:param str url: request url
|
||||||
|
:param dict params: request parameters
|
||||||
|
:param bool check: check response
|
||||||
|
:param kwargs: other params
|
||||||
|
:return: response object
|
||||||
|
"""
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
try:
|
||||||
|
resp = session.head(url,
|
||||||
|
params=params,
|
||||||
|
cookies=self.cookie,
|
||||||
|
headers=self.header,
|
||||||
|
proxies=self.proxy,
|
||||||
|
timeout=self.timeout,
|
||||||
|
verify=self.verify,
|
||||||
|
**kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args[0])
|
||||||
|
return None
|
||||||
|
if not check:
|
||||||
|
return resp
|
||||||
|
if utils.check_response('HEAD', resp):
|
||||||
|
return resp
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get(self, url, params=None, check=True, ignore=False, raise_error=False, **kwargs):
|
||||||
|
"""
|
||||||
|
Custom get request
|
||||||
|
|
||||||
|
:param str url: request url
|
||||||
|
:param dict params: request parameters
|
||||||
|
:param bool check: check response
|
||||||
|
:param bool ignore: ignore error
|
||||||
|
:param bool raise_error: raise error or not
|
||||||
|
:param kwargs: other params
|
||||||
|
:return: response object
|
||||||
|
"""
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
level = 'ERROR'
|
||||||
|
if ignore:
|
||||||
|
level = 'DEBUG'
|
||||||
|
try:
|
||||||
|
resp = session.get(url,
|
||||||
|
params=params,
|
||||||
|
cookies=self.cookie,
|
||||||
|
headers=self.header,
|
||||||
|
proxies=self.proxy,
|
||||||
|
timeout=self.timeout,
|
||||||
|
verify=self.verify,
|
||||||
|
**kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
if raise_error:
|
||||||
|
if isinstance(e, requests.exceptions.ConnectTimeout):
|
||||||
|
logger.log(level, e.args[0])
|
||||||
|
raise e
|
||||||
|
logger.log(level, e.args[0])
|
||||||
|
return None
|
||||||
|
if not check:
|
||||||
|
return resp
|
||||||
|
if utils.check_response('GET', resp):
|
||||||
|
return resp
|
||||||
|
return None
|
||||||
|
|
||||||
|
def post(self, url, data=None, check=True, **kwargs):
|
||||||
|
"""
|
||||||
|
Custom post request
|
||||||
|
|
||||||
|
:param str url: request url
|
||||||
|
:param dict data: request data
|
||||||
|
:param bool check: check response
|
||||||
|
:param kwargs: other params
|
||||||
|
:return: response object
|
||||||
|
"""
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
try:
|
||||||
|
resp = session.post(url,
|
||||||
|
data=data,
|
||||||
|
cookies=self.cookie,
|
||||||
|
headers=self.header,
|
||||||
|
proxies=self.proxy,
|
||||||
|
timeout=self.timeout,
|
||||||
|
verify=self.verify,
|
||||||
|
**kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args[0])
|
||||||
|
return None
|
||||||
|
if not check:
|
||||||
|
return resp
|
||||||
|
if utils.check_response('POST', resp):
|
||||||
|
return resp
|
||||||
|
return None
|
||||||
|
|
||||||
|
def delete(self, url, check=True, **kwargs):
|
||||||
|
"""
|
||||||
|
Custom delete request
|
||||||
|
|
||||||
|
:param str url: request url
|
||||||
|
:param bool check: check response
|
||||||
|
:param kwargs: other params
|
||||||
|
:return: response object
|
||||||
|
"""
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
try:
|
||||||
|
resp = session.delete(url,
|
||||||
|
cookies=self.cookie,
|
||||||
|
headers=self.header,
|
||||||
|
proxies=self.proxy,
|
||||||
|
timeout=self.timeout,
|
||||||
|
verify=self.verify,
|
||||||
|
**kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args[0])
|
||||||
|
return None
|
||||||
|
if not check:
|
||||||
|
return resp
|
||||||
|
if utils.check_response('DELETE', resp):
|
||||||
|
return resp
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_header(self):
|
||||||
|
"""
|
||||||
|
Get request header
|
||||||
|
|
||||||
|
:return: header
|
||||||
|
"""
|
||||||
|
headers = utils.gen_fake_header()
|
||||||
|
if isinstance(headers, dict):
|
||||||
|
self.header = headers
|
||||||
|
return headers
|
||||||
|
return self.header
|
||||||
|
|
||||||
|
def get_proxy(self, module):
|
||||||
|
"""
|
||||||
|
Get proxy
|
||||||
|
|
||||||
|
:param str module: module name
|
||||||
|
:return: proxy
|
||||||
|
"""
|
||||||
|
if not settings.enable_request_proxy:
|
||||||
|
logger.log('TRACE', f'All modules do not use proxy')
|
||||||
|
return self.proxy
|
||||||
|
if settings.proxy_all_module:
|
||||||
|
logger.log('TRACE', f'{module} module uses proxy')
|
||||||
|
return utils.get_random_proxy()
|
||||||
|
if module in settings.proxy_partial_module:
|
||||||
|
logger.log('TRACE', f'{module} module uses proxy')
|
||||||
|
return utils.get_random_proxy()
|
||||||
|
else:
|
||||||
|
logger.log('TRACE', f'{module} module does not use proxy')
|
||||||
|
return self.proxy
|
||||||
|
|
||||||
|
def match_subdomains(self, resp, distinct=True, fuzzy=True):
|
||||||
|
if not resp:
|
||||||
|
return set()
|
||||||
|
elif isinstance(resp, str):
|
||||||
|
return utils.match_subdomains(self.domain, resp, distinct, fuzzy)
|
||||||
|
elif hasattr(resp, 'text'):
|
||||||
|
return utils.match_subdomains(self.domain, resp.text, distinct, fuzzy)
|
||||||
|
else:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
def collect_subdomains(self, resp):
|
||||||
|
subdomains = self.match_subdomains(resp)
|
||||||
|
self.subdomains.update(subdomains)
|
||||||
|
return self.subdomains
|
||||||
|
|
||||||
|
def save_json(self):
|
||||||
|
"""
|
||||||
|
Save the results of each module as a json file
|
||||||
|
|
||||||
|
:return bool: whether saved successfully
|
||||||
|
"""
|
||||||
|
if not settings.save_module_result:
|
||||||
|
return False
|
||||||
|
logger.log('TRACE', f'Save the subdomain results found by '
|
||||||
|
f'{self.source} module as a json file')
|
||||||
|
path = settings.result_save_dir.joinpath(self.domain, self.module)
|
||||||
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
|
name = self.source + '.json'
|
||||||
|
path = path.joinpath(name)
|
||||||
|
with open(path, mode='w', errors='ignore') as file:
|
||||||
|
result = {'domain': self.domain,
|
||||||
|
'name': self.module,
|
||||||
|
'source': self.source,
|
||||||
|
'elapse': self.elapse,
|
||||||
|
'find': len(self.subdomains),
|
||||||
|
'subdomains': list(self.subdomains),
|
||||||
|
'infos': self.infos}
|
||||||
|
json.dump(result, file, ensure_ascii=False, indent=4)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def gen_result(self):
|
||||||
|
"""
|
||||||
|
Generate results
|
||||||
|
"""
|
||||||
|
logger.log('DEBUG', f'Generating final results')
|
||||||
|
if not len(self.subdomains): # 该模块一个子域都没有发现的情况
|
||||||
|
logger.log('DEBUG', f'{self.source} module result is empty')
|
||||||
|
result = {'id': None,
|
||||||
|
'alive': None,
|
||||||
|
'request': None,
|
||||||
|
'resolve': None,
|
||||||
|
'url': None,
|
||||||
|
'subdomain': None,
|
||||||
|
'port': None,
|
||||||
|
'level': None,
|
||||||
|
'cname': None,
|
||||||
|
'ip': None,
|
||||||
|
'public': None,
|
||||||
|
'cdn': None,
|
||||||
|
'status': None,
|
||||||
|
'reason': None,
|
||||||
|
'title': None,
|
||||||
|
'banner': None,
|
||||||
|
'header': None,
|
||||||
|
'history': None,
|
||||||
|
'response': None,
|
||||||
|
'ip_times': None,
|
||||||
|
'cname_times': None,
|
||||||
|
'ttl': None,
|
||||||
|
'cidr': None,
|
||||||
|
'asn': None,
|
||||||
|
'org': None,
|
||||||
|
'addr': None,
|
||||||
|
'isp': None,
|
||||||
|
'resolver': None,
|
||||||
|
'module': self.module,
|
||||||
|
'source': self.source,
|
||||||
|
'elapse': self.elapse,
|
||||||
|
'find': None}
|
||||||
|
self.results.append(result)
|
||||||
|
else:
|
||||||
|
for subdomain in self.subdomains:
|
||||||
|
url = 'http://' + subdomain
|
||||||
|
level = subdomain.count('.') - self.domain.count('.')
|
||||||
|
info = self.infos.get(subdomain)
|
||||||
|
if info is None:
|
||||||
|
info = dict()
|
||||||
|
cname = info.get('cname')
|
||||||
|
ip = info.get('ip')
|
||||||
|
ip_times = info.get('ip_times')
|
||||||
|
cname_times = info.get('cname_times')
|
||||||
|
ttl = info.get('ttl')
|
||||||
|
if isinstance(cname, list):
|
||||||
|
cname = ','.join(cname)
|
||||||
|
ip = ','.join(ip)
|
||||||
|
ip_times = ','.join([str(num) for num in ip_times])
|
||||||
|
cname_times = ','.join([str(num) for num in cname_times])
|
||||||
|
ttl = ','.join([str(num) for num in ttl])
|
||||||
|
result = {'id': None,
|
||||||
|
'alive': info.get('alive'),
|
||||||
|
'request': info.get('request'),
|
||||||
|
'resolve': info.get('resolve'),
|
||||||
|
'url': url,
|
||||||
|
'subdomain': subdomain,
|
||||||
|
'port': 80,
|
||||||
|
'level': level,
|
||||||
|
'cname': cname,
|
||||||
|
'ip': ip,
|
||||||
|
'public': info.get('public'),
|
||||||
|
'cdn': info.get('cdn'),
|
||||||
|
'status': None,
|
||||||
|
'reason': info.get('reason'),
|
||||||
|
'title': None,
|
||||||
|
'banner': None,
|
||||||
|
'header': None,
|
||||||
|
'history': None,
|
||||||
|
'response': None,
|
||||||
|
'ip_times': ip_times,
|
||||||
|
'cname_times': cname_times,
|
||||||
|
'ttl': ttl,
|
||||||
|
'cidr': info.get('cidr'),
|
||||||
|
'asn': info.get('asn'),
|
||||||
|
'org': info.get('org'),
|
||||||
|
'addr': info.get('addr'),
|
||||||
|
'isp': info.get('isp'),
|
||||||
|
'resolver': info.get('resolver'),
|
||||||
|
'module': self.module,
|
||||||
|
'source': self.source,
|
||||||
|
'elapse': self.elapse,
|
||||||
|
'find': len(self.subdomains)}
|
||||||
|
self.results.append(result)
|
||||||
|
|
||||||
|
def save_db(self):
|
||||||
|
"""
|
||||||
|
Save module results into the database
|
||||||
|
"""
|
||||||
|
logger.log('DEBUG', f'Saving results to database')
|
||||||
|
lock.acquire()
|
||||||
|
db = Database()
|
||||||
|
db.create_table(self.domain)
|
||||||
|
db.save_db(self.domain, self.results, self.source)
|
||||||
|
db.close()
|
||||||
|
lock.release()
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
# coding=utf-8
|
from common.module import Module
|
||||||
from .module import Module
|
|
||||||
|
|
||||||
|
class Query(Module):
|
||||||
class Query(Module):
|
"""
|
||||||
"""
|
Query base class
|
||||||
查询基类
|
"""
|
||||||
"""
|
def __init__(self):
|
||||||
def __init__(self):
|
Module.__init__(self)
|
||||||
Module.__init__(self)
|
|
||||||
@@ -0,0 +1,363 @@
|
|||||||
|
import os
|
||||||
|
from collections import OrderedDict
|
||||||
|
from inspect import isclass
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine, exc, inspect, text
|
||||||
|
|
||||||
|
from .tablib import tablib
|
||||||
|
|
||||||
|
DATABASE_URL = os.environ.get('DATABASE_URL')
|
||||||
|
|
||||||
|
|
||||||
|
def is_exception(obj):
|
||||||
|
"""Given an object, return a boolean indicating whether it is an instance
|
||||||
|
or subclass of :py:class:`Exception`.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, Exception):
|
||||||
|
return True
|
||||||
|
if isclass(obj) and issubclass(obj, Exception):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Record(object):
|
||||||
|
"""A row, from a query, from a database."""
|
||||||
|
__slots__ = ('_keys', '_values')
|
||||||
|
|
||||||
|
def __init__(self, keys, values):
|
||||||
|
self._keys = keys
|
||||||
|
self._values = values
|
||||||
|
|
||||||
|
# Ensure that lengths match properly.
|
||||||
|
assert len(self._keys) == len(self._values)
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
"""Returns the list of column names from the query."""
|
||||||
|
return self._keys
|
||||||
|
|
||||||
|
def values(self):
|
||||||
|
"""Returns the list of values from the query."""
|
||||||
|
return self._values
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Record {}>'.format(self.export('json')[1:-1])
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
# Support for index-based lookup.
|
||||||
|
if isinstance(key, int):
|
||||||
|
return self.values()[key]
|
||||||
|
|
||||||
|
# Support for string-based lookup.
|
||||||
|
if key in self.keys():
|
||||||
|
i = self.keys().index(key)
|
||||||
|
if self.keys().count(key) > 1:
|
||||||
|
raise KeyError("Record contains multiple '{}' fields.".format(key))
|
||||||
|
return self.values()[i]
|
||||||
|
|
||||||
|
raise KeyError("Record contains no '{}' field.".format(key))
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
try:
|
||||||
|
return self[key]
|
||||||
|
except KeyError as e:
|
||||||
|
raise AttributeError(e)
|
||||||
|
|
||||||
|
def __dir__(self):
|
||||||
|
standard = dir(super(Record, self))
|
||||||
|
# Merge standard attrs with generated ones (from column names).
|
||||||
|
return sorted(standard + [str(k) for k in self.keys()])
|
||||||
|
|
||||||
|
def get(self, key, default=None):
|
||||||
|
"""Returns the value for a given key, or default."""
|
||||||
|
try:
|
||||||
|
return self[key]
|
||||||
|
except KeyError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def as_dict(self, ordered=False):
|
||||||
|
"""Returns the row as a dictionary, as ordered."""
|
||||||
|
items = zip(self.keys(), self.values())
|
||||||
|
|
||||||
|
return OrderedDict(items) if ordered else dict(items)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dataset(self):
|
||||||
|
"""A Tablib Dataset containing the row."""
|
||||||
|
data = tablib.Dataset()
|
||||||
|
data.headers = self.keys()
|
||||||
|
|
||||||
|
row = _reduce_datetimes(self.values())
|
||||||
|
data.append(row)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def export(self, format, **kwargs):
|
||||||
|
"""Exports the row to the given format."""
|
||||||
|
return self.dataset.export(format, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RecordCollection(object):
|
||||||
|
"""A set of excellent Records from a query."""
|
||||||
|
|
||||||
|
def __init__(self, rows):
|
||||||
|
self._rows = rows
|
||||||
|
self._all_rows = []
|
||||||
|
self.pending = True
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<RecordCollection size={} pending={}>'.format(len(self), self.pending)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Iterate over all rows, consuming the underlying generator
|
||||||
|
only when necessary."""
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
# Other code may have iterated between yields,
|
||||||
|
# so always check the cache.
|
||||||
|
if i < len(self):
|
||||||
|
yield self[i]
|
||||||
|
else:
|
||||||
|
# Throws StopIteration when done.
|
||||||
|
# Prevent StopIteration bubbling from generator,
|
||||||
|
# following https://www.python.org/dev/peps/pep-0479/
|
||||||
|
try:
|
||||||
|
yield next(self)
|
||||||
|
except StopIteration:
|
||||||
|
return
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
return self.__next__()
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
try:
|
||||||
|
nextrow = next(self._rows)
|
||||||
|
self._all_rows.append(nextrow)
|
||||||
|
return nextrow
|
||||||
|
except StopIteration:
|
||||||
|
self.pending = False
|
||||||
|
raise StopIteration('RecordCollection contains no more rows.')
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
is_int = isinstance(key, int)
|
||||||
|
|
||||||
|
# Convert RecordCollection[1] into slice.
|
||||||
|
if is_int:
|
||||||
|
key = slice(key, key + 1)
|
||||||
|
|
||||||
|
while len(self) < key.stop or key.stop is None:
|
||||||
|
try:
|
||||||
|
next(self)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
rows = self._all_rows[key]
|
||||||
|
if is_int:
|
||||||
|
return rows[0]
|
||||||
|
else:
|
||||||
|
return RecordCollection(iter(rows))
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._all_rows)
|
||||||
|
|
||||||
|
def export(self, format, **kwargs):
|
||||||
|
"""Export the RecordCollection to a given format (courtesy of Tablib)."""
|
||||||
|
return self.dataset.export(format, **kwargs)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dataset(self):
|
||||||
|
"""A Tablib Dataset representation of the RecordCollection."""
|
||||||
|
# Create a new Tablib Dataset.
|
||||||
|
data = tablib.Dataset()
|
||||||
|
|
||||||
|
# If the RecordCollection is empty, just return the empty set
|
||||||
|
# Check number of rows by typecasting to list
|
||||||
|
if len(list(self)) == 0:
|
||||||
|
return data
|
||||||
|
|
||||||
|
# Set the column names as headers on Tablib Dataset.
|
||||||
|
first = self[0]
|
||||||
|
|
||||||
|
data.headers = first.keys()
|
||||||
|
for row in self.all():
|
||||||
|
row = _reduce_datetimes(row.values())
|
||||||
|
data.append(row)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def all(self, as_dict=False, as_ordereddict=False):
|
||||||
|
"""Returns a list of all rows for the RecordCollection. If they haven't
|
||||||
|
been fetched yet, consume the iterator and cache the results."""
|
||||||
|
|
||||||
|
# By calling list it calls the __iter__ method
|
||||||
|
rows = list(self)
|
||||||
|
|
||||||
|
if as_dict:
|
||||||
|
return [r.as_dict() for r in rows]
|
||||||
|
elif as_ordereddict:
|
||||||
|
return [r.as_dict(ordered=True) for r in rows]
|
||||||
|
|
||||||
|
return rows
|
||||||
|
|
||||||
|
def as_dict(self, ordered=False):
|
||||||
|
return self.all(as_dict=not (ordered), as_ordereddict=ordered)
|
||||||
|
|
||||||
|
def first(self, default=None, as_dict=False, as_ordereddict=False):
|
||||||
|
"""Returns a single record for the RecordCollection, or `default`. If
|
||||||
|
`default` is an instance or subclass of Exception, then raise it
|
||||||
|
instead of returning it."""
|
||||||
|
|
||||||
|
# Try to get a record, or return/raise default.
|
||||||
|
try:
|
||||||
|
record = self[0]
|
||||||
|
except IndexError:
|
||||||
|
if is_exception(default):
|
||||||
|
raise default
|
||||||
|
return default
|
||||||
|
|
||||||
|
# Cast and return.
|
||||||
|
if as_dict:
|
||||||
|
return record.as_dict()
|
||||||
|
elif as_ordereddict:
|
||||||
|
return record.as_dict(ordered=True)
|
||||||
|
else:
|
||||||
|
return record
|
||||||
|
|
||||||
|
def one(self, default=None, as_dict=False, as_ordereddict=False):
|
||||||
|
"""Returns a single record for the RecordCollection, ensuring that it
|
||||||
|
is the only record, or returns `default`. If `default` is an instance
|
||||||
|
or subclass of Exception, then raise it instead of returning it."""
|
||||||
|
|
||||||
|
# Ensure that we don't have more than one row.
|
||||||
|
try:
|
||||||
|
return self[1]
|
||||||
|
except IndexError:
|
||||||
|
return self.first(default=default, as_dict=as_dict,
|
||||||
|
as_ordereddict=as_ordereddict)
|
||||||
|
else:
|
||||||
|
raise ValueError('RecordCollection contained more than one row. '
|
||||||
|
'Expects only one row when using '
|
||||||
|
'RecordCollection.one')
|
||||||
|
|
||||||
|
def scalar(self, default=None):
|
||||||
|
"""Returns the first column of the first row, or `default`."""
|
||||||
|
row = self.one()
|
||||||
|
return row[0] if row else default
|
||||||
|
|
||||||
|
|
||||||
|
class Database(object):
|
||||||
|
"""A Database. Encapsulates a url and an SQLAlchemy engine with a pool of
|
||||||
|
connections.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, db_url=None, **kwargs):
|
||||||
|
# If no db_url was provided, fallback to $DATABASE_URL.
|
||||||
|
self.db_url = db_url or DATABASE_URL
|
||||||
|
|
||||||
|
if not self.db_url:
|
||||||
|
raise ValueError('You must provide a db_url.')
|
||||||
|
|
||||||
|
# Create an engine.
|
||||||
|
self._engine = create_engine(self.db_url, **kwargs)
|
||||||
|
self.open = True
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Closes the Database."""
|
||||||
|
self._engine.dispose()
|
||||||
|
self.open = False
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc, val, traceback):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Database open={}>'.format(self.open)
|
||||||
|
|
||||||
|
def get_table_names(self):
|
||||||
|
"""Returns a list of table names for the connected database."""
|
||||||
|
|
||||||
|
# Setup SQLAlchemy for Database inspection.
|
||||||
|
return inspect(self._engine).get_table_names()
|
||||||
|
|
||||||
|
def get_connection(self):
|
||||||
|
"""Get a connection to this Database. Connections are retrieved from a
|
||||||
|
pool.
|
||||||
|
"""
|
||||||
|
if not self.open:
|
||||||
|
raise exc.ResourceClosedError('Database closed.')
|
||||||
|
|
||||||
|
return Connection(self._engine.connect())
|
||||||
|
|
||||||
|
def query(self, query, fetchall=False, **params):
|
||||||
|
"""Executes the given SQL query against the Database. Parameters can,
|
||||||
|
optionally, be provided. Returns a RecordCollection, which can be
|
||||||
|
iterated over to get result rows as dictionaries.
|
||||||
|
"""
|
||||||
|
with self.get_connection() as conn:
|
||||||
|
return conn.query(query, fetchall, **params)
|
||||||
|
|
||||||
|
def bulk_query(self, query, *multiparams):
|
||||||
|
"""Bulk insert or update."""
|
||||||
|
|
||||||
|
with self.get_connection() as conn:
|
||||||
|
conn.bulk_query(query, *multiparams)
|
||||||
|
|
||||||
|
|
||||||
|
class Connection(object):
|
||||||
|
"""A Database connection."""
|
||||||
|
|
||||||
|
def __init__(self, connection):
|
||||||
|
self._conn = connection
|
||||||
|
self.open = not connection.closed
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._conn.close()
|
||||||
|
self.open = False
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc, val, traceback):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Connection open={}>'.format(self.open)
|
||||||
|
|
||||||
|
def query(self, query, fetchall=False, **params):
|
||||||
|
"""Executes the given SQL query against the connected Database.
|
||||||
|
Parameters can, optionally, be provided. Returns a RecordCollection,
|
||||||
|
which can be iterated over to get result rows as dictionaries.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Execute the given query.
|
||||||
|
cursor = self._conn.execute(text(query), **params) # TODO: PARAMS GO HERE
|
||||||
|
|
||||||
|
# Row-by-row Record generator.
|
||||||
|
row_gen = (Record(cursor.keys(), row) for row in cursor)
|
||||||
|
|
||||||
|
# Convert psycopg2 results to RecordCollection.
|
||||||
|
results = RecordCollection(row_gen)
|
||||||
|
|
||||||
|
# Fetch all results if desired.
|
||||||
|
if fetchall:
|
||||||
|
results.all()
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def bulk_query(self, query, *multiparams):
|
||||||
|
"""Bulk insert or update."""
|
||||||
|
|
||||||
|
self._conn.execute(text(query), *multiparams)
|
||||||
|
|
||||||
|
|
||||||
|
def _reduce_datetimes(row):
|
||||||
|
"""Receives a row, converts datetimes to strings."""
|
||||||
|
|
||||||
|
row = list(row)
|
||||||
|
|
||||||
|
for i in range(len(row)):
|
||||||
|
if hasattr(row[i], 'isoformat'):
|
||||||
|
row[i] = row[i].isoformat()
|
||||||
|
return tuple(row)
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
import json
|
||||||
|
from threading import Thread
|
||||||
|
from queue import Queue
|
||||||
|
|
||||||
|
import tqdm
|
||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from common import utils
|
||||||
|
from config.log import logger
|
||||||
|
from common.database import Database
|
||||||
|
from config import settings
|
||||||
|
|
||||||
|
|
||||||
|
def req_thread_count():
|
||||||
|
count = settings.request_thread_count
|
||||||
|
if isinstance(count, int):
|
||||||
|
count = max(16, count)
|
||||||
|
else:
|
||||||
|
count = utils.get_request_count()
|
||||||
|
logger.log('DEBUG', f'Number of request threads {count}')
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def get_port_seq(port):
|
||||||
|
logger.log('DEBUG', 'Getting port range')
|
||||||
|
ports = set()
|
||||||
|
if isinstance(port, (set, list, tuple)):
|
||||||
|
ports = port
|
||||||
|
elif isinstance(port, int):
|
||||||
|
if 0 <= port <= 65535:
|
||||||
|
ports = {port}
|
||||||
|
elif port in {'small', 'medium', 'large'}:
|
||||||
|
logger.log('DEBUG', f'{port} port range')
|
||||||
|
ports = settings.ports.get(port)
|
||||||
|
if not ports: # 意外情况
|
||||||
|
logger.log('ERROR', 'The specified request port range is incorrect')
|
||||||
|
ports = {80}
|
||||||
|
logger.log('INFOR', f'Port range:{ports}')
|
||||||
|
return set(ports)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_req_url(domain, port):
|
||||||
|
if str(port).endswith('443'):
|
||||||
|
url = f'https://{domain}:{port}'
|
||||||
|
if port == 443:
|
||||||
|
url = f'https://{domain}'
|
||||||
|
return url
|
||||||
|
url = f'http://{domain}:{port}'
|
||||||
|
if port == 80:
|
||||||
|
url = f'http://{domain}'
|
||||||
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def gen_req_data(data, ports):
|
||||||
|
logger.log('INFOR', 'Generating request urls')
|
||||||
|
req_data = list()
|
||||||
|
req_urls = set()
|
||||||
|
for info in data:
|
||||||
|
resolve = info.get('resolve')
|
||||||
|
# 解析不成功的子域不进行http请求探测
|
||||||
|
if resolve != 1:
|
||||||
|
continue
|
||||||
|
subdomain = info.get('subdomain')
|
||||||
|
for port in ports:
|
||||||
|
tmp_info = info.copy()
|
||||||
|
tmp_info['port'] = port
|
||||||
|
url = gen_req_url(subdomain, port)
|
||||||
|
tmp_info['url'] = url
|
||||||
|
req_data.append(tmp_info)
|
||||||
|
req_urls.add(url)
|
||||||
|
return req_data, req_urls
|
||||||
|
|
||||||
|
|
||||||
|
def get_html_title(markup):
|
||||||
|
"""
|
||||||
|
获取标题
|
||||||
|
|
||||||
|
:param markup: html标签
|
||||||
|
:return: 标题
|
||||||
|
"""
|
||||||
|
soup = BeautifulSoup(markup, 'html.parser')
|
||||||
|
|
||||||
|
title = soup.title
|
||||||
|
if title:
|
||||||
|
return title.text
|
||||||
|
|
||||||
|
h1 = soup.h1
|
||||||
|
if h1:
|
||||||
|
return h1.text
|
||||||
|
|
||||||
|
h2 = soup.h2
|
||||||
|
if h2:
|
||||||
|
return h2.text
|
||||||
|
|
||||||
|
h3 = soup.h3
|
||||||
|
if h3:
|
||||||
|
return h3.text
|
||||||
|
|
||||||
|
desc = soup.find('meta', attrs={'name': 'description'})
|
||||||
|
if desc:
|
||||||
|
return desc['content']
|
||||||
|
|
||||||
|
word = soup.find('meta', attrs={'name': 'keywords'})
|
||||||
|
if word:
|
||||||
|
return word['content']
|
||||||
|
|
||||||
|
text = soup.text
|
||||||
|
if len(text) <= 200:
|
||||||
|
return repr(text)
|
||||||
|
|
||||||
|
return 'None'
|
||||||
|
|
||||||
|
|
||||||
|
def get_jump_urls(history):
|
||||||
|
urls = list()
|
||||||
|
for resp in history:
|
||||||
|
urls.append(str(resp.url))
|
||||||
|
return urls
|
||||||
|
|
||||||
|
|
||||||
|
def get_progress_bar(total):
|
||||||
|
bar = tqdm.tqdm()
|
||||||
|
bar.total = total
|
||||||
|
bar.desc = 'Request Progress'
|
||||||
|
bar.ncols = 80
|
||||||
|
return bar
|
||||||
|
|
||||||
|
|
||||||
|
def get_resp(url, session):
|
||||||
|
timeout = settings.request_timeout_second
|
||||||
|
redirect = settings.request_allow_redirect
|
||||||
|
proxy = utils.get_proxy()
|
||||||
|
try:
|
||||||
|
resp = session.get(url, timeout=timeout, allow_redirects=redirect, proxies=proxy)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('DEBUG', e.args)
|
||||||
|
resp = e
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
def request(urls_queue, resp_queue, session):
|
||||||
|
while not urls_queue.empty():
|
||||||
|
index, url = urls_queue.get()
|
||||||
|
resp = get_resp(url, session)
|
||||||
|
resp_queue.put((index, resp))
|
||||||
|
urls_queue.task_done()
|
||||||
|
|
||||||
|
|
||||||
|
def progress(bar, total, urls_queue):
|
||||||
|
while True:
|
||||||
|
remaining = urls_queue.qsize()
|
||||||
|
done = total - remaining
|
||||||
|
bar.n = done
|
||||||
|
bar.update()
|
||||||
|
if remaining == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def get_session():
|
||||||
|
header = utils.gen_fake_header()
|
||||||
|
verify = settings.request_ssl_verify
|
||||||
|
redirect_limit = settings.request_redirect_limit
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
session.headers = header
|
||||||
|
session.verify = verify
|
||||||
|
session.max_redirects = redirect_limit
|
||||||
|
return session
|
||||||
|
|
||||||
|
|
||||||
|
def gen_new_info(info, resp):
|
||||||
|
if isinstance(resp, Exception):
|
||||||
|
info['reason'] = str(resp.args)
|
||||||
|
info['request'] = 0
|
||||||
|
info['alive'] = 0
|
||||||
|
return info
|
||||||
|
info['reason'] = resp.reason
|
||||||
|
code = resp.status_code
|
||||||
|
info['status'] = code
|
||||||
|
info['request'] = 1
|
||||||
|
if code == 400 or code >= 500:
|
||||||
|
info['alive'] = 0
|
||||||
|
else:
|
||||||
|
info['alive'] = 1
|
||||||
|
headers = resp.headers
|
||||||
|
if settings.enable_banner_identify:
|
||||||
|
info['banner'] = utils.get_sample_banner(headers)
|
||||||
|
info['header'] = json.dumps(dict(headers))
|
||||||
|
history = resp.history
|
||||||
|
info['history'] = json.dumps(get_jump_urls(history))
|
||||||
|
text = utils.decode_resp_text(resp)
|
||||||
|
title = get_html_title(text).strip()
|
||||||
|
info['title'] = utils.remove_invalid_string(title)
|
||||||
|
info['response'] = utils.remove_invalid_string(text)
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
def save(name, total, req_data, resp_queue):
|
||||||
|
db = Database()
|
||||||
|
db.create_table(name)
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
if not resp_queue.empty():
|
||||||
|
i += 1
|
||||||
|
index, resp = resp_queue.get()
|
||||||
|
old_info = req_data[index]
|
||||||
|
new_info = gen_new_info(old_info, resp)
|
||||||
|
db.insert_table(name, new_info)
|
||||||
|
resp_queue.task_done()
|
||||||
|
if i >= total: # 得存入完所有请求结果才能结束
|
||||||
|
break
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def bulk_request(domain, req_data, ret=False):
|
||||||
|
logger.log('INFOR', 'Requesting urls in bulk')
|
||||||
|
resp_queue = Queue()
|
||||||
|
urls_queue = Queue()
|
||||||
|
task_count = len(req_data)
|
||||||
|
for index, info in enumerate(req_data):
|
||||||
|
url = info.get('url')
|
||||||
|
urls_queue.put((index, url))
|
||||||
|
session = get_session()
|
||||||
|
thread_count = req_thread_count()
|
||||||
|
if task_count <= thread_count:
|
||||||
|
# 如果请求任务数很小不用创建很多线程了
|
||||||
|
thread_count = task_count
|
||||||
|
bar = get_progress_bar(task_count)
|
||||||
|
|
||||||
|
progress_thread = Thread(target=progress, name='ProgressThread',
|
||||||
|
args=(bar, task_count, urls_queue), daemon=True)
|
||||||
|
progress_thread.start()
|
||||||
|
|
||||||
|
for i in range(thread_count):
|
||||||
|
request_thread = Thread(target=request, name=f'RequestThread-{i}',
|
||||||
|
args=(urls_queue, resp_queue, session), daemon=True)
|
||||||
|
request_thread.start()
|
||||||
|
if ret:
|
||||||
|
urls_queue.join()
|
||||||
|
return resp_queue
|
||||||
|
save_thread = Thread(target=save, name=f'SaveThread',
|
||||||
|
args=(domain, task_count, req_data, resp_queue), daemon=True)
|
||||||
|
save_thread.start()
|
||||||
|
urls_queue.join()
|
||||||
|
save_thread.join()
|
||||||
|
|
||||||
|
|
||||||
|
def run_request(domain, data, port):
|
||||||
|
"""
|
||||||
|
HTTP request entrance
|
||||||
|
|
||||||
|
:param str domain: domain to be requested
|
||||||
|
:param list data: subdomains data to be requested
|
||||||
|
:param any port: range of ports to be requested
|
||||||
|
:return list: result
|
||||||
|
"""
|
||||||
|
logger.log('INFOR', f'Start requesting subdomains of {domain}')
|
||||||
|
data = utils.set_id_none(data)
|
||||||
|
ports = get_port_seq(port)
|
||||||
|
req_data, req_urls = gen_req_data(data, ports)
|
||||||
|
bulk_request(domain, req_data)
|
||||||
|
count = utils.count_alive(domain)
|
||||||
|
logger.log('INFOR', f'Found that {domain} has {count} alive subdomains')
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
import gc
|
||||||
|
import json
|
||||||
|
|
||||||
|
from config.log import logger
|
||||||
|
from config import settings
|
||||||
|
from common import utils
|
||||||
|
|
||||||
|
|
||||||
|
def filter_subdomain(data):
|
||||||
|
"""
|
||||||
|
过滤出无解析内容的子域到新的子域列表
|
||||||
|
|
||||||
|
:param list data: 待过滤的数据列表
|
||||||
|
:return: 符合条件的子域列表
|
||||||
|
"""
|
||||||
|
logger.log('DEBUG', f'Filtering subdomains to be resolved')
|
||||||
|
subdomains = []
|
||||||
|
for infos in data:
|
||||||
|
if not infos.get('ip'):
|
||||||
|
subdomain = infos.get('subdomain')
|
||||||
|
if subdomain:
|
||||||
|
subdomains.append(subdomain)
|
||||||
|
return subdomains
|
||||||
|
|
||||||
|
|
||||||
|
def update_data(data, infos):
|
||||||
|
"""
|
||||||
|
更新解析结果
|
||||||
|
|
||||||
|
:param list data: 待更新的数据列表
|
||||||
|
:param dict infos: 子域有关结果信息
|
||||||
|
:return: 更新后的数据列表
|
||||||
|
"""
|
||||||
|
logger.log('DEBUG', f'Updating resolved results')
|
||||||
|
if not infos:
|
||||||
|
logger.log('ALERT', f'No valid resolved result')
|
||||||
|
return data
|
||||||
|
new_data = list()
|
||||||
|
for index, items in enumerate(data):
|
||||||
|
if items.get('ip'):
|
||||||
|
new_data.append(items)
|
||||||
|
continue
|
||||||
|
subdomain = items.get('subdomain')
|
||||||
|
record = infos.get(subdomain)
|
||||||
|
if record:
|
||||||
|
items.update(record)
|
||||||
|
new_data.append(items)
|
||||||
|
else:
|
||||||
|
subdomain = items.get('subdomain')
|
||||||
|
logger.log('DEBUG', f'{subdomain} resolution has no result')
|
||||||
|
return new_data
|
||||||
|
|
||||||
|
|
||||||
|
def save_db(name, data):
|
||||||
|
"""
|
||||||
|
Save resolved results to database
|
||||||
|
|
||||||
|
:param str name: table name
|
||||||
|
:param list data: data to be saved
|
||||||
|
"""
|
||||||
|
logger.log('INFOR', f'Saving resolved results')
|
||||||
|
utils.save_to_db(name, data, 'resolve')
|
||||||
|
|
||||||
|
|
||||||
|
def save_subdomains(save_path, subdomain_list):
|
||||||
|
logger.log('DEBUG', f'Saving resolved subdomain')
|
||||||
|
subdomain_data = '\n'.join(subdomain_list)
|
||||||
|
if not utils.save_to_file(save_path, subdomain_data):
|
||||||
|
logger.log('FATAL', 'Save resolved subdomain error')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_infos(data, qname, info, infos):
|
||||||
|
flag = False
|
||||||
|
cnames = list()
|
||||||
|
ips = list()
|
||||||
|
ttl = list()
|
||||||
|
answers = data.get('answers')
|
||||||
|
for answer in answers:
|
||||||
|
if answer.get('type') == 'A':
|
||||||
|
flag = True
|
||||||
|
name = answer.get('name')
|
||||||
|
cname = name[:-1].lower() # 去除最右边的`.`点号
|
||||||
|
cnames.append(cname)
|
||||||
|
ip = answer.get('data')
|
||||||
|
ips.append(ip)
|
||||||
|
ttl.append(str(answer.get('ttl')))
|
||||||
|
info['resolve'] = 1
|
||||||
|
info['reason'] = 'OK'
|
||||||
|
info['cname'] = ','.join(cnames)
|
||||||
|
info['ip'] = ','.join(ips)
|
||||||
|
info['ttl'] = ','.join(ttl)
|
||||||
|
infos[qname] = info
|
||||||
|
if not flag:
|
||||||
|
logger.log('DEBUG', f'Resolving {qname} have not a record')
|
||||||
|
info['alive'] = 0
|
||||||
|
info['resolve'] = 0
|
||||||
|
info['reason'] = 'NoARecord'
|
||||||
|
infos[qname] = info
|
||||||
|
return infos
|
||||||
|
|
||||||
|
|
||||||
|
def deal_output(output_path):
|
||||||
|
logger.log('INFOR', f'Processing resolved results')
|
||||||
|
infos = dict() # 用来记录所有域名有关信息
|
||||||
|
with open(output_path) as fd:
|
||||||
|
for line in fd:
|
||||||
|
line = line.strip()
|
||||||
|
try:
|
||||||
|
items = json.loads(line)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
logger.log('ERROR', f'Error resolve line {line}, skip this line')
|
||||||
|
continue
|
||||||
|
info = dict()
|
||||||
|
info['resolver'] = items.get('resolver')
|
||||||
|
qname = items.get('name')[:-1] # 去除最右边的`.`点号
|
||||||
|
status = items.get('status')
|
||||||
|
if status != 'NOERROR':
|
||||||
|
logger.log('DEBUG', f'Resolving {qname}: {status}')
|
||||||
|
continue
|
||||||
|
data = items.get('data')
|
||||||
|
if 'answers' not in data:
|
||||||
|
logger.log('DEBUG', f'Resolving {qname} have not any answers')
|
||||||
|
info['alive'] = 0
|
||||||
|
info['resolve'] = 0
|
||||||
|
info['reason'] = 'NoAnswer'
|
||||||
|
infos[qname] = info
|
||||||
|
continue
|
||||||
|
infos = gen_infos(data, qname, info, infos)
|
||||||
|
return infos
|
||||||
|
|
||||||
|
|
||||||
|
def run_resolve(domain, data):
|
||||||
|
"""
|
||||||
|
调用子域解析入口函数
|
||||||
|
|
||||||
|
:param str domain: 待解析的主域
|
||||||
|
:param list data: 待解析的子域数据列表
|
||||||
|
:return: 解析得到的结果列表
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
logger.log('INFOR', f'Start resolving subdomains of {domain}')
|
||||||
|
subdomains = filter_subdomain(data)
|
||||||
|
if not subdomains:
|
||||||
|
return data
|
||||||
|
|
||||||
|
massdns_dir = settings.third_party_dir.joinpath('massdns')
|
||||||
|
result_dir = settings.result_save_dir
|
||||||
|
temp_dir = result_dir.joinpath('temp')
|
||||||
|
utils.check_dir(temp_dir)
|
||||||
|
massdns_path = utils.get_massdns_path(massdns_dir)
|
||||||
|
timestring = utils.get_timestring()
|
||||||
|
|
||||||
|
save_name = f'collected_subdomains_{domain}_{timestring}.txt'
|
||||||
|
save_path = temp_dir.joinpath(save_name)
|
||||||
|
save_subdomains(save_path, subdomains)
|
||||||
|
del subdomains
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
output_name = f'resolved_result_{domain}_{timestring}.json'
|
||||||
|
output_path = temp_dir.joinpath(output_name)
|
||||||
|
log_path = result_dir.joinpath('massdns.log')
|
||||||
|
ns_path = utils.get_ns_path()
|
||||||
|
|
||||||
|
logger.log('INFOR', f'Running massdns to resolve subdomains')
|
||||||
|
utils.call_massdns(massdns_path, save_path, ns_path,
|
||||||
|
output_path, log_path, quiet_mode=True)
|
||||||
|
|
||||||
|
infos = deal_output(output_path)
|
||||||
|
data = update_data(data, infos)
|
||||||
|
logger.log('INFOR', f'Finished resolve subdomains of {domain}')
|
||||||
|
return data
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
from config import settings
|
||||||
|
from common.module import Module
|
||||||
|
|
||||||
|
|
||||||
|
class Search(Module):
|
||||||
|
"""
|
||||||
|
Search base class
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
Module.__init__(self)
|
||||||
|
self.page_num = 0 # 要显示搜索起始条数
|
||||||
|
self.per_page_num = 50 # 每页显示搜索条数
|
||||||
|
self.recursive_search = settings.enable_recursive_search
|
||||||
|
self.recursive_times = settings.search_recursive_times
|
||||||
|
self.full_search = settings.enable_full_search
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def filter(domain, subdomain):
|
||||||
|
"""
|
||||||
|
生成搜索过滤语句
|
||||||
|
使用搜索引擎支持的-site:语法过滤掉搜索页面较多的子域以发现新域
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
:param set subdomain: 子域名集合
|
||||||
|
:return: 过滤语句
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
statements_list = []
|
||||||
|
subdomains_temp = set(map(lambda x: x + '.' + domain, settings.common_subnames))
|
||||||
|
subdomains_temp = list(subdomain.intersection(subdomains_temp))
|
||||||
|
for i in range(0, len(subdomains_temp), 2): # 同时排除2个子域
|
||||||
|
statements_list.append(''.join(set(map(lambda s: ' -site:' + s,
|
||||||
|
subdomains_temp[i:i + 2]))))
|
||||||
|
return statements_list
|
||||||
|
|
||||||
|
def match_location(self, url):
|
||||||
|
"""
|
||||||
|
匹配跳转之后的url
|
||||||
|
针对部分搜索引擎(如百度搜索)搜索展示url时有显示不全的情况
|
||||||
|
此函数会向每条结果的链接发送head请求获取响应头的location值并做子域匹配
|
||||||
|
|
||||||
|
:param str url: 展示结果的url链接
|
||||||
|
:return: 匹配的子域
|
||||||
|
:rtype set
|
||||||
|
"""
|
||||||
|
resp = self.head(url, check=False, allow_redirects=False)
|
||||||
|
if not resp:
|
||||||
|
return set()
|
||||||
|
location = resp.headers.get('location')
|
||||||
|
if not location:
|
||||||
|
return set()
|
||||||
|
return set(self.match_subdomains(location))
|
||||||
|
|
||||||
|
def check_subdomains(self, subdomains):
|
||||||
|
"""
|
||||||
|
检查搜索出的子域结果是否满足条件
|
||||||
|
|
||||||
|
:param subdomains: 子域结果
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not subdomains:
|
||||||
|
# 搜索没有发现子域名则停止搜索
|
||||||
|
return False
|
||||||
|
if not self.full_search and subdomains.issubset(self.subdomains):
|
||||||
|
# 在全搜索过程中发现搜索出的结果有完全重复的结果就停止搜索
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def recursive_subdomain(self):
|
||||||
|
# 递归搜索下一层的子域
|
||||||
|
# 从1开始是之前已经做过1层子域搜索了,当前实际递归层数是layer+1
|
||||||
|
subdomains = self.subdomains.copy()
|
||||||
|
for layer_num in range(1, self.recursive_times):
|
||||||
|
for subdomain in subdomains:
|
||||||
|
# 进行下一层子域搜索的限制条件
|
||||||
|
count = subdomain.count('.') - self.domain.count('.')
|
||||||
|
if count == layer_num:
|
||||||
|
yield subdomain
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
"""
|
||||||
|
根据网页结构判断页面相似性(Determine page similarity based on HTML page structure)
|
||||||
|
判断方法:根据网页的DOM树确定网页的模板特征向量,对模板特征向量计算网页结构相似性。
|
||||||
|
来源地址:https://github.com/SPuerBRead/HTMLSimilarity
|
||||||
|
计算参考: https://patents.google.com/patent/CN101694668B/zh
|
||||||
|
"""
|
||||||
|
from treelib import Tree
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
import bs4
|
||||||
|
|
||||||
|
|
||||||
|
class DOMTree(object):
|
||||||
|
def __init__(self, label, attrs):
|
||||||
|
self.label = label
|
||||||
|
self.attrs = attrs
|
||||||
|
|
||||||
|
|
||||||
|
class HTMLParser(object):
|
||||||
|
def __init__(self, html):
|
||||||
|
self.dom_id = 1
|
||||||
|
self.dom_tree = Tree()
|
||||||
|
self.bs_html = BeautifulSoup(html, 'html.parser')
|
||||||
|
|
||||||
|
def get_dom_structure_tree(self):
|
||||||
|
for content in self.bs_html.contents:
|
||||||
|
if isinstance(content, bs4.element.Tag):
|
||||||
|
self.bs_html = content
|
||||||
|
self.recursive_descendants(self.bs_html, 1)
|
||||||
|
return self.dom_tree
|
||||||
|
|
||||||
|
def recursive_descendants(self, descendants, parent_id):
|
||||||
|
if self.dom_id == 1:
|
||||||
|
self.dom_tree.create_node(descendants.name, self.dom_id,
|
||||||
|
data=DOMTree(descendants.name, descendants.attrs))
|
||||||
|
self.dom_id = self.dom_id + 1
|
||||||
|
for child in descendants.contents:
|
||||||
|
if isinstance(child, bs4.element.Tag):
|
||||||
|
self.dom_tree.create_node(child.name, self.dom_id, parent_id,
|
||||||
|
data=DOMTree(child.name, child.attrs))
|
||||||
|
self.dom_id = self.dom_id + 1
|
||||||
|
self.recursive_descendants(child, self.dom_id - 1)
|
||||||
|
|
||||||
|
|
||||||
|
class Converter(object):
|
||||||
|
def __init__(self, dom_tree, dimension):
|
||||||
|
self.dom_tree = dom_tree
|
||||||
|
self.node_info_list = []
|
||||||
|
self.dimension = dimension
|
||||||
|
self.initial_weight = 1
|
||||||
|
self.attenuation_ratio = 0.6
|
||||||
|
self.dom_eigenvector = {}.fromkeys(range(0, dimension), 0)
|
||||||
|
|
||||||
|
def get_eigenvector(self):
|
||||||
|
for node_id in range(1, self.dom_tree.size() + 1):
|
||||||
|
node = self.dom_tree.get_node(node_id)
|
||||||
|
node_feature = self.create_feature(node)
|
||||||
|
feature_hash = self.feature_hash(node_feature)
|
||||||
|
node_weight = self.calculate_weight(node, node_id, feature_hash)
|
||||||
|
self.construct_eigenvector(feature_hash, node_weight)
|
||||||
|
return self.dom_eigenvector
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_feature(node):
|
||||||
|
node_attr_list = []
|
||||||
|
node_feature = node.data.label + '|'
|
||||||
|
for attr in node.data.attrs.keys():
|
||||||
|
node_attr_list.append(attr + ':' + str(node.data.attrs[attr]))
|
||||||
|
node_feature += '|'.join(node_attr_list)
|
||||||
|
return node_feature
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def feature_hash(node_feature):
|
||||||
|
return abs(hash(node_feature)) % (10 ** 8)
|
||||||
|
|
||||||
|
def calculate_weight(self, node, node_id, feature_hash):
|
||||||
|
brother_node_count = 0
|
||||||
|
depth = self.dom_tree.depth(node)
|
||||||
|
for brother_node in self.dom_tree.siblings(node_id):
|
||||||
|
brother_node_feature = self.create_feature(brother_node)
|
||||||
|
brother_node_feature_hash = self.feature_hash(brother_node_feature)
|
||||||
|
if brother_node_feature_hash == feature_hash:
|
||||||
|
brother_node_count = brother_node_count + 1
|
||||||
|
if brother_node_count:
|
||||||
|
node_weight = self.initial_weight * self.attenuation_ratio ** depth \
|
||||||
|
* self.attenuation_ratio ** brother_node_count
|
||||||
|
else:
|
||||||
|
node_weight = self.initial_weight * self.attenuation_ratio ** depth
|
||||||
|
return node_weight
|
||||||
|
|
||||||
|
def construct_eigenvector(self, feature_hash, node_weight):
|
||||||
|
feature_hash = feature_hash % self.dimension
|
||||||
|
self.dom_eigenvector[feature_hash] += node_weight
|
||||||
|
|
||||||
|
|
||||||
|
def calc_pseudodistance(dom1_eigenvector, dom2_eigenvector, dimension):
|
||||||
|
a, b = 0, 0
|
||||||
|
for i in range(dimension):
|
||||||
|
a += dom1_eigenvector[i]-dom2_eigenvector[i]
|
||||||
|
if dom1_eigenvector[i] and dom2_eigenvector[i]:
|
||||||
|
b += dom1_eigenvector[i] + dom2_eigenvector[i]
|
||||||
|
pseudodistance = abs(a)/b
|
||||||
|
return pseudodistance
|
||||||
|
|
||||||
|
|
||||||
|
def get_pseudodistance(html_doc1, html_doc2, dimension=5000):
|
||||||
|
"""
|
||||||
|
获取html文档结构相似度
|
||||||
|
|
||||||
|
:param str html_doc1: html文档
|
||||||
|
:param str html_doc2: html文档
|
||||||
|
:param int dimension: 降维后的维数
|
||||||
|
:return 伪距离
|
||||||
|
"""
|
||||||
|
hp1 = HTMLParser(html_doc1)
|
||||||
|
html_doc1_dom_tree = hp1.get_dom_structure_tree()
|
||||||
|
hp2 = HTMLParser(html_doc2)
|
||||||
|
html_doc2_dom_tree = hp2.get_dom_structure_tree()
|
||||||
|
converter = Converter(html_doc1_dom_tree, dimension)
|
||||||
|
dom1_eigenvector = converter.get_eigenvector()
|
||||||
|
converter = Converter(html_doc2_dom_tree, dimension)
|
||||||
|
dom2_eigenvector = converter.get_eigenvector()
|
||||||
|
return calc_pseudodistance(dom1_eigenvector, dom2_eigenvector, dimension)
|
||||||
|
|
||||||
|
|
||||||
|
def is_similar(html_doc1, html_doc2, dimension=5000):
|
||||||
|
"""
|
||||||
|
根据计算出的伪距离来判断是否html页面结构相似
|
||||||
|
|
||||||
|
:param str html_doc1: html文档
|
||||||
|
:param str html_doc2: html文档
|
||||||
|
:param int dimension: 降维后的维数
|
||||||
|
:return 是否相似(伪距离value<0.2时相似,value>0.2时不相似)
|
||||||
|
"""
|
||||||
|
value = get_pseudodistance(html_doc1, html_doc2, dimension)
|
||||||
|
if value > 0.2:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import decimal
|
||||||
|
import json
|
||||||
|
import csv
|
||||||
|
from io import StringIO
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
""" Tablib - formats
|
||||||
|
"""
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
|
class Registry:
|
||||||
|
_formats = OrderedDict()
|
||||||
|
|
||||||
|
def register(self, key, format_or_path):
|
||||||
|
# Create Databook.<format> read or read/write properties
|
||||||
|
|
||||||
|
# Create Dataset.<format> read or read/write properties,
|
||||||
|
# and Dataset.get_<format>/set_<format> methods.
|
||||||
|
self._formats[key] = format_or_path
|
||||||
|
|
||||||
|
def register_builtins(self):
|
||||||
|
# Registration ordering matters for autodetection.
|
||||||
|
self.register('csv', CSVFormat())
|
||||||
|
self.register('json', JSONFormat())
|
||||||
|
|
||||||
|
def get_format(self, key):
|
||||||
|
if key not in self._formats:
|
||||||
|
raise Exception("OneForAll has no format '%s'." % key)
|
||||||
|
return self._formats[key]
|
||||||
|
|
||||||
|
|
||||||
|
registry = Registry()
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_objects_handler(obj):
|
||||||
|
if isinstance(obj, (decimal.Decimal, UUID)):
|
||||||
|
return str(obj)
|
||||||
|
elif hasattr(obj, 'isoformat'):
|
||||||
|
return obj.isoformat()
|
||||||
|
else:
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Tablib - JSON Support
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class JSONFormat:
|
||||||
|
title = 'json'
|
||||||
|
extensions = ('json',)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_set(cls, dataset):
|
||||||
|
"""Returns JSON representation of Dataset."""
|
||||||
|
return json.dumps(dataset.dict, default=serialize_objects_handler)
|
||||||
|
|
||||||
|
|
||||||
|
""" Tablib - CSV Support.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class CSVFormat:
|
||||||
|
title = 'csv'
|
||||||
|
extensions = ('csv',)
|
||||||
|
|
||||||
|
DEFAULT_DELIMITER = ','
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_stream_set(cls, dataset, **kwargs):
|
||||||
|
"""Returns CSV representation of Dataset as file-like."""
|
||||||
|
stream = StringIO()
|
||||||
|
|
||||||
|
kwargs.setdefault('delimiter', cls.DEFAULT_DELIMITER)
|
||||||
|
|
||||||
|
_csv = csv.writer(stream, **kwargs)
|
||||||
|
|
||||||
|
for row in dataset._package(dicts=False):
|
||||||
|
_csv.writerow(row)
|
||||||
|
|
||||||
|
stream.seek(0)
|
||||||
|
return stream
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_set(cls, dataset, **kwargs):
|
||||||
|
"""Returns CSV representation of Dataset."""
|
||||||
|
stream = cls.export_stream_set(dataset, **kwargs)
|
||||||
|
return stream.getvalue()
|
||||||
@@ -0,0 +1,360 @@
|
|||||||
|
from collections import OrderedDict
|
||||||
|
from .format import registry
|
||||||
|
|
||||||
|
|
||||||
|
class Row:
|
||||||
|
"""Internal Row object. Mainly used for filtering."""
|
||||||
|
|
||||||
|
__slots__ = ['_row', 'tags']
|
||||||
|
|
||||||
|
def __init__(self, row=None, tags=None):
|
||||||
|
if tags is None:
|
||||||
|
tags = list()
|
||||||
|
if row is None:
|
||||||
|
row = list()
|
||||||
|
self._row = list(row)
|
||||||
|
self.tags = list(tags)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return (col for col in self._row)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._row)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return repr(self._row)
|
||||||
|
|
||||||
|
def __getitem__(self, i):
|
||||||
|
return self._row[i]
|
||||||
|
|
||||||
|
def __setitem__(self, i, value):
|
||||||
|
self._row[i] = value
|
||||||
|
|
||||||
|
def __delitem__(self, i):
|
||||||
|
del self._row[i]
|
||||||
|
|
||||||
|
def __getstate__(self):
|
||||||
|
|
||||||
|
slots = dict()
|
||||||
|
|
||||||
|
for slot in self.__slots__:
|
||||||
|
attribute = getattr(self, slot)
|
||||||
|
slots[slot] = attribute
|
||||||
|
|
||||||
|
return slots
|
||||||
|
|
||||||
|
def __setstate__(self, state):
|
||||||
|
for (k, v) in list(state.items()):
|
||||||
|
setattr(self, k, v)
|
||||||
|
|
||||||
|
def rpush(self, value):
|
||||||
|
self.insert(len(self._row), value)
|
||||||
|
|
||||||
|
def append(self, value):
|
||||||
|
self.rpush(value)
|
||||||
|
|
||||||
|
def insert(self, index, value):
|
||||||
|
self._row.insert(index, value)
|
||||||
|
|
||||||
|
def __contains__(self, item):
|
||||||
|
return (item in self._row)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tuple(self):
|
||||||
|
"""Tuple representation of :class:`Row`."""
|
||||||
|
return tuple(self._row)
|
||||||
|
|
||||||
|
|
||||||
|
class Dataset:
|
||||||
|
"""The :class:`Dataset` object is the heart of Tablib. It provides all core
|
||||||
|
functionality.
|
||||||
|
|
||||||
|
Usually you create a :class:`Dataset` instance in your main module, and append
|
||||||
|
rows as you collect data. ::
|
||||||
|
|
||||||
|
data = tablib.Dataset()
|
||||||
|
data.headers = ('name', 'age')
|
||||||
|
|
||||||
|
for (name, age) in some_collector():
|
||||||
|
data.append((name, age))
|
||||||
|
|
||||||
|
|
||||||
|
Setting columns is similar. The column data length must equal the
|
||||||
|
current height of the data and headers must be set. ::
|
||||||
|
|
||||||
|
data = tablib.Dataset()
|
||||||
|
data.headers = ('first_name', 'last_name')
|
||||||
|
|
||||||
|
data.append(('John', 'Adams'))
|
||||||
|
data.append(('George', 'Washington'))
|
||||||
|
|
||||||
|
data.append_col((90, 67), header='age')
|
||||||
|
|
||||||
|
|
||||||
|
You can also set rows and headers upon instantiation. This is useful if
|
||||||
|
dealing with dozens or hundreds of :class:`Dataset` objects. ::
|
||||||
|
|
||||||
|
headers = ('first_name', 'last_name')
|
||||||
|
data = [('John', 'Adams'), ('George', 'Washington')]
|
||||||
|
|
||||||
|
data = tablib.Dataset(*data, headers=headers)
|
||||||
|
|
||||||
|
:param \\*args: (optional) list of rows to populate Dataset
|
||||||
|
:param headers: (optional) list strings for Dataset header row
|
||||||
|
:param title: (optional) string to use as title of the Dataset
|
||||||
|
|
||||||
|
|
||||||
|
.. admonition:: Format Attributes Definition
|
||||||
|
|
||||||
|
If you look at the code, the various output/import formats are not
|
||||||
|
defined within the :class:`Dataset` object. To add support for a new format, see
|
||||||
|
:ref:`Adding New Formats <newformats>`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._data = list(Row(arg) for arg in args)
|
||||||
|
self.__headers = None
|
||||||
|
|
||||||
|
# ('title', index) tuples
|
||||||
|
self._separators = []
|
||||||
|
|
||||||
|
# (column, callback) tuples
|
||||||
|
self._formatters = []
|
||||||
|
|
||||||
|
self.headers = kwargs.get('headers')
|
||||||
|
|
||||||
|
self.title = kwargs.get('title')
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.height
|
||||||
|
|
||||||
|
def _validate(self, row=None, col=None, safety=False):
|
||||||
|
"""Assures size of every row in dataset is of proper proportions."""
|
||||||
|
if row:
|
||||||
|
is_valid = (len(row) == self.width) if self.width else True
|
||||||
|
elif col:
|
||||||
|
if len(col) < 1:
|
||||||
|
is_valid = True
|
||||||
|
else:
|
||||||
|
is_valid = (len(col) == self.height) if self.height else True
|
||||||
|
else:
|
||||||
|
is_valid = all(len(x) == self.width for x in self._data)
|
||||||
|
|
||||||
|
if is_valid:
|
||||||
|
return True
|
||||||
|
if not safety:
|
||||||
|
raise InvalidDimensions
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _package(self, dicts=True, ordered=True):
|
||||||
|
"""Packages Dataset into lists of dictionaries for transmission."""
|
||||||
|
# TODO: Dicts default to false?
|
||||||
|
|
||||||
|
_data = list(self._data)
|
||||||
|
|
||||||
|
if ordered:
|
||||||
|
dict_pack = OrderedDict
|
||||||
|
else:
|
||||||
|
dict_pack = dict
|
||||||
|
|
||||||
|
# Execute formatters
|
||||||
|
if self._formatters:
|
||||||
|
for row_i, row in enumerate(_data):
|
||||||
|
for col, callback in self._formatters:
|
||||||
|
try:
|
||||||
|
if col is None:
|
||||||
|
for j, c in enumerate(row):
|
||||||
|
_data[row_i][j] = callback(c)
|
||||||
|
else:
|
||||||
|
_data[row_i][col] = callback(row[col])
|
||||||
|
except IndexError:
|
||||||
|
raise InvalidDatasetIndex
|
||||||
|
|
||||||
|
if self.headers:
|
||||||
|
if dicts:
|
||||||
|
data = [dict_pack(list(zip(self.headers, data_row)))
|
||||||
|
for data_row in _data]
|
||||||
|
else:
|
||||||
|
data = [list(self.headers)] + list(_data)
|
||||||
|
else:
|
||||||
|
data = [list(row) for row in _data]
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def _get_headers(self):
|
||||||
|
"""An *optional* list of strings to be used for header rows and attribute names.
|
||||||
|
|
||||||
|
This must be set manually. The given list length must equal :class:`Dataset.width`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self.__headers
|
||||||
|
|
||||||
|
def _set_headers(self, collection):
|
||||||
|
"""Validating headers setter."""
|
||||||
|
self._validate(collection)
|
||||||
|
if collection:
|
||||||
|
try:
|
||||||
|
self.__headers = list(collection)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError
|
||||||
|
else:
|
||||||
|
self.__headers = None
|
||||||
|
|
||||||
|
headers = property(_get_headers, _set_headers)
|
||||||
|
|
||||||
|
def _get_dict(self):
|
||||||
|
"""A native Python representation of the :class:`Dataset` object. If headers have
|
||||||
|
been set, a list of Python dictionaries will be returned. If no headers have been
|
||||||
|
set, a list of tuples (rows) will be returned instead.
|
||||||
|
|
||||||
|
A dataset object can also be imported by setting the `Dataset.dict` attribute: ::
|
||||||
|
|
||||||
|
data = tablib.Dataset()
|
||||||
|
data.dict = [{'age': 90, 'first_name': 'Kenneth', 'last_name': 'Reitz'}]
|
||||||
|
|
||||||
|
"""
|
||||||
|
return self._package()
|
||||||
|
|
||||||
|
def _set_dict(self, pickle):
|
||||||
|
"""A native Python representation of the Dataset object. If headers have been
|
||||||
|
set, a list of Python dictionaries will be returned. If no headers have been
|
||||||
|
set, a list of tuples (rows) will be returned instead.
|
||||||
|
|
||||||
|
A dataset object can also be imported by setting the :class:`Dataset.dict` attribute. ::
|
||||||
|
|
||||||
|
data = tablib.Dataset()
|
||||||
|
data.dict = [{'age': 90, 'first_name': 'Kenneth', 'last_name': 'Reitz'}]
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not len(pickle):
|
||||||
|
return
|
||||||
|
|
||||||
|
# if list of rows
|
||||||
|
if isinstance(pickle[0], list):
|
||||||
|
self.wipe()
|
||||||
|
for row in pickle:
|
||||||
|
self.append(Row(row))
|
||||||
|
|
||||||
|
# if list of objects
|
||||||
|
elif isinstance(pickle[0], dict):
|
||||||
|
self.wipe()
|
||||||
|
self.headers = list(pickle[0].keys())
|
||||||
|
for row in pickle:
|
||||||
|
self.append(Row(list(row.values())))
|
||||||
|
else:
|
||||||
|
raise UnsupportedFormat
|
||||||
|
|
||||||
|
dict = property(_get_dict, _set_dict)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height(self):
|
||||||
|
"""The number of rows currently in the :class:`Dataset`.
|
||||||
|
Cannot be directly modified.
|
||||||
|
"""
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def width(self):
|
||||||
|
"""The number of columns currently in the :class:`Dataset`.
|
||||||
|
Cannot be directly modified.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return len(self._data[0])
|
||||||
|
except IndexError:
|
||||||
|
try:
|
||||||
|
return len(self.headers)
|
||||||
|
except TypeError:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def export(self, format, **kwargs):
|
||||||
|
"""
|
||||||
|
Export :class:`Dataset` object to `format`.
|
||||||
|
|
||||||
|
:param format: export format
|
||||||
|
:param kwargs: (optional) custom configuration to the format `export_set`.
|
||||||
|
"""
|
||||||
|
fmt = registry.get_format(format)
|
||||||
|
if not hasattr(fmt, 'export_set'):
|
||||||
|
raise Exception('Format {} cannot be exported.'.format(format))
|
||||||
|
|
||||||
|
return fmt.export_set(self, **kwargs)
|
||||||
|
|
||||||
|
# ----
|
||||||
|
# Rows
|
||||||
|
# ----
|
||||||
|
|
||||||
|
def insert(self, index, row, tags=None):
|
||||||
|
"""Inserts a row to the :class:`Dataset` at the given index.
|
||||||
|
|
||||||
|
Rows inserted must be the correct size (height or width).
|
||||||
|
|
||||||
|
The default behaviour is to insert the given row to the :class:`Dataset`
|
||||||
|
object at the given index.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if tags is None:
|
||||||
|
tags = list()
|
||||||
|
self._validate(row)
|
||||||
|
self._data.insert(index, Row(row, tags=tags))
|
||||||
|
|
||||||
|
def rpush(self, row, tags=None):
|
||||||
|
"""Adds a row to the end of the :class:`Dataset`.
|
||||||
|
See :class:`Dataset.insert` for additional documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if tags is None:
|
||||||
|
tags = list()
|
||||||
|
self.insert(self.height, row=row, tags=tags)
|
||||||
|
|
||||||
|
def append(self, row, tags=None):
|
||||||
|
"""Adds a row to the :class:`Dataset`.
|
||||||
|
See :class:`Dataset.insert` for additional documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if tags is None:
|
||||||
|
tags = list()
|
||||||
|
self.rpush(row, tags)
|
||||||
|
|
||||||
|
def extend(self, rows, tags=None):
|
||||||
|
"""Adds a list of rows to the :class:`Dataset` using
|
||||||
|
:class:`Dataset.append`
|
||||||
|
"""
|
||||||
|
|
||||||
|
if tags is None:
|
||||||
|
tags = list()
|
||||||
|
for row in rows:
|
||||||
|
self.append(row, tags)
|
||||||
|
|
||||||
|
# ----
|
||||||
|
# Misc
|
||||||
|
# ----
|
||||||
|
|
||||||
|
def remove_duplicates(self):
|
||||||
|
"""Removes all duplicate rows from the :class:`Dataset` object
|
||||||
|
while maintaining the original order."""
|
||||||
|
seen = set()
|
||||||
|
self._data[:] = [row for row in self._data if
|
||||||
|
not (tuple(row) in seen or seen.add(tuple(row)))]
|
||||||
|
|
||||||
|
def wipe(self):
|
||||||
|
"""Removes all content and headers from the :class:`Dataset` object."""
|
||||||
|
self._data = list()
|
||||||
|
self.__headers = None
|
||||||
|
|
||||||
|
|
||||||
|
registry.register_builtins()
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidDimensions(Exception):
|
||||||
|
"""Invalid size"""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidDatasetIndex(Exception):
|
||||||
|
"""Outside of Dataset size"""
|
||||||
|
|
||||||
|
|
||||||
|
class UnsupportedFormat(NotImplementedError):
|
||||||
|
"""Format is not supported"""
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""`tldextract` accurately separates the gTLD or ccTLD (generic or country code
|
||||||
|
top-level domain) from the registered domain and subdomains of a URL.
|
||||||
|
|
||||||
|
>>> import tldextract
|
||||||
|
|
||||||
|
>>> tldextract.extract('http://forums.news.cnn.com/')
|
||||||
|
ExtractResult(subdomain='forums.news', domain='cnn', suffix='com')
|
||||||
|
|
||||||
|
>>> tldextract.extract('http://forums.bbc.co.uk/') # United Kingdom
|
||||||
|
ExtractResult(subdomain='forums', domain='bbc', suffix='co.uk')
|
||||||
|
|
||||||
|
>>> tldextract.extract('http://www.worldbank.org.kg/') # Kyrgyzstan
|
||||||
|
ExtractResult(subdomain='www', domain='worldbank', suffix='org.kg')
|
||||||
|
|
||||||
|
`ExtractResult` is a namedtuple, so it's simple to access the parts you want.
|
||||||
|
|
||||||
|
>>> ext = tldextract.extract('http://forums.bbc.co.uk')
|
||||||
|
>>> (ext.subdomain, ext.domain, ext.suffix)
|
||||||
|
('forums', 'bbc', 'co.uk')
|
||||||
|
>>> # rejoin subdomain and domain
|
||||||
|
>>> '.'.join(ext[:2])
|
||||||
|
'forums.bbc'
|
||||||
|
>>> # a common alias
|
||||||
|
>>> ext.registered_domain
|
||||||
|
'bbc.co.uk'
|
||||||
|
|
||||||
|
Note subdomain and suffix are _optional_. Not all URL-like inputs have a
|
||||||
|
subdomain or a valid suffix.
|
||||||
|
|
||||||
|
>>> tldextract.extract('google.com')
|
||||||
|
ExtractResult(subdomain='', domain='google', suffix='com')
|
||||||
|
|
||||||
|
>>> tldextract.extract('google.notavalidsuffix')
|
||||||
|
ExtractResult(subdomain='google', domain='notavalidsuffix', suffix='')
|
||||||
|
|
||||||
|
>>> tldextract.extract('http://127.0.0.1:8080/deployed/')
|
||||||
|
ExtractResult(subdomain='', domain='127.0.0.1', suffix='')
|
||||||
|
|
||||||
|
If you want to rejoin the whole namedtuple, regardless of whether a subdomain
|
||||||
|
or suffix were found:
|
||||||
|
|
||||||
|
>>> ext = tldextract.extract('http://127.0.0.1:8080/deployed/')
|
||||||
|
>>> # this has unwanted dots
|
||||||
|
>>> '.'.join(ext)
|
||||||
|
'.127.0.0.1.'
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import collections
|
||||||
|
from urllib.parse import scheme_chars
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
import idna
|
||||||
|
|
||||||
|
from common import utils
|
||||||
|
|
||||||
|
IP_RE = re.compile(r'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$') # pylint: disable=line-too-long
|
||||||
|
|
||||||
|
SCHEME_RE = re.compile(r'^([' + scheme_chars + ']+:)?//')
|
||||||
|
|
||||||
|
|
||||||
|
class ExtractResult(collections.namedtuple('ExtractResult', 'subdomain domain suffix')):
|
||||||
|
"""namedtuple of a URL's subdomain, domain, and suffix."""
|
||||||
|
|
||||||
|
# Necessary for __dict__ member to get populated in Python 3+
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def registered_domain(self):
|
||||||
|
"""
|
||||||
|
Joins the domain and suffix fields with a dot, if they're both set.
|
||||||
|
|
||||||
|
>>> extract('http://forums.bbc.co.uk').registered_domain
|
||||||
|
'bbc.co.uk'
|
||||||
|
>>> extract('http://localhost:8080').registered_domain
|
||||||
|
''
|
||||||
|
"""
|
||||||
|
if self.domain and self.suffix:
|
||||||
|
return self.domain + '.' + self.suffix
|
||||||
|
return ''
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fqdn(self):
|
||||||
|
"""
|
||||||
|
Returns a Fully Qualified Domain Name, if there is a proper domain/suffix.
|
||||||
|
|
||||||
|
>>> extract('http://forums.bbc.co.uk/path/to/file').fqdn
|
||||||
|
'forums.bbc.co.uk'
|
||||||
|
>>> extract('http://localhost:8080').fqdn
|
||||||
|
''
|
||||||
|
"""
|
||||||
|
if self.domain and self.suffix:
|
||||||
|
# self is the namedtuple (subdomain domain suffix)
|
||||||
|
return '.'.join(i for i in self if i)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ipv4(self):
|
||||||
|
"""
|
||||||
|
Returns the ipv4 if that is what the presented domain/url is
|
||||||
|
|
||||||
|
>>> extract('http://127.0.0.1/path/to/file').ipv4
|
||||||
|
'127.0.0.1'
|
||||||
|
>>> extract('http://127.0.0.1.1/path/to/file').ipv4
|
||||||
|
''
|
||||||
|
>>> extract('http://256.1.1.1').ipv4
|
||||||
|
''
|
||||||
|
"""
|
||||||
|
if not (self.suffix or self.subdomain) and IP_RE.match(self.domain):
|
||||||
|
return self.domain
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
class TLDExtract(object):
|
||||||
|
"""A callable for extracting, subdomain, domain, and suffix components from a URL."""
|
||||||
|
|
||||||
|
def __init__(self, cache_file=None):
|
||||||
|
"""
|
||||||
|
Constructs a callable for extracting subdomain, domain, and suffix
|
||||||
|
components from a URL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.cache_file = os.path.expanduser(cache_file or '')
|
||||||
|
self._extractor = None
|
||||||
|
|
||||||
|
def __call__(self, url):
|
||||||
|
"""
|
||||||
|
Takes a string URL and splits it into its subdomain, domain, and
|
||||||
|
suffix (effective TLD, gTLD, ccTLD, etc.) component.
|
||||||
|
|
||||||
|
>>> ext = TLDExtract()
|
||||||
|
>>> ext('http://forums.news.cnn.com/')
|
||||||
|
ExtractResult(subdomain='forums.news', domain='cnn', suffix='com')
|
||||||
|
>>> ext('http://forums.bbc.co.uk/')
|
||||||
|
ExtractResult(subdomain='forums', domain='bbc', suffix='co.uk')
|
||||||
|
"""
|
||||||
|
netloc = SCHEME_RE.sub("", url) \
|
||||||
|
.partition("/")[0] \
|
||||||
|
.partition("?")[0] \
|
||||||
|
.partition("#")[0] \
|
||||||
|
.split("@")[-1] \
|
||||||
|
.partition(":")[0] \
|
||||||
|
.strip() \
|
||||||
|
.rstrip(".")
|
||||||
|
|
||||||
|
labels = netloc.split(".")
|
||||||
|
|
||||||
|
translations = [_decode_punycode(label) for label in labels]
|
||||||
|
suffix_index = self._get_tld_extractor().suffix_index(translations)
|
||||||
|
|
||||||
|
suffix = ".".join(labels[suffix_index:])
|
||||||
|
if not suffix and netloc and utils.looks_like_ip(netloc):
|
||||||
|
return ExtractResult('', netloc, '')
|
||||||
|
|
||||||
|
subdomain = ".".join(labels[:suffix_index - 1]) if suffix_index else ""
|
||||||
|
domain = labels[suffix_index - 1] if suffix_index else ""
|
||||||
|
return ExtractResult(subdomain, domain, suffix)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tlds(self):
|
||||||
|
return self._get_tld_extractor().tlds
|
||||||
|
|
||||||
|
def _get_tld_extractor(self):
|
||||||
|
"""Get or compute this object's TLDExtractor. Looks up the TLDExtractor
|
||||||
|
in roughly the following order, based on the settings passed to
|
||||||
|
__init__:
|
||||||
|
|
||||||
|
1. Memoized on `self`
|
||||||
|
2. Local system cache file"""
|
||||||
|
# pylint: disable=no-else-return
|
||||||
|
|
||||||
|
if self._extractor:
|
||||||
|
return self._extractor
|
||||||
|
tlds = self._get_cached_tlds()
|
||||||
|
if tlds:
|
||||||
|
self._extractor = _PublicSuffixListTLDExtractor(tlds)
|
||||||
|
return self._extractor
|
||||||
|
else:
|
||||||
|
raise Exception("tlds is empty, cannot proceed without tlds.")
|
||||||
|
|
||||||
|
def _get_cached_tlds(self):
|
||||||
|
"""Read the local TLD cache file. Returns None on IOError or other
|
||||||
|
error, or if this object is not set to use the cache
|
||||||
|
file."""
|
||||||
|
if not self.cache_file:
|
||||||
|
return None
|
||||||
|
|
||||||
|
with open(self.cache_file) as cache_file:
|
||||||
|
return json.loads(cache_file.read())
|
||||||
|
|
||||||
|
|
||||||
|
TLD_EXTRACTOR = TLDExtract()
|
||||||
|
|
||||||
|
|
||||||
|
@wraps(TLD_EXTRACTOR.__call__)
|
||||||
|
def extract(url):
|
||||||
|
return TLD_EXTRACTOR(url)
|
||||||
|
|
||||||
|
|
||||||
|
class _PublicSuffixListTLDExtractor(object):
|
||||||
|
"""Wrapper around this project's main algo for PSL
|
||||||
|
lookups.
|
||||||
|
"""
|
||||||
|
def __init__(self, tlds):
|
||||||
|
self.tlds = frozenset(tlds)
|
||||||
|
|
||||||
|
def suffix_index(self, lower_spl):
|
||||||
|
"""Returns the index of the first suffix label.
|
||||||
|
Returns len(spl) if no suffix is found
|
||||||
|
"""
|
||||||
|
length = len(lower_spl)
|
||||||
|
for i in range(length):
|
||||||
|
maybe_tld = '.'.join(lower_spl[i:])
|
||||||
|
exception_tld = '!' + maybe_tld
|
||||||
|
if exception_tld in self.tlds:
|
||||||
|
return i + 1
|
||||||
|
|
||||||
|
if maybe_tld in self.tlds:
|
||||||
|
return i
|
||||||
|
|
||||||
|
wildcard_tld = '*.' + '.'.join(lower_spl[i + 1:])
|
||||||
|
if wildcard_tld in self.tlds:
|
||||||
|
return i
|
||||||
|
|
||||||
|
return length
|
||||||
|
|
||||||
|
|
||||||
|
def _decode_punycode(label):
|
||||||
|
lowered = label.lower()
|
||||||
|
looks_like_puny = lowered.startswith('xn--')
|
||||||
|
if looks_like_puny:
|
||||||
|
try:
|
||||||
|
return idna.decode(label.encode('ascii')).lower()
|
||||||
|
except (UnicodeError, IndexError):
|
||||||
|
pass
|
||||||
|
return lowered
|
||||||
@@ -0,0 +1,789 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
from urllib.parse import scheme_chars
|
||||||
|
from ipaddress import IPv4Address, ip_address
|
||||||
|
from distutils.version import LooseVersion
|
||||||
|
from pathlib import Path
|
||||||
|
from stat import S_IXUSR
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import tenacity
|
||||||
|
from dns.resolver import Resolver
|
||||||
|
|
||||||
|
from common.database import Database
|
||||||
|
from common.domain import Domain
|
||||||
|
from common.records import Record, RecordCollection
|
||||||
|
from config import settings
|
||||||
|
from config.log import logger
|
||||||
|
|
||||||
|
user_agents = [
|
||||||
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
|
||||||
|
'(KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 '
|
||||||
|
'(KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
|
||||||
|
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 '
|
||||||
|
'(KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
|
||||||
|
'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/68.0',
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:61.0) '
|
||||||
|
'Gecko/20100101 Firefox/68.0',
|
||||||
|
'Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/68.0']
|
||||||
|
|
||||||
|
IP_RE = re.compile(r'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$') # pylint: disable=line-too-long
|
||||||
|
SCHEME_RE = re.compile(r'^([' + scheme_chars + ']+:)?//')
|
||||||
|
|
||||||
|
|
||||||
|
def gen_random_ip():
|
||||||
|
"""
|
||||||
|
Generate random decimal IP string
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
ip = IPv4Address(random.randint(0, 2 ** 32 - 1))
|
||||||
|
if ip.is_global:
|
||||||
|
return ip.exploded
|
||||||
|
|
||||||
|
|
||||||
|
def gen_fake_header():
|
||||||
|
"""
|
||||||
|
Generate fake request headers
|
||||||
|
"""
|
||||||
|
headers = settings.request_default_headers.copy()
|
||||||
|
if not isinstance(headers, dict):
|
||||||
|
headers = dict()
|
||||||
|
if settings.enable_random_ua:
|
||||||
|
ua = random.choice(user_agents)
|
||||||
|
headers['User-Agent'] = ua
|
||||||
|
headers['Accept-Encoding'] = 'gzip, deflate'
|
||||||
|
return headers
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_header():
|
||||||
|
"""
|
||||||
|
Get random header
|
||||||
|
"""
|
||||||
|
headers = gen_fake_header()
|
||||||
|
if not isinstance(headers, dict):
|
||||||
|
headers = None
|
||||||
|
return headers
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_proxy():
|
||||||
|
"""
|
||||||
|
Get random proxy
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return random.choice(settings.request_proxy_pool)
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_proxy():
|
||||||
|
"""
|
||||||
|
Get proxy
|
||||||
|
"""
|
||||||
|
if settings.enable_request_proxy:
|
||||||
|
return get_random_proxy()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def split_list(ls, size):
|
||||||
|
"""
|
||||||
|
Split list
|
||||||
|
|
||||||
|
:param list ls: list
|
||||||
|
:param int size: size
|
||||||
|
:return list: result
|
||||||
|
|
||||||
|
>>> split_list([1, 2, 3, 4], 3)
|
||||||
|
[[1, 2, 3], [4]]
|
||||||
|
"""
|
||||||
|
if size == 0:
|
||||||
|
return ls
|
||||||
|
return [ls[i:i + size] for i in range(0, len(ls), size)]
|
||||||
|
|
||||||
|
|
||||||
|
def match_main_domain(domain):
|
||||||
|
if not isinstance(domain, str):
|
||||||
|
return None
|
||||||
|
item = domain.lower().strip()
|
||||||
|
return Domain(item).match()
|
||||||
|
|
||||||
|
|
||||||
|
def read_target_file(target):
|
||||||
|
domains = list()
|
||||||
|
with open(target, encoding='utf-8', errors='ignore') as file:
|
||||||
|
for line in file:
|
||||||
|
domain = match_main_domain(line)
|
||||||
|
if not domain:
|
||||||
|
continue
|
||||||
|
domains.append(domain)
|
||||||
|
sorted_domains = sorted(set(domains), key=domains.index)
|
||||||
|
return sorted_domains
|
||||||
|
|
||||||
|
|
||||||
|
def get_from_target(target):
|
||||||
|
domains = set()
|
||||||
|
if isinstance(target, str):
|
||||||
|
if target.endswith('.txt'):
|
||||||
|
logger.log('FATAL', 'Use targets parameter for multiple domain names')
|
||||||
|
exit(1)
|
||||||
|
domain = match_main_domain(target)
|
||||||
|
if not domain:
|
||||||
|
return domains
|
||||||
|
domains.add(domain)
|
||||||
|
return domains
|
||||||
|
|
||||||
|
|
||||||
|
def get_from_targets(targets):
|
||||||
|
domains = set()
|
||||||
|
if not isinstance(targets, str):
|
||||||
|
return domains
|
||||||
|
try:
|
||||||
|
path = Path(targets)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
return domains
|
||||||
|
if path.exists() and path.is_file():
|
||||||
|
domains = read_target_file(targets)
|
||||||
|
return domains
|
||||||
|
return domains
|
||||||
|
|
||||||
|
|
||||||
|
def get_domains(target, targets=None):
|
||||||
|
logger.log('DEBUG', f'Getting domains')
|
||||||
|
target_domains = get_from_target(target)
|
||||||
|
targets_domains = get_from_targets(targets)
|
||||||
|
domains = list(target_domains.union(targets_domains))
|
||||||
|
if targets_domains:
|
||||||
|
domains = sorted(domains, key=targets_domains.index) # 按照targets原本的index排序
|
||||||
|
if not domains:
|
||||||
|
logger.log('ERROR', f'Did not get a valid domain name')
|
||||||
|
logger.log('DEBUG', f'The obtained domains \n{domains}')
|
||||||
|
return domains
|
||||||
|
|
||||||
|
|
||||||
|
def check_dir(dir_path):
|
||||||
|
if not dir_path.exists():
|
||||||
|
logger.log('INFOR', f'{dir_path} does not exist, directory will be created')
|
||||||
|
dir_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def check_path(path, name, fmt):
|
||||||
|
"""
|
||||||
|
检查结果输出目录路径
|
||||||
|
|
||||||
|
:param path: 保存路径
|
||||||
|
:param name: 导出名字
|
||||||
|
:param fmt: 保存格式
|
||||||
|
:return: 保存路径
|
||||||
|
"""
|
||||||
|
filename = f'{name}.{fmt}'
|
||||||
|
default_path = settings.result_save_dir.joinpath(filename)
|
||||||
|
if isinstance(path, str):
|
||||||
|
path = repr(path).replace('\\', '/') # 将路径中的反斜杠替换为正斜杠
|
||||||
|
path = path.replace('\'', '') # 去除多余的转义
|
||||||
|
else:
|
||||||
|
path = default_path
|
||||||
|
path = Path(path)
|
||||||
|
if path.is_dir(): # 输入是目录的情况
|
||||||
|
path = path.joinpath(filename)
|
||||||
|
parent_dir = path.parent
|
||||||
|
if not parent_dir.exists():
|
||||||
|
logger.log('ALERT', f'{parent_dir} does not exist, directory will be created')
|
||||||
|
parent_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
if path.exists():
|
||||||
|
logger.log('ALERT', f'The {path} exists and will be overwritten')
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def check_format(fmt):
|
||||||
|
"""
|
||||||
|
检查导出格式
|
||||||
|
|
||||||
|
:param fmt: 传入的导出格式
|
||||||
|
:return: 导出格式
|
||||||
|
"""
|
||||||
|
formats = ['csv', 'json', ]
|
||||||
|
if fmt in formats:
|
||||||
|
return fmt
|
||||||
|
else:
|
||||||
|
logger.log('ALERT', f'Does not support {fmt} format')
|
||||||
|
logger.log('ALERT', 'So use csv format by default')
|
||||||
|
return 'csv'
|
||||||
|
|
||||||
|
|
||||||
|
def load_json(path):
|
||||||
|
with open(path) as fp:
|
||||||
|
return json.load(fp)
|
||||||
|
|
||||||
|
|
||||||
|
def save_to_db(name, data, module):
|
||||||
|
"""
|
||||||
|
Save request results to database
|
||||||
|
|
||||||
|
:param str name: table name
|
||||||
|
:param list data: data to be saved
|
||||||
|
:param str module: module name
|
||||||
|
"""
|
||||||
|
db = Database()
|
||||||
|
db.drop_table(name)
|
||||||
|
db.create_table(name)
|
||||||
|
db.save_db(name, data, module)
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def save_to_file(path, data):
|
||||||
|
"""
|
||||||
|
保存数据到文件
|
||||||
|
|
||||||
|
:param path: 保存路径
|
||||||
|
:param data: 待存数据
|
||||||
|
:return: 保存成功与否
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(path, 'w', errors='ignore', newline='', encoding='utf-8') as file:
|
||||||
|
file.write(data)
|
||||||
|
return True
|
||||||
|
except TypeError:
|
||||||
|
with open(path, 'wb', encoding='utf-8') as file:
|
||||||
|
file.write(data)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_response(method, resp):
|
||||||
|
"""
|
||||||
|
检查响应 输出非正常响应返回json的信息
|
||||||
|
|
||||||
|
:param method: 请求方法
|
||||||
|
:param resp: 响应体
|
||||||
|
:return: 是否正常响应
|
||||||
|
"""
|
||||||
|
if resp.status_code == 200 and resp.content:
|
||||||
|
return True
|
||||||
|
logger.log('ALERT', f'{method} {resp.url} {resp.status_code} - '
|
||||||
|
f'{resp.reason} {len(resp.content)}')
|
||||||
|
content_type = resp.headers.get('Content-Type')
|
||||||
|
if content_type and 'json' in content_type and resp.content:
|
||||||
|
try:
|
||||||
|
msg = resp.json()
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('DEBUG', e.args)
|
||||||
|
else:
|
||||||
|
logger.log('ALERT', msg)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def mark_subdomain(old_data, now_data):
|
||||||
|
"""
|
||||||
|
标记新增子域并返回新的数据集
|
||||||
|
|
||||||
|
:param list old_data: 之前子域数据
|
||||||
|
:param list now_data: 现在子域数据
|
||||||
|
:return: 标记后的的子域数据
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
# 第一次收集子域的情况
|
||||||
|
mark_data = now_data.copy()
|
||||||
|
if not old_data:
|
||||||
|
for index, item in enumerate(mark_data):
|
||||||
|
item['new'] = 1
|
||||||
|
mark_data[index] = item
|
||||||
|
return mark_data
|
||||||
|
# 非第一次收集子域的情况
|
||||||
|
old_subdomains = {item.get('subdomain') for item in old_data}
|
||||||
|
for index, item in enumerate(mark_data):
|
||||||
|
subdomain = item.get('subdomain')
|
||||||
|
if subdomain in old_subdomains:
|
||||||
|
item['new'] = 0
|
||||||
|
else:
|
||||||
|
item['new'] = 1
|
||||||
|
mark_data[index] = item
|
||||||
|
return mark_data
|
||||||
|
|
||||||
|
|
||||||
|
def remove_invalid_string(string):
|
||||||
|
# Excel文件中单元格值不能直接存储以下非法字符
|
||||||
|
return re.sub(r'[\000-\010]|[\013-\014]|[\016-\037]', r'', string)
|
||||||
|
|
||||||
|
|
||||||
|
def export_all_results(path, name, fmt, datas):
|
||||||
|
path = check_path(path, name, fmt)
|
||||||
|
logger.log('ALERT', f'The subdomain result for all main domains: {path}')
|
||||||
|
row_list = list()
|
||||||
|
for row in datas:
|
||||||
|
if 'header' in row:
|
||||||
|
row.pop('header')
|
||||||
|
if 'response' in row:
|
||||||
|
row.pop('response')
|
||||||
|
keys = row.keys()
|
||||||
|
values = row.values()
|
||||||
|
row_list.append(Record(keys, values))
|
||||||
|
rows = RecordCollection(iter(row_list))
|
||||||
|
content = rows.export(fmt)
|
||||||
|
if fmt == 'csv':
|
||||||
|
content = '\ufeff' + content
|
||||||
|
save_to_file(path, content)
|
||||||
|
|
||||||
|
|
||||||
|
def export_all_subdomains(alive, path, name, datas):
|
||||||
|
path = check_path(path, name, 'txt')
|
||||||
|
logger.log('ALERT', f'The txt subdomain result for all main domains: {path}')
|
||||||
|
subdomains = set()
|
||||||
|
for row in datas:
|
||||||
|
subdomain = row.get('subdomain')
|
||||||
|
if alive:
|
||||||
|
if not row.get('alive'):
|
||||||
|
continue
|
||||||
|
subdomains.add(subdomain)
|
||||||
|
else:
|
||||||
|
subdomains.add(subdomain)
|
||||||
|
data = '\n'.join(subdomains)
|
||||||
|
save_to_file(path, data)
|
||||||
|
|
||||||
|
|
||||||
|
def export_all(alive, fmt, path, datas):
|
||||||
|
"""
|
||||||
|
将所有结果数据导出
|
||||||
|
|
||||||
|
:param bool alive: 只导出存活子域结果
|
||||||
|
:param str fmt: 导出文件格式
|
||||||
|
:param str path: 导出文件路径
|
||||||
|
:param list datas: 待导出的结果数据
|
||||||
|
"""
|
||||||
|
fmt = check_format(fmt)
|
||||||
|
timestamp = get_timestring()
|
||||||
|
name = f'all_subdomain_result_{timestamp}'
|
||||||
|
export_all_results(path, name, fmt, datas)
|
||||||
|
export_all_subdomains(alive, path, name, datas)
|
||||||
|
|
||||||
|
|
||||||
|
def dns_resolver():
|
||||||
|
"""
|
||||||
|
dns解析器
|
||||||
|
"""
|
||||||
|
resolver = Resolver()
|
||||||
|
resolver.nameservers = settings.resolver_nameservers
|
||||||
|
resolver.timeout = settings.resolver_timeout
|
||||||
|
resolver.lifetime = settings.resolver_lifetime
|
||||||
|
return resolver
|
||||||
|
|
||||||
|
|
||||||
|
def dns_query(qname, qtype):
|
||||||
|
"""
|
||||||
|
查询域名DNS记录
|
||||||
|
|
||||||
|
:param str qname: 待查域名
|
||||||
|
:param str qtype: 查询类型
|
||||||
|
:return: 查询结果
|
||||||
|
"""
|
||||||
|
logger.log('TRACE', f'Try to query {qtype} record of {qname}')
|
||||||
|
resolver = dns_resolver()
|
||||||
|
try:
|
||||||
|
answer = resolver.query(qname, qtype)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('TRACE', e.args)
|
||||||
|
logger.log('TRACE', f'Query {qtype} record of {qname} failed')
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
logger.log('TRACE', f'Query {qtype} record of {qname} succeeded')
|
||||||
|
return answer
|
||||||
|
|
||||||
|
|
||||||
|
def get_timestamp():
|
||||||
|
return int(time.time())
|
||||||
|
|
||||||
|
|
||||||
|
def get_timestring():
|
||||||
|
return time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time()))
|
||||||
|
|
||||||
|
|
||||||
|
def get_classname(classobj):
|
||||||
|
return classobj.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
|
def python_version():
|
||||||
|
return sys.version
|
||||||
|
|
||||||
|
|
||||||
|
def calc_alive(data):
|
||||||
|
return len(list(filter(lambda item: item.get('alive') == 1, data)))
|
||||||
|
|
||||||
|
|
||||||
|
def count_alive(name):
|
||||||
|
db = Database()
|
||||||
|
result = db.count_alive(name)
|
||||||
|
count = result.scalar()
|
||||||
|
db.close()
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def get_subdomains(data):
|
||||||
|
return set(map(lambda item: item.get('subdomain'), data))
|
||||||
|
|
||||||
|
|
||||||
|
def set_id_none(data):
|
||||||
|
new_data = []
|
||||||
|
for item in data:
|
||||||
|
item['id'] = None
|
||||||
|
new_data.append(item)
|
||||||
|
return new_data
|
||||||
|
|
||||||
|
|
||||||
|
def get_filtered_data(data):
|
||||||
|
filtered_data = []
|
||||||
|
for item in data:
|
||||||
|
resolve = item.get('resolve')
|
||||||
|
if resolve != 1:
|
||||||
|
filtered_data.append(item)
|
||||||
|
return filtered_data
|
||||||
|
|
||||||
|
|
||||||
|
def get_sample_banner(headers):
|
||||||
|
temp_list = []
|
||||||
|
server = headers.get('Server')
|
||||||
|
if server:
|
||||||
|
temp_list.append(server)
|
||||||
|
via = headers.get('Via')
|
||||||
|
if via:
|
||||||
|
temp_list.append(via)
|
||||||
|
power = headers.get('X-Powered-By')
|
||||||
|
if power:
|
||||||
|
temp_list.append(power)
|
||||||
|
banner = ','.join(temp_list)
|
||||||
|
return banner
|
||||||
|
|
||||||
|
|
||||||
|
def check_ip_public(ip_list):
|
||||||
|
for ip_str in ip_list:
|
||||||
|
ip = ip_address(ip_str)
|
||||||
|
if not ip.is_global:
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def ip_is_public(ip_str):
|
||||||
|
ip = ip_address(ip_str)
|
||||||
|
if not ip.is_global:
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def get_request_count():
|
||||||
|
return os.cpu_count() * 16
|
||||||
|
|
||||||
|
|
||||||
|
def uniq_dict_list(dict_list):
|
||||||
|
return list(filter(lambda name: dict_list.count(name) == 1, dict_list))
|
||||||
|
|
||||||
|
|
||||||
|
def delete_file(*paths):
|
||||||
|
for path in paths:
|
||||||
|
try:
|
||||||
|
path.unlink()
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
|
||||||
|
|
||||||
|
@tenacity.retry(stop=tenacity.stop_after_attempt(3),
|
||||||
|
wait=tenacity.wait_fixed(2))
|
||||||
|
def check_net():
|
||||||
|
times = 0
|
||||||
|
while True:
|
||||||
|
times += 1
|
||||||
|
urls = ['https://www.baidu.com', 'https://www.bing.com',
|
||||||
|
'https://www.cloudflare.com', 'https://www.akamai.com/',
|
||||||
|
'https://www.fastly.com/', 'https://www.amazon.com/']
|
||||||
|
url = random.choice(urls)
|
||||||
|
logger.log('DEBUG', f'Trying to access {url}')
|
||||||
|
header = get_random_header()
|
||||||
|
proxy = get_proxy()
|
||||||
|
timeout = settings.request_timeout_second
|
||||||
|
verify = settings.request_ssl_verify
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
try:
|
||||||
|
rsp = session.get(url, headers=header, proxies=proxy,
|
||||||
|
timeout=timeout, verify=verify)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
logger.log('ALERT', f'Unable to access Internet, retrying for the {times}th time')
|
||||||
|
else:
|
||||||
|
if rsp.status_code == 200:
|
||||||
|
logger.log('DEBUG', 'Access to Internet OK')
|
||||||
|
return True
|
||||||
|
if times >= 3:
|
||||||
|
logger.log('ALERT', 'Access to Internet failed')
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_dep():
|
||||||
|
logger.log('INFOR', 'Checking dependent environment')
|
||||||
|
implementation = platform.python_implementation()
|
||||||
|
version = platform.python_version()
|
||||||
|
if implementation != 'CPython':
|
||||||
|
logger.log('FATAL', f'OneForAll only passed the test under CPython')
|
||||||
|
exit(1)
|
||||||
|
if LooseVersion(version) < LooseVersion('3.6'):
|
||||||
|
logger.log('FATAL', 'OneForAll requires Python 3.6 or higher')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def get_net_env():
|
||||||
|
logger.log('INFOR', 'Checking network environment')
|
||||||
|
try:
|
||||||
|
result = check_net()
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('DEBUG', e.args)
|
||||||
|
logger.log('ALERT', 'Please check your network environment.')
|
||||||
|
return False
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def check_version(local):
|
||||||
|
logger.log('INFOR', 'Checking for the latest version')
|
||||||
|
api = 'https://api.github.com/repos/shmilylty/OneForAll/releases/latest'
|
||||||
|
header = get_random_header()
|
||||||
|
proxy = get_proxy()
|
||||||
|
timeout = settings.request_timeout_second
|
||||||
|
verify = settings.request_ssl_verify
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
try:
|
||||||
|
resp = session.get(url=api, headers=header, proxies=proxy,
|
||||||
|
timeout=timeout, verify=verify)
|
||||||
|
resp_json = resp.json()
|
||||||
|
latest = resp_json['tag_name']
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ALERT', 'An error occurred while checking the latest version')
|
||||||
|
logger.log('DEBUG', e.args)
|
||||||
|
return
|
||||||
|
if latest > local:
|
||||||
|
change = resp_json.get("body")
|
||||||
|
logger.log('ALERT', f'The current version is {local} '
|
||||||
|
f'but the latest version is {latest}')
|
||||||
|
logger.log('ALERT', f'The {latest} version mainly has the following changes')
|
||||||
|
logger.log('ALERT', change)
|
||||||
|
else:
|
||||||
|
logger.log('INFOR', f'The current version {local} is already the latest version')
|
||||||
|
|
||||||
|
|
||||||
|
def get_main_domain(domain):
|
||||||
|
if not isinstance(domain, str):
|
||||||
|
return None
|
||||||
|
return Domain(domain).registered()
|
||||||
|
|
||||||
|
|
||||||
|
def call_massdns(massdns_path, dict_path, ns_path, output_path, log_path,
|
||||||
|
query_type='A', process_num=1, concurrent_num=10000,
|
||||||
|
quiet_mode=False):
|
||||||
|
logger.log('DEBUG', 'Start running massdns')
|
||||||
|
quiet = ''
|
||||||
|
if quiet_mode:
|
||||||
|
quiet = '--quiet'
|
||||||
|
status_format = settings.brute_status_format
|
||||||
|
socket_num = settings.brute_socket_num
|
||||||
|
resolve_num = settings.brute_resolve_num
|
||||||
|
cmd = f'{massdns_path} {quiet} --status-format {status_format} ' \
|
||||||
|
f'--processes {process_num} --socket-count {socket_num} ' \
|
||||||
|
f'--hashmap-size {concurrent_num} --resolvers {ns_path} ' \
|
||||||
|
f'--resolve-count {resolve_num} --type {query_type} ' \
|
||||||
|
f'--flush --output J --outfile {output_path} ' \
|
||||||
|
f'--root --error-log {log_path} {dict_path} --filter OK ' \
|
||||||
|
f'--sndbuf 0 --rcvbuf 0'
|
||||||
|
logger.log('DEBUG', f'Run command {cmd}')
|
||||||
|
subprocess.run(args=cmd, shell=True)
|
||||||
|
logger.log('DEBUG', f'Finished massdns')
|
||||||
|
|
||||||
|
|
||||||
|
def get_massdns_path(massdns_dir):
|
||||||
|
path = settings.brute_massdns_path
|
||||||
|
if path:
|
||||||
|
return path
|
||||||
|
system = platform.system().lower()
|
||||||
|
machine = platform.machine().lower()
|
||||||
|
name = f'massdns_{system}_{machine}'
|
||||||
|
if system == 'windows':
|
||||||
|
name = f'massdns.exe'
|
||||||
|
if machine == 'amd64':
|
||||||
|
massdns_dir = massdns_dir.joinpath('windows', 'x64')
|
||||||
|
else:
|
||||||
|
massdns_dir = massdns_dir.joinpath('windows', 'x86')
|
||||||
|
path = massdns_dir.joinpath(name)
|
||||||
|
path.chmod(S_IXUSR)
|
||||||
|
if not path.exists():
|
||||||
|
logger.log('FATAL', 'There is no massdns for this platform or architecture')
|
||||||
|
logger.log('INFOR', 'Please try to compile massdns yourself '
|
||||||
|
'and specify the path in the configuration')
|
||||||
|
exit(0)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def is_subname(name):
|
||||||
|
chars = string.ascii_lowercase + string.digits + '.-'
|
||||||
|
for char in name:
|
||||||
|
if char not in chars:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def ip_to_int(ip):
|
||||||
|
if isinstance(ip, int):
|
||||||
|
return ip
|
||||||
|
try:
|
||||||
|
ipv4 = IPv4Address(ip)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
return 0
|
||||||
|
return int(ipv4)
|
||||||
|
|
||||||
|
|
||||||
|
def match_subdomains(domain, html, distinct=True, fuzzy=True):
|
||||||
|
"""
|
||||||
|
Use regexp to match subdomains
|
||||||
|
|
||||||
|
:param str domain: main domain
|
||||||
|
:param str html: response html text
|
||||||
|
:param bool distinct: deduplicate results or not (default True)
|
||||||
|
:param bool fuzzy: fuzzy match subdomain or not (default True)
|
||||||
|
:return set/list: result set or list
|
||||||
|
"""
|
||||||
|
logger.log('TRACE', f'Use regexp to match subdomains in the response body')
|
||||||
|
if fuzzy:
|
||||||
|
regexp = r'(?:[a-z0-9](?:[a-z0-9\-]{0,61}[a-z0-9])?\.){0,}' \
|
||||||
|
+ domain.replace('.', r'\.')
|
||||||
|
result = re.findall(regexp, html, re.I)
|
||||||
|
if not result:
|
||||||
|
return set()
|
||||||
|
deal = map(lambda s: s.lower(), result)
|
||||||
|
if distinct:
|
||||||
|
return set(deal)
|
||||||
|
else:
|
||||||
|
return list(deal)
|
||||||
|
else:
|
||||||
|
regexp = r'(?:\>|\"|\'|\=|\,)(?:http\:\/\/|https\:\/\/)?' \
|
||||||
|
r'(?:[a-z0-9](?:[a-z0-9\-]{0,61}[a-z0-9])?\.){0,}' \
|
||||||
|
+ domain.replace('.', r'\.')
|
||||||
|
result = re.findall(regexp, html, re.I)
|
||||||
|
if not result:
|
||||||
|
return set()
|
||||||
|
regexp = r'(?:http://|https://)'
|
||||||
|
deal = map(lambda s: re.sub(regexp, '', s[1:].lower()), result)
|
||||||
|
if distinct:
|
||||||
|
return set(deal)
|
||||||
|
else:
|
||||||
|
return list(deal)
|
||||||
|
|
||||||
|
|
||||||
|
def check_random_subdomain(subdomains):
|
||||||
|
if not subdomains:
|
||||||
|
logger.log('ALERT', f'The generated dictionary is empty')
|
||||||
|
return
|
||||||
|
for subdomain in subdomains:
|
||||||
|
if subdomain:
|
||||||
|
logger.log('ALERT', f'Please check whether {subdomain} is correct or not')
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def get_url_resp(url):
|
||||||
|
logger.log('INFOR', f'Attempting to request {url}')
|
||||||
|
timeout = settings.request_timeout_second
|
||||||
|
verify = settings.request_ssl_verify
|
||||||
|
session = requests.Session()
|
||||||
|
session.trust_env = False
|
||||||
|
try:
|
||||||
|
resp = session.get(url, params=None, timeout=timeout, verify=verify)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ALERT', f'Error request {url}')
|
||||||
|
logger.log('DEBUG', e.args)
|
||||||
|
return None
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
|
def decode_resp_text(resp):
|
||||||
|
content = resp.content
|
||||||
|
if not content:
|
||||||
|
return str('')
|
||||||
|
try:
|
||||||
|
# 先尝试用utf-8严格解码
|
||||||
|
content = str(content, encoding='utf-8', errors='strict')
|
||||||
|
except (LookupError, TypeError, UnicodeError):
|
||||||
|
try:
|
||||||
|
# 再尝试用gb18030严格解码
|
||||||
|
content = str(content, encoding='gb18030', errors='strict')
|
||||||
|
except (LookupError, TypeError, UnicodeError):
|
||||||
|
# 最后尝试自动解码
|
||||||
|
content = str(content, errors='replace')
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def sort_by_subdomain(data):
|
||||||
|
return sorted(data, key=lambda item: item.get('subdomain'))
|
||||||
|
|
||||||
|
|
||||||
|
def looks_like_ip(maybe_ip):
|
||||||
|
"""Does the given str look like an IP address?"""
|
||||||
|
if not maybe_ip[0].isdigit():
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
socket.inet_aton(maybe_ip)
|
||||||
|
return True
|
||||||
|
except (AttributeError, UnicodeError):
|
||||||
|
if IP_RE.match(maybe_ip):
|
||||||
|
return True
|
||||||
|
except socket.error:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def deal_data(domain):
|
||||||
|
db = Database()
|
||||||
|
db.remove_invalid(domain)
|
||||||
|
db.deduplicate_subdomain(domain)
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(domain):
|
||||||
|
db = Database()
|
||||||
|
data = db.get_data(domain).as_dict()
|
||||||
|
db.close()
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def clear_data(domain):
|
||||||
|
db = Database()
|
||||||
|
db.drop_table(domain)
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def get_ns_path(in_china=None, enable_wildcard=None, ns_ip_list=None):
|
||||||
|
data_dir = settings.data_storage_dir
|
||||||
|
path = data_dir.joinpath('nameservers.txt')
|
||||||
|
if in_china:
|
||||||
|
path = data_dir.joinpath('nameservers_cn.txt')
|
||||||
|
if not enable_wildcard:
|
||||||
|
return path
|
||||||
|
if not ns_ip_list:
|
||||||
|
return path
|
||||||
|
path = settings.authoritative_dns_path
|
||||||
|
ns_data = '\n'.join(ns_ip_list)
|
||||||
|
save_to_file(path, ns_data)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def init_table(domain):
|
||||||
|
db = Database()
|
||||||
|
db.drop_table(domain)
|
||||||
|
db.create_table(domain)
|
||||||
|
db.close()
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import importlib
|
||||||
|
from config import default
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(object):
|
||||||
|
def __init__(self):
|
||||||
|
# 获取全局变量中的配置信息
|
||||||
|
for attr in dir(default):
|
||||||
|
setattr(self, attr, getattr(default, attr))
|
||||||
|
setting_modules = ['config.setting', 'config.api']
|
||||||
|
for setting_module in setting_modules:
|
||||||
|
setting = importlib.import_module(setting_module)
|
||||||
|
for attr in dir(setting):
|
||||||
|
setattr(self, attr, getattr(setting, attr))
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# 模块API配置
|
||||||
|
# Censys可以免费注册获取API:https://censys.io/api
|
||||||
|
censys_api_id = ''
|
||||||
|
censys_api_secret = ''
|
||||||
|
|
||||||
|
# Binaryedge可以免费注册获取API:https://app.binaryedge.io/account/api
|
||||||
|
# 免费的API有效期只有1个月,到期之后可以再次生成,每月可以查询250次。
|
||||||
|
binaryedge_api = ''
|
||||||
|
|
||||||
|
# BeVigil API: https://bevigil.com/osint-api
|
||||||
|
bevigil_api = ''
|
||||||
|
|
||||||
|
# Chinaz可以免费注册获取API:http://api.chinaz.com/ApiDetails/Alexa
|
||||||
|
chinaz_api = ''
|
||||||
|
|
||||||
|
# Bing可以免费注册获取API:https://azure.microsoft.com/zh-cn/services/
|
||||||
|
# cognitive-services/bing-web-search-api/#web-json
|
||||||
|
bing_api_id = ''
|
||||||
|
bing_api_key = ''
|
||||||
|
|
||||||
|
# SecurityTrails可以免费注册获取API:https://securitytrails.com/corp/api
|
||||||
|
securitytrails_api = ''
|
||||||
|
|
||||||
|
# https://fofa.so/api
|
||||||
|
fofa_api_email = '' # fofa用户邮箱
|
||||||
|
fofa_api_key = '' # fofa用户key
|
||||||
|
|
||||||
|
# Google可以免费注册获取API:
|
||||||
|
# 免费的API只能查询前100条结果
|
||||||
|
# https://developers.google.com/custom-search/v1/overview#search_engine_id
|
||||||
|
# 创建自定义搜索引擎后需要在响应的控制面板上启用Search the entire web
|
||||||
|
google_api_id = '' # Google API自定义搜索引擎id
|
||||||
|
# https://developers.google.com/custom-search/v1/overview#api_key
|
||||||
|
google_api_key = '' # Google API自定义搜索key
|
||||||
|
|
||||||
|
# https://api.passivetotal.org/api/docs/
|
||||||
|
riskiq_api_username = ''
|
||||||
|
riskiq_api_key = ''
|
||||||
|
|
||||||
|
# Shodan可以免费注册获取API: https://account.shodan.io/register
|
||||||
|
# 免费的API限速1秒查询1次
|
||||||
|
shodan_api_key = ''
|
||||||
|
# ThreatBook API 查询子域名需要收费 https://x.threatbook.cn/nodev4/vb4/myAPI
|
||||||
|
threatbook_api_key = ''
|
||||||
|
|
||||||
|
# VirusTotal可以免费注册获取API: https://developers.virustotal.com/reference
|
||||||
|
virustotal_api_key = ''
|
||||||
|
|
||||||
|
# https://www.zoomeye.org/doc?channel=api
|
||||||
|
zoomeye_api_key = ''
|
||||||
|
|
||||||
|
# Spyse可以免费注册获取API: https://spyse.com/
|
||||||
|
spyse_api_token = ''
|
||||||
|
|
||||||
|
# https://www.circl.lu/services/passive-dns/
|
||||||
|
circl_api_username = ''
|
||||||
|
circl_api_password = ''
|
||||||
|
|
||||||
|
# https://www.dnsdb.info/
|
||||||
|
dnsdb_api_key = ''
|
||||||
|
|
||||||
|
# ipv4info可以免费注册获取API: http://ipv4info.com/tools/api/
|
||||||
|
# 免费的API有效期只有2天,到期之后可以再次生成,每天可以查询50次。
|
||||||
|
ipv4info_api_key = ''
|
||||||
|
|
||||||
|
# https://github.com/360netlab/flint
|
||||||
|
# passivedns_api_addr默认空使用http://api.passivedns.cn
|
||||||
|
# passivedns_api_token可为空
|
||||||
|
passivedns_api_addr = ''
|
||||||
|
passivedns_api_token = ''
|
||||||
|
|
||||||
|
# Github Token可以访问https://github.com/settings/tokens生成,user为Github用户名
|
||||||
|
# 用于子域接管和子域收集
|
||||||
|
github_api_user = ''
|
||||||
|
github_api_token = ''
|
||||||
|
|
||||||
|
# obtain Cloudflare API key from https://dash.cloudflare.com/profile/api-tokens
|
||||||
|
cloudflare_api_token = ''
|
||||||
|
|
||||||
|
# https://hunter.qianxin.com/home/userInfo
|
||||||
|
hunter_api_key = ''
|
||||||
|
|
||||||
|
# https://api-docs.fullhunt.io/
|
||||||
|
fullhunt_api_key = ''
|
||||||
|
|
||||||
|
|
||||||
|
# 登录quake之后可在个人中心获取key https://quake.360.net/quake/#/personal?tab=message
|
||||||
|
quake_api_key = ''
|
||||||
|
|
||||||
|
#https://www.racent.com/ctlog F2>Network抓包获取Token
|
||||||
|
racent_api_token = ''
|
||||||
|
|
||||||
|
# https://windvane.lichoin.com/
|
||||||
|
windvane_api_token = ''
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
"""
|
||||||
|
OneForAll默认配置
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
# 禁用所有警告信息
|
||||||
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
|
# 路径设置
|
||||||
|
relative_directory = pathlib.Path(__file__).parent.parent # OneForAll代码相对路径
|
||||||
|
module_dir = relative_directory.joinpath('modules') # OneForAll模块目录
|
||||||
|
third_party_dir = relative_directory.joinpath('thirdparty') # 三方工具目录
|
||||||
|
data_storage_dir = relative_directory.joinpath('data') # 数据存放目录
|
||||||
|
result_save_dir = relative_directory.joinpath('results') # 结果保存目录
|
||||||
|
temp_save_dir = result_save_dir.joinpath('temp')
|
||||||
|
|
||||||
|
# OneForAll入口参数设置
|
||||||
|
enable_check_network = True # 开启网络环境检查
|
||||||
|
enable_check_version = True # 开启最新版本检查
|
||||||
|
enable_brute_module = True # 使用爆破模块(默认True)
|
||||||
|
enable_dns_resolve = True # 使用DNS解析子域(默认True)
|
||||||
|
enable_http_request = True # 使用HTTP请求子域(默认True)
|
||||||
|
enable_finder_module = True # 开启finder模块,开启会从响应体和JS中再次发现子域(默认True)
|
||||||
|
enable_altdns_module = True # 开启altdns模块,开启会利用置换技术重组子域再次发现新子域(默认True)
|
||||||
|
enable_enrich_module = True # 开启enrich模块,开启会富化出信息,如ip的cdn,cidr,asn,org,addr和isp等信息
|
||||||
|
enable_banner_identify = True # 开启WEB指纹识别模块(默认True)
|
||||||
|
enable_takeover_check = False # 开启子域接管风险检查(默认False)
|
||||||
|
# 参数可选值有 'small', 'medium', 'large'
|
||||||
|
http_request_port = 'small' # HTTP请求子域(默认 'small',探测80,443端口)
|
||||||
|
# 参数可选值True,False分别表示导出存活,全部子域结果
|
||||||
|
result_export_alive = False # 只导出存活的子域结果(默认False)
|
||||||
|
# 参数可选格式有 'csv', 'json'
|
||||||
|
result_save_format = 'csv' # 子域结果保存文件格式(默认csv)
|
||||||
|
# 参数path默认None使用OneForAll结果目录自动生成路径
|
||||||
|
result_save_path = None # 子域结果保存文件路径(默认None)
|
||||||
|
|
||||||
|
# 收集模块设置
|
||||||
|
save_module_result = False # 保存各模块发现结果为json文件(默认False)
|
||||||
|
enable_all_module = True # 启用所有收集模块(默认True)
|
||||||
|
enable_partial_module = [] # 启用部分收集模块 必须禁用enable_all_module才能生效
|
||||||
|
# 只使用ask和baidu搜索引擎收集子域的示例
|
||||||
|
# enable_partial_module = ['modules.search.ask', 'modules.search.baidu']
|
||||||
|
module_thread_timeout = 90.0 # 每个收集模块线程超时时间(默认90秒)
|
||||||
|
|
||||||
|
# 爆破模块设置
|
||||||
|
enable_wildcard_check = True # 开启泛解析检测(默认True)
|
||||||
|
enable_wildcard_deal = True # 开启泛解析处理(默认True)
|
||||||
|
brute_massdns_path = None # 默认None自动选择 如需填写请填写绝对路径
|
||||||
|
brute_status_format = 'ansi' # 爆破时状态输出格式(默认asni,可选json)
|
||||||
|
brute_concurrent_num = 2000 # 并发查询数量(默认2000,最大推荐10000)
|
||||||
|
brute_socket_num = 1 # 爆破时每个进程下的socket数量
|
||||||
|
brute_resolve_num = 15 # 解析失败时尝试换名称服务器重查次数
|
||||||
|
# 爆破所使用的字典路径(默认None则使用data/subdomains.txt,自定义字典请使用绝对路径)
|
||||||
|
brute_wordlist_path = None
|
||||||
|
use_china_nameservers = True # 使用中国域名服务器 如果你所在网络不在中国则建议设置False
|
||||||
|
# 域名的权威DNS名称服务器的保存路径 当域名开启了泛解析时会使用该名称服务器来进行A记录查询
|
||||||
|
authoritative_dns_path = data_storage_dir.joinpath('authoritative_dns.txt')
|
||||||
|
enable_recursive_brute = False # 是否使用递归爆破(默认False)
|
||||||
|
brute_recursive_depth = 2 # 递归爆破深度(默认2层)
|
||||||
|
# 爆破下一层子域所使用的字典路径(默认None则使用data/subnames_next.txt,自定义字典请使用绝对路径)
|
||||||
|
recursive_nextlist_path = None
|
||||||
|
enable_check_dict = False # 是否开启字典配置检查提示(默认False)
|
||||||
|
delete_generated_dict = True # 是否删除爆破时临时生成的字典(默认True)
|
||||||
|
delete_massdns_result = True # 是否删除爆破时massdns输出的解析结果 (默认True)
|
||||||
|
only_save_valid = True # 是否在处理爆破结果时只存入解析成功的子域
|
||||||
|
check_time = 10 # 检查字典配置停留时间(默认10秒)
|
||||||
|
enable_fuzz = False # 是否使用fuzz模式枚举域名
|
||||||
|
fuzz_place = None # 指定爆破的位置 指定的位置用`@`表示 示例:www.@.example.com
|
||||||
|
fuzz_rule = None # fuzz域名使用的正则表达式 示例:'[a-z][0-9]' 表示第一位是字母 第二位是数字
|
||||||
|
fuzz_list = None # fuzz域名使用的字典路径
|
||||||
|
brute_ip_blacklist = {'0.0.0.0', '0.0.0.1'} # IP黑名单 子域解析到IP黑名单则标记为非法子域
|
||||||
|
ip_appear_maximum = 100 # 多个子域解析到同一IP次数超过100次则标记为非法(泛解析)子域
|
||||||
|
|
||||||
|
# altdns模块设置
|
||||||
|
altdns_increase_num = True
|
||||||
|
altdns_decrease_num = True
|
||||||
|
altdns_replace_word = False
|
||||||
|
altdns_insert_word = False
|
||||||
|
altdns_add_word = False
|
||||||
|
|
||||||
|
|
||||||
|
# banner识别模块设置
|
||||||
|
banner_process_number = 4 # 识别进程数量(默认4)
|
||||||
|
|
||||||
|
# 代理设置
|
||||||
|
enable_request_proxy = False # 是否使用代理(全局开关,默认False)
|
||||||
|
proxy_all_module = False # 代理所有模块
|
||||||
|
proxy_partial_module = ['GoogleQuery', 'AskSearch', 'DuckDuckGoSearch',
|
||||||
|
'GoogleAPISearch', 'GoogleSearch', 'YahooSearch',
|
||||||
|
'YandexSearch', 'CrossDomainXml',
|
||||||
|
'ContentSecurityPolicy'] # 代理自定义的模块
|
||||||
|
request_proxy_pool = [{'http': 'http://127.0.0.1:1080',
|
||||||
|
'https': 'https://127.0.0.1:1080'}] # 代理池
|
||||||
|
# request_proxy_pool = [{'http': 'socks5h://127.0.0.1:10808',
|
||||||
|
# 'https': 'socks5h://127.0.0.1:10808'}] # 代理池
|
||||||
|
|
||||||
|
|
||||||
|
# 请求设置
|
||||||
|
request_thread_count = None # 请求线程数量(默认None,则根据情况自动设置)
|
||||||
|
request_timeout_second = (13, 27) # 请求超时秒数(默认connect timout推荐略大于3秒)
|
||||||
|
request_ssl_verify = False # 请求SSL验证(默认False)
|
||||||
|
request_allow_redirect = True # 请求允许重定向(默认True)
|
||||||
|
request_redirect_limit = 10 # 请求跳转限制(默认10次)
|
||||||
|
# 默认请求头 可以在headers里添加自定义请求头
|
||||||
|
request_default_headers = {
|
||||||
|
'Accept': 'text/html,application/xhtml+xml,'
|
||||||
|
'application/xml;q=0.9,*/*;q=0.8',
|
||||||
|
'Accept-Encoding': 'gzip, deflate',
|
||||||
|
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7',
|
||||||
|
'Cache-Control': 'max-age=0',
|
||||||
|
'DNT': '1',
|
||||||
|
'Referer': 'https://www.google.com/',
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
|
||||||
|
'(KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'X-Forwarded-For': '127.0.0.1'
|
||||||
|
}
|
||||||
|
enable_random_ua = True # 使用随机UA(默认True,开启可以覆盖request_default_headers的UA)
|
||||||
|
|
||||||
|
# 搜索模块设置
|
||||||
|
# 开启全量搜索会尽量去获取搜索引擎搜索的全部结果,不过搜索耗时可能会过长
|
||||||
|
enable_full_search = False # 启用全量搜索(默认False)
|
||||||
|
enable_recursive_search = False # 递归搜索子域(默认False)
|
||||||
|
search_recursive_times = 2 # 递归搜索层数(默认2)
|
||||||
|
|
||||||
|
# DNS解析设置
|
||||||
|
resolver_nameservers = [
|
||||||
|
'223.5.5.5', # AliDNS
|
||||||
|
'119.29.29.29', # DNSPod
|
||||||
|
'114.114.114.114', # 114DNS
|
||||||
|
'8.8.8.8', # Google DNS
|
||||||
|
'1.1.1.1' # CloudFlare DNS
|
||||||
|
] # 指定查询的DNS域名服务器
|
||||||
|
resolver_timeout = 5.0 # 解析超时时间(默认5.0秒)
|
||||||
|
resolver_lifetime = 10.0 # 解析存活时间(默认10.0秒)
|
||||||
|
|
||||||
|
# 请求端口探测设置
|
||||||
|
# 你可以在端口列表添加自定义端口
|
||||||
|
small_ports = [80, 443] # 默认使用
|
||||||
|
medium_ports = [80, 443, 8000, 8080, 8443]
|
||||||
|
# 注意:建议大厂的域名尽量不使用大端口范围,因为大厂的子域太多,加上使用大端口范围会导致生成的
|
||||||
|
# 请求上十万,百万,千万级,可能会导致内存不足程序奔溃,另外这样级别的请求量等待时间也是漫长的。
|
||||||
|
# OneForAll不是一个端口扫描工具,如果要扫端口建议使用nmap,zmap之类的工具。
|
||||||
|
large_ports = [80, 81, 280, 300, 443, 591, 593, 832, 888, 901, 981, 1010, 1080,
|
||||||
|
1100, 1241, 1311, 1352, 1434, 1521, 1527, 1582, 1583, 1944, 2082,
|
||||||
|
2082, 2086, 2087, 2095, 2096, 2222, 2301, 2480, 3000, 3128, 3333,
|
||||||
|
4000, 4001, 4002, 4100, 4125, 4243, 4443, 4444, 4567, 4711, 4712,
|
||||||
|
4848, 4849, 4993, 5000, 5104, 5108, 5432, 5555, 5800, 5801, 5802,
|
||||||
|
5984, 5985, 5986, 6082, 6225, 6346, 6347, 6443, 6480, 6543, 6789,
|
||||||
|
7000, 7001, 7002, 7396, 7474, 7674, 7675, 7777, 7778, 8000, 8001,
|
||||||
|
8002, 8003, 8004, 8005, 8006, 8008, 8009, 8010, 8014, 8042, 8069,
|
||||||
|
8075, 8080, 8081, 8083, 8088, 8090, 8091, 8092, 8093, 8016, 8118,
|
||||||
|
8123, 8172, 8181, 8200, 8222, 8243, 8280, 8281, 8333, 8384, 8403,
|
||||||
|
8443, 8500, 8530, 8531, 8800, 8806, 8834, 8880, 8887, 8888, 8910,
|
||||||
|
8983, 8989, 8990, 8991, 9000, 9043, 9060, 9080, 9090, 9091, 9200,
|
||||||
|
9294, 9295, 9443, 9444, 9800, 9981, 9988, 9990, 9999, 10000,
|
||||||
|
10880, 11371, 12043, 12046, 12443, 15672, 16225, 16080, 18091,
|
||||||
|
18092, 20000, 20720, 24465, 28017, 28080, 30821, 43110, 61600]
|
||||||
|
ports = {'small': small_ports, 'medium': medium_ports, 'large': large_ports}
|
||||||
|
|
||||||
|
common_subnames = {'i', 'w', 'm', 'en', 'us', 'zh', 'w3', 'app', 'bbs',
|
||||||
|
'web', 'www', 'job', 'docs', 'news', 'blog', 'data',
|
||||||
|
'help', 'live', 'mall', 'blogs', 'files', 'forum',
|
||||||
|
'store', 'mobile'}
|
||||||
|
|
||||||
|
# 模块API配置
|
||||||
|
# Censys可以免费注册获取API:https://censys.io/api
|
||||||
|
censys_api_id = ''
|
||||||
|
censys_api_secret = ''
|
||||||
|
|
||||||
|
# Binaryedge可以免费注册获取API:https://app.binaryedge.io/account/api
|
||||||
|
# 免费的API有效期只有1个月,到期之后可以再次生成,每月可以查询250次。
|
||||||
|
binaryedge_api = ''
|
||||||
|
|
||||||
|
# BeVigil API: https://bevigil.com/osint-api
|
||||||
|
bevigil_api = ''
|
||||||
|
|
||||||
|
# Chinaz可以免费注册获取API:http://api.chinaz.com/ApiDetails/Alexa
|
||||||
|
chinaz_api = ''
|
||||||
|
|
||||||
|
# Bing可以免费注册获取API:https://azure.microsoft.com/zh-cn/services/
|
||||||
|
# cognitive-services/bing-web-search-api/#web-json
|
||||||
|
bing_api_id = ''
|
||||||
|
bing_api_key = ''
|
||||||
|
|
||||||
|
# SecurityTrails可以免费注册获取API:https://securitytrails.com/corp/api
|
||||||
|
securitytrails_api = ''
|
||||||
|
|
||||||
|
# https://fofa.so/api
|
||||||
|
fofa_api_email = '' # fofa用户邮箱
|
||||||
|
fofa_api_key = '' # fofa用户key
|
||||||
|
|
||||||
|
# Google可以免费注册获取API:
|
||||||
|
# 免费的API只能查询前100条结果
|
||||||
|
# https://developers.google.com/custom-search/v1/overview#search_engine_id
|
||||||
|
# 创建自定义搜索引擎后需要在响应的控制面板上启用Search the entire web
|
||||||
|
google_api_id = '' # Google API自定义搜索引擎id
|
||||||
|
# https://developers.google.com/custom-search/v1/overview#api_key
|
||||||
|
google_api_key = '' # Google API自定义搜索key
|
||||||
|
|
||||||
|
# https://api.passivetotal.org/api/docs/
|
||||||
|
riskiq_api_username = ''
|
||||||
|
riskiq_api_key = ''
|
||||||
|
|
||||||
|
# Shodan可以免费注册获取API: https://account.shodan.io/register
|
||||||
|
# 免费的API限速1秒查询1次
|
||||||
|
shodan_api_key = ''
|
||||||
|
# ThreatBook API 查询子域名需要收费 https://x.threatbook.cn/nodev4/vb4/myAPI
|
||||||
|
threatbook_api_key = ''
|
||||||
|
|
||||||
|
# VirusTotal可以免费注册获取API: https://developers.virustotal.com/reference
|
||||||
|
virustotal_api_key = ''
|
||||||
|
|
||||||
|
# https://www.zoomeye.org/doc?channel=api
|
||||||
|
zoomeye_api_key = ''
|
||||||
|
|
||||||
|
# Spyse可以免费注册获取API: https://spyse.com/
|
||||||
|
spyse_api_token = ''
|
||||||
|
|
||||||
|
# https://www.circl.lu/services/passive-dns/
|
||||||
|
circl_api_username = ''
|
||||||
|
circl_api_password = ''
|
||||||
|
|
||||||
|
# https://www.dnsdb.info/
|
||||||
|
dnsdb_api_key = ''
|
||||||
|
|
||||||
|
# ipv4info可以免费注册获取API: http://ipv4info.com/tools/api/
|
||||||
|
# 免费的API有效期只有2天,到期之后可以再次生成,每天可以查询50次。
|
||||||
|
ipv4info_api_key = ''
|
||||||
|
|
||||||
|
# https://github.com/360netlab/flint
|
||||||
|
# passivedns_api_addr默认空使用http://api.passivedns.cn
|
||||||
|
# passivedns_api_token可为空
|
||||||
|
passivedns_api_addr = ''
|
||||||
|
passivedns_api_token = ''
|
||||||
|
|
||||||
|
# Github Token可以访问https://github.com/settings/tokens生成,user为Github用户名
|
||||||
|
# 用于子域接管和子域收集
|
||||||
|
github_api_user = ''
|
||||||
|
github_api_token = ''
|
||||||
|
|
||||||
|
# obtain Cloudflare API key from https://dash.cloudflare.com/profile/api-tokens
|
||||||
|
cloudflare_api_token = ''
|
||||||
|
|
||||||
|
# https://hunter.qianxin.com/home/userInfo
|
||||||
|
hunter_api_key = ''
|
||||||
|
|
||||||
|
# https://api-docs.fullhunt.io/
|
||||||
|
fullhunt_api_key = ''
|
||||||
|
|
||||||
|
# https://windvane.lichoin.com/
|
||||||
|
windvane_api_token = ''
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import sys
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
# 路径设置
|
||||||
|
relative_directory = pathlib.Path(__file__).parent.parent # OneForAll代码相对路径
|
||||||
|
result_save_dir = relative_directory.joinpath('results') # 结果保存目录
|
||||||
|
log_path = result_save_dir.joinpath('oneforall.log') # OneForAll日志保存路径
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
# 终端日志输出格式
|
||||||
|
stdout_fmt = '<cyan>{time:HH:mm:ss,SSS}</cyan> ' \
|
||||||
|
'[<level>{level: <5}</level>] ' \
|
||||||
|
'<blue>{module}</blue>:<cyan>{line}</cyan> - ' \
|
||||||
|
'<level>{message}</level>'
|
||||||
|
# 日志文件记录格式
|
||||||
|
logfile_fmt = '<light-green>{time:YYYY-MM-DD HH:mm:ss,SSS}</light-green> ' \
|
||||||
|
'[<level>{level: <5}</level>] ' \
|
||||||
|
'<cyan>{process.name}({process.id})</cyan>:' \
|
||||||
|
'<cyan>{thread.name: <18}({thread.id: <5})</cyan> | ' \
|
||||||
|
'<blue>{module}</blue>.<blue>{function}</blue>:' \
|
||||||
|
'<blue>{line}</blue> - <level>{message}</level>'
|
||||||
|
|
||||||
|
logger.remove()
|
||||||
|
logger.level(name='TRACE', color='<cyan><bold>')
|
||||||
|
logger.level(name='DEBUG', color='<blue><bold>')
|
||||||
|
logger.level(name='INFOR', no=20, color='<green><bold>')
|
||||||
|
logger.level(name='QUITE', no=25, color='<green><bold>')
|
||||||
|
logger.level(name='ALERT', no=30, color='<yellow><bold>')
|
||||||
|
logger.level(name='ERROR', color='<red><bold>')
|
||||||
|
logger.level(name='FATAL', no=50, color='<RED><bold>')
|
||||||
|
|
||||||
|
# 如果你想在命令终端静默运行OneForAll,可以将以下一行中的level设置为QUITE
|
||||||
|
# 命令终端日志级别默认为INFOR
|
||||||
|
logger.add(sys.stderr, level='INFOR', format=stdout_fmt, enqueue=True)
|
||||||
|
# 日志文件默认为级别为DEBUG
|
||||||
|
logger.add(log_path, level='DEBUG', format=logfile_fmt, enqueue=True, encoding='utf-8')
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# coding=utf-8
|
||||||
|
"""
|
||||||
|
OneForAll自定义配置
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
# 路径设置
|
||||||
|
relative_directory = pathlib.Path(__file__).parent.parent # OneForAll代码相对路径
|
||||||
|
data_storage_dir = relative_directory.joinpath('data') # 数据存放目录
|
||||||
|
|
||||||
|
# OneForAll入口参数设置
|
||||||
|
enable_check_network = True # 开启网络环境检查
|
||||||
|
enable_check_version = True # 开启最新版本检查
|
||||||
|
enable_brute_module = True # 使用爆破模块(默认True)
|
||||||
|
enable_dns_resolve = True # 使用DNS解析子域(默认True)
|
||||||
|
enable_http_request = True # 使用HTTP请求子域(默认True)
|
||||||
|
enable_finder_module = True # 开启finder模块,开启会从响应体和JS中再次发现子域(默认True)
|
||||||
|
enable_altdns_module = True # 开启altdns模块,开启会利用置换技术重组子域再次发现新子域(默认True)
|
||||||
|
enable_cdn_check = True # 开启cdn检查模块(默认True)
|
||||||
|
enable_banner_identify = True # 开启WEB指纹识别模块(默认True)
|
||||||
|
enable_takeover_check = False # 开启子域接管风险检查(默认False)
|
||||||
|
# HTTP请求子域的端口范围 参数可选值有 'small', 'medium', 'large'
|
||||||
|
http_request_port = 'small' # 请求端口范围(默认 'small',表示请求子域的80,443端口)
|
||||||
|
# 参数可选值True,False分别表示导出存活,全部子域结果
|
||||||
|
result_export_alive = False # 只导出存活的子域结果(默认False)
|
||||||
|
result_save_format = 'csv' # 子域结果保存文件格式(默认csv)
|
||||||
|
# 参数path默认None使用OneForAll结果目录自动生成路径
|
||||||
|
result_save_path = None # 子域结果保存文件路径(默认None)
|
||||||
|
|
||||||
|
# 收集模块设置
|
||||||
|
save_module_result = False # 保存各模块发现结果为json文件(默认False)
|
||||||
|
enable_all_module = True # 启用所有收集模块(默认True)
|
||||||
|
enable_partial_module = [] # 启用部分收集模块 必须禁用enable_all_module才能生效
|
||||||
|
# 只使用ask和baidu搜索引擎收集子域的示例
|
||||||
|
# enable_partial_module = ['modules.search.ask', 'modules.search.baidu']
|
||||||
|
|
||||||
|
# 爆破模块设置
|
||||||
|
brute_concurrent_num = 2000 # 爆破时并发查询数量(默认2000,最大推荐10000)
|
||||||
|
# 爆破所使用的字典路径(默认None则使用data/subdomains.txt,自定义字典请使用绝对路径)
|
||||||
|
brute_wordlist_path = None
|
||||||
|
use_china_nameservers = True # 使用中国域名服务器 如果你所在网络不在中国则建议设置False
|
||||||
|
enable_recursive_brute = False # 是否使用递归爆破(默认False)
|
||||||
|
brute_recursive_depth = 2 # 递归爆破深度(默认2层)
|
||||||
|
# 爆破下一层子域所使用的字典路径(默认None则使用data/subnames_next.txt,自定义字典请使用绝对路径)
|
||||||
|
recursive_nextlist_path = None
|
||||||
|
enable_check_dict = False # 是否开启字典配置检查提示(默认False)
|
||||||
|
delete_generated_dict = True # 是否删除爆破时临时生成的字典(默认True)
|
||||||
|
# 是否删除爆破时massdns输出的解析结果 (默认True)
|
||||||
|
# massdns输出的结果中包含更详细解析结果
|
||||||
|
# 注意: 当爆破的字典较大或使用递归爆破或目标域名存在泛解析时生成的文件可能会很大
|
||||||
|
delete_massdns_result = True
|
||||||
|
only_save_valid = True # 是否在处理爆破结果时只存入解析成功的子域
|
||||||
|
check_time = 10 # 检查字典配置停留时间(默认10秒)
|
||||||
|
enable_fuzz = False # 是否使用fuzz模式枚举域名
|
||||||
|
fuzz_place = None # 指定爆破的位置 指定的位置用`*`表示 示例:www.*.example.com
|
||||||
|
fuzz_rule = None # fuzz域名的正则 示例:'[a-z][0-9]' 表示第一位是字母 第二位是数字
|
||||||
|
brute_ip_blacklist = {'0.0.0.0', '0.0.0.1'} # IP黑名单 子域解析到IP黑名单则标记为非法子域
|
||||||
|
# CNAME黑名单 子域解析到CNAME黑名单则标记为非法子域
|
||||||
|
brute_cname_blacklist = {'nonexist.sdo.com', 'shop.taobao.com'}
|
||||||
|
ip_appear_maximum = 100 # 多个子域解析到同一IP次数超过100次则标记为非法(泛解析)子域
|
||||||
|
cname_appear_maximum = 50 # 多个子域解析到同一cname次数超过50次则标记为非法(泛解析)子域
|
||||||
|
|
||||||
|
# 代理设置
|
||||||
|
enable_request_proxy = False # 是否使用代理(全局开关)
|
||||||
|
proxy_all_module = False # 代理所有模块
|
||||||
|
proxy_partial_module = ['GoogleQuery', 'AskSearch', 'DuckDuckGoSearch',
|
||||||
|
'GoogleAPISearch', 'GoogleSearch', 'YahooSearch',
|
||||||
|
'YandexSearch', 'CrossDomainXml',
|
||||||
|
'ContentSecurityPolicy'] # 代理自定义的模块
|
||||||
|
request_proxy_pool = [{'http': 'http://127.0.0.1:1080',
|
||||||
|
'https': 'http://127.0.0.1:1080'}] # 代理池
|
||||||
|
# request_proxy_pool = [{'http': 'socks5h://127.0.0.1:10808',
|
||||||
|
# 'https': 'socks5h://127.0.0.1:10808'}] # 代理池
|
||||||
|
|
||||||
|
|
||||||
|
# 请求设置
|
||||||
|
request_thread_count = None # 请求线程数量(默认None,则根据情况自动设置)
|
||||||
|
request_timeout_second = (13, 27) # 请求超时秒数(默认connect timout推荐略大于3秒)
|
||||||
|
request_ssl_verify = False # 请求SSL验证(默认False)
|
||||||
|
request_allow_redirect = True # 请求允许重定向(默认True)
|
||||||
|
request_redirect_limit = 10 # 请求跳转限制(默认10次)
|
||||||
|
# 默认请求头 可以在headers里添加自定义请求头
|
||||||
|
request_default_headers = {
|
||||||
|
'Accept': 'text/html,application/xhtml+xml,'
|
||||||
|
'application/xml;q=0.9,*/*;q=0.8',
|
||||||
|
'Accept-Encoding': 'gzip, deflate',
|
||||||
|
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7',
|
||||||
|
'Cache-Control': 'max-age=0',
|
||||||
|
'DNT': '1',
|
||||||
|
'Referer': 'https://www.google.com/',
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
|
||||||
|
'(KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
|
||||||
|
'Upgrade-Insecure-Requests': '1',
|
||||||
|
'X-Forwarded-For': '127.0.0.1'
|
||||||
|
}
|
||||||
|
enable_random_ua = True # 使用随机UA(默认True,开启可以覆盖request_default_headers的UA)
|
||||||
|
|
||||||
|
|
||||||
|
# 搜索模块设置
|
||||||
|
enable_recursive_search = False # 递归搜索子域
|
||||||
|
search_recursive_times = 2 # 递归搜索层数
|
||||||
|
|
||||||
|
# 网络空间测绘引擎设置
|
||||||
|
cam_records_maximum_per_domain = 1000 # 对于单个主域名,在测绘引擎中的最多查询多少条记录,防止泛解析和CDN浪费积分,对 fofa, hunter, quake, zoomeye 生效,最低为100
|
||||||
|
|
||||||
|
# 是否从输入的数据中使用tldextract提取主域名。若设为 False,OneForAll会直接将输入的域名作为主域名,比如北京分公司的域名 beijing.10086.com 就不会被解析成母公司的域名 10086.com
|
||||||
|
use_tld_extract = True
|
||||||
@@ -0,0 +1,986 @@
|
|||||||
|
adm
|
||||||
|
admin
|
||||||
|
alpha
|
||||||
|
api
|
||||||
|
api-docs
|
||||||
|
app
|
||||||
|
auth
|
||||||
|
backend
|
||||||
|
beta
|
||||||
|
brand
|
||||||
|
bucket
|
||||||
|
cdn
|
||||||
|
cert
|
||||||
|
chef
|
||||||
|
ci
|
||||||
|
client
|
||||||
|
cms
|
||||||
|
confluence
|
||||||
|
container
|
||||||
|
control
|
||||||
|
cvs
|
||||||
|
data
|
||||||
|
demo
|
||||||
|
dev
|
||||||
|
developer
|
||||||
|
devops
|
||||||
|
devs
|
||||||
|
docker
|
||||||
|
elastic
|
||||||
|
email
|
||||||
|
engine
|
||||||
|
eureka
|
||||||
|
europe
|
||||||
|
events
|
||||||
|
ext
|
||||||
|
front
|
||||||
|
frontpage
|
||||||
|
fw
|
||||||
|
gateway
|
||||||
|
gh
|
||||||
|
git
|
||||||
|
gitlab
|
||||||
|
gw
|
||||||
|
h5
|
||||||
|
help
|
||||||
|
inner
|
||||||
|
int
|
||||||
|
internal
|
||||||
|
intra
|
||||||
|
it
|
||||||
|
lab
|
||||||
|
latin
|
||||||
|
lax
|
||||||
|
lb
|
||||||
|
legacy
|
||||||
|
login
|
||||||
|
mail
|
||||||
|
mirror
|
||||||
|
net
|
||||||
|
node
|
||||||
|
oid
|
||||||
|
ops
|
||||||
|
org
|
||||||
|
origin
|
||||||
|
page
|
||||||
|
partner
|
||||||
|
pass
|
||||||
|
pay
|
||||||
|
payment
|
||||||
|
php
|
||||||
|
pre
|
||||||
|
preview
|
||||||
|
priv
|
||||||
|
private
|
||||||
|
pro
|
||||||
|
prod
|
||||||
|
production
|
||||||
|
profile
|
||||||
|
proxy
|
||||||
|
qa
|
||||||
|
raw
|
||||||
|
region
|
||||||
|
reset
|
||||||
|
s3
|
||||||
|
sandbox
|
||||||
|
scm
|
||||||
|
search
|
||||||
|
secure
|
||||||
|
security
|
||||||
|
server
|
||||||
|
service
|
||||||
|
signed
|
||||||
|
sit
|
||||||
|
skins
|
||||||
|
ssl
|
||||||
|
st
|
||||||
|
staff
|
||||||
|
stage
|
||||||
|
staging
|
||||||
|
static
|
||||||
|
stg
|
||||||
|
support
|
||||||
|
svc
|
||||||
|
swagger
|
||||||
|
system
|
||||||
|
team
|
||||||
|
test
|
||||||
|
tpe
|
||||||
|
train
|
||||||
|
trial
|
||||||
|
uat
|
||||||
|
us
|
||||||
|
ut
|
||||||
|
v
|
||||||
|
v1
|
||||||
|
v2
|
||||||
|
vpn
|
||||||
|
wx
|
||||||
|
acc
|
||||||
|
account
|
||||||
|
accounting
|
||||||
|
active
|
||||||
|
administrator
|
||||||
|
administrators
|
||||||
|
admins
|
||||||
|
analytics
|
||||||
|
apac
|
||||||
|
apache
|
||||||
|
apidocs
|
||||||
|
apiserver
|
||||||
|
apps
|
||||||
|
application
|
||||||
|
applications
|
||||||
|
assets
|
||||||
|
asana
|
||||||
|
authenticate
|
||||||
|
authentication
|
||||||
|
authorization
|
||||||
|
aws
|
||||||
|
azure
|
||||||
|
billing
|
||||||
|
bitbucket
|
||||||
|
cgi
|
||||||
|
chd
|
||||||
|
cloud
|
||||||
|
cloudapp
|
||||||
|
cloudfront
|
||||||
|
controller
|
||||||
|
ctl
|
||||||
|
customer
|
||||||
|
dashboard
|
||||||
|
development
|
||||||
|
develop
|
||||||
|
disabled
|
||||||
|
docs
|
||||||
|
docsapi
|
||||||
|
document
|
||||||
|
documents
|
||||||
|
documentation
|
||||||
|
edge
|
||||||
|
elasticbeanstalk
|
||||||
|
ebs
|
||||||
|
emea
|
||||||
|
engineering
|
||||||
|
europewest
|
||||||
|
file
|
||||||
|
firewall
|
||||||
|
get
|
||||||
|
getter
|
||||||
|
gist
|
||||||
|
github
|
||||||
|
global
|
||||||
|
history
|
||||||
|
hw
|
||||||
|
hwcdn
|
||||||
|
iad
|
||||||
|
ids
|
||||||
|
ingress
|
||||||
|
internals
|
||||||
|
jenkins
|
||||||
|
k8s
|
||||||
|
k8s-dev
|
||||||
|
k8s-prd
|
||||||
|
k8s-prod
|
||||||
|
kube
|
||||||
|
kubectl
|
||||||
|
kubernetes
|
||||||
|
loadbalancer
|
||||||
|
machine
|
||||||
|
manage
|
||||||
|
management
|
||||||
|
mgmt
|
||||||
|
marketing
|
||||||
|
market
|
||||||
|
metrics
|
||||||
|
metric
|
||||||
|
merchant
|
||||||
|
mobile
|
||||||
|
mobileclient
|
||||||
|
nautilus
|
||||||
|
nginx
|
||||||
|
northamerica
|
||||||
|
old
|
||||||
|
panel
|
||||||
|
paywall
|
||||||
|
portal
|
||||||
|
prd
|
||||||
|
productions
|
||||||
|
profiles
|
||||||
|
promo
|
||||||
|
redir
|
||||||
|
redirect
|
||||||
|
redirector
|
||||||
|
repo
|
||||||
|
repository
|
||||||
|
restricted
|
||||||
|
rpc
|
||||||
|
stats
|
||||||
|
swag
|
||||||
|
testing
|
||||||
|
tester
|
||||||
|
traffic
|
||||||
|
tomcat
|
||||||
|
toolbar
|
||||||
|
training
|
||||||
|
upload
|
||||||
|
uploads
|
||||||
|
v3
|
||||||
|
w3
|
||||||
|
web
|
||||||
|
webapp
|
||||||
|
1
|
||||||
|
10
|
||||||
|
11
|
||||||
|
12
|
||||||
|
13
|
||||||
|
14
|
||||||
|
15
|
||||||
|
16
|
||||||
|
17
|
||||||
|
18
|
||||||
|
19
|
||||||
|
2
|
||||||
|
20
|
||||||
|
2009
|
||||||
|
2010
|
||||||
|
2011
|
||||||
|
2012
|
||||||
|
2013
|
||||||
|
2014
|
||||||
|
2015
|
||||||
|
2016
|
||||||
|
2017
|
||||||
|
2018
|
||||||
|
2019
|
||||||
|
2020
|
||||||
|
2021
|
||||||
|
2022
|
||||||
|
2023
|
||||||
|
2024
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7
|
||||||
|
8
|
||||||
|
9
|
||||||
|
a
|
||||||
|
accept
|
||||||
|
accounts
|
||||||
|
admin1
|
||||||
|
akali
|
||||||
|
akamai
|
||||||
|
alt
|
||||||
|
america
|
||||||
|
api1
|
||||||
|
apollo
|
||||||
|
april
|
||||||
|
b
|
||||||
|
boards
|
||||||
|
box
|
||||||
|
brasil
|
||||||
|
brazil
|
||||||
|
bucky
|
||||||
|
c
|
||||||
|
cf
|
||||||
|
cms1
|
||||||
|
cn
|
||||||
|
com
|
||||||
|
dec
|
||||||
|
dev1
|
||||||
|
drop
|
||||||
|
elb
|
||||||
|
eng
|
||||||
|
engima
|
||||||
|
eu
|
||||||
|
euw
|
||||||
|
euwe
|
||||||
|
evelynn
|
||||||
|
feb
|
||||||
|
fet
|
||||||
|
forms
|
||||||
|
forum
|
||||||
|
games
|
||||||
|
germany
|
||||||
|
ghcpi
|
||||||
|
hkg
|
||||||
|
i
|
||||||
|
jinx
|
||||||
|
july
|
||||||
|
june
|
||||||
|
kor
|
||||||
|
korea
|
||||||
|
kr
|
||||||
|
lan
|
||||||
|
las
|
||||||
|
latinamerica
|
||||||
|
lax1
|
||||||
|
march
|
||||||
|
merch
|
||||||
|
na
|
||||||
|
netherlands
|
||||||
|
nl
|
||||||
|
nov
|
||||||
|
oceania
|
||||||
|
oct
|
||||||
|
pantheon
|
||||||
|
pc
|
||||||
|
pl
|
||||||
|
poland
|
||||||
|
preferences
|
||||||
|
promotion
|
||||||
|
restrict
|
||||||
|
reviews
|
||||||
|
s
|
||||||
|
sept
|
||||||
|
singed
|
||||||
|
spring
|
||||||
|
stage1
|
||||||
|
t
|
||||||
|
test1
|
||||||
|
testbed
|
||||||
|
testing1
|
||||||
|
tr
|
||||||
|
tur
|
||||||
|
turk
|
||||||
|
turkey
|
||||||
|
twitch
|
||||||
|
vi
|
||||||
|
web1
|
||||||
|
westeurope
|
||||||
|
z
|
||||||
|
previous
|
||||||
|
new
|
||||||
|
new1
|
||||||
|
east
|
||||||
|
south
|
||||||
|
west
|
||||||
|
north
|
||||||
|
southeast
|
||||||
|
northwest
|
||||||
|
es
|
||||||
|
nt
|
||||||
|
tx
|
||||||
|
tencent
|
||||||
|
ali
|
||||||
|
aliyun
|
||||||
|
center
|
||||||
|
stable
|
||||||
|
release
|
||||||
|
ga
|
||||||
|
rc
|
||||||
|
01
|
||||||
|
02
|
||||||
|
03
|
||||||
|
dc
|
||||||
|
mq
|
||||||
|
oa
|
||||||
|
uc
|
||||||
|
zk
|
||||||
|
apm
|
||||||
|
bbs
|
||||||
|
biw
|
||||||
|
bot
|
||||||
|
bus
|
||||||
|
cat
|
||||||
|
crm
|
||||||
|
dc1
|
||||||
|
doc
|
||||||
|
ec2
|
||||||
|
efk
|
||||||
|
elk
|
||||||
|
gce
|
||||||
|
hub
|
||||||
|
job
|
||||||
|
jpa
|
||||||
|
jwt
|
||||||
|
lib
|
||||||
|
log
|
||||||
|
mbs
|
||||||
|
mgt
|
||||||
|
mvc
|
||||||
|
nms
|
||||||
|
pan
|
||||||
|
pod
|
||||||
|
srv
|
||||||
|
sso
|
||||||
|
svn
|
||||||
|
tms
|
||||||
|
wms
|
||||||
|
www
|
||||||
|
amqp
|
||||||
|
apis
|
||||||
|
app1
|
||||||
|
avro
|
||||||
|
blog
|
||||||
|
boot
|
||||||
|
cicd
|
||||||
|
flex
|
||||||
|
gocd
|
||||||
|
gogs
|
||||||
|
good
|
||||||
|
grid
|
||||||
|
guns
|
||||||
|
hdfs
|
||||||
|
hive
|
||||||
|
ldap
|
||||||
|
live
|
||||||
|
logs
|
||||||
|
mqtt
|
||||||
|
nifi
|
||||||
|
note
|
||||||
|
open
|
||||||
|
pipe
|
||||||
|
pods
|
||||||
|
shop
|
||||||
|
show
|
||||||
|
solr
|
||||||
|
tsdb
|
||||||
|
user
|
||||||
|
wiki
|
||||||
|
work
|
||||||
|
zuul
|
||||||
|
apiv1
|
||||||
|
apiv2
|
||||||
|
apiv3
|
||||||
|
apiv4
|
||||||
|
apiv5
|
||||||
|
app01
|
||||||
|
batch
|
||||||
|
beats
|
||||||
|
cacti
|
||||||
|
camel
|
||||||
|
chaos
|
||||||
|
drone
|
||||||
|
dubbo
|
||||||
|
event
|
||||||
|
feign
|
||||||
|
flink
|
||||||
|
flume
|
||||||
|
geode
|
||||||
|
gitea
|
||||||
|
goods
|
||||||
|
graph
|
||||||
|
group
|
||||||
|
habor
|
||||||
|
hbase
|
||||||
|
html5
|
||||||
|
infra
|
||||||
|
kafka
|
||||||
|
kylin
|
||||||
|
label
|
||||||
|
maven
|
||||||
|
mesos
|
||||||
|
micro
|
||||||
|
minio
|
||||||
|
nacos
|
||||||
|
neo4j
|
||||||
|
nerve
|
||||||
|
nexus
|
||||||
|
node1
|
||||||
|
oauth
|
||||||
|
oozie
|
||||||
|
redis
|
||||||
|
route
|
||||||
|
scala
|
||||||
|
spark
|
||||||
|
sqoop
|
||||||
|
stack
|
||||||
|
store
|
||||||
|
storm
|
||||||
|
webui
|
||||||
|
api-v1
|
||||||
|
api-v2
|
||||||
|
api-v3
|
||||||
|
api-v4
|
||||||
|
api-v5
|
||||||
|
consul
|
||||||
|
falcon
|
||||||
|
galaxy
|
||||||
|
goblin
|
||||||
|
gradle
|
||||||
|
group1
|
||||||
|
hadoop
|
||||||
|
harbor
|
||||||
|
influx
|
||||||
|
kibana
|
||||||
|
lcinga
|
||||||
|
logapi
|
||||||
|
logger
|
||||||
|
nagios
|
||||||
|
node01
|
||||||
|
oauth2
|
||||||
|
office
|
||||||
|
pgraph
|
||||||
|
ribbon
|
||||||
|
router
|
||||||
|
scribe
|
||||||
|
sleuth
|
||||||
|
spark1
|
||||||
|
splunk
|
||||||
|
stream
|
||||||
|
syslog
|
||||||
|
sysmon
|
||||||
|
tracer
|
||||||
|
travis
|
||||||
|
triton
|
||||||
|
tuning
|
||||||
|
web-ui
|
||||||
|
weblog
|
||||||
|
zabbix
|
||||||
|
zipkin
|
||||||
|
airflow
|
||||||
|
akumuli
|
||||||
|
ansible
|
||||||
|
bigdata
|
||||||
|
breaker
|
||||||
|
brogmon
|
||||||
|
catalog
|
||||||
|
circuit
|
||||||
|
content
|
||||||
|
datadog
|
||||||
|
diagram
|
||||||
|
eureka1
|
||||||
|
fluentd
|
||||||
|
gemfire
|
||||||
|
grafana
|
||||||
|
hystrix
|
||||||
|
invoker
|
||||||
|
jupyter
|
||||||
|
kinesis
|
||||||
|
library
|
||||||
|
logging
|
||||||
|
manager
|
||||||
|
meeting
|
||||||
|
monitor
|
||||||
|
netdata
|
||||||
|
netflix
|
||||||
|
nodered
|
||||||
|
pushapi
|
||||||
|
recruit
|
||||||
|
restapi
|
||||||
|
restful
|
||||||
|
rsyslog
|
||||||
|
storage
|
||||||
|
tracing
|
||||||
|
turbine
|
||||||
|
webflow
|
||||||
|
activemq
|
||||||
|
actuator
|
||||||
|
cadvisor
|
||||||
|
collectd
|
||||||
|
contract
|
||||||
|
dataflow
|
||||||
|
elkstack
|
||||||
|
exporter
|
||||||
|
filebeat
|
||||||
|
graphite
|
||||||
|
heapster
|
||||||
|
influxdb
|
||||||
|
librenms
|
||||||
|
logstash
|
||||||
|
marathon
|
||||||
|
node-red
|
||||||
|
opentsdb
|
||||||
|
pipeline
|
||||||
|
platform
|
||||||
|
push-api
|
||||||
|
rabbitmq
|
||||||
|
schedule
|
||||||
|
searcher
|
||||||
|
sentinel
|
||||||
|
tracking
|
||||||
|
atlassian
|
||||||
|
cassandra
|
||||||
|
community
|
||||||
|
discovery
|
||||||
|
dockerhub
|
||||||
|
dzzoffice
|
||||||
|
elk-stack
|
||||||
|
hostgroup
|
||||||
|
kubernete
|
||||||
|
logsearch
|
||||||
|
mapreduce
|
||||||
|
office365
|
||||||
|
openstack
|
||||||
|
pagerduty
|
||||||
|
serverset
|
||||||
|
terraform
|
||||||
|
websocket
|
||||||
|
zookeeper
|
||||||
|
clickhouse
|
||||||
|
cloudwatch
|
||||||
|
dashboards
|
||||||
|
datacenter
|
||||||
|
dispatcher
|
||||||
|
hostgroup1
|
||||||
|
kube-state
|
||||||
|
openfalcon
|
||||||
|
prometheus
|
||||||
|
servicelog
|
||||||
|
skywalking
|
||||||
|
usercenter
|
||||||
|
api-gateway
|
||||||
|
data-center
|
||||||
|
eureka-zuul
|
||||||
|
kube-status
|
||||||
|
loganalysis
|
||||||
|
open-falcon
|
||||||
|
opentracing
|
||||||
|
pushgateway
|
||||||
|
recruitment
|
||||||
|
restful-api
|
||||||
|
service-log
|
||||||
|
serviceslog
|
||||||
|
system-cube
|
||||||
|
user-center
|
||||||
|
alertmanager
|
||||||
|
apis-gateway
|
||||||
|
eureka-admin
|
||||||
|
grafana-kong
|
||||||
|
nacos-config
|
||||||
|
push-gateway
|
||||||
|
rest-gateway
|
||||||
|
services-log
|
||||||
|
zuul-gateway
|
||||||
|
consul-config
|
||||||
|
elasticsearch
|
||||||
|
eureka-client
|
||||||
|
eureka-server
|
||||||
|
node-exporter
|
||||||
|
kong-dashboard
|
||||||
|
kylin-dashboard
|
||||||
|
restful-gateway
|
||||||
|
grafana-dashboard
|
||||||
|
kylin-system-cube
|
||||||
|
grafana-management
|
||||||
|
apollo
|
||||||
|
apolloconfig
|
||||||
|
apolloadmin
|
||||||
|
apolloservice
|
||||||
|
ws
|
||||||
|
lucene
|
||||||
|
beam
|
||||||
|
struts
|
||||||
|
arrow
|
||||||
|
hudi
|
||||||
|
cloudstack
|
||||||
|
pulsar
|
||||||
|
commons
|
||||||
|
subversion
|
||||||
|
asterixdb
|
||||||
|
superset
|
||||||
|
mxnet
|
||||||
|
httpd
|
||||||
|
ignite
|
||||||
|
sling
|
||||||
|
shardingsphere
|
||||||
|
axis
|
||||||
|
spamassassin
|
||||||
|
apisix
|
||||||
|
openoffice
|
||||||
|
db
|
||||||
|
cordova
|
||||||
|
qpid
|
||||||
|
ofbiz
|
||||||
|
tapestry
|
||||||
|
impala
|
||||||
|
ambari
|
||||||
|
cocoon
|
||||||
|
carbondata
|
||||||
|
geronimo
|
||||||
|
dolphinscheduler
|
||||||
|
doris
|
||||||
|
tvm
|
||||||
|
wicket
|
||||||
|
ant
|
||||||
|
iceberg
|
||||||
|
jackrabbit
|
||||||
|
myfaces
|
||||||
|
pdfbox
|
||||||
|
james
|
||||||
|
nuttx
|
||||||
|
drill
|
||||||
|
incubator
|
||||||
|
tomee
|
||||||
|
ozone
|
||||||
|
trafficserver
|
||||||
|
accumulo
|
||||||
|
kudu
|
||||||
|
druid
|
||||||
|
pinot
|
||||||
|
phoenix
|
||||||
|
harmony
|
||||||
|
directory
|
||||||
|
perl
|
||||||
|
thrift
|
||||||
|
pig
|
||||||
|
felix
|
||||||
|
cxf
|
||||||
|
echarts
|
||||||
|
karaf
|
||||||
|
servicemix
|
||||||
|
xmlgraphics
|
||||||
|
openwhisk
|
||||||
|
iotdb
|
||||||
|
tuscany
|
||||||
|
couchdb
|
||||||
|
jclouds
|
||||||
|
rocketmq
|
||||||
|
trafficcontrol
|
||||||
|
tinkerpop
|
||||||
|
hc
|
||||||
|
calcite
|
||||||
|
jmeter
|
||||||
|
netbeans
|
||||||
|
uima
|
||||||
|
poi
|
||||||
|
zeppelin
|
||||||
|
tez
|
||||||
|
isis
|
||||||
|
atlas
|
||||||
|
mahout
|
||||||
|
mynewt
|
||||||
|
tika
|
||||||
|
mina
|
||||||
|
nutch
|
||||||
|
deltaspike
|
||||||
|
xalan
|
||||||
|
portals
|
||||||
|
servicecomb
|
||||||
|
ranger
|
||||||
|
groovy
|
||||||
|
jena
|
||||||
|
brooklyn
|
||||||
|
whimsical
|
||||||
|
knox
|
||||||
|
forrest
|
||||||
|
avalon
|
||||||
|
openmeetings
|
||||||
|
xerces
|
||||||
|
bookkeeper
|
||||||
|
bigtop
|
||||||
|
apr
|
||||||
|
metron
|
||||||
|
samza
|
||||||
|
openjpa
|
||||||
|
tajo
|
||||||
|
lenya
|
||||||
|
stratos
|
||||||
|
cayenne
|
||||||
|
airavata
|
||||||
|
weex
|
||||||
|
shindig
|
||||||
|
shenyu
|
||||||
|
velocity
|
||||||
|
aries
|
||||||
|
lens
|
||||||
|
fineract
|
||||||
|
gobblin
|
||||||
|
parquet
|
||||||
|
apex
|
||||||
|
seatunnel
|
||||||
|
syncope
|
||||||
|
jakarta
|
||||||
|
royale
|
||||||
|
usergrid
|
||||||
|
manifoldcf
|
||||||
|
inlong
|
||||||
|
libcloud
|
||||||
|
hawq
|
||||||
|
gump
|
||||||
|
allura
|
||||||
|
aurora
|
||||||
|
ibatis
|
||||||
|
ratis
|
||||||
|
helix
|
||||||
|
synapse
|
||||||
|
kyuubi
|
||||||
|
sentry
|
||||||
|
archiva
|
||||||
|
daffodil
|
||||||
|
reef
|
||||||
|
trafodion
|
||||||
|
sis
|
||||||
|
xml
|
||||||
|
devlake
|
||||||
|
hama
|
||||||
|
openwebbeans
|
||||||
|
guacamole
|
||||||
|
pivot
|
||||||
|
lucenenet
|
||||||
|
curator
|
||||||
|
gora
|
||||||
|
olingo
|
||||||
|
ode
|
||||||
|
jspwiki
|
||||||
|
river
|
||||||
|
stanbol
|
||||||
|
giraph
|
||||||
|
streams
|
||||||
|
slider
|
||||||
|
dlab
|
||||||
|
plc4x
|
||||||
|
shiro
|
||||||
|
juddi
|
||||||
|
eventmesh
|
||||||
|
roller
|
||||||
|
taverna
|
||||||
|
oodt
|
||||||
|
climate
|
||||||
|
bloodhound
|
||||||
|
chemistry
|
||||||
|
unomi
|
||||||
|
yunikorn
|
||||||
|
brpc
|
||||||
|
orc
|
||||||
|
rave
|
||||||
|
beehive
|
||||||
|
eagle
|
||||||
|
yetus
|
||||||
|
deltacloud
|
||||||
|
clerezza
|
||||||
|
datasketches
|
||||||
|
systemml
|
||||||
|
continuum
|
||||||
|
tamaya
|
||||||
|
opennlp
|
||||||
|
buildstream
|
||||||
|
stdcxx
|
||||||
|
rya
|
||||||
|
santuario
|
||||||
|
buildr
|
||||||
|
submarine
|
||||||
|
linkis
|
||||||
|
freemarker
|
||||||
|
juneau
|
||||||
|
any23
|
||||||
|
ariatosca
|
||||||
|
lucy
|
||||||
|
marmotta
|
||||||
|
heron
|
||||||
|
datalab
|
||||||
|
singa
|
||||||
|
whirr
|
||||||
|
celix
|
||||||
|
vcl
|
||||||
|
streampipes
|
||||||
|
chukwa
|
||||||
|
ctakes
|
||||||
|
crunch
|
||||||
|
madlib
|
||||||
|
metamodel
|
||||||
|
xmlbeans
|
||||||
|
quickstep
|
||||||
|
edgent
|
||||||
|
wookie
|
||||||
|
griffin
|
||||||
|
hop
|
||||||
|
uniffle
|
||||||
|
esme
|
||||||
|
fluo
|
||||||
|
nlpcraft
|
||||||
|
abdera
|
||||||
|
creadur
|
||||||
|
predictionio
|
||||||
|
wink
|
||||||
|
ace
|
||||||
|
pagespeed
|
||||||
|
kvrocks
|
||||||
|
mnemonic
|
||||||
|
tcl
|
||||||
|
twill
|
||||||
|
gearpump
|
||||||
|
tubemq
|
||||||
|
oltu
|
||||||
|
quetz
|
||||||
|
livy
|
||||||
|
sdap
|
||||||
|
johnzon
|
||||||
|
systemds
|
||||||
|
polygene
|
||||||
|
labs
|
||||||
|
pegasus
|
||||||
|
streampark
|
||||||
|
attic
|
||||||
|
age
|
||||||
|
pekko
|
||||||
|
teaclave
|
||||||
|
htrace
|
||||||
|
ponymail
|
||||||
|
tiles
|
||||||
|
hivemall
|
||||||
|
bahir
|
||||||
|
click
|
||||||
|
empire-db
|
||||||
|
bval
|
||||||
|
hivemind
|
||||||
|
myriad
|
||||||
|
hugegraph
|
||||||
|
devicemap
|
||||||
|
excalibur
|
||||||
|
tuweni
|
||||||
|
sedona
|
||||||
|
vxquery
|
||||||
|
datafu
|
||||||
|
joshua
|
||||||
|
commonsrdf
|
||||||
|
shale
|
||||||
|
directmemory
|
||||||
|
mrunit
|
||||||
|
tephra
|
||||||
|
nemo
|
||||||
|
senssoft
|
||||||
|
flagon
|
||||||
|
pirk
|
||||||
|
toree
|
||||||
|
omid
|
||||||
|
distributedlog
|
||||||
|
celeborn
|
||||||
|
s2graph
|
||||||
|
onami
|
||||||
|
serf
|
||||||
|
etch
|
||||||
|
samoa
|
||||||
|
milagro
|
||||||
|
corinthia
|
||||||
|
wayang
|
||||||
|
diversity
|
||||||
|
batchee
|
||||||
|
ripple
|
||||||
|
opendal
|
||||||
|
gossip
|
||||||
|
annotator
|
||||||
|
causeway
|
||||||
|
sirona
|
||||||
|
spot
|
||||||
|
baremaps
|
||||||
|
mrql
|
||||||
|
depot
|
||||||
|
crail
|
||||||
|
steve
|
||||||
|
petri
|
||||||
|
amaterasu
|
||||||
|
liminal
|
||||||
|
iota
|
||||||
|
paimon
|
||||||
|
kibble
|
||||||
|
horn
|
||||||
|
hdt
|
||||||
|
drat
|
||||||
|
openaz
|
||||||
|
marvin
|
||||||
|
provisionr
|
||||||
|
mesatee
|
||||||
|
cmda
|
||||||
|
bluemarlin
|
||||||
|
concerted
|
||||||
|
warble
|
||||||
|
tac
|
||||||
|
composer
|
||||||
|
cotton
|
||||||
|
kie
|
||||||
|
android
|
||||||
|
infratest
|
||||||
|
zabbix
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
47.254.51.88
|
||||||
|
163.177.156.225
|
||||||
|
161.117.97.232
|
||||||
|
218.98.58.194
|
||||||
|
117.91.188.195
|
||||||
|
14.17.109.84
|
||||||
|
58.52.135.164
|
||||||
|
172.96.125.3
|
||||||
|
106.38.197.52
|
||||||
|
163.177.156.225
|
||||||
|
117.91.188.196
|
||||||
|
218.98.58.194
|
||||||
|
58.52.135.165
|
||||||
|
172.96.125.3
|
||||||
|
106.38.197.48
|
||||||
|
47.254.51.88
|
||||||
|
161.117.97.232
|
||||||
|
14.17.109.85
|
||||||
|
172.96.125.3
|
||||||
|
14.17.109.83
|
||||||
|
163.177.156.225
|
||||||
|
161.117.97.232
|
||||||
|
218.98.58.194
|
||||||
|
47.254.51.88
|
||||||
|
58.52.135.163
|
||||||
|
117.91.188.194
|
||||||
|
106.38.197.52
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
[
|
||||||
|
"AS10576",
|
||||||
|
"AS10762",
|
||||||
|
"AS11748",
|
||||||
|
"AS131099",
|
||||||
|
"AS132601",
|
||||||
|
"AS133496",
|
||||||
|
"AS134409",
|
||||||
|
"AS135295",
|
||||||
|
"AS136764",
|
||||||
|
"AS137187",
|
||||||
|
"AS13777",
|
||||||
|
"AS13890",
|
||||||
|
"AS14103",
|
||||||
|
"AS14520",
|
||||||
|
"AS17132",
|
||||||
|
"AS199251",
|
||||||
|
"AS200013",
|
||||||
|
"AS200325",
|
||||||
|
"AS200856",
|
||||||
|
"AS201263",
|
||||||
|
"AS202294",
|
||||||
|
"AS203075",
|
||||||
|
"AS203139",
|
||||||
|
"AS204248",
|
||||||
|
"AS204286",
|
||||||
|
"AS204545",
|
||||||
|
"AS206227",
|
||||||
|
"AS206734",
|
||||||
|
"AS206848",
|
||||||
|
"AS206986",
|
||||||
|
"AS207158",
|
||||||
|
"AS208559",
|
||||||
|
"AS209403",
|
||||||
|
"AS21030",
|
||||||
|
"AS21257",
|
||||||
|
"AS23327",
|
||||||
|
"AS23393",
|
||||||
|
"AS23637",
|
||||||
|
"AS23794",
|
||||||
|
"AS24997",
|
||||||
|
"AS26492",
|
||||||
|
"AS268843",
|
||||||
|
"AS28709",
|
||||||
|
"AS29264",
|
||||||
|
"AS30282",
|
||||||
|
"AS30637",
|
||||||
|
"AS328126",
|
||||||
|
"AS36408",
|
||||||
|
"AS38107",
|
||||||
|
"AS397192",
|
||||||
|
"AS40366",
|
||||||
|
"AS43303",
|
||||||
|
"AS44907",
|
||||||
|
"AS46071",
|
||||||
|
"AS46177",
|
||||||
|
"AS47542",
|
||||||
|
"AS49287",
|
||||||
|
"AS49689",
|
||||||
|
"AS51286",
|
||||||
|
"AS55082",
|
||||||
|
"AS55254",
|
||||||
|
"AS56636",
|
||||||
|
"AS57363",
|
||||||
|
"AS58127",
|
||||||
|
"AS59730",
|
||||||
|
"AS59776",
|
||||||
|
"AS60068",
|
||||||
|
"AS60626",
|
||||||
|
"AS60922",
|
||||||
|
"AS61107",
|
||||||
|
"AS61159",
|
||||||
|
"AS62026",
|
||||||
|
"AS62229",
|
||||||
|
"AS63062",
|
||||||
|
"AS64232",
|
||||||
|
"AS8868",
|
||||||
|
"AS9053",
|
||||||
|
"AS55770",
|
||||||
|
"AS49846",
|
||||||
|
"AS49249",
|
||||||
|
"AS48163",
|
||||||
|
"AS45700",
|
||||||
|
"AS43639",
|
||||||
|
"AS39836",
|
||||||
|
"AS393560",
|
||||||
|
"AS393234",
|
||||||
|
"AS36183",
|
||||||
|
"AS35994",
|
||||||
|
"AS35993",
|
||||||
|
"AS35204",
|
||||||
|
"AS34850",
|
||||||
|
"AS34164",
|
||||||
|
"AS33905",
|
||||||
|
"AS32787",
|
||||||
|
"AS31377",
|
||||||
|
"AS31110",
|
||||||
|
"AS31109",
|
||||||
|
"AS31108",
|
||||||
|
"AS31107",
|
||||||
|
"AS30675",
|
||||||
|
"AS24319",
|
||||||
|
"AS23903",
|
||||||
|
"AS23455",
|
||||||
|
"AS23454",
|
||||||
|
"AS22207",
|
||||||
|
"AS21399",
|
||||||
|
"AS21357",
|
||||||
|
"AS21342",
|
||||||
|
"AS20940",
|
||||||
|
"AS20189",
|
||||||
|
"AS18717",
|
||||||
|
"AS18680",
|
||||||
|
"AS17334",
|
||||||
|
"AS16702",
|
||||||
|
"AS16625",
|
||||||
|
"AS12222",
|
||||||
|
"AS209101",
|
||||||
|
"AS201585",
|
||||||
|
"AS135429",
|
||||||
|
"AS395747",
|
||||||
|
"AS394536",
|
||||||
|
"AS209242",
|
||||||
|
"AS203898",
|
||||||
|
"AS202623",
|
||||||
|
"AS14789",
|
||||||
|
"AS133877",
|
||||||
|
"AS13335",
|
||||||
|
"AS132892",
|
||||||
|
"AS21859",
|
||||||
|
"AS6185",
|
||||||
|
"AS47823",
|
||||||
|
"AS4134"
|
||||||
|
]
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
{
|
||||||
|
"cdn": "cdn",
|
||||||
|
"cache": "cache",
|
||||||
|
"tbcache.com": "Alibaba Cloud",
|
||||||
|
"alicdn.com": "Alibaba Cloud",
|
||||||
|
"tcdn.qq.com": "tcdn.qq.com",
|
||||||
|
"00cdn.com": "XYcdn",
|
||||||
|
"21cvcdn.com": "21Vianet",
|
||||||
|
"21okglb.cn": "21Vianet",
|
||||||
|
"21speedcdn.com": "21Vianet",
|
||||||
|
"21vianet.com.cn": "21Vianet",
|
||||||
|
"21vokglb.cn": "21Vianet",
|
||||||
|
"360wzb.com": "360",
|
||||||
|
"51cdn.com": "ChinaCache",
|
||||||
|
"acadn.com": "Dnion",
|
||||||
|
"aicdn.com": "UPYUN",
|
||||||
|
"akadns.net": "Akamai",
|
||||||
|
"akamai-staging.net": "Akamai",
|
||||||
|
"akamai.com": "Akamai",
|
||||||
|
"akamai.net": "Akamai",
|
||||||
|
"akamaitech.net": "Akamai",
|
||||||
|
"akamaized.net": "Akamai",
|
||||||
|
"alicloudlayer.com": "ALiyun",
|
||||||
|
"alikunlun.com": "ALiyun",
|
||||||
|
"aliyun-inc.com": "ALiyun",
|
||||||
|
"alicloudsec.com": "ALiyun",
|
||||||
|
"aliyuncs.com": "ALiyun",
|
||||||
|
"amazonaws.com": "Amazon Cloudfront",
|
||||||
|
"anankecdn.com.br": "Ananke",
|
||||||
|
"aodianyun.com": "VOD",
|
||||||
|
"aqb.so": "AnQuanBao",
|
||||||
|
"awsdns": "KeyCDN",
|
||||||
|
"azioncdn.net": "Azion",
|
||||||
|
"azureedge.net": "Azure CDN",
|
||||||
|
"bdydns.com": "Baiduyun",
|
||||||
|
"bitgravity.com": "Tata Communications",
|
||||||
|
"cachecn.com": "CnKuai",
|
||||||
|
"cachefly.net": "Cachefly",
|
||||||
|
"ccgslb.com": "ChinaCache",
|
||||||
|
"ccgslb.net": "ChinaCache",
|
||||||
|
"ccgslb.com.cn": "ChinaCache",
|
||||||
|
"cdn-cdn.net": "",
|
||||||
|
"cdn.cloudflare.net": "CloudFlare",
|
||||||
|
"cdn.dnsv1.com": "Tengxunyun",
|
||||||
|
"cdn.ngenix.net": "",
|
||||||
|
"cdn20.com": "ChinaCache",
|
||||||
|
"cdn77.net": "CDN77",
|
||||||
|
"cdn77.org": "CDN77",
|
||||||
|
"cdnetworks.net": "CDNetworks",
|
||||||
|
"cdnify.io": "CDNify",
|
||||||
|
"cdnnetworks.com": "CDNetworks",
|
||||||
|
"cdnsun.net": "CDNsun",
|
||||||
|
"cdntip.com": "QCloud",
|
||||||
|
"cdnudns.com": "PowerLeader",
|
||||||
|
"cdnvideo.ru": "CDNvideo",
|
||||||
|
"cdnzz.net": "SuZhi",
|
||||||
|
"chinacache.net": "ChinaCache",
|
||||||
|
"chinaidns.net": "LineFuture",
|
||||||
|
"chinanetcenter.com": "ChinaCache",
|
||||||
|
"cloudcdn.net": "CnKuai",
|
||||||
|
"cloudfront.net": "Amazon Cloudfront",
|
||||||
|
"customcdn.cn": "ChinaCache",
|
||||||
|
"customcdn.com": "ChinaCache",
|
||||||
|
"dnion.com": "Dnion",
|
||||||
|
"dnspao.com": "",
|
||||||
|
"edgecastcdn.net": "EdgeCast",
|
||||||
|
"edgesuite.net": "Akamai",
|
||||||
|
"ewcache.com": "Dnion",
|
||||||
|
"fastcache.com": "FastCache",
|
||||||
|
"fastcdn.cn": "Dnion",
|
||||||
|
"fastly.net": "Fastly",
|
||||||
|
"fastweb.com": "CnKuai",
|
||||||
|
"fastwebcdn.com": "CnKuai",
|
||||||
|
"footprint.net": "Level3",
|
||||||
|
"fpbns.net": "Level3",
|
||||||
|
"fwcdn.com": "CnKuai",
|
||||||
|
"fwdns.net": "CnKuai",
|
||||||
|
"globalcdn.cn": "Dnion",
|
||||||
|
"hacdn.net": "CnKuai",
|
||||||
|
"hadns.net": "CnKuai",
|
||||||
|
"hichina.com": "WWW",
|
||||||
|
"hichina.net": "WWW",
|
||||||
|
"hwcdn.net": "Highwinds",
|
||||||
|
"incapdns.net": "Incapsula",
|
||||||
|
"internapcdn.net": "Internap",
|
||||||
|
"jiashule.com": "Jiasule",
|
||||||
|
"kunlun.com": "ALiyun",
|
||||||
|
"kunlunar.com": "ALiyun",
|
||||||
|
"kunlunca.com": "ALiyun",
|
||||||
|
"kxcdn.com": "KeyCDN",
|
||||||
|
"lswcdn.net": "Leaseweb",
|
||||||
|
"lxcdn.com": "ChinaCache",
|
||||||
|
"mwcloudcdn.com": "QUANTIL",
|
||||||
|
"netdna-cdn.com": "MaxCDN",
|
||||||
|
"okcdn.com": "21Vianet",
|
||||||
|
"okglb.com": "21Vianet",
|
||||||
|
"ourwebcdn.net": "ChinaCache",
|
||||||
|
"ourwebpic.com": "ChinaCache",
|
||||||
|
"presscdn.com": "Presscdn",
|
||||||
|
"qingcdn.com": "",
|
||||||
|
"qiniudns.com": "QiNiu",
|
||||||
|
"skyparkcdn.net": "",
|
||||||
|
"speedcdns.com": "QUANTIL",
|
||||||
|
"sprycdn.com": "PowerLeader",
|
||||||
|
"tlgslb.com": "Dnion",
|
||||||
|
"txcdn.cn": "CDNetworks",
|
||||||
|
"txnetworks.cn": "CDNetworks",
|
||||||
|
"ucloud.cn": "UCloud",
|
||||||
|
"unicache.com": "LineFuture",
|
||||||
|
"verygslb.com": "VeryCloud",
|
||||||
|
"vo.llnwd.net": "Limelight",
|
||||||
|
"wscdns.com": "ChinaNetCenter",
|
||||||
|
"wscloudcdn.com": "ChinaNetCenter",
|
||||||
|
"xgslb.net": "Webluker",
|
||||||
|
"ytcdn.net": "Akamai",
|
||||||
|
"yunjiasu-cdn": "Baiduyun",
|
||||||
|
"cloudfront": "CloudFront",
|
||||||
|
"kunlun.com": "Alibaba Cloud",
|
||||||
|
"ccgslb": "ChinaCache",
|
||||||
|
"edgekey": "Akamai",
|
||||||
|
"fastly": "Fastly",
|
||||||
|
"chinacache": "ChinaCache",
|
||||||
|
"akamai": "Akamai",
|
||||||
|
"edgecast": "EdgeCast",
|
||||||
|
"azioncdn": "Azion",
|
||||||
|
"cachefly": "CacheFly",
|
||||||
|
"cdn77": "CDN77",
|
||||||
|
"cdnetworks": "CDNetworks",
|
||||||
|
"cdnify": "CDNify",
|
||||||
|
"wscloudcdn": "ChinaNetCenter",
|
||||||
|
"speedcdns": "ChinaNetCenter/Quantil",
|
||||||
|
"mwcloudcdn": "ChinaNetCenter/Quantil",
|
||||||
|
"cloudflare": "CloudFlare",
|
||||||
|
"hwcdn": "HighWinds",
|
||||||
|
"kxcdn": "KeyCDN",
|
||||||
|
"fpbns": "Level3",
|
||||||
|
"footprint": "Level3",
|
||||||
|
"llnwd": "LimeLight",
|
||||||
|
"netdna": "MaxCDN",
|
||||||
|
"bitgravity": "Tata CDN",
|
||||||
|
"azureedge": "Azure CDN",
|
||||||
|
"anankecdn": "Anake CDN",
|
||||||
|
"presscdn": "Press CDN",
|
||||||
|
"telefonica": "Telefonica CDN",
|
||||||
|
"dnsv1": "Tecent CDN",
|
||||||
|
"cdntip": "Tecent CDN",
|
||||||
|
"skyparkcdn": "Sky Park CDN",
|
||||||
|
"ngenix": "Ngenix",
|
||||||
|
"lswcdn": "LeaseWeb",
|
||||||
|
"internapcdn": "Internap",
|
||||||
|
"incapdns": "Incapsula",
|
||||||
|
"cdnsun": "CDN SUN",
|
||||||
|
"cdnvideo": "CDN Video",
|
||||||
|
"clients.turbobytes.net": "TurboBytes",
|
||||||
|
"turbobytes-cdn.com": "TurboBytes",
|
||||||
|
"afxcdn.net": "afxcdn.net",
|
||||||
|
"akamaiedge.net": "Akamai",
|
||||||
|
"akamaitechnologies.com": "Akamai",
|
||||||
|
"gslb.tbcache.com": "Alimama",
|
||||||
|
"att-dsa.net": "AT&T",
|
||||||
|
"belugacdn.com": "BelugaCDN",
|
||||||
|
"bluehatnetwork.com": "Blue Hat Network",
|
||||||
|
"systemcdn.net": "EdgeCast",
|
||||||
|
"panthercdn.com": "CDNetworks",
|
||||||
|
"cdngc.net": "CDNetworks",
|
||||||
|
"gccdn.net": "CDNetworks",
|
||||||
|
"gccdn.cn": "CDNetworks",
|
||||||
|
"c3cache.net": "ChinaCache",
|
||||||
|
"cncssr.chinacache.net": "ChinaCache",
|
||||||
|
"c3cdn.net": "ChinaCache",
|
||||||
|
"lxdns.com": "ChinaNetCenter",
|
||||||
|
"speedcdns.com": "QUANTIL/ChinaNetCenter",
|
||||||
|
"mwcloudcdn.com": "QUANTIL/ChinaNetCenter",
|
||||||
|
"cloudflare.com": "Cloudflare",
|
||||||
|
"cloudflare.net": "Cloudflare",
|
||||||
|
"adn.": "EdgeCast",
|
||||||
|
"wac.": "EdgeCast",
|
||||||
|
"wpc.": "EdgeCast",
|
||||||
|
"fastlylb.net": "Fastly",
|
||||||
|
"google.": "Google",
|
||||||
|
"googlesyndication.": "Google",
|
||||||
|
"youtube.": "Google",
|
||||||
|
"googleusercontent.com": "Google",
|
||||||
|
"l.doubleclick.net": "Google",
|
||||||
|
"hiberniacdn.com": "Hibernia",
|
||||||
|
"inscname.net": "Instartlogic",
|
||||||
|
"insnw.net": "Instartlogic",
|
||||||
|
"lswcdn.net": "LeaseWeb CDN",
|
||||||
|
"llnwd.net": "Limelight",
|
||||||
|
"lldns.net": "Limelight",
|
||||||
|
"netdna-ssl.com": "MaxCDN",
|
||||||
|
"netdna.com": "MaxCDN",
|
||||||
|
"stackpathdns.com": "StackPath",
|
||||||
|
"mncdn.com": "Medianova",
|
||||||
|
"instacontent.net": "Mirror Image",
|
||||||
|
"mirror-image.net": "Mirror Image",
|
||||||
|
"cap-mii.net": "Mirror Image",
|
||||||
|
"rncdn1.com": "Reflected Networks",
|
||||||
|
"simplecdn.net": "Simple CDN",
|
||||||
|
"swiftcdn1.com": "SwiftCDN",
|
||||||
|
"swiftserve.com": "SwiftServe",
|
||||||
|
"gslb.taobao.com": "Taobao",
|
||||||
|
"cdn.bitgravity.com": "Tata communications",
|
||||||
|
"cdn.telefonica.com": "Telefonica",
|
||||||
|
"vo.msecnd.net": "Windows Azure",
|
||||||
|
"ay1.b.yahoo.com": "Yahoo",
|
||||||
|
"yimg.": "Yahoo",
|
||||||
|
"zenedge.net": "Zenedge",
|
||||||
|
"cdnsun.net.": "CDNsun",
|
||||||
|
"pilidns.com": "QiNiu",
|
||||||
|
"cdngslb.com": "AliCDN Global",
|
||||||
|
"ialicdn.com": "AliCDN",
|
||||||
|
"alivecdn.com": "AliCDN",
|
||||||
|
"myalicdn.com": "AliCDN"
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
[
|
||||||
|
"xcs",
|
||||||
|
"via",
|
||||||
|
"x-via",
|
||||||
|
"x-cdn",
|
||||||
|
"x-cdn-forward",
|
||||||
|
"x-ser",
|
||||||
|
"x-cf1",
|
||||||
|
"cache",
|
||||||
|
"x-cache",
|
||||||
|
"x-cached",
|
||||||
|
"x-cacheable",
|
||||||
|
"x-hit-cache",
|
||||||
|
"x-cache-status",
|
||||||
|
"x-cache-hits",
|
||||||
|
"x-cache-lookup",
|
||||||
|
"cc_cache",
|
||||||
|
"webcache",
|
||||||
|
"chinacache",
|
||||||
|
"x-req-id",
|
||||||
|
"x-requestid",
|
||||||
|
"cf-request-id",
|
||||||
|
"x-github-request-id",
|
||||||
|
"x-sucuri-id",
|
||||||
|
"x-amz-cf-id",
|
||||||
|
"x-airee-node",
|
||||||
|
"x-cdn-provider",
|
||||||
|
"x-fastly",
|
||||||
|
"x-iinfo",
|
||||||
|
"x-llid",
|
||||||
|
"sozu-id",
|
||||||
|
"x-cf-tsc",
|
||||||
|
"x-ws-request-id",
|
||||||
|
"fss-cache",
|
||||||
|
"powered-by-chinacache",
|
||||||
|
"verycdn",
|
||||||
|
"yunjiasu",
|
||||||
|
"skyparkcdn",
|
||||||
|
"x-beluga-cache-status",
|
||||||
|
"x-content-type-options",
|
||||||
|
"x-download-options",
|
||||||
|
"x-proxy-node",
|
||||||
|
"access-control-max-age",
|
||||||
|
"age",
|
||||||
|
"etag",
|
||||||
|
"expires",
|
||||||
|
"pragma",
|
||||||
|
"cache-control",
|
||||||
|
"last-modified"
|
||||||
|
]
|
||||||
@@ -0,0 +1,542 @@
|
|||||||
|
[
|
||||||
|
"223.99.255.0/24",
|
||||||
|
"71.152.0.0/17",
|
||||||
|
"219.153.73.0/24",
|
||||||
|
"125.39.46.0/24",
|
||||||
|
"190.93.240.0/20",
|
||||||
|
"14.0.113.0/24",
|
||||||
|
"14.0.47.0/24",
|
||||||
|
"113.20.148.0/22",
|
||||||
|
"103.75.201.0/24",
|
||||||
|
"1.32.239.0/24",
|
||||||
|
"101.79.239.0/24",
|
||||||
|
"52.46.0.0/18",
|
||||||
|
"125.88.189.0/24",
|
||||||
|
"150.138.248.0/24",
|
||||||
|
"180.153.235.0/24",
|
||||||
|
"205.251.252.0/23",
|
||||||
|
"103.1.65.0/24",
|
||||||
|
"115.127.227.0/24",
|
||||||
|
"14.0.42.0/24",
|
||||||
|
"109.199.58.0/24",
|
||||||
|
"116.211.155.0/24",
|
||||||
|
"112.253.3.0/24",
|
||||||
|
"14.0.58.0/24",
|
||||||
|
"223.112.227.0/24",
|
||||||
|
"113.20.150.0/23",
|
||||||
|
"61.182.141.0/24",
|
||||||
|
"34.216.51.0/25",
|
||||||
|
"124.95.188.0/24",
|
||||||
|
"42.51.25.0/24",
|
||||||
|
"183.136.133.0/24",
|
||||||
|
"52.220.191.0/26",
|
||||||
|
"119.84.93.0/24",
|
||||||
|
"182.118.38.0/24",
|
||||||
|
"13.59.250.0/26",
|
||||||
|
"54.178.75.0/24",
|
||||||
|
"119.84.92.0/24",
|
||||||
|
"183.131.62.0/24",
|
||||||
|
"111.32.136.0/24",
|
||||||
|
"13.124.199.0/24",
|
||||||
|
"111.47.227.0/24",
|
||||||
|
"104.37.177.0/24",
|
||||||
|
"14.0.50.0/24",
|
||||||
|
"183.230.70.0/24",
|
||||||
|
"114.111.59.0/24",
|
||||||
|
"220.181.135.0/24",
|
||||||
|
"112.140.32.0/19",
|
||||||
|
"101.79.230.0/24",
|
||||||
|
"14.0.115.0/24",
|
||||||
|
"103.28.248.0/22",
|
||||||
|
"117.34.72.0/24",
|
||||||
|
"109.199.57.0/24",
|
||||||
|
"101.79.149.0/24",
|
||||||
|
"116.128.128.0/24",
|
||||||
|
"115.231.186.0/24",
|
||||||
|
"103.22.200.0/22",
|
||||||
|
"61.155.165.0/24",
|
||||||
|
"113.20.148.0/23",
|
||||||
|
"185.254.242.0/24",
|
||||||
|
"59.36.120.0/24",
|
||||||
|
"70.132.0.0/18",
|
||||||
|
"116.31.126.0/24",
|
||||||
|
"119.147.134.0/24",
|
||||||
|
"115.127.246.0/24",
|
||||||
|
"52.47.139.0/24",
|
||||||
|
"118.107.175.0/24",
|
||||||
|
"52.78.247.128/26",
|
||||||
|
"110.93.176.0/20",
|
||||||
|
"54.240.128.0/18",
|
||||||
|
"46.51.216.0/21",
|
||||||
|
"119.31.251.0/24",
|
||||||
|
"125.39.18.0/24",
|
||||||
|
"108.175.33.0/24",
|
||||||
|
"1.31.128.0/24",
|
||||||
|
"61.151.163.0/24",
|
||||||
|
"103.95.132.0/24",
|
||||||
|
"58.215.118.0/24",
|
||||||
|
"54.233.255.128/26",
|
||||||
|
"120.52.113.0/24",
|
||||||
|
"118.107.174.0/24",
|
||||||
|
"1.32.242.0/24",
|
||||||
|
"221.195.34.0/24",
|
||||||
|
"101.79.228.0/24",
|
||||||
|
"205.251.249.0/24",
|
||||||
|
"113.200.91.0/24",
|
||||||
|
"101.79.146.0/24",
|
||||||
|
"221.238.22.0/24",
|
||||||
|
"134.19.183.0/24",
|
||||||
|
"110.93.160.0/20",
|
||||||
|
"180.97.158.0/24",
|
||||||
|
"115.127.251.0/24",
|
||||||
|
"119.167.147.0/24",
|
||||||
|
"115.127.238.0/24",
|
||||||
|
"115.127.240.0/22",
|
||||||
|
"14.0.48.0/24",
|
||||||
|
"115.127.240.0/24",
|
||||||
|
"113.7.183.0/24",
|
||||||
|
"112.140.128.0/20",
|
||||||
|
"115.127.255.0/24",
|
||||||
|
"114.31.36.0/22",
|
||||||
|
"101.79.232.0/24",
|
||||||
|
"218.98.44.0/24",
|
||||||
|
"106.119.182.0/24",
|
||||||
|
"101.79.167.0/24",
|
||||||
|
"125.39.5.0/24",
|
||||||
|
"58.49.105.0/24",
|
||||||
|
"124.202.164.0/24",
|
||||||
|
"111.177.6.0/24",
|
||||||
|
"61.133.127.0/24",
|
||||||
|
"185.11.124.0/22",
|
||||||
|
"150.138.150.0/24",
|
||||||
|
"115.127.248.0/24",
|
||||||
|
"103.74.80.0/22",
|
||||||
|
"101.79.166.0/24",
|
||||||
|
"101.71.55.0/24",
|
||||||
|
"198.41.128.0/17",
|
||||||
|
"117.21.219.0/24",
|
||||||
|
"103.231.170.0/24",
|
||||||
|
"221.204.202.0/24",
|
||||||
|
"101.79.224.0/24",
|
||||||
|
"112.25.16.0/24",
|
||||||
|
"111.177.3.0/24",
|
||||||
|
"204.246.168.0/22",
|
||||||
|
"103.40.7.0/24",
|
||||||
|
"134.226.0.0/16",
|
||||||
|
"52.15.127.128/26",
|
||||||
|
"122.190.2.0/24",
|
||||||
|
"101.203.192.0/18",
|
||||||
|
"1.32.238.0/24",
|
||||||
|
"101.79.144.0/24",
|
||||||
|
"176.34.28.0/24",
|
||||||
|
"119.84.15.0/24",
|
||||||
|
"18.216.170.128/25",
|
||||||
|
"222.88.94.0/24",
|
||||||
|
"101.79.150.0/24",
|
||||||
|
"114.111.48.0/21",
|
||||||
|
"124.95.168.0/24",
|
||||||
|
"114.111.48.0/20",
|
||||||
|
"110.93.176.0/21",
|
||||||
|
"223.111.127.0/24",
|
||||||
|
"117.23.61.0/24",
|
||||||
|
"140.207.120.0/24",
|
||||||
|
"157.255.26.0/24",
|
||||||
|
"221.204.14.0/24",
|
||||||
|
"183.222.96.0/24",
|
||||||
|
"104.37.180.0/24",
|
||||||
|
"42.236.93.0/24",
|
||||||
|
"111.63.51.0/24",
|
||||||
|
"114.31.32.0/20",
|
||||||
|
"118.180.50.0/24",
|
||||||
|
"222.240.184.0/24",
|
||||||
|
"205.251.192.0/19",
|
||||||
|
"101.79.225.0/24",
|
||||||
|
"115.127.228.0/24",
|
||||||
|
"113.20.148.0/24",
|
||||||
|
"61.213.176.0/24",
|
||||||
|
"112.65.75.0/24",
|
||||||
|
"111.13.147.0/24",
|
||||||
|
"113.20.145.0/24",
|
||||||
|
"103.253.132.0/24",
|
||||||
|
"52.222.128.0/17",
|
||||||
|
"183.203.7.0/24",
|
||||||
|
"27.221.27.0/24",
|
||||||
|
"103.79.134.0/24",
|
||||||
|
"123.150.187.0/24",
|
||||||
|
"103.15.194.0/24",
|
||||||
|
"162.158.0.0/15",
|
||||||
|
"61.163.30.0/24",
|
||||||
|
"182.140.227.0/24",
|
||||||
|
"112.25.60.0/24",
|
||||||
|
"117.148.161.0/24",
|
||||||
|
"61.182.136.0/24",
|
||||||
|
"114.31.56.0/22",
|
||||||
|
"64.252.128.0/18",
|
||||||
|
"183.61.185.0/24",
|
||||||
|
"115.127.250.0/24",
|
||||||
|
"150.138.138.0/24",
|
||||||
|
"13.210.67.128/26",
|
||||||
|
"211.162.64.0/24",
|
||||||
|
"61.174.9.0/24",
|
||||||
|
"14.0.112.0/24",
|
||||||
|
"52.52.191.128/26",
|
||||||
|
"27.221.124.0/24",
|
||||||
|
"103.4.203.0/24",
|
||||||
|
"103.14.10.0/24",
|
||||||
|
"34.232.163.208/29",
|
||||||
|
"114.31.48.0/20",
|
||||||
|
"59.51.81.0/24",
|
||||||
|
"183.60.235.0/24",
|
||||||
|
"101.227.206.0/24",
|
||||||
|
"125.39.174.0/24",
|
||||||
|
"119.167.246.0/24",
|
||||||
|
"118.107.160.0/21",
|
||||||
|
"223.166.151.0/24",
|
||||||
|
"110.93.160.0/19",
|
||||||
|
"204.246.172.0/23",
|
||||||
|
"119.31.253.0/24",
|
||||||
|
"143.204.0.0/16",
|
||||||
|
"14.0.60.0/24",
|
||||||
|
"123.151.76.0/24",
|
||||||
|
"116.193.80.0/24",
|
||||||
|
"120.241.102.0/24",
|
||||||
|
"180.96.20.0/24",
|
||||||
|
"216.137.32.0/19",
|
||||||
|
"223.94.95.0/24",
|
||||||
|
"103.4.201.0/24",
|
||||||
|
"14.0.56.0/24",
|
||||||
|
"115.127.234.0/24",
|
||||||
|
"113.20.144.0/23",
|
||||||
|
"103.248.104.0/24",
|
||||||
|
"122.143.15.0/24",
|
||||||
|
"101.79.229.0/24",
|
||||||
|
"101.79.163.0/24",
|
||||||
|
"104.37.112.0/22",
|
||||||
|
"115.127.253.0/24",
|
||||||
|
"141.101.64.0/18",
|
||||||
|
"113.20.144.0/22",
|
||||||
|
"101.79.155.0/24",
|
||||||
|
"117.148.160.0/24",
|
||||||
|
"124.193.166.0/24",
|
||||||
|
"109.94.168.0/24",
|
||||||
|
"203.90.247.0/24",
|
||||||
|
"101.79.208.0/21",
|
||||||
|
"182.118.12.0/24",
|
||||||
|
"114.31.58.0/23",
|
||||||
|
"202.162.109.0/24",
|
||||||
|
"101.79.164.0/24",
|
||||||
|
"58.216.2.0/24",
|
||||||
|
"222.216.190.0/24",
|
||||||
|
"101.79.165.0/24",
|
||||||
|
"111.6.191.0/24",
|
||||||
|
"1.255.100.0/24",
|
||||||
|
"52.84.0.0/15",
|
||||||
|
"112.65.74.0/24",
|
||||||
|
"183.250.179.0/24",
|
||||||
|
"101.79.236.0/24",
|
||||||
|
"119.31.252.0/24",
|
||||||
|
"113.20.150.0/24",
|
||||||
|
"60.12.166.0/24",
|
||||||
|
"101.79.234.0/24",
|
||||||
|
"113.17.174.0/24",
|
||||||
|
"101.79.237.0/24",
|
||||||
|
"61.54.46.0/24",
|
||||||
|
"118.212.233.0/24",
|
||||||
|
"183.110.242.0/24",
|
||||||
|
"150.138.149.0/24",
|
||||||
|
"117.34.13.0/24",
|
||||||
|
"115.127.245.0/24",
|
||||||
|
"14.0.102.0/24",
|
||||||
|
"14.0.109.0/24",
|
||||||
|
"61.130.28.0/24",
|
||||||
|
"113.20.151.0/24",
|
||||||
|
"219.159.84.0/24",
|
||||||
|
"114.111.62.0/24",
|
||||||
|
"172.64.0.0/13",
|
||||||
|
"61.155.222.0/24",
|
||||||
|
"120.52.29.0/24",
|
||||||
|
"115.127.231.0/24",
|
||||||
|
"14.0.49.0/24",
|
||||||
|
"113.202.0.0/16",
|
||||||
|
"103.248.104.0/22",
|
||||||
|
"205.251.250.0/23",
|
||||||
|
"103.216.136.0/22",
|
||||||
|
"118.107.160.0/20",
|
||||||
|
"109.87.0.0/21",
|
||||||
|
"54.239.128.0/18",
|
||||||
|
"115.127.224.0/19",
|
||||||
|
"111.202.98.0/24",
|
||||||
|
"109.94.169.0/24",
|
||||||
|
"59.38.112.0/24",
|
||||||
|
"204.246.176.0/20",
|
||||||
|
"123.133.84.0/24",
|
||||||
|
"103.4.200.0/24",
|
||||||
|
"111.161.109.0/24",
|
||||||
|
"112.84.34.0/24",
|
||||||
|
"103.82.129.0/24",
|
||||||
|
"183.3.254.0/24",
|
||||||
|
"112.137.184.0/21",
|
||||||
|
"122.227.237.0/24",
|
||||||
|
"36.42.75.0/24",
|
||||||
|
"13.35.0.0/16",
|
||||||
|
"101.226.4.0/24",
|
||||||
|
"116.140.35.0/24",
|
||||||
|
"58.250.143.0/24",
|
||||||
|
"13.54.63.128/26",
|
||||||
|
"205.251.254.0/24",
|
||||||
|
"173.245.48.0/20",
|
||||||
|
"183.61.177.0/24",
|
||||||
|
"113.20.144.0/24",
|
||||||
|
"104.37.183.0/24",
|
||||||
|
"35.158.136.0/24",
|
||||||
|
"116.211.121.0/24",
|
||||||
|
"42.236.94.0/24",
|
||||||
|
"117.34.91.0/24",
|
||||||
|
"123.6.13.0/24",
|
||||||
|
"13.224.0.0/14",
|
||||||
|
"113.20.146.0/24",
|
||||||
|
"58.58.81.0/24",
|
||||||
|
"52.124.128.0/17",
|
||||||
|
"122.228.198.0/24",
|
||||||
|
"197.234.240.0/22",
|
||||||
|
"99.86.0.0/16",
|
||||||
|
"144.220.0.0/16",
|
||||||
|
"119.188.97.0/24",
|
||||||
|
"36.27.212.0/24",
|
||||||
|
"104.37.178.0/24",
|
||||||
|
"114.31.52.0/22",
|
||||||
|
"218.65.212.0/24",
|
||||||
|
"1.255.41.0/24",
|
||||||
|
"14.0.45.0/24",
|
||||||
|
"1.32.243.0/24",
|
||||||
|
"220.170.185.0/24",
|
||||||
|
"122.190.3.0/24",
|
||||||
|
"103.79.133.0/24",
|
||||||
|
"220.181.55.0/24",
|
||||||
|
"125.39.191.0/24",
|
||||||
|
"115.127.226.0/24",
|
||||||
|
"125.39.32.0/24",
|
||||||
|
"61.120.154.0/24",
|
||||||
|
"103.4.202.0/24",
|
||||||
|
"103.79.134.0/23",
|
||||||
|
"115.127.224.0/24",
|
||||||
|
"113.20.147.0/24",
|
||||||
|
"61.156.149.0/24",
|
||||||
|
"210.209.122.0/24",
|
||||||
|
"115.127.249.0/24",
|
||||||
|
"104.37.179.0/24",
|
||||||
|
"120.52.18.0/24",
|
||||||
|
"54.192.0.0/16",
|
||||||
|
"14.0.55.0/24",
|
||||||
|
"61.160.224.0/24",
|
||||||
|
"113.207.101.0/24",
|
||||||
|
"101.79.157.0/24",
|
||||||
|
"110.93.128.0/20",
|
||||||
|
"58.251.121.0/24",
|
||||||
|
"61.240.149.0/24",
|
||||||
|
"130.176.0.0/16",
|
||||||
|
"113.107.238.0/24",
|
||||||
|
"112.65.73.0/24",
|
||||||
|
"103.75.200.0/23",
|
||||||
|
"199.83.128.0/21",
|
||||||
|
"123.129.220.0/24",
|
||||||
|
"54.230.0.0/16",
|
||||||
|
"114.111.60.0/24",
|
||||||
|
"199.27.128.0/21",
|
||||||
|
"14.0.118.0/24",
|
||||||
|
"101.79.158.0/24",
|
||||||
|
"119.31.248.0/21",
|
||||||
|
"54.182.0.0/16",
|
||||||
|
"113.31.27.0/24",
|
||||||
|
"14.17.69.0/24",
|
||||||
|
"101.79.145.0/24",
|
||||||
|
"113.20.144.0/21",
|
||||||
|
"180.163.22.0/24",
|
||||||
|
"104.37.176.0/21",
|
||||||
|
"117.25.156.0/24",
|
||||||
|
"115.127.252.0/24",
|
||||||
|
"115.127.244.0/23",
|
||||||
|
"14.0.46.0/24",
|
||||||
|
"113.207.102.0/24",
|
||||||
|
"52.199.127.192/26",
|
||||||
|
"13.113.203.0/24",
|
||||||
|
"64.252.64.0/18",
|
||||||
|
"1.32.240.0/24",
|
||||||
|
"123.129.232.0/24",
|
||||||
|
"1.32.241.0/24",
|
||||||
|
"180.163.189.0/24",
|
||||||
|
"157.255.25.0/24",
|
||||||
|
"1.32.244.0/24",
|
||||||
|
"103.248.106.0/24",
|
||||||
|
"121.48.95.0/24",
|
||||||
|
"54.239.192.0/19",
|
||||||
|
"113.20.146.0/23",
|
||||||
|
"61.136.173.0/24",
|
||||||
|
"35.162.63.192/26",
|
||||||
|
"117.34.14.0/24",
|
||||||
|
"183.232.29.0/24",
|
||||||
|
"42.81.93.0/24",
|
||||||
|
"122.228.238.0/24",
|
||||||
|
"183.61.190.0/24",
|
||||||
|
"125.39.239.0/24",
|
||||||
|
"115.127.230.0/24",
|
||||||
|
"103.140.200.0/23",
|
||||||
|
"202.102.85.0/24",
|
||||||
|
"14.0.32.0/21",
|
||||||
|
"14.0.57.0/24",
|
||||||
|
"112.25.90.0/24",
|
||||||
|
"58.211.137.0/24",
|
||||||
|
"210.22.63.0/24",
|
||||||
|
"34.226.14.0/24",
|
||||||
|
"13.32.0.0/15",
|
||||||
|
"101.79.156.0/24",
|
||||||
|
"103.89.176.0/24",
|
||||||
|
"14.0.116.0/24",
|
||||||
|
"106.42.25.0/24",
|
||||||
|
"101.79.233.0/24",
|
||||||
|
"101.79.231.0/24",
|
||||||
|
"103.75.200.0/24",
|
||||||
|
"119.188.9.0/24",
|
||||||
|
"183.232.51.0/24",
|
||||||
|
"149.126.72.0/21",
|
||||||
|
"103.21.244.0/22",
|
||||||
|
"115.127.233.0/24",
|
||||||
|
"27.221.20.0/24",
|
||||||
|
"198.143.32.0/19",
|
||||||
|
"103.248.107.0/24",
|
||||||
|
"101.79.227.0/24",
|
||||||
|
"115.127.242.0/24",
|
||||||
|
"119.31.250.0/24",
|
||||||
|
"103.82.130.0/24",
|
||||||
|
"99.84.0.0/16",
|
||||||
|
"222.73.144.0/24",
|
||||||
|
"103.79.132.0/22",
|
||||||
|
"101.79.208.0/20",
|
||||||
|
"104.37.182.0/24",
|
||||||
|
"101.79.152.0/24",
|
||||||
|
"36.99.18.0/24",
|
||||||
|
"101.71.56.0/24",
|
||||||
|
"36.250.5.0/24",
|
||||||
|
"61.158.240.0/24",
|
||||||
|
"119.188.14.0/24",
|
||||||
|
"13.249.0.0/16",
|
||||||
|
"183.214.156.0/24",
|
||||||
|
"60.221.236.0/24",
|
||||||
|
"58.30.212.0/24",
|
||||||
|
"115.127.254.0/24",
|
||||||
|
"188.114.96.0/20",
|
||||||
|
"115.127.241.0/24",
|
||||||
|
"103.4.200.0/22",
|
||||||
|
"115.127.239.0/24",
|
||||||
|
"115.127.243.0/24",
|
||||||
|
"111.32.135.0/24",
|
||||||
|
"120.221.29.0/24",
|
||||||
|
"115.127.232.0/24",
|
||||||
|
"14.0.43.0/24",
|
||||||
|
"14.0.59.0/24",
|
||||||
|
"183.61.236.0/24",
|
||||||
|
"34.223.12.224/27",
|
||||||
|
"103.24.120.0/24",
|
||||||
|
"52.57.254.0/24",
|
||||||
|
"113.207.100.0/24",
|
||||||
|
"222.186.19.0/24",
|
||||||
|
"113.20.149.0/24",
|
||||||
|
"150.138.151.0/24",
|
||||||
|
"115.231.110.0/24",
|
||||||
|
"52.56.127.0/25",
|
||||||
|
"104.37.176.0/24",
|
||||||
|
"163.177.8.0/24",
|
||||||
|
"163.53.89.0/24",
|
||||||
|
"52.82.128.0/19",
|
||||||
|
"114.111.63.0/24",
|
||||||
|
"108.162.192.0/18",
|
||||||
|
"14.136.130.0/24",
|
||||||
|
"115.127.229.0/24",
|
||||||
|
"14.17.71.0/24",
|
||||||
|
"52.212.248.0/26",
|
||||||
|
"180.163.188.0/24",
|
||||||
|
"61.182.137.0/24",
|
||||||
|
"119.161.224.0/21",
|
||||||
|
"14.0.41.0/24",
|
||||||
|
"202.162.108.0/24",
|
||||||
|
"106.122.248.0/24",
|
||||||
|
"52.66.194.128/26",
|
||||||
|
"115.127.237.0/24",
|
||||||
|
"220.170.186.0/24",
|
||||||
|
"14.0.32.0/19",
|
||||||
|
"14.0.114.0/24",
|
||||||
|
"112.90.216.0/24",
|
||||||
|
"115.127.236.0/24",
|
||||||
|
"116.193.84.0/24",
|
||||||
|
"113.207.76.0/24",
|
||||||
|
"101.79.235.0/24",
|
||||||
|
"101.79.224.0/20",
|
||||||
|
"61.155.149.0/24",
|
||||||
|
"101.79.148.0/24",
|
||||||
|
"180.163.224.0/24",
|
||||||
|
"204.246.174.0/23",
|
||||||
|
"183.60.136.0/24",
|
||||||
|
"101.227.207.0/24",
|
||||||
|
"103.248.105.0/24",
|
||||||
|
"119.188.35.0/24",
|
||||||
|
"42.236.7.0/24",
|
||||||
|
"116.193.88.0/21",
|
||||||
|
"116.193.83.0/24",
|
||||||
|
"120.199.69.0/24",
|
||||||
|
"122.226.182.0/24",
|
||||||
|
"58.20.204.0/24",
|
||||||
|
"110.93.128.0/21",
|
||||||
|
"115.231.187.0/24",
|
||||||
|
"69.28.58.0/24",
|
||||||
|
"114.31.32.0/19",
|
||||||
|
"112.25.91.0/24",
|
||||||
|
"59.52.28.0/24",
|
||||||
|
"117.27.149.0/24",
|
||||||
|
"61.147.92.0/24",
|
||||||
|
"14.0.117.0/24",
|
||||||
|
"14.0.40.0/24",
|
||||||
|
"119.97.151.0/24",
|
||||||
|
"103.199.228.0/22",
|
||||||
|
"122.70.134.0/24",
|
||||||
|
"115.127.244.0/24",
|
||||||
|
"223.112.198.0/24",
|
||||||
|
"115.127.225.0/24",
|
||||||
|
"104.16.0.0/12",
|
||||||
|
"121.12.98.0/24",
|
||||||
|
"103.31.4.0/22",
|
||||||
|
"204.246.164.0/22",
|
||||||
|
"223.94.66.0/24",
|
||||||
|
"35.167.191.128/26",
|
||||||
|
"116.31.127.0/24",
|
||||||
|
"101.79.226.0/24",
|
||||||
|
"34.195.252.0/24",
|
||||||
|
"115.127.247.0/24",
|
||||||
|
"61.240.144.0/24",
|
||||||
|
"108.175.32.0/20",
|
||||||
|
"120.197.85.0/24",
|
||||||
|
"183.232.53.0/24",
|
||||||
|
"111.161.66.0/24",
|
||||||
|
"117.34.28.0/24",
|
||||||
|
"45.64.64.0/22",
|
||||||
|
"14.0.44.0/24",
|
||||||
|
"109.86.0.0/15",
|
||||||
|
"182.23.211.0/24",
|
||||||
|
"58.211.2.0/24",
|
||||||
|
"119.36.164.0/24",
|
||||||
|
"116.55.250.0/24",
|
||||||
|
"101.227.163.0/24",
|
||||||
|
"13.228.69.0/24",
|
||||||
|
"120.221.136.0/24",
|
||||||
|
"119.188.132.0/24",
|
||||||
|
"115.127.235.0/24",
|
||||||
|
"42.236.6.0/24",
|
||||||
|
"125.88.190.0/24",
|
||||||
|
"61.54.47.0/24",
|
||||||
|
"103.27.12.0/22",
|
||||||
|
"116.193.80.0/21",
|
||||||
|
"101.79.159.0/24",
|
||||||
|
"123.155.158.0/24",
|
||||||
|
"111.47.226.0/24",
|
||||||
|
"131.0.72.0/22",
|
||||||
|
"192.230.64.0/18"
|
||||||
|
]
|
||||||
@@ -0,0 +1,627 @@
|
|||||||
|
[
|
||||||
|
"npm.js",
|
||||||
|
"bower.js",
|
||||||
|
"component.js",
|
||||||
|
"spm.js",
|
||||||
|
"jam.js",
|
||||||
|
"jspm.js",
|
||||||
|
"ender.js",
|
||||||
|
"volo.js",
|
||||||
|
"duo.js",
|
||||||
|
"yarn.js",
|
||||||
|
"requirejs.js",
|
||||||
|
"browserify.js",
|
||||||
|
"seajs.js",
|
||||||
|
"headjs.js",
|
||||||
|
"curl.js",
|
||||||
|
"lazyload.js",
|
||||||
|
"systemjs.js",
|
||||||
|
"lodjs.js",
|
||||||
|
"esl.js",
|
||||||
|
"modulejs.js",
|
||||||
|
"webpack.js",
|
||||||
|
"rollup.js",
|
||||||
|
"brunch.js",
|
||||||
|
"parcel.js",
|
||||||
|
"microbundle.js",
|
||||||
|
"typescript.js",
|
||||||
|
"hegel.js",
|
||||||
|
"typl.js",
|
||||||
|
"mocha.js",
|
||||||
|
"jasmine.js",
|
||||||
|
"qunit.js",
|
||||||
|
"jest.js",
|
||||||
|
"prova.js",
|
||||||
|
"dalekjs.js",
|
||||||
|
"protractor.js",
|
||||||
|
"tape.js",
|
||||||
|
"testcafe.js",
|
||||||
|
"ava.js",
|
||||||
|
"cypress.js",
|
||||||
|
"chai.js",
|
||||||
|
"enzyme.js",
|
||||||
|
"proxyquire.js",
|
||||||
|
"istanbul.js",
|
||||||
|
"blanket.js",
|
||||||
|
"jscover.js",
|
||||||
|
"phantomjs.js",
|
||||||
|
"slimerjs.js",
|
||||||
|
"casperjs.js",
|
||||||
|
"zombie.js",
|
||||||
|
"totoro.js",
|
||||||
|
"karma.js",
|
||||||
|
"nightwatch.js",
|
||||||
|
"intern.js",
|
||||||
|
"yolpo.js",
|
||||||
|
"puppeteer.js",
|
||||||
|
"webdriverio.js",
|
||||||
|
"prettier.js",
|
||||||
|
"jshint.js",
|
||||||
|
"jscs.js",
|
||||||
|
"jsfmt.js",
|
||||||
|
"jsinspect.js",
|
||||||
|
"eslint.js",
|
||||||
|
"jslint.js",
|
||||||
|
"aurelia.js",
|
||||||
|
"backbone.js",
|
||||||
|
"meteor.js",
|
||||||
|
"ractive.js",
|
||||||
|
"vue.js",
|
||||||
|
"svelte.js",
|
||||||
|
"knockout.js",
|
||||||
|
"spine.js",
|
||||||
|
"canjs.js",
|
||||||
|
"react.js",
|
||||||
|
"hyperapp.js",
|
||||||
|
"preact.js",
|
||||||
|
"nativescript.js",
|
||||||
|
"riot.js",
|
||||||
|
"thorax.js",
|
||||||
|
"chaplin.js",
|
||||||
|
"marionette.js",
|
||||||
|
"ripple.js",
|
||||||
|
"rivets.js",
|
||||||
|
"derby.js",
|
||||||
|
"jsblocks.js",
|
||||||
|
"liquidlava.js",
|
||||||
|
"feathers.js",
|
||||||
|
"keo.js",
|
||||||
|
"atvjs.js",
|
||||||
|
"makefun.js",
|
||||||
|
"keystonejs.js",
|
||||||
|
"ghost.js",
|
||||||
|
"apostrophe.js",
|
||||||
|
"taracotjs.js",
|
||||||
|
"nodizecms.js",
|
||||||
|
"cody.js",
|
||||||
|
"pencilblue.js",
|
||||||
|
"strapi.js",
|
||||||
|
"factor.js",
|
||||||
|
"nunjucks.js",
|
||||||
|
"dot.js",
|
||||||
|
"dustjs.js",
|
||||||
|
"eco.js",
|
||||||
|
"pug.js",
|
||||||
|
"ejs.js",
|
||||||
|
"xtemplate.js",
|
||||||
|
"marko.js",
|
||||||
|
"swig.js",
|
||||||
|
"ehtml.js",
|
||||||
|
"d3.js",
|
||||||
|
"peity.js",
|
||||||
|
"raphael.js",
|
||||||
|
"echarts.js",
|
||||||
|
"vis.js",
|
||||||
|
"arbor.js",
|
||||||
|
"cubism.js",
|
||||||
|
"vega.js",
|
||||||
|
"envisionjs.js",
|
||||||
|
"rickshaw.js",
|
||||||
|
"flot.js",
|
||||||
|
"nvd3.js",
|
||||||
|
"trianglify.js",
|
||||||
|
"d4.js",
|
||||||
|
"epoch.js",
|
||||||
|
"c3.js",
|
||||||
|
"babylonjs.js",
|
||||||
|
"recharts.js",
|
||||||
|
"graphicsjs.js",
|
||||||
|
"mxgraph.js",
|
||||||
|
"amchart.js",
|
||||||
|
"anychart.js",
|
||||||
|
"plotly.js",
|
||||||
|
"highchart.js",
|
||||||
|
"handsontable.js",
|
||||||
|
"ace.js",
|
||||||
|
"codemirror.js",
|
||||||
|
"esprima.js",
|
||||||
|
"quill.js",
|
||||||
|
"pen.js",
|
||||||
|
"editor.js",
|
||||||
|
"epiceditor.js",
|
||||||
|
"jsoneditor.js",
|
||||||
|
"squire.js",
|
||||||
|
"tinymce.js",
|
||||||
|
"trix.js",
|
||||||
|
"trumbowyg.js",
|
||||||
|
"wysihtml5.js",
|
||||||
|
"popline.js",
|
||||||
|
"summernote.js",
|
||||||
|
"devdocs.js",
|
||||||
|
"dexy.js",
|
||||||
|
"docco.js",
|
||||||
|
"styledocco.js",
|
||||||
|
"ronn.js",
|
||||||
|
"dox.js",
|
||||||
|
"jsdox.js",
|
||||||
|
"esdoc.js",
|
||||||
|
"yuidoc.js",
|
||||||
|
"coddoc.js",
|
||||||
|
"sphinx.js",
|
||||||
|
"jsduck.js",
|
||||||
|
"codecrumbs.js",
|
||||||
|
"jbinary.js",
|
||||||
|
"diff2html.js",
|
||||||
|
"jspdf.js",
|
||||||
|
"underscore.js",
|
||||||
|
"lodash.js",
|
||||||
|
"sugar.js",
|
||||||
|
"ramda.js",
|
||||||
|
"mout.js",
|
||||||
|
"mesh.js",
|
||||||
|
"preludejs.js",
|
||||||
|
"rxjs.js",
|
||||||
|
"bacon.js",
|
||||||
|
"kefir.js",
|
||||||
|
"highland.js",
|
||||||
|
"mobx.js",
|
||||||
|
"mori.js",
|
||||||
|
"buckets.js",
|
||||||
|
"hashmap.js",
|
||||||
|
"moment.js",
|
||||||
|
"date.js",
|
||||||
|
"fecha.js",
|
||||||
|
"dayjs.js",
|
||||||
|
"voca.js",
|
||||||
|
"selecting.js",
|
||||||
|
"he.js",
|
||||||
|
"multiline.js",
|
||||||
|
"jsurl.js",
|
||||||
|
"plexis.js",
|
||||||
|
"odometer.js",
|
||||||
|
"localforage.js",
|
||||||
|
"jstorage.js",
|
||||||
|
"cookies.js",
|
||||||
|
"crumbsjs.js",
|
||||||
|
"randomcolor.js",
|
||||||
|
"color.js",
|
||||||
|
"colors.js",
|
||||||
|
"pleasejs.js",
|
||||||
|
"tinycolor.js",
|
||||||
|
"i18next.js",
|
||||||
|
"polyglot.js",
|
||||||
|
"babelfish.js",
|
||||||
|
"ttag.js",
|
||||||
|
"async.js",
|
||||||
|
"q.js",
|
||||||
|
"step.js",
|
||||||
|
"contra.js",
|
||||||
|
"bluebird.js",
|
||||||
|
"when.js",
|
||||||
|
"objecteventtarget.js",
|
||||||
|
"sporadic.js",
|
||||||
|
"director.js",
|
||||||
|
"pathjs.js",
|
||||||
|
"crossroads.js",
|
||||||
|
"navaid.js",
|
||||||
|
"dompurify.js",
|
||||||
|
"log.js",
|
||||||
|
"conzole.js",
|
||||||
|
"loglevel.js",
|
||||||
|
"minilog.js",
|
||||||
|
"storyboard.js",
|
||||||
|
"regex101.js",
|
||||||
|
"regexr.js",
|
||||||
|
"regexpbuilder.js",
|
||||||
|
"annyang.js",
|
||||||
|
"axios.js",
|
||||||
|
"bottleneck.js",
|
||||||
|
"amygdala.js",
|
||||||
|
"wretch.js",
|
||||||
|
"farfetch.js",
|
||||||
|
"tailor.js",
|
||||||
|
"convnetjs.js",
|
||||||
|
"dn2a.js",
|
||||||
|
"synapses.js",
|
||||||
|
"bowser.js",
|
||||||
|
"matcha.js",
|
||||||
|
"prismjs.js",
|
||||||
|
"nprogress.js",
|
||||||
|
"pace.js",
|
||||||
|
"topbar.js",
|
||||||
|
"nanobar.js",
|
||||||
|
"pageloadingeffects.js",
|
||||||
|
"spinkit.js",
|
||||||
|
"ladda.js",
|
||||||
|
"ajaxload.js",
|
||||||
|
"preloaders.js",
|
||||||
|
"cssload.js",
|
||||||
|
"validatr.js",
|
||||||
|
"formvalidation.js",
|
||||||
|
"fieldval.js",
|
||||||
|
"funval.js",
|
||||||
|
"mousetrap.js",
|
||||||
|
"keymaster.js",
|
||||||
|
"keypress.js",
|
||||||
|
"keyboardjs.js",
|
||||||
|
"jwerty.js",
|
||||||
|
"shepherd.js",
|
||||||
|
"tourist.js",
|
||||||
|
"pageguide.js",
|
||||||
|
"hopscotch.js",
|
||||||
|
"joyride.js",
|
||||||
|
"focusable.js",
|
||||||
|
"izitoast.js",
|
||||||
|
"messenger.js",
|
||||||
|
"noty.js",
|
||||||
|
"pnotify.js",
|
||||||
|
"toastr.js",
|
||||||
|
"notie.js",
|
||||||
|
"swiper.js",
|
||||||
|
"slick.js",
|
||||||
|
"slidesjs.js",
|
||||||
|
"flexslider.js",
|
||||||
|
"unslider.js",
|
||||||
|
"sly.js",
|
||||||
|
"vegas.js",
|
||||||
|
"sequence.js",
|
||||||
|
"strut.js",
|
||||||
|
"photoswipe.js",
|
||||||
|
"jcslider.js",
|
||||||
|
"slidr.js",
|
||||||
|
"flickity.js",
|
||||||
|
"jqrangeslider.js",
|
||||||
|
"nouislider.js",
|
||||||
|
"fancyinput.js",
|
||||||
|
"awesomplete.js",
|
||||||
|
"pikaday.js",
|
||||||
|
"fullcalendar.js",
|
||||||
|
"rome.js",
|
||||||
|
"datedropper.js",
|
||||||
|
"select2.js",
|
||||||
|
"chosen.js",
|
||||||
|
"dropzone.js",
|
||||||
|
"fileapi.js",
|
||||||
|
"plupload.js",
|
||||||
|
"form.js",
|
||||||
|
"countable.js",
|
||||||
|
"card.js",
|
||||||
|
"stretchy.js",
|
||||||
|
"analytics.js",
|
||||||
|
"tipsy.js",
|
||||||
|
"opentip.js",
|
||||||
|
"qtip2.js",
|
||||||
|
"tooltipster.js",
|
||||||
|
"simptip.js",
|
||||||
|
"toolbar.js",
|
||||||
|
"vex.js",
|
||||||
|
"sweetalert.js",
|
||||||
|
"colorbox.js",
|
||||||
|
"fancybox.js",
|
||||||
|
"swipebox.js",
|
||||||
|
"jbox.js",
|
||||||
|
"scrollmonitor.js",
|
||||||
|
"headroom.js",
|
||||||
|
"iscroll.js",
|
||||||
|
"skrollr.js",
|
||||||
|
"parallax.js",
|
||||||
|
"plax.js",
|
||||||
|
"jparallax.js",
|
||||||
|
"fullpage.js",
|
||||||
|
"scrollmenu.js",
|
||||||
|
"simpleparallax.js",
|
||||||
|
"slideout.js",
|
||||||
|
"jtable.js",
|
||||||
|
"datatables.js",
|
||||||
|
"tabulator.js",
|
||||||
|
"floatthead.js",
|
||||||
|
"masonry.js",
|
||||||
|
"packery.js",
|
||||||
|
"isotope.js",
|
||||||
|
"flexboxgrid.js",
|
||||||
|
"w2ui.js",
|
||||||
|
"fluidity.js",
|
||||||
|
"ink.js",
|
||||||
|
"dataformsjs.js",
|
||||||
|
"webplate.js",
|
||||||
|
"cerberus.js",
|
||||||
|
"touchemulator.js",
|
||||||
|
"dragula.js",
|
||||||
|
"leaflet.js",
|
||||||
|
"cesium.js",
|
||||||
|
"gmaps.js",
|
||||||
|
"polymaps.js",
|
||||||
|
"jqvmap.js",
|
||||||
|
"openlayers3.js",
|
||||||
|
"html5media.js",
|
||||||
|
"polyplayer.js",
|
||||||
|
"flowplayer.js",
|
||||||
|
"mediaelement.js",
|
||||||
|
"soundjs.js",
|
||||||
|
"clappr.js",
|
||||||
|
"exifr.js",
|
||||||
|
"bigtext.js",
|
||||||
|
"circletype.js",
|
||||||
|
"slabtext.js",
|
||||||
|
"velocity.js",
|
||||||
|
"transitionend.js",
|
||||||
|
"textillate.js",
|
||||||
|
"animatable.js",
|
||||||
|
"tsparticles.js",
|
||||||
|
"pica.js",
|
||||||
|
"cropper.js",
|
||||||
|
"es6features.js",
|
||||||
|
"gridsome.js",
|
||||||
|
"docusaurus.js",
|
||||||
|
"echo.js",
|
||||||
|
"picturefill.js",
|
||||||
|
"json3.js",
|
||||||
|
"mixitup.js",
|
||||||
|
"grid.js",
|
||||||
|
"ky.js",
|
||||||
|
"fcal.js",
|
||||||
|
"iooxa.js",
|
||||||
|
"idyll.js",
|
||||||
|
"primer.js",
|
||||||
|
"glue.js",
|
||||||
|
"postcss.js",
|
||||||
|
"mui.js",
|
||||||
|
"img2css.js",
|
||||||
|
"weui.js",
|
||||||
|
"csscss.js",
|
||||||
|
"simditor.js",
|
||||||
|
"htmlhint.js",
|
||||||
|
"csslint.js",
|
||||||
|
"grunt.js",
|
||||||
|
"yeoman.js",
|
||||||
|
"gulp.js",
|
||||||
|
"zrender.js",
|
||||||
|
"highcharts.js",
|
||||||
|
"tweenjs.js",
|
||||||
|
"swipe.js",
|
||||||
|
"superslides.js",
|
||||||
|
"slider.js",
|
||||||
|
"polymer.js",
|
||||||
|
"ionic.js",
|
||||||
|
"timelinejs.js",
|
||||||
|
"togetherjs.js",
|
||||||
|
"foundation.js",
|
||||||
|
"todomvc.js",
|
||||||
|
"vuejs.js",
|
||||||
|
"webuploader.js",
|
||||||
|
"fastclick.js",
|
||||||
|
"wangeditor.js",
|
||||||
|
"tooling.js",
|
||||||
|
"judge.js",
|
||||||
|
"amdoc.js",
|
||||||
|
"amazeui.js",
|
||||||
|
"zepto.js",
|
||||||
|
"nodeclub.js",
|
||||||
|
"nodeppt.js",
|
||||||
|
"hexo.js",
|
||||||
|
"koa.js",
|
||||||
|
"connect.js",
|
||||||
|
"nvm.js",
|
||||||
|
"flux.js",
|
||||||
|
"browserquest.js",
|
||||||
|
"html5shiv.js",
|
||||||
|
"ulkit.js",
|
||||||
|
"arttemplate.js",
|
||||||
|
"jade.js",
|
||||||
|
"modernizr.js",
|
||||||
|
"css3please.js",
|
||||||
|
"babel.js",
|
||||||
|
"f2etest.js",
|
||||||
|
"brackets.js",
|
||||||
|
"ueditor.js",
|
||||||
|
"electron.js",
|
||||||
|
"base.js",
|
||||||
|
"basscss.js",
|
||||||
|
"bootflat.js",
|
||||||
|
"bootswatch.js",
|
||||||
|
"bulma.js",
|
||||||
|
"cardinal.js",
|
||||||
|
"caramel.js",
|
||||||
|
"corpus.js",
|
||||||
|
"kube.js",
|
||||||
|
"materialize.js",
|
||||||
|
"milligram.js",
|
||||||
|
"papercss.js",
|
||||||
|
"papier.js",
|
||||||
|
"pavilion.js",
|
||||||
|
"picnicss.js",
|
||||||
|
"pure.js",
|
||||||
|
"skeleton.js",
|
||||||
|
"tachyons.js",
|
||||||
|
"tacit.js",
|
||||||
|
"uikit.js",
|
||||||
|
"wing.js",
|
||||||
|
"angular.js",
|
||||||
|
"choo.js",
|
||||||
|
"deku.js",
|
||||||
|
"displayjs.js",
|
||||||
|
"inferno.js",
|
||||||
|
"mercury.js",
|
||||||
|
"mithril.js",
|
||||||
|
"moon.js",
|
||||||
|
"skatejs.js",
|
||||||
|
"solid.js",
|
||||||
|
"bliss.js",
|
||||||
|
"cash.js",
|
||||||
|
"jquery.js",
|
||||||
|
"nanojs.js",
|
||||||
|
"selector.js",
|
||||||
|
"umbrella.js",
|
||||||
|
"zeptojs.js",
|
||||||
|
"chartist.js",
|
||||||
|
"charts.js",
|
||||||
|
"chartjs.js",
|
||||||
|
"dc.js",
|
||||||
|
"dimple.js",
|
||||||
|
"d3xter.js",
|
||||||
|
"f2.js",
|
||||||
|
"frappe.js",
|
||||||
|
"ggraph.js",
|
||||||
|
"jsplumb.js",
|
||||||
|
"metricsgraphics.js",
|
||||||
|
"morrisjs.js",
|
||||||
|
"muze.js",
|
||||||
|
"sparkline.js",
|
||||||
|
"sparky.js",
|
||||||
|
"taucharts.js",
|
||||||
|
"uvcharts.js",
|
||||||
|
"vivagraph.js",
|
||||||
|
"z3d.js",
|
||||||
|
"kartograph.js",
|
||||||
|
"mapsicon.js",
|
||||||
|
"osmbuildings.js",
|
||||||
|
"planetary.js",
|
||||||
|
"smallworld.js",
|
||||||
|
"tangram.js",
|
||||||
|
"topojson.js",
|
||||||
|
"turf.js",
|
||||||
|
"dynatables.js",
|
||||||
|
"listjs.js",
|
||||||
|
"sortable.js",
|
||||||
|
"tablesaw.js",
|
||||||
|
"flipside.js",
|
||||||
|
"nudge.js",
|
||||||
|
"glide.js",
|
||||||
|
"lory.js",
|
||||||
|
"siema.js",
|
||||||
|
"blotter.js",
|
||||||
|
"fitty.js",
|
||||||
|
"flowtype.js",
|
||||||
|
"lettering.js",
|
||||||
|
"shave.js",
|
||||||
|
"typeplate.js",
|
||||||
|
"fitvid.js",
|
||||||
|
"medialementjs.js",
|
||||||
|
"plyr.js",
|
||||||
|
"talkie.js",
|
||||||
|
"videojs.js",
|
||||||
|
"abcjs.js",
|
||||||
|
"audio5js.js",
|
||||||
|
"bap.js",
|
||||||
|
"blip.js",
|
||||||
|
"howler.js",
|
||||||
|
"soundcite.js",
|
||||||
|
"tonal.js",
|
||||||
|
"vexflow.js",
|
||||||
|
"easeljs.js",
|
||||||
|
"konva.js",
|
||||||
|
"panzoom.js",
|
||||||
|
"p5js.js",
|
||||||
|
"vizflow.js",
|
||||||
|
"zdog.js",
|
||||||
|
"scenejs.js",
|
||||||
|
"whitestormjs.js",
|
||||||
|
"camanjs.js",
|
||||||
|
"grafijs.js",
|
||||||
|
"smartcrop.js",
|
||||||
|
"basicscroll.js",
|
||||||
|
"moveto.js",
|
||||||
|
"scrollmagic.js",
|
||||||
|
"scrollreveal.js",
|
||||||
|
"verge.js",
|
||||||
|
"alloyfinger.js",
|
||||||
|
"anime.js",
|
||||||
|
"choreographer.js",
|
||||||
|
"gsap.js",
|
||||||
|
"impulse.js",
|
||||||
|
"mojs.js",
|
||||||
|
"popmotion.js",
|
||||||
|
"rebound.js",
|
||||||
|
"repaintless.js",
|
||||||
|
"shifty.js",
|
||||||
|
"snabbt.js",
|
||||||
|
"snapsvg.js",
|
||||||
|
"vivus.js",
|
||||||
|
"dotjs.js",
|
||||||
|
"handlebars.js",
|
||||||
|
"hogan.js",
|
||||||
|
"mustache.js",
|
||||||
|
"vdo.js",
|
||||||
|
"aja.js",
|
||||||
|
"fetch.js",
|
||||||
|
"qwest.js",
|
||||||
|
"reqwest.js",
|
||||||
|
"superagent.js",
|
||||||
|
"bean.js",
|
||||||
|
"eventemitter2.js",
|
||||||
|
"mitt.js",
|
||||||
|
"elegant.js",
|
||||||
|
"feather.js",
|
||||||
|
"flaticon.js",
|
||||||
|
"fontawesome.js",
|
||||||
|
"fontello.js",
|
||||||
|
"icomoon.js",
|
||||||
|
"ikonate.js",
|
||||||
|
"ionicons.js",
|
||||||
|
"octicons.js",
|
||||||
|
"weloveiconfonts.js",
|
||||||
|
"chromajs.js",
|
||||||
|
"coolors.js",
|
||||||
|
"colorbrewer2.js",
|
||||||
|
"colorhexa.js",
|
||||||
|
"colourco.js",
|
||||||
|
"colormind.js",
|
||||||
|
"kewler.js",
|
||||||
|
"khroma.js",
|
||||||
|
"polychrome.js",
|
||||||
|
"uigradients.js",
|
||||||
|
"forerunnerdb.js",
|
||||||
|
"lokijs.js",
|
||||||
|
"lovefield.js",
|
||||||
|
"pouchdb.js",
|
||||||
|
"rxdb.js",
|
||||||
|
"taffydb.js",
|
||||||
|
"zangodb.js",
|
||||||
|
"parsley.js",
|
||||||
|
"dateformat.js",
|
||||||
|
"flatpickr.js",
|
||||||
|
"instadate.js",
|
||||||
|
"luxon.js",
|
||||||
|
"tinytime.js",
|
||||||
|
"l10ns.js",
|
||||||
|
"globalize.js",
|
||||||
|
"datakit.js",
|
||||||
|
"datalib.js",
|
||||||
|
"gauss.js",
|
||||||
|
"jstat.js",
|
||||||
|
"statkit.js",
|
||||||
|
"stdlib.js",
|
||||||
|
"theoremjs.js",
|
||||||
|
"stealjs.js",
|
||||||
|
"aload.js",
|
||||||
|
"lazysizes.js",
|
||||||
|
"loadxt.js",
|
||||||
|
"unveil.js",
|
||||||
|
"brain.js",
|
||||||
|
"mind.js",
|
||||||
|
"neurojs.js",
|
||||||
|
"rrssb.js",
|
||||||
|
"sharingbuttons.js",
|
||||||
|
"socialcount.js",
|
||||||
|
"moutjs.js",
|
||||||
|
"ramdajs.js",
|
||||||
|
"formstone.js",
|
||||||
|
"tether.js",
|
||||||
|
"upup.js",
|
||||||
|
"iconfont.js",
|
||||||
|
"iconfont2.js",
|
||||||
|
"jwplayer.js",
|
||||||
|
"polyfills.js",
|
||||||
|
"md5.js",
|
||||||
|
"mifihybrid.js"
|
||||||
|
]
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name":"github",
|
||||||
|
"cname":["github.io", "github.map.fastly.net"],
|
||||||
|
"response":["There isn't a GitHub Pages site here.", "For root URLs (like http://example.com/) you must provide an index.html file"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"heroku",
|
||||||
|
"cname":["herokudns.com", "herokussl.com", "herokuapp.com"],
|
||||||
|
"response":["There's nothing here, yet.", "herokucdn.com/error-pages/no-such-app.html", "<title>No such app</title>"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"unbounce",
|
||||||
|
"cname":["unbouncepages.com"],
|
||||||
|
"response":["Sorry, the page you were looking for doesn’t exist.", "The requested URL was not found on this server"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"tumblr",
|
||||||
|
"cname":["tumblr.com"],
|
||||||
|
"response":["There's nothing here.", "Whatever you were looking for doesn't currently exist at this address."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"shopify",
|
||||||
|
"cname":["myshopify.com"],
|
||||||
|
"response":["Sorry, this shop is currently unavailable.", "Only one step left!"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"desk",
|
||||||
|
"cname":["desk.com"],
|
||||||
|
"response":["Please try again or try Desk.com free for 14 days.", "Sorry, We Couldn't Find That Page"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"campaignmonitor",
|
||||||
|
"cname":["createsend.com", "name.createsend.com"],
|
||||||
|
"response":["Double check the URL", "<strong>Trying to access your account?</strong>"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"cargocollective",
|
||||||
|
"cname":["cargocollective.com"],
|
||||||
|
"response":["404 Not Found"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"statuspage",
|
||||||
|
"cname":["statuspage.io"],
|
||||||
|
"response":["Better Status Communication", "You are being <a href=\"https://www.statuspage.io\">redirected"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"amazonaws",
|
||||||
|
"cname":["amazonaws.com"],
|
||||||
|
"response":["NoSuchBucket", "The specified bucket does not exist"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"bitbucket",
|
||||||
|
"cname":["bitbucket.org"],
|
||||||
|
"response":["The page you have requested does not exist","Repository not found"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"smartling",
|
||||||
|
"cname":["smartling.com"],
|
||||||
|
"response":["Domain is not configured"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"acquia",
|
||||||
|
"cname":["acquia.com"],
|
||||||
|
"response":["If you are an Acquia Cloud customer and expect to see your site at this address","The site you are looking for could not be found."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"fastly",
|
||||||
|
"cname":["fastly.net"],
|
||||||
|
"response":["Please check that this domain has been added to a service", "Fastly error: unknown domain"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"pantheon",
|
||||||
|
"cname":["pantheonsite.io"],
|
||||||
|
"response":["The gods are wise", "The gods are wise, but do not know of the site which you seek."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"zendesk",
|
||||||
|
"cname":["zendesk.com"],
|
||||||
|
"response":["Help Center Closed"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"uservoice",
|
||||||
|
"cname":["uservoice.com"],
|
||||||
|
"response":["This UserVoice subdomain is currently available!"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ghost",
|
||||||
|
"cname":["ghost.io"],
|
||||||
|
"response":["The thing you were looking for is no longer here", "The thing you were looking for is no longer here, or never was"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"pingdom",
|
||||||
|
"cname":["stats.pingdom.com"],
|
||||||
|
"response":["pingdom"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"tilda",
|
||||||
|
"cname":["tilda.ws"],
|
||||||
|
"response":["Domain has been assigned"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"wordpress",
|
||||||
|
"cname":["wordpress.com"],
|
||||||
|
"response":["Do you want to register"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"teamwork",
|
||||||
|
"cname":["teamwork.com"],
|
||||||
|
"response":["Oops - We didn't find your site."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"helpjuice",
|
||||||
|
"cname":["helpjuice.com"],
|
||||||
|
"response":["We could not find what you're looking for."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"helpscout",
|
||||||
|
"cname":["helpscoutdocs.com"],
|
||||||
|
"response":["No settings were found for this company:"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"cargo",
|
||||||
|
"cname":["cargocollective.com"],
|
||||||
|
"response":["If you're moving your domain away from Cargo you must make this configuration through your registrar's DNS control panel."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"feedpress",
|
||||||
|
"cname":["redirect.feedpress.me"],
|
||||||
|
"response":["The feed has not been found."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"surge",
|
||||||
|
"cname":["surge.sh"],
|
||||||
|
"response":["project not found"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"surveygizmo",
|
||||||
|
"cname":["privatedomain.sgizmo.com", "privatedomain.surveygizmo.eu", "privatedomain.sgizmoca.com"],
|
||||||
|
"response":["data-html-name"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"mashery",
|
||||||
|
"cname":["mashery.com"],
|
||||||
|
"response":["Unrecognized domain <strong>"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"intercom",
|
||||||
|
"cname":["custom.intercom.help"],
|
||||||
|
"response":["This page is reserved for artistic dogs.","<h1 class=\"headline\">Uh oh. That page doesn’t exist.</h1>"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"webflow",
|
||||||
|
"cname":["proxy.webflow.io"],
|
||||||
|
"response":["<p class=\"description\">The page you are looking for doesn't exist or has been moved.</p>"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"kajabi",
|
||||||
|
"cname":["endpoint.mykajabi.com"],
|
||||||
|
"response":["<h1>The page you were looking for doesn't exist.</h1>"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"thinkific",
|
||||||
|
"cname":["thinkific.com"],
|
||||||
|
"response":["You may have mistyped the address or the page may have moved."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"tave",
|
||||||
|
"cname":["clientaccess.tave.com"],
|
||||||
|
"response":["<h1>Error 404: Page Not Found</h1>"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"wishpond",
|
||||||
|
"cname":["wishpond.com"],
|
||||||
|
"response":["https://www.wishpond.com/404?campaign=true"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"aftership",
|
||||||
|
"cname":["aftership.com"],
|
||||||
|
"response":["Oops.</h2><p class=\"text-muted text-tight\">The page you're looking for doesn't exist."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"aha",
|
||||||
|
"cname":["ideas.aha.io"],
|
||||||
|
"response":["There is no portal here ... sending you back to Aha!"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"brightcove",
|
||||||
|
"cname":["brightcovegallery.com", "gallery.video", "bcvp0rtal.com"],
|
||||||
|
"response":["<p class=\"bc-gallery-error-code\">Error Code: 404</p>"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"bigcartel",
|
||||||
|
"cname":["bigcartel.com"],
|
||||||
|
"response":["<h1>Oops! We couldn’t find that page.</h1>"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"activecompaign",
|
||||||
|
"cname":["activehosted.com"],
|
||||||
|
"response":["alt=\"LIGHTTPD - fly light.\""]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"compaignmonitor",
|
||||||
|
"cname":["createsend.com"],
|
||||||
|
"response":["Double check the URL or <a href=\"mailto:help@createsend.com"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"simplebooklet",
|
||||||
|
"cname":["simplebooklet.com"],
|
||||||
|
"response":["We can't find this <a href=\"https://simplebooklet.com"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"getresponse",
|
||||||
|
"cname":[".gr8.com"],
|
||||||
|
"response":["With GetResponse Landing Pages, lead generation has never been easier"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"vend",
|
||||||
|
"cname":["vendecommerce.com"],
|
||||||
|
"response":["Looks like you've traveled too far into cyberspace."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"jetbrains",
|
||||||
|
"cname":["myjetbrains.com"],
|
||||||
|
"response":["is not a registered InCloud YouTrack.","is not a registered InCloud YouTrack."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"azure",
|
||||||
|
"cname":["azurewebsites.net",
|
||||||
|
".cloudapp.net",
|
||||||
|
".cloudapp.azure.com",
|
||||||
|
".trafficmanager.net",
|
||||||
|
".blob.core.windows.net",
|
||||||
|
".azure-api.net",
|
||||||
|
".azurehdinsight.net",
|
||||||
|
".azureedge.net"],
|
||||||
|
"response":["404 Web Site not found"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"readme",
|
||||||
|
"cname":["readme.io"],
|
||||||
|
"response":["Project doesnt exist... yet!"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"alibaba_oss",
|
||||||
|
"cname":["aliyuncs.com"],
|
||||||
|
"response":["NoSuchBucket", "The specified bucket does not exist."]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
8.8.8.8
|
||||||
|
8.8.4.4
|
||||||
|
9.9.9.9
|
||||||
|
9.9.9.10
|
||||||
|
149.112.112.112
|
||||||
|
4.2.2.1
|
||||||
|
4.2.2.2
|
||||||
|
4.2.2.3
|
||||||
|
4.2.2.4
|
||||||
|
4.2.2.5
|
||||||
|
4.2.2.6
|
||||||
|
1.1.1.1
|
||||||
|
1.0.0.1
|
||||||
|
1.0.0.2
|
||||||
|
1.0.0.3
|
||||||
|
1.0.0.19
|
||||||
|
208.67.222.222
|
||||||
|
208.67.220.220
|
||||||
|
8.26.56.26
|
||||||
|
8.20.247.20
|
||||||
|
84.200.69.80
|
||||||
|
84.200.70.40
|
||||||
|
185.228.168.9
|
||||||
|
185.228.169.9
|
||||||
|
64.6.64.6
|
||||||
|
64.6.65.6
|
||||||
|
198.101.242.72
|
||||||
|
23.253.163.53
|
||||||
|
176.103.130.130
|
||||||
|
176.103.130.131
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
223.5.5.5
|
||||||
|
223.6.6.6
|
||||||
|
114.114.114.114
|
||||||
|
114.114.115.115
|
||||||
|
180.76.76.76
|
||||||
|
119.29.29.29
|
||||||
|
182.254.116.116
|
||||||
@@ -1,102 +1,102 @@
|
|||||||
[
|
[
|
||||||
"_afpovertcp._tcp.",
|
"_afpovertcp._tcp.",
|
||||||
"_aix._tcp.",
|
"_aix._tcp.",
|
||||||
"_autodiscover._tcp.",
|
"_autodiscover._tcp.",
|
||||||
"_caldav._tcp.",
|
"_caldav._tcp.",
|
||||||
"_certificates._tcp.",
|
"_certificates._tcp.",
|
||||||
"_client._smtp.",
|
"_client._smtp.",
|
||||||
"_cmp._tcp.",
|
"_cmp._tcp.",
|
||||||
"_crls._tcp.",
|
"_crls._tcp.",
|
||||||
"_crl._tcp.",
|
"_crl._tcp.",
|
||||||
"_finger._tcp.",
|
"_finger._tcp.",
|
||||||
"_ftp._tcp.",
|
"_ftp._tcp.",
|
||||||
"_gc._tcp.",
|
"_gc._tcp.",
|
||||||
"_h323be._tcp.",
|
"_h323be._tcp.",
|
||||||
"_h323be._udp.",
|
"_h323be._udp.",
|
||||||
"_h323cs._tcp.",
|
"_h323cs._tcp.",
|
||||||
"_h323cs._udp.",
|
"_h323cs._udp.",
|
||||||
"_h323ls._tcp.",
|
"_h323ls._tcp.",
|
||||||
"_h323ls._udp.",
|
"_h323ls._udp.",
|
||||||
"_h323rs._tcp.",
|
"_h323rs._tcp.",
|
||||||
"_hkps._tcp.",
|
"_hkps._tcp.",
|
||||||
"_hkp._tcp.",
|
"_hkp._tcp.",
|
||||||
"_http._tcp.",
|
"_http._tcp.",
|
||||||
"_iax.udp.",
|
"_iax.udp.",
|
||||||
"_imaps._tcp.",
|
"_imaps._tcp.",
|
||||||
"_imap._tcp.",
|
"_imap._tcp.",
|
||||||
"_jabber-client._tcp.",
|
"_jabber-client._tcp.",
|
||||||
"_jabber-client._udp.",
|
"_jabber-client._udp.",
|
||||||
"_jabber._tcp.",
|
"_jabber._tcp.",
|
||||||
"_jabber._udp.",
|
"_jabber._udp.",
|
||||||
"_kerberos-adm._tcp.",
|
"_kerberos-adm._tcp.",
|
||||||
"_kerberos._tcp.",
|
"_kerberos._tcp.",
|
||||||
"_kerberos._tcp.dc._msdcs.",
|
"_kerberos._tcp.dc._msdcs.",
|
||||||
"_kerberos._udp.",
|
"_kerberos._udp.",
|
||||||
"_kpasswd._tcp.",
|
"_kpasswd._tcp.",
|
||||||
"_kpasswd._udp.",
|
"_kpasswd._udp.",
|
||||||
"_ldap._tcp.",
|
"_ldap._tcp.",
|
||||||
"_ldap._tcp.dc._msdcs.",
|
"_ldap._tcp.dc._msdcs.",
|
||||||
"_ldap._tcp.gc._msdcs.",
|
"_ldap._tcp.gc._msdcs.",
|
||||||
"_ldap._tcp.pdc._msdcs.",
|
"_ldap._tcp.pdc._msdcs.",
|
||||||
"_msdcs.",
|
"_msdcs.",
|
||||||
"_mysqlsrv._tcp.",
|
"_mysqlsrv._tcp.",
|
||||||
"_nntp._tcp.",
|
"_nntp._tcp.",
|
||||||
"_ntp._udp.",
|
"_ntp._udp.",
|
||||||
"_ocsp._tcp.",
|
"_ocsp._tcp.",
|
||||||
"_pgpkeys._tcp.",
|
"_pgpkeys._tcp.",
|
||||||
"_pgprevokations._tcp.",
|
"_pgprevokations._tcp.",
|
||||||
"_PKIXREP._tcp.",
|
"_PKIXREP._tcp.",
|
||||||
"_pop3s._tcp.",
|
"_pop3s._tcp.",
|
||||||
"_pop3._tcp.",
|
"_pop3._tcp.",
|
||||||
"_sipfederationtls._tcp.",
|
"_sipfederationtls._tcp.",
|
||||||
"_sipinternal._tcp.",
|
"_sipinternal._tcp.",
|
||||||
"_sipinternaltls._tcp.",
|
"_sipinternaltls._tcp.",
|
||||||
"_sips._tcp.",
|
"_sips._tcp.",
|
||||||
"_sip._tcp.",
|
"_sip._tcp.",
|
||||||
"_sip._tls.",
|
"_sip._tls.",
|
||||||
"_sip._udp.",
|
"_sip._udp.",
|
||||||
"_smtp._tcp.",
|
"_smtp._tcp.",
|
||||||
"_ssh._tcp.",
|
"_ssh._tcp.",
|
||||||
"_stun._tcp.",
|
"_stun._tcp.",
|
||||||
"_stun._udp.",
|
"_stun._udp.",
|
||||||
"_svcp._tcp.",
|
"_svcp._tcp.",
|
||||||
"_tcp.",
|
"_tcp.",
|
||||||
"_telnet._tcp.",
|
"_telnet._tcp.",
|
||||||
"_test._tcp.",
|
"_test._tcp.",
|
||||||
"_tls.",
|
"_tls.",
|
||||||
"_udp.",
|
"_udp.",
|
||||||
"_vlmcs._tcp.",
|
"_vlmcs._tcp.",
|
||||||
"_vlmcs._udp.",
|
"_vlmcs._udp.",
|
||||||
"_whois._tcp.",
|
"_whois._tcp.",
|
||||||
"_wpad._tcp.",
|
"_wpad._tcp.",
|
||||||
"_xmpp-client._tcp.",
|
"_xmpp-client._tcp.",
|
||||||
"_xmpp-client._udp.",
|
"_xmpp-client._udp.",
|
||||||
"_xmpp-server._tcp.",
|
"_xmpp-server._tcp.",
|
||||||
"_xmpp-server._udp.",
|
"_xmpp-server._udp.",
|
||||||
"_https._tcp.",
|
"_https._tcp.",
|
||||||
"_imap.tcp.",
|
"_imap.tcp.",
|
||||||
"_kerberos.tcp.dc._msdcs.",
|
"_kerberos.tcp.dc._msdcs.",
|
||||||
"_ldap._tcp.ForestDNSZones.",
|
"_ldap._tcp.ForestDNSZones.",
|
||||||
"_submission._tcp.",
|
"_submission._tcp.",
|
||||||
"_caldavs._tcp.",
|
"_caldavs._tcp.",
|
||||||
"_carddav._tcp.",
|
"_carddav._tcp.",
|
||||||
"_carddavs._tcp.",
|
"_carddavs._tcp.",
|
||||||
"_x-puppet._tcp.",
|
"_x-puppet._tcp.",
|
||||||
"_x-puppet-ca._tcp.",
|
"_x-puppet-ca._tcp.",
|
||||||
"_domainkey.",
|
"_domainkey.",
|
||||||
"_pkixrep._tcp.",
|
"_pkixrep._tcp.",
|
||||||
"_cisco-phone-http.",
|
"_cisco-phone-http.",
|
||||||
"_cisco-phone-tftp.",
|
"_cisco-phone-tftp.",
|
||||||
"_cisco-uds._tcp.",
|
"_cisco-uds._tcp.",
|
||||||
"_ciscowtp._tcp.",
|
"_ciscowtp._tcp.",
|
||||||
"_collab-edge._tls.",
|
"_collab-edge._tls.",
|
||||||
"_cuplogin._tcp.",
|
"_cuplogin._tcp.",
|
||||||
"_client._smtp._tcp.",
|
"_client._smtp._tcp.",
|
||||||
"_sftp._tcp.",
|
"_sftp._tcp.",
|
||||||
"_h323rs._udp.",
|
"_h323rs._udp.",
|
||||||
"_sql._tcp.",
|
"_sql._tcp.",
|
||||||
"_sip._tcp.internal.",
|
"_sip._tcp.internal.",
|
||||||
"_snmp._udp.",
|
"_snmp._udp.",
|
||||||
"_rdp._tcp.",
|
"_rdp._tcp.",
|
||||||
"_xmpp-server._udp."
|
"_xmpp-server._udp."
|
||||||
]
|
]
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
如果在安装依赖过程遇到编译某个依赖库失败时可以尝试以下方法:
|
|
||||||
|
|
||||||
1. 到提供编译好的whl文件的第三方平台,找到对应库手动下载安装。第三方平台平台有:
|
|
||||||
* [https://www.lfd.uci.edu/~gohlke/pythonlibs](https://www.lfd.uci.edu/~gohlke/pythonlibs)
|
|
||||||
* [https://pythonwheels.com/](https://pythonwheels.com/)
|
|
||||||
|
|
||||||
选择好对应版本执行以下命令手动安装。举个例子,当编译pycares时失败时,找到[https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycares](https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycares),由于我的系统是Windows 10 64位,使用的Python 3.7便下载`pycares‑3.0.0‑cp37‑cp37m‑win_amd64.whl`(一般来说下载最新版本的),然后手动安装:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip3 install pycares‑3.0.0‑cp37‑cp37m‑win_amd64.whl
|
|
||||||
```
|
|
||||||
|
|
||||||
2. 到库的项目地址issues和wiki等找找有没有解决方法,如果没有就给他们提issues发邮件😜。
|
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
# 更新日志
|
||||||
|
OneForAll的所有值得注意的更改都将记录在此文件中。
|
||||||
|
|
||||||
|
OneForAll的更新日志格式基于[Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
|
||||||
|
|
||||||
|
OneForAll遵守[语义化版本格式](https://semver.org/)。
|
||||||
|
|
||||||
|
# Unreleased
|
||||||
|
|
||||||
|
# Released
|
||||||
|
## [0.4.5](https://github.com/shmilylty/oneforall/releases/tag/v0.4.5) - 2022-07-10
|
||||||
|
- 修复了#254
|
||||||
|
|
||||||
|
## [0.4.4](https://github.com/shmilylty/oneforall/releases/tag/v0.4.4) - 2022-07-03
|
||||||
|
- 修复了多个已知问题
|
||||||
|
- 添加了多个查询接口
|
||||||
|
- 添加对M1芯片的Mac支持
|
||||||
|
|
||||||
|
## [0.4.3](https://github.com/shmilylty/oneforall/releases/tag/v0.4.3) - 2020-11-29
|
||||||
|
- 修复了已知问题
|
||||||
|
- 更新了文档
|
||||||
|
|
||||||
|
## [0.4.2](https://github.com/shmilylty/oneforall/releases/tag/v0.4.2) - 2020-11-23
|
||||||
|
- 添加了数据表初始化处理流程,修复了#163中出现的问题。
|
||||||
|
|
||||||
|
## [0.4.1](https://github.com/shmilylty/oneforall/releases/tag/v0.4.1) - 2020-11-18
|
||||||
|
- 修复了数字开头主域(如58.com)出现数据库报错的问题
|
||||||
|
|
||||||
|
## [0.4.0](https://github.com/shmilylty/oneforall/releases/tag/v0.4.0) - 2020-11-18
|
||||||
|
- 重构了子域请求模块,解决了内存占用过大问题
|
||||||
|
- 新增了子域置换模块,能从现有的子域发现更多新子域
|
||||||
|
- 新增了数据富化模块,富化出更多有用的信息
|
||||||
|
- 新增了finder模块,能从响应体和JS及跳转历史收集子域
|
||||||
|
- 重构了泛解析探测,泛解析探测更加准确
|
||||||
|
- 实现了配置插拔式设计
|
||||||
|
- 实现了版本更新检查、运行环境检查、网络环境检查
|
||||||
|
- 优化了子域爆破模块
|
||||||
|
- 优化了泛解析处理
|
||||||
|
- 优化了子域字典
|
||||||
|
- 删除和优化了部分收集模块
|
||||||
|
- 修复了一些反馈的bug
|
||||||
|
- 更新了文档
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/shmilylty/oneforall/releases/tag/v0.3.0) - 2020-05-13
|
||||||
|
- 重构了项目目录结构
|
||||||
|
- 修改了输出显示为英文
|
||||||
|
- 优化了泛解析处理
|
||||||
|
- 优化了部分收集模块
|
||||||
|
- 新增了静默级别的日志输出
|
||||||
|
- 新增了超大爆破压缩包字典
|
||||||
|
- 新增了利用NSEC记录遍历DNS域模块
|
||||||
|
- 新增了sublist3r接口查询模块
|
||||||
|
- 现在`docker pull shmilylty/oneforall`是自动构建的
|
||||||
|
- 修复了一些反馈的bug
|
||||||
|
- 更新了文档
|
||||||
|
|
||||||
|
## [0.2.0](https://github.com/shmilylty/oneforall/releases/tag/v0.2.0) - 2020-04-27
|
||||||
|
- 重构子域爆破和解析模块 改用massdns 一般情况下可以达到10000pps 速度非常快
|
||||||
|
- 优化泛解析处理
|
||||||
|
- 优化部分子域收集模块
|
||||||
|
- 新增了爆破字典
|
||||||
|
- 新增了github_api和rapiddns收集模块
|
||||||
|
- 修复了一些bug
|
||||||
|
- 更新了文档
|
||||||
|
|
||||||
|
## [0.1.0](https://github.com/shmilylty/oneforall/releases/tag/v0.1.0) - 2020-03-02
|
||||||
|
- 重构OneForAll入口
|
||||||
|
- 添加1个新的子域收集模块
|
||||||
|
- 添加了查询类型type和子域层数level两个结果字段
|
||||||
|
- 调整项目结构
|
||||||
|
- 优化个别收集模块
|
||||||
|
- 优化子域爆破字典
|
||||||
|
- 优化响应体解码处理
|
||||||
|
- 更新说明文档
|
||||||
|
- 修复已知bug
|
||||||
|
|
||||||
|
## [0.0.9](https://github.com/shmilylty/oneforall/releases/tag/v0.0.9) - 2020-02-20
|
||||||
|
- 重构子域解析模块
|
||||||
|
- 添加4个新的子域收集模块
|
||||||
|
- 添加了Docker部署
|
||||||
|
- 优化配置参数和收集模块
|
||||||
|
- 优化子域爆破字典和默认参数
|
||||||
|
- 优化响应体解码处理
|
||||||
|
- 更新说明文档
|
||||||
|
- 修复已知bug
|
||||||
|
|
||||||
|
## [0.0.8](https://github.com/shmilylty/oneforall/releases/tag/v0.0.8) - 2019-10-30
|
||||||
|
- 添加新子域监控功能
|
||||||
|
- 优化子域爆破字典和默认参数
|
||||||
|
- 修复端口重复问题
|
||||||
|
- 移除aiodns依赖
|
||||||
|
|
||||||
|
## [0.0.7](https://github.com/shmilylty/oneforall/releases/tag/v0.0.7) - 2019-10-18
|
||||||
|
- 修复一些已知问题
|
||||||
|
- 添加百度云观测接口
|
||||||
|
- 添加添加英文Readme文档
|
||||||
|
- 更新有关文档
|
||||||
|
- 优化标题获取
|
||||||
|
- 更新依赖
|
||||||
|
|
||||||
|
## [0.0.6](https://github.com/shmilylty/oneforall/releases/tag/v0.0.6) - 2019-08-27
|
||||||
|
- 修复一些已知问题
|
||||||
|
- 添加PassiveDNS查询和Github子域搜索模块
|
||||||
|
- 优化FoFa和BufferOver收集模块
|
||||||
|
- 更新有关文档
|
||||||
|
- 更新依赖
|
||||||
|
|
||||||
|
## [0.0.5](https://github.com/shmilylty/oneforall/releases/tag/v0.0.5) - 2019-08-19
|
||||||
|
- 修复一些已知Bugs
|
||||||
|
- 优化各子域收集接口并添加新的子域收集接口
|
||||||
|
- 添加子域DNS解析和子域HTTP探测进度条
|
||||||
|
- 添加子域接管风险检查模块及其使用说明
|
||||||
|
- 更新OneForAll依赖
|
||||||
|
|
||||||
|
## [0.0.4](https://github.com/shmilylty/oneforall/releases/tag/v0.0.4) - 2019-08-11
|
||||||
|
### 修复
|
||||||
|
- 修复一些已知Bugs
|
||||||
|
|
||||||
|
## [0.0.3](https://github.com/shmilylty/oneforall/releases/tag/v0.0.3) - 2019-08-08
|
||||||
|
### 修改
|
||||||
|
- 代码PEP8格式化
|
||||||
|
### 修改
|
||||||
|
- 修改一些已知Bugs
|
||||||
|
|
||||||
|
## [0.0.2](https://github.com/shmilylty/oneforall/releases/tag/v0.0.2) - 2019-08-04
|
||||||
|
### 新增
|
||||||
|
- 新增有关文档
|
||||||
|
### 修改
|
||||||
|
- 修改有关日志输出格式和信息
|
||||||
|
### 修复
|
||||||
|
- 升级fire库版本解决运行报错问题
|
||||||
|
### 移除
|
||||||
|
- 移除brotlipy依赖
|
||||||
|
|
||||||
|
|
||||||
|
## [0.0.1](https://github.com/shmilylty/oneforall/releases/tag/v0.0.1) - 2019-08-02
|
||||||
|
### 新增
|
||||||
|
- 新增检查crossdomain.xml收集子域功能
|
||||||
|
- 新增检查域名证书收集子域功能
|
||||||
|
- 新增检查内容安全策略头收集子域功能
|
||||||
|
- 新增域传送利用功能
|
||||||
|
- 新增子域收集功能(搜索引擎,DNS数据集,证书透明度,网上爬虫档案)
|
||||||
|
- 新增子域爆破功能
|
||||||
|
- 新增数据库导出功能
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
# 收集模块说明 #
|
# 收集模块说明 #
|
||||||
|
|
||||||
如果要使用通过API收集子域的模块请先到[config.py](../oneforall/config.py)配置相关信息,大多平台的API都是可以注册账号免费获取的。
|
如果要使用通过API收集子域的模块请先到[api.py](../config/api.py)配置相关信息,大多平台的API都是可以注册账号免费获取的。
|
||||||
|
|
||||||
如果你指定使用某些模块可以在[config.py](../oneforall/config.py)中设置:
|
如果你指定使用某些模块可以在[api.py](../config/api.py)中设置:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
enable_all_module = False # 不开启所有模块
|
enable_all_module = False # 不开启所有模块
|
||||||
enable_partial_module = [('modules.search', 'ask'),('modules.search', 'baidu')] # 只使用ask和baidu搜索引擎收集子域
|
enable_partial_module = [('modules.search', 'ask'), ('modules.search', 'baidu')] # 只使用ask和baidu搜索引擎收集子域
|
||||||
```
|
```
|
||||||
|
|
||||||
如果你指定使用某些模块使用代理可以在[config.py](../oneforall/config.py)中设置:
|
如果你指定使用某些模块使用代理可以在[api.py](../config/api.py)中设置:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
enable_proxy = True # 使用代理
|
enable_proxy = True # 使用代理
|
||||||
@@ -23,12 +23,12 @@ proxy_partial_module = ['GoogleQuery', 'AskSearch'] # 只代理GoogleQuery和As
|
|||||||
|
|
||||||
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
|
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
|
||||||
| ----------- | ------------ | ----------- | -------------------------------------------------- |
|
| ----------- | ------------ | ----------- | -------------------------------------------------- |
|
||||||
| censys_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| censys_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| certdb_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
|
||||||
| certspotter | 否 | 否 | |
|
| certspotter | 否 | 否 | |
|
||||||
| crtsh | 否 | 否 | |
|
| crtsh | 否 | 否 | |
|
||||||
| entrust | 否 | 否 | |
|
| entrust | 否 | 否 | |
|
||||||
| google | 是 | 否 | |
|
| google | 是 | 否 | |
|
||||||
|
| spyse_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
|
|
||||||
|
|
||||||
2. 常规检查收集子域(目前有4个模块:域传送漏洞利用`axfr`,检查跨域策略文件`cdx`,检查HTTPS证书`cert`,检查内容安全策略`csp`,后续会添加检查NSEC记录,NSEC3记录等模块)
|
2. 常规检查收集子域(目前有4个模块:域传送漏洞利用`axfr`,检查跨域策略文件`cdx`,检查HTTPS证书`cert`,检查内容安全策略`csp`,后续会添加检查NSEC记录,NSEC3记录等模块)
|
||||||
@@ -48,58 +48,67 @@ proxy_partial_module = ['GoogleQuery', 'AskSearch'] # 只代理GoogleQuery和As
|
|||||||
| archivecrawl | 否 | 否 | |
|
| archivecrawl | 否 | 否 | |
|
||||||
| commoncrawl | 否 | 否 | |
|
| commoncrawl | 否 | 否 | |
|
||||||
|
|
||||||
4. 利用DNS数据集收集子域(目前有16个模块:`binaryedge_api`, `circl_api`, `hackertarget`, `riddler`, `bufferover`, `dnsdb`, `ipv4info`, `robtex`, `chinaz`, `dnsdb_api`, `netcraft`, `securitytrails_api`, `chinaz_api`, `dnsdumpster`, `ptrarchive`, `sitedossier`)
|
4. 利用DNS数据集收集子域(目前有24个模块:`cebaidu`, `binaryedge_api`, `circl_api`, `cloudflare`, `hackertarget`, `riddler`, `bufferover`, `dnsdb`, `ipv4info`, `robtex`, `chinaz`, `dnsdb_api`, `netcraft`, `securitytrails_api`, `chinaz_api`, `dnsdumpster`, `passivedns_api`, `ptrarchive`, `sitedossier`,`threatcrowd`)
|
||||||
|
|
||||||
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
|
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
|
||||||
| ------------------ | ------------ | ----------- | -------------------------------------------------- |
|
| ------------------ | ------------ | ----------- | -------------------------------------------------- |
|
||||||
| binaryedge_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| binaryedge_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| bufferover | 否 | 否 | |
|
| bufferover | 否 | 否 | |
|
||||||
|
| cebaidu | 否 | 否 | |
|
||||||
| chinaz | 否 | 否 | |
|
| chinaz | 否 | 否 | |
|
||||||
| chinaz_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| chinaz_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| circl_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| circl_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
|
| cloudflare_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| dnsdb | 否 | 否 | |
|
| dnsdb | 否 | 否 | |
|
||||||
| dnsdb_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| dnsdb_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| dnsdumpster | 否 | 否 | |
|
| dnsdumpster | 否 | 否 | |
|
||||||
| hackertarget | 否 | 否 | |
|
| hackertarget | 否 | 否 | |
|
||||||
| ipv4info | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| ip138 | 否 | 否 | |
|
||||||
|
| ipv4info | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| netcraft | 否 | 否 | |
|
| netcraft | 否 | 否 | |
|
||||||
| ptrarchive | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| passivedns_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| riddler | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| ptrarchive | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
|
| riddler | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| robtex | 否 | 否 | |
|
| robtex | 否 | 否 | |
|
||||||
| securitytrails_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| securitytrails_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| sitedossier | 否 | 否 | |
|
| sitedossier | 否 | 否 | |
|
||||||
|
| threatcrowd | 否 | 否 | |
|
||||||
|
| ximcx | 否 | 否 | |
|
||||||
5. 利用DNS查询收集子域(目前有1个模块:通过枚举常见的SRV记录并做查询来收集子域`srv`,该模块还有待添加和完善)
|
5. 利用DNS查询收集子域(目前有1个模块:通过枚举常见的SRV记录并做查询来收集子域`srv`,该模块还有待添加和完善)
|
||||||
|
|
||||||
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
|
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
|
||||||
| -------- | ------------ | ----------- | ----------------------------- |
|
| -------- | ------------ | ----------- | ----------------------------- |
|
||||||
| srv | 否 | 否 | 枚举域名常见的SRV记录发现子域 |
|
| srv | 否 | 否 | 枚举域名常见的SRV记录发现子域 |
|
||||||
6. 利用威胁平台数据收集子域(目前有5个模块:`riskiq_api`,`threatbook_api`,`threatminer`,`virustotal`,`virustotal_api`该模块还有待添加和完善)
|
6. 利用威胁平台数据收集子域(目前有6个模块:`riskiq_api`,`threatbook_api`,`threatminer`,`virustotal`,`virustotal_api`该模块还有待添加和完善)
|
||||||
|
|
||||||
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
|
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
|
||||||
| -------------- | ------------ | ----------- | -------------------------------------------------- |
|
| -------------- | ------------ | ----------- | ------------------------------------------------- |
|
||||||
| riskiq_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| alienvault | 否 | 否 | |
|
||||||
| threatbook_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| riskiq_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
|
| threatbook_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| threatminer | 否 | 否 | |
|
| threatminer | 否 | 否 | |
|
||||||
| virustotal | 否 | 否 | |
|
| virustotal | 否 | 否 | |
|
||||||
| virustotal_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| virustotal_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
7. 利用搜索引擎发现子域(目前有15个模块:`ask`, `bing_api`, `fofa_api`, `shodan_api`, `yahoo`, `baidu`, `duckduckgo`, `google`, `so`, `yandex`, `bing`, `exalead`, `google_api`, `sogou`, `zoomeye_api`)
|
7. 利用搜索引擎发现子域(目前有16个模块:`ask`, `bing_api`, `fofa_api`, `shodan_api`, `yahoo`, `baidu`, `duckduckgo`, `github`, `google`, `so`, `yandex`, `bing`, `exalead`, `google_api`, `sogou`, `zoomeye_api`)
|
||||||
|
|
||||||
除特殊搜索引擎,通用的搜索引擎都支持自动排除搜索,全量搜索,递归搜索。
|
除特殊搜索引擎,通用的搜索引擎都支持自动排除搜索,全量搜索,递归搜索。
|
||||||
|
|
||||||
| 模块 | 是否需要代理 | 是否需要API | 其他说明 |
|
| 模块 | 是否需要代理 | 是否需要API | 其他说明 |
|
||||||
| ----------- | ---------------------- | ----------- | -------------------------------------------------- |
|
| ----------- | ---------------------- | -----------| ----------------------------------------------------------- |
|
||||||
| ask | 是 | 否 | |
|
| ask | 是 | 否 | |
|
||||||
| baidu | 否 | 否 | |
|
| baidu | 否 | 否 | |
|
||||||
| bing | 否 | 否 | |
|
| bing | 否 | 否 | |
|
||||||
| bing_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| bing_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| duckduckgo | 是 | 否 | |
|
| duckduckgo | 是 | 否 | |
|
||||||
| exalead | 否,最好使用国外代理。 | 否 | |
|
| exalead | 否,最好使用国外代理。 | 否 | |
|
||||||
| fofa_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| fofa_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| google | 是 | 否 | |
|
| gitee | 否 | 否 | |
|
||||||
| google_api | 是 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| github | 否 | 否 | 在[api.py](../config/api.py)设置Github邮件名和密码。 |
|
||||||
| shodan_api | 否,最好使用国外代理。 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| google | 是 | 否 | |
|
||||||
| so | 否 | 否 | |
|
| google_api | 是 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| sogou | 否 | 否 | |
|
| shodan_api | 否,最好使用国外代理。 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
| yahoo | 是 | 否 | |
|
| so | 否 | 否 | |
|
||||||
| yandex | 是 | 否 | |
|
| sogou | 否 | 否 | |
|
||||||
| zoomeye_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
|
| yahoo | 是 | 否 | |
|
||||||
|
| yandex | 是 | 否 | |
|
||||||
|
| zoomeye_api | 否 | 是 | API使用和申请见[api.py](../config/api.py) |
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# OneForAll贡献者
|
||||||
|
|
||||||
|
* **[Jing Ling](https://github.com/shmilylty)**
|
||||||
|
* 核心开发
|
||||||
|
|
||||||
|
* **[Black Star](https://github.com/blackstar24)** **[Echocipher](https://github.com/Echocipher)** **[JrDw0](https://github.com/JrDw0)**
|
||||||
|
* 模块贡献
|
||||||
|
|
||||||
|
* **[JrDw0](https://github.com/JrDw0)**
|
||||||
|
* 项目翻译
|
||||||
|
|
||||||
|
* **[iceMatcha](https://github.com/iceMatcha)** **[mikuKeeper](https://github.com/mikuKeeper)**
|
||||||
|
* 工具测试
|
||||||
|
|
||||||
|
* **[奶茶](https://github.com/Tardis07)** **[boy-hack](https://github.com/boy-hack)**
|
||||||
|
* Docker构建
|
||||||
|
|
||||||
|
* **Anyone**
|
||||||
|
* 工具反馈
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
关于子域字典来源的说明:
|
||||||
|
1. 开源子域收集工具中的部分高频子域名字字典。
|
||||||
|
2. 网上有关服务商公布的最流行子域列表。
|
||||||
|
* [DNSPod](https://github.com/DNSPod/oh-my-free-data)
|
||||||
|
3. 网上有关安全研究人员关于对全网常见子域的研究结果。
|
||||||
|
* [the_most_popular_subdomains_on_the_internet](https://bitquark.co.uk/blog/2016/02/29/the_most_popular_subdomains_on_the_internet)
|
||||||
|
* [The most popular subdomains on the internet (2017 edition)](https://medium.com/@cmeister2/the-most-popular-subdomains-on-the-internet-2017-edition-a6b9c8a20fd8)
|
||||||
|
4. 网上有关安全研究人员关于对证书透明度中常见子域提取的结果。
|
||||||
|
* [Top 100000 Subdomains in Certificate Transparency](https://github.com/internetwache/CT_subdomains)
|
||||||
|
5. 常见业务命名规律:
|
||||||
|
* 单字母、单字母+单数字、双字母、双字母+单数字、双字母+双数字、三字母、四字母;
|
||||||
|
* 单数字、双数字、三数字;
|
||||||
|
6. 在公司或者说在DevOps中常见的工具和软件名称。
|
||||||
|
7. 常见中文单词拼音和常见英文单词。
|
||||||
|
8. 从以上获取的字典做优化排序以及脏数据去除处理。
|
||||||
|
9. 非常欢迎你贡献更好的字典。
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
```
|
||||||
|
D:.
|
||||||
|
| .gitignore
|
||||||
|
| .travis.yml
|
||||||
|
| brute.py 可以单独运行的子域爆破模块
|
||||||
|
| collect.py 各个收集模块上层调用
|
||||||
|
| dbexport.py 可以单独运行的数据库导出模块
|
||||||
|
| Dockerfile
|
||||||
|
| LICENSE
|
||||||
|
| oneforall.py OneForAll主入口
|
||||||
|
| Pipfile
|
||||||
|
| Pipfile.lock
|
||||||
|
| README.en.md
|
||||||
|
| README.md
|
||||||
|
| requirements.txt
|
||||||
|
| takeover.py 可以单独运行的子域接口风险检查模块
|
||||||
|
| _config.yml
|
||||||
|
|
|
||||||
|
+---.github
|
||||||
|
| +---ISSUE_TEMPLATE
|
||||||
|
| | bug_report.md
|
||||||
|
| | bug_report_zh.md
|
||||||
|
| | custom.md
|
||||||
|
| | feature_request.md
|
||||||
|
| |
|
||||||
|
| \---workflows
|
||||||
|
| test.yml
|
||||||
|
|
|
||||||
|
|
|
||||||
|
+---common 公共调用模块
|
||||||
|
| crawl.py
|
||||||
|
| database.py
|
||||||
|
| domain.py
|
||||||
|
| lookup.py
|
||||||
|
| module.py
|
||||||
|
| query.py
|
||||||
|
| request.py
|
||||||
|
| resolve.py
|
||||||
|
| search.py
|
||||||
|
| utils.py
|
||||||
|
| __init__.py
|
||||||
|
|
|
||||||
|
+---config 配置目录
|
||||||
|
| api.py 部分收集模块的API配置文件
|
||||||
|
| log.py 日志模块配置文件
|
||||||
|
| setting.py OneForAll主要配置文件
|
||||||
|
|
|
||||||
|
+---data 存放一些所需数据
|
||||||
|
| authoritative_dns.txt 临时存放开启了泛解析域名的权威DNS名称服务器IP地址
|
||||||
|
| subnames_big.7z 子域爆破超大字典
|
||||||
|
| nameservers_cn.txt 中国主流名称服务器IP地址
|
||||||
|
| fingerprints.json 检查子域接管风险的指纹
|
||||||
|
| nameservers.txt 全球主流名称服务器IP地址
|
||||||
|
| subnames_next.txt 下一层子域字典
|
||||||
|
| public_suffix_list.dat 顶级域名后缀
|
||||||
|
| srv_prefixes.json 常见SRV记录前缀名
|
||||||
|
| subnames.txt 子域爆破常见字典
|
||||||
|
|
|
||||||
|
+---docs 有关文档
|
||||||
|
| changes.md
|
||||||
|
| collection_modules.md
|
||||||
|
| contributors.md
|
||||||
|
| installation_dependency.md
|
||||||
|
| todo.md
|
||||||
|
| troubleshooting.md
|
||||||
|
| usage_example.svg
|
||||||
|
| usage_help.en.md
|
||||||
|
| usage_help.md
|
||||||
|
|
|
||||||
|
+---images
|
||||||
|
| Database.png
|
||||||
|
| Donate.png
|
||||||
|
| Result.png
|
||||||
|
|
|
||||||
|
+---modules
|
||||||
|
| +---autotake 自动接管模块
|
||||||
|
| | github.py
|
||||||
|
| |
|
||||||
|
| +---certificates 利用证书透明度收集子域模块
|
||||||
|
| | censys_api.py
|
||||||
|
| | certspotter.py
|
||||||
|
| | crtsh.py
|
||||||
|
| | entrust.py
|
||||||
|
| | google.py
|
||||||
|
| | spyse_api.py
|
||||||
|
| |
|
||||||
|
| +---check 常规检查收集子域模块
|
||||||
|
| | axfr.py
|
||||||
|
| | cdx.py
|
||||||
|
| | cert.py
|
||||||
|
| | csp.py
|
||||||
|
| | robots.py
|
||||||
|
| | sitemap.py
|
||||||
|
| |
|
||||||
|
| +---crawl 利用网上爬虫档案收集子域模块
|
||||||
|
| | archivecrawl.py
|
||||||
|
| | commoncrawl.py
|
||||||
|
| |
|
||||||
|
| +---datasets 利用DNS数据集收集子域模块
|
||||||
|
| | binaryedge_api.py
|
||||||
|
| | bufferover.py
|
||||||
|
| | cebaidu.py
|
||||||
|
| | chinaz.py
|
||||||
|
| | chinaz_api.py
|
||||||
|
| | circl_api.py
|
||||||
|
| | dnsdb_api.py
|
||||||
|
| | dnsdumpster.py
|
||||||
|
| | hackertarget.py
|
||||||
|
| | ip138.py
|
||||||
|
| | ipv4info_api.py
|
||||||
|
| | netcraft.py
|
||||||
|
| | passivedns_api.py
|
||||||
|
| | ptrarchive.py
|
||||||
|
| | qianxun.py
|
||||||
|
| | rapiddns.py
|
||||||
|
| | riddler.py
|
||||||
|
| | robtex.py
|
||||||
|
| | securitytrails_api.py
|
||||||
|
| | sitedossier.py
|
||||||
|
| | threatcrowd.py
|
||||||
|
| | wzpc.py
|
||||||
|
| | ximcx.py
|
||||||
|
| |
|
||||||
|
| +---dnsquery 利用DNS查询收集子域模块
|
||||||
|
| | mx.py
|
||||||
|
| | ns.py
|
||||||
|
| | soa.py
|
||||||
|
| | srv.py
|
||||||
|
| | txt.py
|
||||||
|
| |
|
||||||
|
| +---intelligence 利用威胁情报平台数据收集子域模块
|
||||||
|
| | alienvault.py
|
||||||
|
| | riskiq_api.py
|
||||||
|
| | threatbook_api.py
|
||||||
|
| | threatminer.py
|
||||||
|
| | virustotal.py
|
||||||
|
| | virustotal_api.py
|
||||||
|
| |
|
||||||
|
| \---search 利用搜索引擎发现子域模块
|
||||||
|
| ask.py
|
||||||
|
| baidu.py
|
||||||
|
| bing.py
|
||||||
|
| bing_api.py
|
||||||
|
| exalead.py
|
||||||
|
| fofa_api.py
|
||||||
|
| gitee.py
|
||||||
|
| github_api.py
|
||||||
|
| google.py
|
||||||
|
| google_api.py
|
||||||
|
| shodan_api.py
|
||||||
|
| so.py
|
||||||
|
| sogou.py
|
||||||
|
| yahoo.py
|
||||||
|
| yandex.py
|
||||||
|
| zoomeye_api.py
|
||||||
|
|
|
||||||
|
+---results 结果目录
|
||||||
|
+---test 测试目录
|
||||||
|
| example.py
|
||||||
|
|
|
||||||
|
\---thirdparty 存放要调用的三方工具
|
||||||
|
\---massdns
|
||||||
|
| LICENSE
|
||||||
|
| massdns_darwin_x86_64
|
||||||
|
| massdns_linux_i686
|
||||||
|
| massdns_linux_x86_64
|
||||||
|
| README.md
|
||||||
|
|
|
||||||
|
\---windows
|
||||||
|
+---x64
|
||||||
|
| cygwin1.dll
|
||||||
|
| massdns_windows_amd64.exe
|
||||||
|
|
|
||||||
|
\---x86
|
||||||
|
cyggcc_s-1.dll
|
||||||
|
cygwin1.dll
|
||||||
|
massdns_windows_i686.exe
|
||||||
|
```
|
||||||
@@ -0,0 +1,309 @@
|
|||||||
|
# OneForAll
|
||||||
|
|
||||||
|
[](https://travis-ci.org/shmilylty/OneForAll)
|
||||||
|
[](https://codecov.io/gh/shmilylty/OneForAll)
|
||||||
|
[](https://codeclimate.com/github/shmilylty/OneForAll/maintainability)
|
||||||
|
[](https://github.com/shmilylty/OneForAll/tree/master/LICENSE)
|
||||||
|
[](https://github.com/shmilylty/OneForAll/tree/master/)
|
||||||
|
[](https://github.com/shmilylty/OneForAll/releases)
|
||||||
|
|
||||||
|
👊**OneForAll is a powerful subdomain integration tool** 📝[中文文档](https://github.com/shmilylty/OneForAll/tree/master/README.md)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 🚀Start Guide
|
||||||
|
|
||||||
|
📢 Please read this document to help you start quickly!
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>🐍Installation requirements</b></summary>
|
||||||
|
|
||||||
|
OneForAll is developed and tested based on [Python 3.6.0](https://www.python.org/downloads/release/python-360/), OneForAll needs to be higher than Python 3.6.0 to run.
|
||||||
|
For more information on installing the Python environment, please read [Python 3 Installation Guide](https://pythonguidecn.readthedocs.io/zh/latest/starting/installation.html#python-3).
|
||||||
|
|
||||||
|
After installation python, run the following command to check the Python and pip3 versions:
|
||||||
|
```bash
|
||||||
|
python -V
|
||||||
|
pip3 -V
|
||||||
|
```
|
||||||
|
If you see the following output, there is no problem with the Python environment:
|
||||||
|
```bash
|
||||||
|
Python 3.6.0
|
||||||
|
pip 19.2.2 from C:\Users\shmilylty\AppData\Roaming\Python\Python36\site-packages\pip (python 3.6)
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>✔Installation steps (for Git)</b></summary>
|
||||||
|
|
||||||
|
1. **Download**
|
||||||
|
|
||||||
|
Because OneForAll is under development yet, it is recommended that use `git clone` to clone the latest code repository. Downloading from Releases is not recommended.
|
||||||
|
|
||||||
|
If you are in China, it is recommended that you choose [Gitee](https://gitee.com/shmilylty/OneForAll.git) for cloning:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://gitee.com/shmilylty/OneForAll.git
|
||||||
|
```
|
||||||
|
or:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/shmilylty/OneForAll.git
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Installation**
|
||||||
|
|
||||||
|
|
||||||
|
You can use pip3 install requirements, the following is an example of using **pip3** to install dependencies under **Windows**: (Note: If your Python3 is installed in the system Program Files In the directory, such as: `C:\Program Files\Python36`, please run the following as an administrator!)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd OneForAll/
|
||||||
|
python3 -m pip install -U pip setuptools wheel
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
python3 oneforall.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
For other system platforms, please read [dependency installation](https://github.com/shmilylty/OneForAll/tree/master/docs/installation_dependency.md). If you compile failed during the installation, you can find solution in [Q&A](https://github.com/shmilylty/OneForAll/tree/master/docs/troubleshooting.md) documentation. If still not resolved, welcome [issues](https://github.com/shmilylty/OneForAll/issues).
|
||||||
|
|
||||||
|
3. **Update**
|
||||||
|
|
||||||
|
Run the following command to **update** project ( maintain your updates to `/config/setting.py`and`/config/api.py`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git stash # Stash local Git changes
|
||||||
|
git fetch --all # Fetch updates
|
||||||
|
git pull # Pull updates
|
||||||
|
git stash pop # Apply the local Git changes stash
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>✔Installation steps (for Docker)</b></summary>
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker pull shmilylty/oneforall
|
||||||
|
docker run -it --rm -v ~/results:/OneForAll/results oneforall
|
||||||
|
```
|
||||||
|
Result will be saved in `~/results`.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>✨OneForAll usage</b></summary>
|
||||||
|
|
||||||
|
If you are use pip3, run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 oneforall.py --target example.com run
|
||||||
|
python3 oneforall.py --targets ./example.txt run
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>🧐Instructions for results</b></summary>
|
||||||
|
|
||||||
|
Let's take the command `python3 oneforall.py --target example.com run` as an example. When command finished in the default configuration, OneForAll will generate results in the results directory:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`example.com.csv` is the result for each domain.
|
||||||
|
|
||||||
|
`all_subdomain_result_1583034493.csv` is the result for all domains when your target have multiple domains.
|
||||||
|
|
||||||
|
`result.sqlite3` is the SQLite3 database that stores all the subdomains collected by OneForAll. The database structure is shown below:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`example_com_origin_result` table stores the origin subdomain results of each module.
|
||||||
|
|
||||||
|
`example_com_resolve_result` table stores the results of resolving subdomains.
|
||||||
|
|
||||||
|
`example_com_last_result` table stores the results of subdomain collection last time.
|
||||||
|
|
||||||
|
`example_com_now_result` table stores the collection results of the current subdomains. Usually using this table is enough.
|
||||||
|
|
||||||
|
For more information, please see [Field explanation](../field.md).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>🤔Instructions for Use</b></summary>
|
||||||
|
|
||||||
|
The CLI only provide some common parameters. For more configuration, please read [setting.py](https://github.com/shmilylty/OneForAll/tree/master/config/setting.py). IF you have any suggestions, welcome feedback. Some modules need access API (most of which are freely available after registered accounts). If you need , please go to [api.py](https://github.com/shmilylty/OneForAll/tree/master/config/api.py) to configure the API. If not used, just ignore the error message. (For module detailes, please read [collection module description](https://github.com/shmilylty/OneForAll/tree/master/docs/collection_modules.md))
|
||||||
|
|
||||||
|
The OneForAll command line interface is based on [Fire](https://github.com/google/python-fire/). For more advanced usage of Fire, please refer to [using the Fire CLI](https://github.com/google/Python-fire/blob/master/docs/using-cli.md), if you have any doubts during the use, please feel free to give me feedback.
|
||||||
|
|
||||||
|
[oneforall.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall.py) is the program main entrence, and oneforall.py can call [brute.py](https://github.com/shmilylty/OneForAll/tree/master/brute.py), [takerover.py](https://github.com/shmilylty/OneForAll/tree/master/takerover.py), [dbexport.py ](https://github.com/shmilylty/OneForAll/tree/master/dbexport.py) and other modules. But you can also use these modules separately, if you want, please refer to the [usage help](https://github.com/shmilylty/OneForAll/tree/master/docs/en-us/usage_help.md).
|
||||||
|
|
||||||
|
❗ Note: When you encounter some problems or doubts during use, please search answers on [issues](https://github.com/shmilylty/OneForAll/issues) first. You can also read [Q&A](https://github.com/shmilylty/OneForAll/tree/master/docs/troubleshooting.md).
|
||||||
|
|
||||||
|
**OneForAll help summary**
|
||||||
|
|
||||||
|
The following help information may not be up to date. You can use `python oneforall.py --help` to get the latest help information.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python oneforall.py --help
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
oneforall.py - OneForAll help summary page
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
oneforall.py COMMAND | --target=TARGET <flags>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
OneForAll is a powerful subdomain integration tool
|
||||||
|
|
||||||
|
Example:
|
||||||
|
python3 oneforall.py version
|
||||||
|
python3 oneforall.py --target example.com run
|
||||||
|
python3 oneforall.py --targets ./domains.txt run
|
||||||
|
python3 oneforall.py --target example.com --alive False run
|
||||||
|
python3 oneforall.py --target example.com --brute True run
|
||||||
|
python3 oneforall.py --target example.com --port medium run
|
||||||
|
python3 oneforall.py --target example.com --fmt csv run
|
||||||
|
python3 oneforall.py --target example.com --dns False run
|
||||||
|
python3 oneforall.py --target example.com --req False run
|
||||||
|
python3 oneforall.py --target example.com --takeover False run
|
||||||
|
python3 oneforall.py --target example.com --show True run
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--alive True/False Only export alive subdomains or not (default False)
|
||||||
|
--port default/small/large See details in ./config/setting.py(default port 80)
|
||||||
|
--fmt csv/json (result format)
|
||||||
|
--path Result directory (default directory is ./results)
|
||||||
|
|
||||||
|
ARGUMENTS
|
||||||
|
TARGET
|
||||||
|
One domain (required)
|
||||||
|
TARGETS
|
||||||
|
File path of one domain per line (required)
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--brute=BRUTE
|
||||||
|
Use brute module (default True)
|
||||||
|
--dns=DNS
|
||||||
|
Use DNS resolution (default True)
|
||||||
|
--req=REQ
|
||||||
|
HTTP request subdomains (default True)
|
||||||
|
--port=PORT
|
||||||
|
The port range request to the subdomains (default port 80)
|
||||||
|
--alive=ALIVE
|
||||||
|
Only export alive subdomains (default False)
|
||||||
|
--fmt=FMT
|
||||||
|
Result format (default csv)
|
||||||
|
--path=PATH
|
||||||
|
Result directory (default None)
|
||||||
|
--takeover=TAKEOVER
|
||||||
|
Scan subdomain takeover (default False)
|
||||||
|
|
||||||
|
COMMANDS
|
||||||
|
COMMAND is one of the following:
|
||||||
|
|
||||||
|
version
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## 🎉Why OneForAll?
|
||||||
|
|
||||||
|
Project address : [https://github.com/shmilylty/OneForAll](https://github.com/shmilylty/OneForAll)
|
||||||
|
|
||||||
|
Problems with other tools
|
||||||
|
|
||||||
|
* **Not powerful enough**, few api, cannot automate, cannot valid subdomain, etc.
|
||||||
|
|
||||||
|
* **Not friendly enough**, do not have a good user interface.
|
||||||
|
|
||||||
|
* **Not quickly enough**, do not use multi-process, multi-threading, coroutine, etc.
|
||||||
|
|
||||||
|
* **Lack of maintenance**, lots of issues and bugs, and no one fixed it.
|
||||||
|
|
||||||
|
In order to solve the above problems, OneForAll born! As its name, OneForAll is committed to becoming the only one subdomain integration tool you need. We hope that one day OneForAll can be called "probably the best subdomain tool"
|
||||||
|
|
||||||
|
At present, OneForAll is under development, there must be a lot of problems and areas for improvement. Welcome to submit [Issues](https://github.com/shmilylty/OneForAll/issues) or [PR](https://github.com/shmilylty/OneForAll/pulls), If you like, star please✨. You can contact me through QQ group [**824414244**](https://shang.qq.com/wpa/qunwpa?idkey=125d3689b60445cdbb11e4ddff38036b7f6f2abbf4f7957df5dddba81aa90771) or twitter [tweet](https://twitter.com/shmilylty) to me: 👨👨👦👦.
|
||||||
|
|
||||||
|
## 👍Features
|
||||||
|
|
||||||
|
* **Powerful collection capability**, For more details, please read [collection module description](https://github.com/shmilylty/OneForAll/tree/master/docs/collection_modules.md).
|
||||||
|
1. Use 6 certificate modules: `censys_api`, `certspotter`, `crtsh`, `entrust`, `google`, `spyse_api`.
|
||||||
|
2. Use 6 baseline testing modules: scan domain transfer vulnerability `axfr`, cross-domain policy file `cdx`, HTTPS certificate `cert`, content security policy `csp`, robots file `robots`, and sitemap file `sitemap`, NSEC record `nsec`. NSEC3 record and other modules will be added later.
|
||||||
|
3. Use 2 web crawler modules: `archirawl`, `commoncrawl`, which is still being debugged and needs to be added and improved).
|
||||||
|
4. Use 24 DNS datasets modules: `bevigil`, `binaryedge_api`, `bufferover`, `cebaidu`, `chinaz`, `chinaz_api`, `circl_api`, `cloudflare`, `dnsdb_api`, `dnsdumpster`, `hackertarget`, `ip138`, `ipv4info_api`, `netcraft`, `passivedns_api`, `ptrarchive`, `qianxun`, `rapiddns`, `riddler`, `robtex`, `securitytrails_api`, `sitedossier`, `threatcrowd`, `wzpc`, `ximcx`.
|
||||||
|
5. Use 6 DNS queries modules: enumerating SRV records `srv` and collect from `MX`, `NS`, `SOA`, `TXT`, `SPF`.
|
||||||
|
6. Use 6 threat intelligence modules: `alienvault`, `riskiq_ api`, `threatbook_ api`, `threatkeeper `, `virustotal`, `virustotal_ api`, which need to be added and improved.
|
||||||
|
7. Use 16 search engines modules: `ask`, `baidu`, `bing`, `bing_api`, `fofa_api`, `gitee`, `github_api`, `google`, `google_api`, `shodan_api`, `so`, `sogou`, `yahoo`, `yandex`, `zoomeye_api`, except for special search engines. General search engines support automatic exclusion of search, full search and recursive search.
|
||||||
|
* **Support subdomain brute force**, can use dictionary mode or custom fuzz mode. Supports bulk brute and recursive brute, and automatically determine wildcard or not and processing.
|
||||||
|
* **Support subdomain verification**, default enable, automatically resolve DNS, request subdomain to obtain response, and determine subdomain alive or not.
|
||||||
|
* **Support subdomain crawling**, according to the existing subdomains, the response body of the request subdomain and the JS in the response body can be found again from the new subdomain.
|
||||||
|
* **Support subdomain replacement**, according to the existing subdomain, use subdomain replacement technology to discover new subdomains again.
|
||||||
|
* **Support subdomain takeover**, default enable, supports bulk inspection, and automatic takeover subdomain (only Github, remains to be improved at present).
|
||||||
|
* **Powerful processing feature**, support automatic deduplicate, DNS resolve, HTTP request, filter valid subdomains and information for subdomains. Supported export formats: `txt`, `csv`, `json`.
|
||||||
|
* **Very fast**, [collection module](https://github.com/shmilylty/OneForAll/tree/master/collect.py) uses multi-threading, [brute module](https://github.com/shmilylty/OneForAll/tree/master/brute.py) uses [MassDNS](https://github.com/blechschmidt/massdns), MassDNS is capable of resolving over 350,000 names per second using publicly available resolvers. DNS resolve and HTTP requests use async-coroutine. [subdomain takeover](https://github.com/shmilylty/OneForAll/tree/master/takeover.py) uses multi-threading.
|
||||||
|
* **Good experience**, each module has a progress bar, and save results asynchronously.
|
||||||
|
|
||||||
|
If you have any other good ideas, please let me know!😎
|
||||||
|
|
||||||
|
## 🌲Directory structure
|
||||||
|
|
||||||
|
For more information, please see [Directory structure description](https://github.com/shmilylty/OneForAll/tree/master/docs/directory_structure.md).
|
||||||
|
|
||||||
|
Some help and instructions are also provided in the [docs](https://github.com/shmilylty/OneForAll/tree/master/docs/) directory of this project, such as [dictionary Source description](https://github.com/shmilylty/OneForAll/tree/master/docs/dictionary_source.md), [wildcard judgment process](https://github.com/shmilylty/OneForAll/tree/master/docs/wildcard_judgment.png).
|
||||||
|
|
||||||
|
## 👏Framework used
|
||||||
|
|
||||||
|
* [aiohttp](https://github.com/aio-libs/aiohttp) - Asynchronous HTTP client/server framework for asyncio and Python
|
||||||
|
* [beautifulsoup4](https://pypi.org/project/beautifulsoup4/) - Beautiful Soup is a library that makes it easy to scrape information from web pages
|
||||||
|
* [fire](https://github.com/google/python-fire) - Python Fire is a library for automatically generating command line interfaces (CLIs) from absolutely any Python object
|
||||||
|
* [loguru](https://github.com/Delgan/loguru) - Loguru is a library which aims to bring enjoyable logging in Python.
|
||||||
|
* [massdns](https://github.com/blechschmidt/massdns) - A high-performance DNS stub resolver
|
||||||
|
* [records](https://github.com/kennethreitz/records) - Records is a very simple, but powerful, library for making raw SQL queries to most relational databases
|
||||||
|
* [requests](https://github.com/psf/requests) - A simple, yet elegant HTTP library
|
||||||
|
* [tqdm](https://github.com/tqdm/tqdm) - A Fast, Extensible Progress Bar for Python and CLI
|
||||||
|
|
||||||
|
Thanks to these great Python libraries!
|
||||||
|
|
||||||
|
## 🔖Version control
|
||||||
|
|
||||||
|
The project uses [SemVer](https://semver.org/) for version management, and you can view the available version in [Releases](https://github.com/shmilylty/OneForAll/releases), You can refer to the [change record instructions](https://github.com/shmilylty/OneForAll/tree/master/docs/changes.md) for historical changes.
|
||||||
|
|
||||||
|
## ⌛Follow-up plan
|
||||||
|
|
||||||
|
- [ ] Continuous optimize and improve of each module
|
||||||
|
- [ ] Implementation of front-end interface for powerful interaction
|
||||||
|
|
||||||
|
For more details, read [todo.md](https://github.com/shmilylty/OneForAll/tree/master/docs/todo.md).
|
||||||
|
|
||||||
|
## 🙏Contribution
|
||||||
|
|
||||||
|
Very warmly welcome all people to make OneForAll better together!
|
||||||
|
|
||||||
|
## 👨💻Contributors
|
||||||
|
|
||||||
|
* **[Jing Ling](https://github.com/shmilylty)**
|
||||||
|
* Core developer
|
||||||
|
|
||||||
|
You can view all contributors and their contributions in the [contributor documentation](https://github.com/shmilylty/OneForAll/tree/master/docs/contributors.md) and thank them for making OneForAll more powerful and useful.
|
||||||
|
|
||||||
|
## 📄License
|
||||||
|
|
||||||
|
The project has signed a GPL-3.0 license, for more information, please read [LICENSE](https://github.com/shmilylty/OneForAll/blob/master/LICENSE).
|
||||||
|
|
||||||
|
## 😘Acknowledgement
|
||||||
|
|
||||||
|
Thanks to the various subdomain collection projects of online open source!
|
||||||
|
|
||||||
|
Thanks ace of [A-Team](https://github.com/QAX-A-Team) for their enthusiastic and unselfish answers!
|
||||||
|
|
||||||
|
Developed with drive and [PyCharm](https://www.jetbrains.com/pycharm/)!
|
||||||
|
|
||||||
|
## 📜Disclaimer
|
||||||
|
|
||||||
|
This tool can only be used in the safety construction of enterprises with sufficient legal authorization.
|
||||||
|
During the use of this tool, you should ensure that all your actions comply with local laws and regulations.
|
||||||
|
If you have any illegal behavior in the process of using this tool, you will bear all the consequences yourself,
|
||||||
|
and all developers and all contributors of this tool will not bear any legal and joint liability.
|
||||||
|
|
||||||
|
## 💖Stargazers over time
|
||||||
|
|
||||||
|
[](https://starchart.cc/shmilylty/OneForAll)
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
**🤔Help**
|
||||||
|
|
||||||
|
The command line parameters only provide some common parameters. For more detailed parameter configuration, please see [config.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/config/setting.py) if you think Some parameters are frequently used in the command interface or missing parameters. Feedback is welcome. For well-known reasons, if you want to use some of the wall's collection interface, please go to [config.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/config/setting.py) to configure the proxy, some collection Modules need to provide APIs (most of which are freely available for registered accounts). If you need to use them, please go to [api.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/config/api.py) to configure the API. Information, if not used, please ignore the error message. (For detailed modules, please read [collection module description](https://github.com/shmilylty/OneForAll/tree/master/docs/collection_modules.md))
|
||||||
|
|
||||||
|
The OneForAll command line interface is based on [Fire](https://github.com/google/python-fire/). For more advanced usage of Fire, please refer to [using the Fire CLI](https://github.com/google/Python-fire/blob/master/docs/using-cli.md), if you have any doubts during the use, please feel free to give me feedback.
|
||||||
|
|
||||||
|
[oneforall.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/oneforall.py) is the main program entry, and oneforall.py can call [aiobrute.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/aiobrute.py), [takerover.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/takerover.py) and [dbexport.py ](https://github.com/shmilylty/OneForAll/tree/master/oneforall/dbexport.py) and other modules, in order to facilitate the sub-field blasting, aiobrute.py is isolated independently, in order to facilitate the subdomain takeover risk check independently takeover.py, in order to facilitate the database export independently dbexport.py, these modules can be run separately, and the parameters accepted are more abundant.
|
||||||
|
|
||||||
|
❗ Note: When you encounter some problems or doubts during use, please use [Issues](https://github.com/shmilylty/OneForAll/issues) to search for answers. Also see [Q&A](https://github.com/shmilylty/OneForAll/tree/master/docs/Q&A.md).
|
||||||
|
|
||||||
|
1. **oneforall.py help**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python oneforall.py --help
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
oneforall.py - OneForAll help summary page
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
oneforall.py COMMAND | <flags>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
OneForAll is a powerful subdomain integration tool
|
||||||
|
|
||||||
|
Example:
|
||||||
|
python3 oneforall.py version
|
||||||
|
python3 oneforall.py check
|
||||||
|
python3 oneforall.py --target example.com run
|
||||||
|
python3 oneforall.py --targets ./domains.txt run
|
||||||
|
python3 oneforall.py --target example.com --alive False run
|
||||||
|
python3 oneforall.py --target example.com --brute True run
|
||||||
|
python3 oneforall.py --target example.com --port medium run
|
||||||
|
python3 oneforall.py --target example.com --format csv run
|
||||||
|
python3 oneforall.py --target example.com --dns False run
|
||||||
|
python3 oneforall.py --target example.com --req False run
|
||||||
|
python3 oneforall.py --target example.com --takeover False run
|
||||||
|
python3 oneforall.py --target example.com --show True run
|
||||||
|
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--port small/medium/large See details in ./config/setting.py(default small)
|
||||||
|
--format csv/json (result format)
|
||||||
|
--path Result path (default None, automatically generated)
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--target=TARGET
|
||||||
|
One domain (target or targets parameters must be provided)
|
||||||
|
--targets=TARGETS
|
||||||
|
File path of one domain per line
|
||||||
|
--brute=BRUTE
|
||||||
|
Use brute module (default False)
|
||||||
|
--dns=DNS
|
||||||
|
Use DNS resolution (default True)
|
||||||
|
--req=REQ
|
||||||
|
HTTP request subdomains (default True)
|
||||||
|
--port=PORT
|
||||||
|
The port range to request (default small port is 80,443)
|
||||||
|
--alive=ALIVE
|
||||||
|
Only export alive subdomains (default False)
|
||||||
|
--format=FORMAT
|
||||||
|
Result format (default csv)
|
||||||
|
--path=PATH
|
||||||
|
Result path (default None, automatically generated)
|
||||||
|
--takeover=TAKEOVER
|
||||||
|
Scan subdomain takeover (default False)
|
||||||
|
|
||||||
|
COMMANDS
|
||||||
|
COMMAND is one of the following:
|
||||||
|
|
||||||
|
check
|
||||||
|
Check if there is a new version and exit
|
||||||
|
|
||||||
|
version
|
||||||
|
Print version information and exit
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **aiobrute.py help**
|
||||||
|
|
||||||
|
With regard to the handling of the universal parsing problem, first of all, OneForAll accesses a random subdomain to determine whether universal parsing is used, and if universal parsing is used, it is handled by the following judgment:
|
||||||
|
- First, it is mainly compared with the pan-parsed IP set and TTL values, see [this article](http://sh3ll.me/archives/201704041222.txt).
|
||||||
|
|
||||||
|
- Second, the number of times to resolve to the same IP collection multiple times (the default is 10, which can be set to size in config.py).
|
||||||
|
|
||||||
|
- Third, considering the blasting efficiency, there is no HTTP response volume similarity comparison and response volume content judgment, this function has not been implemented yet, and will be implemented if necessary.
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python brute.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
brute.py - OneForAll subdomain brute module
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
brute.py <flags>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Example:
|
||||||
|
brute.py --target domain.com --word True run
|
||||||
|
brute.py --targets ./domains.txt --word True run
|
||||||
|
brute.py --target domain.com --word True --concurrent 2000 run
|
||||||
|
brute.py --target domain.com --word True --wordlist subnames.txt run
|
||||||
|
brute.py --target domain.com --word True --recursive True --depth 2 run
|
||||||
|
brute.py --target d.com --fuzz True --place m.*.d.com --rule '[a-z]' run
|
||||||
|
brute.py --target d.com --fuzz True --place m.*.d.com --fuzzlist subnames.txt run
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--format rst/csv/tsv/json/yaml/html/jira/xls/xlsx/dbf/latex/ods (result format)
|
||||||
|
--path Result path (default None, automatically generated)
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--target=TARGET
|
||||||
|
One domain (target or targets must be provided)
|
||||||
|
--targets=TARGETS
|
||||||
|
File path of one domain per line
|
||||||
|
--process=PROCESS
|
||||||
|
Number of processes (default 1)
|
||||||
|
--concurrent=CONCURRENT
|
||||||
|
Number of concurrent (default 2000)
|
||||||
|
--word=WORD
|
||||||
|
Use word mode generate dictionary (default False)
|
||||||
|
--wordlist=WORDLIST
|
||||||
|
Dictionary path used in word mode (default use ./config/default.py)
|
||||||
|
--recursive=RECURSIVE
|
||||||
|
Use recursion (default False)
|
||||||
|
--depth=DEPTH
|
||||||
|
Recursive depth (default 2)
|
||||||
|
--nextlist=NEXTLIST
|
||||||
|
Dictionary file path used by recursive (default use ./config/default.py)
|
||||||
|
--fuzz=FUZZ
|
||||||
|
Use fuzz mode generate dictionary (default False)
|
||||||
|
--place=PLACE
|
||||||
|
Designated fuzz position (required if use fuzz mode)
|
||||||
|
--rule=RULE
|
||||||
|
Specify the regexp rules used in fuzz mode (required if use fuzz mode)
|
||||||
|
--fuzzlist=FUZZLIST
|
||||||
|
Dictionary path used in fuzz mode (default use ./config/default.py)
|
||||||
|
--export=EXPORT
|
||||||
|
Export the results (default True)
|
||||||
|
--alive=ALIVE
|
||||||
|
Only export alive subdomains (default False)
|
||||||
|
--format=FORMAT
|
||||||
|
Result format (default csv)
|
||||||
|
--path=PATH
|
||||||
|
Result directory (default None)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **takeover.py help**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python takeover.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
takeover.py - OneForAll subdomain takeover module SYNOPSIS takeover.py <flags> DESCRIPTION
|
||||||
|
|
||||||
|
Example:
|
||||||
|
python3 takeover.py --target www.example.com --format csv run
|
||||||
|
python3 takeover.py --targets ./subdomains.txt --thread 10 run
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--format rst/csv/tsv/json/yaml/html/jira/xls/xlsx/dbf/latex/ods (result format)
|
||||||
|
--path Result directory (default directory is ./results)
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--target=TARGET
|
||||||
|
One domain (target or targets must be provided)
|
||||||
|
--targets=TARGETS
|
||||||
|
File path of one domain per line
|
||||||
|
--thread=THREAD
|
||||||
|
threads number (default 20)
|
||||||
|
--path=PATH
|
||||||
|
Result directory (default None)
|
||||||
|
--format=FORMAT
|
||||||
|
Result format (default csv)
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **dbexport.py help**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python dbexport.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
dbexport.py - OneForAll export from database module
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
dbexport.py TARGET <flags>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Example:
|
||||||
|
python3 dbexport.py --target name --format csv --dir= ./result.csv
|
||||||
|
python3 dbexport.py --db result.db --target name --show False
|
||||||
|
python3 dbexport.py --target table_name --tb True --show False
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--format rst/csv/tsv/json/yaml/html/jira/xls/xlsx/dbf/latex/ods (result format)
|
||||||
|
--path Result directory (default directory is ./results)
|
||||||
|
|
||||||
|
POSITIONAL ARGUMENTS
|
||||||
|
TARGET
|
||||||
|
Table to be exported
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--type=TYPE
|
||||||
|
Type of target
|
||||||
|
--db=DB
|
||||||
|
Database path to be exported (default ./results/result.sqlite3)
|
||||||
|
--alive=ALIVE
|
||||||
|
Only export the results of alive subdomains (default False)
|
||||||
|
--limit=LIMIT
|
||||||
|
Export limit (default None)
|
||||||
|
--path=PATH
|
||||||
|
Result directory (default None)
|
||||||
|
--format=FORMAT
|
||||||
|
Result format (default csv)
|
||||||
|
--show=SHOW
|
||||||
|
Displays the exported data in terminal (default False)
|
||||||
|
```
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
注意:以下部分字段只存于结果数据库中
|
||||||
|
|
||||||
|
### id
|
||||||
|
|
||||||
|
标识作用无意义
|
||||||
|
|
||||||
|
### new
|
||||||
|
|
||||||
|
标记是否是新发现的子域名
|
||||||
|
|
||||||
|
### alive
|
||||||
|
|
||||||
|
是否存活,不存活的判定情况包含:无法解析IP、网络不可达、400、5XX等
|
||||||
|
|
||||||
|
### request
|
||||||
|
|
||||||
|
记录HTTP请求是否成功字段,为空是无法解析IP,为0是网络不可达,为1是成功请求
|
||||||
|
|
||||||
|
### resolve
|
||||||
|
|
||||||
|
记录DNS解析是否成功
|
||||||
|
|
||||||
|
### url
|
||||||
|
|
||||||
|
请求的url链接
|
||||||
|
|
||||||
|
### subdomain
|
||||||
|
|
||||||
|
子域名
|
||||||
|
|
||||||
|
### level
|
||||||
|
|
||||||
|
是几级子域名
|
||||||
|
|
||||||
|
### cname
|
||||||
|
|
||||||
|
cname记录
|
||||||
|
|
||||||
|
### ip
|
||||||
|
|
||||||
|
解析到的IP
|
||||||
|
|
||||||
|
### public
|
||||||
|
|
||||||
|
是否是公网IP
|
||||||
|
|
||||||
|
### cdn
|
||||||
|
|
||||||
|
解析的IP是否CDN
|
||||||
|
|
||||||
|
### port
|
||||||
|
|
||||||
|
请求的网络端口
|
||||||
|
|
||||||
|
### status
|
||||||
|
|
||||||
|
HTTP响应的状态码
|
||||||
|
|
||||||
|
### reason
|
||||||
|
|
||||||
|
网络连接情况及详情
|
||||||
|
|
||||||
|
### title
|
||||||
|
|
||||||
|
网站标题
|
||||||
|
|
||||||
|
### banner
|
||||||
|
|
||||||
|
网站指纹信息
|
||||||
|
|
||||||
|
### history
|
||||||
|
|
||||||
|
请求时URL跳转历史
|
||||||
|
|
||||||
|
### response
|
||||||
|
|
||||||
|
响应体文本内容
|
||||||
|
|
||||||
|
### times
|
||||||
|
|
||||||
|
在爆破中ip重复出现的次数
|
||||||
|
|
||||||
|
### ttl
|
||||||
|
|
||||||
|
DNS解析返回的TTL值
|
||||||
|
|
||||||
|
### cidr
|
||||||
|
|
||||||
|
ip2location库查询出的CIDR
|
||||||
|
|
||||||
|
### asn
|
||||||
|
|
||||||
|
ip2location库查询出的ASN
|
||||||
|
|
||||||
|
### addr
|
||||||
|
|
||||||
|
ip2region库查询出的物理地址
|
||||||
|
|
||||||
|
### isp
|
||||||
|
|
||||||
|
ip2region库查询出的网络服务提供商
|
||||||
|
|
||||||
|
### resolver
|
||||||
|
|
||||||
|
所使用的DNS解析服务器
|
||||||
|
|
||||||
|
### module
|
||||||
|
|
||||||
|
发现本子域名所使用的模块
|
||||||
|
|
||||||
|
### source
|
||||||
|
|
||||||
|
发现本子域名的具体来源
|
||||||
|
|
||||||
|
### elapse
|
||||||
|
|
||||||
|
当前模块发现用时
|
||||||
|
|
||||||
|
### find
|
||||||
|
|
||||||
|
当前模块发现的子域个数
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 安装依赖
|
||||||
|
|
||||||
|
## Windows系统
|
||||||
|
|
||||||
|
注意:如果你的Python3安装在系统Program Files目录下,如:`C:\Program Files\Python38`,那么请以管理员身份运行命令提示符cmd执行以下命令!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd OneForAll/
|
||||||
|
python -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
python oneforall.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## Linux系统
|
||||||
|
|
||||||
|
### Ubuntu/Debian系统(包括kali)
|
||||||
|
|
||||||
|
1. 安装git和pip3
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install git python3-pip -y
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 克隆OneForAll项目
|
||||||
|
```bash
|
||||||
|
git clone https://gitee.com/shmilylty/OneForAll.git
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 安装相关依赖
|
||||||
|
```bash
|
||||||
|
cd OneForAll/
|
||||||
|
sudo apt install python3-dev python3-pip python3-testresources -y
|
||||||
|
sudo python3 -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
sudo pip3 install --ignore-installed -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
python3 oneforall.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### RHEL/Centos系统
|
||||||
|
|
||||||
|
1. 安装git和pip3
|
||||||
|
```bash
|
||||||
|
sudo yum update
|
||||||
|
sudo yum install git python3-pip -y
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 克隆OneForAll项目
|
||||||
|
```bash
|
||||||
|
git clone https://gitee.com/shmilylty/OneForAll.git
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 安装相关依赖
|
||||||
|
```bash
|
||||||
|
cd OneForAll/
|
||||||
|
sudo yum install gcc python3-devel python3-pip -y
|
||||||
|
sudo python3 -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
sudo pip3 install --ignore-installed -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
python3 oneforall.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## Darwin系统
|
||||||
|
|
||||||
|
克隆OneForAll项目
|
||||||
|
```bash
|
||||||
|
git clone https://gitee.com/shmilylty/OneForAll.git
|
||||||
|
```
|
||||||
|
|
||||||
|
安装相关依赖
|
||||||
|
```bash
|
||||||
|
cd OneForAll/
|
||||||
|
python3 -m pip install -U pip setuptools wheel -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
python3 oneforall.py --help
|
||||||
|
```
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# OneForAll后续开发计划
|
||||||
|
|
||||||
|
## 下一步计划
|
||||||
|
|
||||||
|
- [ ] 各模块支持优化和完善
|
||||||
|
- [x] 子域监控(标记每次新发现的子域)
|
||||||
|
- [ ] 子域收集爬虫实现(包括从JS等静态资源文件中收集子域)
|
||||||
|
- [ ] 操作强大交互人性的前端界面实现(暂定:Element+Flask)
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# 常见问题与回答
|
||||||
|
|
||||||
|
## 依赖问题
|
||||||
|
|
||||||
|
**Q: 在安装依赖过程遇到编译某个依赖库失败,怎么解决?**
|
||||||
|
|
||||||
|
A: 可以尝试以下方法:
|
||||||
|
|
||||||
|
1. 到提供编译好的whl文件的第三方平台,找到对应库手动下载安装。第三方平台平台有:
|
||||||
|
* [https://www.lfd.uci.edu/~gohlke/pythonlibs](https://www.lfd.uci.edu/~gohlke/pythonlibs)
|
||||||
|
* [https://pythonwheels.com/](https://pythonwheels.com/)
|
||||||
|
|
||||||
|
选择好对应版本执行以下命令手动安装。举个例子,当编译 brotlipy 时失败时,找到[https://www.lfd.uci.edu/~gohlke/pythonlibs/#brotlipy](https://www.lfd.uci.edu/~gohlke/pythonlibs/#brotlipy),由于我的系统是Windows 10 64位,使用的Python 3.8便下载`brotlipy‑0.7.0‑cp38‑cp38‑win_amd64.whl`(一般来说下载最新版本的),然后手动安装:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip3 install brotlipy‑0.7.0‑cp38‑cp38‑win_amd64.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 到库的项目地址issues和wiki等找找有没有解决方法,如果没有就给他们提issues发邮件😜。
|
||||||
|
|
||||||
|
## 使用问题
|
||||||
|
|
||||||
|
**Q: 为什么运行OneForAll之后最终结果为空?**
|
||||||
|
|
||||||
|
*A: 有几种可能性:第一可能目标域名没有子域。第二由于OneForAll默认会自动验证子域,在导出是只会有效子域,所以存在导出时没有有效子域的情况,你可以在运行OneForAll使用--valid=None指定导出所有发现的子域,你也可以使用--verify=False指定不验证子域的有效性。*
|
||||||
|
|
||||||
|
**Q: 安装依赖时出现以下类似报错,怎么解决?**
|
||||||
|
Cannot uninstall 'PyYAML'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.
|
||||||
|
|
||||||
|
*A: 安装依赖时尝试加上--ignore-installed参数*
|
||||||
|
|
||||||
|
**Q:使用Excel打开结果的csv中有中文乱码,怎么解决?**
|
||||||
|
|
||||||
|
*A:由于Excel打开文件需要带BOM头识别编码,而默认生成的结果csv文件是UTF-8编码不是UTF-8-BOM,所以会使用Excel打开会出现中文乱码*,有以下解决办法:
|
||||||
|
|
||||||
|
1.在一开始运行OneForAll,设置format参数为xls或者xlsx格式。
|
||||||
|
|
||||||
|
2.使用NotePad++类似工具转化编码格式为UTF-8-BOM。
|
||||||
|
|
||||||
|
3.使用Excel软件导入结果csv文件中的数据。
|
||||||
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 142 KiB |
@@ -0,0 +1,225 @@
|
|||||||
|
# 使用帮助
|
||||||
|
|
||||||
|
命令行参数只提供了一些常用参数,更多详细的参数配置请见[config.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/config/setting.py),如果你认为有些参数是命令界面经常使用到的或缺少了什么参数等问题非常欢迎反馈。由于众所周知的原因,如果要使用一些被墙的收集接口请先到[config.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/config/setting.py)配置代理,有些收集模块需要提供API(大多都是可以注册账号免费获取),如果需要使用请到[api.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/config/api.py)配置API信息,如果不使用请忽略有关报错提示。(详细模块请阅读[收集模块说明](https://github.com/shmilylty/OneForAll/tree/master/docs/collection_modules.md))
|
||||||
|
|
||||||
|
OneForAll命令行界面基于[Fire](https://github.com/google/python-fire/)实现,有关Fire更高级使用方法请参阅[使用Fire CLI](https://github.com/google/python-fire/blob/master/docs/using-cli.md)。
|
||||||
|
|
||||||
|
[oneforall.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/oneforall.py)是主程序入口,oneforall.py可以调用[aiobrute.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/aiobrute.py),[takerover.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/takerover.py)及[dbexport.py](https://github.com/shmilylty/OneForAll/tree/master/oneforall/dbexport.py)等模块,为了方便进行子域爆破独立出了aiobrute.py,为了方便进行子域接管风险检查独立出了takerover.py,为了方便数据库导出独立出了dbexport.py,这些模块都可以单独运行,并且所接受参数要更丰富一点。
|
||||||
|
|
||||||
|
❗注意:当你在使用过程中遇到一些问题或者疑惑时,请先到[Issues](https://github.com/shmilylty/OneForAll/issues)里使用搜索找找答案,还可以参阅[常见问题与回答](https://github.com/shmilylty/OneForAll/tree/master/docs/Q&A.md)。
|
||||||
|
|
||||||
|
1. oneforall.py使用帮助
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python oneforall.py --help
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
oneforall.py - OneForAll使用帮助
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
oneforall.py COMMAND | <flags>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
OneForAll是一款功能强大的子域收集工具
|
||||||
|
|
||||||
|
Example:
|
||||||
|
python3 oneforall.py version
|
||||||
|
python3 oneforall.py check
|
||||||
|
python3 oneforall.py --target example.com run
|
||||||
|
python3 oneforall.py --targets ./domains.txt run
|
||||||
|
python3 oneforall.py --target example.com --alive False run
|
||||||
|
python3 oneforall.py --target example.com --brute True run
|
||||||
|
python3 oneforall.py --target example.com --port medium run
|
||||||
|
python3 oneforall.py --target example.com --format csv run
|
||||||
|
python3 oneforall.py --target example.com --dns False run
|
||||||
|
python3 oneforall.py --target example.com --req False run
|
||||||
|
python3 oneforall.py --target example.com --takeover False run
|
||||||
|
python3 oneforall.py --target example.com --show True run
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--port small/medium/large 详见./config/setting.py(默认small)
|
||||||
|
--format csv/json (结果格式,默认CSV)
|
||||||
|
--path 结果路径(默认None,自动生成)
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--target=TARGET
|
||||||
|
单个域名(必须提供target或targets参数)
|
||||||
|
--targets=TARGETS
|
||||||
|
每行一个域名的文件路径
|
||||||
|
--brute=BRUTE
|
||||||
|
使用爆破模块(默认True)
|
||||||
|
--dns=DNS
|
||||||
|
开启子域解析(默认True)
|
||||||
|
--req=REQ
|
||||||
|
开启子域请求(默认True)
|
||||||
|
--port=PORT
|
||||||
|
请求验证的端口范围(默认medium)
|
||||||
|
--alive=ALIVE
|
||||||
|
只导出存活子域(默认False)
|
||||||
|
--format=FORMAT
|
||||||
|
结果格式(默认csv)
|
||||||
|
--path=PATH
|
||||||
|
结果路径(默认None,自动生成)
|
||||||
|
--takeover=TAKEOVER
|
||||||
|
开启子域接管检查(默认False)
|
||||||
|
|
||||||
|
COMMANDS
|
||||||
|
COMMAND is one of the following:
|
||||||
|
|
||||||
|
check
|
||||||
|
检查新版本并退出
|
||||||
|
|
||||||
|
version
|
||||||
|
打印版本信息并退出
|
||||||
|
```
|
||||||
|
|
||||||
|
2. aiobrute.py使用帮助
|
||||||
|
|
||||||
|
关于泛解析问题处理程序首先会访问一个随机的子域判断是否泛解析,如果使用了泛解析则是通过以下判断处理:
|
||||||
|
- 一是主要是与泛解析的IP集合和TTL值做对比,可以参考[这篇文章](http://sh3ll.me/archives/201704041222.txt)。
|
||||||
|
|
||||||
|
- 二是多次解析到同一IP集合次数(默认设置为10,可以在config.py设置大小)
|
||||||
|
|
||||||
|
- 考虑爆破效率问题目前还没有加上HTTP响应体相似度对比和响应体内容判断
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python aiobrute.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
brute.py - OneForAll子域爆破模块
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
brute.py <flags>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Example:
|
||||||
|
brute.py --target domain.com --word True run
|
||||||
|
brute.py --targets ./domains.txt --word True run
|
||||||
|
brute.py --target domain.com --word True --coroutine 2000 run
|
||||||
|
brute.py --target domain.com --word True --wordlist subnames.txt run
|
||||||
|
brute.py --target domain.com --word True --recursive True --depth 2 run
|
||||||
|
brute.py --target d.com --fuzz True --place m.*.d.com --rule '[a-z]' run
|
||||||
|
brute.py --target d.com --fuzz True --place m.*.d.com --fuzzlist subnames.txt run
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--format rst/csv/tsv/json/yaml/html/jira/xls/xlsx/dbf/latex/ods (结果格式,默认CSV)
|
||||||
|
--path 导出路径(默认None,自动生成)
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--target=TARGET
|
||||||
|
单个域名(必须提供target或targets参数)
|
||||||
|
--targets=TARGETS
|
||||||
|
每行一个域名的文件路径
|
||||||
|
--process=PROCESS
|
||||||
|
爆破的进程数(默认CPU核心数)
|
||||||
|
--coroutine=COROUTINE
|
||||||
|
每个爆破进程下的协程数(默认2000)
|
||||||
|
--wordlist=WORDLIST
|
||||||
|
指定爆破所使用的字典路径(默认使用config.py配置)
|
||||||
|
--recursive=RECURSIVE
|
||||||
|
是否使用递归爆破(默认False)
|
||||||
|
--depth=DEPTH
|
||||||
|
递归爆破的深度(默认2)
|
||||||
|
--nextlist=NEXTLIST
|
||||||
|
指定递归爆破所使用的字典路径(默认使用config.py配置)
|
||||||
|
--fuzz=FUZZ
|
||||||
|
是否使用fuzz模式进行爆破(默认False)
|
||||||
|
--rule=RULE
|
||||||
|
fuzz模式使用的正则规则(默认使用config.py配置)
|
||||||
|
--fuzzlist=FUZZLIST
|
||||||
|
指定fuzz模式所使用的字典路径(默认使用config.py配置)
|
||||||
|
--export=EXPORT
|
||||||
|
是否导出爆破结果(默认True)
|
||||||
|
--alive=ALIVE
|
||||||
|
只导出存活子域(默认False)
|
||||||
|
--format=FORMAT
|
||||||
|
导出格式(默认csv)
|
||||||
|
--path=PATH
|
||||||
|
导出路径(默认None)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
3. takeover.py使用帮助
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python takeover.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
takeover.py - OneForAll多线程子域接管风险检查模块
|
||||||
|
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
takeover.py COMMAND | --target=TARGET <flags>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Example:
|
||||||
|
python3 takeover.py --target www.example.com --format csv run
|
||||||
|
python3 takeover.py --target ./subdomains.txt --thread 10 run
|
||||||
|
|
||||||
|
Note:
|
||||||
|
参数format可选格式有'txt', 'rst', 'csv', 'tsv', 'json', 'yaml', 'html',
|
||||||
|
'jira', 'xls', 'xlsx', 'dbf', 'latex', 'ods'
|
||||||
|
参数dpath为None默认使用OneForAll结果目录
|
||||||
|
|
||||||
|
ARGUMENTS
|
||||||
|
TARGET
|
||||||
|
单个子域或者每行一个子域的文件路径(必需参数)
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--thread=THREAD
|
||||||
|
线程数(默认100)
|
||||||
|
--dpath=DPATH
|
||||||
|
导出目录(默认None)
|
||||||
|
--format=FORMAT
|
||||||
|
导出格式(默认xls)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
4. dbexport.py使用帮助
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python dbexport.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NAME
|
||||||
|
dbexport.py - OneForAll数据库导出模块
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
dbexport.py TARGET <flags>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Example:
|
||||||
|
python3 dbexport.py --target name --format csv --dir= ./result.csv
|
||||||
|
python3 dbexport.py --db result.db --target name --show False
|
||||||
|
python3 dbexport.py --target table_name --tb True --show False
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--type target/table 要导出的目标类型(默认target)
|
||||||
|
--format rst/csv/tsv/json/yaml/html/jira/xls/xlsx/dbf/latex/ods (结果格式,默认CSV)
|
||||||
|
--path 结果路径(默认None,自动生成)
|
||||||
|
|
||||||
|
POSITIONAL ARGUMENTS
|
||||||
|
TARGET
|
||||||
|
要导出的目标类型
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
--type=TYPE
|
||||||
|
要导出的目标类型(默认target)
|
||||||
|
--db=DB
|
||||||
|
要导出的数据库路径(默认为results/result.sqlite3)
|
||||||
|
--valid=VALID
|
||||||
|
导出子域的有效性(默认None)
|
||||||
|
--path=PATH
|
||||||
|
导出路径(默认None)
|
||||||
|
--format=FORMAT
|
||||||
|
导出格式(默认xlsx)
|
||||||
|
--show=SHOW
|
||||||
|
终端显示导出数据(默认False)
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 43 KiB |
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
OneForAll export from database module
|
||||||
|
|
||||||
|
:copyright: Copyright (c) 2019, Jing Ling. All rights reserved.
|
||||||
|
:license: GNU General Public License v3.0, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import fire
|
||||||
|
|
||||||
|
from common import utils
|
||||||
|
from common.database import Database
|
||||||
|
from config.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
def export_data(target, db=None, alive=False, limit=None, path=None, fmt='csv', show=False):
|
||||||
|
"""
|
||||||
|
OneForAll export from database module
|
||||||
|
|
||||||
|
Example:
|
||||||
|
python3 export.py --target name --fmt csv --dir= ./result.csv
|
||||||
|
python3 export.py --target name --tb True --show False
|
||||||
|
python3 export.py --db result.db --target name --show False
|
||||||
|
|
||||||
|
Note:
|
||||||
|
--fmt csv/json (result format)
|
||||||
|
--path Result directory (default directory is ./results)
|
||||||
|
|
||||||
|
:param str target: Table to be exported
|
||||||
|
:param str db: Database path to be exported (default ./results/result.sqlite3)
|
||||||
|
:param bool alive: Only export the results of alive subdomains (default False)
|
||||||
|
:param str limit: Export limit (default None)
|
||||||
|
:param str fmt: Result format (default csv)
|
||||||
|
:param str path: Result directory (default None)
|
||||||
|
:param bool show: Displays the exported data in terminal (default False)
|
||||||
|
"""
|
||||||
|
|
||||||
|
database = Database(db)
|
||||||
|
domains = utils.get_domains(target)
|
||||||
|
datas = list()
|
||||||
|
if domains:
|
||||||
|
for domain in domains:
|
||||||
|
table_name = domain.replace('.', '_')
|
||||||
|
rows = database.export_data(table_name, alive, limit)
|
||||||
|
if rows is None:
|
||||||
|
continue
|
||||||
|
data, _, _ = do_export(fmt, path, rows, show, domain, target)
|
||||||
|
datas.extend(data)
|
||||||
|
database.close()
|
||||||
|
if len(domains) > 1:
|
||||||
|
utils.export_all(alive, fmt, path, datas)
|
||||||
|
return datas
|
||||||
|
|
||||||
|
|
||||||
|
def do_export(fmt, path, rows, show, domain, target):
|
||||||
|
fmt = utils.check_format(fmt)
|
||||||
|
path = utils.check_path(path, target, fmt)
|
||||||
|
if show:
|
||||||
|
print(rows.dataset)
|
||||||
|
data = rows.export(fmt)
|
||||||
|
if fmt == 'csv':
|
||||||
|
data = '\ufeff' + data
|
||||||
|
utils.save_to_file(path, data)
|
||||||
|
logger.log('ALERT', f'The subdomain result for {domain}: {path}')
|
||||||
|
data = rows.as_dict()
|
||||||
|
return data, fmt, path
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
fire.Fire(export_data)
|
||||||
|
Before Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,216 @@
|
|||||||
|
"""
|
||||||
|
Reference: https://github.com/ProjectAnte/dnsgen
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
from config import settings
|
||||||
|
|
||||||
|
from modules import wildcard
|
||||||
|
from common import utils
|
||||||
|
from common import resolve
|
||||||
|
from common import request
|
||||||
|
from common.domain import Domain
|
||||||
|
from common.module import Module
|
||||||
|
from config.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
def split_domain(domain):
|
||||||
|
"""
|
||||||
|
Split domain base on subdomain levels
|
||||||
|
Root+TLD is taken as one part, regardless of its levels
|
||||||
|
"""
|
||||||
|
|
||||||
|
# test.1.foo.example.com -> [test, 1, foo, example.com]
|
||||||
|
# test.2.foo.example.com.cn -> [test, 2, foo, example.com.cn]
|
||||||
|
# test.example.co.uk -> [test, example.co.uk]
|
||||||
|
|
||||||
|
ext = Domain(domain).extract()
|
||||||
|
subname = ext.subdomain
|
||||||
|
parts = ext.subdomain.split('.') + [ext.registered_domain]
|
||||||
|
return subname, parts
|
||||||
|
|
||||||
|
|
||||||
|
class Altdns(Module):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Module.__init__(self)
|
||||||
|
self.module = 'Altdns'
|
||||||
|
self.source = 'Altdns'
|
||||||
|
self.start = time.time()
|
||||||
|
self.domain = domain
|
||||||
|
self.words = set()
|
||||||
|
self.now_subdomains = set()
|
||||||
|
self.new_subdomains = set()
|
||||||
|
self.wordlen = 6 # Min length of custom words extracted from domains
|
||||||
|
self.num_count = 3
|
||||||
|
|
||||||
|
def get_words(self):
|
||||||
|
path = settings.data_storage_dir.joinpath('altdns_wordlist.txt')
|
||||||
|
with open(path) as fd:
|
||||||
|
for line in fd:
|
||||||
|
word = line.lower().strip()
|
||||||
|
if word:
|
||||||
|
self.words.add(word)
|
||||||
|
|
||||||
|
def extract_words(self):
|
||||||
|
"""
|
||||||
|
Extend the dictionary based on target's domain naming conventions
|
||||||
|
"""
|
||||||
|
|
||||||
|
for subdomain in self.now_subdomains:
|
||||||
|
_, parts = split_domain(subdomain)
|
||||||
|
tokens = set(itertools.chain(*[word.lower().split('-') for word in parts]))
|
||||||
|
tokens = tokens.union({word.lower() for word in parts})
|
||||||
|
for token in tokens:
|
||||||
|
if len(token) >= self.wordlen:
|
||||||
|
self.words.add(token)
|
||||||
|
|
||||||
|
def increase_num(self, subname):
|
||||||
|
"""
|
||||||
|
If number is found in existing subdomain,
|
||||||
|
increase this number without any other alteration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# test.1.foo.example.com -> test.2.foo.example.com, test.3.foo.example.com, ...
|
||||||
|
# test1.example.com -> test2.example.com, test3.example.com, ...
|
||||||
|
# test01.example.com -> test02.example.com, test03.example.com, ...
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
digits = re.findall(r'\d{1,3}', subname)
|
||||||
|
for d in digits:
|
||||||
|
for m in range(self.num_count):
|
||||||
|
replacement = str(int(d) + 1 + m).zfill(len(d))
|
||||||
|
tmp_domain = subname.replace(d, replacement)
|
||||||
|
new_domain = f'{tmp_domain}.{self.domain}'
|
||||||
|
self.new_subdomains.add(new_domain)
|
||||||
|
count += 1
|
||||||
|
logger.log('DEBUG', f'The increase_num generated {count} subdomains')
|
||||||
|
|
||||||
|
def decrease_num(self, subname):
|
||||||
|
"""
|
||||||
|
If number is found in existing subdomain,
|
||||||
|
decrease this number without any other alteration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# test.4.foo.example.com -> test.3.foo.example.com, test.2.foo.example.com, ...
|
||||||
|
# test4.example.com -> test3.example.com, test2.example.com, ...
|
||||||
|
# test04.example.com -> test03.example.com, test02.example.com, ...
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
digits = re.findall(r'\d{1,3}', subname)
|
||||||
|
for d in digits:
|
||||||
|
for m in range(self.num_count):
|
||||||
|
new_digit = (int(d) - 1 - m)
|
||||||
|
if new_digit < 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
replacement = str(new_digit).zfill(len(d))
|
||||||
|
tmp_domain = subname.replace(d, replacement)
|
||||||
|
new_domain = f'{tmp_domain}.{self.domain}'
|
||||||
|
self.new_subdomains.add(new_domain)
|
||||||
|
count += 1
|
||||||
|
logger.log('DEBUG', f'The decrease_num generated {count} subdomains')
|
||||||
|
|
||||||
|
def insert_word(self, parts):
|
||||||
|
"""
|
||||||
|
Create new subdomain levels by inserting the words between existing levels
|
||||||
|
"""
|
||||||
|
|
||||||
|
# test.1.foo.example.com -> WORD.test.1.foo.example.com,
|
||||||
|
# test.WORD.1.foo.example.com,
|
||||||
|
# test.1.WORD.foo.example.com,
|
||||||
|
# test.1.foo.WORD.example.com,
|
||||||
|
# ...
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for word in self.words:
|
||||||
|
for index in range(len(parts)):
|
||||||
|
tmp_parts = parts.copy()
|
||||||
|
tmp_parts.insert(index, word)
|
||||||
|
new_domain = '.'.join(tmp_parts)
|
||||||
|
self.new_subdomains.add(new_domain)
|
||||||
|
count += 1
|
||||||
|
logger.log('DEBUG', f'The insert_word generated {count} subdomains')
|
||||||
|
|
||||||
|
def add_word(self, subnames):
|
||||||
|
"""
|
||||||
|
On every subdomain level, prepend existing content with WORD-`,
|
||||||
|
append existing content with `-WORD`
|
||||||
|
"""
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for word in self.words:
|
||||||
|
for index, name in enumerate(subnames):
|
||||||
|
# Prepend with `-`
|
||||||
|
# test.1.foo.example.com -> WORD-test.1.foo.example.com
|
||||||
|
tmp_subnames = subnames.copy()
|
||||||
|
tmp_subnames[index] = f'{word}-{name}'
|
||||||
|
new_subname = '.'.join(tmp_subnames + [self.domain])
|
||||||
|
self.new_subdomains.add(new_subname)
|
||||||
|
|
||||||
|
# Prepend with `-`
|
||||||
|
# test.1.foo.example.com -> test-WORD.1.foo.example.com
|
||||||
|
tmp_subnames = subnames.copy()
|
||||||
|
tmp_subnames[index] = f'{name}-{word}'
|
||||||
|
new_subname = '.'.join(tmp_subnames + [self.domain])
|
||||||
|
self.new_subdomains.add(new_subname)
|
||||||
|
count += 1
|
||||||
|
logger.log('DEBUG', f'The add_word generated {count} subdomains')
|
||||||
|
|
||||||
|
def replace_word(self, subname):
|
||||||
|
"""
|
||||||
|
If word longer than 3 is found in existing subdomain,
|
||||||
|
replace it with other words from the dictionary
|
||||||
|
"""
|
||||||
|
|
||||||
|
# WORD1.1.foo.example.com -> WORD2.1.foo.example.com,
|
||||||
|
# WORD3.1.foo.example.com,
|
||||||
|
# WORD4.1.foo.example.com,
|
||||||
|
# ..
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for word in self.words:
|
||||||
|
if word not in subname:
|
||||||
|
continue
|
||||||
|
for word_alt in self.words:
|
||||||
|
if word == word_alt:
|
||||||
|
continue
|
||||||
|
new_subname = subname.replace(word, word_alt)
|
||||||
|
new_subdomain = f'{new_subname}.{self.domain}'
|
||||||
|
self.new_subdomains.add(new_subdomain)
|
||||||
|
count += 1
|
||||||
|
logger.log('DEBUG', f'The replace_word generated {count} subdomains')
|
||||||
|
|
||||||
|
def gen_new_subdomains(self):
|
||||||
|
for subdomain in self.now_subdomains:
|
||||||
|
subname, parts = split_domain(subdomain)
|
||||||
|
subnames = subname.split('.')
|
||||||
|
if settings.altdns_increase_num:
|
||||||
|
self.increase_num(subname)
|
||||||
|
if settings.altdns_decrease_num:
|
||||||
|
self.decrease_num(subname)
|
||||||
|
if settings.altdns_replace_word:
|
||||||
|
self.replace_word(subname)
|
||||||
|
if settings.altdns_insert_word:
|
||||||
|
self.insert_word(parts)
|
||||||
|
if settings.altdns_add_word:
|
||||||
|
self.add_word(subnames)
|
||||||
|
count = len(self.new_subdomains)
|
||||||
|
logger.log('DEBUG', f'The altdns module generated {count} subdomains')
|
||||||
|
|
||||||
|
def run(self, data, port):
|
||||||
|
logger.log('INFOR', f'Start altdns module')
|
||||||
|
self.now_subdomains = utils.get_subdomains(data)
|
||||||
|
self.get_words()
|
||||||
|
self.extract_words()
|
||||||
|
self.gen_new_subdomains()
|
||||||
|
self.subdomains = self.new_subdomains - self.now_subdomains
|
||||||
|
count = len(self.subdomains)
|
||||||
|
logger.log('INFOR', f'The altdns module generated {count} new subdomains')
|
||||||
|
self.end = time.time()
|
||||||
|
self.elapse = round(self.end - self.start, 1)
|
||||||
|
self.gen_result()
|
||||||
|
resolved_data = resolve.run_resolve(self.domain, self.results)
|
||||||
|
valid_data = wildcard.deal_wildcard(resolved_data) # 强制开启泛解析处理
|
||||||
|
request.run_request(self.domain, valid_data, port)
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
github自动接管
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
import requests
|
||||||
|
from config import settings
|
||||||
|
|
||||||
|
HEADERS = {
|
||||||
|
"Accept": "application/json, text/javascript, */*; q=0.01",
|
||||||
|
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||||
|
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) "
|
||||||
|
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||||
|
"Chrome/63.0.3239.84 Safari/537.36",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def github_takeover(url):
|
||||||
|
# 读取config配置文件
|
||||||
|
repo_name = url
|
||||||
|
print('[*]正在读取配置文件')
|
||||||
|
user = settings.github_api_user
|
||||||
|
token = settings.github_api_token
|
||||||
|
headers = {
|
||||||
|
"Authorization": 'token ' + token,
|
||||||
|
"Accept": "application/vnd.github.switcheroo-preview+json"
|
||||||
|
}
|
||||||
|
repos_url = 'https://api.github.com/repos/' + user + '/' + repo_name
|
||||||
|
repos_r = requests.get(url=repos_url, headers=headers)
|
||||||
|
# 验证token是否正确
|
||||||
|
if 'message' in repos_r.json():
|
||||||
|
if repos_r.json()['message'] == 'Bad credentials':
|
||||||
|
print('[*]请检查Token是否正确')
|
||||||
|
elif repos_r.json()['message'] == 'Not Found':
|
||||||
|
print('[*]正在生成接管库') # 生成接管库
|
||||||
|
creat_repo_dict = {
|
||||||
|
"name": repo_name,
|
||||||
|
"description": "This is a subdomain takeover Repository",
|
||||||
|
}
|
||||||
|
creat_repo_url = 'https://api.github.com/user/repos'
|
||||||
|
creat_repo_r = requests.post(url=creat_repo_url,
|
||||||
|
headers=headers,
|
||||||
|
data=json.dumps(creat_repo_dict))
|
||||||
|
creat_repo_status = creat_repo_r.status_code
|
||||||
|
if creat_repo_status == 201:
|
||||||
|
print('[*]创建接管库' + repo_name + '成功,正在进行自动接管')
|
||||||
|
# 接管文件生成
|
||||||
|
# index.html文件
|
||||||
|
html = b'''
|
||||||
|
<html>
|
||||||
|
<p>Subdomain Takerover Test!</>
|
||||||
|
</html>
|
||||||
|
'''
|
||||||
|
html64 = base64.b64encode(html).decode('utf-8')
|
||||||
|
html_dict = {
|
||||||
|
"message": "my commit message",
|
||||||
|
"committer": {
|
||||||
|
"name": "user", # 提交id,非必改项
|
||||||
|
"email": "user@163.com" # 同上
|
||||||
|
},
|
||||||
|
"content": html64
|
||||||
|
}
|
||||||
|
# CNAME文件
|
||||||
|
cname_url = bytes(url, encoding='utf-8')
|
||||||
|
cname_url64 = base64.b64encode(cname_url).decode('utf-8')
|
||||||
|
url_dict = {
|
||||||
|
"message": "my commit message",
|
||||||
|
"committer": {
|
||||||
|
"name": "user",
|
||||||
|
"email": "user@163.com"
|
||||||
|
},
|
||||||
|
"content": cname_url64
|
||||||
|
}
|
||||||
|
base_url = 'https://api.github.com/repos/'
|
||||||
|
html_url = base_url + user + '/' + repo_name + '/contents/index.html'
|
||||||
|
url_url = base_url + user + '/' + repo_name + '/contents/CNAME'
|
||||||
|
html_r = requests.put(url=html_url, data=json.dumps(html_dict),
|
||||||
|
headers=headers) # 上传index.html
|
||||||
|
cname_r = requests.put(url=url_url, data=json.dumps(url_dict),
|
||||||
|
headers=headers) # 上传CNAME
|
||||||
|
rs = cname_r.status_code
|
||||||
|
if rs == 201:
|
||||||
|
print('[*]生成接管库成功,正在开启Github pages')
|
||||||
|
page_url = "https://api.github.com/repos/" + user + "/" + url + "/pages"
|
||||||
|
page_dict = {
|
||||||
|
"source": {
|
||||||
|
"branch": "master"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page_r = requests.post(url=page_url,
|
||||||
|
data=json.dumps(page_dict),
|
||||||
|
headers=headers) # 开启page
|
||||||
|
if page_r.status_code == 201:
|
||||||
|
print('[+]自动接管成功,请稍后访问http://' + str(url) + '查看结果')
|
||||||
|
else:
|
||||||
|
print('[+]开启Github pages失败,请检查网络或稍后重试')
|
||||||
|
else:
|
||||||
|
print('[+]生成接管库失败,请检查网络或稍后重试')
|
||||||
|
elif url in repos_r.json()['name']:
|
||||||
|
print('[*]生成接管库失败,请检查https://github.com/' + user +
|
||||||
|
'?tab=repositories是否存在同名接管库')
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
from config import settings
|
||||||
|
from common.query import Query
|
||||||
|
from config.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
class CensysAPI(Query):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Query.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'Certificate'
|
||||||
|
self.source = "CensysAPIQuery"
|
||||||
|
self.addr = 'https://search.censys.io/api/v2/certificates/search'
|
||||||
|
self.id = settings.censys_api_id
|
||||||
|
self.secret = settings.censys_api_secret
|
||||||
|
self.delay = 3.0 # Censys 接口查询速率限制 最快2.5秒查1次
|
||||||
|
|
||||||
|
def query(self):
|
||||||
|
"""
|
||||||
|
向接口查询子域并做子域匹配
|
||||||
|
"""
|
||||||
|
self.header = self.get_header()
|
||||||
|
self.proxy = self.get_proxy(self.source)
|
||||||
|
params = {
|
||||||
|
'q': f'names: {self.domain}',
|
||||||
|
'per_page': 100,
|
||||||
|
}
|
||||||
|
resp = self.get(self.addr, params=params, auth=(self.id, self.secret))
|
||||||
|
if not resp:
|
||||||
|
return
|
||||||
|
json = resp.json()
|
||||||
|
status = json.get('status')
|
||||||
|
if status != 'OK':
|
||||||
|
logger.log('ALERT', f'{self.source} module {status}')
|
||||||
|
return
|
||||||
|
subdomains = self.match_subdomains(resp.text)
|
||||||
|
self.subdomains.update(subdomains)
|
||||||
|
next_cursor = json.get("result").get("links").get("next")
|
||||||
|
while next_cursor:
|
||||||
|
tmp_params = {
|
||||||
|
'q': f'names: {self.domain}',
|
||||||
|
'per_page': 100,
|
||||||
|
"cursor": next_cursor
|
||||||
|
}
|
||||||
|
tmp_resp = self.get(self.addr, params=tmp_params, auth=(self.id, self.secret))
|
||||||
|
self.subdomains = self.collect_subdomains(tmp_resp)
|
||||||
|
next_cursor = tmp_resp.json().get("result").get("links").get("next")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
if not self.have_api(self.id, self.secret):
|
||||||
|
return
|
||||||
|
self.begin()
|
||||||
|
self.query()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
|
query = CensysAPI(domain)
|
||||||
|
query.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('example.com')
|
||||||
@@ -1,54 +1,48 @@
|
|||||||
import time
|
from common.query import Query
|
||||||
from common import utils
|
|
||||||
from common.query import Query
|
|
||||||
|
class CertSpotter(Query):
|
||||||
|
def __init__(self, domain):
|
||||||
class CertSpotter(Query):
|
Query.__init__(self)
|
||||||
def __init__(self, domain):
|
self.domain = domain
|
||||||
Query.__init__(self)
|
self.module = 'Certificate'
|
||||||
self.domain = domain
|
self.source = 'CertSpotterQuery'
|
||||||
self.module = 'Certificate'
|
self.addr = 'https://api.certspotter.com/v1/issuances'
|
||||||
self.source = 'CertSpotterQuery'
|
|
||||||
self.addr = 'https://api.certspotter.com/v1/issuances'
|
def query(self):
|
||||||
|
"""
|
||||||
def query(self):
|
向接口查询子域并做子域匹配
|
||||||
"""
|
"""
|
||||||
向接口查询子域并做子域匹配
|
self.header = self.get_header()
|
||||||
"""
|
self.proxy = self.get_proxy(self.source)
|
||||||
time.sleep(self.delay)
|
params = {'domain': self.domain,
|
||||||
self.header = self.get_header()
|
'include_subdomains': 'true',
|
||||||
self.proxy = self.get_proxy(self.source)
|
'expand': 'dns_names'}
|
||||||
params = {'domain': self.domain, 'include_subdomains': 'true',
|
resp = self.get(self.addr, params)
|
||||||
'expand': 'dns_names'}
|
self.subdomains = self.collect_subdomains(resp)
|
||||||
resp = self.get(self.addr, params)
|
|
||||||
if not resp:
|
def run(self):
|
||||||
return
|
"""
|
||||||
subdomains_find = utils.match_subdomain(self.domain, str(resp.json()))
|
类执行入口
|
||||||
# 合并搜索子域名搜索结果
|
"""
|
||||||
self.subdomains = self.subdomains.union(subdomains_find)
|
self.begin()
|
||||||
|
self.query()
|
||||||
def run(self):
|
self.finish()
|
||||||
"""
|
self.save_json()
|
||||||
类执行入口
|
self.gen_result()
|
||||||
"""
|
self.save_db()
|
||||||
self.begin()
|
|
||||||
self.query()
|
|
||||||
self.finish()
|
def run(domain):
|
||||||
self.save_json()
|
"""
|
||||||
self.gen_result()
|
类统一调用入口
|
||||||
self.save_db()
|
|
||||||
|
:param str domain: 域名
|
||||||
|
|
||||||
def do(domain): # 统一入口名字 方便多线程调用
|
"""
|
||||||
"""
|
query = CertSpotter(domain)
|
||||||
类统一调用入口
|
query.run()
|
||||||
|
|
||||||
:param str domain: 域名
|
|
||||||
|
if __name__ == '__main__':
|
||||||
"""
|
run('example.com')
|
||||||
query = CertSpotter(domain)
|
|
||||||
query.run()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
do('example.com')
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
from common.query import Query
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class Crtsh(Query):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Query.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'Certificate'
|
||||||
|
self.source = 'CrtshQuery'
|
||||||
|
self.addr = 'https://crt.sh/'
|
||||||
|
|
||||||
|
def query(self):
|
||||||
|
"""
|
||||||
|
向接口查询子域并做子域匹配
|
||||||
|
"""
|
||||||
|
self.header = self.get_header()
|
||||||
|
self.proxy = self.get_proxy(self.source)
|
||||||
|
self.timeout = 120
|
||||||
|
params = {'q': f'%.{self.domain}', 'output': 'json'}
|
||||||
|
resp = self.get(self.addr, params)
|
||||||
|
if not resp:
|
||||||
|
return
|
||||||
|
text = resp.text.replace(r'\n', ' ')
|
||||||
|
"""
|
||||||
|
* > altdns
|
||||||
|
"""
|
||||||
|
subDomains = set()
|
||||||
|
try:
|
||||||
|
jsonData = json.loads(text)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
for i in range(len(jsonData)):
|
||||||
|
try:
|
||||||
|
name_value = str(jsonData[i]['name_value'])
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
if '*' in name_value:
|
||||||
|
try:
|
||||||
|
if 'certificates' in os.path.dirname(os.path.abspath(__file__)):
|
||||||
|
dictFile = open("../../data/altdns_wordlist.txt", "r", encoding='utf8')
|
||||||
|
else:
|
||||||
|
dictFile = open("./data/altdns_wordlist.txt", "r", encoding='utf8')
|
||||||
|
for line in dictFile.readlines():
|
||||||
|
altdns = line.strip()
|
||||||
|
result = name_value.replace('*', altdns)
|
||||||
|
if self.domain in result:
|
||||||
|
subDomains.add(result)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
if len(subDomains) > 0:
|
||||||
|
for x in subDomains:
|
||||||
|
text = text + ',' + x + ','
|
||||||
|
"""
|
||||||
|
* > altdns end
|
||||||
|
"""
|
||||||
|
subdomains = self.match_subdomains(text)
|
||||||
|
self.subdomains.update(subdomains)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
self.begin()
|
||||||
|
self.query()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
|
query = Crtsh(domain)
|
||||||
|
query.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('163.com')
|
||||||
@@ -1,54 +1,48 @@
|
|||||||
import time
|
from common.query import Query
|
||||||
from common import utils
|
|
||||||
from common.query import Query
|
|
||||||
|
class Google(Query):
|
||||||
|
def __init__(self, domain):
|
||||||
class Google(Query):
|
Query.__init__(self)
|
||||||
def __init__(self, domain):
|
self.domain = domain
|
||||||
Query.__init__(self)
|
self.module = 'Certificate'
|
||||||
self.domain = self.register(domain)
|
self.source = 'GoogleQuery'
|
||||||
self.module = 'Certificate'
|
self.addr = 'https://transparencyreport.google.com/' \
|
||||||
self.source = 'GoogleQuery'
|
'transparencyreport/api/v3/httpsreport/ct/certsearch'
|
||||||
self.addr = 'https://transparencyreport.google.com/' \
|
|
||||||
'transparencyreport/api/v3/httpsreport/ct/certsearch'
|
def query(self):
|
||||||
|
"""
|
||||||
def query(self):
|
向接口查询子域并做子域匹配
|
||||||
"""
|
"""
|
||||||
向接口查询子域并做子域匹配
|
self.header = self.get_header()
|
||||||
"""
|
self.proxy = self.get_proxy(self.source)
|
||||||
time.sleep(self.delay)
|
params = {'include_expired': 'true',
|
||||||
self.header = self.get_header()
|
'include_subdomains': 'true',
|
||||||
self.proxy = self.get_proxy(self.source)
|
'domain': self.domain}
|
||||||
params = {'include_expired': 'true', 'include_subdomains': 'true',
|
resp = self.get(self.addr, params)
|
||||||
'domain': self.domain}
|
self.subdomains = self.collect_subdomains(resp)
|
||||||
resp = self.get(self.addr, params)
|
|
||||||
if not resp:
|
def run(self):
|
||||||
return
|
"""
|
||||||
subdomains_find = utils.match_subdomain(self.domain, resp.text)
|
类执行入口
|
||||||
# 合并搜索子域名搜索结果
|
"""
|
||||||
self.subdomains = self.subdomains.union(subdomains_find)
|
self.begin()
|
||||||
|
self.query()
|
||||||
def run(self):
|
self.finish()
|
||||||
"""
|
self.save_json()
|
||||||
类执行入口
|
self.gen_result()
|
||||||
"""
|
self.save_db()
|
||||||
self.begin()
|
|
||||||
self.query()
|
|
||||||
self.finish()
|
def run(domain):
|
||||||
self.save_json()
|
"""
|
||||||
self.gen_result()
|
类统一调用入口
|
||||||
self.save_db()
|
|
||||||
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
def do(domain): # 统一入口名字 方便多线程调用
|
query = Google(domain)
|
||||||
"""
|
query.run()
|
||||||
类统一调用入口
|
|
||||||
|
|
||||||
:param str domain: 域名
|
if __name__ == '__main__':
|
||||||
"""
|
run('example.com')
|
||||||
query = Google(domain)
|
|
||||||
query.run()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
do('example.com')
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
from common.query import Query
|
||||||
|
|
||||||
|
|
||||||
|
class MySSL(Query):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Query.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'Certificate'
|
||||||
|
self.source = 'MySSLQuery'
|
||||||
|
self.addr = 'https://myssl.com/api/v1/discover_sub_domain'
|
||||||
|
|
||||||
|
def query(self):
|
||||||
|
"""
|
||||||
|
向接口查询子域并做子域匹配
|
||||||
|
"""
|
||||||
|
self.header = self.get_header()
|
||||||
|
self.proxy = self.get_proxy(self.source)
|
||||||
|
params = {'domain': self.domain}
|
||||||
|
resp = self.get(self.addr, params)
|
||||||
|
self.subdomains = self.collect_subdomains(resp)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
self.begin()
|
||||||
|
self.query()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
|
||||||
|
"""
|
||||||
|
query = MySSL(domain)
|
||||||
|
query.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('freebuf.com')
|
||||||
@@ -1,51 +1,49 @@
|
|||||||
import time
|
from config import settings
|
||||||
from common import utils
|
from common.query import Query
|
||||||
from common.query import Query
|
|
||||||
|
|
||||||
|
class Racent(Query):
|
||||||
class Crtsh(Query):
|
def __init__(self, domain):
|
||||||
def __init__(self, domain):
|
Query.__init__(self)
|
||||||
Query.__init__(self)
|
self.domain = domain
|
||||||
self.domain = self.register(domain)
|
self.module = 'Certificate'
|
||||||
self.module = 'Certificate'
|
self.source = 'RacentQuery'
|
||||||
self.source = 'CrtshQuery'
|
self.addr = 'https://face.racent.com/tool/query_ctlog'
|
||||||
self.addr = 'https://crt.sh/'
|
self.api = settings.racent_api_token
|
||||||
|
|
||||||
def query(self):
|
def query(self):
|
||||||
"""
|
"""
|
||||||
向接口查询子域并做子域匹配
|
向接口查询子域并做子域匹配
|
||||||
"""
|
"""
|
||||||
time.sleep(self.delay)
|
self.header = self.get_header()
|
||||||
self.header = self.get_header()
|
self.proxy = self.get_proxy(self.source)
|
||||||
self.proxy = self.get_proxy(self.source)
|
params = {'token': self.api, 'keyword': self.domain}
|
||||||
params = {'q': f'%.{self.domain}', 'output': 'json'}
|
resp = self.get(self.addr, params)
|
||||||
resp = self.get(self.addr, params)
|
self.subdomains = self.collect_subdomains(resp)
|
||||||
if not resp:
|
|
||||||
return
|
def run(self):
|
||||||
subdomains_find = utils.match_subdomain(self.domain, str(resp.json()))
|
"""
|
||||||
self.subdomains = self.subdomains.union(subdomains_find)
|
类执行入口
|
||||||
|
"""
|
||||||
def run(self):
|
if not self.have_api(self.api):
|
||||||
"""
|
return
|
||||||
类执行入口
|
self.begin()
|
||||||
"""
|
self.query()
|
||||||
self.begin()
|
self.finish()
|
||||||
self.query()
|
self.save_json()
|
||||||
self.finish()
|
self.gen_result()
|
||||||
self.save_json()
|
self.save_db()
|
||||||
self.gen_result()
|
|
||||||
self.save_db()
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
def do(domain): # 统一入口名字 方便多线程调用
|
类统一调用入口
|
||||||
"""
|
|
||||||
类统一调用入口
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
:param str domain: 域名
|
query = Racent(domain)
|
||||||
"""
|
query.run()
|
||||||
query = Crtsh(domain)
|
|
||||||
query.run()
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('example.com')
|
||||||
if __name__ == '__main__':
|
|
||||||
do('example.com')
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
"""
|
||||||
|
查询域名的NS记录(域名服务器记录,记录该域名由哪台域名服务器解析),检查查出的域名服务器是
|
||||||
|
否开启DNS域传送,如果开启且没做访问控制和身份验证便加以利用获取域名的所有记录。
|
||||||
|
|
||||||
|
DNS域传送(DNS zone transfer)指的是一台备用域名服务器使用来自主域名服务器的数据刷新自己
|
||||||
|
的域数据库,目的是为了做冗余备份,防止主域名服务器出现故障时 dns 解析不可用。
|
||||||
|
当主服务器开启DNS域传送同时又对来请求的备用服务器未作访问控制和身份验证便可以利用此漏洞获
|
||||||
|
取某个域的所有记录。
|
||||||
|
"""
|
||||||
|
import dns.resolver
|
||||||
|
import dns.zone
|
||||||
|
|
||||||
|
from common import utils
|
||||||
|
from common.check import Check
|
||||||
|
from config.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
class AXFR(Check):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Check.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'check'
|
||||||
|
self.source = 'AXFRCheck'
|
||||||
|
self.results = []
|
||||||
|
|
||||||
|
def axfr(self, server):
|
||||||
|
"""
|
||||||
|
Perform domain transfer
|
||||||
|
|
||||||
|
:param server: domain server
|
||||||
|
"""
|
||||||
|
logger.log('DEBUG', f'Trying to perform domain transfer in {server} '
|
||||||
|
f'of {self.domain}')
|
||||||
|
try:
|
||||||
|
xfr = dns.query.xfr(where=server, zone=self.domain,
|
||||||
|
timeout=5.0, lifetime=10.0)
|
||||||
|
zone = dns.zone.from_xfr(xfr)
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('DEBUG', e.args)
|
||||||
|
logger.log('DEBUG', f'Domain transfer to server {server} of '
|
||||||
|
f'{self.domain} failed')
|
||||||
|
return
|
||||||
|
names = zone.nodes.keys()
|
||||||
|
for name in names:
|
||||||
|
full_domain = str(name) + '.' + self.domain
|
||||||
|
subdomain = self.match_subdomains(full_domain)
|
||||||
|
self.subdomains.update(subdomain)
|
||||||
|
record = zone[name].to_text(name)
|
||||||
|
self.results.append(record)
|
||||||
|
if self.results:
|
||||||
|
logger.log('DEBUG', f'Found the domain transfer record of '
|
||||||
|
f'{self.domain} on {server}')
|
||||||
|
logger.log('DEBUG', '\n'.join(self.results))
|
||||||
|
self.results = []
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
"""
|
||||||
|
check
|
||||||
|
"""
|
||||||
|
resolver = utils.dns_resolver()
|
||||||
|
try:
|
||||||
|
answers = resolver.query(self.domain, "NS")
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('ERROR', e.args)
|
||||||
|
return
|
||||||
|
nsservers = [str(answer) for answer in answers]
|
||||||
|
if not len(nsservers):
|
||||||
|
logger.log('ALERT', f'No name server record found for {self.domain}')
|
||||||
|
return
|
||||||
|
for nsserver in nsservers:
|
||||||
|
self.axfr(nsserver)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
self.begin()
|
||||||
|
self.check()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
|
check = AXFR(domain)
|
||||||
|
check.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('ZoneTransfer.me')
|
||||||
|
# run('example.com')
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
"""
|
||||||
|
检查crossdomain.xml文件收集子域名
|
||||||
|
"""
|
||||||
|
from common.check import Check
|
||||||
|
|
||||||
|
|
||||||
|
class CrossDomain(Check):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Check.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'check'
|
||||||
|
self.source = "CrossDomainCheck"
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
"""
|
||||||
|
检查crossdomain.xml收集子域名
|
||||||
|
"""
|
||||||
|
filenames = {'crossdomain.xml'}
|
||||||
|
self.to_check(filenames)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
self.begin()
|
||||||
|
self.check()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param domain: 域名
|
||||||
|
"""
|
||||||
|
check = CrossDomain(domain)
|
||||||
|
check.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('example.com')
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
"""
|
||||||
|
检查域名证书收集子域名
|
||||||
|
"""
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
|
||||||
|
from config.log import logger
|
||||||
|
from common.check import Check
|
||||||
|
|
||||||
|
|
||||||
|
class CertInfo(Check):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Check.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'check'
|
||||||
|
self.source = 'CertInfo'
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
"""
|
||||||
|
获取域名证书并匹配证书中的子域名
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
sock = socket.socket()
|
||||||
|
sock.settimeout(10)
|
||||||
|
wrap_sock = ctx.wrap_socket(sock, server_hostname=self.domain)
|
||||||
|
wrap_sock.connect((self.domain, 443))
|
||||||
|
cert_dict = wrap_sock.getpeercert()
|
||||||
|
except Exception as e:
|
||||||
|
logger.log('DEBUG', e.args)
|
||||||
|
return
|
||||||
|
subdomains = self.match_subdomains(str(cert_dict))
|
||||||
|
self.subdomains.update(subdomains)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
self.begin()
|
||||||
|
self.check()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
|
check = CertInfo(domain)
|
||||||
|
check.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('example.com')
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
"""
|
||||||
|
Collect subdomains from ContentSecurityPolicy
|
||||||
|
"""
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from config.log import logger
|
||||||
|
from common.check import Check
|
||||||
|
|
||||||
|
|
||||||
|
class CSP(Check):
|
||||||
|
"""
|
||||||
|
Collect subdomains from ContentSecurityPolicy
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, domain, header):
|
||||||
|
Check.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'check'
|
||||||
|
self.source = 'CSPCheck'
|
||||||
|
self.csp_header = header
|
||||||
|
|
||||||
|
@property
|
||||||
|
def grab_header(self):
|
||||||
|
"""
|
||||||
|
Get header
|
||||||
|
|
||||||
|
:return: ContentSecurityPolicy header
|
||||||
|
"""
|
||||||
|
csp_header = dict()
|
||||||
|
urls = [f'http://{self.domain}',
|
||||||
|
f'https://{self.domain}']
|
||||||
|
urls_www = [f'http://www.{self.domain}',
|
||||||
|
f'https://www.{self.domain}']
|
||||||
|
header = self.grab_loop(csp_header, urls)
|
||||||
|
if header:
|
||||||
|
return header
|
||||||
|
header = self.grab_loop(csp_header, urls_www)
|
||||||
|
return header
|
||||||
|
|
||||||
|
def grab_loop(self, csp_header, urls):
|
||||||
|
for url in urls:
|
||||||
|
self.header = self.get_header()
|
||||||
|
self.proxy = self.get_proxy(self.source)
|
||||||
|
try:
|
||||||
|
response = self.get(url, check=False, ignore=True, raise_error=True)
|
||||||
|
except requests.exceptions.ConnectTimeout:
|
||||||
|
logger.log('DEBUG', f'Connection to {url} timed out, so break check')
|
||||||
|
break
|
||||||
|
if response:
|
||||||
|
return response.headers
|
||||||
|
return csp_header
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
"""
|
||||||
|
正则匹配响应头中的内容安全策略字段以发现子域名
|
||||||
|
"""
|
||||||
|
if not self.csp_header:
|
||||||
|
self.csp_header = self.grab_header
|
||||||
|
csp = self.csp_header.get('Content-Security-Policy')
|
||||||
|
if not self.csp_header:
|
||||||
|
logger.log('DEBUG', f'Failed to get header of {self.domain} domain')
|
||||||
|
return
|
||||||
|
if not csp:
|
||||||
|
logger.log('DEBUG', f'There is no Content-Security-Policy in the header '
|
||||||
|
f'of {self.domain}')
|
||||||
|
return
|
||||||
|
self.subdomains = self.match_subdomains(csp)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
self.begin()
|
||||||
|
self.check()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain, header=None):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
:param dict or None header: 响应头
|
||||||
|
"""
|
||||||
|
check = CSP(domain, header)
|
||||||
|
check.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
resp = requests.get('https://content-security-policy.com/')
|
||||||
|
run('google-analytics.com', dict(resp.headers))
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# https://www.icann.org/resources/pages/dnssec-what-is-it-why-important-2019-03-20-zh
|
||||||
|
# https://appsecco.com/books/subdomain-enumeration/active_techniques/zone_walking.html
|
||||||
|
|
||||||
|
from common import utils
|
||||||
|
from common.check import Check
|
||||||
|
|
||||||
|
|
||||||
|
class NSEC(Check):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Check.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'check'
|
||||||
|
self.source = "NSECCheck"
|
||||||
|
|
||||||
|
def walk(self):
|
||||||
|
domain = self.domain
|
||||||
|
while True:
|
||||||
|
answer = utils.dns_query(domain, 'NSEC')
|
||||||
|
if answer is None:
|
||||||
|
break
|
||||||
|
subdomain = str()
|
||||||
|
for item in answer:
|
||||||
|
record = item.to_text()
|
||||||
|
subdomains = self.match_subdomains(record)
|
||||||
|
subdomain = ''.join(subdomains) # 其实这里的subdomains的长度为1 也就是说只会有一个子域
|
||||||
|
self.subdomains.update(subdomains)
|
||||||
|
if subdomain == self.domain: # 当查出子域为主域 说明完成了一个循环 不再继续查询
|
||||||
|
break
|
||||||
|
if domain != self.domain: # 防止出现wwdmas.cn 000.000.wwdmas.cn 000.000.000.wwdmas.cn情况
|
||||||
|
if domain.split('.')[0] == subdomain.split('.')[0]:
|
||||||
|
break
|
||||||
|
domain = subdomain
|
||||||
|
return self.subdomains
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
self.begin()
|
||||||
|
self.walk()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
|
brute = NSEC(domain)
|
||||||
|
brute.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('iana.org')
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
"""
|
||||||
|
检查内容安全策略收集子域名收集子域名
|
||||||
|
"""
|
||||||
|
from common.check import Check
|
||||||
|
|
||||||
|
|
||||||
|
class Robots(Check):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Check.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'check'
|
||||||
|
self.source = 'RobotsCheck'
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
"""
|
||||||
|
正则匹配域名的robots.txt文件中的子域
|
||||||
|
"""
|
||||||
|
filenames = {'robots.txt'}
|
||||||
|
self.to_check(filenames)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
self.begin()
|
||||||
|
self.check()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
|
check = Robots(domain)
|
||||||
|
check.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('qq.com')
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
"""
|
||||||
|
检查内容安全策略收集子域名收集子域名
|
||||||
|
"""
|
||||||
|
from common.check import Check
|
||||||
|
|
||||||
|
|
||||||
|
class Sitemap(Check):
|
||||||
|
def __init__(self, domain):
|
||||||
|
Check.__init__(self)
|
||||||
|
self.domain = domain
|
||||||
|
self.module = 'check'
|
||||||
|
self.source = 'SitemapCheck'
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
"""
|
||||||
|
正则匹配域名的sitemap文件中的子域
|
||||||
|
"""
|
||||||
|
filenames = {'sitemap.xml', 'sitemap.txt', 'sitemap.html', 'sitemapindex.xml'}
|
||||||
|
self.to_check(filenames)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
类执行入口
|
||||||
|
"""
|
||||||
|
self.begin()
|
||||||
|
self.check()
|
||||||
|
self.finish()
|
||||||
|
self.save_json()
|
||||||
|
self.gen_result()
|
||||||
|
self.save_db()
|
||||||
|
|
||||||
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
|
类统一调用入口
|
||||||
|
|
||||||
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
|
check = Sitemap(domain)
|
||||||
|
check.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('qq.com')
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import threading
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
from config.log import logger
|
||||||
|
from config import settings
|
||||||
|
|
||||||
|
|
||||||
|
class Collect(object):
|
||||||
|
def __init__(self, domain):
|
||||||
|
self.domain = domain
|
||||||
|
self.modules = []
|
||||||
|
self.collect_funcs = []
|
||||||
|
|
||||||
|
def get_mod(self):
|
||||||
|
"""
|
||||||
|
Get modules
|
||||||
|
"""
|
||||||
|
if settings.enable_all_module:
|
||||||
|
# The crawl module has some problems
|
||||||
|
modules = ['certificates', 'check', 'datasets',
|
||||||
|
'dnsquery', 'intelligence', 'search']
|
||||||
|
for module in modules:
|
||||||
|
module_path = settings.module_dir.joinpath(module)
|
||||||
|
for path in module_path.rglob('*.py'):
|
||||||
|
import_module = f'modules.{module}.{path.stem}'
|
||||||
|
self.modules.append(import_module)
|
||||||
|
else:
|
||||||
|
self.modules = settings.enable_partial_module
|
||||||
|
|
||||||
|
def import_func(self):
|
||||||
|
"""
|
||||||
|
Import do function
|
||||||
|
"""
|
||||||
|
for module in self.modules:
|
||||||
|
name = module.split('.')[-1]
|
||||||
|
import_object = importlib.import_module(module)
|
||||||
|
func = getattr(import_object, 'run')
|
||||||
|
self.collect_funcs.append([func, name])
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""
|
||||||
|
Class entrance
|
||||||
|
"""
|
||||||
|
logger.log('INFOR', f'Start collecting subdomains of {self.domain}')
|
||||||
|
self.get_mod()
|
||||||
|
self.import_func()
|
||||||
|
|
||||||
|
threads = []
|
||||||
|
# Create subdomain collection threads
|
||||||
|
for func_obj, func_name in self.collect_funcs:
|
||||||
|
thread = threading.Thread(target=func_obj, name=func_name,
|
||||||
|
args=(self.domain,), daemon=True)
|
||||||
|
threads.append(thread)
|
||||||
|
# Start all threads
|
||||||
|
for thread in threads:
|
||||||
|
thread.start()
|
||||||
|
# Wait for all threads to finish
|
||||||
|
for thread in threads:
|
||||||
|
# 挨个线程判断超时 最坏情况主线程阻塞时间=线程数*module_thread_timeout
|
||||||
|
# 超时线程将脱离主线程 由于创建线程时已添加守护属于 所有超时线程会随着主线程结束
|
||||||
|
thread.join(settings.module_thread_timeout)
|
||||||
|
|
||||||
|
for thread in threads:
|
||||||
|
if thread.is_alive():
|
||||||
|
logger.log('ALERT', f'{thread.name} module thread timed out')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
collect = Collect('example.com')
|
||||||
|
collect.run()
|
||||||
@@ -1,61 +1,59 @@
|
|||||||
import cdx_toolkit
|
import cdx_toolkit
|
||||||
from common.crawl import Crawl
|
from common.crawl import Crawl
|
||||||
from config import logger
|
from config.log import logger
|
||||||
|
|
||||||
|
|
||||||
class ArchiveCrawl(Crawl):
|
class ArchiveCrawl(Crawl):
|
||||||
def __init__(self, domain):
|
def __init__(self, domain):
|
||||||
Crawl.__init__(self)
|
Crawl.__init__(self)
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
self.module = 'Crawl'
|
self.module = 'Crawl'
|
||||||
self.source = 'ArchiveCrawl'
|
self.source = 'ArchiveCrawl'
|
||||||
|
|
||||||
def crawl(self, domain, limit):
|
def crawl(self, domain, limit):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param domain:
|
:param domain:
|
||||||
:param limit:
|
:param limit:
|
||||||
"""
|
"""
|
||||||
self.header = self.get_header()
|
self.header = self.get_header()
|
||||||
self.proxy = self.get_proxy(self.source)
|
self.proxy = self.get_proxy(self.source)
|
||||||
cdx = cdx_toolkit.CDXFetcher(source='ia')
|
cdx = cdx_toolkit.CDXFetcher(source='ia')
|
||||||
url = f'*.{domain}/*'
|
url = f'*.{domain}/*'
|
||||||
size = cdx.get_size_estimate(url)
|
size = cdx.get_size_estimate(url)
|
||||||
logger.log('DEBUG', f'{url} ArchiveCrawl size estimate {size}')
|
logger.log('DEBUG', f'{url} ArchiveCrawl size estimate {size}')
|
||||||
|
|
||||||
for resp in cdx.iter(url, limit=limit):
|
for resp in cdx.iter(url, limit=limit):
|
||||||
if resp.data.get('status') not in ['301', '302']:
|
if resp.data.get('status') not in ['301', '302']:
|
||||||
url = resp.data.get('url')
|
url = resp.data.get('url')
|
||||||
subdomains_find = self.match(self.register(domain),
|
subdomains = self.match_subdomains(domain, url + resp.text)
|
||||||
url + resp.text)
|
self.subdomains.update(subdomains)
|
||||||
# 合并搜索子域名搜索结果
|
|
||||||
self.subdomains = self.subdomains.union(subdomains_find)
|
def run(self):
|
||||||
|
"""
|
||||||
def run(self):
|
类执行入口
|
||||||
"""
|
"""
|
||||||
类执行入口
|
self.begin()
|
||||||
"""
|
self.crawl(self.domain, 50)
|
||||||
self.begin()
|
# 爬取已发现的子域以发现新的子域
|
||||||
self.crawl(self.domain, 50)
|
for subdomain in self.subdomains:
|
||||||
# 爬取已发现的子域以发现新的子域
|
if subdomain != self.domain:
|
||||||
for subdomain in self.subdomains:
|
self.crawl(subdomain, 10)
|
||||||
if subdomain != self.domain:
|
self.finish()
|
||||||
self.crawl(subdomain, 10)
|
self.save_json()
|
||||||
self.finish()
|
self.gen_result()
|
||||||
self.save_json()
|
self.save_db()
|
||||||
self.gen_result()
|
|
||||||
self.save_db()
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
def do(domain): # 统一入口名字 方便多线程调用
|
类统一调用入口
|
||||||
"""
|
|
||||||
类统一调用入口
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
:param str domain: 域名
|
crawl = ArchiveCrawl(domain)
|
||||||
"""
|
crawl.run()
|
||||||
crawl = ArchiveCrawl(domain)
|
|
||||||
crawl.run()
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('example.com')
|
||||||
if __name__ == '__main__':
|
|
||||||
do('example.com')
|
|
||||||
@@ -1,61 +1,59 @@
|
|||||||
import cdx_toolkit
|
import cdx_toolkit
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from common.crawl import Crawl
|
from common.crawl import Crawl
|
||||||
from config import logger
|
|
||||||
|
|
||||||
|
class CommonCrawl(Crawl):
|
||||||
class CommonCrawl(Crawl):
|
def __init__(self, domain):
|
||||||
def __init__(self, domain):
|
Crawl.__init__(self)
|
||||||
Crawl.__init__(self)
|
self.domain = domain
|
||||||
self.domain = domain
|
self.module = 'Crawl'
|
||||||
self.module = 'Crawl'
|
self.source = 'CommonCrawl'
|
||||||
self.source = 'CommonCrawl'
|
|
||||||
|
def crawl(self, domain, limit):
|
||||||
def crawl(self, domain, limit):
|
"""
|
||||||
"""
|
|
||||||
|
:param domain:
|
||||||
:param domain:
|
:param limit:
|
||||||
:param limit:
|
"""
|
||||||
"""
|
self.header = self.get_header()
|
||||||
self.header = self.get_header()
|
self.proxy = self.get_proxy(self.source)
|
||||||
self.proxy = self.get_proxy(self.source)
|
cdx = cdx_toolkit.CDXFetcher()
|
||||||
cdx = cdx_toolkit.CDXFetcher()
|
url = f'*.{domain}/*'
|
||||||
url = f'*.{domain}/*'
|
size = cdx.get_size_estimate(url)
|
||||||
size = cdx.get_size_estimate(url)
|
print(url, 'CommonCrawl size estimate', size)
|
||||||
print(url, 'CommonCrawl size estimate', size)
|
|
||||||
|
for resp in tqdm(cdx.iter(url, limit=limit), total=limit):
|
||||||
for resp in tqdm(cdx.iter(url, limit=limit), total=limit):
|
if resp.data.get('status') not in ['301', '302']:
|
||||||
if resp.data.get('status') not in ['301', '302']:
|
subdomains = self.match_subdomains(domain, resp.text)
|
||||||
subdomains_find = self.match(self.register(domain), resp.text)
|
self.subdomains.update(subdomains)
|
||||||
# 合并搜索子域名搜索结果
|
|
||||||
self.subdomains = self.subdomains.union(subdomains_find)
|
def run(self):
|
||||||
|
"""
|
||||||
def run(self):
|
类执行入口
|
||||||
"""
|
"""
|
||||||
类执行入口
|
self.begin()
|
||||||
"""
|
self.crawl(self.domain, 50)
|
||||||
self.begin()
|
# 爬取已发现的子域以发现新的子域
|
||||||
self.crawl(self.domain, 50)
|
for subdomain in self.subdomains:
|
||||||
# 爬取已发现的子域以发现新的子域
|
if subdomain != self.domain:
|
||||||
for subdomain in self.subdomains:
|
self.crawl(subdomain, 10)
|
||||||
if subdomain != self.domain:
|
self.finish()
|
||||||
self.crawl(subdomain, 10)
|
self.save_json()
|
||||||
self.finish()
|
self.gen_result()
|
||||||
self.save_json()
|
self.save_db()
|
||||||
self.gen_result()
|
|
||||||
self.save_db()
|
|
||||||
|
def run(domain):
|
||||||
|
"""
|
||||||
def do(domain): # 统一入口名字 方便多线程调用
|
类统一调用入口
|
||||||
"""
|
|
||||||
类统一调用入口
|
:param str domain: 域名
|
||||||
|
"""
|
||||||
:param str domain: 域名
|
crawl = CommonCrawl(domain)
|
||||||
"""
|
crawl.run()
|
||||||
crawl = CommonCrawl(domain)
|
|
||||||
crawl.run()
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run('example.com')
|
||||||
if __name__ == '__main__':
|
|
||||||
do('example.com')
|
|
||||||