Compare commits

..

502 Commits

Author SHA1 Message Date
Jing Ling 5f2371bc55 v0.3.0 2020-05-13 13:53:59 +08:00
Jing Ling abbad040a3 typos 2020-05-13 13:32:30 +08:00
Jing Ling 3497bbdb68 Merge remote-tracking branch 'origin/master' 2020-05-13 13:19:20 +08:00
Jing Ling 3242149c13 typos 2020-05-13 00:54:34 +08:00
Jing Ling 6df4e8df40 添加sublist3r模块 2020-05-12 22:16:41 +08:00
Jing Ling 9b81453d40 Merge pull request #86 from shmilylty/translation
translate
2020-05-12 21:09:14 +08:00
Jing Ling 314b391c52 Merge branch 'master' into translation 2020-05-12 21:05:22 +08:00
JrD 0258636827 translate 2020-05-12 20:16:46 +08:00
Jing Ling f23bb9b42e 大字典压缩 2020-05-12 17:43:38 +08:00
Jing Ling 274edba91f Add Zone walking NSEC module 2020-05-12 14:02:11 +08:00
Jing Ling d6aee35a8d Add Zone walking NSEC module 2020-05-12 13:56:01 +08:00
Jing Ling a7530b6cbe 添加利用的DNS记录的SPF记录收集子域模块 2020-05-12 13:54:32 +08:00
Jing Ling ec4a81c812 typos 2020-05-12 13:54:06 +08:00
Jing Ling 654b006a5b typos 2020-05-11 22:26:08 +08:00
Jing Ling ebf60407f8 将collect.py下放到modules目录 2020-05-11 22:25:15 +08:00
Jing Ling f08f60d9b7 更新和调整说明文档 2020-05-11 22:18:59 +08:00
Jing Ling 26fe3e4e4d 不使用Github Page 2020-05-11 21:52:06 +08:00
Jing Ling 2b7eae3a0a typos 2020-05-11 21:43:22 +08:00
Jing Ling fc7a5353d9 typos 2020-05-11 21:40:32 +08:00
Jing Ling 74d5354ead 统一调整子域匹配函数 2020-05-11 21:17:25 +08:00
Jing Ling ebd55faa9e Merge pull request #85 from shmilylty/translation
translate
2020-05-11 20:38:32 +08:00
Jing Ling 3de588385b Update brute.py 2020-05-11 20:37:42 +08:00
Jing Ling 05c2b42499 Update takeover.py 2020-05-11 20:36:47 +08:00
Jing Ling a03395cda2 Update utils.py 2020-05-11 20:30:16 +08:00
Jing Ling 8da083794f Update module.py 2020-05-11 20:25:48 +08:00
Jing Ling 8acae6c5d6 Update database.py 2020-05-11 20:21:27 +08:00
Jing Ling 823b50aa63 Update brute.py 2020-05-11 20:16:41 +08:00
Jing Ling e3d60063dc Merge branch 'master' into translation 2020-05-11 20:10:53 +08:00
JrD 038b91b52f translate 2020-05-11 18:36:50 +08:00
Jing Ling 5854c7ffa8 更新依赖 2020-05-11 16:39:00 +08:00
Jing Ling f0e3a343bf 移除js2py依赖 2020-05-11 16:29:57 +08:00
Jing Ling 6d1e8d8fb1 typos 2020-05-11 14:23:33 +08:00
Jing Ling ddd8f3dbbb 添加静默级别日志 2020-05-11 13:28:25 +08:00
Jing Ling 8e0daa48a1 Merge pull request #84 from shmilylty/translation
Translation
2020-05-11 11:01:05 +08:00
JrDw0 4f45c72d03 Update collect.py 2020-05-11 10:42:41 +08:00
JrDw0 7dfa8874ba Update dbexport.py 2020-05-11 10:28:52 +08:00
JrDw0 c12d70b56a Update brute.py 2020-05-11 10:27:00 +08:00
JrDw0 88e825a123 Update brute.py 2020-05-11 09:59:30 +08:00
Jing Ling 69744cf7cb 重构以适应新接口#82 2020-05-10 15:19:10 +08:00
Jing Ling 739518eac9 解决massdns在Centos运行异常问题 2020-05-10 03:26:58 +08:00
Jing Ling 9033b82896 更新文档 2020-05-10 03:25:07 +08:00
Jing Ling 6728c3f545 typos 2020-05-10 01:00:10 +08:00
tardis07 c8a8cd3f0f 修改docker使用说明
双语同步修改
2020-05-10 00:39:58 +08:00
Jing Ling bf1fb2e5c9 OneForAll调用brute模块默认安静模式 2020-05-09 20:37:44 +08:00
Jing Ling 9a6c2492f1 typos 2020-05-09 20:26:49 +08:00
Jing Ling 0e3584d906 添加导出纯子域结果功能 2020-05-09 18:48:16 +08:00
Jing Ling 2535b62dbe 更新 2020-05-09 18:03:09 +08:00
JrDw0 9710d2bf8d Update test.py 2020-05-09 17:54:41 +08:00
JrDw0 d824433885 Update takeover.py 2020-05-09 17:54:03 +08:00
Jing Ling d2c90c8c52 typos 2020-05-09 17:53:05 +08:00
Jing Ling 5fc6c8faad 修改为正确的日志级别 2020-05-09 17:23:33 +08:00
Jing Ling 0af745f948 Merge remote-tracking branch 'origin/master' 2020-05-09 17:18:54 +08:00
Jing Ling 98d6e6d515 ctsearch.entrust.com不能使用了 暂时移除 2020-05-09 17:13:17 +08:00
JrDw0 8d2af5d1e5 translate 2020-05-09 17:11:01 +08:00
Jing Ling baf6f8e20b exalead.com加了谷歌人机验证 暂时移除这个模块 2020-05-09 17:09:19 +08:00
JrDw0 117d4eff85 translate 2020-05-09 17:06:23 +08:00
Jing Ling d8989f7a86 优化 2020-05-09 16:43:03 +08:00
Jing Ling 83b4f1a865 优化 2020-05-09 16:25:46 +08:00
Jing Ling db5664c383 添加正确检查 2020-05-09 15:52:15 +08:00
Jing Ling e3037671d4 ptrarchive.com做了请求验证 暂时移除 2020-05-09 15:43:04 +08:00
Jing Ling ca218d78ee 将test.py放到主目录 2020-05-08 22:01:42 +08:00
Jing Ling 3bde010133 修复#80 2020-05-08 21:45:16 +08:00
Jing Ling 8920a52a0f 修改为相对导入 2020-05-08 18:44:28 +08:00
Jing Ling 5d64f69348 重构项目目录结构 2020-05-08 18:37:07 +08:00
Jing Ling e09c8b9dfc 目标调整为博客 2020-05-08 15:18:39 +08:00
Jing Ling fa5d25b8fd 调整顺序 2020-05-08 14:32:29 +08:00
Jing Ling 9fd678e1bd 解决爆破进程不为1出错的问题 2020-05-04 16:50:41 +08:00
Jing Ling ba09528944 优化 2020-05-01 17:15:23 +08:00
Jing Ling f211c9c2e3 根据#76建议优化搜索 2020-05-01 16:45:58 +08:00
Jing Ling 82073d4f84 优化网络检查 2020-04-30 10:58:33 +08:00
Jing Ling 8d3c39335d 安装uvloop 2020-04-28 22:23:57 +08:00
Jing Ling f09576ea26 使用pip3安装方式建议安装uvloop 2020-04-28 22:14:52 +08:00
Jing Ling 2486714b7b 测试时启用子域请求 2020-04-28 22:03:41 +08:00
Jing Ling 21f57aaf07 使用uvloop库 2020-04-28 22:03:11 +08:00
Jing Ling 588d397cf7 Linux和Darwin系统使用uvloop库 2020-04-28 21:48:17 +08:00
Jing Ling 2fd767fb6f 使用uvloop加速子域请求 2020-04-28 21:47:17 +08:00
Jing Ling 99af838fcb 优化无解析结果处理 2020-04-28 20:23:34 +08:00
Jing Ling e16916dc94 优化泛解析探测 2020-04-27 12:50:09 +08:00
Jing Ling 06c4f141d5 添加无有效解析结果处理 2020-04-27 11:01:07 +08:00
Jing Ling 2c883fad50 v0.2.0 2020-04-27 02:06:12 +08:00
Jing Ling 1a65897d81 默认收集线程超时为3分钟 2020-04-27 01:56:27 +08:00
Jing Ling 0d0f14d8e3 测试暂时不请求子域 2020-04-27 00:12:55 +08:00
Jing Ling b49d2242aa 不使用https 2020-04-27 00:08:52 +08:00
Jing Ling 2d9b7924ab typo 2020-04-26 23:46:59 +08:00
Jing Ling 040b478ae7 添加环境检查 2020-04-26 23:37:15 +08:00
Jing Ling 611717ff3c pep8 2020-04-26 22:25:38 +08:00
Jing Ling 0f954c79a7 Merge pull request #67 from x-Ai/patch-1
建议更改 bing_api.py 搜索方式
2020-04-26 22:24:00 +08:00
Jing Ling e76b2d039c Merge pull request #73 from boy-hack/master
更新dockerfile
2020-04-26 22:12:43 +08:00
Jing Ling 433ad720a2 优化泛解析探测 2020-04-26 13:34:48 +08:00
boy-hack 55326f01db 更新dockerfile 2020-04-26 11:17:58 +08:00
boy-hack 190ab6049c 更新readme 2020-04-26 10:46:09 +08:00
Jing Ling 58e926a149 更安全地执行massdns 2020-04-26 02:26:05 +08:00
Jing Ling 2b98d841d4 优化泛解析探测 2020-04-26 02:15:24 +08:00
Jing Ling dbc6581de7 改用自带的html.parser 2020-04-26 01:48:15 +08:00
Jing Ling 7fcf135456 更改测试域名和参数 2020-04-26 01:33:34 +08:00
Jing Ling 656b646fc1 修复结果重复问题 2020-04-26 01:26:48 +08:00
Jing Ling d91e8f429d 重构子域解析模块 2020-04-25 18:12:10 +08:00
Jing Ling edebcc6b92 修复无效子域添加到结果中的问题 2020-04-24 15:42:15 +08:00
Jing Ling 428f9ca83d 内存优化效果显著 2020-04-24 05:07:25 +08:00
Jing Ling 3ec24630db 删除github模拟登录搜索模块 2020-04-23 18:04:20 +08:00
Jing Ling e4a23fc28c 减少在处理爆破结果过程中内存的使用 2020-04-23 17:54:05 +08:00
Jing Ling 84a7530268 暂时删除duckduckgo#34 2020-04-23 15:14:41 +08:00
Jing Ling a9e2ceaf50 优化 2020-04-23 14:15:28 +08:00
Jing Ling 27d6710a21 typos 2020-04-23 11:58:59 +08:00
Jing Ling a1171b1655 bugfix 2020-04-23 11:50:15 +08:00
Jing Ling febfdc732a 优化 2020-04-23 11:43:01 +08:00
Jing Ling 13e14c4f76 优化 2020-04-23 10:41:26 +08:00
Jing Ling 8d03f29503 优化泛解析IP收集 2020-04-22 18:36:10 +08:00
Jing Ling efa9a175ca typo 2020-04-22 17:46:09 +08:00
Jing Ling 366d79bab5 更新字典说明 2020-04-22 17:44:16 +08:00
Jing Ling a77bfde23b 更新文档 2020-04-22 14:56:10 +08:00
Jing Ling 23e9a1316b 优化 2020-04-22 01:05:32 +08:00
Jing Ling 358d2bc672 修复#69 2020-04-22 01:05:12 +08:00
Jing Ling 30ae7ff21b 修复#69 2020-04-21 22:32:00 +08:00
Jing Ling f91f151234 优化超时重新探测 2020-04-21 22:12:20 +08:00
Jing Ling 1e234c9e67 探测容错处理 更新依赖 2020-04-21 18:36:47 +08:00
Jing Ling 240422631e 添加运行环境检测 2020-04-21 15:25:24 +08:00
Jing Ling 9c71ea54ba 移除$ 2020-04-20 22:36:11 +08:00
Jing Ling ba97a70970 添加RapidDNS查询 2020-04-20 22:29:48 +08:00
Jing Ling 6c37ecaff7 valid参数改为alive参数 2020-04-20 16:23:01 +08:00
Jing Ling 2e1e56774f 更新文档 2020-04-20 16:22:06 +08:00
F。 b445ce29ed Update bing_api.py
建议改为必应搜索v7,而不是继续使用必应自定义搜索。应为必应自定义搜索在配置时会要求用户指定搜索的域,并且无法使用通配形式查询。
2020-04-15 04:24:23 +08:00
Jing Ling a6390d59cc 更新文档 2020-04-14 02:34:16 +08:00
Jing Ling 269237dd8e Merge pull request #66 from Richard-Tang/master
修复bing搜索模块判断下一页条件编写错误
2020-04-14 02:06:28 +08:00
RichardTang aa4021b7d5 bing搜索模块,判断下一页条件编写错误! 2020-04-11 10:45:46 +08:00
Jing Ling 4a7fab3822 修复传入包含批量主域的文件路径无效的问题 2020-04-11 00:18:35 +08:00
Jing Ling 3b3e5551e2 Merge pull request #65 from JrDw0/patch-2
优化
2020-04-10 02:45:05 +08:00
Jing Ling 1e44b46b17 添加判断 2020-04-10 02:44:02 +08:00
Jing Ling 686325b386 优化 2020-04-10 00:24:55 +08:00
JrDw0 e736f6b0b0 优化 2020-04-09 16:38:23 +08:00
Jing Ling dd7935b69c 优化域名获取 2020-04-08 21:58:30 +08:00
Jing Ling 74f7077c51 删除例外情况 2020-04-08 21:33:20 +08:00
Jing Ling 04722df95a 优化 2020-04-07 21:38:13 +08:00
Jing Ling 8bafdeffe1 优化 2020-04-07 21:37:28 +08:00
Jing Ling 69e7efa02c 解决判断问题 2020-04-07 21:09:47 +08:00
Jing Ling 29b12196c4 优化 2020-04-07 20:15:27 +08:00
Jing Ling 60c34f6532 优化 2020-04-06 19:04:14 +08:00
Jing Ling 7b6fb65d93 优化 2020-04-06 18:11:19 +08:00
Jing Ling 7d39ebb014 优化 2020-04-06 18:04:54 +08:00
Jing Ling 30547ebc64 优化 2020-04-04 04:48:50 +08:00
Jing Ling 8a058ad524 Merge pull request #63 from JrDw0/patch-1
Merged github_api.py
2020-04-04 04:33:43 +08:00
Jing Ling 6dd35cdfc7 更新文档 2020-04-04 04:30:29 +08:00
Jing Ling b36a9842ce 添加名称服务器列表 2020-04-04 04:24:58 +08:00
Jing Ling f8af4bdc7d 添加已编译的massdns 2020-04-04 04:24:16 +08:00
Jing Ling aee7ce727d 重构子域爆破模块 2020-04-04 04:22:22 +08:00
JrDw0 1c956f5998 Create github_api.py 2020-04-03 14:03:54 +08:00
Jing Ling 4b1aafa719 添加来源模块说明 2020-03-20 02:34:14 +08:00
Jing Ling 58d831001f 添加网站普查查询接口 2020-03-20 02:17:27 +08:00
Jing Ling 991befb4c3 typos 2020-03-19 00:03:32 +08:00
Jing Ling f8b6f5ece2 调整代码位置 2020-03-18 22:04:32 +08:00
Jing Ling b51236ac51 修复一直没有退出的bug 2020-03-15 18:06:11 +08:00
Jing Ling 6868e32a84 默认关闭连接 2020-03-15 17:49:42 +08:00
Jing Ling 6431ede868 1.修改默认情况下获取进程数和协程数方式
2.更新依赖
2020-03-12 21:30:44 +08:00
Jing Ling 598f91c6f7 尝试解决macOS队列大小限制问题 2020-03-07 19:11:00 +08:00
Jing Ling 57dd7d8c1e 更新文档 2020-03-07 17:29:48 +08:00
Jing Ling 046680f5f0 更新依赖 2020-03-07 17:23:41 +08:00
Jing Ling f6342dce4f Merge branch 'master' of https://github.com/shmilylty/OneForAll 2020-03-07 17:05:39 +08:00
Jing Ling 8df6d2f26d 改用Value对象来统计进度 2020-03-07 17:00:10 +08:00
Jing Ling ff7a351d78 Update .travis.yml 2020-03-07 16:02:48 +08:00
Jing Ling baa09f64b8 Update .travis.yml 2020-03-07 15:39:24 +08:00
Jing Ling 115f9f6558 typo 2020-03-07 03:35:28 +08:00
Jing Ling 5b8ae6b74a typo 2020-03-07 03:27:30 +08:00
Jing Ling ecdcad9290 添加如何获取最新帮助信息提示 2020-03-07 03:21:10 +08:00
Jing Ling 3bff500927 添加字典 2020-03-07 03:04:27 +08:00
Jing Ling 4abdc9424d 添加子域字典说明 2020-03-07 02:52:55 +08:00
Jing Ling 7a545b7d3e 修复不能正常使用fuzz模块bug 2020-03-06 21:41:56 +08:00
Jing Ling 4dc58f8d33 添加IP是否为公网IP判断 2020-03-04 04:22:39 +08:00
Jing Ling 3e1fe7723f 参数调优 2020-03-04 01:35:35 +08:00
Jing Ling 53106bc17e 调整请求超时 2020-03-03 22:31:08 +08:00
Jing Ling b39be9afd8 移除txt导出格式 2020-03-03 22:29:34 +08:00
Jing Ling 5529737988 移除txt导出格式 2020-03-03 22:27:20 +08:00
Jing Ling 8ba5b4d747 v0.1.0 2020-03-02 03:32:52 +08:00
Jing Ling 6f3cdb6ee4 再次修改banner获取 2020-03-02 03:22:09 +08:00
Jing Ling 71e5c89689 修改banner获取 2020-03-02 03:09:11 +08:00
Jing Ling 6180b7d2ce 丰富dnsquery查询模块 2020-03-02 02:54:53 +08:00
Jing Ling 13762db3c4 添加千寻查询模块 2020-03-01 19:51:21 +08:00
Jing Ling 3feb21204e 增加字典 2020-03-01 18:36:06 +08:00
Jing Ling 67eff11737 添加结果说明图 2020-03-01 12:49:11 +08:00
Jing Ling ceb051dc3b 添加结果说明 2020-03-01 12:46:47 +08:00
Jing Ling bf94afff20 调整表中id列的顺序 2020-03-01 11:38:38 +08:00
Jing Ling bf0a9e92ce 修改IPS的结果处理 2020-02-29 22:57:58 +08:00
Jing Ling f213a93a32 elapsed重命名为elapse 2020-02-29 20:38:41 +08:00
Jing Ling 98762cbfa2 pep8 2020-02-29 20:36:00 +08:00
Jing Ling f6eb395e4a ips更改为content 2020-02-29 20:34:05 +08:00
Jing Ling 453a3d049c 修改解析内容处理 2020-02-29 19:26:54 +08:00
Jing Ling 0679e96cd0 添加type标记 2020-02-29 18:49:24 +08:00
Jing Ling dcd2a77d06 修复每次生成结果都是相同表名问题 2020-02-29 18:48:16 +08:00
Jing Ling 1164f08f56 不是利用的DNS记录的A记录查询子域默认都有效 2020-02-29 18:07:22 +08:00
Jing Ling f1f85f9e8c 1.在结果里添加利用的DNS记录查询类型type,子域层数level。
2.把结果中valid,new放在结果首列
3.修改结果中的elapsed为elapse
2020-02-29 17:54:40 +08:00
Jing Ling 3269a9f724 pep8 2020-02-29 17:50:41 +08:00
Jing Ling fd18639259 pep8 2020-02-29 16:56:08 +08:00
Jing Ling fc9f51ec17 更新依赖 2020-02-25 01:40:35 +08:00
Jing Ling 664278009f 更新文档 2020-02-25 01:12:58 +08:00
Jing Ling 269d2bab2b 添加api配置检查 2020-02-25 00:49:50 +08:00
Jing Ling cc12b2d8b8 修复逻辑bug 2020-02-24 18:40:02 +08:00
Jing Ling 38e80587a9 更新状态 2020-02-24 18:13:18 +08:00
Jing Ling 15ca27421b 添加获取结果示例 2020-02-24 18:11:54 +08:00
Jing Ling 67d83f6220 增加字典 2020-02-24 16:49:27 +08:00
Jing Ling c66043336f 重命名 2020-02-24 00:28:44 +08:00
Jing Ling e647fc1107 typo 2020-02-24 00:28:08 +08:00
Jing Ling 14e157ea3c 调整图片位置 2020-02-23 22:06:15 +08:00
Jing Ling e9eb13edcb 重命名 2020-02-23 19:17:01 +08:00
Jing Ling 2f5e4118c1 移除暂时用不到的说明 2020-02-23 18:52:48 +08:00
Jing Ling 41677ed1b8 更新文档 2020-02-23 18:39:16 +08:00
Jing Ling 647148b123 添加注释 2020-02-23 18:08:16 +08:00
Jing Ling 885d0124c9 调整进度 2020-02-23 17:59:10 +08:00
Jing Ling 8da256262c 修复路径问题 2020-02-23 17:56:44 +08:00
Jing Ling 6e4ce73785 不返回值 2020-02-23 17:55:25 +08:00
Jing Ling 094fedc22a 重构:OneForAll入口逻辑更加清晰 2020-02-23 04:23:25 +08:00
Jing Ling 36bfa0b7a0 添加Star趋势 2020-02-23 03:10:41 +08:00
Jing Ling 7ccab37f47 添加响应判断 2020-02-22 20:34:47 +08:00
Jing Ling 5ff1598d82 v0.0.9 2020-02-20 15:05:46 +08:00
Jing Ling 57ddd501b4 v0.0.9 2020-02-20 15:04:17 +08:00
Jing Ling 46ebb3479c 更新文档 2020-02-20 14:49:55 +08:00
Jing Ling 488e9cfce7 typo 2020-02-20 14:37:05 +08:00
Jing Ling f75836d58d 版本信息移到oneforall.py 2020-02-20 14:27:38 +08:00
Jing Ling 4c3e4bb203 更新文档 2020-02-20 14:19:51 +08:00
Jing Ling 56f6a35cd6 添加兼容的路径处理 2020-02-19 16:06:17 +08:00
Jing Ling 247cb65212 为OneForAll入口添加path参数 2020-02-18 16:00:11 +08:00
Jing Ling 51307ae19c 不使用encode函数 2020-02-17 13:02:05 +08:00
Jing Ling 6b831c376a Update bug_report_zh.md 2020-02-17 12:57:47 +08:00
Jing Ling 8b2c13b22b Update bug_report_zh.md 2020-02-17 12:56:40 +08:00
Jing Ling c638acdfc7 Update and rename --bug.md to bug_report_zh.md 2020-02-17 12:53:19 +08:00
Jing Ling ece8001f00 Update .travis.yml 2020-02-17 12:42:19 +08:00
Jing Ling 8353acb404 Update .travis.yml 2020-02-17 12:38:16 +08:00
Jing Ling 8c2f10c7d7 添加编码 2020-02-17 12:17:23 +08:00
Jing Ling b60838ebee Update .travis.yml 2020-02-17 11:09:45 +08:00
Jing Ling e43c510a5f 在Window上指定shell使用UTF-8编码 2020-02-17 10:45:49 +08:00
Jing Ling f9b706db4e 解决解码问题 2020-02-17 04:05:12 +08:00
Jing Ling f2ff9c166a 调宽进度条 2020-02-17 03:51:47 +08:00
Jing Ling 6c902544b5 重构解析模块 2020-02-17 03:39:37 +08:00
Jing Ling 246cad2511 重构 2020-02-16 19:53:00 +08:00
Jing Ling 581122ac47 修复IP138请求问题 2020-02-16 15:34:01 +08:00
Jing Ling 80cae7c2d2 更新依赖 2020-02-16 02:47:20 +08:00
Jing Ling 41166ce1ff 为搜索模块线程设置超时 2020-02-16 01:51:57 +08:00
Jing Ling 38afa8807b 解决RuntimeError 2020-02-15 01:50:28 +08:00
Jing Ling e8bc8e2941 GitHub登录页面发生了变化 2020-02-14 16:09:47 +08:00
Jing Ling d06f8c2a40 增加HEAD请求功能 2020-02-12 18:21:52 +08:00
Jing Ling cd88e34dd9 增加测试功能 2020-02-12 15:01:26 +08:00
Jing Ling 29e849ef1a 更改api名称 2020-02-12 15:00:59 +08:00
Jing Ling 5695b36c77 更改api配置说明信息 2020-02-12 10:53:22 +08:00
Jing Ling 2f6e8206d8 添加gitee模块说明 2020-02-12 10:46:55 +08:00
Jing Ling a7b0def3c2 修复路径问题 2020-02-12 10:44:15 +08:00
Jing Ling d527536192 重构请求解决大量超时导致子域无效问题 2020-02-11 03:05:16 +08:00
Jing Ling 8ba01b4527 调整日志等级 2020-02-11 01:55:33 +08:00
Jing Ling 58301560d0 提前路径判断是否为None 2020-02-10 22:04:01 +08:00
Jing Ling 135fce0993 修复路径问题和解包错误问题 2020-02-10 21:59:47 +08:00
Jing Ling 38c4de6896 api配置分离 2020-02-10 20:11:15 +08:00
Jing Ling 3f8aa8b74d pep8 2020-02-10 18:48:42 +08:00
Jing Ling 463325e694 解决Github登录出现两个authenticity_token问题 2020-02-10 18:34:38 +08:00
Jing Ling 46521a8170 正确地判断某表是否存在 2020-02-09 21:22:24 +08:00
Jing Ling ab2932642f 用列表存放端口 2020-02-09 19:23:40 +08:00
Jing Ling 1cd2082e71 添加函数说明 2020-02-09 19:00:04 +08:00
Jing Ling a5f51aaefc 添加获取版本信息命令 2020-02-09 18:58:47 +08:00
Jing Ling c4eb6cdcfa 更友好的提示 2020-02-09 16:10:34 +08:00
Jing Ling e0ca9dbea0 添加返回值说明 2020-02-02 18:18:50 +08:00
Jing Ling 02ab78d835 调整顺序 2020-02-02 18:17:20 +08:00
Jing Ling e66edb9717 添加Gitee码云搜索模块 2020-02-02 18:06:37 +08:00
Jing Ling e3cb914901 模块提示更正 2020-02-02 18:04:59 +08:00
Jing Ling d2e350b851 增加常见Web应用端口 2020-02-01 22:39:58 +08:00
Jing Ling 6cc6341e3a Merge remote-tracking branch 'origin/master' 2020-02-01 17:45:02 +08:00
Jing Ling 2c7a7fa663 重构 2020-02-01 17:44:21 +08:00
Jing Ling 562dcefc7a Merge pull request #44 from azimut/patch-1
Fix axfr module name in README.en.md
2020-01-30 21:20:04 +08:00
NCM a21947fc5c Fix axfr module name in README.en.md 2020-01-27 23:14:59 -03:00
Jing Ling 65a64e3929 解码兼容处理 2020-01-23 15:57:32 +08:00
Jing Ling 67937a6313 更加兼容的解码方式 2020-01-23 00:29:54 +08:00
Jing Ling 6b3636ba3b Merge remote-tracking branch 'origin/master' 2020-01-10 15:59:38 +08:00
Jing Ling bc4d4853ec 整理字典 2020-01-10 15:57:49 +08:00
Jing Ling 2ff7d45348 增加open容错处理 2020-01-10 15:26:41 +08:00
Jing Ling d99cf64ae8 移除segment参数 2020-01-10 15:21:29 +08:00
Jing Ling e1d224f8c7 更新文档 2020-01-10 15:20:47 +08:00
Jing Ling 501159f6eb 参数调优 2020-01-09 22:53:33 +08:00
Jing Ling 5553bd10f4 实在没有获取到标题就返回None 2020-01-09 22:06:22 +08:00
Jing Ling 2e003137de 修改请求默认不验证SSL 2020-01-09 20:54:29 +08:00
Jing Ling 9386643c5d 修复reason记录问题 2020-01-08 23:00:39 +08:00
Jing Ling 3220b363bf 正确计算有效子域数量 2020-01-07 01:35:32 +08:00
Jing Ling ae8a7a65d0 重构 2020-01-07 01:34:43 +08:00
Jing Ling c16bcada89 重构 2020-01-05 09:51:25 +08:00
Jing Ling 55b9188a13 添加大字典 2020-01-03 10:49:54 +08:00
Jing Ling d6a85ccd53 Merge pull request #37 from MaxSecurity/master
解决在win下面无法读取字典小Bug
2020-01-03 10:19:58 +08:00
Jing Ling fefdeef55a 参数优化 2020-01-03 00:38:07 +08:00
Jing Ling 8408d538de 重构 2020-01-02 23:12:58 +08:00
Jing Ling 4cb3c05b15 更新依赖 2020-01-02 22:20:12 +08:00
Jing Ling 175e8a47f0 默认全导出 2020-01-02 22:07:18 +08:00
MAX丶 b8ea1eb226 Rename oneforall/aiobrute.py to oneforall/aiobrute.py 2020-01-01 12:50:40 +08:00
Jing Ling 16f2bf4eda 增加编码错误忽略 2020-01-01 12:32:09 +08:00
MaxSecurity eda4aa13f1 解决在win下面无法读取字典小Bug 2020-01-01 12:17:55 +08:00
Jing Ling 5cd71ca6e7 修复从config.py传入参数无效问题 2019-12-29 17:17:33 +08:00
Jing Ling aab9bcd3b4 修复导出问题 2019-12-29 12:48:30 +08:00
Jing Ling e47df26436 修改入口 2019-12-28 14:24:40 +08:00
Jing Ling 212e548486 模块优化 2019-12-28 13:56:22 +08:00
Jing Ling 9daefc9eee 修复进度条问题 2019-12-28 13:53:19 +08:00
Jing Ling d5f478654b 添加当查询多个主域时结果自动合并导出功能 2019-12-27 18:54:10 +08:00
Jing Ling 7c8bd44c37 Update installation_dependency.md 2019-12-25 18:00:11 +08:00
Jing Ling dd952759e6 Update installation_dependency.md 2019-12-25 17:56:30 +08:00
Jing Ling 93c31fd826 默认关闭保存各个模块结果为json文件 2019-12-23 23:09:31 +08:00
Jing Ling d013fc2042 打印有效子域个数 2019-12-23 23:06:31 +08:00
Jing Ling ac15eb567b 降低代码重复率 2019-12-23 23:06:07 +08:00
Jing Ling 9752e49e2a Merge remote-tracking branch 'origin/master' 2019-12-23 21:56:37 +08:00
Jing Ling d03429900b 更新依赖 2019-12-23 21:35:50 +08:00
shmilylty 6f4fd16cdb 移除没有必要的请求时延 2019-12-17 18:50:36 +08:00
shmilylty 05141046bd 不进行响应检查 2019-12-17 18:49:53 +08:00
shmilylty 6a22a3aed9 优化模块 2019-12-17 18:47:15 +08:00
shmilylty 05d281521d 完善模块 2019-12-17 17:43:17 +08:00
shmilylty ce43bd75de 默认不启用子域接管检查 2019-12-17 11:55:38 +08:00
Jing Ling 30bc74a9a9 注释pytest 2019-12-13 01:46:54 +08:00
Jing Ling 70d3267803 创建测试 2019-12-13 01:43:14 +08:00
shmilylty 1fb30647df 不使用全搜索 2019-12-13 01:22:33 +08:00
shmilylty 6ff55b3693 Python38 2019-12-13 01:12:50 +08:00
shmilylty 207064e975 更新依赖 2019-12-10 22:25:00 +08:00
shmilylty d8aa7487d2 修改为响应状态码判断 2019-12-10 22:06:21 +08:00
shmilylty 00928c367a Merge remote-tracking branch 'origin/master' 2019-12-10 14:10:35 +08:00
shmilylty f5d02d74ef 统一异常处理 2019-12-10 12:12:44 +08:00
shmilylty 0cafe696a6 添加Docker 2019-12-10 12:10:57 +08:00
Jing Ling 28c7305d52 Merge pull request #31 from Tardis07/master
Add Docker supports
2019-12-09 22:52:33 +08:00
shmilylty 0093cd0522 使用https 2019-12-09 17:13:51 +08:00
milktea 7e22e2c7cd Update README.md 2019-12-04 20:25:38 +08:00
milktea abb10159b9 Add Docker supports
Docker版基于alpine构建,轻量化。
已知缺点:中文显示不全。
推荐将结果导出后浏览
后期维护考虑改为Ubuntu作为基础镜像
2019-12-04 20:24:46 +08:00
shmilylty 8c9e69ab9d 添加AlienVault接口说明 2019-11-30 23:28:19 +08:00
shmilylty b6c4724dcb 添加超时 2019-11-30 21:45:34 +08:00
shmilylty 9421e16826 添加AlienVault接口 2019-11-30 21:00:04 +08:00
shmilylty a94001e7e2 修复github搜索时间长问题 2019-11-30 20:59:24 +08:00
shmilylty 81520c43db 更新Linux下依赖安装帮助 2019-11-29 01:27:58 +08:00
shmilylty 59b2e4e59b 修复github搜索时间长问题 2019-11-25 18:00:54 +08:00
shmilylty c4f77f16a9 重构异步DNS解析方法 修复DNS解析进度条问题 2019-11-18 22:51:18 +08:00
shmilylty 85b6c28d5d 新添2个查询接口 2019-11-18 15:46:10 +08:00
shmilylty 971b5fef1e 添加ip138查询接口 2019-11-18 15:41:18 +08:00
shmilylty 2840583bc1 说明只支持Python 3.8 2019-11-15 15:08:09 +08:00
shmilylty f64a9712f5 添加http://sbd.ximcx.cn/查询接口 2019-11-14 23:08:17 +08:00
shmilylty a6a9c6d3fa 更新依赖 2019-11-14 22:33:13 +08:00
shmilylty f323e1364c 增加请求异常处理 2019-11-06 01:17:11 +08:00
shmilylty 3e19062670 增加HEAD请求异常处理 2019-11-06 01:09:30 +08:00
shmilylty a987b9d96c 登录失败线程退出 2019-11-06 00:26:37 +08:00
shmilylty d8a0c55fe5 添加takeover参数说明 2019-10-31 00:02:08 +08:00
shmilylty 9119e051b7 修复dns和req参数传入无效的问题 2019-10-30 23:59:28 +08:00
shmilylty 9f23e580e4 添加dns和req参数说明,移除verity参数说明。 2019-10-30 23:01:49 +08:00
shmilylty 3b24db2d89 添加dns和req参数,移除verity参数。 2019-10-30 22:51:45 +08:00
shmilylty 3f56cda716 调整大小 2019-10-30 20:40:16 +08:00
shmilylty 1690683f41 添加赞赏 2019-10-30 19:10:53 +08:00
shmilylty e662f28407 v0.0.8 2019-10-30 00:46:06 +08:00
shmilylty 26d32d3cd9 实现新子域标记 2019-10-29 22:47:44 +08:00
shmilylty 55c4cf734a 去掉中括号 2019-10-29 16:00:25 +08:00
shmilylty c568f73b51 去掉中括号 2019-10-28 20:06:16 +08:00
shmilylty 9c25b77aa6 新增new,header,response字段 2019-10-28 18:45:52 +08:00
shmilylty 9fbaf19f35 修正安装命令 2019-10-28 11:33:58 +08:00
shmilylty e3c3965020 优化字典 2019-10-28 11:20:05 +08:00
shmilylty 40000472a5 由于dnsdb.org域名不在 暂时删除此模块 2019-10-28 10:19:22 +08:00
shmilylty cb8ac909d7 更改cname查询错误日志级别 2019-10-28 10:18:00 +08:00
shmilylty 7627b53650 使用多线程重写 2019-10-27 18:21:14 +08:00
shmilylty f30f48aea2 修复解析没有结果的问题 2019-10-26 17:58:31 +08:00
shmilylty e66c6d7e21 更新依赖 2019-10-25 21:40:24 +08:00
shmilylty 8ede7d9943 更新依赖 2019-10-25 21:40:04 +08:00
shmilylty 0d72aefb62 更改日志级别 2019-10-25 21:39:28 +08:00
shmilylty 893e6628e6 优化标题获取 2019-10-25 15:46:27 +08:00
shmilylty baf394fc0b 参数优化 2019-10-24 22:30:49 +08:00
shmilylty 035963758d 修复端口重复问题 2019-10-24 21:56:07 +08:00
shmilylty d760e02289 请求优化 2019-10-24 19:06:21 +08:00
shmilylty e6b145d563 修正入口 2019-10-24 18:46:19 +08:00
shmilylty 7afb359918 调整参数 2019-10-24 18:44:58 +08:00
shmilylty 707a5eda93 请求优化 2019-10-24 18:44:35 +08:00
shmilylty 6d3c0ae873 请求优化 2019-10-24 16:08:55 +08:00
shmilylty 476e75869c 修改提示类型 2019-10-24 15:57:39 +08:00
shmilylty e7110c24c5 默认请求只探测80端口 2019-10-23 01:31:51 +08:00
shmilylty 0e4c7961fe 请求子域返回状态码为400的暂时标记为无效子域 2019-10-22 23:49:29 +08:00
shmilylty 6f0cb70e02 推荐使用Python 3.8 2019-10-21 21:58:00 +08:00
shmilylty 4dec25d187 优化真实子域判断 2019-10-21 21:30:32 +08:00
shmilylty e5e24804f6 更新番剧季数 2019-10-21 09:39:33 +08:00
shmilylty f1b24e5674 不使用aiodns库并更新依赖库 2019-10-20 23:53:33 +08:00
shmilylty d0edcb9a0b 不使用aiodns 2019-10-20 23:44:11 +08:00
shmilylty 2dbb5a1097 不使用aiodns 2019-10-20 23:42:24 +08:00
shmilylty 451992f07c 不使用aiodns 2019-10-20 23:24:16 +08:00
shmilylty 5cfbf885ca 不使用aiodns 2019-10-20 16:22:11 +08:00
shmilylty ecc1cb53dc 简化命名 2019-10-19 21:26:48 +08:00
shmilylty 39d9adb6bc 正确判断查询结果 2019-10-19 21:15:09 +08:00
shmilylty 8675dc0202 移除aiodns依赖 2019-10-19 21:10:17 +08:00
shmilylty de81a73097 目前加上超时会查不出结果,暂时去掉超时参数。 2019-10-19 18:38:03 +08:00
shmilylty 9d6f122254 删除无用输出 2019-10-19 16:48:19 +08:00
shmilylty be11d5c3a2 更新Python 3.8在Win10上的配置 2019-10-18 15:29:36 +08:00
shmilylty 786be39398 v0.0.7 2019-10-18 01:06:05 +08:00
shmilylty c92794263b 更新依赖 2019-10-18 00:34:21 +08:00
shmilylty 953b7a4e9b 添加百度云观测接口查询 2019-10-18 00:33:31 +08:00
shmilylty 88b7089d1b 修复一些语法错误 2019-10-14 22:21:37 +08:00
shmilylty d4745fe7cf 修正一些语法错误 2019-10-14 22:09:37 +08:00
shmilylty 94423d2d12 添加英文Readme 2019-10-14 21:37:54 +08:00
shmilylty f5476ab381 修正 2019-10-14 21:37:14 +08:00
shmilylty 1cd52ce2d6 修改为批量存储 2019-10-13 17:11:18 +08:00
Jing Ling 071c115d70 删除无用输出 2019-09-27 01:09:57 +08:00
Jing Ling 20eea3518a 更新依赖 2019-09-26 23:11:05 +08:00
Jing Ling 0a049e991d 修复#23问题 2019-09-26 22:14:08 +08:00
shmilylty 6ef7c96a93 日志等级修正(FETAL->FATAL) 2019-09-23 22:14:44 +08:00
shmilylty edcdaa2f05 处理获取data为None情况 2019-09-17 14:01:27 +08:00
shmilylty f7c03656bf 处理resp.json()异常 2019-09-16 16:41:48 +08:00
shmilylty a9ff58061e 添加8106端口 2019-09-10 16:29:32 +08:00
shmilylty 5a269fc601 certdb_api修改为spyse_api 2019-09-09 18:53:18 +08:00
shmilylty 57b581c85b 修复一些链接404错误 2019-09-08 13:11:05 +08:00
shmilylty dcb1d99b26 添加结果中出现乱码问题的解决方法 2019-09-03 16:45:51 +08:00
shmilylty 372a2de406 忽略解码错误 2019-08-28 17:59:48 +08:00
shmilylty c8fb3a2faf 优化标题获取 2019-08-28 17:16:14 +08:00
shmilylty c9208b61dd 修改为单次存储 2019-08-28 17:15:30 +08:00
shmilylty e74a738565 更新文档 2019-08-28 12:13:40 +08:00
shmilylty 5d6b7c1b7b v0.0.6 2019-08-27 17:56:37 +08:00
shmilylty bc9b5b0afe 重命名 2019-08-27 17:39:54 +08:00
shmilylty a332d3bdc4 重写标题获取 2019-08-27 17:36:19 +08:00
shmilylty c3f6f0b08d 优化标题获取 2019-08-26 18:27:10 +08:00
shmilylty 515bd34c7e 修复问题15 https://github.com/shmilylty/OneForAll/issues/15 2019-08-25 18:01:50 +08:00
shmilylty 32330fa8b2 修复问题16 https://github.com/shmilylty/OneForAll/issues/16 2019-08-25 17:51:16 +08:00
shmilylty 16d3530ac3 修复问题16 https://github.com/shmilylty/OneForAll/issues/16 2019-08-25 17:48:34 +08:00
Jing Ling cc10ce78d3 Update issue templates 2019-08-23 22:34:29 +08:00
shmilylty f35239de40 v0.0.6 2019-08-23 18:55:16 +08:00
shmilylty daaf7891d6 更新文档 2019-08-23 18:51:22 +08:00
Jing Ling 46d447392c Update fofa_api.py 2019-08-23 18:35:47 +08:00
shmilylty b4432dfd47 utf-8 2019-08-23 17:10:17 +08:00
shmilylty 1b9b9cb11d 添加brotlipy依赖 2019-08-23 17:04:13 +08:00
shmilylty f4b4f06333 一直请求bug修复 2019-08-23 15:48:56 +08:00
shmilylty fca97c7d5c 修复需要登录问题 2019-08-23 14:54:10 +08:00
shmilylty 818af18355 去掉无用打印 2019-08-23 10:18:03 +08:00
shmilylty 69f94193f6 默认导出格式修改为csv 2019-08-23 10:17:33 +08:00
shmilylty 403a3fefcb 添加Github子域搜索模块 2019-08-22 19:04:31 +08:00
shmilylty 8ce39a9127 修复编码问题 2019-08-22 14:42:19 +08:00
shmilylty f3a1b2bada 更新文档 2019-08-22 11:27:09 +08:00
shmilylty 41422f4826 修改为正确的doctest格式 2019-08-21 18:24:36 +08:00
Jing Ling 6189bc73ac 错字纠正 2019-08-21 16:52:01 +08:00
Jing Ling 45d9ae2f2d 修复检查线程无限等待问题 2019-08-21 16:50:17 +08:00
Jing Ling c329f6c77e 添加PassiveDNS查询 2019-08-21 16:45:40 +08:00
Jing Ling 9fa77a16b6 添加passivedns_api配置 2019-08-21 16:44:23 +08:00
Jing Ling 2c581c31aa 删除无用判断 2019-08-21 16:42:57 +08:00
shmilylty 76fc12c365 添加绕过cloudFlare验证 2019-08-20 21:44:30 +08:00
shmilylty 3413869bf1 禁用SSL安全警告信息 2019-08-20 21:29:13 +08:00
shmilylty 84aea67aa0 修复链接 2019-08-20 12:07:19 +08:00
shmilylty 73d92a92e1 文档更新 2019-08-20 11:59:00 +08:00
shmilylty 9c8d7e937e 更新文档 2019-08-20 11:47:28 +08:00
shmilylty b165670ed3 合并building_fail_solution.md 2019-08-20 11:41:17 +08:00
shmilylty 4e6dd23129 使用推荐使用socks5h 2019-08-19 21:26:09 +08:00
shmilylty f0a3b44c53 v0.0.5 2019-08-19 20:05:01 +08:00
shmilylty 472da35a36 tqdm修改 2019-08-19 19:58:00 +08:00
shmilylty b60d86fdc3 tqdm配置 2019-08-19 19:45:11 +08:00
shmilylty 9b99b9fd35 添加不能正常安装依赖问题的解决方法 2019-08-19 19:00:29 +08:00
shmilylty dde3da84fd 更新依赖 2019-08-19 18:56:08 +08:00
shmilylty 147f1030c3 添加takeover使用帮助 2019-08-19 18:44:23 +08:00
shmilylty 43d4b90782 pep8 2019-08-19 18:21:47 +08:00
shmilylty dee7ee2d2c 添加github自动接管 2019-08-19 18:11:36 +08:00
shmilylty bf8df3bc02 添加进度条 修复线程退出卡死bug 2019-08-19 18:10:44 +08:00
shmilylty 82973c295c 标题信息获取处理 2019-08-19 02:24:00 +08:00
shmilylty ac654f9bab 添加默认子域接管检查目标列表 2019-08-18 19:35:26 +08:00
shmilylty 310c40d7aa 添加子域接管检查功能 2019-08-18 19:34:16 +08:00
shmilylty b3b528966c 重构 2019-08-18 19:33:23 +08:00
shmilylty e31ff755f4 重构响应检查 2019-08-18 19:32:30 +08:00
shmilylty 101742de3c 添加部分公用函数 2019-08-18 19:31:50 +08:00
shmilylty 25e8541964 添加进度条 2019-08-18 19:31:22 +08:00
shmilylty 1251efd752 使用回调 2019-08-18 19:30:54 +08:00
shmilylty 61075f40ac 添加超时 2019-08-18 19:29:06 +08:00
shmilylty 39b92b79d9 pep8 2019-08-18 19:27:33 +08:00
shmilylty 0e88591a3b 修改默认导出格式为xls 2019-08-18 19:27:09 +08:00
shmilylty 32b06362bd 修改默认请求超时为30秒 2019-08-18 19:26:30 +08:00
shmilylty 298ef2072c 修改默认目前域名 2019-08-18 19:11:51 +08:00
shmilylty 7390e941a7 重命名 2019-08-18 19:02:54 +08:00
shmilylty 1fad2a413e pep8 2019-08-18 19:01:43 +08:00
shmilylty de96822c0d 添加请求超时 2019-08-18 02:45:32 +08:00
shmilylty 4c6af1a925 pep8 2019-08-18 02:12:40 +08:00
shmilylty 7ccddb93f7 修复误把domain替换为subdomain的问题 2019-08-18 02:12:12 +08:00
shmilylty 3883a14365 pep8 2019-08-18 00:03:08 +08:00
shmilylty 837b2f4d51 pep8 2019-08-18 00:02:25 +08:00
Jing Ling 03126872bb Merge pull request #13 from Echocipher/master
subdomain takeover
2019-08-17 14:57:30 +08:00
shmilylty 8eaf1bd60b 修改parsed.names为正确值,解决[问题14](https://github.com/shmilylty/OneForAll/issues/14)。 2019-08-16 23:03:49 +08:00
shmilylty 7f9fbac68b 添加threatcrowd模块 2019-08-16 19:49:39 +08:00
shmilylty 335f6b6c0c 添加常见问题与问答文档 2019-08-16 19:41:21 +08:00
shmilylty f52768632a pep8 2019-08-16 19:13:19 +08:00
shmilylty 8d46ce0862 添加绕过cloudFlare检查 2019-08-16 18:33:11 +08:00
shmilylty 11cf46a006 添加cloudscraper和Js2Py依赖 2019-08-16 16:48:51 +08:00
Echocipher f1e2b739e1 Update takeover.py 2019-08-16 16:44:21 +08:00
Echocipher 3e4a067c6b Update takeover.py 2019-08-16 16:43:48 +08:00
shmilylty d2dc83ebb2 添加ThreatCrowd接口 2019-08-16 16:22:33 +08:00
echocipher c040501ee4 subdomain takeover 2019-08-16 15:35:40 +08:00
echocipher 70b1ddfda2 subdomain takeover 2019-08-16 15:31:45 +08:00
shmilylty cf2e91bfcc 添加其他支持格式说明 2019-08-15 01:57:11 +08:00
Jing Ling daed4d5a7c Set theme jekyll-theme-architect 2019-08-14 23:58:17 +08:00
shmilylty e99497fdc7 美化排版 2019-08-14 23:29:04 +08:00
shmilylty cb7df51209 添加码云厂库链接和说明 2019-08-14 23:12:12 +08:00
shmilylty a3c8834f05 美化排版 2019-08-14 22:22:30 +08:00
shmilylty 3f4336cca9 修改默认导出格式为xls,添加txt,rst,jira格式输出。 2019-08-14 19:07:52 +08:00
shmilylty da4e3bbd2a 修复[问题10](https://github.com/shmilylty/OneForAll/issues/10) 2019-08-14 16:35:29 +08:00
shmilylty 20e582135d 移除freebuf.com 2019-08-14 16:15:49 +08:00
shmilylty bfb069509e 添加subdomains为None情况判断 2019-08-14 16:13:27 +08:00
shmilylty d9c1cb3937 解决批量获取子域问题 2019-08-14 16:07:30 +08:00
shmilylty f3540ee07b 8443端口使用https协议 2019-08-14 13:44:32 +08:00
shmilylty 794fec26db 修改使用帮助 2019-08-14 03:36:53 +08:00
shmilylty 46282e37a4 修复使用帮助链接 2019-08-14 03:26:12 +08:00
shmilylty 87749f1452 更友好的使用帮助 2019-08-14 03:25:15 +08:00
shmilylty 4428790766 添加使用帮助文档 2019-08-14 03:24:44 +08:00
shmilylty 51c42f2958 pep8 2019-08-14 03:22:00 +08:00
shmilylty aaf5863d9a 对齐 2019-08-14 02:39:34 +08:00
shmilylty 88e084e695 添加详细的安装依赖文档 2019-08-14 02:02:31 +08:00
shmilylty 96487aec2e 模块优化 2019-08-13 19:15:13 +08:00
shmilylty 1179f5f118 添加limit_open_conn检查 2019-08-13 19:14:47 +08:00
shmilylty ef3a1f5245 pep8 2019-08-13 19:13:56 +08:00
shmilylty f223d9cf1b 参数优化 2019-08-13 19:13:08 +08:00
shmilylty 78832b4790 响应解码问题处理 2019-08-13 17:35:28 +08:00
shmilylty 3c065f56ae 修复字典路径问题 2019-08-12 22:31:17 +08:00
shmilylty a65840b013 修复self.verify值的获取 2019-08-12 22:30:54 +08:00
shmilylty 49e4278647 修改output参数为show参数 2019-08-12 18:41:58 +08:00
shmilylty 5a324971ed 添加验证子域有效性参数 2019-08-12 18:41:18 +08:00
shmilylty f3e76f7ee5 添加跳出判断 2019-08-12 11:26:50 +08:00
153 changed files with 842296 additions and 29201 deletions
-35
View File
@@ -1,35 +0,0 @@
---
name: 提交Bug
about: "请务必按照模板提交Bug\U0001F64F"
title: 请填写BUG标题
labels: bug
assignees: shmilylty
---
**是否使用了最新代码**
是或否(如果不是的话尝试克隆最新的代码再跑一下)
**Bug描述**
清晰而简洁的Bug描述
**如何复现**
复现步骤(可不写)
复现命令
**预期结果**
清晰而简洁的预期结果描述(可不写)
**实际结果**
清晰而简洁的实际结果描述(如出现什么错误)
**屏幕截图**
**运行环境**
- 系统:[例如Windows 10 x64]
- Python版本:[例如3.7.1]
**报错文本**
复制完整的报错文本
+39
View File
@@ -0,0 +1,39 @@
---
name: 请使用这个中文模板提交Bug
about: "请务必按照模板提交Bug\U0001F64F"
title: 请填写BUG标题
labels: bug
assignees: shmilylty
---
**是否使用了最新代码**
是或否(如果不是的话尝试克隆最新的代码再跑一下!)
**Bug描述**
清晰而简洁的Bug描述(必写)
**运行环境**
- 系统:[例如Windows 10 x64](必写)
- Python版本:[例如3.7.1](必写)
- OneForAll版本:[例如0.0.6](必写)
**如何复现**
复现步骤(选写)
复现命令(必写)
**报错文本**
复制完整的报错文本(必写)
**预期结果**
清晰而简洁的预期结果描述(选写,如正常情况应该是怎么样的)
**实际结果**
清晰而简洁的实际结果描述(选写,如出现什么错误)
**屏幕截图**
完整OneForAll执行流程截图(建议上传)
**日志上传**
上传oneforall.log日志文件(复杂问题建议上传)
+35
View File
@@ -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
+18 -4
View File
@@ -9,31 +9,45 @@ matrix:
include:
- name: "Python 3.6 on Linux"
python: 3.6 # this works for Linux but is ignored on macOS or Windows
before_install:
- pip3 install -U pip
- pip3 install -U uvloop
- name: "Python 3.7 on Xenial Linux"
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
- pip3 install -U uvloop
- name: "Python 3.8 on Xenial Linux"
python: 3.8-dev # this works for Linux but is ignored on macOS or Windows
dist: xenial # required for Python >= 3.7
before_install:
- pip3 install -U pip
- pip3 install -U uvloop
- name: "Python 3.7 on macOS"
os: osx
osx_image: xcode10.2 # Python 3.7 running on macOS 10.14.3
language: shell # 'language: python' is an error on Travis CI macOS
- name: "Python 3.7 on Windows"
before_install:
- pip3 install -U pip
- pip3 install -U uvloop
- name: "Python 3.8 on Windows"
os: windows # Windows 10.0.17134 N/A Build 17134
language: shell
before_install:
- choco install python
- 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
install:
- pip3 install -U pip
- pip3 install codecov
- pip3 install -r requirements.txt
script:
- coverage run oneforall/example.py
- coverage run test.py
after_success:
- codecov
-38
View File
@@ -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数据集,证书透明度,网上爬虫档案)
- 新增子域爆破功能
- 新增数据库导出功能
-51
View File
@@ -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).
-42
View File
@@ -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.
-11
View File
@@ -1,11 +0,0 @@
# OneForAll贡献者
* **[Jing Ling](https://github.com/shmilylty)**
* 核心开发
* **[Black Star](https://github.com/blackstar24)**
* 模块贡献
* [**iceMatcha**](https://github.com/iceMatcha)
* bug调试
+16
View File
@@ -0,0 +1,16 @@
FROM python:3.8-alpine3.10
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
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_x86_64
WORKDIR /OneForAll/
ENTRYPOINT ["/bin/ash"]
-1
View File
@@ -1 +0,0 @@
+7 -6
View File
@@ -6,9 +6,7 @@ verify_ssl = true
[dev-packages]
[packages]
aiodns = "*"
tqdm = "*"
aiomultiprocess = "*"
loguru = "*"
dnspython = "*"
requests = "*"
@@ -16,11 +14,14 @@ records = "*"
tldextract = "*"
exrex = "*"
aiohttp = "*"
fire = "==0.2.1"
fire = "*"
bs4 = "*"
cchardet = "*"
lxml = "*"
pysocks = "*"
cloudscraper = "*"
tablib = "*"
brotli = "*"
psutil = "*"
tenacity = "*"
[requires]
python_version = "3.7"
python_version = "3.8"
Generated
+201 -293
View File
@@ -1,11 +1,11 @@
{
"_meta": {
"hash": {
"sha256": "d95e37615ca48fe39a66e8c24eff76910c66b01b0739c0ab00ffa0f4df022107"
"sha256": "331fa72389c511a19913a7b58136a45391775ade2ef946e4790cd199e4098a39"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.7"
"python_version": "3.8"
},
"sources": [
{
@@ -16,48 +16,23 @@
]
},
"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"
"sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e",
"sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326",
"sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a",
"sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654",
"sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a",
"sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4",
"sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17",
"sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec",
"sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd",
"sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48",
"sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59",
"sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965"
],
"index": "pypi",
"version": "==3.5.4"
},
"aiomultiprocess": {
"hashes": [
"sha256:fd9b616d5145ac2b01f315725277231ffad7e56eb6675885598f6fe861ef9fa3"
],
"index": "pypi",
"version": "==0.6.0"
"version": "==3.6.2"
},
"async-timeout": {
"hashes": [
@@ -68,25 +43,57 @@
},
"attrs": {
"hashes": [
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
],
"version": "==19.1.0"
},
"backports.csv": {
"hashes": [
"sha256:1277dfff73130b2e106bf3dd347adb3c5f6c4340882289d88f31240da92cbd6d",
"sha256:21f6e09bab589e6c1f877edbc40277b65e626262a86e69a70137db714eaac5ce"
],
"version": "==1.0.7"
"version": "==19.3.0"
},
"beautifulsoup4": {
"hashes": [
"sha256:05668158c7b85b791c5abde53e50265e16f98ad601c402ba44d70f96c4159612",
"sha256:25288c9e176f354bf277c0a10aa96c782a6a18a17122dba2e8cec4a97e03343b",
"sha256:f040590be10520f2ea4c2ae8c3dae441c7cfff5308ec9d58a0ec0c1b8f81d469"
"sha256:594ca51a10d2b3443cbac41214e12dbb2a1cd57e1a7344659849e2e20ba6a8d8",
"sha256:a4bbe77fd30670455c5296242967a123ec28c37e9702a8a81bd2f20a4baf0368",
"sha256:d4e96ac9b0c3a6d3f0caae2e4124e6055c5dcafde8e2f831ff194c104f0775a0"
],
"version": "==4.8.0"
"version": "==4.9.0"
},
"brotli": {
"hashes": [
"sha256:0538dc1744fd17c314d2adc409ea7d1b779783b89fd95bcfb0c2acc93a6ea5a7",
"sha256:0970a47f471782912d7705160b2b0a9306e68e6fadf9cffcaeb42d8f0951e26c",
"sha256:113f51658e6fe548dce4b3749f6ef6c24de4184ba9c10a909cbee4261c2a5da0",
"sha256:1e1aa9c4d1558889f42749c8baf846007953bfd32c8209230cf1cd1f5ef33495",
"sha256:2f2f4f78f29ac4a45d15b3d9fc3fd9705e0ad313a44b129f6e1d0c6916bad0e2",
"sha256:3269f6de1dd150fd0cce1c158b61ff5ac06d627fd3ae9c6ea03aed26fbbff7ea",
"sha256:3f4a1f6240916c7984c7f2542786710f622992508dafee0b1714e6d340fb9ffd",
"sha256:50dd9ad2a2bb12da4e9002a438672d182f98e546e99952de80280a1e1729664f",
"sha256:5519a4b01b1a4f965083cbfa2ef2b9774c5a5f352341c47b50776ad109423d72",
"sha256:5eb27722d320370315971c427eb8aa7cc0791f2a458840d357ac653bd0ad3a14",
"sha256:5f06b4d5b6f58e5b5c220c2f23cad034dc5efa51b01fde2351ced1605bd980e2",
"sha256:71ceee286ea7ec613f1c36f1c6181864a6ca24ebb55e371276f33d6af8742834",
"sha256:72848d25a5f9e736db4af4512e0c3feecc094d57d241f8f1ae959115a2c39756",
"sha256:743001bca75f4a6b4454be3510feca46f9d61a0c782a9bc2bc684bdb245e279e",
"sha256:7ac98c71a15648fd11bc1f32608b6110e396121280790082e32b9a3109048bc6",
"sha256:9d1c2dd27a1083fefd05b1b2f8df4a6bc2aaa6c21dd82cd41c8ae5e7c23a87f8",
"sha256:a13ce9b419fe9f277c63f700efb0e444331509d1881b5610d2ba7e9080606967",
"sha256:a19ef0952b9d2803df88dff07f45a6c92d5676afb9b8d69cf32232d684036d11",
"sha256:ad766ca8b8c1419b71a22756b45264f45725c86133dc80a7cbe30b6b78c75620",
"sha256:ad7963f261988ee0883816b6b9f206f11461c9b3cb5cfbca0c9ab5adc406d395",
"sha256:af0451e23016631a2f52925a10d738ac4a0f794ac315c30380b22efc0c90cbc6",
"sha256:c16201060c5a3f8742e3deae759014251ac92f382f82bc2a41dc079ff18c3f24",
"sha256:c43b202f65891861a9a336984a103de25de235f756de69e32db893156f767013",
"sha256:c675c6cce4295cb1a692f3de7416aacace7314e064b94bc86e93aceefce7fd3e",
"sha256:d17cec0b992b1434f5f9df9986563605a4d1b1acd5574c87fc2ac014bcbd3316",
"sha256:dc91f6129953861a73d9a65c52a8dd682b561a9ebaf65283541645cab6489917",
"sha256:e2f4cbd1760d2bf2f30e396c2301999aab0191aec031a6a8a04950b2f575a536",
"sha256:f192e6d3556714105c10486bbd6d045e38a0c04d9da3cef21e0a8dfd8e162df4",
"sha256:f775b07026af2b1b0b5a8b05e41571cdcf3a315a67df265d60af301656a5425b",
"sha256:f969ec7f56ba9636679e69ca07fba548312ccaca37412ee823c7f413541ad7e0",
"sha256:f9dc52cd70907aafb99a773b66b156f2f995c7a0d284397c487c8b71ddbef2f9",
"sha256:f9ee88bb52352588ceb811d045b5c9bb1dc38927bc150fd156244f60ff3f59f1",
"sha256:fc7212e36ebeb81aebf7949c92897b622490d7c0e333a479c0395591e7994600"
],
"index": "pypi",
"version": "==1.0.7"
},
"bs4": {
"hashes": [
@@ -95,74 +102,12 @@
"index": "pypi",
"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": {
"hashes": [
"sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939",
"sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695"
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
],
"version": "==2019.6.16"
},
"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"
"version": "==2020.4.5.1"
},
"chardet": {
"hashes": [
@@ -171,20 +116,21 @@
],
"version": "==3.0.4"
},
"cloudscraper": {
"hashes": [
"sha256:06eb4fd7462dc08a193228830f45097993efc8af4fd75a74815ba16a05c6a0fd",
"sha256:dec9d92a323e85d390af8d02e475de425604212bc6e50c78c0897bf05d355352"
],
"index": "pypi",
"version": "==1.2.36"
},
"colorama": {
"hashes": [
"sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d",
"sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"
"sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff",
"sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"
],
"markers": "sys_platform == 'win32'",
"version": "==0.4.1"
},
"defusedxml": {
"hashes": [
"sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93",
"sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"
],
"version": "==0.6.0"
"version": "==0.4.3"
},
"dnspython": {
"hashes": [
@@ -215,17 +161,17 @@
},
"fire": {
"hashes": [
"sha256:6865fefc6981a713d2ce56a2a2c92c56c729269f74a6cddd6f4b94d16ae084c9"
"sha256:9736a16227c3d469e5d2d296bce5b4d8fa8d7851e953bda327a455fc2994307f"
],
"index": "pypi",
"version": "==0.2.1"
"version": "==0.3.1"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
],
"version": "==2.8"
"version": "==2.9"
},
"jdcal": {
"hashes": [
@@ -236,79 +182,33 @@
},
"loguru": {
"hashes": [
"sha256:b6fad0d7aed357b5c147edcc6982606b933754338950b72d8123f48c150c5a4f",
"sha256:e3138bfdee5f57481a2a6e078714be20f8c71ab1ff3f07f8fb1cfa25191fed2a"
"sha256:074b3caa6748452c1e4f2b302093c94b65d5a4c5a4d7743636b4121e06437b0e",
"sha256:a6101fd435ac89ba5205a105a26a6ede9e4ddbb4408a6e167852efca47806d11"
],
"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"
"version": "==0.4.1"
},
"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"
"sha256:317f96bc0950d249e96d8d29ab556d01dd38888fbe68324f46fd834b430169f1",
"sha256:42f56542166040b4474c0c608ed051732033cd821126493cf25b6c276df7dd35",
"sha256:4b7df040fb5fe826d689204f9b544af469593fb3ff3a069a6ad3409f742f5928",
"sha256:544fae9261232a97102e27a926019100a9db75bec7b37feedd74b3aa82f29969",
"sha256:620b37c3fea181dab09267cd5a84b0f23fa043beb8bc50d8474dd9694de1fa6e",
"sha256:6e6fef114741c4d7ca46da8449038ec8b1e880bbe68674c01ceeb1ac8a648e78",
"sha256:7774e9f6c9af3f12f296131453f7b81dabb7ebdb948483362f5afcaac8a826f1",
"sha256:85cb26c38c96f76b7ff38b86c9d560dea10cf3459bb5f4caf72fc1bb932c7136",
"sha256:a326f4240123a2ac66bb163eeba99578e9d63a8654a59f4688a79198f9aa10f8",
"sha256:ae402f43604e3b2bc41e8ea8b8526c7fa7139ed76b0d64fc48e28125925275b2",
"sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e",
"sha256:b51249fdd2923739cd3efc95a3d6c363b67bbf779208e9f37fd5e68540d1a4d4",
"sha256:bb519becc46275c594410c6c28a8a0adc66fe24fef154a9addea54c1adb006f5",
"sha256:c2c37185fb0af79d5c117b8d2764f4321eeb12ba8c141a95d0aa8c2c1d0a11dd",
"sha256:dc561313279f9d05a3d0ffa89cd15ae477528ea37aa9795c4654588a3287a9ab",
"sha256:e439c9a10a95cb32abd708bb8be83b2134fa93790a4fb0535ca36db3dda94d20",
"sha256:fc3b4adc2ee8474cb3cd2a155305d5f8eda0a9c91320f83e55748e1fcb68f8e3"
],
"version": "==4.5.2"
},
"odfpy": {
"hashes": [
"sha256:596021f0519623ca8717331951c95e3b8d7b21e86edc7efe8cb650a0d0f59a2b"
],
"version": "==1.4.0"
"version": "==4.7.5"
},
"openpyxl": {
"hashes": [
@@ -316,56 +216,38 @@
],
"version": "==2.4.11"
},
"pycares": {
"psutil": {
"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"
"sha256:1413f4158eb50e110777c4f15d7c759521703bd6beb58926f1d562da40180058",
"sha256:298af2f14b635c3c7118fd9183843f4e73e681bb6f01e12284d4d70d48a60953",
"sha256:60b86f327c198561f101a92be1995f9ae0399736b6eced8f24af41ec64fb88d4",
"sha256:685ec16ca14d079455892f25bd124df26ff9137664af445563c1bd36629b5e0e",
"sha256:73f35ab66c6c7a9ce82ba44b1e9b1050be2a80cd4dcc3352cc108656b115c74f",
"sha256:75e22717d4dbc7ca529ec5063000b2b294fc9a367f9c9ede1f65846c7955fd38",
"sha256:a02f4ac50d4a23253b68233b07e7cdb567bd025b982d5cf0ee78296990c22d9e",
"sha256:d008ddc00c6906ec80040d26dc2d3e3962109e40ad07fd8a12d0284ce5e0e4f8",
"sha256:d84029b190c8a66a946e28b4d3934d2ca1528ec94764b180f7d6ea57b0e75e26",
"sha256:e2d0c5b07c6fe5a87fa27b7855017edb0d52ee73b71e6ee368fae268605cc3f5",
"sha256:f344ca230dd8e8d5eee16827596f1c22ec0876127c28e800d7ae20ed44c4b310"
],
"version": "==3.0.0"
"index": "pypi",
"version": "==5.7.0"
},
"pycparser": {
"pyparsing": {
"hashes": [
"sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"version": "==2.19"
"version": "==2.4.7"
},
"pysocks": {
"hashes": [
"sha256:15d38914b60dbcb231d276f64882a20435c049450160e953ca7d313d1405f16f",
"sha256:32238918ac0f19e9fd870a8692ac9bd14f5e8752b3c62624cda5851424642210",
"sha256:d9031ea45fdfacbe59a99273e9f0448ddb33c1580fe3831c1b09557c5718977c"
"sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299",
"sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5",
"sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"
],
"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"
"version": "==1.7.1"
},
"records": {
"hashes": [
@@ -377,46 +259,80 @@
},
"requests": {
"hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
],
"index": "pypi",
"version": "==2.22.0"
"version": "==2.23.0"
},
"requests-file": {
"hashes": [
"sha256:75c175eed739270aec3c5279ffd74e6527dada275c5c0d76b5817e9c86bb7dea",
"sha256:8f04aa6201bacda0567e7ac7f677f1499b0fc76b22140c54bc06edf1ba92e2fa"
"sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e",
"sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"
],
"version": "==1.4.3"
"version": "==1.5.1"
},
"requests-toolbelt": {
"hashes": [
"sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f",
"sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"
],
"version": "==0.9.1"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
],
"version": "==1.12.0"
"version": "==1.14.0"
},
"soupsieve": {
"hashes": [
"sha256:72b5f1aea9101cf720a36bb2327ede866fd6f1a07b1e87c92a1cc18113cbc946",
"sha256:e4e9c053d59795e440163733a7fec6c5972210e1790c507e4c7b051d6c5259de"
"sha256:e914534802d7ffd233242b785229d5ba0766a7f487385e3f714446a07bf540ae",
"sha256:fcd71e08c0aee99aca1b73f45478549ee7e7fc006d51b37bec9e9def7dc22b69"
],
"version": "==1.9.2"
"version": "==2.0"
},
"sqlalchemy": {
"hashes": [
"sha256:217e7fc52199a05851eee9b6a0883190743c4fb9c8ac4313ccfceaffd852b0ff"
"sha256:083e383a1dca8384d0ea6378bd182d83c600ed4ff4ec8247d3b2442cf70db1ad",
"sha256:0a690a6486658d03cc6a73536d46e796b6570ac1f8a7ec133f9e28c448b69828",
"sha256:114b6ace30001f056e944cebd46daef38fdb41ebb98f5e5940241a03ed6cad43",
"sha256:128f6179325f7597a46403dde0bf148478f868df44841348dfc8d158e00db1f9",
"sha256:13d48cd8b925b6893a4e59b2dfb3e59a5204fd8c98289aad353af78bd214db49",
"sha256:211a1ce7e825f7142121144bac76f53ac28b12172716a710f4bf3eab477e730b",
"sha256:2dc57ee80b76813759cccd1a7affedf9c4dbe5b065a91fb6092c9d8151d66078",
"sha256:3e625e283eecc15aee5b1ef77203bfb542563fa4a9aa622c7643c7b55438ff49",
"sha256:43078c7ec0457387c79b8d52fff90a7ad352ca4c7aa841c366238c3e2cf52fdf",
"sha256:5b1bf3c2c2dca738235ce08079783ef04f1a7fc5b21cf24adaae77f2da4e73c3",
"sha256:6056b671aeda3fc451382e52ab8a753c0d5f66ef2a5ccc8fa5ba7abd20988b4d",
"sha256:68d78cf4a9dfade2e6cf57c4be19f7b82ed66e67dacf93b32bb390c9bed12749",
"sha256:7025c639ce7e170db845e94006cf5f404e243e6fc00d6c86fa19e8ad8d411880",
"sha256:7224e126c00b8178dfd227bc337ba5e754b197a3867d33b9f30dc0208f773d70",
"sha256:7d98e0785c4cd7ae30b4a451416db71f5724a1839025544b4edbd92e00b91f0f",
"sha256:8d8c21e9d4efef01351bf28513648ceb988031be4159745a7ad1b3e28c8ff68a",
"sha256:bbb545da054e6297242a1bb1ba88e7a8ffb679f518258d66798ec712b82e4e07",
"sha256:d00b393f05dbd4ecd65c989b7f5a81110eae4baea7a6a4cdd94c20a908d1456e",
"sha256:e18752cecaef61031252ca72031d4d6247b3212ebb84748fc5d1a0d2029c23ea"
],
"markers": "python_version >= '3.0'",
"version": "==1.3.6"
"version": "==1.3.16"
},
"tablib": {
"hashes": [
"sha256:0f88a9cebdaa1a2cc29ae57387082ee81015d1149ecd34e48a8c8d3b4dd21670",
"sha256:5f33c079b07eb10cf9c4b4696add2ecf32c89db7729240546ecdcd5c92f67e13"
"sha256:4d1909aa3ff1c85ba97ad16176c0aeec33c8e894dc7ea6f10f2dd44701e99ba7",
"sha256:80f6c3453431cedf1125f23d16b3d96b92b426495714ebf0b4dede1fa75b447d"
],
"version": "==0.13.0"
"index": "pypi",
"version": "==1.1.0"
},
"tenacity": {
"hashes": [
"sha256:29ae90e7faf488a8628432154bb34ace1cca58244c6ea399fd33f066ac71339a",
"sha256:5a5d3dcd46381abe8b4f82b5736b8726fd3160c6c7161f53f8af7f1eb9b82173"
],
"index": "pypi",
"version": "==6.2.0"
},
"termcolor": {
"hashes": [
@@ -426,26 +342,26 @@
},
"tldextract": {
"hashes": [
"sha256:2c1c5d9d454f79734b4f3da0d603856dd9f820753410a3e9abf0a0c9fde33e97",
"sha256:b72bef6013de67c7fa181250bc2c2e089a994d259c09ca95a9771f2f97e29ed1"
"sha256:16b2f7e81d89c2a5a914d25bdbddd3932c31a6b510db886c3ce0764a195c0ee7",
"sha256:9aa21a1f7827df4209e242ec4fc2293af5940ec730cde46ea80f66ed97bfc808"
],
"index": "pypi",
"version": "==2.2.1"
"version": "==2.2.2"
},
"tqdm": {
"hashes": [
"sha256:14a285392c32b6f8222ecfbcd217838f88e11630affe9006cd0e94c7eff3cb61",
"sha256:25d4c0ea02a305a688e7e9c2cdc8f862f989ef2a4701ab28ee963295f5b109ab"
"sha256:4733c4a10d0f2a4d098d801464bdaf5240c7dadd2a7fde4ee93b0a0efd9fb25e",
"sha256:acdafb20f51637ca3954150d0405ff1a7edde0ff19e38fb99a80a66210d2a28f"
],
"index": "pypi",
"version": "==4.32.2"
"version": "==4.46.0"
},
"urllib3": {
"hashes": [
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
"sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
"sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
],
"version": "==1.25.3"
"version": "==1.25.9"
},
"win32-setctime": {
"hashes": [
@@ -455,35 +371,27 @@
"markers": "sys_platform == 'win32'",
"version": "==1.0.1"
},
"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"
"sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce",
"sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6",
"sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce",
"sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae",
"sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d",
"sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f",
"sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b",
"sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b",
"sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb",
"sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462",
"sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea",
"sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70",
"sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1",
"sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a",
"sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b",
"sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080",
"sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2"
],
"version": "==1.3.0"
"version": "==1.4.2"
}
},
"develop": {}
+191 -245
View File
@@ -3,262 +3,236 @@
[![Build Status](https://travis-ci.org/shmilylty/OneForAll.svg?branch=master)](https://travis-ci.org/shmilylty/OneForAll)
[![codecov](https://codecov.io/gh/shmilylty/OneForAll/branch/master/graph/badge.svg)](https://codecov.io/gh/shmilylty/OneForAll)
[![Maintainability](https://api.codeclimate.com/v1/badges/1287668a6b4c72af683e/maintainability)](https://codeclimate.com/github/shmilylty/OneForAll/maintainability)
[![License](https://img.shields.io/github/license/shmilylty/OneForAll)](./LICENSE)
[![python](https://img.shields.io/badge/python-3.6%20%7C%203.7%20%7C%203.8-blue)](./)
[![python](https://img.shields.io/badge/release-v0.0.4-brightgreen)](https://github.com/shmilylty/OneForAll/releases)
[![License](https://img.shields.io/github/license/shmilylty/OneForAll)](https://github.com/shmilylty/OneForAll/tree/master/LICENSE)
[![python](https://img.shields.io/badge/python-3.8-blue)](https://github.com/shmilylty/OneForAll/tree/master/)
[![python](https://img.shields.io/badge/release-v0.3.0-brightgreen)](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)
![](./images/All_Might.jpg)
![Example](./docs/usage_example.svg)
## 🎉项目简介
项目地址:[https://github.com/shmilylty/OneForAll](https://github.com/shmilylty/OneForAll)
在渗透测试中信息收集的重要性不言而喻,子域收集是信息收集中必不可少且非常重要的一环,目前网上也开源了许多子域收集的工具,但是总是存在以下部分问题:
* **不够强大**,子域收集的接口不够多,不能做到对批量子域自动收集,没有自动子域解析,验证,FUZZ以及信息拓展等功能。
* **不够友好**,固然命令行模块比较方便,但是当可选的参数很多,要实现的操作复杂,用命令行模式就有点不够友好,如果有交互良好,高可操作的前端那么使用体验就会好很多。
* **缺少维护**,很多工具几年没有更新过一次,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)。
1. 利用证书透明度收集子域(目前有6个模块:`censys_api``certdb_api``certspotter``crtsh``entrust``google`
2. 常规检查收集子域(目前有4个模块:域传送漏洞利用`axfr`,检查跨域策略文件`cdx`,检查HTTPS证书`cert`,检查内容安全策略`csp`,检查robots文件`robots`,检查sitemap文件`sitemap`,后续会添加检查NSEC记录,NSEC3记录等模块)
* **收集能力强大**,详细模块请阅读[收集模块说明](https://github.com/shmilylty/OneForAll/tree/master/docs/collection_modules.md)。
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记录等模块)
3. 利用网上爬虫档案收集子域(目前有2个模块:`archivecrawl``commoncrawl`,此模块还在调试,该模块还有待添加和完善)
4. 利用DNS数据集收集子域(目前有23个模块:`binaryedge_api`, `bufferover`, `cebaidu`, `chinaz`, `chinaz_api`, `circl_api`, `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,并综合判断子域存活情况。
* **支持子域接管**,默认开启子域接管风险检查,支持子域自动接管(目前只有Github,有待完善),支持批量检查。
* **处理功能强大**,发现的子域结果支持自动去除,自动DNS解析,HTTP请求探测,自动筛选出有效子域,拓展子域的Banner信息,最终支持的导出格式有`rst`, `csv`, `tsv`, `json`, `yaml`, `html`, `xls`, `xlsx`, `dbf`, `latex`, `ods`
* **速度极快**[收集模块](https://github.com/shmilylty/OneForAll/tree/master/collect.py)使用多线程调用,[爆破模块](https://github.com/shmilylty/OneForAll/tree/master/brute.py)使用[massdns](https://github.com/blechschmidt/massdns),默认配置下速度最少能达到10000pps,子域验证中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`该模块还有待添加和完善)
7. 利用搜索引擎发现子域(目前有15个模块:`ask`, `bing_api`, `fofa_api`, `shodan_api`, `yahoo`, `baidu`, `duckduckgo`, `google`, `so`, `yandex`, `bing`, `exalead`, `google_api`, `sogou`, `zoomeye_api`),在搜索模块中除特殊搜索引擎,通用的搜索引擎都支持自动排除搜索,全量搜索,递归搜索。
* **处理功能强大**,发现的子域结果支持自动去除,自动DNS解析,HTTP请求探测,自动移除无效子域,拓展子域的Banner信息,最终支持的导出格式有`csv`, `tsv`, `json`, `yaml`, `html`, `xls`, `xlsx`, `dbf`, `latex`, `ods`
* **速度极速**[收集模块](./oneforall/collect.py)使用多线程调用,[爆破模块](./oneforall/aiobrute.py)使用异步多进程多协程,DNS解析和HTTP请求使用异步多协程。
如果你有其他很棒的想法请务必告诉我!😎
## 🚀上手指南
由于该项目**处于开发中**,会不断进行更新迭代,下载使用最好**克隆**最新项目。
📢 请务必花一点时间阅读此文档,有助于你快速熟悉OneForAll!
**🐍安装要求**
1. Python 3.6-3.7
OneForAll基于[Python 3.8.0]( https://www.python.org/downloads/release/python-380/ )开发和测试,请使用高于Python 3.8.0的稳定发行版本,其他版本可能会出现一些问题(Windows平台必须使用3.8.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.8.0
pip 19.2.2 from C:\Users\shmilylty\AppData\Roaming\Python\Python38\site-packages\pip (python 3.8)
```
**✔安装步骤**
**✔安装步骤git 版)**
1. 下载更新
1. **下载**
```bash
git clone https://github.com/shmilylty/OneForAll.git
```
由于该项目**处于开发中**,会不断进行更新迭代,下载时使用`git clone`**克隆**最新代码仓库,也方便后续的更新,不推荐从Releases下载,因为Releases里版本更新缓慢,也不方便更新,
本项目已经在[码云](https://gitee.com/shmilylty/OneForAll.git)(Gitee)镜像了一份,国内推荐使用码云进行克隆比较快:
❗如果你之前已经克隆了项目运行之前请**备份**自己修改过的文件到项目外的地方(如**config.py**),然后执行以下命令**更新**项目:
```bash
git clone https://gitee.com/shmilylty/OneForAll.git
```
或者:
```bash
git clone https://github.com/shmilylty/OneForAll.git
```
```bash
git fetch --all
git reset --hard origin/master
git pull
```
2. **安装**
2. 安装依赖
你可以通过pip3安装OneForAll的依赖,以下为**Windows系统**下使用**pip3**安装依赖的示例:注意:如果你的Python3安装在系统Program Files目录下,如:`C:\Program Files\Python38`,那么请以管理员身份运行命令提示符cmd执行以下命令!
* 使用pipenv
```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
```
```bash
pip3 install pipenv
cd OneForAll/
pipenv install --python 3.7
cd oneforall
pipenv run python3 oneforall.py --help
```
其他系统平台的请参考[依赖安装](https://github.com/shmilylty/OneForAll/tree/master/docs/installation_dependency.md),如果在安装依赖过程中发现编译某个依赖库失败时可以参考[troubleshooting.md](https://github.com/shmilylty/OneForAll/tree/master/docs/troubleshooting.md)中解决方法,如果还没有解决欢迎加群反馈。
* 使用pip3
3. **更新**
```bash
cd OneForAll/
pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
cd oneforall/
python3 oneforall.py --help
```
❗注意:如果你之前已经克隆了项目运行之前请**备份**自己修改过的文件到项目外的地方(如**config.py**),然后执行以下命令**更新**项目:
```bash
git fetch --all
git reset --hard origin/master
git pull
```
如果在安装依赖过程中发现编译某个依赖库失败时可以参考[编译失败解决方法](./docs/building_fail_solution.md),如果还没有解决欢迎加群反馈。
**✔安装步骤(docker 版)**
```shell
docker pull shmilylty/oneforall
docker run -it --rm -v ~/results:/OneForAll/results oneforall
```
结果会输出在本地目录`~/results`
**✨使用演示**
1. 如果你是通过pip3安装的依赖则使用以下命令运行示例:
```bash
python3 oneforall.py --target example.com run
```
![Example](./docs/usage_example.svg)
2. 如果你通过pipenv安装的依赖则使用以下命令运行示例:
```bash
pipenv run python oneforall.py --target example.com run
```
**🧐结果说明**
我们以`python3 oneforall.py --target example.com run`命令为例,OneForAll在默认参数正常执行完毕会在results目录生成相应结果:
![Result](./images/Result.png)
`example.com.csv`是每个主域下的子域收集结果。
`all_subdomain_result_1583034493.csv`是每次运行OneForAll收集到子域的汇总结果,包含`example.com.csv`,方便在批量收集场景中获取全部结果。
`result.sqlite3`是存放每次运行OneForAll收集到子域的SQLite3结果数据库,其数据库结构如下图:
![Database](./images/Database.png)
其中类似`example_com_origin_result`表存放每个模块最初子域收集结果。
其中类似`example_com_resolve_result`表存放对子域进行解析后的结果。
其中类似`example_com_last_result`表存放上一次子域收集结果(需要收集两次以上才会生成)。
其中类似`example_com_now_result`表存放现在子域收集结果,一般情况关注这张表就可以了。
**🤔使用帮助**
命令行参数只提供了一些常用参数,更多详细的参数配置请见[config.py](./oneforall/config.py),如果你认为有些参数是命令界面经常使用到的或缺少了什么参数等问题非常欢迎反馈。由于众所周知的原因,如果要使用一些被墙的收集接口请先到[config.py](./oneforall/config.py)配置代理,有些收集模块需要提供API(大多都是可以注册账号免费获取),如果需要使用请到[config.py](./oneforall/config.py)配置API信息,如果不使用请忽略有关报错提示。(详细模块请阅读[收集模块说明](./docs/collection_modules.md)
命令行参数只提供了一些常用参数,更多详细的参数配置请见[config.py](https://github.com/shmilylty/OneForAll/tree/master/config/setting.py),如果你认为有些参数是命令界面经常使用到的或缺少了什么参数等问题非常欢迎反馈。由于众所周知的原因,如果要使用一些被墙的收集接口请先到[config.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命令行界面基于[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.pydbexport.py,这两个文件可以单独运行,并且所接受参数要更丰富一点
[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)
1. oneforall.py使用帮助
❗注意:当你在使用过程中遇到一些问题或者疑惑时,请先到[Issues](https://github.com/shmilylty/OneForAll/issues)里使用搜索找找答案,还可以参阅[常见问题与回答](https://github.com/shmilylty/OneForAll/tree/master/docs/Q&A.md)。
```bash
pipenv run python oneforall.py --help
```
```bash
NAME
oneforall.py - OneForAll是一款功能强大的子域收集工具
**oneforall.py使用帮助**
SYNOPSIS
oneforall.py --target=TARGET <flags>
以下帮助信息可能不是最新的,你可以使用`python oneforall.py --help`获取最新的帮助信息。
DESCRIPTION
Version: 0.0.2
Project: https://github.com/shmilylty/OneForAll/
```bash
python oneforall.py --help
```
```bash
NAME
oneforall.py - 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
SYNOPSIS
oneforall.py COMMAND | --target=TARGET <flags>
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参数和域名名称在项目结果目录生成相应文件
DESCRIPTION
OneForAll是一款功能强大的子域收集工具
ARGUMENTS
TARGET
单个域名或者每行一个域名的文件路径
Example:
python3 oneforall.py version
python3 oneforall.py --target example.com run
python3 oneforall.py --target ./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 --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
FLAGS
--brute=BRUTE
是否使用爆破模块(默认禁用)
--port=PORT
HTTP请求验证的端口范围(默认medium)
--valid=VALID
导出子域的有效性(默认1)
--path=PATH
导出路径(默认None)
--format=FORMAT
导出格式(默认xlsx)
--output=OUTPUT
是否将导出数据输出到终端(默认False)
```
Note:
参数alive可选值True,False分别表示导出存活,全部子域结果
参数port可选值有'default', 'small', 'large', 详见config.py配置
参数format可选格式有'rst', 'csv', 'tsv', 'json', 'yaml', 'html',
'jira', 'xls', 'xlsx', 'dbf', 'latex', 'ods'
参数path默认None使用OneForAll结果目录生成路径
2. aiobrute.py使用帮助
ARGUMENTS
TARGET
单个域名或者每行一个域名的文件路径(必需参数)
关于泛解析问题处理程序首先会访问一个随机的子域判断是否泛解析,如果使用了泛解析则是通过以下判断处理:
- 一是主要是与泛解析的IP集合和TTL值做对比,可以参考[这篇文章](http://sh3ll.me/archives/201704041222.txt)。
- 二是多次解析到同一IP集合次数(默认设置为10,可以在config.py设置大小)
- 考虑爆破效率问题目前还没有加上HTTP响应体相似度对比和响应体内容判断
经过测试在16核心的CPU,使用16进程64协程,100M带宽的环境下,设置任务分割为50000,跑两百万字典大概10分钟左右跑完,大概3333个子域每秒。
FLAGS
--brute=BRUTE
使用爆破模块(默认False)
--dns=DNS
DNS解析子域(默认True)
--req=REQ
HTTP请求子域(默认True)
--port=PORT
请求验证子域的端口范围(默认只探测80端口)
--valid=VALID
只导出存活的子域结果(默认False)
--format=FORMAT
结果保存格式(默认csv)
--path=PATH
结果保存路径(默认None)
--takeover=TAKEOVER
检查子域接管(默认False)
```
```bash
pipenv run python aiobrute.py --help
```
## 🌲目录结构
项目的目录结构说明请参阅[directory_structure](https://github.com/shmilylty/OneForAll/tree/master/docs/directory_structure.md)。
```bash
NAME
aiobrute.py - OneForAll多进程多协程异步子域爆破模块
关于子域字典来源的说明:
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. 常见业务命名规律:
* 单字母、单字母+单数字、双字母、双字母+单数字、双字母+双数字、三字母、四字母;
* 单数字、双数字、三数字;
5. 在公司或者说在DevOps中常见的工具和软件名称。
6. 常见中文单词拼音和常见英文单词。
7. 从以上获取的字典做优化排序以及脏数据去除处理。
8. 非常欢迎你贡献更好的字典。
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客户端/服务器框架
@@ -266,81 +240,46 @@ oneforall.py是主程序入口,oneforall.py里有调用aiobrute.py和dbexport.
* [beautifulsoup4](https://pypi.org/project/beautifulsoup4/) - 可以轻松从HTML或XML文件中提取数据的Python库
* [fire](https://github.com/google/python-fire) - Python Fire是一个纯粹根据任何Python对象自动生成命令行界面(CLI)的库
* [loguru](https://github.com/Delgan/loguru) - 旨在带来愉快的日志记录Python库
* [massdns](https://github.com/blechschmidt/massdns) - 高性能的DNS解析器
* [records](https://github.com/kennethreitz/records) - Records是一个非常简单但功能强大的库,用于对大多数关系数据库进行最原始SQL查询。
* [requests](https://github.com/psf/requests) - Requests 唯一的一个非转基因的 Python HTTP 库,人类可以安全享用。
* [tqdm](https://github.com/tqdm/tqdm) - 适用于Python和CLI的快速,可扩展的进度条库
感谢这些伟大优秀的Python库!
## 🌲目录结构
```bash
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 利用搜索引擎发现子域模块
```
## 🙏贡献
非常热烈欢迎各位大佬一起完善本项目!
## ⌛后续计划
- [ ] 子域收集模块优化
- [ ] 子域接管功能实现
- [ ] 子域收集爬虫实现
- [ ] 操作强大交互人性的前端界面实现
- [ ] 各模块持续优化和完善
- [x] 子域监控(标记每次新发现的子域)
- [ ] 子域收集爬虫实现(包括从JS等静态资源文件中收集子域)
- [ ] 操作强大交互人性的前端界面实现(暂定:前端:Element + 后端:Flask
更多详细信息请阅读[TODO.md](./TODO.md)。
更多详细信息请阅读[todo.md](https://github.com/shmilylty/OneForAll/tree/master/docs/todo.md)。
## 🔖版本控制
该项目使用[SemVer](https://semver.org/)语言化版本格式进行版本管理,你可以在[Releases](https://github.com/shmilylty/OneForAll/releases)查看可用版本。
该项目使用[SemVer](https://semver.org/)语言化版本格式进行版本管理,你可以在[Releases](https://github.com/shmilylty/OneForAll/releases)查看可用版本,你可以查阅[changes.md](https://github.com/shmilylty/OneForAll/tree/master/docs/changes.md)了解历史变更情况
## 👨‍💻
## 👨‍💻贡献
* **[Jing Ling](https://github.com/shmilylty)**
* 核心开发
* **[Black Star](https://github.com/blackstar24)**
* 模块贡献
你可以在[contributors.md](https://github.com/shmilylty/OneForAll/tree/master/docs/contributors.md)中查看所有参与该项目的开发者。
* [**iceMatcha**](https://github.com/iceMatcha)
* bug调试
## ☕赞赏
*你也可以在[CONTRIBUTORS.md](./CONTRIBUTORS.md)中参看所有参与该项目的开发者。*
如果你觉得这个项目帮助到了你,你可以打赏一杯咖啡以资鼓励:)
![](https://raw.githubusercontent.com/shmilylty/OneForAll/master/images/Donate.png)
## 📄版权
该项目签署了GPL-3.0授权许可,详情请参阅[LICENSE.md](./LICENSE.md)。
该项目签署了GPL-3.0授权许可,详情请参阅[LICENSE](https://github.com/shmilylty/OneForAll/blob/master/LICENSE)。
## 😘鸣谢
@@ -348,6 +287,13 @@ D:.
感谢[A-Team](https://github.com/QAX-A-Team)大哥们热情无私的问题解答!
## 📜免责声明 ##
## 📜免责声明
本工具仅限于安全研究与教学使用,用户使用本工具所造成的所有后果,由用户承担全部法律及连带责任,本项目所有作者和贡献者不承担任何法律及连带责任
本工具仅限于合法授权的企业安全建设,在使用本工具过程中,您应确保自己所有行为符合当地的法律法规,并且已经取得了足够的授权
如您在使用本工具的过程中存在任何非法行为,您需自行承担所有后果,本工具所有作者和所有贡献者不承担任何法律及连带责任。
除非您已充分阅读、完全理解并接受本协议所有条款,否则,请您不要安装并使用本工具。
您的使用行为或者您以其他任何明示或者默示方式表示接受本协议的,即视为您已阅读并同意本协议的约束。
## 💖Star趋势
[![Stargazers over time](https://starchart.cc/shmilylty/OneForAll.svg)](https://starchart.cc/shmilylty/OneForAll)
-9
View File
@@ -1,9 +0,0 @@
# OneForAll后续开发计划
## 下一步计划
- [ ] 子域收集模块优化
- [ ] 子域接管功能实现
- [ ] 子域收集爬虫实现
- [ ] 操作强大交互人性的前端界面实现
+651
View File
@@ -0,0 +1,651 @@
#!/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 random
import secrets
import exrex
import fire
import tenacity
from dns.exception import Timeout
from dns.resolver import NXDOMAIN, YXDOMAIN, NoAnswer, NoNameservers
import dbexport
from common import utils
from config import setting
from common.module import Module
from config.log import logger
@tenacity.retry(stop=tenacity.stop_after_attempt(3))
def do_query_a(domain, resolver):
try:
answer = resolver.query(domain, 'A')
# If resolve random subdomain raise timeout error, try again
except Timeout as e:
logger.log('ALERT', f'DNS resolve timeout, retrying')
logger.log('DEBUG', e.args)
raise tenacity.TryAgain
# If resolve random subdomain raise NXDOMAIN, YXDOMAIN, NoAnswer, NoNameservers error
# It means that there is no A record of random subdomain and not use wildcard dns record
except (NXDOMAIN, YXDOMAIN, NoAnswer, NoNameservers) as e:
logger.log('DEBUG', e.args)
logger.log('INFOR', f'{domain} seems not use wildcard dns record')
return False
except Exception as e:
logger.log('ALERT', f'Detect {domain} wildcard dns record error')
logger.log('FATAL', e.args)
exit(1)
else:
if answer.rrset is None:
logger.log('ALERT', f'DNS resolve dont have result, retrying')
raise tenacity.TryAgain
ttl = answer.ttl
name = answer.name
ips = {item.address for item in answer}
logger.log('ALERT', f'{domain} use wildcard dns record')
logger.log('ALERT', f'{domain} resolve to: {name} '
f'IP: {ips} TTL: {ttl}')
return True
def detect_wildcard(domain, authoritative_ns):
"""
Detect use wildcard dns record or not
:param str domain: domain
:param list authoritative_ns: authoritative name server
:return bool use wildcard dns record or not
"""
logger.log('INFOR', f'Detecting {domain} use wildcard dns record or not')
token = secrets.token_hex(4)
random_subdomain = f'{token}.{domain}'
resolver = utils.dns_resolver()
resolver.nameservers = authoritative_ns
resolver.rotate = True
resolver.cache = None
try:
wildcard = do_query_a(random_subdomain, resolver)
except Exception as e:
logger.log('DEBUG', e.args)
logger.log('ALERT', f'Multiple detection errors, so temporarily {domain} does not use wildcard dns record')
return False
else:
return wildcard
def gen_fuzz_subdomains(expression, rule):
"""
Generate subdomains based on fuzz mode
:param str expression: generate subdomains's expression
:param str rule: regexp rule
:return list subdomains: list of subdomains
"""
subdomains = list()
fuzz_count = exrex.count(rule)
if fuzz_count > 10000000:
logger.log('ALERT', f'The dictionary generated by this rule is too large{fuzz_count} > 10000000')
logger.log('DEBUG', f'Dictionary size based on fuzz mode: {fuzz_count}')
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.append(fuzz_domain)
random_domain = random.choice(subdomains)
logger.log('ALERT', f'Please check whether {random_domain} is correct or not')
return subdomains
def gen_word_subdomains(expression, path):
"""
Generate subdomains based on word mode
:param str expression: generate subdomains's expression
:param str path: path of wordlist
:return list subdomains: list of subdomains
"""
subdomains = list()
with open(path, encoding='utf-8', errors='ignore') as fd:
for line in fd:
word = line.strip().lower()
if not word.isalnum():
continue
if word.endswith('.'):
word = word[:-1]
subdomain = expression.replace('*', word)
subdomains.append(subdomain)
random_domain = random.choice(subdomains)
logger.log('DEBUG', f'Dictionary based on word mode size: {len(subdomains)}')
logger.log('ALERT', f'Please check whether {random_domain} is correct or not')
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_maindomain(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
@tenacity.retry(stop=tenacity.stop_after_attempt(2))
def get_wildcard_record(domain, resolver):
logger.log('INFOR', f'Query {domain} \'s wildcard dns record in authoritative name server')
try:
answer = resolver.query(domain, 'A')
# 如果查询随机域名A记录时抛出Timeout异常则重新查询
except Timeout as e:
logger.log('ALERT', f'Query timeout, retrying')
logger.log('DEBUG', e.args)
raise tenacity.TryAgain
except (NXDOMAIN, YXDOMAIN, NoAnswer, NoNameservers) as e:
logger.log('DEBUG', e.args)
logger.log('INFOR', f'{domain} dont have A record on authoritative name server')
return None, None
except Exception as e:
logger.log('ERROR', e.args)
logger.log('ERROR', f'Query {domain} wildcard dns record in authoritative name server error')
exit(1)
else:
if answer.rrset is None:
logger.log('DEBUG', f'No record of query result')
return None, None
name = answer.name
ip = {item.address for item in answer}
ttl = answer.ttl
logger.log('INFOR', f'{domain} results on authoritative name server: {name} '
f'IP: {ip} TTL: {ttl}')
return ip, ttl
def collect_wildcard_record(domain, authoritative_ns):
logger.log('INFOR', f'Collecting wildcard dns record for {domain}')
if not authoritative_ns:
return list(), int()
resolver = utils.dns_resolver()
resolver.nameservers = authoritative_ns
resolver.rotate = True
resolver.cache = None
ips = set()
ttl = int()
ips_stat = dict()
while True:
token = secrets.token_hex(4)
random_subdomain = f'{token}.{domain}'
try:
ip, ttl = get_wildcard_record(random_subdomain, resolver)
except Exception as e:
logger.log('DEBUG', e.args)
logger.log('ALERT', f'Multiple query errors, try to query a new random subdomain')
continue
if ip is None:
continue
ips = ips.union(ip)
# 统计每个泛解析IP出现次数
for addr in ip:
count = ips_stat.setdefault(addr, 0)
ips_stat[addr] = count + 1
# 筛选出出现次数2次以上的IP地址
addrs = list()
for addr, times in ips_stat.items():
if times >= 2:
addrs.append(addr)
# 大部分的IP地址出现次数大于2次停止收集泛解析IP记录
if len(addrs) / len(ips) >= 0.8:
break
logger.log('DEBUG', f'Collected the wildcard dns record of {domain}\n{ips}\n{ttl}')
return ips, ttl
def get_nameservers_path(enable_wildcard, ns_ip_list):
path = setting.brute_nameservers_path
if not enable_wildcard:
return path
if not ns_ip_list:
return path
path = setting.authoritative_dns_path
ns_data = '\n'.join(ns_ip_list)
utils.save_data(path, ns_data)
return path
def check_dict():
if not setting.enable_check_dict:
return
sec = setting.check_time
logger.log('ALERT', f'You have {sec} seconds to check 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_records(items, records, subdomains, ip_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')
record = dict()
cname = list()
ips = list()
public = list()
times = list()
ttls = list()
is_valid_flags = list()
have_a_record = False
for answer in answers:
if answer.get('type') != 'A':
logger.log('TRACE', f'The query result of {qname} has no A record\n{answer}')
continue
logger.log('TRACE', f'The query result of {qname} no A record\n{answer}')
have_a_record = True
ttl = answer.get('ttl')
ttls.append(ttl)
cname.append(answer.get('name')[:-1]) # 去除最右边的`.`点号
ip = answer.get('data')
ips.append(ip)
public.append(utils.ip_is_public(ip))
num = ip_times.get(ip)
times.append(num)
isvalid, reason = is_valid_subdomain(ip, ttl, num, wc_ips, wc_ttl)
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):
record['resolve'] = 1
record['reason'] = reason
record['ttl'] = ttls
record['cname'] = cname
record['content'] = ips
record['public'] = public
record['times'] = times
record['resolver'] = resolver
records[qname] = record
subdomains.append(qname)
return records, subdomains
def stat_ip_times(result_paths):
logger.log('INFOR', f'Counting IP')
times = dict()
for result_path in result_paths:
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} 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 = times.setdefault(ip, 0)
times[ip] = value + 1
return times
def deal_output(output_paths, ip_times, wildcard_ips, wildcard_ttl):
logger.log('INFOR', f'Processing result')
records = dict() # 用来记录所有域名解析数据
subdomains = list() # 用来保存所有通过有效性检查的子域
for output_path in output_paths:
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} while processing {line}')
continue
data = items.get('data')
if 'answers' not in data:
logger.log('TRACE', f'Processing {line}, {qname} no response')
continue
records, subdomains = gen_records(items, records, subdomains,
ip_times, wildcard_ips,
wildcard_ttl)
return records, subdomains
def check_by_compare(ip, ttl, wc_ips, wc_ttl):
"""
Use TTL comparison to detect wildcard dns record
:param set ip: A record IP address set
:param int ttl: A record TTL value
:param set wc_ips: wildcard dns record IP address set
:param int wc_ttl: wildcard dns record TTL value
:return bool: result
"""
# Referencehttp://sh3ll.me/archives/201704041222.txt
if ip not in wc_ips:
return False # 子域IP不在泛解析IP集合则不是泛解析
if ttl != wc_ttl and ttl % 60 == 0 and wc_ttl % 60 == 0:
return False
return True
def check_ip_times(times):
"""
Use IP address times to determine wildcard or not
:param times: IP address times
:return bool: result
"""
if times > setting.ip_appear_maximum:
return True
return False
def is_valid_subdomain(ip, ttl, times, wc_ips, wc_ttl):
ip_blacklist = setting.brute_ip_blacklist
if ip in ip_blacklist: # 解析ip在黑名单ip则为非法子域
return 0, 'IP blacklist'
if all([wc_ips, wc_ttl]): # 有泛解析记录才进行对比
if check_by_compare(ip, ttl, wc_ips, wc_ttl):
return 0, 'IP wildcard'
if check_ip_times(times):
return 0, 'IP exceeded'
return 1, 'OK'
def save_brute_dict(dict_path, dict_set):
dict_data = '\n'.join(dict_set)
if not utils.save_data(dict_path, dict_data):
logger.log('FATAL', 'Saving dictionary error')
exit(1)
def delete_file(dict_path, output_paths):
if setting.delete_generated_dict:
dict_path.unlink()
if setting.delete_massdns_result:
for output_path in output_paths:
output_path.unlink()
class Brute(Module):
"""
OneForAll subdomain brute module
Example
brute.py --target domain.com --word True run
brute.py --target ./domains.txt --word True run
brute.py --target domain.com --word True --process 1 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
Note:
--alive True/False Only export alive subdomains or not (default False)
--format rst/csv/tsv/json/yaml/html/jira/xls/xlsx/dbf/latex/ods (result format)
--path Result directory (default directory is ./results)
:param str target: One domain or File path of one domain per line (required)
:param int process: Number of processes (default 1)
:param int concurrent: Number of concurrent (default 10000)
:param bool word: Use word mode generate dictionary (default False)
:param str wordlist: Dictionary path used in word mode (default use ./config/setting.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/setting.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 bool export: Export the results (default True)
:param str format: Result format (default csv)
:param str path: Result directory (default None)
"""
def __init__(self, target, process=None, concurrent=None, word=False,
wordlist=None, recursive=False, depth=None, nextlist=None,
fuzz=False, place=None, rule=None, export=True, alive=True,
format='csv', path=None):
Module.__init__(self)
self.module = 'Brute'
self.source = 'Brute'
self.target = target
self.process_num = process or utils.get_process_num()
self.concurrent_num = concurrent or setting.brute_concurrent_num
self.word = word
self.wordlist = wordlist or setting.brute_wordlist_path
self.recursive_brute = recursive or setting.enable_recursive_brute
self.recursive_depth = depth or setting.brute_recursive_depth
self.recursive_nextlist = nextlist or setting.recursive_nextlist_path
self.fuzz = fuzz or setting.enable_fuzz
self.place = place or setting.fuzz_place
self.rule = rule or setting.fuzz_rule
self.export = export
self.alive = alive
self.format = format
self.path = path
self.bulk = False # 是否是批量爆破场景
self.domains = list() # 待爆破的所有域名集合
self.domain = str() # 当前正在进行爆破的域名
self.ips_times = dict() # IP集合出现次数
self.enable_wildcard = False # 当前域名是否使用泛解析
self.wildcard_check = setting.enable_wildcard_check
self.wildcard_deal = setting.enable_wildcard_deal
self.check_env = True
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.place is None:
self.place = '*.' + domain
wordlist = self.wordlist
main_domain = self.register(domain)
if domain != main_domain:
wordlist = self.recursive_nextlist
if self.word:
word_subdomains = gen_word_subdomains(self.place, wordlist)
# set可以合并list
dict_set = dict_set.union(word_subdomains)
if self.fuzz:
fuzz_subdomains = gen_fuzz_subdomains(self.place, self.rule)
dict_set = dict_set.union(fuzz_subdomains)
# logger.log('INFOR', f'正在去重爆破字典')
# dict_set = utils.uniq_dict_list(dict_set)
count = len(dict_set)
logger.log('INFOR', f'Dictionary size: {count}')
if count > 10000000:
logger.log('ALERT', f'The dictionary generated is 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 or self.rule is None:
logger.log('FATAL', f'No fuzz position or rules 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 main(self, domain):
start = time.time()
logger.log('INFOR', f'Blasting {domain} ')
massdns_dir = setting.third_party_dir.joinpath('massdns')
result_dir = setting.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记录列表
self.enable_wildcard = detect_wildcard(domain, ns_ip_list)
if self.enable_wildcard:
wildcard_ips, wildcard_ttl = collect_wildcard_record(domain,
ns_ip_list)
ns_path = get_nameservers_path(self.enable_wildcard, ns_ip_list)
dict_set = self.gen_brute_dict(domain)
dict_len = len(dict_set)
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,
process_num=self.process_num,
concurrent_num=self.concurrent_num)
output_paths = []
if self.process_num == 1:
output_paths.append(output_path)
else:
for i in range(self.process_num):
output_name = f'resolved_result_{domain}_{timestring}.json{i}'
output_path = temp_dir.joinpath(output_name)
output_paths.append(output_path)
ip_times = stat_ip_times(output_paths)
self.records, self.subdomains = deal_output(output_paths, ip_times,
wildcard_ips, wildcard_ttl)
delete_file(dict_path, output_paths)
end = time.time()
self.elapse = round(end - start, 1)
logger.log('INFOR', 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}:\n'
f'{self.subdomains}')
self.gen_result(brute=dict_len, valid=len(self.subdomains))
self.save_db()
return self.subdomains
def run(self):
logger.log('INFOR', f'Start runing {self.source} module')
if self.check_env:
utils.check_env()
self.domains = utils.get_domains(self.target)
all_subdomains = list()
for self.domain in self.domains:
self.check_brute_params()
if self.recursive_brute:
logger.log('INFOR', f'Start recursively brute the first layer subdomain 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'
f'the {layer_num + 1} 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\'s brute {self.domain}')
if not self.path:
name = f'{self.domain}_brute_result.{self.format}'
self.path = setting.result_save_dir.joinpath(name)
# 数据库导出
if self.export:
dbexport.export(self.domain,
alive=self.alive,
limit='resolve',
path=self.path,
format=self.format)
if __name__ == '__main__':
fire.Fire(Brute)
@@ -1,10 +1,10 @@
# coding=utf-8
from .module import Module
from common.module import Module
class Crawl(Module):
"""
Crawl base class
"""
def __init__(self):
Module.__init__(self)
+250
View File
@@ -0,0 +1,250 @@
#!/usr/bin/env python3
# coding=utf-8
"""
SQLite database initialization and operation
"""
import records
from records import Connection
from config.log import logger
from config import setting
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}{setting.result_save_dir}/result.sqlite3'
else:
db_path = 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)
else:
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'type text,'
f'alive int,'
f'request int,'
f'resolve int,'
f'new int,'
f'url text,'
f'subdomain text,'
f'port int,'
f'level int,'
f'cname text,'
f'content text,'
f'public int,'
f'status int,'
f'reason text,'
f'title text,'
f'banner text,'
f'header text,'
f'response text,'
f'times text,'
f'ttl text,'
f'resolver text,'
f'module text,'
f'source text,'
f'elapse float,'
f'find int,'
f'brute int,'
f'valid int)')
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: mo
"""
logger.log('TRACE',
f'Saving the subdomain results of {table_name} 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, type, alive, resolve, request, new, url, subdomain,'
f'port, level, cname, content, public, status, reason,'
f'title, banner, header, response, times, ttl, resolver,'
f'module, source, elapse, find, brute, valid) '
f'values (:id, :type, :alive, :resolve, :request, :new,'
f':url, :subdomain, :port, :level, :cname, :content,'
f':public, :status, :reason, :title, :banner, :header,'
f':response, :times, :ttl, :resolver, :module, :source,'
f':elapse, :find, :brute, :valid)', 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 '
f'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):
"""
Deduplicates of 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 deal_table(self, deal_table_name, backup_table_name):
"""
Process the table when the collection task is complete
:param str deal_table_name: Pending table name
:param str backup_table_name: Table name for backup
"""
self.copy_table(deal_table_name, backup_table_name)
self.remove_invalid(deal_table_name)
self.deduplicate_subdomain(deal_table_name)
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('.', '_')
query = f'select id, type, new, alive, request, resolve, url, ' \
f'subdomain, level, cname, content, public, port, status, ' \
f'reason, title, banner, times, ttl, resolver, module, ' \
f'source, elapse, find, brute, valid from "{table_name}"'
if alive and limit:
if limit in ['resolve', 'request']:
where = f' where {limit} = 1'
query += where
elif alive:
where = f' where alive = 1'
query += where
logger.log('TRACE', f'Get the data from {table_name} table')
return self.query(query)
def close(self):
"""
Close the database connection
"""
self.conn.close()
+16 -15
View File
@@ -1,15 +1,15 @@
# coding=utf-8
import re
import tldextract
import config
from config import setting
class Domain(object):
"""
域名处理类
Processing domain class
:param str string: 传入的字符串
:param str string: input string
"""
def __init__(self, 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'
@@ -17,9 +17,9 @@ class Domain(object):
def match(self):
"""
域名匹配
match domain
:return: 匹配结果
:return : result
"""
result = re.search(self.regexp, self.string, re.I)
if result:
@@ -29,16 +29,17 @@ class Domain(object):
def extract(self):
"""
域名导出
extract domain
>>>d = Domain('www.example.com')
>>> d = Domain('www.example.com')
<domain.Domain object>
>>>d.extract()
>>> d.extract()
ExtractResult(subdomain='www', domain='example', suffix='com')
:return: 导出结果
:return: extracted domain results
"""
extract_cache_file = config.data_storage_path.joinpath('public_suffix_list.dat')
data_storage_dir = setting.data_storage_dir
extract_cache_file = data_storage_dir.joinpath('public_suffix_list.dat')
tldext = tldextract.TLDExtract(extract_cache_file)
result = self.match()
if result:
@@ -48,14 +49,14 @@ class Domain(object):
def registered(self):
"""
获取注册域名
registered domain
>>>d = Domain('www.example.com')
>>> d = Domain('www.example.com')
<domain.Domain object>
>>>d.registered()
>>> d.registered()
example.com
:return: 注册域名
:return: registered domain result
"""
result = self.extract()
if result:
+26
View File
@@ -0,0 +1,26 @@
from common.module import Module
from common import utils
class Lookup(Module):
"""
DNS query base class
"""
def __init__(self):
Module.__init__(self)
def query(self):
"""
Query the TXT record of domain
:return: query result
"""
answer = utils.dns_query(self.domain, self.type)
if answer is None:
return None
for item in answer:
record = item.to_text()
subdomains = self.match_subdomains(self.domain, record)
self.subdomains = self.subdomains.union(subdomains)
self.gen_record(subdomains, record)
return self.subdomains
+354
View File
@@ -0,0 +1,354 @@
# coding=utf-8
"""
Module base class
"""
import json
import re
import threading
import time
import requests
from config.log import logger
from config import setting
from common import utils
from common.domain import Domain
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 = setting.request_delay # 请求睡眠时延
self.timeout = setting.request_timeout # 请求超时时间
self.verify = setting.request_verify # 请求SSL验证
self.domain = str() # 当前进行子域名收集的主域
self.type = 'A' # 对主域进行子域收集时利用的DNS记录查询类型(默认利用A记录)
self.subdomains = set() # 存放发现的子域
self.records = dict() # 存放子域解析记录
self.results = list() # 存放模块结果
self.start = time.time() # 模块开始执行时间
self.end = None # 模块结束执行时间
self.elapse = None # 模块执行耗时
def check(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 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 collect {self.domain}\'s subdomains')
logger.log('INFOR', f'The {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: requests's response object
"""
try:
resp = requests.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)
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, **kwargs):
"""
Custom get request
:param str url: request url
:param dict params: request parameters
:param bool check: check response
:param kwargs: other params
:return: requests's response object
"""
try:
resp = requests.get(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)
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 parameters
:param bool check: check response
:param kwargs: other params
:return: requests's response object
"""
try:
resp = requests.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)
return None
if not check:
return resp
if utils.check_response('POST', resp):
return resp
return None
def get_header(self):
"""
Get request header
:return: header
"""
# logger.log('DEBUG', f'Get request header')
if setting.enable_fake_header:
return utils.gen_fake_header()
else:
return self.header
def get_proxy(self, module):
"""
Get proxy
:param str module: module name
:return: proxy
"""
if not setting.enable_proxy:
logger.log('TRACE', f'All modules do not use proxy')
return self.proxy
if setting.proxy_all_module:
logger.log('TRACE', f'{module} module uses proxy')
return utils.get_random_proxy()
if module in setting.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
@staticmethod
def match_subdomains(domain, text, distinct=True):
"""
Use regexp to match subdomains
:param str domain: domain
:param str text: text
:param bool distinct: deduplicate results or not (default True)
:return set/list: result set or list
"""
logger.log('TRACE', f'Use regexp to match subdomains in the response body')
regexp = r'(?:[a-z0-9](?:[a-z0-9\-]{0,61}[a-z0-9])?\.){0,}' \
+ domain.replace('.', r'\.')
result = re.findall(regexp, text, re.I)
if not result:
return set()
deal = map(lambda s: s.lower(), result)
if distinct:
return set(deal)
else:
return list(deal)
@staticmethod
def register(domain):
"""
Get registered domain
:param str domain: domain
:return: registered domain
"""
return Domain(domain).registered()
def save_json(self):
"""
Save the results of each module as a json file
:return bool: whether saved successfully
"""
if not setting.save_module_result:
return False
logger.log('TRACE', f'Save the subdomain results found by {self.source} module as a json file')
path = setting.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', encoding='utf-8', 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),
'records': self.records}
json.dump(result, file, ensure_ascii=False, indent=4)
return True
def gen_record(self, subdomains, record):
"""
Generate record dictionary
"""
item = dict()
item['content'] = record
for subdomain in subdomains:
self.records[subdomain] = item
def gen_result(self, find=0, brute=None, valid=0):
"""
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,
'type': self.type,
'alive': None,
'request': None,
'resolve': None,
'new': None,
'url': None,
'subdomain': None,
'level': None,
'cname': None,
'content': None,
'public': None,
'port': None,
'status': None,
'reason': None,
'title': None,
'banner': None,
'header': None,
'response': None,
'times': None,
'ttl': None,
'resolver': None,
'module': self.module,
'source': self.source,
'elapse': self.elapse,
'find': find,
'brute': brute,
'valid': valid}
self.results.append(result)
else:
for subdomain in self.subdomains:
url = 'http://' + subdomain
level = subdomain.count('.') - self.domain.count('.')
record = self.records.get(subdomain)
if record is None:
record = dict()
resolve = record.get('resolve')
request = record.get('request')
alive = record.get('alive')
if self.type != 'A': # 不是利用的DNS记录的A记录查询子域默认都有效
resolve = 1
request = 1
alive = 1
reason = record.get('reason')
resolver = record.get('resolver')
cname = record.get('cname')
content = record.get('content')
times = record.get('times')
ttl = record.get('ttl')
public = record.get('public')
if isinstance(cname, list):
cname = ','.join(cname)
content = ','.join(content)
times = ','.join([str(num) for num in times])
ttl = ','.join([str(num) for num in ttl])
public = ','.join([str(num) for num in public])
result = {'id': None,
'type': self.type,
'alive': alive,
'request': request,
'resolve': resolve,
'new': None,
'url': url,
'subdomain': subdomain,
'level': level,
'cname': cname,
'content': content,
'public': public,
'port': 80,
'status': None,
'reason': reason,
'title': None,
'banner': None,
'header': None,
'response': None,
'times': times,
'ttl': ttl,
'resolver': resolver,
'module': self.module,
'source': self.source,
'elapse': self.elapse,
'find': find,
'brute': brute,
'valid': valid}
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,10 @@
# coding=utf-8
from .module import Module
from common.module import Module
class Query(Module):
"""
查询基类
Query base class
"""
def __init__(self):
Module.__init__(self)
+273
View File
@@ -0,0 +1,273 @@
import asyncio
import functools
import aiohttp
import tqdm
from aiohttp import ClientSession
from bs4 import BeautifulSoup
from common import utils
from config.log import logger
from config import setting
from common.database import Database
def get_limit_conn():
limit_open_conn = setting.limit_open_conn
if limit_open_conn is None: # 默认情况
limit_open_conn = utils.get_semaphore()
elif not isinstance(limit_open_conn, int): # 如果传入不是数字的情况
limit_open_conn = utils.get_semaphore()
return limit_open_conn
def get_ports(port):
logger.log('DEBUG', f'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 {'default', 'small', 'large'}:
logger.log('DEBUG', f'{port} port range')
ports = setting.ports.get(port)
if not ports: # 意外情况
logger.log('ERROR', f'The specified request port range is incorrect')
ports = {80}
logger.log('INFOR', f'Port range:{ports}')
return set(ports)
def gen_req_data(data, ports):
logger.log('INFOR', f'Generating request urls')
new_data = []
for data in data:
resolve = data.get('resolve')
# 解析失败(0)的子域不进行http请求探测
if resolve == 0:
continue
subdomain = data.get('subdomain')
for port in ports:
if str(port).endswith('443'):
url = f'https://{subdomain}:{port}'
if port == 443:
url = f'https://{subdomain}'
data['id'] = None
data['url'] = url
data['port'] = port
new_data.append(data)
data = dict(data) # 需要生成一个新的字典对象
else:
url = f'http://{subdomain}:{port}'
if port == 80:
url = f'http://{subdomain}'
data['id'] = None
data['url'] = url
data['port'] = port
new_data.append(data)
data = dict(data) # 需要生成一个新的字典对象
return new_data
async def fetch(session, url):
"""
请求
:param session: session对象
:param str url: url地址
:return: 响应对象和响应文本
"""
method = setting.request_method.upper()
timeout = aiohttp.ClientTimeout(total=None,
connect=None,
sock_read=setting.sockread_timeout,
sock_connect=setting.sockconn_timeout)
try:
if method == 'HEAD':
async with session.head(url,
ssl=setting.verify_ssl,
allow_redirects=setting.allow_redirects,
timeout=timeout,
proxy=setting.aiohttp_proxy) as resp:
text = await resp.text()
else:
async with session.get(url,
ssl=setting.verify_ssl,
allow_redirects=setting.allow_redirects,
timeout=timeout,
proxy=setting.aiohttp_proxy) as resp:
try:
# 先尝试用utf-8解码
text = await resp.text(encoding='utf-8', errors='strict')
except UnicodeError:
try:
# 再尝试用gb18030解码
text = await resp.text(encoding='gb18030',
errors='strict')
except UnicodeError:
# 最后尝试自动解码
text = await resp.text(encoding=None,
errors='ignore')
return resp, text
except Exception as e:
return e
def get_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 h2:
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 text
return 'None'
def request_callback(future, index, datas):
result = future.result()
if isinstance(result, BaseException):
logger.log('TRACE', result.args)
name = utils.get_classname(result)
datas[index]['reason'] = name + ' ' + str(result)
datas[index]['request'] = 0
datas[index]['alive'] = 0
elif isinstance(result, tuple):
resp, text = result
datas[index]['reason'] = resp.reason
datas[index]['status'] = resp.status
if resp.status == 400 or resp.status >= 500:
datas[index]['request'] = 0
datas[index]['alive'] = 0
else:
datas[index]['request'] = 1
datas[index]['alive'] = 1
headers = resp.headers
datas[index]['banner'] = utils.get_sample_banner(headers)
datas[index]['header'] = str(dict(headers))[1:-1]
if isinstance(text, str):
title = get_title(text).strip()
datas[index]['title'] = utils.remove_invalid_string(title)
datas[index]['response'] = utils.remove_invalid_string(text)
def get_connector():
limit_open_conn = get_limit_conn()
return aiohttp.TCPConnector(ttl_dns_cache=300,
ssl=setting.verify_ssl,
limit=limit_open_conn,
limit_per_host=setting.limit_per_host)
def get_header():
header = None
if setting.fake_header:
header = utils.gen_fake_header()
return header
async def bulk_request(data, port):
ports = get_ports(port)
no_req_data = utils.get_filtered_data(data)
to_req_data = gen_req_data(data, ports)
method = setting.request_method
logger.log('INFOR', f'Use {method} method to request')
logger.log('INFOR', f'Async subdomains request in progress')
connector = get_connector()
header = get_header()
async with ClientSession(connector=connector, headers=header) as session:
tasks = []
for i, data in enumerate(to_req_data):
url = data.get('url')
task = asyncio.ensure_future(fetch(session, url))
task.add_done_callback(functools.partial(request_callback,
index=i,
datas=to_req_data))
tasks.append(task)
# 任务列表里有任务不空时才进行解析
if tasks:
# 等待所有task完成 错误聚合到结果列表里
futures = asyncio.as_completed(tasks)
for future in tqdm.tqdm(futures,
total=len(tasks),
desc='Request Progress',
ncols=80):
await future
return to_req_data + no_req_data
def set_loop_policy():
try:
import uvloop
except ImportError:
pass
else:
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
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 str port: range of ports to be requested
:return list: result
"""
logger.log('INFOR', f'Start subdomain request module')
set_loop_policy()
loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)
data = utils.set_id_none(data)
request_coroutine = bulk_request(data, port)
data = loop.run_until_complete(request_coroutine)
# 在关闭事件循环前加入一小段延迟让底层连接得到关闭的缓冲时间
loop.run_until_complete(asyncio.sleep(0.25))
count = utils.count_alive(data)
logger.log('INFOR', f'Request module found {domain} have {count} alive subdomains')
return data
def save_data(name, data):
"""
Save request results to database
:param str name: table name
:param list data: data to be saved
"""
db = Database()
db.drop_table(name)
db.create_table(name)
db.save_db(name, data, 'request')
db.close()
+171
View File
@@ -0,0 +1,171 @@
import gc
import json
from config.log import logger
from config import setting
from common import utils
from common.database import Database
def filter_subdomain(data):
"""
过滤出无解析内容的子域到新的子域列表
:param list data: 待过滤的数据列表
:return: 符合条件的子域列表
"""
logger.log('DEBUG', f'Filtering subdomains to be resolved')
subdomains = []
for data in data:
if not data.get('content'):
subdomain = data.get('subdomain')
subdomains.append(subdomain)
return subdomains
def update_data(data, records):
"""
更新解析结果
:param list data: 待更新的数据列表
:param dict records: 解析结果字典
:return: 更新后的数据列表
"""
logger.log('DEBUG', f'Updating resolved results')
if not records:
logger.log('ERROR', f'No valid resolved result')
return data
for index, items in enumerate(data):
if not items.get('content'):
subdomain = items.get('subdomain')
record = records.get(subdomain)
if record:
items.update(record)
data[index] = items
return data
def save_data(name, data):
"""
保存解析结果到数据库
:param str name: 保存表名
:param list data: 待保存的数据
"""
logger.log('INFOR', f'Saving resolved results')
db = Database()
db.drop_table(name)
db.create_table(name)
db.save_db(name, data, 'resolve')
db.close()
def save_subdomains(save_path, subdomain_list):
logger.log('DEBUG', f'Saving resolved subdomain')
subdomain_data = '\n'.join(subdomain_list)
if not utils.save_data(save_path, subdomain_data):
logger.log('FATAL', 'Save resolved subdomain error')
exit(1)
def deal_output(output_path):
logger.log('INFOR', f'Processing resolved results')
records = 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
record = dict()
record['resolver'] = items.get('resolver')
qname = items.get('name')[:-1] # 去除最右边的`.`点号
status = items.get('status')
if status != 'NOERROR':
record['alive'] = 0
record['resolve'] = 0
record['reason'] = status
records[qname] = record
continue
data = items.get('data')
if 'answers' not in data:
record['alive'] = 0
record['resolve'] = 0
record['reason'] = 'NOANSWER'
records[qname] = record
continue
flag = False
cname = list()
ips = list()
public = list()
ttls = list()
answers = data.get('answers')
for answer in answers:
if answer.get('type') == 'A':
flag = True
cname.append(answer.get('name')[:-1]) # 去除最右边的`.`点号
ip = answer.get('data')
ips.append(ip)
ttl = answer.get('ttl')
ttls.append(str(ttl))
is_public = utils.ip_is_public(ip)
public.append(str(is_public))
record['resolve'] = 1
record['reason'] = status
record['cname'] = ','.join(cname)
record['content'] = ','.join(ips)
record['public'] = ','.join(public)
record['ttl'] = ','.join(ttls)
records[qname] = record
if not flag:
record['alive'] = 0
record['resolve'] = 0
record['reason'] = 'NOARECORD'
records[qname] = record
return records
def run_resolve(domain, data):
"""
调用子域解析入口函数
:param str domain: 待解析的主域
:param list data: 待解析的子域数据列表
:return: 解析得到的结果列表
:rtype: list
"""
logger.log('INFOR', f'Start resolve subdomains of {domain}')
subdomains = filter_subdomain(data)
if not subdomains:
return data
massdns_dir = setting.third_party_dir.joinpath('massdns')
result_dir = setting.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 = setting.brute_nameservers_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)
records = deal_output(output_path)
data = update_data(data, records)
logger.log('INFOR', f'Finished resolve subdomains of {domain}')
return data
+39 -12
View File
@@ -1,20 +1,20 @@
# coding=utf-8
import requests
import config
from .module import Module
from . import utils
import re
from config import setting
from config.log import logger
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 = config.enable_recursive_search
self.recursive_times = config.search_recursive_times
self.recursive_search = setting.enable_recursive_search
self.recursive_times = setting.search_recursive_times
@staticmethod
def filter(domain, subdomain):
@@ -29,7 +29,7 @@ class Search(Module):
"""
statements_list = []
subdomains_temp = set(map(lambda x: x + '.' + domain,
config.subdomains_common))
setting.subdomains_common))
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,
@@ -47,7 +47,34 @@ class Search(Module):
:return: 匹配的子域
:rtype set
"""
resp = requests.head(url, headers=self.header, proxies=self.proxy,
timeout=self.timeout, allow_redirects=False)
resp = self.head(url, check=False, allow_redirects=False)
if not resp:
return set()
location = resp.headers.get('location')
return set(utils.match_subdomain(domain, location))
if not location:
return set()
return set(self.match_subdomains(domain, location))
@staticmethod
def match_subdomains(domain, html, distinct=True):
"""
Use regexp to match subdomains
:param str domain: domain
:param str html: response html text
:param bool distinct: deduplicate results or not (default True)
:return set/list: result set or list
"""
logger.log('TRACE', f'Use regexp to match subdomains in the response body')
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)
+588
View File
@@ -0,0 +1,588 @@
import os
import re
import sys
import time
import random
import platform
import subprocess
from ipaddress import IPv4Address, ip_address
from stat import S_IXUSR
import psutil
import tenacity
import requests
from pathlib import Path
from records import Record, RecordCollection
from dns.resolver import Resolver
from common.domain import Domain
from config import setting
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']
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
"""
ua = random.choice(user_agents)
ip = gen_random_ip()
headers = {
'Accept': 'text/html,application/xhtml+xml,'
'application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7',
'Cache-Control': 'max-age=0',
'Connection': 'close',
'DNT': '1',
'Referer': 'https://www.google.com/',
'Upgrade-Insecure-Requests': '1',
'User-Agent': ua,
'X-Forwarded-For': ip,
'X-Real-IP': ip
}
return headers
def get_random_proxy():
"""
Get random proxy
"""
try:
return random.choice(setting.proxy_pool)
except IndexError:
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 get_domains(target):
"""
Get domains
:param set or str target:
:return list: domain list
"""
domains = list()
logger.log('DEBUG', f'Getting domains')
if isinstance(target, (set, tuple)):
domains = list(target)
elif isinstance(target, list):
domains = target
elif isinstance(target, str):
path = Path(target)
if path.exists() and path.is_file():
with open(target, encoding='utf-8', errors='ignore') as file:
for line in file:
line = line.lower().strip()
domain = Domain(line).match()
if domain:
domains.append(domain)
else:
target = target.lower().strip()
domain = Domain(target).match()
if domain:
domains.append(domain)
count = len(domains)
if count == 0:
logger.log('FATAL', f'Get {count} domains')
exit(1)
logger.log('INFOR', f'Get {count} domains')
return domains
def get_semaphore():
"""
获取查询并发值
:return: 并发整型值
"""
system = platform.system()
if system == 'Windows':
return 800
elif system == 'Linux':
return 800
elif system == 'Darwin':
return 800
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, format):
"""
检查结果输出目录路径
:param path: 保存路径
:param name: 导出名字
:param format: 保存格式
:return: 保存路径
"""
filename = f'{name}.{format}'
default_path = setting.result_save_dir.joinpath(filename)
if isinstance(path, str):
path = repr(path).replace('\\', '/') # 将路径中的反斜杠替换为正斜杠
path = path.replace('\'', '') # 去除多余的转义
else:
path = default_path
path = Path(path)
if not path.suffix: # 输入是目录的情况
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(format, count):
"""
检查导出格式
:param format: 传入的导出格式
:param count: 数量
:return: 导出格式
"""
formats = ['rst', 'csv', 'tsv', 'json', 'yaml', 'html',
'jira', 'xls', 'xlsx', 'dbf', 'latex', 'ods']
if format == 'xls' and count > 65000:
logger.log('ALERT', '\'xls\' file is limited to 65000 lines')
logger.log('ALERT', 'So use xlsx format replace')
return 'xlsx'
if format in formats:
return format
else:
logger.log('ALERT', f'Does not support {format} format')
logger.log('ALERT', 'So use csv format by default')
return 'csv'
def save_data(path, data):
"""
保存数据到文件
:param path: 保存路径
:param data: 待存数据
:return: 保存成功与否
"""
try:
with open(path, 'w', encoding="utf-8",
errors='ignore', newline='') as file:
file.write(data)
return True
except TypeError:
with open(path, 'wb') 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 check_value(values):
if not isinstance(values, dict):
return values
for key, value in values.items():
if value is None:
continue
if isinstance(value, str) and len(value) > 32767:
# Excel文件中单元格值长度不能超过32767
values[key] = value[:32767]
return values
def export_all_results(path, name, format, datas):
path = check_path(path, name, format)
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()
if format in {'xls', 'xlsx'}:
values = check_value(values)
row_list.append(Record(keys, values))
rows = RecordCollection(iter(row_list))
content = rows.export(format)
save_data(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_data(path, data)
def export_all(alive, format, path, datas):
"""
将所有结果数据导出
:param bool alive: 只导出存活子域结果
:param str format: 导出文件格式
:param str path: 导出文件路径
:param list datas: 待导出的结果数据
"""
format = check_format(format, len(datas))
timestamp = get_timestring()
name = f'all_subdomain_result_{timestamp}'
export_all_results(path, name, format, datas)
export_all_subdomains(alive, path, name, datas)
def dns_resolver():
"""
dns解析器
"""
resolver = Resolver()
resolver.nameservers = setting.resolver_nameservers
resolver.timeout = setting.resolver_timeout
resolver.lifetime = setting.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 count_alive(data):
return len(list(filter(lambda item: item.get('alive') == 1, data)))
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:
valid = item.get('resolve')
if valid == 0:
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_process_num():
process_num = setting.brute_process_num
if isinstance(process_num, int):
return min(os.cpu_count(), process_num)
else:
return 1
def get_coroutine_num():
coroutine_num = setting.resolve_coroutine_num
if isinstance(coroutine_num, int):
return max(64, coroutine_num)
elif coroutine_num is None:
mem = psutil.virtual_memory()
total_mem = mem.total
g_size = 1024 * 1024 * 1024
if total_mem <= 1 * g_size:
return 64
elif total_mem <= 2 * g_size:
return 128
elif total_mem <= 4 * g_size:
return 256
elif total_mem <= 8 * g_size:
return 512
elif total_mem <= 16 * g_size:
return 1024
else:
return 2048
else:
return 64
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))
def check_net():
logger.log('INFOR', 'Checking Internet environment')
urls = ['http://www.example.com', 'http://www.baidu.com',
'http://www.bing.com', 'http://www.taobao.com',
'http://www.linkedin.com', 'http://www.msn.com',
'http://www.apple.com', 'http://microsoft.com']
url = random.choice(urls)
logger.log('INFOR', f'Trying to access {url}')
try:
rsp = requests.get(url)
except Exception as e:
logger.log('ERROR', e.args)
logger.log('ALERT', 'Can not access Internet, retrying')
raise tenacity.TryAgain
if rsp.status_code != 200:
logger.log('ALERT', f'{rsp.request.method} {rsp.request.url} '
f'{rsp.status_code} {rsp.reason}')
logger.log('ALERT', 'Can not access Internet normally, retrying')
raise tenacity.TryAgain
logger.log('INFOR', 'Access to Internet OK')
def check_pre():
logger.log('INFOR', 'Checking dependent environment')
system = platform.system()
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 version < '3.6':
logger.log('FATAL', 'OneForAll requires Python 3.6 or higher')
exit(1)
if system == 'Windows' and implementation == 'CPython':
if version < '3.8':
logger.log('FATAL', 'OneForAll requires Python 3.8 or higher when running on Windows')
exit(1)
if system in {"Linux", "Darwin"}:
try:
import uvloop
except ImportError:
logger.log('ALERT', f'Please install the uvloop library manually to accelerate subdomain requests')
def check_env():
logger.log('INFOR', 'Checking the environment')
try:
check_net()
except Exception as e:
logger.log('DEBUG', e.args)
logger.log('FATAL', 'Can not access Internet')
exit(1)
check_pre()
def get_maindomain(domain):
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', f'Start running massdns')
quiet = ''
if quiet_mode:
quiet = '--quiet'
status_format = setting.brute_status_format
socket_num = setting.brute_socket_num
resolve_num = setting.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}'
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 = setting.brute_massdns_path
if path:
return path
system = platform.system().lower()
machine = platform.machine().lower()
name = f'massdns_{system}_{machine}'
if system == 'windows':
name = name + '.exe'
if machine == 'amd64':
massdns_dir = massdns_dir.joinpath('windows', 'x64')
else:
massdns_dir = massdns_dir.joinpath('windows', 'x84')
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
+71
View File
@@ -0,0 +1,71 @@
# 模块API配置
# Censys可以免费注册获取APIhttps://censys.io/api
censys_api_id = ''
censys_api_secret = ''
# Binaryedge可以免费注册获取APIhttps://app.binaryedge.io/account/api
# 免费的API有效期只有1个月,到期之后可以再次生成,每月可以查询250次。
binaryedge_api = ''
# Chinaz可以免费注册获取APIhttp://api.chinaz.com/ApiDetails/Alexa
chinaz_api = ''
# Bing可以免费注册获取APIhttps://azure.microsoft.com/zh-cn/services/
# cognitive-services/bing-web-search-api/#web-json
bing_api_id = ''
bing_api_key = ''
# SecurityTrails可以免费注册获取APIhttps://securitytrails.com/corp/api
securitytrails_api = ''
# https://fofa.so/api
fofa_api_email = '' # fofa用户邮箱
fofa_api_key = '' # fofa用户key
# Google可以免费注册获取API:
# https://developers.google.com/custom-search/v1/overview
# 免费的API只能查询前100条结果
google_api_key = '' # Google API搜索key
google_api_cx = '' # Google API搜索cx
# 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_usermail = ''
zoomeye_api_password = ''
# 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 = ''
+37
View File
@@ -0,0 +1,37 @@
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', no=5, color='<cyan><bold>', icon='✏️')
logger.level(name='DEBUG', no=10, color='<blue><bold>', icon='🐞 ')
logger.level(name='INFOR', no=20, color='<green><bold>', icon='')
logger.level(name='QUITE', no=25, color='<green><bold>', icon='🤫 ')
logger.level(name='ALERT', no=30, color='<yellow><bold>', icon='⚠️')
logger.level(name='ERROR', no=40, color='<red><bold>', icon='❌️')
logger.level(name='FATAL', no=50, color='<RED><bold>', icon='☠️')
# 如果你想在命令终端静默运行OneForAll,可以将以下一行中的level设置为QUITE
logger.add(sys.stderr, level='INFOR', format=stdout_fmt, enqueue=True) # 命令终端日志级别默认为INFOR
logger.add(log_path, level='DEBUG', format=logfile_fmt, enqueue=True,
encoding='utf-8') # 日志文件默认为级别为DEBUG
+157
View File
@@ -0,0 +1,157 @@
# coding=utf-8
"""
OneForAll配置
"""
import pathlib
import urllib3
# 路径设置
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') # 结果保存目录
# OneForAll入口参数设置
enable_dns_resolve = True # 使用DNS解析子域(默认True)
enable_http_request = True # 使用HTTP请求子域(默认True)
enable_takeover_check = False # 开启子域接管风险检查(默认False)
# 参数port可选值有'default', 'small', 'large'
http_request_port = 'default' # HTTP请求子域(默认'default',探测80端口)
# 参数alive可选值True,False分别表示导出存活,全部子域结果
result_export_alive = False # 只导出存活的子域结果(默认False)
# 参数format可选格式有'rst', 'csv', 'tsv', 'json', 'yaml', 'html',
# 'jira', 'xls', 'xlsx', 'dbf', 'latex', 'ods'
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 = 180.0 # 每个收集模块线程超时时间(默认3分钟)
# 爆破模块设置
enable_brute_module = False # 使用爆破模块(默认False)
enable_wildcard_check = True # 开启泛解析检测(默认True)
enable_wildcard_deal = True # 开启泛解析处理(默认True)
brute_massdns_path = None # 默认None自动选择 如需填写请填写绝对路径
brute_status_format = 'ansi' # 爆破时状态输出格式(默认asni,可选json)
# 爆破时使用的进程数(根据计算机中CPU数量情况设置 不宜大于逻辑CPU个数)
brute_process_num = 1 # 默认1
brute_concurrent_num = 10000 # 并发查询数量(默认10000)
brute_socket_num = 1 # 爆破时每个进程下的socket数量
brute_resolve_num = 50 # 解析失败时尝试换名称服务器重查次数
# 爆破所使用的字典路径 默认data/subdomains.txt
brute_wordlist_path = data_storage_dir.joinpath('subnames.txt')
brute_nameservers_path = data_storage_dir.joinpath('cn_nameservers.txt')
# 域名的权威DNS名称服务器的保存路径 当域名开启了泛解析时会使用该名称服务器来进行A记录查询
authoritative_dns_path = data_storage_dir.joinpath('authoritative_dns.txt')
enable_recursive_brute = False # 是否使用递归爆破(默认False)
brute_recursive_depth = 2 # 递归爆破深度(默认2层)
# 爆破下一层子域所使用的字典路径 默认data/next_subdomains.txt
recursive_nextlist_path = data_storage_dir.joinpath('next_subnames.txt')
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黑名单则标记为非法子域
ip_appear_maximum = 100 # 多个子域解析到同一IP次数超过100次则标记为非法(泛解析)子域
# 代理设置
enable_proxy = False # 是否使用代理(全局开关)
proxy_all_module = False # 代理所有模块
proxy_partial_module = ['GoogleQuery', 'AskSearch', 'DuckDuckGoSearch',
'GoogleAPISearch', 'GoogleSearch', 'YahooSearch',
'YandexSearch', 'CrossDomainXml',
'ContentSecurityPolicy'] # 代理自定义的模块
proxy_pool = [{'http': 'http://127.0.0.1:1080',
'https': 'https://127.0.0.1:1080'}] # 代理池
# proxy_pool = [{'http': 'socks5h://127.0.0.1:10808',
# 'https': 'socks5h://127.0.0.1:10808'}] # 代理池
# 网络请求设置
enable_fake_header = True # 启用伪造请求头
request_delay = 1 # 请求时延
request_timeout = 60 # 请求超时
request_verify = False # 请求SSL验证
# 禁用安全警告信息
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# 搜索模块设置
enable_recursive_search = False # 递归搜索子域
search_recursive_times = 2 # 递归搜索层数
# DNS解析设置
resolve_coroutine_num = 64
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 # 解析超时时间
resolver_lifetime = 60.0 # 解析存活时间
limit_resolve_conn = 500 # 限制同一时间解析的数量(默认500)
# 请求端口探测设置
# 你可以在端口列表添加自定义端口
default_ports = [80] # 默认使用
small_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 = {'default': default_ports, 'small': small_ports, 'large': large_ports}
# aiohttp有关配置
verify_ssl = False
# aiohttp 支持 HTTP/HTTPS形式的代理
aiohttp_proxy = None # proxy="http://user:pass@some.proxy.com"
allow_redirects = True # 允许请求跳转
fake_header = True # 使用伪造请求头
# 为了保证请求质量 请谨慎更改以下设置
# request_method只能是HEAD或GET,HEAD请求方法更快,但是不能获取响应体并提取从中提取
request_method = 'GET' # 使用请求方法,默认GET
sockread_timeout = 10 # 每个请求socket读取超时时间,默认5秒
sockconn_timeout = 10 # 每个请求socket连接超时时间,默认5秒
# 限制同一时间打开的连接总数
limit_open_conn = 100 # 默认100
# 限制同一时间在同一个端点((host, port, is_ssl) 3者都一样的情况)打开的连接数
limit_per_host = 10 # 0表示不限制,默认10
subdomains_common = {'i', 'w', 'm', 'en', 'us', 'zh', 'w3', 'app', 'bbs',
'web', 'www', 'job', 'docs', 'news', 'blog', 'data',
'help', 'live', 'mall', 'blogs', 'files', 'forum',
'store', 'mobile'}
Binary file not shown.
+7
View File
@@ -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
+249
View File
@@ -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 doesnt 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":"instapage",
"cname":["pageserve.co", "secure.pageserve.co", "https://instapage.com/"],
"response":["Looks Like You're Lost","The page you're looking for is no longer available."]
},
{
"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 doesnt 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&#8217;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!"]
}
]
+11078
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+819703
View File
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
#!/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(table, db=None, alive=False, limit=None, path=None, format='csv', show=False):
"""
OneForAll export from database module
Example:
python3 dbexport.py --table name --format csv --dir= ./result.csv
python3 dbexport.py --db result.db --table name --show False
Note:
--alive True/False Only export alive subdomains or not (default False)
--format rst/csv/tsv/json/yaml/html/jira/xls/xlsx/dbf/latex/ods (result format)
--path Result directory (default directory is ./results)
:param str table: 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 format: 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)
rows = database.export_data(table, alive, limit)
format = utils.check_format(format, len(rows))
path = utils.check_path(path, table, format)
if show:
print(rows.dataset)
data = rows.export(format)
database.close()
utils.save_data(path, data)
logger.log('ALERT', f'The subdomain result for {table}: {path}')
data_dict = rows.as_dict()
return data_dict
if __name__ == '__main__':
fire.Fire(export)
# save('example_com_last', format='txt')
-13
View File
@@ -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便下载`pycares3.0.0cp37cp37mwin_amd64.whl`(一般来说下载最新版本的),然后手动安装:
```bash
pip3 install pycares3.0.0cp37cp37mwin_amd64.whl
```
2. 到库的项目地址issues和wiki等找找有没有解决方法,如果没有就给他们提issues发邮件😜。
+111
View File
@@ -0,0 +1,111 @@
# 更新日志
OneForAll的所有值得注意的更改都将记录在此文件中。
OneForAll的更新日志格式基于[Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
OneForAll遵守[语义化版本格式](https://semver.org/)。
# Unreleased
# Released
## [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数据集,证书透明度,网上爬虫档案)
- 新增子域爆破功能
- 新增数据库导出功能
+45 -37
View File
@@ -1,15 +1,15 @@
# 收集模块说明 #
如果要使用通过API收集子域的模块请先到[config.py](../oneforall/config.py)配置相关信息,大多平台的API都是可以注册账号免费获取的。
如果要使用通过API收集子域的模块请先到[api.py](../oneforall/config/api.py)配置相关信息,大多平台的API都是可以注册账号免费获取的。
如果你指定使用某些模块可以在[config.py](../oneforall/config.py)中设置:
如果你指定使用某些模块可以在[api.py](../oneforall/config/api.py)中设置:
```python
enable_all_module = False # 不开启所有模块
enable_partial_module = [('modules.search', 'ask')('modules.search', 'baidu')] # 只使用ask和baidu搜索引擎收集子域
```
如果你指定使用某些模块使用代理可以在[config.py](../oneforall/config.py)中设置:
如果你指定使用某些模块使用代理可以在[api.py](../oneforall/config/api.py)中设置:
```python
enable_proxy = True # 使用代理
@@ -23,12 +23,12 @@ proxy_partial_module = ['GoogleQuery', 'AskSearch'] # 只代理GoogleQuery和As
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
| ----------- | ------------ | ----------- | -------------------------------------------------- |
| censys_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| certdb_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| censys_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| certspotter | 否 | 否 | |
| crtsh | 否 | 否 | |
| entrust | 否 | 否 | |
| google | 是 | 否 | |
| spyse_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
2. 常规检查收集子域(目前有4个模块:域传送漏洞利用`axfr`,检查跨域策略文件`cdx`,检查HTTPS证书`cert`,检查内容安全策略`csp`,后续会添加检查NSEC记录,NSEC3记录等模块)
@@ -48,58 +48,66 @@ proxy_partial_module = ['GoogleQuery', 'AskSearch'] # 只代理GoogleQuery和As
| archivecrawl | 否 | 否 | |
| 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数据集收集子域(目前有22个模块:`cebaidu`, `binaryedge_api`, `circl_api`, `hackertarget`, `riddler`, `bufferover`, `dnsdb`, `ipv4info`, `robtex`, `chinaz`, `dnsdb_api`, `netcraft`, `securitytrails_api`, `chinaz_api`, `dnsdumpster`, `passivedns_api`, `ptrarchive`, `sitedossier`,`threatcrowd`
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
| ------------------ | ------------ | ----------- | -------------------------------------------------- |
| binaryedge_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| binaryedge_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| bufferover | 否 | 否 | |
| cebaidu | 否 | 否 | |
| chinaz | 否 | 否 | |
| chinaz_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| circl_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| chinaz_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| circl_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| dnsdb | 否 | 否 | |
| dnsdb_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| dnsdb_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| dnsdumpster | 否 | 否 | |
| hackertarget | 否 | 否 | |
| ipv4info | 否 | | API使用和申请见[config.py](../oneforall/config.py) |
| ip138 | 否 | | |
| ipv4info | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| netcraft | 否 | 否 | |
| ptrarchive | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| riddler | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| passivedns_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| ptrarchive | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| riddler | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| robtex | 否 | 否 | |
| securitytrails_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| securitytrails_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| sitedossier | 否 | 否 | |
| threatcrowd | 否 | 否 | |
| ximcx | 否 | 否 | |
5. 利用DNS查询收集子域(目前有1个模块:通过枚举常见的SRV记录并做查询来收集子域`srv`,该模块还有待添加和完善)
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
| -------- | ------------ | ----------- | ----------------------------- |
| srv | 否 | 否 | 枚举域名常见的SRV记录发现子域 |
6. 利用威胁平台数据收集子域(目前有5个模块:`riskiq_api``threatbook_api``threatminer``virustotal``virustotal_api`该模块还有待添加和完善)
6. 利用威胁平台数据收集子域(目前有6个模块:`riskiq_api``threatbook_api``threatminer``virustotal``virustotal_api`该模块还有待添加和完善)
| 模块名称 | 是否需要代理 | 是否需要API | 其他说明 |
| -------------- | ------------ | ----------- | -------------------------------------------------- |
| riskiq_api | 否 | | API使用和申请见[config.py](../oneforall/config.py) |
| threatbook_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| -------------- | ------------ | ----------- | ------------------------------------------------- |
| alienvault | 否 | | |
| riskiq_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| threatbook_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| threatminer | 否 | 否 | |
| virustotal | 否 | 否 | |
| virustotal_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
7. 利用搜索引擎发现子域(目前有15个模块:`ask`, `bing_api`, `fofa_api`, `shodan_api`, `yahoo`, `baidu`, `duckduckgo`, `google`, `so`, `yandex`, `bing`, `exalead`, `google_api`, `sogou`, `zoomeye_api`
| virustotal_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
7. 利用搜索引擎发现子域(目前有16个模块:`ask`, `bing_api`, `fofa_api`, `shodan_api`, `yahoo`, `baidu`, `duckduckgo`, `github`, `google`, `so`, `yandex`, `bing`, `exalead`, `google_api`, `sogou`, `zoomeye_api`
除特殊搜索引擎,通用的搜索引擎都支持自动排除搜索,全量搜索,递归搜索。
| 模块 | 是否需要代理 | 是否需要API | 其他说明 |
| ----------- | ---------------------- | ----------- | -------------------------------------------------- |
| ask | 是 | 否 | |
| baidu | 否 | 否 | |
| bing | 否 | 否 | |
| bing_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| duckduckgo | 是 | 否 | |
| exalead | 否,最好使用国外代理。 | 否 | |
| fofa_api | 否 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| google | | 否 | |
| google_api | | | API使用和申请见[config.py](../oneforall/config.py) |
| shodan_api | 否,最好使用国外代理。 | 是 | API使用和申请见[config.py](../oneforall/config.py) |
| so | | | |
| sogou | 否 | | |
| yahoo | | 否 | |
| yandex | | 否 | |
| zoomeye_api | | | API使用和申请见[config.py](../oneforall/config.py) |
| 模块 | 是否需要代理 | 是否需要API | 其他说明 |
| ----------- | ---------------------- | -----------| ----------------------------------------------------------- |
| ask | 是 | 否 | |
| baidu | 否 | 否 | |
| bing | 否 | 否 | |
| bing_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| duckduckgo | 是 | 否 | |
| exalead | 否,最好使用国外代理。 | 否 | |
| fofa_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
| gitee | | 否 | |
| github | | | 在[api.py](../oneforall/config/api.py)设置Github邮件名和密码。 |
| google | 是 | 否 | |
| google_api | | | API使用和申请见[api.py](../oneforall/config/api.py) |
| shodan_api | 否,最好使用国外代理。 | | API使用和申请见[api.py](../oneforall/config/api.py) |
| so | | 否 | |
| sogou | | 否 | |
| yahoo | | | |
| yandex | 是 | 否 | |
| zoomeye_api | 否 | 是 | API使用和申请见[api.py](../oneforall/config/api.py) |
+20
View File
@@ -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**
* 工具反馈
+178
View File
@@ -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地址
| big_subnames.txt 子域爆破超大字典
| cn_nameservers.txt 中国主流名称服务器IP地址
| fingerprints.json 检查子域接管风险的指纹
| nameservers.txt 全球主流名称服务器IP地址
| next_subnames.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
```
+302
View File
@@ -0,0 +1,302 @@
# OneForAll
[![Build Status](https://travis-ci.org/shmilylty/OneForAll.svg?branch=master)](https://travis-ci.org/shmilylty/OneForAll)
[![codecov](https://codecov.io/gh/shmilylty/OneForAll/branch/master/graph/badge.svg)](https://codecov.io/gh/shmilylty/OneForAll)
[![Maintainability](https://api.codeclimate.com/v1/badges/1287668a6b4c72af683e/maintainability)](https://codeclimate.com/github/shmilylty/OneForAll/maintainability)
[![License](https://img.shields.io/github/license/shmilylty/OneForAll)](https://github.com/shmilylty/OneForAll/tree/master/LICENSE)
[![python](https://img.shields.io/badge/python-3.8-blue)](https://github.com/shmilylty/OneForAll/tree/master/)
[![python](https://img.shields.io/badge/release-v0.3.0-brightgreen)](https://github.com/shmilylty/OneForAll/releases)
👊**OneForAll is a powerful subdomain integration tool** 📝[中文文档](https://github.com/shmilylty/OneForAll/tree/master/README.md)
![Example](../usage_example.svg)
## 🎉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**](//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 23 DNS datasets modules: `binaryedge_api`, `bufferover`, `cebaidu`, `chinaz`, `chinaz_api`, `circl_api`, `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 subdmain verification**, default enable, automatically resolve DNS, request subdomain to obtain response, and determine subdomain alive or not.
* **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: `rst`, `csv`, `tsv`, `json`, `yaml`, `html`, `xls`, `xlsx`, `dbf`, `latex`, `ods`.
* **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), the speed can at least reach 10000pps by the default configuration. 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!😎
## 🚀Start Guide
📢 Please read this document to help you start quickly!
**🐍Installation requirements**
OneForAll is developed and tested based on [Python 3.8.0](https://www.python.org/downloads/release/python-380/). Recommend use release higher than Python 3.8.0 (Windows platform must use Python 3.8.0 or later). 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.8.0
pip 19.2.2 from C:\Users\shmilylty\AppData\Roaming\Python\Python37\site-packages\pip (python 3.8)
```
**✔Installation steps (from Git)**
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\Python38`, please run the following as an administrator!)
```bash
cd OneForAll/
python -m pip install -U pip setuptools wheel
pip3 install -r requirements.txt
python 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 the [troubleshooting.md](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**
❗Note: If you have cloned the project before, please backup modified files (such as **./config**) before updating, then run the following command to **update** project:
```bash
git fetch --all
git reset --hard origin/master
git pull
```
**✔Installation steps (from Docker)**
```shell
docker pull shmilylty/oneforall
docker run -it --rm -v ~/results:/OneForAll/results oneforall
```
Result will be saved in `~/results`.
**✨Usage**
1. If you are use pip3, run the following command:
```bash
python3 oneforall.py --target example.com run
```
![Example](../usage_example.svg)
2. If you use pipenv, run the following command:
```bash
pipenv run python oneforall.py --target example.com run
```
**🧐Instructions for results**
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:
![Result](../../images/Result.png)
`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:
![Database](../../images/Database.png)
`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.
**🤔Instructions for Use**
The CLI only provide some common parameters. For more configuration, please read [config.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 [troubleshooting.md](https://github.com/shmilylty/OneForAll/tree/master/docs/troubleshooting.md).
**OneForAll help summary page**
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 --target ./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:
--alive True/False Only export alive subdomains or not (default False)
--port default/small/large See details in ./config/setting.py(default port 80)
--format rst/csv/tsv/json/yaml/html/jira/xls/xlsx/dbf/latex/ods (result format)
--path Result directory (default directory is ./results)
ARGUMENTS
TARGET
One domain or File path of one domain per line (required)
FLAGS
--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 request to the subdomains (default port 80)
--alive=ALIVE
Only export alive subdomains (default False)
--format=FORMAT
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
```
## 🌲Directory structure
For the description of the project's directory structure, please refer to [directory_structure](https://github.com/shmilylty/OneForAll/tree/master/docs/directory_structure.md).
Description of the subdomain dictionary source:
1. Some common subdomain dictionary in open source tool.
2. List of the most popular subdomains published by domain service providers.
* [DNSPod](https://github.com/DNSPod/oh-my-free-data)
3. Research results by security researchers:
* [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. Common naming rules:
* single letter, single letter + single number, double letter, double letter + single number, double letter + double number, three letters, four letters;
* single number, double number, three numbers;
5. The names of tools and software that are common in companies or DevOps.
6. Common Chinese Pinyin words and common English words.
7. Optimize sorting and remove dirty data from the dictionary obtained above.
8. You are very welcome to contribute a better dictionary.
## 👏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!
## 🙏Contribution
Very warmly welcome all people to make OneForAll better together!
## ⌛Follow-up plan
- [ ] Continuous optimize and improve of each module
- [x] Subdomain monitoring (mark newly discovered subdomain)
- [ ] Subdomain collection crawler (collect subdomains from static files such as JS)
- [ ] Implementation of front-end interface for powerful interaction (tentative: front-end: Element + back-end: Flask)
For more details, read [todo.md](https://github.com/shmilylty/OneForAll/tree/master/docs/todo.md).
## 🔖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 check [changes.md](https://github.com/shmilylty/OneForAll/tree/master/docs/changes.md)) for historical changes.
## 👨‍💻Contributors
* **[Jing Ling](https://github.com/shmilylty)**
* Core developer
You can see all the developers involved in the project in [contributors.md](https://github.com/shmilylty/OneForAll/tree/master/docs/contributors.md).
## 📄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!
## 📜Announce
Please do not use in illegal purposes, don't be a dick.
## 💖Stargazers over time
[![Stargazers over time](https://starchart.cc/shmilylty/OneForAll.svg)](https://starchart.cc/shmilylty/OneForAll)
+147
View File
@@ -0,0 +1,147 @@
**🤔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 Information
SYNOPSIS
oneforall.py --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 --target ./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 --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:
Parameter valid optional value 1, 0, none indicates that the export is
valid, invalid, and all subdomains, respectively.
Parameter port have optional values 'default' 'small', 'large',
See config.py configuration for details.
Parameter format have optional values 'txt', 'rst', 'csv', 'tsv', 'json',
'yaml', 'html', 'jira', 'xls', 'xlsx', 'dbf', 'latex', 'ods'.
If the parameter path is None, the appropriate file is generated in the
project result directory based on the format parameter and the domain
name.
Parameter path default None uses the OneForAll result directory generation path
ARGUMENTS
TARGET
Single domain name or file path for one domain name per line (required)
FLAGS
--brute=BRUTE
Use blasting module (default False)
--dns=DNS
DNS resolve subdomain (default True)
--req=REQ
HTTP request subdomain (default True)
--port=PORT
Port range for request authentication (default 80 port)
--valid=VALID
Export validity of subdomains (default None)
--format=FORMAT
Export format (default xls)
--path=PATH
Export path (default None)
--takeover=TAKEOVER
Check subdomain takeover (default False)
--show=SHOW
Terminal display exported data (default False)
```
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 aiobrute.py --help
```
```bash
NAME
aiobrute.py - OneForAll multi-process multi-correlation asynchronous subdomain blasting module
SYNOPSIS
aiobrute.py --target=TARGET <flags>
DESCRIPTION
Example
python3 aiobrute.py --target example.com run
python3 aiobrute.py --target ./domains.txt run
python3 aiobrute.py --target example.com --process 4 --coroutine 64 run
python3 aiobrute.py --target example.com --wordlist subdomains.txt run
python3 aiobrute.py --target example.com --recursive True --depth 2 run
python3 aiobrute.py --target m.{fuzz}.a.bz --fuzz True --rule [a-z] run
Note:
Parameter valid optional value 1, 0, none indicates that the export is
valid, invalid, and all subdomains, respectively.
Parameter format have optional values 'txt', 'rst', 'csv', 'tsv', 'json',
'yaml', 'html', 'jira', 'xls', 'xlsx', 'dbf', 'latex', 'ods'.
If the parameter path is None, the appropriate file is generated in the
project result directory based on the format parameter and the domain
name.
ARGUMENTS
TARGET
Single domain name or file path for one domain name per line (required)
FLAGS
--process=PROCESS
Number of processes blasted (default CPU core count)
--coroutine=COROUTINE
Number of coroutines per blasting process (default 1024)
--wordlist=WORDLIST
Specify the dictionary path used for blasting (config.py is used by default)
--recursive=RECURSIVE
Whether to use recursive blasting (default False)
--depth=DEPTH
Depth of recursive blasting (default 2)
--namelist=NAMELIST
Specifies the dictionary path used by recursive blasting (configured by default using config.py)
--fuzz=FUZZ
Whether to use the fuzz mode for blasting (default False, you must specify the fuzz regular rule)
--rule=RULE
Regular rules used by fuzz mode (configured by default using config.py)
--export=EXPORT
Whether to export the blast result (default True)
--valid=VALID
Export validity of subdomains (default None)
--format=FORMAT
Export format (default xls)
--path=PATH
Export path (default None)
--show=SHOW
Terminal display exported data (default False)
```
+76
View File
@@ -0,0 +1,76 @@
# 安装依赖
## 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
```bash
sudo apt update
sudo apt install git -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 uvloop -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
```bash
sudo yum update
sudo yum install git -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 uvloop -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/
pip3 install uvloop -i https://mirrors.aliyun.com/pypi/simple/
python3 oneforall.py --help
```
+9
View File
@@ -0,0 +1,9 @@
# OneForAll后续开发计划
## 下一步计划
- [ ] 各模块支持优化和完善
- [x] 子域监控(标记每次新发现的子域)
- [ ] 子域收集爬虫实现(包括从JS等静态资源文件中收集子域)
- [ ] 操作强大交互人性的前端界面实现(暂定:Element+Flask
+40
View File
@@ -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便下载`brotlipy0.7.0cp38cp38win_amd64.whl`(一般来说下载最新版本的),然后手动安装:
```bash
pip3 install brotlipy0.7.0cp38cp38win_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文件中的数据。
+219
View File
@@ -0,0 +1,219 @@
# 使用帮助
命令行参数只提供了一些常用参数,更多详细的参数配置请见[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 --target=TARGET <flags>
DESCRIPTION
Version: 0.0.4
Project: https://git.io/fjHT1
Example:
python3 oneforall.py --target example.com run
python3 oneforall.py --target ./domains.txt run
python3 oneforall.py --target example.com --brute True run
python3 oneforall.py --target example.com --verify False run
python3 oneforall.py --target example.com --valid None 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 --show True run
Note:
参数valid可选值1,0,None分别表示导出有效,无效,全部子域
参数verify为True会尝试解析和请求子域并根据结果给子域有效性打上标签
参数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
使用爆破模块(默认False)
--verify=VERIFY
验证子域有效性(默认True)
--port=PORT
请求验证的端口范围(默认medium)
--valid=VALID
导出子域的有效性(默认1)
--path=PATH
导出路径(默认None)
--format=FORMAT
导出格式(默认xlsx)
--show=SHOW
终端显示导出数据(默认False)
```
2. aiobrute.py使用帮助
关于泛解析问题处理程序首先会访问一个随机的子域判断是否泛解析,如果使用了泛解析则是通过以下判断处理:
- 一是主要是与泛解析的IP集合和TTL值做对比,可以参考[这篇文章](http://sh3ll.me/archives/201704041222.txt)。
- 二是多次解析到同一IP集合次数(默认设置为10,可以在config.py设置大小)
- 考虑爆破效率问题目前还没有加上HTTP响应体相似度对比和响应体内容判断
```bash
python aiobrute.py --help
```
```bash
NAME
aiobrute.py - OneForAll多进程多协程异步子域爆破模块
SYNOPSIS
aiobrute.py --target=TARGET <flags>
DESCRIPTION
Example
python3 aiobrute.py --target example.com run
python3 aiobrute.py --target ./domains.txt run
python3 aiobrute.py --target example.com --process 4 --coroutine 64 run
python3 aiobrute.py --target example.com --wordlist subdomains.txt run
python3 aiobrute.py --target example.com --recursive True --depth 2 run
python3 aiobrute.py --target m.{fuzz}.a.bz --fuzz True --rule [a-z] run
Note:
参数segment的设置受CPU性能,网络带宽,运营商限制等问题影响,默认设置500个子域为任务组,
当你觉得你的环境不受以上因素影响,当前爆破速度较慢,那么强烈建议根据字典大小调整大小:
十万字典建议设置为5000,百万字典设置为50000
参数valid可选值1,0,None,分别表示导出有效,无效,全部子域
参数format可选格式:'csv', 'tsv', 'json', 'yaml', 'html', 'xls', 'xlsx',
'dbf', 'latex', 'ods'
参数path为None会根据format参数和域名名称在项目结果目录生成相应文件
ARGUMENTS
TARGET
单个域名或者每行一个域名的文件路径
FLAGS
--process=PROCESS
爆破的进程数(默认CPU核心数)
--coroutine=COROUTINE
每个爆破进程下的协程数(默认64)
--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配置)
--export=EXPORT
是否导出爆破结果(默认True)
--valid=VALID
导出子域的有效性(默认None)
--format=FORMAT
导出格式(默认xlsx)
--path=PATH
导出路径(默认None)
--show=SHOW
终端显示导出数据(默认False)
```
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 TABLE <flags>
DESCRIPTION
Example:
python3 dbexport.py --table name --format csv --path= ./result.csv
python3 dbexport.py --db result.db --table name --show False
Note:
参数port可选值有'small', 'medium', 'large', 'xlarge',详见config.py配置
参数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)
--show=SHOW
终端显示导出数据(默认False)
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env python3
# coding=utf-8
"""
github自动接管
"""
import json
import base64
import requests
from config import api
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 = api.github_api_user
token = api.github_api_token
CHECK_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=CHECK_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=CHECK_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
}
html_url = 'https://api.github.com/repos/' + user + '/' + repo_name + '/contents/index.html'
url_url = 'https://api.github.com/repos/' + user + '/' + repo_name + '/contents/CNAME'
html_r = requests.put(url=html_url, data=json.dumps(html_dict),
headers=CHECK_HEADERS) # 上传index.html
cname_r = requests.put(url=url_url, data=json.dumps(url_dict),
headers=CHECK_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=CHECK_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是否存在同名接管库')
@@ -1,7 +1,6 @@
import time
import config
from config import api
from common.query import Query
from config import logger
from config.log import logger
class CensysAPI(Query):
@@ -11,8 +10,8 @@ class CensysAPI(Query):
self.module = 'Certificate'
self.source = "CensysAPIQuery"
self.addr = 'https://www.censys.io/api/v1/search/certificates'
self.id = config.censys_api_id
self.secret = config.censys_api_secret
self.id = api.censys_api_id
self.secret = api.censys_api_secret
self.delay = 3.0 # Censys 接口查询速率限制 最快2.5秒查1次
def query(self):
@@ -22,29 +21,28 @@ class CensysAPI(Query):
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
data = {
'query': 'parsed.names: example.com',
'query': f'parsed.names: {self.domain}',
'page': 1,
'fields': ['parsed.subject_dn'],
'fields': ['parsed.subject_dn', 'parsed.names'],
'flatten': True}
resp = self.post(self.addr, json=data, auth=(self.id, self.secret))
if not resp:
return
resp_json = resp.json()
status = resp_json.get('status')
json = resp.json()
status = json.get('status')
if status != 'ok':
logger.log('ALERT', status)
logger.log('ALERT', f'{self.source} module {status}')
return
subdomains_find = self.match(self.domain, str(resp_json))
self.subdomains = self.subdomains.union(subdomains_find)
pages = resp_json.get('metadata').get('pages')
subdomains = self.match_subdomains(self.domain, str(json))
self.subdomains = self.subdomains.union(subdomains)
pages = json.get('metadata').get('pages')
for page in range(2, pages + 1):
time.sleep(self.delay)
data['page'] = page
resp = self.post(self.addr, json=data, auth=(self.id, self.secret))
if not resp:
return
subdomains_find = self.match(self.domain, str(resp.json()))
self.subdomains = self.subdomains.union(subdomains_find)
subdomains = self.match_subdomains(self.domain, str(resp.json()))
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,4 +1,3 @@
import time
from common import utils
from common.query import Query
@@ -15,17 +14,17 @@ class CertSpotter(Query):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'domain': self.domain, 'include_subdomains': 'true',
params = {'domain': self.domain,
'include_subdomains': 'true',
'expand': 'dns_names'}
resp = self.get(self.addr, params)
if not resp:
return
subdomains_find = utils.match_subdomain(self.domain, str(resp.json()))
subdomains = self.match_subdomains(self.domain, str(resp.json()))
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,4 +1,3 @@
import time
from common import utils
from common.query import Query
@@ -15,15 +14,14 @@ class Crtsh(Query):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'q': f'%.{self.domain}', 'output': 'json'}
resp = self.get(self.addr, params)
if not resp:
return
subdomains_find = utils.match_subdomain(self.domain, str(resp.json()))
self.subdomains = self.subdomains.union(subdomains_find)
subdomains = self.match_subdomains(self.domain, str(resp.json()))
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,4 +1,3 @@
import time
from common import utils
from common.query import Query
@@ -16,17 +15,17 @@ class Google(Query):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'include_expired': 'true', 'include_subdomains': 'true',
params = {'include_expired': 'true',
'include_subdomains': 'true',
'domain': self.domain}
resp = self.get(self.addr, params)
if not resp:
return
subdomains_find = utils.match_subdomain(self.domain, resp.text)
subdomains = self.match_subdomains(self.domain, resp.text)
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,24 +1,25 @@
"""
查询域名的NS记录(域名服务器记录记录该域名由哪台域名服务器解析)检查查出的域名服务器是否开启
DNS域传送如果开启且没做访问控制和身份验证便加以利用获取域名的所有记录
查询域名的NS记录(域名服务器记录记录该域名由哪台域名服务器解析)检查查出的域名服务器是
否开启DNS域传送如果开启且没做访问控制和身份验证便加以利用获取域名的所有记录
DNS域传送(DNS zone transfer)指的是一台备用域名服务器使用来自主域名服务器的数据刷新自己的域
数据库目的是为了做冗余备份防止主域名服务器出现故障时 dns 解析不可用
当主服务器开启DNS域传送同时又对来请求的备用服务器未作访问控制和身份验证便可以利用此漏洞获取某个
域的所有记录
DNS域传送(DNS zone transfer)指的是一台备用域名服务器使用来自主域名服务器的数据刷新自己
的域数据库目的是为了做冗余备份防止主域名服务器出现故障时 dns 解析不可用
当主服务器开启DNS域传送同时又对来请求的备用服务器未作访问控制和身份验证便可以利用此漏洞获
取某个域的所有记录
"""
import dns.resolver
import dns.zone
from common import resolve, utils
from common import utils
from common.module import Module
from config import logger
from config.log import logger
class CheckAXFR(Module):
"""
DNS域传送漏洞检查类
DNS zone transfer vulnerability base class
"""
def __init__(self, domain: str):
Module.__init__(self)
self.domain = self.register(domain)
@@ -28,43 +29,44 @@ class CheckAXFR(Module):
def axfr(self, server):
"""
执行域传送
Perform domain transfer
:param server: 域名服务器
:param server: domain server
"""
logger.log('DEBUG', f'尝试对{self.domain}的域名服务器{server}进行域传送')
logger.log('DEBUG', f'Trying to perform domain transfer in {server} of {self.domain}')
try:
xfr = dns.query.xfr(server, self.domain, timeout=30.0)
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', str(e))
logger.log('DEBUG', f'{self.domain}的域名服务器{server}进行域传送失败')
logger.log('DEBUG', e.args)
logger.log('DEBUG', f'Domain transfer to server {server} of {self.domain} failed')
return
names = zone.nodes.keys()
for name in names:
full_domain = str(name) + '.' + self.domain
subdomain = utils.match_subdomain(self.domain, full_domain)
subdomain = self.match_subdomains(self.domain, full_domain)
self.subdomains = self.subdomains.union(subdomain)
record = zone[name].to_text(name)
self.results.append(record)
if self.results:
logger.log('DEBUG', f'发现{self.domain}{server}上的域传送记录')
logger.log('DEBUG', f'Found the domain transfer record of {self.domain} on {server}')
logger.log('DEBUG', '\n'.join(self.results))
self.results = []
def check(self):
"""
正则匹配响应头中的内容安全策略字段以发现子域名
check
"""
resolver = resolve.dns_resolver()
resolver = utils.dns_resolver()
try:
answers = resolver.query(self.domain, "NS")
except Exception as e:
logger.log('ERROR', e)
logger.log('ERROR', e.args)
return
nsservers = [str(answer) for answer in answers]
if not len(nsservers):
logger.log('ALERT', f'没有找到{self.domain}的NS域名服务器记录')
logger.log('ALERT', f'No name server record found for {self.domain}')
return
for nsserver in nsservers:
self.axfr(nsserver)
@@ -93,4 +95,4 @@ def do(domain): # 统一入口名字 方便多线程调用
if __name__ == '__main__':
do('ZoneTransfer.me')
do('example.com')
# do('example.com')
@@ -10,7 +10,6 @@ class CheckCDX(Module):
"""
检查crossdomain.xml文件收集子域名
"""
def __init__(self, domain: str):
Module.__init__(self)
self.domain = self.register(domain)
@@ -25,16 +24,15 @@ class CheckCDX(Module):
f'https://{self.domain}/crossdomain.xml',
f'http://www.{self.domain}/crossdomain.xml',
f'https://www.{self.domain}/crossdomain.xml']
response = None
for url in urls:
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
response = self.get(url)
if response:
break
if not response:
return
self.subdomains = utils.match_subdomain(self.domain, response.text)
response = self.get(url, check=False)
if not response:
return
if response and len(response.content):
self.subdomains = self.match_subdomains(self.domain,
response.text)
def run(self):
"""
@@ -8,7 +8,7 @@ import ssl
from common import utils
from common.module import Module
from config import logger
from config.log import logger
class CheckCert(Module):
@@ -23,16 +23,17 @@ class CheckCert(Module):
"""
获取域名证书并匹配证书中的子域名
"""
ctx = ssl.create_default_context()
sock = ctx.wrap_socket(socket.socket(), server_hostname=self.domain)
try:
ctx = ssl.create_default_context()
sock = ctx.wrap_socket(socket.socket(),
server_hostname=self.domain)
sock.connect((self.domain, self.port))
cert_dict = sock.getpeercert()
except Exception as e:
logger.log('ERROR', e)
logger.log('DEBUG', e.args)
return
subdomains_find = utils.match_subdomain(self.domain, str(cert_dict))
self.subdomains = self.subdomains.union(subdomains_find)
subdomains = self.match_subdomains(self.domain, str(cert_dict))
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
+82
View File
@@ -0,0 +1,82 @@
"""
Collect subdomains from ContentSecurityPolicy
"""
import requests
from common import utils
from common.module import Module
from config.log import logger
class CheckCSP(Module):
"""
Collect subdomains from ContentSecurityPolicy
"""
def __init__(self, domain, header):
Module.__init__(self)
self.domain = self.register(domain)
self.module = 'Check'
self.source = 'ContentSecurityPolicy'
self.csp_header = header
def grab_header(self):
"""
Get header
:return: ContentSecurityPolicy header
"""
csp_header = dict()
urls = [f'http://{self.domain}',
f'https://{self.domain}',
f'http://www.{self.domain}',
f'https://www.{self.domain}']
for url in urls:
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
response = self.get(url, check=False)
if response:
csp_header = response.headers
break
return csp_header
def check(self):
"""
正则匹配响应头中的内容安全策略字段以发现子域名
"""
if not self.csp_header:
self.csp_header = self.grab_header()
csp = self.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 of {self.domain}')
return
self.subdomains = self.match_subdomains(self.domain, csp)
def run(self):
"""
类执行入口
"""
self.begin()
self.check()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain, header=None): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
:param dict or None header: 响应头
"""
check = CheckCSP(domain, header)
check.run()
if __name__ == '__main__':
resp = requests.get('https://content-security-policy.com/')
do('google-analytics.com', resp.headers)
+56
View File
@@ -0,0 +1,56 @@
# 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.module import Module
from common import utils
class CheckNSEC(Module):
def __init__(self, domain):
Module.__init__(self)
self.domain = self.register(domain)
self.module = 'check'
self.source = "CheckNSEC"
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(self.domain, record)
subdomain = ''.join(subdomains) # 其实这里的subdomains的长度为1 也就是说只会有一个子域
self.subdomains = self.subdomains.union(subdomains)
self.gen_record(subdomains, record)
if subdomain == self.domain: # 当查出子域为主域 说明完成了一个循环 不再继续查询
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 do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
brute = CheckNSEC(domain)
brute.run()
if __name__ == '__main__':
do('iana.org')
@@ -1,18 +1,14 @@
"""
检查内容安全策略收集子域名收集子域名
"""
import requests
from common.module import Module
from common import utils
from config import logger
class CheckRobots(Module):
"""
检查robots.txt收集子域名
"""
def __init__(self, domain):
Module.__init__(self)
self.domain = self.register(domain)
@@ -27,16 +23,15 @@ class CheckRobots(Module):
f'https://{self.domain}/robots.txt',
f'http://www.{self.domain}/robots.txt',
f'https://www.{self.domain}/robots.txt']
response = None
for url in urls:
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
response = self.get(url, allow_redirects=False)
if response:
break
if not response:
return
self.subdomains = utils.match_subdomain(self.domain, response.text)
response = self.get(url, check=False, allow_redirects=False)
if not response:
return
if response and len(response.content):
self.subdomains = self.match_subdomains(self.domain,
response.text)
def run(self):
"""
@@ -1,18 +1,14 @@
"""
检查内容安全策略收集子域名收集子域名
"""
import requests
from common.module import Module
from common import utils
from config import logger
class CheckRobots(Module):
"""
检查sitemap收集子域名
"""
def __init__(self, domain):
Module.__init__(self)
self.domain = self.register(domain)
@@ -39,17 +35,17 @@ class CheckRobots(Module):
f'https://{self.domain}/sitemap_index.xml',
f'http://www.{self.domain}/sitemap_index.xml',
f'https://www.{self.domain}/sitemap_index.xml']
response = None
for url in urls:
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
self.timeout = 10
response = self.get(url, allow_redirects=False)
if response:
break
if not response:
return
self.subdomains = utils.match_subdomain(self.domain, response.text)
response = self.get(url, check=False, allow_redirects=False)
if not response:
return
if response and len(response.content):
self.subdomains = self.match_subdomains(self.domain,
response.text)
def run(self):
"""
+96
View File
@@ -0,0 +1,96 @@
import time
import threading
import importlib
import dbexport
from config.log import logger
from config import setting
class Collect(object):
"""
Collect subdomains
"""
def __init__(self, domain, export=True):
self.domain = domain
self.elapse = 0.0
self.modules = []
self.collect_funcs = []
self.path = None
self.export = export
self.format = 'csv'
def get_mod(self):
"""
Get modules
"""
if setting.enable_all_module:
# modules = ['brute', 'certificates', 'crawl',
# 'datasets', 'intelligence', 'search']
# The crawl module has some problems
modules = ['certificates', 'check', 'datasets',
'dnsquery', 'intelligence', 'search']
# modules = ['intelligence'] # The crawl module has some problems
for module in modules:
module_path = setting.module_dir.joinpath(module)
for path in module_path.rglob('*.py'):
# Classes to be imported
import_module = ('modules.' + module, path.stem)
self.modules.append(import_module)
else:
self.modules = setting.enable_partial_module
def import_func(self):
"""
Import do function
"""
for package, name in self.modules:
import_object = importlib.import_module('.' + name, package)
func = getattr(import_object, 'do')
self.collect_funcs.append([func, name])
def run(self):
"""
Class entrance
"""
start = time.time()
logger.log('INFOR', f'Start collecting subdomains of {self.domain}')
self.get_mod()
self.import_func()
threads = []
# Create subdomain collection threads
for collect_func in self.collect_funcs:
func_obj, func_name = collect_func
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(setting.module_thread_timeout)
for thread in threads:
if thread.is_alive():
logger.log('ALERT', f'{thread.name} module thread timed out')
# Export
if self.export:
if not self.path:
name = f'{self.domain}.{self.format}'
self.path = setting.result_save_dir.joinpath(name)
dbexport.export(self.domain, path=self.path, format=self.format)
end = time.time()
self.elapse = round(end - start, 1)
if __name__ == '__main__':
collect = Collect('example.com')
collect.run()
@@ -1,6 +1,6 @@
import cdx_toolkit
from common.crawl import Crawl
from config import logger
from config.log import logger
class ArchiveCrawl(Crawl):
@@ -26,10 +26,10 @@ class ArchiveCrawl(Crawl):
for resp in cdx.iter(url, limit=limit):
if resp.data.get('status') not in ['301', '302']:
url = resp.data.get('url')
subdomains_find = self.match(self.register(domain),
url + resp.text)
subdomains = self.match_subdomains(self.register(domain),
url + resp.text)
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -2,7 +2,6 @@ import cdx_toolkit
from tqdm import tqdm
from common.crawl import Crawl
from config import logger
class CommonCrawl(Crawl):
@@ -27,9 +26,9 @@ class CommonCrawl(Crawl):
for resp in tqdm(cdx.iter(url, limit=limit), total=limit):
if resp.data.get('status') not in ['301', '302']:
subdomains_find = self.match(self.register(domain), resp.text)
subdomains = self.match_subdomains(self.register(domain), resp.text)
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,5 +1,4 @@
import time
import config
from config import api
from common.query import Query
@@ -10,13 +9,12 @@ class BinaryEdgeAPI(Query):
self.module = 'Dataset'
self.source = 'BinaryEdgeAPIQuery'
self.addr = 'https://api.binaryedge.io/v2/query/domains/subdomain/'
self.api = config.binaryedge_api
self.api = api.binaryedge_api
def query(self):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.header.update({'X-Key': self.api})
self.proxy = self.get_proxy(self.source)
@@ -24,8 +22,8 @@ class BinaryEdgeAPI(Query):
resp = self.get(url)
if not resp:
return
subdomains_find = self.match(self.domain, str(resp.json()))
self.subdomains = self.subdomains.union(subdomains_find)
subdomains = self.match_subdomains(self.domain, str(resp.json()))
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
+56
View File
@@ -0,0 +1,56 @@
import cloudscraper
from common.query import Query
from config.log import logger
class BufferOver(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Dataset'
self.source = 'BufferOverQuery'
self.addr = 'https://dns.bufferover.run/dns?q='
def query(self):
"""
向接口查询子域并做子域匹配
"""
# 绕过cloudFlare验证
scraper = cloudscraper.create_scraper()
scraper.proxies = self.get_proxy(self.source)
url = self.addr + self.domain
try:
resp = scraper.get(url, timeout=self.timeout)
except Exception as e:
logger.log('ERROR', e.args)
return
if resp.status_code != 200:
return
subdomains = self.match_subdomains(self.domain, str(resp.json()))
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = BufferOver(domain)
query.run()
if __name__ == '__main__':
do('example.com')
@@ -1,29 +1,27 @@
import time
from common.query import Query
class BufferOver(Query):
class CeBaidu(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Dataset'
self.source = 'BufferOverQuery'
self.addr = 'https://dns.bufferover.run/dns'
self.source = 'CeBaiduQuery'
self.addr = 'https://ce.baidu.com/index/getRelatedSites'
def query(self):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'q': self.domain}
params = {'site_address': self.domain}
resp = self.get(self.addr, params)
if not resp:
return
subdomains_find = self.match(self.domain, resp.text)
subdomains = self.match_subdomains(self.domain, str(resp.json()))
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -43,7 +41,7 @@ def do(domain): # 统一入口名字 方便多线程调用
:param str domain: 域名
"""
query = BufferOver(domain)
query = CeBaidu(domain)
query.run()
@@ -1,4 +1,3 @@
import time
from common.query import Query
@@ -14,16 +13,15 @@ class Chinaz(Query):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
self.addr = self.addr + self.domain
resp = self.get(self.addr)
if not resp:
return
subdomains_find = self.match(self.domain, resp.text)
subdomains = self.match_subdomains(self.domain, resp.text)
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,5 +1,4 @@
import time
import config
from config import api
from common.query import Query
@@ -10,22 +9,21 @@ class ChinazAPI(Query):
self.module = 'Dataset'
self.source = 'ChinazAPIQuery'
self.addr = 'https://apidata.chinaz.com/CallAPI/Alexa'
self.api = config.chinaz_api
self.api = api.chinaz_api
def query(self):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'key': self.api, 'domainName': self.domain}
resp = self.get(self.addr, params)
if not resp:
return
subdomains_find = self.match(self.domain, str(resp.json()))
subdomains = self.match_subdomains(self.domain, str(resp.json()))
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,5 +1,4 @@
import time
import config
from config import api
from common.query import Query
@@ -10,22 +9,21 @@ class CirclAPI(Query):
self.module = 'Dataset'
self.source = 'CirclAPIQuery'
self.addr = 'https://www.circl.lu/pdns/query/'
self.user = config.circl_api_username
self.pwd = config.circl_api_password
self.user = api.circl_api_username
self.pwd = api.circl_api_password
def query(self):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
resp = self.get(self.addr + self.domain, auth=(self.user, self.pwd))
if not resp:
return
subdomains_find = self.match(self.domain, str(resp.json()))
subdomains = self.match_subdomains(self.domain, str(resp.json()))
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,5 +1,4 @@
import time
import config
from config import api
from common import utils
from common.query import Query
@@ -11,13 +10,12 @@ class DNSdbAPI(Query):
self.module = 'Dataset'
self.source = 'DNSdbAPIQuery'
self.addr = 'https://api.dnsdb.info/lookup/rrset/name/'
self.api = config.dnsdb_api_key
self.api = api.dnsdb_api_key
def query(self):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.header.update({'X-API-Key': self.api})
self.proxy = self.get_proxy(self.source)
@@ -25,10 +23,9 @@ class DNSdbAPI(Query):
resp = self.get(url)
if not resp:
return
if resp.status_code == 200:
subdomains_find = utils.match_subdomain(self.domain, resp.text)
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
subdomains = self.match_subdomains(self.domain, resp.text)
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,4 +1,3 @@
import time
from common import utils
from common.query import Query
@@ -15,7 +14,6 @@ class DNSdumpster(Query):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.header.update({'Referer': 'https://dnsdumpster.com'})
self.proxy = self.get_proxy(self.source)
@@ -28,10 +26,10 @@ class DNSdumpster(Query):
resp = self.post(self.addr, data)
if not resp:
return
subdomains_find = utils.match_subdomain(self.domain, resp.text)
if subdomains_find:
subdomains = self.match_subdomains(self.domain, resp.text)
if subdomains:
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -21,10 +21,10 @@ class HackerTarget(Query):
if not resp:
return
if resp.status_code == 200:
subdomains_find = utils.match_subdomain(self.domain, resp.text)
if subdomains_find:
subdomains = self.match_subdomains(self.domain, resp.text)
if subdomains:
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,16 +1,13 @@
import random
from common import utils
from common.query import Query
class PTRArchive(Query):
class IP138(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Dataset'
self.source = "PTRArchiveQuery"
self.addr = 'http://ptrarchive.com/tools/search3.htm'
self.source = 'IP138Query'
self.addr = 'https://site.ip138.com/{domain}/domain.htm'
def query(self):
"""
@@ -18,17 +15,13 @@ class PTRArchive(Query):
"""
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
# 绕过主页前端JS验证
self.cookie = {'pa_id': str(random.randint(0, 1000000000))}
params = {'label': self.domain, 'date': 'ALL'}
resp = self.get(self.addr, params)
self.addr = self.addr.format(domain=self.domain)
resp = self.get(self.addr)
if not resp:
return
if resp.status_code == 200:
subdomains_find = utils.match_subdomain(self.domain, resp.text)
if subdomains_find:
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
subdomains = self.match_subdomains(self.domain, resp.text)
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -48,7 +41,7 @@ def do(domain): # 统一入口名字 方便多线程调用
:param str domain: 域名
"""
query = PTRArchive(domain)
query = IP138(domain)
query.run()
@@ -1,5 +1,6 @@
import config
from config import api
from common.query import Query
from config.log import logger
class IPv4InfoAPI(Query):
@@ -9,7 +10,7 @@ class IPv4InfoAPI(Query):
self.module = 'Dataset'
self.source = 'IPv4InfoAPIQuery'
self.addr = ' http://ipv4info.com/api_v1/'
self.api = config.ipv4info_api_key
self.api = api.ipv4info_api_key
def query(self):
"""
@@ -26,17 +27,22 @@ class IPv4InfoAPI(Query):
return
if resp.status_code != 200:
break # 请求不正常通常网络是有问题,不再继续请求下去
resp_json = resp.json()
subdomains_find = self.match(self.domain, str(resp_json))
if not subdomains_find:
try:
json = resp.json()
except Exception as e:
logger.log('DEBUG', e.args)
break
subdomains = self.match_subdomains(self.domain, str(json))
if not subdomains:
break
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
# 不直接使用subdomains是因为可能里面会出现不符合标准的子域名
subdomains = resp_json.get('Subdomains')
# ipv4info子域查询接口每次最多返回300个 用来判断是否还有下一页
if len(subdomains) < 300:
break
subdomains = json.get('Subdomains')
if subdomains:
# ipv4info子域查询接口每次最多返回300个 用来判断是否还有下一页
if len(subdomains) < 300:
break
page += 1
if page >= 50: # ipv4info子域查询接口最多允许查询50页
break
@@ -45,6 +51,8 @@ class IPv4InfoAPI(Query):
"""
类执行入口
"""
if not self.check(self.api):
return
self.begin()
self.query()
self.finish()
@@ -24,12 +24,13 @@ class NetCraft(Query):
self.header = self.get_header() # Netcraft会检查User-Agent
resp = self.get(self.init)
if not resp:
return None
return False
self.cookie = resp.cookies
cookie_value = self.cookie['netcraft_js_verification_challenge']
cookie_encode = parse.unquote(cookie_value).encode('utf-8')
verify_taken = hashlib.sha1(cookie_encode).hexdigest()
self.cookie['netcraft_js_verification_response'] = verify_taken
return True
def query(self):
"""
@@ -42,19 +43,23 @@ class NetCraft(Query):
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'host': '*.' + self.domain, 'from': self.page_num}
params = {'restriction': 'site ends with',
'host': '.' + self.domain,
'from': self.page_num}
resp = self.get(self.addr + last, params)
if not resp:
return
subdomains_find = self.match(self.domain, resp.text)
if not subdomains_find: # 搜索没有发现子域名则停止搜索
subdomains = self.match_subdomains(self.domain, resp.text)
if not subdomains: # 搜索没有发现子域名则停止搜索
break
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
if 'Next page' not in resp.text: # 搜索页面没有出现下一页时停止搜索
self.subdomains = self.subdomains.union(subdomains)
if 'Next Page' not in resp.text: # 搜索页面没有出现下一页时停止搜索
break
last = re.search(r'&last=.*' + self.domain, resp.text).group(0)
self.page_num += self.per_page_num
if self.page_num > 500:
break
def run(self):
"""
+55
View File
@@ -0,0 +1,55 @@
from config import api
from common.query import Query
class PassiveDnsAPI(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Dataset'
self.source = 'PassiveDnsQuery'
self.addr = api.passivedns_api_addr or 'http://api.passivedns.cn'
self.token = api.passivedns_api_token
def query(self):
"""
向接口查询子域并做子域匹配
"""
self.header = self.get_header()
self.header.update({'X-AuthToken': self.token})
self.proxy = self.get_proxy(self.source)
url = self.addr + '/flint/rrset/*.' + self.domain
resp = self.get(url)
if not resp:
return
subdomains = self.match_subdomains(self.domain, str(resp.json()))
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
类执行入口
"""
if 'api.passivedns.cn' in self.addr:
if not self.check(self.token):
return
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = PassiveDnsAPI(domain)
query.run()
if __name__ == '__main__':
do('example.com')
+61
View File
@@ -0,0 +1,61 @@
from common.query import Query
class QianXun(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = domain
self.module = 'Query'
self.source = 'QianXunQuery'
def query(self):
"""
向接口查询子域并做子域匹配
"""
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
num = 1
while True:
data = {'ecmsfrom': '',
'show': '',
'num': '',
'classid': '0',
'keywords': self.domain}
url = f'https://www.dnsscan.cn/dns.html?' \
f'keywords={self.domain}&page={num}'
resp = self.post(url, data)
if not resp:
break
subdomains = self.match_subdomains(self.domain, resp.text)
self.subdomains = self.subdomains.union(subdomains)
if '<div id="page" class="pagelist">' not in resp.text:
break
if '<li class="disabled"><span>&raquo;</span></li>' in resp.text:
break
num += 1
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = QianXun(domain)
query.run()
if __name__ == '__main__':
do('example.com')
@@ -1,30 +1,28 @@
import time
from common import utils
from common.query import Query
class Entrust(Query):
class RapidDNS(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Certificate'
self.source = 'EntrustQuery'
self.addr = 'https://ctsearch.entrust.com/api/v1/certificates'
self.module = 'Dataset'
self.source = 'RapidDNSQuery'
def query(self):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'fields': 'subjectDN', 'domain': self.domain,
'includeExpired': 'true'}
resp = self.get(self.addr, params)
url = f'http://rapiddns.io/subdomain/{self.domain}'
params = {'full': '1'}
resp = self.get(url, params)
if not resp:
return
subdomains_find = utils.match_subdomain(self.domain, str(resp.json()))
self.subdomains = self.subdomains.union(subdomains_find)
subdomains = self.match_subdomains(self.domain, resp.text)
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -44,7 +42,7 @@ def do(domain): # 统一入口名字 方便多线程调用
:param str domain: 域名
"""
query = Entrust(domain)
query = RapidDNS(domain)
query.run()
@@ -1,4 +1,3 @@
import time
from common.query import Query
@@ -14,16 +13,15 @@ class Riddler(Query):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'q': 'pld:' + self.domain}
resp = self.get(self.addr, params)
if not resp:
return
subdomains_find = self.match(self.domain, resp.text)
subdomains = self.match_subdomains(self.domain, resp.text)
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -32,10 +32,10 @@ class Robtex(Query):
resp = self.get(url)
if not resp:
return
subdomains_find = self.match(self.domain, resp.text)
if subdomains_find:
subdomains = self.match_subdomains(self.domain, resp.text)
if subdomains:
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,5 +1,4 @@
import time
import config
from config import api
from common.query import Query
@@ -8,16 +7,15 @@ class SecurityTrailsAPI(Query):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Dataset'
self.source = 'SecurityTrailsQuery'
self.source = 'SecurityTrailsAPIQuery'
self.addr = 'https://api.securitytrails.com/v1/domain/'
self.api = config.securitytrails_api
self.api = api.securitytrails_api
self.delay = 2 # SecurityTrails查询时延至少2秒
def query(self):
"""
向接口查询子域并做子域匹配
"""
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'apikey': self.api}
@@ -26,10 +24,10 @@ class SecurityTrailsAPI(Query):
if not resp:
return
prefixs = resp.json()['subdomains']
subdomains_find = [f'{prefix}.{self.domain}' for prefix in prefixs]
if subdomains_find:
subdomains = [f'{prefix}.{self.domain}' for prefix in prefixs]
if subdomains:
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
@@ -1,4 +1,3 @@
import time
from common.query import Query
@@ -19,18 +18,17 @@ class SiteDossier(Query):
向接口查询子域并做子域匹配
"""
while True:
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
url = f'{self.addr}{self.domain}/{self.page_num}'
resp = self.get(url)
if not resp:
return
subdomains_find = self.match(self.domain, resp.text)
if not subdomains_find: # 搜索没有发现子域名则停止搜索
subdomains = self.match_subdomains(self.domain, resp.text)
if not subdomains: # 搜索没有发现子域名则停止搜索
break
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
self.subdomains = self.subdomains.union(subdomains)
# 搜索页面没有出现下一页时停止搜索
if 'Show next 100 items' not in resp.text:
break
@@ -1,40 +1,39 @@
import time
import config
from config import api
from common import utils
from common.query import Query
class CertDBAPI(Query):
class SpyseAPI(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = domain
self.module = 'Certificate'
self.source = 'CertDBAPIQuery'
self.addr = 'https://api.spyse.com/v1/subdomains'
self.token = config.certdb_api_token
self.module = 'Dataset'
self.source = 'SpyseAPIQuery'
self.token = api.spyse_api_token
def query(self):
"""
向接口查询子域并做子域匹配
"""
page_num = 1
limit = 100
offset = 0
while True:
time.sleep(self.delay)
self.header = self.get_header()
self.header.update({'Authorization': 'Bearer ' + self.token})
self.proxy = self.get_proxy(self.source)
params = {'domain': self.domain,
'api_token': self.token,
'page': page_num}
resp = self.get(self.addr, params)
addr = 'https://api.spyse.com/v2/data/domain/subdomain'
params = {'domain': self.domain, 'offset': offset, 'limit': limit}
resp = self.get(addr, params)
if not resp:
return
json = resp.json()
subdomains_find = utils.match_subdomain(self.domain, str(json))
subdomains = self.match_subdomains(self.domain, str(json))
if not subdomains: # 搜索没有发现子域名则停止搜索
break
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains_find)
page_num += 1
# 默认每次查询最多返回30条 当前条数小于30条说明已经查完
if json.get('count') < 30:
self.subdomains = self.subdomains.union(subdomains)
offset += limit
if len(json.get('data').get('items')) < limit:
break
def run(self):
@@ -57,7 +56,7 @@ def do(domain): # 统一入口名字 方便多线程调用
:param str domain: 域名
"""
query = CertDBAPI(domain)
query = SpyseAPI(domain)
query.run()
+48
View File
@@ -0,0 +1,48 @@
from common.query import Query
class Sublist3r(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Dataset'
self.source = 'Sublist3rQuery'
def query(self):
"""
向接口查询子域并做子域匹配
"""
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
addr = 'https://api.sublist3r.com/search.php'
param = {'domain': self.domain}
resp = self.get(addr, param)
if not resp:
return
subdomains = self.match_subdomains(self.domain, resp.text)
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = Sublist3r(domain)
query.run()
if __name__ == '__main__':
do('example.com')
+55
View File
@@ -0,0 +1,55 @@
import cloudscraper
from common.query import Query
from config.log import logger
class ThreatCrowd(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Dataset'
self.source = 'ThreatCrowdQuery'
self.addr = 'https://www.threatcrowd.org/searchApi' \
'/v2/domain/report?domain='
def query(self):
# 绕过cloudFlare验证
scraper = cloudscraper.create_scraper()
scraper.proxies = self.get_proxy(self.source)
url = self.addr + self.domain
try:
resp = scraper.get(url, timeout=self.timeout)
except Exception as e:
logger.log('ERROR', e.args)
return
if resp.status_code != 200:
return
subdomains = self.match_subdomains(self.domain, str(resp.json()))
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = ThreatCrowd(domain)
query.run()
if __name__ == '__main__':
do('mi.com')
+66
View File
@@ -0,0 +1,66 @@
import time
from config.log import logger
from common.query import Query
class WZPCQuery(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Dataset'
self.source = 'WZPCQuery'
def query(self):
"""
向接口查询子域并做子域匹配
"""
base_addr = 'http://114.55.181.28/check_web/' \
'databaseInfo_mainSearch.action'
page_num = 1
while True:
time.sleep(self.delay)
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
params = {'isSearch': 'true', 'searchType': 'url',
'term': self.domain, 'pageNo': page_num}
try:
resp = self.get(base_addr, params)
except Exception as e:
logger.log('ERROR', e.args)
break
if not resp:
break
subdomains = self.match_subdomains(self.domain, resp.text)
self.subdomains = self.subdomains.union(subdomains)
if not subdomains:
break
if page_num > 10:
break
page_num += 1
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain):
"""
类统一调用入口
:param str domain: 域名
"""
query = WZPCQuery(domain)
query.run()
if __name__ == '__main__':
do('sc.gov.cn')
do('bkzy.org')
+50
View File
@@ -0,0 +1,50 @@
from common.query import Query
class Ximcx(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Dataset'
self.source = 'XimcxQuery'
self.addr = 'http://sbd.ximcx.cn/DomainServlet'
def query(self):
"""
向接口查询子域并做子域匹配
"""
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
data = {'domain': self.domain}
resp = self.post(self.addr, data=data)
if not resp:
return
json = resp.json()
subdomains = self.match_subdomains(self.domain, str(json))
# 合并搜索子域名搜索结果
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = Ximcx(domain)
query.run()
if __name__ == '__main__':
do('example.com')
+35
View File
@@ -0,0 +1,35 @@
from common.lookup import Lookup
class QueryMX(Lookup):
def __init__(self, domain):
Lookup.__init__(self)
self.domain = self.register(domain)
self.module = 'dnsquery'
self.source = "QueryMX"
self.type = 'MX' # 利用的DNS记录的MX记录收集子域
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = QueryMX(domain)
query.run()
if __name__ == '__main__':
do('cuit.edu.cn')
+35
View File
@@ -0,0 +1,35 @@
from common.lookup import Lookup
class QueryNS(Lookup):
def __init__(self, domain):
Lookup.__init__(self)
self.domain = self.register(domain)
self.module = 'dnsquery'
self.source = "QueryNS"
self.type = 'NS' # 利用的DNS记录的NS记录收集子域
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = QueryNS(domain)
query.run()
if __name__ == '__main__':
do('cuit.edu.cn')
+35
View File
@@ -0,0 +1,35 @@
from common.lookup import Lookup
class QuerySOA(Lookup):
def __init__(self, domain):
Lookup.__init__(self)
self.domain = self.register(domain)
self.module = 'dnsquery'
self.source = "QuerySOA"
self.type = 'SOA' # 利用的DNS记录的SOA记录收集子域
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = QuerySOA(domain)
query.run()
if __name__ == '__main__':
do('cuit.edu.cn')
+35
View File
@@ -0,0 +1,35 @@
from common.lookup import Lookup
class QuerySPF(Lookup):
def __init__(self, domain):
Lookup.__init__(self)
self.domain = self.register(domain)
self.module = 'dnsquery'
self.source = "QuerySPF"
self.type = 'SPF' # 利用的DNS记录的SPF记录收集子域
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
brute = QuerySPF(domain)
brute.run()
if __name__ == '__main__':
do('qq.com')
+95
View File
@@ -0,0 +1,95 @@
"""
通过枚举域名常见的SRV记录并做查询来发现子域
"""
import json
import queue
import threading
from common import utils
from common.module import Module
from config.setting import data_storage_dir
class BruteSRV(Module):
def __init__(self, domain):
Module.__init__(self)
self.domain = self.register(domain)
self.module = 'dnsquery'
self.source = "BruteSRV"
self.type = 'SRV' # 利用的DNS记录的SRV记录查询子域
self.thread_num = 10
self.names_que = queue.Queue()
self.answers_que = queue.Queue()
def gen_names(self):
path = data_storage_dir.joinpath('srv_prefixes.json')
with open(path, encoding='utf-8', errors='ignore') as file:
prefixes = json.load(file)
names = map(lambda prefix: prefix + self.domain, prefixes)
for name in names:
self.names_que.put(name)
def brute(self):
"""
枚举域名的SRV记录
"""
self.gen_names()
for i in range(self.thread_num):
thread = BruteThread(self.names_que, self.answers_que)
thread.daemon = True
thread.start()
self.names_que.join()
while not self.answers_que.empty():
answer = self.answers_que.get()
if answer is None:
continue
for item in answer:
record = str(item)
subdomains = self.match_subdomains(self.domain, record)
self.subdomains = self.subdomains.union(subdomains)
self.gen_record(subdomains, record)
def run(self):
"""
类执行入口
"""
self.begin()
self.brute()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
class BruteThread(threading.Thread):
def __init__(self, names_que, answers_que):
threading.Thread.__init__(self)
self.names_que = names_que
self.answers_que = answers_que
def run(self):
while True:
name = self.names_que.get()
answer = utils.dns_query(name, 'SRV')
self.answers_que.put(answer)
self.names_que.task_done()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
brute = BruteSRV(domain)
brute.run()
if __name__ == '__main__':
do('zonetransfer.me')
# do('example.com')
+35
View File
@@ -0,0 +1,35 @@
from common.lookup import Lookup
class QueryTXT(Lookup):
def __init__(self, domain):
Lookup.__init__(self)
self.domain = self.register(domain)
self.module = 'dnsquery'
self.source = "QueryTXT"
self.type = 'TXT' # 利用的DNS记录的TXT记录收集子域
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = QueryTXT(domain)
query.run()
if __name__ == '__main__':
do('cuit.edu.cn')
+58
View File
@@ -0,0 +1,58 @@
from common.query import Query
class AlienVault(Query):
def __init__(self, domain):
Query.__init__(self)
self.domain = self.register(domain)
self.module = 'Intelligence'
self.source = 'AlienVaultQuery'
def query(self):
"""
向接口查询子域并做子域匹配
"""
self.header = self.get_header()
self.proxy = self.get_proxy(self.source)
base = 'https://otx.alienvault.com/api/v1/indicators/domain'
dns = f'{base}/{self.domain}/passive_dns'
resp = self.get(dns)
if not resp:
return
json = resp.json()
subdomains = self.match_subdomains(self.domain, str(json))
self.subdomains = self.subdomains.union(subdomains)
url = f'{base}/{self.domain}/url_list'
resp = self.get(url)
if not resp:
return
json = resp.json()
subdomains = self.match_subdomains(self.domain, str(json))
self.subdomains = self.subdomains.union(subdomains)
def run(self):
"""
类执行入口
"""
self.begin()
self.query()
self.finish()
self.save_json()
self.gen_result()
self.save_db()
def do(domain): # 统一入口名字 方便多线程调用
"""
类统一调用入口
:param str domain: 域名
"""
query = AlienVault(domain)
query.run()
if __name__ == '__main__':
do('example.com')
@@ -1,4 +1,4 @@
import config
from config import api
from common.query import Query
@@ -9,8 +9,8 @@ class RiskIQ(Query):
self.module = 'Intelligence'
self.source = 'RiskIQAPIQuery'
self.addr = 'https://api.passivetotal.org/v2/enrichment/subdomains'
self.user = config.riskiq_api_username
self.key = config.riskiq_api_key
self.user = api.riskiq_api_username
self.key = api.riskiq_api_key
def query(self):
"""
@@ -24,11 +24,9 @@ class RiskIQ(Query):
auth=(self.user, self.key))
if not resp:
return
resp_json = resp.json()
subnames = resp_json.get('subdomains')
if subnames:
self.subdomains = set(map(lambda sub: f'{sub}.{self.domain}',
subnames))
data = resp.json()
names = data.get('subdomains')
self.subdomains = set(map(lambda sub: f'{sub}.{self.domain}', names))
def run(self):
"""

Some files were not shown because too many files have changed in this diff Show More