Zusammenfassung: Dieser Artikel zeigt, wie sich falsch konfigurierte Spring Boot Actuator-Endpunkte in Pentests oder Bug-Bounty-Programmen nutzen lassen. Er behandelt Discovery-Methoden jenseits von /actuator/, den Einsatz spezieller Header (z. B. X-Forwarded-For), Path-Traversal- und Semikolon-Bypasses sowie den Zugriff auf kritische Endpunkte wie mappings, metrics, httptrace oder heapdump. Anhand praxisnaher Beispiele wird erklärt, wie Angreifer damit sensible Informationen gewinnen oder sogar Sessions übernehmen können. Gleichzeitig wird deutlich: Actuator ist nach wie vor ein lohnendes Ziel, wenn man gezielt sucht. Für Administratoren und Entwickler ergibt sich daraus die Notwendigkeit, Actuator-Endpunkte konsequent abzusichern, nur notwendige Schnittstellen freizugeben und Logging/Monitoring zu etablieren.
Einleitung
Spring Boot ist ein häufig verwendetes Java-Framework, mit dem API und Web Applikationen umgesetzt werden können. Vor allem im Enterprise-Bereich ist es recht stark verbreitet. Unternehmen, wie Salesforce, AT&T, Amazon, Porsche, Daimler, Zoom und VIELE andere, haben es für ihre Tools und Software in Verwendung.
Aus Sicht eines Penetrationstesters bzw. Red Teamers und Bug Bounty Hunters ist Spring Boot insofern interessant, da es ein Modul enthält, welches als Actuator bekannt ist. Im Grunde bietet es Monitoring- und Management-Endpunkte, die Debug-Informationen bereitstellen. Wer Interesse an Actuator allgemein hat, wird im Internet mehr als genug Artikel dazu finden. In diesem Artikel geht es primär um Spring Boot Actuator und wie man - wenn vorhanden - diesen während Pentests oder Bug Bounty Hunting nutzen kann.
Allgemein gilt Actuator als etwas, was man - zumindest im Kontext Bug Bounty Hunting - sehr selten sieht, weil sie nicht mehr so einfach zu finden sind und neue Versionen mit relativ sicheren Standard-Einstellungen kommen und/oder viele Assets durch WAF vor unberechtigtem Zugriff geschützt sind (Danke speziell an Akamai und Cloudflare dafür, dass sie mir kontinuierlich das Leben schwerer machen ;)).
Kürzlich wurde ich mehrmals angeschrieben, wie ich immer noch so viele Actuator finde. Da sich die Fragen häufen und ich mir immer mal wieder vornehme etwas zu schreiben, was vielleicht dem ein oder anderen weiterhelfen kann, dachte ich mir: Ich schreibe mal was zusammen.
Andere Quellen und Beiträge
In der Vergangenheit wurde immer wieder - meist mit speziellem Fokus - das Thema Spring Boot oder Submodule davon behandelt. Alle genannten Beiträge sind lesenswert.
Allgemeines zu Actuators und Fehlkonfigurtionen: wiz.io
Das Spring Boot-Modul Jolokia als Zielscheibe: blog.wss.sh
Mit Sicherheit lassen sich im Internet noch weitere interessante Beiträge finden. Speziell der Beitrag von WIZ ist lesenswert, zumal er dem einige grundlegende Konzepte näher bringt. Diese werde ich nicht wiederholen - sondern versuchen eher in die Tiefe zu gehen. Wenn du dich also entschließt diesen Artikel zu lesen, erwarte ich, dass du Actuator grob verstehst!
Actuator finden - Pfade und Variationen
Die meisten suchen Actuator primär unter /actuator/
. Hier gibt es mehrere Dinge zu beachten:
-
die relevanten Endpunkte (
env
,mappings
,heapdump
, …) müssen NICHT unter “/actuator/
” verfügbar sein -
nur weil “
/actuator/
” nicht verfügbar ist, heißt es nicht, dass z.B. “/actuator/env
” auch nicht verfügbar ist (oder andere Endpunkte) - 1) und 2) können zusammen auftreten
Zu 1):
Systemadministratoren können die Endpunkte direkt im root des Assets zur Verfügung stellen:
GET /env HTTP/2
Genauso kann Actuator in einem Verzeichnis vorhanden sein:
GET /att-admin/env HTTP/2
GET /att-admin/actuator/env HTTP/2
GET /att-admin/info/env HTTP/2
Was lernen wir hier daraus: Fuzzen ist notwendig. Blindes fuzzen mit SecLists würde ich nicht empfehlen. Bis ihr damit etwas gefunden habt, werdet ihr schon 20x blockiert sein. Lest den HTML Code, Javascript Files, schaut euch die Domain/Subdomain an. Als konkretes Beispiel:

Man stelle sich folgende Subdomain vor: vadt.management.domain.com
- Actuator war zu finden unter “vadt
”. Es gibt genauso Fälle, wo “management
” auch getestet werden sollte.
Ansonsten ist eurer Kreativität freien Lauf gelassen. Macht ihr das einige Jahre werdet ihr ein sehr gutes Gefühl dafür bekommen, was man prüfen sollte und hoffentlich jeden Fund in eure Wortliste schreiben.
Zu 2)
In wirklich vielen Fällen ist die Index-Seite von Actuator nicht verfügbar, weil sie gesperrt ist oder aus sonstigen Gründen. Es lohnt sich also eher nach den inneren Endpunkten zu suchen. Eine solide Taktik ist, statt nach “env
” oder “heapdump
” zu suchen, “health
” zu verwenden. Ihr könnt natürlich stattdessen “env
” verwenden, wenn dieser aber gesperrt ist, können euch andere Wege verschlossen bleiben - weil ihr einfach nicht in der Lage wart, zu erkennen, dass vor euch ein Actuator vorhanden ist.
Zu 3)
Bitter wird es, wenn sowohl das in Punkt 1) als auch Punkt 2) beschriebene zutrifft:

Wir haben einen nicht trivialen Pfad, in dem der Actuator lebt und env
ist nicht erreichbar. Der HTTP 404 Fehler deutet aber auf den Actuator hin. DASS man hier vielleicht weiter suchen sollte, wird einem erst klar, wenn man eigentlich nach health
gesucht hat:

Zugriff auf Actuator via speziellen HTTP Headern
Wenn das “normale” finden eines Actuators nicht funktioniert hat, macht es oft Sinn zu prüfen, ob man mit dem Setzen spezieller HTTP Header eventuell Zugriff bekommen könnte. Im speziellen haben die beiden folgenden Header eine hohe Erfolgsquote:
X-Forwarded-For: 127.0.0.1
X-Original-URL: /actuator/env
Als Beispiel sieht man hier einen echten Fall innerhalb eines Bug Bounty Programms:

Was seht ihr? Die simple Existenz von XFF: 127.0.0.1
war genug, um Zugriff auf den Actuator zu erlangen. Was sieht man noch? Actuator existierte nicht in “actuator/
” sondern in “api/
”. Was sieht man noch? Ich habe weder health noch env
verwendet. configprops
empfehle ich ebenfalls wärmstens.
Der andere relevante Header - X-Original-URL
kommt wesentlich seltener vor - es lohnt sich hier aber immer mal wieder zu testen. Das Verhalten hiervon ist eigentlich gut dokumentiert, daher gehe ich nicht auf die Details an.
Außerdem: Es gibt noch weitere Header, die man prüfen sollte. Welche es sind, überlasse ich euch herauszufinden.
Zugriff auf mappings
Wie eingangs erwähnt ist es gar nicht so einfach, einen Actuator zu finden, weil es diverse externe Faktoren geben kann (WAF, Server, …). Manchmal hat man einen Actuator gefunden, hat aber weder Zugang zu env
noch heapdump
. gateway
und all die tollen Techniken funktionieren auch nicht. Hier sollte man schauen, ob man Zugriff auf mappings
hat:
GET /actuator/mappings HTTP/2
Ist das der Fall kann man zumindest aus Sicht eines Angreifers versuchen die Applikation zu verstehen und so einen gescheiten Angriff mit Impact zu erreichen. Der mappings
-Endpunkt gibt im Grunde alle registrierten Endpunkte und Parameter innerhalb der Route aus. Allgemein sieht sie HTTP Antwort so aus:

Konkret kann man dort unter Umständen so etwas finden:

Das einzige, was jetzt gemacht werden muss: die genannte Route “adminDashBoard/getUsers
” muss aufgerufen werden. Mit etwas Glück bekommt man - wie der Name schon sagt - alle Nutzer. Im konkreten Fall war das tatsächlich auch der Fall.
Im schlechtesten Fall sind die Routes authentifiziert und man kommt nicht weiter. Im besten Fall bekommt man einen 404,403,400,500 HTTP Status - dann lohnt es sich beispielsweise nach Parametern zu fuzzen:
GET /adminDashBoard/getUsers?FUZZ=a HTTP/2
Das interessante hierbei ist, das ohne diese Information man nie und nimmer an diese Route gekommen wäre. Das sind in der Regel Begriffe, die in keiner Wortliste zu finden sind. Konkret war hier die Route auch noch case-sensitive.
Kein mappings vorhanden? metrics als Alternative
Ist auch mappings
nicht vorhanden, lohnt der Blick auf den metrics
Endpunkt:
GET /actuator/metrics HTTP/2
Dieser antwortet mit verfügbaren Namen, die im darauf folgenden Request genutzt werden können:
{"names":["application.ready.time","application.started.time","disk.free","disk.total","executor.active","executor.completed","executor.pool.core","executor.pool.max","executor.pool.size","executor.queue.remaining","executor.queued","http.client.requests","http.client.requests.active","http.server.requests","http.server.requests.active","jvm.buffer.count","jvm.buffer.memory.used","jvm.buffer.total.capacity","jvm.classes.loaded","jvm.classes.unloaded","jvm.compilation.time","jvm.gc.live.data.size","jvm.gc.max.data.size","jvm.gc.memory.allocated","jvm.gc.memory.promoted","jvm.gc.overhead","jvm.gc.pause","jvm.info","jvm.memory.committed","jvm.memory.max","jvm.memory.usage.after.gc","jvm.memory.used","jvm.threads.daemon","jvm.threads.live","jvm.threads.peak","jvm.threads.started","jvm.threads.states","logback.events","process.cpu.time","process.cpu.usage","process.files.max","process.files.open","process.start.time","process.uptime","spring.cloud.gateway.requests","spring.cloud.gateway.routes.count","system.cpu.count","system.cpu.usage","system.load.average.1m"]}
Das meiste hiervon ist langweilig - Meta Informationen.
Von Relevanz sind:
spring.cloud.gateway.requests
http.server.requests
http.client.requests
Verwendet werden sie folgendermaßen:
GET /actuator/metrics/{metric-name} HTTP/2
Sie geben ebenfalls - wenn man weniger verbose - aktuelle Requests, die Server und Client verschicken/empfangen.
spring.cloud.gateway.requests

Eventuell lohnt es sich hiermit mal vhost Fuzzing zu versuchen. Auch im Kontext von SSRF sind genau diese Hosts von größter Relevanz.
http.server.requests

Hier haben wir quasi den Ersatz für mappings
- mit weniger Informationen aber nützlich. Ab hier sollte man anfangen, diese Routen zu testen.
http.client.requests
Hierzu hat WIZ im oben genannten Artikel einiges geschrieben - im Grunde das gleiche wie für die oberen Beispiele. Es sind nützliche Informationen, die einen indirekt weiterbringen können.
Nichts geht mehr? Path traversals und Bypasses
Allgemein sollte jeder wissen, das einfaches und doppeltes URL-Encoding durchaus hilfreich sein kann, um auf “gesperrte” Actuator trotzdem Zugriff zu bekommen. Ich bin hier aber ehrlich - habe ich noch NIE in Real Life gesehen. Gleiches gilt für Mixed Encoding, UTF8 Overlong Encoding und was es da sonst so alles gibt. Ich lasse mich natürlich gerne eines besseren belehren und freue mich darauf, wenn mir jemand actuator+encoding in Real Life zeigt.
Was hat mir trotzdem oft geholfen:
Path traversals
Im speziellen gibt es zwei Zeichenfolgen, die ich immer wieder verwende und die in mein Toolset eingearbeitet sind (nicht nur für Actuators - HINT!!): “..;
” sowie “..
”
Technische Details erspare ich euch - Orange Tsai hat vor Jahren bereits darüber berichtet:
Lesebefehl! Im Internet wird man ansonsten ebenfalls mehr dazu finden.
Wie sieht das in der Praxis beim Actuator - oft - aus? So:
GET /..;/actuator/env HTTP/2
GET /static../actuator/env HTTP/2
GET /actuator/health/..;/env HTTP/2
Hier einige der oben genannten Punkte, kombiniert mit Path Traversals:

Alternative: Bypass mittels “;”
So simple und stupide, klappt oft: hinzufügen von “;
” nach relevanten Begriffen - hierzu muss man einiges testen. Gründe, dass es funktioniert, sind häufig schlechte Rewrite Rules, Blockliste, Regeln, …
GET /;/actuator/env HTTP/2
GET /actuator;/env HTTP/2
GET /actuator/env; HTTP/2
Auch das wurde im Kontext BBP gefunden - und das recht oft mittlerweile.

In der Praxis ist mir ebenfalls folgendes untergekommen, das funktioniert hat (im Gegensatz zu den oben genannten):
GET /actuator/env;.. HTTP/2
Path traversals und diese Bypasses lassen sich oft auch gut kombinieren - ohne Fuzzen kommt man da aber nicht wirklich weit - auch hier aus einem bekannten Bug Bounty Programm:

Session overtake via httptrace Endpunkt
Durchaus spannend kann der httptrace
Endpunkt sein, wenn er vorhanden ist:
GET /actuator/httptrace HTTP/2
Hier besteht die Gefahr, das User Sessions, Header samt validen Werten und Cookies geleakt werden:

In meinem Fall konnte ich so die User-Session eines Administrators übernehmen und hatte damit Zugriff auf PII von Millionen von Nutzern. Ist das nicht möglich, kann man oft die Nutzersession von “normalen” Nutzern übernehmen und so an deren Nutzerdaten/PII kommen. Für etwas Impact lohnt es sich ein kleines Tool zu programmieren, das den Endpoint alle paar Sekunden abruft, Sessions/Cookies/Header speichert, diese verwendet, um PII zu exfiltrieren - sofern es seitens des Auftraggebers bzw. Bug Bounty Programms gestattet ist. An dieser Stelle folgt ein freundlicher Hinweis, sich die Policy von Programmen genau durchzulesen.
Und sonst so? logfile & gateway
Actuator bietet oft den gateway/routes
Endpunkt. Hat man dort Schreibrechte, ist eine SSRF realistisch. In alten Spring Boot-Versionen auch eine RCE. Hierzu der freundliche Verweis auf den Artikel von Wiz (siehe oben).
Ebenfalls von Interesse: logfile
Dieser Endpunkt muss vom Admin konfiguriert sein. Er gibt die Logs für bestimmte Ereignisse aus. Ich hatte hier nie wirklich Glück, weil ich NIE Schreibrechte hatte um den Debug-Mode zu ändern und häufigere Writes in die Logfile zu forcieren. Allgemeine Informationen finden sich HIER. Bezüglich des Änderns der Loglevel lohnt das Lesen von diesem Artikel.
Bonus: CVE-2022-22978
In 2022 wurde ein CVE veröffentlicht mit dem ein Auth Bypass möglich war. Ich habe seit Jahren nichts damit gefunden - pro forma erwähne ich es, weil es ggf. in internen Netzwerken in denen man mit Updates stark zurück ist, nützlich sein könnte. Ein Request würde in etwa so aussehen und Zugriff auf Actuator erlauben:
GET /actuator/%0Aenv HTTP/1.1
Heapdump geladen! Was jetzt?
Spannend wird die gesamte Sache, wenn man es schafft den Heapdump zu laden. Das geht für gewöhnlich via:
GET /actuator/heapdump HTTP/2
Das Thema ist in diversen Artikeln behandelt worden. Daher kurz von mir: Nutzt VisualVM um heapdumps zu öffnen und zu analysieren. In 90% der Fälle findet man dort Passwörter, PII - komplett zugänglich.
OQL erleichert es einem schnell nach diesen Dingen zu suchen - hier ein kurzes Beispiel von mir:
select {o: s,val:s.value.toString()} from java.lang.String s
where
/secret|passwd|password|token|api|key|auth|AWS_|kube|redis|kubernetes|k8s|grafana|jira|docker|app_|ssh|credential|confluence|elastic|solr|beats|logstash|slack|Basic |Bearer |django|jdbc|ftp|sftp|odbc|mysql|couchdb|neo4j|leveldb/.test(s.value.toString())
Wer mit einem heapdump gearbeitet hat, wird jetzt sagen: “Leider gibt das oft keine Ergebnisse aus”. Das stimmt - Grund hierfür ist die Tatsache, dass intern Strings nicht mehr via char[] sondern byte[] abgepeichert werden. Das macht das einfache Suchen nach einem String nicht mehr praktikabel. Aber auch hier kommt OQL zum Einsatz:
select {
object: s,
value: (function(){
var bytes = s.value;
var coder = s.coder;
var chars = [];
if (coder == 0) {
for (var i = 0; i < bytes.length; i++) chars.push(String.fromCharCode(bytes[i]&0xff));
} else if (coder == 1) {
for (var i = 0; i < bytes.length; i+=2) chars.push(String.fromCharCode((bytes[i]<<8&0xff00)|(bytes[i+1]&0xff)));
} else {
return "";
}
return chars.join('');
})()
}
from java.lang.String s
where (function(){
var bytes = s.value;
var coder = s.coder;
var chars = [];
if (coder == 0) {
for (var i = 0; i < bytes.length; i++) chars.push(String.fromCharCode(bytes[i]&0xff));
} else if (coder == 1) {
for (var i = 0; i < bytes.length; i+=2) chars.push(String.fromCharCode((bytes[i]<<8&0xff00)|(bytes[i+1]&0xff)));
} else {
return false;
}
return /SUPERSECRET/.test(chars.join(''));
})()
Remediation
Schön und gut zu wissen, was alles geht und was nicht. Aus Sicht eines Sysadmins bringt dir das alles wenig. Daher einige Tipps, damit du dich nicht mit Leuten, wie mir, rumschlagen musst:
- Alles, was an Actuator-Endpunkten nicht benötigt wird, sollte deaktiviert werden - auch wenn wenig praktikabel: in Produktivumgebungen sowie aber auch in Test/Dev-Umgebungen - wenn das nicht möglich ist - pack eine HTTP-Authentifizierung / .htaccess davor und teste sie ordentlich durch (oder lasse durchtesten)
- Nutze management.endpoints.web.exposure.include/exclude um nur das zu erlauben, was wirklich nötig ist (z.B. nur der health Endpunkt)
- eine strikte gut umgesetzte IP-Whitelist kann helfen
- verzichte darauf den Zugriff auf die Endpunkte mittels kompletexer Regular Expressions auf Server oder WAF-Ebene zu blockieren - oder sei dir sehr sicher, was du machst
Fazit
Ich habe diesen Artikel geschrieben, weil man mir sagte, dass das Finden von Actuators mittlerweile schwer ist und sie selten vorkommen. Mit diesem Artikel will ich dem widersprechen. Man muss sich nur etwas mehr anstrengen - sie sind da.
Die ein oder andere Methode - aber nicht alle - habe ich hier vorgestellt. Diese sollte man allgemein nie isoliert betrachten und testen. Kombiniert man sie, findet man das, was man sucht. Kreativität ist am Ende immer das A&O!
In dem Sinne:
GET /;/bye/..;/actuator/heapdump;.. HTTP/2
Host: dsecured.com
X-Forwarded-For: 127.0.0.1