prommetrix.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-"
  3. """
  4. Prommetrix - Tool to obtain relevant information from the instances of 'Node Exporter' executed by 'Prometheus' - 2024 - by psy (https://03c8.net)
  5. ----------
  6. Prometheus is an open-source, metrics-based event monitoring and alerting solution for cloud applications. It is used by nearly 800 cloud-native organizations including Uber, Slack, Robinhood, and more. By scraping real-time metrics from various endpoints, Prometheus allows easy observation of a system’s state in addition to observation of hardware and software metrics such as memory usage, network usage and software-specific defined metrics (ex. number of failed login attempts to a web application).
  7. - https://prometheus.io/docs/guides/node-exporter/
  8. Since the numeric metrics captured by Prometheus are not considered sensitive data, Prometheus has held an understandable policy of avoiding built-in support for security features such as authentication and encryption, in order to focus on developing the monitoring-related features. This changed less than a year ago (Jan 2021), on the release of version 2.24.0 where Transport Layer Security (TLS) and basic authentication support were introduced.
  9. Due to the fact that authentication and encryption support is relatively new, many organizations that use Prometheus haven’t yet enabled these features and thus many Prometheus endpoints are completely exposed to the Internet (e.g. endpoints that run earlier versions), leaking metric and label data.
  10. ----------
  11. Dork (using default port):
  12. - inurl:":9100/metrics"
  13. ----------
  14. This vulnerabily can be described in a Pentest/Report like:
  15. - PRM-01-001 Client: Clients leak Metrics data through unprotected endpoint (LOW)
  16. "Metric data are to be collected for some services and these items need to implement a
  17. client-library that enables the core Prometheus service to scrape the data. The client-
  18. library opens a minimal HTTP server and exposes a route which is then registered with
  19. the core service for scraping. This endpoint is unauthenticated by default, which allows
  20. anybody who knows the URI to read the metric data. It is recommended to put some
  21. form of authentication in place. Only the core Prometheus service should be allowed to
  22. read the metric data."
  23. ----------
  24. Prommetrix - will take advantage of these metrics to obtain relevant information from the Prometheus instance, as well as, of the machine in which it is running.
  25. [!] The information obtained can be used to build other types of attacks over the different pieces of software/versions exposed (ex: CVE).
  26. ----------
  27. You should have received a copy of the GNU General Public License along
  28. with Prommetrix; if not, write to the Free Software Foundation, Inc., 51
  29. Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  30. """
  31. VERSION=str(0.2)
  32. import os, sys, requests, random, re
  33. def banner():
  34. print(r'''====================================================================
  35. ___ _ ___ __
  36. | _ \ _ __ ___ _ __ ___ _ __ ___ ___| |_ _ __(_) \/ /
  37. | |_) | '__/ _ \| '_ ` _ \| '_ ` _ \ / _ \ __| '__| |\ /
  38. | __/| | | (_) | | | | | | | | | | | __/ |_| | | |/ \
  39. |_| |_| \___/|_| |_| |_|_| |_| |_|\___|\__|_| |_/_/\_\
  40. (v'''+VERSION+''') by psy (https://03c8.net) | 2024
  41. Source Code:
  42. - Official: https://code.03c8.net/epsylon/prommetrix
  43. - Mirror: https://github.com/epsylon/prommetrix
  44. Usage:
  45. python3 prommetrix.py --target <IP> --port <PORT> (default: 9100)
  46. ====================================================================''')
  47. def init():
  48. if "--target" in sys.argv:
  49. print("")
  50. banner()
  51. user_agent_list = [
  52. 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36',
  53. 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1',
  54. 'Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)',
  55. 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Edg/87.0.664.75',
  56. 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18363',
  57. ]
  58. headers={"User-Agent": user_agent_list[random.randint(0, len(user_agent_list)-1)]}
  59. try:
  60. if "--port" in sys.argv:
  61. r = requests.get("http://"+sys.argv[2]+":"+sys.argv[4]+"/metrics", headers=headers)
  62. else:
  63. r = requests.get("http://"+sys.argv[2]+":9100"+"/metrics", headers=headers)
  64. except:
  65. print("")
  66. banner()
  67. sys.exit(2)
  68. if r.status_code != 200:
  69. print("\n[ERROR] Not any 'Prometheus' have been detected <-> ABORTING!\n")
  70. banner()
  71. sys.exit(2)
  72. else:
  73. with open('tmp.txt','w') as fd:
  74. metrics= re.sub(r'^#.*\n?', '', r.text, flags=re.MULTILINE)
  75. fd.write(metrics)
  76. r_text = open("tmp.txt", "r").read()
  77. print("\n[INFO] 'Prometheus' detected at: "+sys.argv[2]+" <-> EXPOSING!\n")
  78. print(" - Metrics path:")
  79. print(" - URL: "+r.url)
  80. print("\n - 'Go' (environment):")
  81. print(" - Version: "+r_text.split('go_info{version="')[1].split('"}')[0])
  82. node_exporter_build_branch = r_text.split('node_exporter_build_info{branch="')[1].split('"')[0]
  83. node_exporter_build_goversion = r_text.split('goversion="')[1].split('"')[0]
  84. node_exporter_build_revision = r_text.split('revision="')[1].split('"')[0]
  85. node_exporter_build_version = r_text.split('version="')[1].split('"')[0]
  86. try:
  87. node_dmi_bios_date = r_text.split('node_dmi_info{bios_date="')[1].split('"')[0]
  88. node_dmi_bios_release = r_text.split('bios_release="')[1].split('"')[0]
  89. node_dmi_bios_version = r_text.split('bios_version="')[1].split('"')[0]
  90. node_dmi_bios_vendor = r_text.split('bios_vendor="')[1].split('"')[0]
  91. system_flag = True
  92. bios_flag = True
  93. except:
  94. node_dmi_bios_date = None
  95. node_dmi_bios_release = None
  96. node_dmi_bios_version = None
  97. node_dmi_bios_vendor = None
  98. system_flag = False
  99. bios_flag = False
  100. try:
  101. node_os_build = r_text.split('node_os_info{build_id="')[1].split('",id')[0]
  102. node_os_id = r_text.split(',id="')[1].split('",id_like')[0]
  103. node_os_id_like = r_text.split('id_like="')[1].split('",image_id')[0]
  104. node_os_image_id = r_text.split('image_id="')[1].split('",image_version')[0]
  105. node_os_image_version = r_text.split('image_version="')[1].split('",name')[0]
  106. node_os_pretty_name = r_text.split('pretty_name="')[1].split('",variant')[0]
  107. node_os_variant = r_text.split('variant="')[1].split('",variant_id')[0]
  108. node_os_variant_id = r_text.split('variant_id="')[1].split('",version')[0]
  109. node_os_version_codename = r_text.split('version_codename="')[1].split('",version_id')[0]
  110. node_os_version_id = r_text.split('version_id="')[1].split('"}')[0]
  111. os_flag = True
  112. except:
  113. node_os_build = None
  114. node_os_id = None
  115. node_os_id_like = None
  116. node_os_image_id = None
  117. node_os_image_version = None
  118. node_os_name = None
  119. node_os_pretty_name = None
  120. node_os_variant = None
  121. node_os_variant_id = None
  122. node_os_version_codename = None
  123. node_os_version_id = None
  124. os_flag = False
  125. try:
  126. node_dmi_board_asset_tag = r_text.split('board_asset_tag="')[1].split('"')[0]
  127. node_dmi_board_name = r_text.split('board_name="')[1].split('"')[0]
  128. node_dmi_board_version = r_text.split('board_version="')[1].split('"')[0]
  129. node_dmi_board_vendor = r_text.split('board_vendor="')[1].split('"')[0]
  130. node_dmi_chassis_asset_tag = r_text.split('chassis_asset_tag="')[1].split('"')[0]
  131. node_dmi_chassis_version = r_text.split('chassis_version="')[1].split('"')[0]
  132. node_dmi_chassis_vendor = r_text.split('chassis_vendor="')[1].split('"')[0]
  133. node_dmi_product_family = r_text.split('product_family="')[1].split('"')[0]
  134. node_dmi_product_name = r_text.split('product_name="')[1].split('"')[0]
  135. node_dmi_product_sku = r_text.split('product_sku="')[1].split('"')[0]
  136. node_dmi_product_version = r_text.split('product_version="')[1].split('"')[0]
  137. node_dmi_system_vendor = r_text.split('system_vendor="')[1].split('"')[0]
  138. board_flag = True
  139. except:
  140. node_dmi_board_asset_tag = None
  141. node_dmi_board_name = None
  142. node_dmi_board_version = None
  143. node_dmi_board_vendor = None
  144. node_dmi_chassis_asset_tag = None
  145. node_dmi_chassis_version = None
  146. node_dmi_chassis_vendor = None
  147. node_dmi_product_family = None
  148. node_dmi_product_name = None
  149. node_dmi_product_sku = None
  150. node_dmi_product_version = None
  151. node_dmi_system_vendor = None
  152. board_flag = False
  153. try:
  154. node_cpus = r_text.split('node_softnet_dropped_total{cpu="')
  155. node_cpus_flag = True
  156. except:
  157. node_cpus_flag = False
  158. try:
  159. node_uname_info_domainname = r_text.split('node_uname_info{domainname="')[1].split('"')[0]
  160. node_uname_info_machine = r_text.split('machine="')[1].split('",nodename')[0]
  161. node_uname_info_nodename = r_text.split('nodename="')[1].split('",release')[0]
  162. node_uname_info_release = r_text.split(',release="')[1].split('",sysname')[0]
  163. node_uname_info_sysname = r_text.split(',sysname="')[1].split('",version')[0]
  164. node_uname_info_version = r_text.split('version="#')[1].split('"} ')[0]
  165. node_uname_flag = True
  166. except:
  167. node_uname_flag = False
  168. try:
  169. node_time_zone = r_text.split('node_time_zone_offset_seconds{time_zone="')[1].split('"')[0]
  170. time_zone_flag = True
  171. except:
  172. node_time_zone = None
  173. time_zone_flag = False
  174. print("\n - 'Node Export' (build):")
  175. if node_exporter_build_branch:
  176. print(" - Branch: "+node_exporter_build_branch)
  177. if node_exporter_build_revision:
  178. print(" - Revision: "+node_exporter_build_revision)
  179. if node_exporter_build_version:
  180. print(" - Version: "+node_exporter_build_version)
  181. if node_cpus_flag == True:
  182. node_cpus_number = 0
  183. for d in node_cpus[1:]:
  184. node_cpus_number = node_cpus_number + 1
  185. if node_cpus_number > 0:
  186. print("\n - CPUs (total):")
  187. print(" - "+str(node_cpus_number))
  188. if system_flag == True:
  189. print("\n - SYSTEM:")
  190. if node_dmi_bios_vendor:
  191. print(" - Vendor: "+node_dmi_bios_vendor)
  192. if bios_flag == True:
  193. print("\n - BIOS:")
  194. if node_dmi_bios_date:
  195. print(" - Date: "+node_dmi_bios_date)
  196. if node_dmi_bios_release:
  197. print(" - Release: "+node_dmi_bios_release)
  198. if node_dmi_bios_version:
  199. print(" - Version: "+node_dmi_bios_version)
  200. if os_flag == True:
  201. print("\n - OS:")
  202. if node_os_build:
  203. print(" - Build ID: "+node_os_build)
  204. if node_os_id:
  205. print(" - ID: "+node_os_id)
  206. if node_os_id_like:
  207. print(" - ID Like: "+node_os_id_like)
  208. if node_os_image_id:
  209. print(" - Image ID: "+node_os_image_id)
  210. if node_os_image_version:
  211. print(" - Image version: "+node_os_image_version)
  212. if node_os_pretty_name:
  213. print(" - Name: "+node_os_pretty_name)
  214. if node_os_variant:
  215. print(" - Variant: "+node_os_variant)
  216. if node_os_variant_id:
  217. print(" - Variant ID: "+node_os_variant_id)
  218. if node_os_version_codename:
  219. print(" - Version codename: "+node_os_version_codename)
  220. if node_os_version_id:
  221. print(" - Version ID: "+node_os_version_id)
  222. if node_uname_flag == True:
  223. print("\n - UNAME:")
  224. if node_uname_info_domainname:
  225. print(" - Domainname: "+node_uname_info_domainname)
  226. if node_uname_info_machine:
  227. print(" - Machine: "+node_uname_info_machine)
  228. if node_uname_info_nodename:
  229. print(" - Nodename: "+node_uname_info_nodename)
  230. if node_uname_info_release:
  231. print(" - Release: "+node_uname_info_release)
  232. if node_uname_info_sysname:
  233. print(" - Sysname: "+node_uname_info_sysname)
  234. if node_uname_info_version:
  235. print(" - Version: "+node_uname_info_version)
  236. if time_zone_flag == True:
  237. if node_time_zone:
  238. print("\n - TIMEZONE:")
  239. print(" - Location: "+node_time_zone)
  240. if board_flag == True:
  241. print("\n - BOARD:")
  242. if node_dmi_board_asset_tag:
  243. print(" - TAG: "+node_dmi_board_asset_tag)
  244. if node_dmi_board_name:
  245. print(" - Name: "+node_dmi_board_name)
  246. if node_dmi_board_vendor:
  247. print(" - Vendor: "+node_dmi_board_vendor)
  248. if node_dmi_board_version:
  249. print(" - Version: "+node_dmi_board_version)
  250. print("\n - CHASSIS:")
  251. if node_dmi_chassis_asset_tag:
  252. print(" - TAG: "+node_dmi_chassis_asset_tag)
  253. if node_dmi_chassis_vendor:
  254. print(" - Vendor: "+node_dmi_chassis_vendor)
  255. if node_dmi_chassis_version:
  256. print(" - Version: "+node_dmi_chassis_version)
  257. print("\n - PRODUCT:")
  258. if node_dmi_product_family:
  259. print(" - Family: "+node_dmi_product_family)
  260. if node_dmi_product_name:
  261. print(" - Name: "+node_dmi_product_name)
  262. if node_dmi_product_sku:
  263. print(" - SKU: "+node_dmi_product_sku)
  264. if node_dmi_product_version:
  265. print(" - Version: "+node_dmi_product_version)
  266. try:
  267. node_selinux = r_text.split('node_selinux_enabled')
  268. node_selinux_flag = True
  269. except:
  270. node_selinux_flag = False
  271. if node_selinux_flag == True:
  272. print("\n - SELINUX:")
  273. if node_selinux == 1:
  274. print(" - Status: ON")
  275. else:
  276. print(" - Status: OFF")
  277. try:
  278. node_disk_info_devices = r_text.split('node_disk_info{device="')
  279. node_disk_info_devices_flag = True
  280. except:
  281. node_disk_info_devices_flag = False
  282. if node_disk_info_devices_flag == True:
  283. if node_disk_info_devices:
  284. print("\n - Info of /sys/block/<block_device>:")
  285. for d in node_disk_info_devices[1:]:
  286. node_disk_info_device = d.split('"')[0]
  287. print(" - "+node_disk_info_device)
  288. node_disk_filesystem_devices = r_text.split('node_filesystem_files_free{device="')
  289. if node_disk_filesystem_devices:
  290. print("\n - Info of node_filesystem_files:")
  291. for d in node_disk_filesystem_devices[1:]:
  292. node_disk_filesystem_device = d.split('} ')[0]
  293. print(" - "+node_disk_filesystem_device)
  294. node_network_iface_id_devices = r_text.split('node_network_iface_id{device="')
  295. if node_network_iface_id_devices:
  296. print("\n - NETWORK devices:")
  297. for d in node_network_iface_id_devices[1:]:
  298. node_network_iface_id_device = d.split('"')[0]
  299. print(" - "+node_network_iface_id_device)
  300. node_network_info_devices = r_text.split('node_network_info{')
  301. if node_network_info_devices:
  302. print("\n - NETWORK entries by device:")
  303. for d in node_network_info_devices[1:]:
  304. node_network_info_device = d.split('} ')[0]
  305. print(" - "+node_network_info_device)
  306. node_arp_devices = r_text.split('node_arp_entries{device="')
  307. if node_arp_devices:
  308. print("\n - ARP entries by device:")
  309. for d in node_arp_devices[1:]:
  310. arp_device = d.split('"')[0]
  311. print(" - "+arp_device)
  312. print("\n - PROMETHEUS HTTP_metrics:")
  313. try:
  314. promhttp_metric_handler_errors_total_encoding = r_text.split('promhttp_metric_handler_errors_total{cause="encoding"}')[1].split("\n")[0]
  315. promhttp_metric_handler_errors_total_gathering = r_text.split('promhttp_metric_handler_errors_total{cause="gathering"}')[1].split("\n")[0]
  316. prom_gathering_flag = True
  317. except:
  318. prom_gathering_flag = False
  319. promhttp_metric_handler_requests_in_flight = r_text.split('promhttp_metric_handler_requests_in_flight')[1].split("\n")[0]
  320. promhttp_metric_handler_requests_total_200 = r_text.split('promhttp_metric_handler_requests_total{code="200"}')[1].split("\n")[0]
  321. promhttp_metric_handler_requests_total_500 = r_text.split('promhttp_metric_handler_requests_total{code="500"}')[1].split("\n")[0]
  322. promhttp_metric_handler_requests_total_503 = r_text.split('promhttp_metric_handler_requests_total{code="503"}')[1].split("\n")[0]
  323. print(" - HTTP-200 (OK) : "+promhttp_metric_handler_requests_total_200)
  324. print(" - HTTP-500 (FAIL) : "+promhttp_metric_handler_requests_total_500)
  325. print(" - HTTP-503 (FAIL) : "+promhttp_metric_handler_requests_total_503)
  326. if prom_gathering_flag == True:
  327. print(" - ENCODING (FAIL) : "+promhttp_metric_handler_errors_total_encoding)
  328. print(" - GHATERING (FAIL): "+promhttp_metric_handler_errors_total_gathering)
  329. print("")
  330. else:
  331. print("")
  332. banner()
  333. if os.path.exists("tmp.txt"):
  334. os.remove("tmp.txt")
  335. init()