wg-metasploit_data_models 4.1.4.01 → 4.1.4.02

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (440) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.github/workflows/verify.yml +68 -0
  4. data/.gitignore +29 -0
  5. data/.rspec +3 -0
  6. data/.simplecov +38 -0
  7. data/.yardopts +4 -0
  8. data/CHANGELOG.md +6 -0
  9. data/CONTRIBUTING.md +133 -0
  10. data/Gemfile +46 -0
  11. data/LICENSE +27 -0
  12. data/README.md +65 -0
  13. data/RELEASING.md +82 -0
  14. data/Rakefile +72 -0
  15. data/UPGRADING.md +1 -0
  16. data/app/models/mdm/api_key.rb +61 -0
  17. data/app/models/mdm/async_callback.rb +64 -0
  18. data/app/models/mdm/client.rb +50 -0
  19. data/app/models/mdm/cred.rb +205 -0
  20. data/app/models/mdm/event.rb +83 -0
  21. data/app/models/mdm/exploit_attempt.rb +105 -0
  22. data/app/models/mdm/exploited_host.rb +42 -0
  23. data/app/models/mdm/host.rb +619 -0
  24. data/app/models/mdm/host_detail.rb +62 -0
  25. data/app/models/mdm/host_tag.rb +49 -0
  26. data/app/models/mdm/listener.rb +82 -0
  27. data/app/models/mdm/loot.rb +161 -0
  28. data/app/models/mdm/macro.rb +62 -0
  29. data/app/models/mdm/mod_ref.rb +24 -0
  30. data/app/models/mdm/module/action.rb +33 -0
  31. data/app/models/mdm/module/arch.rb +28 -0
  32. data/app/models/mdm/module/author.rb +34 -0
  33. data/app/models/mdm/module/detail.rb +388 -0
  34. data/app/models/mdm/module/mixin.rb +31 -0
  35. data/app/models/mdm/module/platform.rb +29 -0
  36. data/app/models/mdm/module/ref.rb +42 -0
  37. data/app/models/mdm/module/target.rb +37 -0
  38. data/app/models/mdm/nexpose_console.rb +121 -0
  39. data/app/models/mdm/note.rb +125 -0
  40. data/app/models/mdm/payload.rb +103 -0
  41. data/app/models/mdm/profile.rb +45 -0
  42. data/app/models/mdm/ref.rb +48 -0
  43. data/app/models/mdm/route.rb +28 -0
  44. data/app/models/mdm/service.rb +267 -0
  45. data/app/models/mdm/session.rb +203 -0
  46. data/app/models/mdm/session_event.rb +44 -0
  47. data/app/models/mdm/tag.rb +114 -0
  48. data/app/models/mdm/task.rb +168 -0
  49. data/app/models/mdm/task_cred.rb +45 -0
  50. data/app/models/mdm/task_host.rb +41 -0
  51. data/app/models/mdm/task_service.rb +41 -0
  52. data/app/models/mdm/task_session.rb +41 -0
  53. data/app/models/mdm/user.rb +230 -0
  54. data/app/models/mdm/vuln.rb +204 -0
  55. data/app/models/mdm/vuln_attempt.rb +76 -0
  56. data/app/models/mdm/vuln_detail.rb +156 -0
  57. data/app/models/mdm/vuln_ref.rb +21 -0
  58. data/app/models/mdm/web_form.rb +53 -0
  59. data/app/models/mdm/web_page.rb +92 -0
  60. data/app/models/mdm/web_site.rb +113 -0
  61. data/app/models/mdm/web_vuln.rb +193 -0
  62. data/app/models/mdm/wmap_request.rb +101 -0
  63. data/app/models/mdm/wmap_target.rb +56 -0
  64. data/app/models/mdm/workspace.rb +286 -0
  65. data/app/models/metasploit_data_models/automatic_exploitation/match.rb +43 -0
  66. data/app/models/metasploit_data_models/automatic_exploitation/match_result.rb +71 -0
  67. data/app/models/metasploit_data_models/automatic_exploitation/match_set.rb +40 -0
  68. data/app/models/metasploit_data_models/automatic_exploitation/run.rb +29 -0
  69. data/app/models/metasploit_data_models/ip_address/v4/cidr.rb +14 -0
  70. data/app/models/metasploit_data_models/ip_address/v4/nmap.rb +14 -0
  71. data/app/models/metasploit_data_models/ip_address/v4/range.rb +12 -0
  72. data/app/models/metasploit_data_models/ip_address/v4/segment/nmap/list.rb +125 -0
  73. data/app/models/metasploit_data_models/ip_address/v4/segment/nmap/range.rb +12 -0
  74. data/app/models/metasploit_data_models/ip_address/v4/segment/single.rb +123 -0
  75. data/app/models/metasploit_data_models/ip_address/v4/segmented.rb +200 -0
  76. data/app/models/metasploit_data_models/ip_address/v4/single.rb +53 -0
  77. data/app/models/metasploit_data_models/module_run.rb +213 -0
  78. data/app/models/metasploit_data_models/search/operation/ip_address.rb +60 -0
  79. data/app/models/metasploit_data_models/search/operation/port/number.rb +25 -0
  80. data/app/models/metasploit_data_models/search/operation/port/range.rb +79 -0
  81. data/app/models/metasploit_data_models/search/operation/range.rb +56 -0
  82. data/app/models/metasploit_data_models/search/operator/ip_address.rb +33 -0
  83. data/app/models/metasploit_data_models/search/operator/multitext.rb +73 -0
  84. data/app/models/metasploit_data_models/search/operator/port/list.rb +67 -0
  85. data/app/models/metasploit_data_models/search/visitor/attribute.rb +17 -0
  86. data/app/models/metasploit_data_models/search/visitor/includes.rb +47 -0
  87. data/app/models/metasploit_data_models/search/visitor/joins.rb +67 -0
  88. data/app/models/metasploit_data_models/search/visitor/method.rb +16 -0
  89. data/app/models/metasploit_data_models/search/visitor/relation.rb +91 -0
  90. data/app/models/metasploit_data_models/search/visitor/where.rb +128 -0
  91. data/config/initializers/arel_helper.rb +5 -0
  92. data/config/initializers/ipaddr.rb +29 -0
  93. data/config/locales/en.yml +94 -0
  94. data/console_db.yml +9 -0
  95. data/db/migrate/000_create_tables.rb +79 -0
  96. data/db/migrate/001_add_wmap_tables.rb +35 -0
  97. data/db/migrate/002_add_workspaces.rb +36 -0
  98. data/db/migrate/003_move_notes.rb +20 -0
  99. data/db/migrate/004_add_events_table.rb +16 -0
  100. data/db/migrate/005_expand_info.rb +58 -0
  101. data/db/migrate/006_add_timestamps.rb +26 -0
  102. data/db/migrate/007_add_loots.rb +20 -0
  103. data/db/migrate/008_create_users.rb +16 -0
  104. data/db/migrate/009_add_loots_ctype.rb +10 -0
  105. data/db/migrate/010_add_alert_fields.rb +16 -0
  106. data/db/migrate/011_add_reports.rb +19 -0
  107. data/db/migrate/012_add_tasks.rb +24 -0
  108. data/db/migrate/013_add_tasks_result.rb +10 -0
  109. data/db/migrate/014_add_loots_fields.rb +12 -0
  110. data/db/migrate/015_rename_user.rb +16 -0
  111. data/db/migrate/016_add_host_purpose.rb +10 -0
  112. data/db/migrate/017_expand_info2.rb +58 -0
  113. data/db/migrate/018_add_workspace_user_info.rb +29 -0
  114. data/db/migrate/019_add_workspace_desc.rb +23 -0
  115. data/db/migrate/020_add_user_preferences.rb +11 -0
  116. data/db/migrate/021_standardize_info_and_data.rb +18 -0
  117. data/db/migrate/022_enlarge_event_info.rb +10 -0
  118. data/db/migrate/023_add_report_downloaded_at.rb +10 -0
  119. data/db/migrate/024_convert_service_info_to_text.rb +12 -0
  120. data/db/migrate/025_add_user_admin.rb +19 -0
  121. data/db/migrate/026_add_creds_table.rb +19 -0
  122. data/db/migrate/20100819123300_migrate_cred_data.rb +154 -0
  123. data/db/migrate/20100824151500_add_exploited_table.rb +16 -0
  124. data/db/migrate/20100908001428_add_owner_to_workspaces.rb +9 -0
  125. data/db/migrate/20100911122000_add_report_templates.rb +18 -0
  126. data/db/migrate/20100916151530_require_admin_flag.rb +15 -0
  127. data/db/migrate/20100916175000_add_campaigns_and_templates.rb +61 -0
  128. data/db/migrate/20100920012100_add_generate_exe_column.rb +8 -0
  129. data/db/migrate/20100926214000_add_template_prefs.rb +11 -0
  130. data/db/migrate/20101001000000_add_web_tables.rb +57 -0
  131. data/db/migrate/20101002000000_add_query.rb +10 -0
  132. data/db/migrate/20101007000000_add_vuln_info.rb +15 -0
  133. data/db/migrate/20101008111800_add_clients_to_campaigns.rb +10 -0
  134. data/db/migrate/20101009023300_add_campaign_attachments.rb +15 -0
  135. data/db/migrate/20101104135100_add_imported_creds.rb +17 -0
  136. data/db/migrate/20101203000000_fix_web_tables.rb +34 -0
  137. data/db/migrate/20101203000001_expand_host_comment.rb +12 -0
  138. data/db/migrate/20101206212033_add_limit_to_network_to_workspaces.rb +9 -0
  139. data/db/migrate/20110112154300_add_module_uuid_to_tasks.rb +9 -0
  140. data/db/migrate/20110204112800_add_host_tags.rb +28 -0
  141. data/db/migrate/20110317144932_add_session_table.rb +110 -0
  142. data/db/migrate/20110414180600_add_local_id_to_session_table.rb +11 -0
  143. data/db/migrate/20110415175705_add_routes_table.rb +18 -0
  144. data/db/migrate/20110422000000_convert_binary.rb +73 -0
  145. data/db/migrate/20110425095900_add_last_seen_to_sessions.rb +8 -0
  146. data/db/migrate/20110513143900_track_successful_exploits.rb +31 -0
  147. data/db/migrate/20110517160800_rename_and_prune_nessus_vulns.rb +26 -0
  148. data/db/migrate/20110527000000_add_task_id_to_reports_table.rb +11 -0
  149. data/db/migrate/20110527000001_add_api_keys_table.rb +12 -0
  150. data/db/migrate/20110606000001_add_macros_table.rb +16 -0
  151. data/db/migrate/20110622000000_add_settings_to_tasks_table.rb +12 -0
  152. data/db/migrate/20110624000001_add_listeners_table.rb +19 -0
  153. data/db/migrate/20110625000001_add_macro_to_listeners_table.rb +12 -0
  154. data/db/migrate/20110630000001_add_nexpose_consoles_table.rb +21 -0
  155. data/db/migrate/20110630000002_add_name_to_nexpose_consoles_table.rb +12 -0
  156. data/db/migrate/20110717000001_add_profiles_table.rb +15 -0
  157. data/db/migrate/20110727163801_expand_cred_ptype_column.rb +9 -0
  158. data/db/migrate/20110730000001_add_initial_indexes.rb +85 -0
  159. data/db/migrate/20110812000001_prune_indexes.rb +23 -0
  160. data/db/migrate/20110922000000_expand_notes.rb +9 -0
  161. data/db/migrate/20110928101300_add_mod_ref_table.rb +17 -0
  162. data/db/migrate/20111011110000_add_display_name_to_reports_table.rb +24 -0
  163. data/db/migrate/20111203000000_inet_columns.rb +13 -0
  164. data/db/migrate/20111204000000_more_inet_columns.rb +17 -0
  165. data/db/migrate/20111210000000_add_scope_to_hosts.rb +9 -0
  166. data/db/migrate/20120126110000_add_virtual_host_to_hosts.rb +9 -0
  167. data/db/migrate/20120411173220_rename_workspace_members.rb +9 -0
  168. data/db/migrate/20120601152442_add_counter_caches_to_hosts.rb +21 -0
  169. data/db/migrate/20120625000000_add_vuln_details.rb +34 -0
  170. data/db/migrate/20120625000001_add_host_details.rb +16 -0
  171. data/db/migrate/20120625000002_expand_details.rb +16 -0
  172. data/db/migrate/20120625000003_expand_details2.rb +24 -0
  173. data/db/migrate/20120625000004_add_vuln_attempts.rb +19 -0
  174. data/db/migrate/20120625000005_add_vuln_and_host_counter_caches.rb +14 -0
  175. data/db/migrate/20120625000006_add_module_details.rb +118 -0
  176. data/db/migrate/20120625000007_add_exploit_attempts.rb +26 -0
  177. data/db/migrate/20120625000008_add_fail_message.rb +12 -0
  178. data/db/migrate/20120718202805_add_owner_and_payload_to_web_vulns.rb +13 -0
  179. data/db/migrate/20130228214900_change_required_columns_to_null_false_in_web_vulns.rb +19 -0
  180. data/db/migrate/20130412154159_change_foreign_key_in_module_actions.rb +25 -0
  181. data/db/migrate/20130412171844_change_foreign_key_in_module_archs.rb +25 -0
  182. data/db/migrate/20130412173121_change_foreign_key_in_module_authors.rb +25 -0
  183. data/db/migrate/20130412173640_change_foreign_key_in_module_mixins.rb +25 -0
  184. data/db/migrate/20130412174254_change_foreign_key_in_module_platforms.rb +25 -0
  185. data/db/migrate/20130412174719_change_foreign_key_in_module_refs.rb +25 -0
  186. data/db/migrate/20130412175040_change_foreign_key_in_module_targets.rb +25 -0
  187. data/db/migrate/20130423211152_add_creds_counter_cache.rb +24 -0
  188. data/db/migrate/20130430151353_change_required_columns_to_null_false_in_hosts.rb +11 -0
  189. data/db/migrate/20130430162145_enforce_address_uniqueness_in_workspace_in_hosts.rb +101 -0
  190. data/db/migrate/20130510021637_remove_campaigns.rb +11 -0
  191. data/db/migrate/20130515164311_change_web_vulns_confidence_to_integer.rb +48 -0
  192. data/db/migrate/20130515172727_valid_mdm_web_vuln_params.rb +30 -0
  193. data/db/migrate/20130516204810_making_vulns_refs_a_real_ar_model.rb +5 -0
  194. data/db/migrate/20130522001343_create_task_creds.rb +9 -0
  195. data/db/migrate/20130522032517_create_task_hosts.rb +9 -0
  196. data/db/migrate/20130522041110_create_task_services.rb +9 -0
  197. data/db/migrate/20130525015035_remove_campaign_id_from_clients.rb +9 -0
  198. data/db/migrate/20130525212420_drop_table_imported_creds.rb +14 -0
  199. data/db/migrate/20130531144949_making_host_tags_a_real_ar_model.rb +6 -0
  200. data/db/migrate/20130604145732_create_task_sessions.rb +9 -0
  201. data/db/migrate/20130717150737_remove_pname_validation.rb +7 -0
  202. data/db/migrate/20131002004641_create_automatic_exploitation_matches.rb +13 -0
  203. data/db/migrate/20131002164449_create_automatic_exploitation_match_sets.rb +12 -0
  204. data/db/migrate/20131008213344_create_automatic_exploitation_runs.rb +11 -0
  205. data/db/migrate/20131011184338_module_detail_on_automatic_exploitation_match.rb +10 -0
  206. data/db/migrate/20131017150735_create_automatic_exploitation_match_results.rb +11 -0
  207. data/db/migrate/20131021185657_make_match_polymorphic.rb +11 -0
  208. data/db/migrate/20140905031549_add_detected_arch_to_host.rb +5 -0
  209. data/db/migrate/20150112203945_remove_duplicate_services.rb +17 -0
  210. data/db/migrate/20150205192745_drop_service_uniqueness_index.rb +5 -0
  211. data/db/migrate/20150209195939_add_vuln_id_to_note.rb +6 -0
  212. data/db/migrate/20150212214222_remove_duplicate_services2.rb +17 -0
  213. data/db/migrate/20150219173821_create_module_runs.rb +23 -0
  214. data/db/migrate/20150219215039_add_module_run_to_session.rb +8 -0
  215. data/db/migrate/20150226151459_add_module_run_fk_to_loot.rb +8 -0
  216. data/db/migrate/20150312155312_add_module_full_name_to_match.rb +6 -0
  217. data/db/migrate/20150317145455_rename_module_indices.rb +29 -0
  218. data/db/migrate/20150326183742_add_missing_ae_indices.rb +13 -0
  219. data/db/migrate/20150421211719_rename_automatic_exploitation_index.rb +16 -0
  220. data/db/migrate/20150514182921_add_origin_to_mdm_vuln.rb +13 -0
  221. data/db/migrate/20160415153312_remove_not_null_from_web_vuln_p_arams.rb +5 -0
  222. data/db/migrate/20161004165612_add_fingerprinted_to_workspace.rb +5 -0
  223. data/db/migrate/20161227212223_add_os_family_to_hosts.rb +5 -0
  224. data/db/migrate/20180904120211_create_payloads.rb +21 -0
  225. data/db/migrate/20190308134512_create_async_callbacks.rb +13 -0
  226. data/db/migrate/20190507120211_remove_payload_workspaces.rb +5 -0
  227. data/lib/mdm/host/operating_system_normalization.rb +942 -0
  228. data/lib/mdm/module.rb +13 -0
  229. data/lib/mdm.rb +57 -0
  230. data/lib/metasploit_data_models/automatic_exploitation.rb +25 -0
  231. data/lib/metasploit_data_models/base64_serializer.rb +99 -0
  232. data/lib/metasploit_data_models/change_required_columns_to_null_false.rb +21 -0
  233. data/lib/metasploit_data_models/engine.rb +32 -0
  234. data/lib/metasploit_data_models/ip_address/cidr.rb +174 -0
  235. data/lib/metasploit_data_models/ip_address/range.rb +181 -0
  236. data/lib/metasploit_data_models/ip_address/v4/segment/nmap.rb +7 -0
  237. data/lib/metasploit_data_models/ip_address/v4/segment.rb +7 -0
  238. data/lib/metasploit_data_models/ip_address/v4.rb +11 -0
  239. data/lib/metasploit_data_models/ip_address.rb +9 -0
  240. data/lib/metasploit_data_models/match/child.rb +48 -0
  241. data/lib/metasploit_data_models/match/parent.rb +103 -0
  242. data/lib/metasploit_data_models/match.rb +8 -0
  243. data/lib/metasploit_data_models/search/operation/port.rb +9 -0
  244. data/lib/metasploit_data_models/search/operation.rb +9 -0
  245. data/lib/metasploit_data_models/search/operator/port.rb +6 -0
  246. data/lib/metasploit_data_models/search/operator.rb +8 -0
  247. data/lib/metasploit_data_models/search/visitor.rb +11 -0
  248. data/lib/metasploit_data_models/search.rb +8 -0
  249. data/lib/metasploit_data_models/serialized_prefs.rb +27 -0
  250. data/lib/metasploit_data_models/version.rb +13 -0
  251. data/lib/metasploit_data_models.rb +56 -0
  252. data/metasploit_data_models.gemspec +65 -0
  253. data/script/rails +8 -0
  254. data/spec/app/models/mdm/api_key_spec.rb +3 -0
  255. data/spec/app/models/mdm/client_spec.rb +43 -0
  256. data/spec/app/models/mdm/cred_spec.rb +346 -0
  257. data/spec/app/models/mdm/event_spec.rb +90 -0
  258. data/spec/app/models/mdm/exploit_attempt_spec.rb +59 -0
  259. data/spec/app/models/mdm/exploited_host_spec.rb +44 -0
  260. data/spec/app/models/mdm/host_detail_spec.rb +48 -0
  261. data/spec/app/models/mdm/host_spec.rb +1139 -0
  262. data/spec/app/models/mdm/host_tag_spec.rb +69 -0
  263. data/spec/app/models/mdm/listener_spec.rb +107 -0
  264. data/spec/app/models/mdm/loot_spec.rb +84 -0
  265. data/spec/app/models/mdm/macro_spec.rb +3 -0
  266. data/spec/app/models/mdm/mod_ref_spec.rb +3 -0
  267. data/spec/app/models/mdm/module/action_spec.rb +34 -0
  268. data/spec/app/models/mdm/module/arch_spec.rb +34 -0
  269. data/spec/app/models/mdm/module/author_spec.rb +52 -0
  270. data/spec/app/models/mdm/module/detail_spec.rb +746 -0
  271. data/spec/app/models/mdm/module/mixin_spec.rb +34 -0
  272. data/spec/app/models/mdm/module/platform_spec.rb +34 -0
  273. data/spec/app/models/mdm/module/ref_spec.rb +58 -0
  274. data/spec/app/models/mdm/module/target_spec.rb +36 -0
  275. data/spec/app/models/mdm/nexpose_console_spec.rb +146 -0
  276. data/spec/app/models/mdm/note_spec.rb +91 -0
  277. data/spec/app/models/mdm/profile_spec.rb +3 -0
  278. data/spec/app/models/mdm/ref_spec.rb +71 -0
  279. data/spec/app/models/mdm/route_spec.rb +35 -0
  280. data/spec/app/models/mdm/service_spec.rb +232 -0
  281. data/spec/app/models/mdm/session_event_spec.rb +42 -0
  282. data/spec/app/models/mdm/session_spec.rb +118 -0
  283. data/spec/app/models/mdm/tag_spec.rb +116 -0
  284. data/spec/app/models/mdm/task_cred_spec.rb +51 -0
  285. data/spec/app/models/mdm/task_host_spec.rb +50 -0
  286. data/spec/app/models/mdm/task_service_spec.rb +50 -0
  287. data/spec/app/models/mdm/task_session_spec.rb +46 -0
  288. data/spec/app/models/mdm/task_spec.rb +71 -0
  289. data/spec/app/models/mdm/user_spec.rb +50 -0
  290. data/spec/app/models/mdm/vuln_attempt_spec.rb +53 -0
  291. data/spec/app/models/mdm/vuln_detail_spec.rb +65 -0
  292. data/spec/app/models/mdm/vuln_ref_spec.rb +46 -0
  293. data/spec/app/models/mdm/vuln_spec.rb +299 -0
  294. data/spec/app/models/mdm/web_form_spec.rb +46 -0
  295. data/spec/app/models/mdm/web_page_spec.rb +101 -0
  296. data/spec/app/models/mdm/web_site_spec.rb +85 -0
  297. data/spec/app/models/mdm/web_vuln_spec.rb +312 -0
  298. data/spec/app/models/mdm/wmap_request_spec.rb +5 -0
  299. data/spec/app/models/mdm/wmap_target_spec.rb +5 -0
  300. data/spec/app/models/mdm/workspace_spec.rb +500 -0
  301. data/spec/app/models/metasploit_data_models/automatic_exploitation/match_result_spec.rb +86 -0
  302. data/spec/app/models/metasploit_data_models/automatic_exploitation/match_set_spec.rb +46 -0
  303. data/spec/app/models/metasploit_data_models/automatic_exploitation/match_spec.rb +37 -0
  304. data/spec/app/models/metasploit_data_models/automatic_exploitation/run_spec.rb +38 -0
  305. data/spec/app/models/metasploit_data_models/ip_address/v4/cidr_spec.rb +119 -0
  306. data/spec/app/models/metasploit_data_models/ip_address/v4/nmap_spec.rb +149 -0
  307. data/spec/app/models/metasploit_data_models/ip_address/v4/range_spec.rb +298 -0
  308. data/spec/app/models/metasploit_data_models/ip_address/v4/segment/nmap/list_spec.rb +276 -0
  309. data/spec/app/models/metasploit_data_models/ip_address/v4/segment/nmap/range_spec.rb +302 -0
  310. data/spec/app/models/metasploit_data_models/ip_address/v4/segment/segmented_spec.rb +27 -0
  311. data/spec/app/models/metasploit_data_models/ip_address/v4/segment/single_spec.rb +324 -0
  312. data/spec/app/models/metasploit_data_models/ip_address/v4/single_spec.rb +181 -0
  313. data/spec/app/models/metasploit_data_models/module_run_spec.rb +134 -0
  314. data/spec/app/models/metasploit_data_models/search/operation/ip_address_spec.rb +180 -0
  315. data/spec/app/models/metasploit_data_models/search/operation/port/number_spec.rb +39 -0
  316. data/spec/app/models/metasploit_data_models/search/operation/port/range_spec.rb +138 -0
  317. data/spec/app/models/metasploit_data_models/search/operation/range_spec.rb +233 -0
  318. data/spec/app/models/metasploit_data_models/search/operator/ip_address_spec.rb +17 -0
  319. data/spec/app/models/metasploit_data_models/search/operator/multitext_spec.rb +160 -0
  320. data/spec/app/models/metasploit_data_models/search/operator/port/list_spec.rb +162 -0
  321. data/spec/app/models/metasploit_data_models/search/visitor/attribute_spec.rb +96 -0
  322. data/spec/app/models/metasploit_data_models/search/visitor/includes_spec.rb +175 -0
  323. data/spec/app/models/metasploit_data_models/search/visitor/joins_spec.rb +396 -0
  324. data/spec/app/models/metasploit_data_models/search/visitor/method_spec.rb +49 -0
  325. data/spec/app/models/metasploit_data_models/search/visitor/relation_spec.rb +925 -0
  326. data/spec/app/models/metasploit_data_models/search/visitor/where_spec.rb +187 -0
  327. data/spec/dummy/Rakefile +7 -0
  328. data/spec/dummy/app/assets/config/manifest.js +1 -0
  329. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  330. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  331. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  332. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  333. data/spec/dummy/app/mailers/.gitkeep +0 -0
  334. data/spec/dummy/app/models/.gitkeep +0 -0
  335. data/spec/dummy/app/models/application_record.rb +3 -0
  336. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  337. data/spec/dummy/bin/bundle +3 -0
  338. data/spec/dummy/bin/rails +4 -0
  339. data/spec/dummy/bin/rake +4 -0
  340. data/spec/dummy/config/application.rb +61 -0
  341. data/spec/dummy/config/boot.rb +4 -0
  342. data/spec/dummy/config/database.yml.example +22 -0
  343. data/spec/dummy/config/database.yml.github_actions +21 -0
  344. data/spec/dummy/config/environment.rb +5 -0
  345. data/spec/dummy/config/environments/development.rb +37 -0
  346. data/spec/dummy/config/environments/production.rb +78 -0
  347. data/spec/dummy/config/environments/test.rb +39 -0
  348. data/spec/dummy/config/initializers/active_record_migrations.rb +4 -0
  349. data/spec/dummy/config/initializers/assets.rb +8 -0
  350. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  351. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  352. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  353. data/spec/dummy/config/initializers/inflections.rb +16 -0
  354. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  355. data/spec/dummy/config/initializers/session_store.rb +3 -0
  356. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  357. data/spec/dummy/config/locales/en.yml +23 -0
  358. data/spec/dummy/config/routes.rb +2 -0
  359. data/spec/dummy/config.ru +4 -0
  360. data/spec/dummy/db/structure.sql +3430 -0
  361. data/spec/dummy/db/structure.sql.from_rails_3 +3403 -0
  362. data/spec/dummy/lib/assets/.gitkeep +0 -0
  363. data/spec/dummy/log/.gitkeep +0 -0
  364. data/spec/dummy/public/404.html +26 -0
  365. data/spec/dummy/public/422.html +26 -0
  366. data/spec/dummy/public/500.html +25 -0
  367. data/spec/dummy/public/favicon.ico +0 -0
  368. data/spec/dummy/script/rails +6 -0
  369. data/spec/factories/mdm/addresses.rb +12 -0
  370. data/spec/factories/mdm/clients.rb +8 -0
  371. data/spec/factories/mdm/creds.rb +17 -0
  372. data/spec/factories/mdm/events.rb +15 -0
  373. data/spec/factories/mdm/exploit_attempts.rb +8 -0
  374. data/spec/factories/mdm/exploited_hosts.rb +7 -0
  375. data/spec/factories/mdm/fingerprints/nessus_fingerprints.rb +6 -0
  376. data/spec/factories/mdm/fingerprints/nexpose_fingerprints.rb +6 -0
  377. data/spec/factories/mdm/fingerprints/nmap_fingerprints.rb +6 -0
  378. data/spec/factories/mdm/fingerprints/retina_fingerprints.rb +6 -0
  379. data/spec/factories/mdm/fingerprints/session_fingerprints.rb +6 -0
  380. data/spec/factories/mdm/host_details.rb +8 -0
  381. data/spec/factories/mdm/host_tags.rb +9 -0
  382. data/spec/factories/mdm/hosts.rb +85 -0
  383. data/spec/factories/mdm/listeners.rb +12 -0
  384. data/spec/factories/mdm/loots.rb +11 -0
  385. data/spec/factories/mdm/module/actions.rb +14 -0
  386. data/spec/factories/mdm/module/archs.rb +14 -0
  387. data/spec/factories/mdm/module/authors.rb +22 -0
  388. data/spec/factories/mdm/module/details.rb +73 -0
  389. data/spec/factories/mdm/module/mixins.rb +14 -0
  390. data/spec/factories/mdm/module/platforms.rb +14 -0
  391. data/spec/factories/mdm/module/refs.rb +14 -0
  392. data/spec/factories/mdm/module/targets.rb +19 -0
  393. data/spec/factories/mdm/nexpose_consoles.rb +15 -0
  394. data/spec/factories/mdm/notes.rb +12 -0
  395. data/spec/factories/mdm/refs.rb +9 -0
  396. data/spec/factories/mdm/routes.rb +36 -0
  397. data/spec/factories/mdm/services.rb +41 -0
  398. data/spec/factories/mdm/session_events.rb +8 -0
  399. data/spec/factories/mdm/sessions.rb +13 -0
  400. data/spec/factories/mdm/tags.rb +14 -0
  401. data/spec/factories/mdm/task.rb +16 -0
  402. data/spec/factories/mdm/task_creds.rb +9 -0
  403. data/spec/factories/mdm/task_hosts.rb +9 -0
  404. data/spec/factories/mdm/task_services.rb +8 -0
  405. data/spec/factories/mdm/task_sessions.rb +8 -0
  406. data/spec/factories/mdm/users.rb +22 -0
  407. data/spec/factories/mdm/vuln_attempts.rb +8 -0
  408. data/spec/factories/mdm/vuln_details.rb +8 -0
  409. data/spec/factories/mdm/vuln_refs.rb +4 -0
  410. data/spec/factories/mdm/vulns.rb +20 -0
  411. data/spec/factories/mdm/web_forms.rb +33 -0
  412. data/spec/factories/mdm/web_pages.rb +64 -0
  413. data/spec/factories/mdm/web_sites.rb +8 -0
  414. data/spec/factories/mdm/web_vulns.rb +64 -0
  415. data/spec/factories/mdm/workspaces.rb +23 -0
  416. data/spec/factories/metasploit_data_models/automatic_exploitation/match_results.rb +7 -0
  417. data/spec/factories/metasploit_data_models/automatic_exploitation/match_sets.rb +8 -0
  418. data/spec/factories/metasploit_data_models/automatic_exploitation/matches.rb +7 -0
  419. data/spec/factories/metasploit_data_models/automatic_exploitation/runs.rb +6 -0
  420. data/spec/factories/module_runs.rb +40 -0
  421. data/spec/lib/base64_serializer_spec.rb +172 -0
  422. data/spec/lib/ipaddr_spec.rb +29 -0
  423. data/spec/lib/metasploit_data_models/ip_address/cidr_spec.rb +356 -0
  424. data/spec/lib/metasploit_data_models/ip_address/range_spec.rb +75 -0
  425. data/spec/lib/metasploit_data_models/match/child_spec.rb +59 -0
  426. data/spec/lib/metasploit_data_models/match/parent_spec.rb +153 -0
  427. data/spec/lib/metasploit_data_models_spec.rb +13 -0
  428. data/spec/spec_helper.rb +148 -0
  429. data/spec/support/matchers/match_regex_exactly.rb +28 -0
  430. data/spec/support/shared/contexts/rex/text.rb +15 -0
  431. data/spec/support/shared/examples/coerces_inet_column_type_to_string.rb +15 -0
  432. data/spec/support/shared/examples/mdm/module/detail/does_not_support_stance_with_mtype.rb +20 -0
  433. data/spec/support/shared/examples/mdm/module/detail/supports_stance_with_mtype.rb +36 -0
  434. data/spec/support/shared/examples/metasploit_data_models/search/operation/ipaddress/match.rb +109 -0
  435. data/spec/support/shared/examples/metasploit_data_models/search/visitor/includes/visit/with_children.rb +38 -0
  436. data/spec/support/shared/examples/metasploit_data_models/search/visitor/includes/visit/with_metasploit_model_search_operation_base.rb +26 -0
  437. data/spec/support/shared/examples/metasploit_data_models/search/visitor/relation/visit/matching_record.rb +50 -0
  438. data/spec/support/shared/examples/metasploit_data_models/search/visitor/where/visit/with_equality.rb +34 -0
  439. data/spec/support/shared/examples/metasploit_data_models/search/visitor/where/visit/with_metasploit_model_search_group_base.rb +51 -0
  440. metadata +444 -6
@@ -0,0 +1,942 @@
1
+ #
2
+ # Leverage the Recog gem as much as possible for sane fingerprint management
3
+ #
4
+ require 'recog'
5
+
6
+ #
7
+ # Rules for operating system fingerprinting in Metasploit
8
+ #
9
+ # The `os.product` key identifies the common-name of a specific operating system
10
+ # Examples include: Linux, Windows XP, Mac OS X, IOS, AIX, HP-UX, VxWorks
11
+ #
12
+ # The `os.version` key identifies the service pack or version of the operating system
13
+ # Sometimes this means a kernel or firmware version when the distribution or OS
14
+ # version is not available.
15
+ # Examples include: SP2, 10.04, 2.6.47, 10.6.1
16
+ #
17
+ # The `os.vendor` key identifies the manufacturer of the operating system
18
+ # Examples include: Microsoft, Ubuntu, Cisco, HP, IBM, Wind River
19
+ #
20
+ # The `os.family` key identifies the group of the operating system. This is often a
21
+ # duplicate of os.product, unless a more specific product name is available.
22
+ # Examples include: Windows, Linux, IOS, HP-UX, AIX
23
+ #
24
+ # The `os.edition` key identifies the specific variant of the operating system
25
+ # Examples include: Enterprise, Professional, Starter, Evaluation, Home, Datacenter
26
+ #
27
+ # An example breakdown of a common operating system is shown below
28
+ #
29
+ # * Microsoft Windows XP Professional Service Pack 3 English (x86)
30
+ # - os.product = 'Windows XP'
31
+ # - os.edition = 'Professional'
32
+ # - os.vendor = 'Microsoft'
33
+ # - os.version = 'SP3'
34
+ # - os.language = 'English'
35
+ # - os.arch = 'x86'
36
+ #
37
+ # These rules are then mapped to the {Mdm::Host} attributes below:
38
+ #
39
+ # * os_name - Maps to a normalized os.product key
40
+ # * os_flavor - Maps to a normalized os.edition key
41
+ # * os_sp - Maps to a normalized os.version key (soon os_version)
42
+ # * os_lang - Maps to a normalized os.language key
43
+ # * arch - Maps to a normalized os.arch key
44
+ #
45
+ # Additional rules include the following mappings:
46
+ #
47
+ # * name - Maps to the host.name key
48
+ # * mac - Maps to the host.mac key
49
+ #
50
+ # The following keys are not mapped to {Mdm::Host} at this time (but should be):
51
+ #
52
+ # * os.vendor
53
+ #
54
+ # In order to execute these rules, this module is responsible for mapping various
55
+ # fingerprint sources to {Mdm::Host} values. This requires some ugly glue code to
56
+ # account for differences between each supported input (external scanners), the
57
+ # Recog gem and associated databases, and how Metasploit itself likes to handle
58
+ # these values. Getting a mapping wrong is often harmless, but can impact the
59
+ # automatic targetting capabilities of certain exploit modules.
60
+ #
61
+ # In other words, this is a best-effort attempt to rationalize multiple competing
62
+ # sources of information about a host and come up with the values representing a
63
+ # normalized assessment of the system. The use of `Recog` and multiple scanner
64
+ # fingerprints can result in a comprehensive (and confident) identification of the
65
+ # remote operating system and associated services.
66
+ #
67
+ # Historically, there are direct conflicts between certain Metasploit modules,
68
+ # certain scanners, and external fingerprint databases in terms of how a
69
+ # particular OS and patch level is represented. This module attempts to fix what
70
+ # it can and serve as documentation and live workarounds for the rest.
71
+ #
72
+ # Examples of known conflicts that are still in progress:
73
+ #
74
+ # * Metasploit defines an OS constant of 'win'/'windows' as Microsoft Windows
75
+ #
76
+ # - Scanner modules report a mix of 'Microsoft Windows' and 'Windows'
77
+ # - Nearly all exploit modules reference 'Windows <Release> SP<Version>'
78
+ # - Nmap (and other scanners) also prefix the vendor before Windows
79
+ #
80
+ #
81
+ # * Windows service packs represented as 'Service Pack X' or 'SPX'
82
+ #
83
+ # - The preferred form is to set os.version to 'SPX'
84
+ # - Many external scanners & Recog prefer 'Service Pack X'
85
+ #
86
+ # * Apple Mac OS X, Cisco IOS, IBM AIX, Ubuntu Linux, all reported with vendor prefix
87
+ #
88
+ # - The preferred form is to remove the vendor from os.product
89
+ # - {Mdm::Host} currently has no vendor field, so this information is lost today
90
+ # - Many scanners report leading vendor strings and require normalization
91
+ #
92
+ # * The os_flavor field is used in contradictory ways across Metasploit
93
+ #
94
+ # - The preferred form is to be a 'display only' field
95
+ # - Some Recog fingerprints still append the edition to os.product
96
+ # - Many scanners report the edition as a trailing suffix to os.product
97
+ #
98
+ #
99
+ #
100
+ #
101
+ # Maintenance:
102
+ #
103
+ # 1. Ensure that the latest Recog gem is present and installed
104
+ # 2. For new operating system releases, update relevant sections
105
+ # a) Windows releases will require updates to a few methods
106
+ # 1) parse_windows_os_str()
107
+ # 2) normalize_nmap_fingerprint()
108
+ # 3) normalize_nexpose_fingerprint()
109
+ # 4) Other scanner normalizers
110
+ # b) Mobile operating systems are minimally recognized
111
+ #
112
+ #
113
+ # @todo Handle OS icon incompatiblities with new fingerprint names
114
+ # Note that VMWare ESX(i) was special cased before as well, make sure it still works
115
+ # 1) Cisco IOS -> IOS breaks the icon mapping in MSP/MSCE of /cisco/
116
+ # 2) Ubuntu Linux -> Linux breaks the distro selection
117
+ # The real solution is to add os_vendor and take this into account for icons
118
+ #
119
+ # @todo Implement rspec coverage for normalize_os()
120
+ # @todo Implement smb.generic fingerprint database (replace {#parse_windows_os_str}?)
121
+ # @todo Implement Samba version matching for specific distributions and OS versions
122
+ # @todo Implement DD-WRT and various embedded device signatures currently missing
123
+ # @todo Correct inconsistencies in os_name use by removing the vendor string (Microsoft Windows -> Windows)
124
+ # This applies to MSF core and a handful of modules, not to mention some Recog fingerprints.
125
+ # @todo Rename host.os_sp to host.os_version
126
+ # @todo Add host.os_vendor
127
+ # @todo Add host.os_confidence
128
+ # @todo Add host.domain
129
+ #
130
+ module Mdm::Host::OperatingSystemNormalization
131
+
132
+ # Cap nmap certainty at 0.84 until we update it more frequently
133
+ # XXX: Without this, Nmap will beat the default certainty of recog
134
+ # matches and its less-confident guesses will take precedence
135
+ # over service-based fingerprints.
136
+ MAX_NMAP_CERTAINTY = 0.84
137
+
138
+ #
139
+ # Normalize the operating system fingerprints provided by various scanners
140
+ # (nmap, nexpose, retina, nessus, metasploit modules, and more!)
141
+ #
142
+ # These are stored as {Mdm::Note notes} (instead of directly in the os_*
143
+ # fields) specifically for this purpose.
144
+ #
145
+ # The goal is to infer as much as we can about the OS of the device and the
146
+ # various {Mdm::Service services} offered using the Recog gem and some glue
147
+ # logic to determine the best weights. This method can result in changes to
148
+ # the recorded {#os_name}, {#os_flavor}, {#os_sp}, {#os_lang}, {#purpose},
149
+ # {#name}, {#arch}, and the {Mdm::Service service details}.
150
+ #
151
+ def normalize_os
152
+ host = self
153
+ matches = []
154
+
155
+ # Note that we're already restricting the query to this host by using
156
+ # host.notes instead of Note, so don't need a host_id in the
157
+ # conditions.
158
+ fingerprintable_notes = self.notes.where("ntype like '%%fingerprint'")
159
+ fingerprintable_notes.each do |fp_note|
160
+ matches += recog_matches_for_note(fp_note)
161
+ end
162
+
163
+ # XXX: This hack solves the memory leak generated by self.services.each {}
164
+ fingerprintable_services = self.services.where("name is not null and name != '' and info is not null and info != ''")
165
+ fingerprintable_services.each do |s|
166
+ matches += recog_matches_for_service(s)
167
+ end
168
+
169
+ #
170
+ # Look for generic fingerprint.match notes that generate a match hash from modules
171
+ # This handles ad-hoc os.language, host.name, etc identifications
172
+ #
173
+ generated_matches = self.notes.where(ntype: 'fingerprint.match')
174
+ generated_matches.each do |m|
175
+ next unless (m.data and m.data.kind_of?(::Hash))
176
+ matches << m.data.dup
177
+ end
178
+
179
+ # Normalize matches for consistency during the ranking phase
180
+ matches = matches.map{ |m| normalize_match(m) }
181
+
182
+ # Calculate the best OS match based on fingerprint hits
183
+ match = Recog::Nizer.best_os_match(matches)
184
+
185
+ # Merge and normalize the best match to the host object
186
+ apply_match_to_host(match) if match
187
+
188
+ # Set some sane defaults if needed
189
+ host.os_name ||= 'Unknown'
190
+ host.purpose ||= 'device'
191
+
192
+ host.save if host.changed?
193
+ end
194
+
195
+ # Recog matches for the `s` service.
196
+ #
197
+ # @param s [Mdm::Service]
198
+ # @return [Array<Hash>] Keys will be host, service, and os attributes
199
+ def recog_matches_for_service(s)
200
+ #
201
+ # We assume that the service.info field contains certain types of probe
202
+ # replies and associate these with one or more Recog databases. The mapping
203
+ # of service.name to a specific database only fits into so many places and
204
+ # Mdm currently serves that role.
205
+ #
206
+
207
+ service_match_keys = {
208
+ # TODO: Implement smb.generic fingerprint database
209
+ # 'smb' => [ 'smb.generic' ], # Distinct from smb.fingerprint, use os.certainty to choose best match
210
+ # 'netbios' => [ 'smb.generic' ], # Distinct from smb.fingerprint, use os.certainty to choose best match
211
+
212
+ 'ssh' => [ 'ssh.banner' ], # Recog expects just the vendor string, not the protocol version
213
+ 'http' => [ 'http_header.server', 'apache_os'], # The 'Apache' fingerprints try to infer OS/distribution from the extra information in the Server header
214
+ 'https' => [ 'http_header.server', 'apache_os'], # XXX: verify vmware esx(i) case on https (TODO: normalize https to http, track SSL elsewhere, such as a new set of fields)
215
+ 'snmp' => [ 'snmp.sys_description' ],
216
+ 'telnet' => [ 'telnet.banner' ],
217
+ 'smtp' => [ 'smtp.banner' ],
218
+ 'imap' => [ 'imap4.banner' ], # Metasploit reports 143/993 as imap (TODO: normalize imap to imap4)
219
+ 'pop3' => [ 'pop3.banner' ], # Metasploit reports 110/995 as pop3
220
+ 'nntp' => [ 'nntp.banner' ],
221
+ 'ftp' => [ 'ftp.banner' ],
222
+ 'ssdp' => [ 'http_header.server' ]
223
+ }
224
+
225
+ matches = []
226
+
227
+ return matches unless service_match_keys.has_key?(s.name)
228
+
229
+ service_match_keys[s.name].each do |rdb|
230
+ banner = s.info
231
+ if self.respond_to?("service_banner_recog_filter_#{s.name}")
232
+ banner = self.send("service_banner_recog_filter_#{s.name}", banner)
233
+ end
234
+ res = Recog::Nizer.match(rdb, banner)
235
+ matches << res if res
236
+ end
237
+
238
+ matches
239
+ end
240
+
241
+ # Recog matches for the fingerprint in `note`.
242
+ #
243
+ # @return [Array<Hash>] Keys will be host, service, and os attributes
244
+ def recog_matches_for_note(note)
245
+ # Skip notes that are missing the correct structure or have been blacklisted
246
+ return [] if not validate_fingerprint_data(note)
247
+
248
+ #
249
+ # These rules define the relationship between fingerprint note keys
250
+ # and specific Recog databases for detailed matching. Notes that do
251
+ # not match a rule are passed to the generic matcher.
252
+ #
253
+ fingerprint_note_match_keys = {
254
+ 'smb.fingerprint' => {
255
+ :native_os => [ 'smb.native_os' ],
256
+ :native_lm => [ 'smb.native_lm' ],
257
+ },
258
+ 'http.fingerprint' => {
259
+ :header_server => [ 'http_header.server', 'apache_os' ],
260
+ :header_set_cookie => [ 'http_header.cookie' ],
261
+ :header_www_authenticate => [ 'http_header.wwwauth' ],
262
+ # TODO: Candidates for future Recog support
263
+ # :content => 'http_body'
264
+ # :code => 'http_response_code'
265
+ # :message => 'http_response_message'
266
+ }
267
+ }
268
+
269
+ matches = []
270
+
271
+ # Look for a specific Recog database for this type and data key
272
+ if fingerprint_note_match_keys.has_key?( note.ntype )
273
+ fingerprint_note_match_keys[ note.ntype ].each_pair do |k,rdbs|
274
+ if note.data.has_key?(k)
275
+ rdbs.each do |rdb|
276
+ res = Recog::Nizer.match(rdb, note.data[k])
277
+ matches << res if res
278
+ end
279
+ end
280
+ end
281
+ else
282
+ # Add all generic match results to the overall match array
283
+ normalize_scanner_fp(note).each do |m|
284
+ next unless m
285
+ matches << m
286
+ end
287
+ end
288
+
289
+ matches
290
+ end
291
+
292
+ # Determine if the fingerprint data is readable. If not, it nearly always
293
+ # means that there was a problem with the YAML or the Marshal'ed data,
294
+ # so let's log that for later investigation.
295
+ def validate_fingerprint_data(fp)
296
+ if fp.data.kind_of?(Hash) and !fp.data.empty?
297
+ return true
298
+ elsif fp.ntype == "postgresql.fingerprint"
299
+ # Special case postgresql.fingerprint; it's always a string,
300
+ # and should not be used for OS fingerprinting (yet), so
301
+ # don't bother logging it. TODO: fix os fingerprint finding, this
302
+ # name collision seems silly.
303
+ return false
304
+ else
305
+ return false
306
+ end
307
+ end
308
+
309
+ #
310
+ # Normalize matches in order to handle inconsistencies between fingerprint
311
+ # sources and our desired usage in Metasploit. This amounts to yet more
312
+ # duct tape, but the situation should improve as the fingerprint sources
313
+ # are updated and enhanced. In the future, this method will no longer
314
+ # be needed (or at least, doing less and less work)
315
+ #
316
+ def normalize_match(m)
317
+ # Normalize os.version strings containing 'Service Pack X' to just 'SPX'
318
+ if m['os.version'] and m['os.version'].index('Service Pack ') == 0
319
+ m['os.version'] = m['os.version'].gsub(/Service Pack /, 'SP')
320
+ end
321
+
322
+ if m['os.product']
323
+
324
+ # Normalize Apple Mac OS X to just Mac OS X
325
+ if m['os.product'] =~ /^Apple Mac/
326
+ m['os.product'] = m['os.product'].gsub(/Apple Mac/, 'Mac')
327
+ m['os.vendor'] ||= 'Apple'
328
+ end
329
+
330
+ # Normalize Sun Solaris/Sun SunOS to just Solaris/SunOS
331
+ if m['os.product'] =~ /^Sun (Solaris|SunOS)/
332
+ m['os.product'] = m['os.product'].gsub(/^Sun /, '')
333
+ m['os.vendor'] ||= 'Oracle'
334
+ end
335
+
336
+ # Normalize Microsoft Windows to just Windows to catch any stragglers
337
+ if m['os.product'] =~ /^Microsoft Windows/
338
+ m['os.product'] = m['os.product'].gsub(/Microsoft Windows/, 'Windows')
339
+ m['os.vendor'] ||= 'Microsoft'
340
+ end
341
+
342
+ # Normalize Windows Server to just Windows to match Metasploit target names
343
+ if m['os.product'] =~ /^Windows Server/
344
+ m['os.product'] = m['os.product'].gsub(/Windows Server/, 'Windows')
345
+ end
346
+
347
+ # Normalize OS Family
348
+ m = normalize_match_family(m)
349
+ end
350
+
351
+ m
352
+ end
353
+
354
+ # Normalize matches in order to ensure that an os.family entry exists
355
+ # if we have enough data to put one together.
356
+ def normalize_match_family(m)
357
+ # If the os.family already exists, we don't need to do anything
358
+ return m if m['os.family'].present?
359
+ case m['os.product']
360
+ when /Windows/
361
+ m['os.family'] = 'Windows'
362
+ when /Linux/
363
+ m['os.family'] = 'Linux'
364
+ when /Solaris/
365
+ m['os.family'] = 'Solaris'
366
+ when /SunOS/
367
+ m['os.family'] = 'SunOS'
368
+ when /AIX/
369
+ m['os.family'] = 'AIX'
370
+ when /HP-UX/
371
+ m['os.family'] = 'HP-UX'
372
+ when /OS X/
373
+ m['os.family'] = 'OS X'
374
+ end
375
+ m
376
+ end
377
+
378
+ #
379
+ # Recog assumes that the protocol version of the SSH banner has been removed
380
+ #
381
+ def service_banner_recog_filter_ssh(banner)
382
+ if banner =~ /^SSH-\d+\.\d+-(.*)/
383
+ $1
384
+ else
385
+ banner
386
+ end
387
+ end
388
+
389
+ #
390
+ # Examine the assertations of the merged best match and map these
391
+ # back to fields of {Mdm::Host}. Take particular care not to leave
392
+ # related fields (os_*) in a conflicting state, leverage existing
393
+ # values where possible, and use the most confident values we have.
394
+ #
395
+ def apply_match_to_host(match)
396
+ host = self
397
+
398
+ # These values in a match always override the current value unless
399
+ # the host attribute has been explicitly locked by the user
400
+
401
+ if match['host.mac'] && !host.attribute_locked?(:mac)
402
+ host.mac = sanitize(match['host.mac'])
403
+ end
404
+
405
+ if match['host.name'] && !host.attribute_locked?(:name)
406
+ host.name = sanitize(match['host.name'])
407
+ end
408
+
409
+ # Select the os architecture if available
410
+ if match['os.arch'] && !host.attribute_locked?(:arch)
411
+ host.arch = sanitize(match['os.arch'])
412
+ end
413
+
414
+ # Guess the purpose using some basic heuristics
415
+ if ! host.attribute_locked?(:purpose)
416
+ host.purpose = guess_purpose_from_match(match)
417
+ end
418
+
419
+ #
420
+ # Map match fields from Recog fingerprint style to Metasploit style
421
+ #
422
+
423
+ # os.build: Examples: 9001, 2600, 7602
424
+ # os.device: Examples: General, ADSL Modem, Broadband router, Cable Modem, Camera, Copier, CSU/DSU
425
+ # os.edition: Examples: Web, Storage, HPC, MultiPoint, Enterprise, Home, Starter, Professional
426
+ # os.family: Examples: Windows, Linux, Solaris, NetWare, ProCurve, Mac OS X, HP-UX, AIX
427
+ # os.product: Examples: Windows, Linux, Windows Server 2008 R2, Windows XP, Enterprise Linux, NEO Tape Library
428
+ # os.vendor: Examples: Microsoft, HP, IBM, Sun, 3Com, Ricoh, Novell, Ubuntu, Apple, Cisco, Xerox
429
+ # os.version: Examples: SP1, SP2, 6.5 SP3 CPR, 10.04, 8.04, 12.10, 4.0, 6.1, 8.5
430
+ # os.language: Examples: English, Arabic, German
431
+ # linux.kernel.version: Examples: 2.6.32
432
+
433
+ # Metasploit currently ignores os.build, os.device, and os.vendor as separate fields.
434
+
435
+ # Select the OS name from os.name, fall back to os.family
436
+ if ! host.attribute_locked?(:os_name)
437
+ # Try to fill this value from os.product first if it exists
438
+ if match.has_key?('os.product')
439
+ host.os_name = sanitize(match['os.product'])
440
+ else
441
+ # Fall back to os.family otherwise, if available
442
+ if match.has_key?('os.family')
443
+ host.os_name = sanitize(match['os.family'])
444
+ end
445
+ end
446
+ end
447
+
448
+ if match.has_key?('os.family')
449
+ host.os_family = sanitize(match['os.family'])
450
+ end
451
+
452
+ # Select the flavor from os.edition if available
453
+ if match.has_key?('os.edition') and ! host.attribute_locked?(:os_flavor)
454
+ host.os_flavor = sanitize(match['os.edition'])
455
+ end
456
+
457
+ # Select an OS version as os.version, fall back to linux.kernel.version
458
+ if ! host.attribute_locked?(:os_sp)
459
+ if match['os.version']
460
+ host.os_sp = sanitize(match['os.version'])
461
+ else
462
+ if match['linux.kernel.version']
463
+ host.os_sp = sanitize(match['linux.kernel.version'])
464
+ end
465
+ end
466
+ end
467
+
468
+ # Select the os language if available
469
+ if match.has_key?('os.language') and ! host.attribute_locked?(:os_lang)
470
+ host.os_lang = sanitize(match['os.language'])
471
+ end
472
+
473
+ # Normalize MAC addresses to lower-case colon-delimited format
474
+ if host.mac and ! host.attribute_locked?(:mac)
475
+ host.mac = host.mac.downcase
476
+ if host.mac =~ /^[a-f0-9]{12}$/
477
+ host.mac = host.mac.scan(/../).join(':')
478
+ end
479
+ end
480
+ end
481
+
482
+ #
483
+ # Loosely guess the purpose of a device based on available
484
+ # match values. In the future, also take into account the
485
+ # exposed services and rename to guess_purpose_with_match()
486
+ #
487
+ def guess_purpose_from_match(match)
488
+ # some data that is sent to this is numeric; we do not want that
489
+ pstr = ""
490
+ # Go through each character of each value and make sure it is all
491
+ # UTF-8
492
+ match.values.each do |i|
493
+ if i.respond_to?(:encoding)
494
+ i.each_char do |j|
495
+ begin
496
+ pstr << j.downcase.encode("UTF-8")
497
+ rescue Encoding::UndefinedConversionError
498
+ # rescue Encoding::UndefinedConversionError => e
499
+ # this works in Framework, but causes a Travis CI error
500
+ # elog("Found incompatible (non-ANSI) character in guess_purpose_from_match")
501
+ end
502
+ end
503
+ end
504
+ end
505
+ # Loosely map keywords to specific purposes
506
+ case pstr
507
+ when /windows server|windows (nt|20)/
508
+ 'server'
509
+ when /windows (xp|vista|[78]|10)/
510
+ 'client'
511
+ when /printer|print server/
512
+ 'printer'
513
+ when /router/
514
+ 'router'
515
+ when /firewall/
516
+ 'firewall'
517
+ when /linux/
518
+ 'server'
519
+ else
520
+ 'device'
521
+ end
522
+ end
523
+
524
+ # Ensure that the host attribute is using ascii safe text
525
+ # and escapes any other byte value.
526
+ def sanitize(text)
527
+ Rex::Text.ascii_safe_hex(text)
528
+ end
529
+
530
+ #
531
+ # Normalize data from Meterpreter's client.sys.config.sysinfo()
532
+ #
533
+ def normalize_session_fingerprint(data)
534
+ ret = {}
535
+ case data[:os]
536
+ when /Windows/
537
+ ret.update(parse_windows_os_str(data[:os]))
538
+ # Switch to this code block once the multi-meterpreter code review is complete
539
+ =begin
540
+
541
+ when /^(Windows \w+)\s*\(Build (\d+)(.*)\)/
542
+ ret['os.product'] = $1
543
+ ret['os.build'] = $2
544
+ ret['os.vendor'] = 'Microsoft'
545
+ possible_sp = $3
546
+ if possible_sp =~ /Service Pack (\d+)/
547
+ ret['os.version'] = 'SP' + $1
548
+ end
549
+ =end
550
+ when /Linux (\d+\.\d+\.\d+\S*)\s* \((\w*)\)/
551
+ ret['os.product'] = "Linux"
552
+ ret['os.version'] = $1
553
+ ret['os.arch'] = get_arch_from_string($2)
554
+ else
555
+ ret['os.product'] = data[:os]
556
+ end
557
+ ret['os.arch'] = data[:arch] if data[:arch]
558
+ ret['host.name'] = data[:name] if data[:name]
559
+ [ ret ]
560
+ end
561
+
562
+ #
563
+ # Normalize data from Nmap fingerprints
564
+ #
565
+ def normalize_nmap_fingerprint(data)
566
+ ret = {}
567
+
568
+ # :os_vendor=>"Microsoft" :os_family=>"Windows" :os_version=>"2000" :os_accuracy=>"94"
569
+ ret['os.certainty'] = ( data[:os_accuracy].to_f / 100.0 ).to_s if data[:os_accuracy]
570
+ if (data[:os_vendor] == data[:os_family])
571
+ ret['os.product'] = data[:os_family]
572
+ else
573
+ ret['os.product'] = data[:os_family]
574
+ ret['os.vendor'] = data[:os_vendor]
575
+ end
576
+
577
+ # Nmap places the type of Windows (XP, 7, etc) into the version field
578
+ if ret['os.product'] == 'Windows' and data[:os_version]
579
+ ret['os.product'] = ret['os.product'] + ' ' + data[:os_version].to_s
580
+ else
581
+ ret['os.version'] = data[:os_version]
582
+ end
583
+
584
+ ret['host.name'] = data[:hostname] if data[:hostname]
585
+
586
+ if ret['os.certainty']
587
+ ret['os.certainty'] = [ ret['os.certainty'].to_f, MAX_NMAP_CERTAINTY ].min.to_s
588
+ end
589
+
590
+ [ ret ]
591
+ end
592
+
593
+ #
594
+ # Normalize data from MBSA fingerprints
595
+ #
596
+ def normalize_mbsa_fingerprint(data)
597
+ ret = {}
598
+ # :os_match=>"Microsoft Windows Vista SP0 or SP1, Server 2008, or Windows 7 Ultimate (build 7000)"
599
+ # :os_vendor=>"Microsoft" :os_family=>"Windows" :os_version=>"7" :os_accuracy=>"100"
600
+ ret['os.certainty'] = ( data[:os_accuracy].to_f / 100.0 ).to_s if data[:os_accuracy]
601
+ ret['os.family'] = data[:os_family] if data[:os_family]
602
+ ret['os.vendor'] = data[:os_vendor] if data[:os_vendor]
603
+
604
+ if data[:os_family] and data[:os_version]
605
+ ret['os.product'] = data[:os_family] + " " + data[:os_version]
606
+ end
607
+
608
+ ret['host.name'] = data[:hostname] if data[:hostname]
609
+
610
+ [ ret ]
611
+ end
612
+
613
+
614
+ #
615
+ # Normalize data from Nexpose fingerprints
616
+ #
617
+ def normalize_nexpose_fingerprint(data)
618
+ ret = {}
619
+ # :family=>"Windows" :certainty=>"0.85" :vendor=>"Microsoft" :product=>"Windows 7 Ultimate Edition"
620
+ # :family=>"Windows" :certainty=>"0.67" :vendor=>"Microsoft" :arch=>"x86" :product=>'Windows 7' :version=>'SP1'
621
+ # :family=>"Linux" :certainty=>"0.64" :vendor=>"Linux" :product=>"Linux"
622
+ # :family=>"Linux" :certainty=>"0.80" :vendor=>"Ubuntu" :product=>"Linux"
623
+ # :family=>"IOS" :certainty=>"0.80" :vendor=>"Cisco" :product=>"IOS"
624
+ # :family=>"embedded" :certainty=>"0.61" :vendor=>"Linksys" :product=>"embedded"
625
+
626
+ ret['os.certainty'] = data[:certainty] if data[:certainty]
627
+ ret['os.family'] = data[:family] if data[:family]
628
+ ret['os.vendor'] = data[:vendor] if data[:vendor]
629
+
630
+ case data[:product]
631
+ when /^Windows/
632
+
633
+ # TODO: Verify Windows CE and Windows 8 RT fingerprints
634
+ # Translate the version into the representation we want
635
+
636
+ case data[:version].to_s
637
+
638
+ # These variants are normalized to just 'Windows <Version>'
639
+ when "NT", "2000", "95", "ME", "XP", "Vista", "7", "8", "8.1"
640
+ ret['os.product'] = "Windows #{data[:version]}"
641
+
642
+ # Service pack in the version field should be recognized
643
+ when /^SP\d+/, /^Service Pack \d+/
644
+ ret['os.product'] = data[:product]
645
+ ret['os.version'] = data[:version]
646
+
647
+ # No version means the version is part of the product already
648
+ when nil, ''
649
+ # Trim any 'Server' suffix and use as it is
650
+ ret['os.product'] = data[:product].sub(/ Server$/, '')
651
+
652
+ # Otherwise, we assume a Server version of Windows
653
+ else
654
+ ret['os.product'] = "Windows Server #{data[:version]}"
655
+ end
656
+
657
+ # Extract the edition string if it is present
658
+ if data[:product] =~ /(XP|Vista|\d+(?:\.\d+)) (\w+|\w+ \w+|\w+ \w+ \w+) Edition/
659
+ ret['os.edition'] = $2
660
+ end
661
+
662
+ when nil, 'embedded'
663
+ # Use the family or vendor name when the product is empty or 'embedded'
664
+ ret['os.product'] = data[:family] unless data[:family] == 'embedded'
665
+ ret['os.product'] ||= data[:vendor]
666
+ ret['os.version'] = data[:version] if data[:version]
667
+ else
668
+ # Default to using the product name reported by Nexpose
669
+ ret['os.product'] = data[:product] if data[:product]
670
+ end
671
+
672
+ ret['os.arch'] = get_arch_from_string(data[:arch]) if data[:arch]
673
+ ret['os.arch'] ||= get_arch_from_string(data[:desc]) if data[:desc]
674
+
675
+ [ ret ]
676
+ end
677
+
678
+
679
+ #
680
+ # Normalize data from Retina fingerprints
681
+ #
682
+ def normalize_retina_fingerprint(data)
683
+ ret = {}
684
+ # :os=>"Windows Server 2003 (X64), Service Pack 2"
685
+ case data[:os]
686
+ when /Windows/
687
+ ret.update(parse_windows_os_str(data[:os]))
688
+ else
689
+ # No idea what this looks like if it isn't windows. Just store
690
+ # the whole thing and hope for the best.
691
+ # TODO: Add examples of non-Windows results
692
+ ret['os.product'] = data[:os] if data[:os]
693
+ end
694
+ [ ret ]
695
+ end
696
+
697
+
698
+ #
699
+ # Normalize data from Nessus fingerprints
700
+ #
701
+ def normalize_nessus_fingerprint(data)
702
+ ret = {}
703
+ # :os=>"Microsoft Windows 2000 Advanced Server (English)"
704
+ # :os=>"Microsoft Windows 2000\nMicrosoft Windows XP"
705
+ # :os=>"Linux Kernel 2.6"
706
+ # :os=>"Sun Solaris 8"
707
+ # :os=>"IRIX 6.5"
708
+
709
+ # Nessus sometimes jams multiple OS names together with a newline.
710
+ oses = data[:os].split(/\n/)
711
+ if oses.length > 1
712
+ # Multiple fingerprints means Nessus wasn't really sure, reduce
713
+ # the certainty accordingly
714
+ ret['os.certainty'] = 0.5
715
+ else
716
+ ret['os.certainty'] = 0.8
717
+ end
718
+
719
+ # Since there is no confidence associated with them, the best we
720
+ # can do is just take the first one.
721
+ case oses.first
722
+ when /^(Microsoft |)Windows/
723
+ ret.update(parse_windows_os_str(data[:os]))
724
+
725
+ when /(2\.[46]\.\d+[-a-zA-Z0-9]+)/
726
+ # Look for older Linux kernel versions
727
+ ret['os.product'] = "Linux"
728
+ ret['os.version'] = $1
729
+
730
+ when /^Linux Kernel ([\d\.]+)(.*)/
731
+ # Look for strings like "Linux Kernel 2.6 on Ubuntu 9.10 (karmic)"
732
+ # Ex: Linux Kernel 2.2 on Red Hat Linux release 6.2 (Zoot)
733
+ # Ex: Linux Kernel 2.6 on Ubuntu Linux 8.04 (hardy)
734
+ ret['os.product'] = "Linux"
735
+ ret['os.version'] = $1
736
+
737
+ vendor = $2.to_s
738
+
739
+ # Try to snag the vendor name as well
740
+ if vendor =~ /on (\w+|\w+ \w+|\w+ \w+ \w+) (Linux|\d)/
741
+ ret['os.vendor'] = $1
742
+ end
743
+
744
+ when /(.*) ([0-9\.]+)$/
745
+ # Then we don't necessarily know what the os is, but this fingerprint has
746
+ # some version information at the end, pull it off, treat the first part
747
+ # as the OS, and the rest as the version.
748
+ ret['os.product'] = $1.gsub("Kernel", '').strip
749
+ ret['os.version'] = $2
750
+ else
751
+ # TODO: Return each OS guess as a separate match
752
+ ret['os.product'] = oses.first
753
+ end
754
+
755
+ ret['host.name'] = data[:hname] if data[:hname]
756
+ [ ret ]
757
+ end
758
+
759
+ #
760
+ # Normalize data from Qualys fingerprints
761
+ #
762
+ def normalize_qualys_fingerprint(data)
763
+ ret = {}
764
+ # :os=>"Microsoft Windows 2000"
765
+ # :os=>"Windows 2003"
766
+ # :os=>"Microsoft Windows XP Professional SP3"
767
+ # :os=>"Ubuntu Linux"
768
+ # :os=>"Cisco IOS 12.0(3)T3"
769
+ # :os=>"Red-Hat Linux 6.0"
770
+ case data[:os]
771
+ when /Windows/
772
+ ret.update(parse_windows_os_str(data[:os]))
773
+
774
+ when /^(Cisco) (IOS) (\d+[^\s]+)/
775
+ ret['os.product'] = $2
776
+ ret['os.vendor'] = $1
777
+ ret['os.version'] = $3
778
+
779
+ when /^([^\s]+) (Linux)(.*)/
780
+ ret['os.product'] = $2
781
+ ret['os.vendor'] = $1
782
+
783
+ ver = $3.to_s.strip.split(/\s+/).first
784
+ if ver =~ /^\d+\./
785
+ ret['os.version'] = ver
786
+ end
787
+
788
+ else
789
+ parts = data[:os].split(/\s+/, 3)
790
+ ret['os.product'] = "Unknown"
791
+ ret['os.product'] = parts[0] if parts[0]
792
+ ret['os.product'] << " " + parts[1] if parts[1]
793
+ ret['os.version'] = parts[2] if parts[2]
794
+ end
795
+ [ ret ]
796
+ end
797
+
798
+ #
799
+ # Normalize data from FusionVM fingerprints
800
+ #
801
+ def normalize_fusionvm_fingerprint(data)
802
+ ret = {}
803
+ case data[:os]
804
+ when /Windows/
805
+ ret.update(parse_windows_os_str(data[:os]))
806
+ when /Linux ([^[:space:]]*) ([^[:space:]]*) .* (\(.*\))/
807
+ ret['os.product'] = "Linux"
808
+ ret['host.name'] = $1
809
+ ret['os.version'] = $2
810
+ ret['os.arch'] = get_arch_from_string($3)
811
+ else
812
+ ret['os.product'] = data[:os]
813
+ end
814
+ ret['os.arch'] = data[:arch] if data[:arch]
815
+ ret['host.name'] = data[:name] if data[:name]
816
+ [ ret ]
817
+ end
818
+
819
+ #
820
+ # Normalize data from generic fingerprints
821
+ #
822
+ def normalize_generic_fingerprint(data)
823
+ ret = {}
824
+ ret['os.product'] = data[:os_name] || data[:os] || data[:os_fingerprint] || "Unknown"
825
+ ret['os.arch'] = data[:os_arch] if data[:os_arch]
826
+ ret['os.certainty'] = data[:os_certainty] || 0.5
827
+ [ ret ]
828
+ end
829
+
830
+ #
831
+ # Convert a host.os.*_fingerprint Note into a hash containing 'os.*' and 'host.*' fields
832
+ #
833
+ # Also includes a os.certainty which is a float from 0 - 1.00 indicating the
834
+ # scanner's confidence in its fingerprint. If the particular scanner does
835
+ # not provide such information, default to 0.80.
836
+ #
837
+ def normalize_scanner_fp(fp)
838
+ hits = []
839
+
840
+ return hits if not validate_fingerprint_data(fp)
841
+
842
+ case fp.ntype
843
+ when /^host\.os\.(.*_fingerprint)$/
844
+ pname = $1
845
+ pmeth = 'normalize_' + pname
846
+ if self.respond_to?(pmeth)
847
+ hits = self.send(pmeth, fp.data)
848
+ else
849
+ hits = normalize_generic_fingerprint(fp.data)
850
+ end
851
+ end
852
+ hits.each {|hit| hit['os.certainty'] ||= 0.80}
853
+ hits
854
+ end
855
+
856
+ #
857
+ # Take a windows version string and return a hash with fields suitable for
858
+ # Host this object's version fields. This is used as a fall-back to parse
859
+ # external fingerprints and should eventually be replaced by per-source
860
+ # mappings.
861
+ #
862
+ # A few example strings that this will have to parse:
863
+ # sessions
864
+ # Windows XP (Build 2600, Service Pack 3).
865
+ # Windows .NET Server (Build 3790).
866
+ # Windows 2008 (Build 6001, Service Pack 1).
867
+ # retina
868
+ # Windows Server 2003 (X64), Service Pack 2
869
+ # nessus
870
+ # Microsoft Windows 2000 Advanced Server (English)
871
+ # qualys
872
+ # Microsoft Windows XP Professional SP3
873
+ # Windows 2003
874
+ #
875
+ # Note that this list doesn't include nexpose or nmap, since they are
876
+ # both kind enough to give us the various strings in seperate pieces
877
+ # that we don't have to parse out manually.
878
+ #
879
+ def parse_windows_os_str(str)
880
+ ret = {}
881
+
882
+ # Set some reasonable defaults for Windows
883
+ ret['os.vendor'] = 'Microsoft'
884
+ ret['os.product'] = 'Windows'
885
+
886
+ # Determine the actual Windows product name
887
+ case str
888
+ when /\.NET Server/
889
+ ret['os.product'] << ' Server 2003'
890
+ when / (2000|2003|2008|2012)/
891
+ ret['os.product'] << ' Server ' + $1
892
+ when / (NT (?:3\.51|4\.0))/
893
+ ret['os.product'] << ' ' + $1
894
+ when /Windows (95|98|ME|XP|Vista|[\d\.]+)/
895
+ ret['os.product'] << ' ' + $1
896
+ else
897
+ # If we couldn't pull out anything specific for the flavor, just cut
898
+ # off the stuff we know for sure isn't it and hope for the best
899
+ ret['os.product'] = (ret['os.product'] + ' ' + str.gsub(/(Microsoft )|(Windows )|(Service Pack|SP) ?(\d+)/i, '').strip).strip
900
+
901
+ # Make sure the product name doesn't include any non-alphanumeric stuff
902
+ # This fixes cases where the above code leaves 'Windows XX (Build 3333,)...'
903
+ ret['os.product'] = ret['os.product'].split(/[^a-zA-Z0-9 ]/).first.strip
904
+
905
+ end
906
+
907
+ # Take a guess at the architecture
908
+ arch = get_arch_from_string(str)
909
+ ret['os.arch'] = arch if arch
910
+
911
+ # Extract any service pack value in the string
912
+ if str =~ /(Service Pack|SP) ?(\d+)/i
913
+ ret['os.version'] = "SP#{$2}"
914
+ end
915
+
916
+ # Extract any build ID found in the string
917
+ if str =~ /build (\d+)/i
918
+ ret['os.build'] = $1
919
+ end
920
+
921
+ # Extract the OS edition if available
922
+ if str =~ /(\d+|\d+\.\d+) (\w+|\w+ \w+|\w+ \w+ \w+) Edition/
923
+ ret['os.edition'] = $2
924
+ else
925
+ if str =~ /(Professional|Enterprise|Pro|Home|Start|Datacenter|Web|Storage|MultiPoint)/
926
+ ret['os.edition'] = $1
927
+ end
928
+ end
929
+
930
+ ret
931
+ end
932
+
933
+ #
934
+ # Return a normalized architecture based on patterns in the input string.
935
+ # This will identify things like sparc, powerpc, x86_x64, and i686
936
+ #
937
+ def get_arch_from_string(str)
938
+ res = Recog::Nizer.match("architecture", str)
939
+ return unless (res and res['os.arch'])
940
+ res['os.arch']
941
+ end
942
+ end