
Analyse von zwei Umgehungsmethoden für die shiro-cve-2020-17523 Schwachstelle sowie die dazugehörige Schwachstellenumgebung
Apache Shiro ist ein leistungsstarkes und benutzerfreundliches Java-Sicherheitsframework, das Authentifizierung, Autorisierung, Passwort- und Sitzungsverwaltung durchführt. Mit der leicht verständlichen API von Shiro können Sie schnell und einfach jede Anwendung erstellen, von der kleinsten mobilen Anwendung bis hin zu den größten Web- und Unternehmensanwendungen.
Wenn es mit Spring kombiniert wird, kann ein Angreifer unter bestimmten Berechtigungs-Matching-Regeln durch das Erstellen spezieller HTTP-Anfragepakete die Authentifizierung umgehen.
Betroffene Versionen: Apache Shiro < 1.7.1
shiro 1.7.0
https://github.com/jweny/shiro-cve-2020-17523 Die Umgebungen für beide Methoden wurden aktualisiert.
Methode 1:
http://127.0.0.1:8080/admin/%20 oder http://127.0.0.1:8080/admin/%20/
Durch die Verwendung von Leerzeichen oder anderen Leerzeichen kann die Shiro-Authentifizierung umgangen werden.

Methode 2:
Nach Austausch mit Meister p0desta wurde eine weitere Nutzungsmethode unter speziellen Umständen entdeckt.
http://127.0.0.1:8080/admin/%2e oder http://127.0.0.1:8080/admin/%2e/
Allerdings repräsentieren . (und auch /) in den Pfad-Matching-Regeln von Spring Pfadtrennzeichen und werden nicht als normale Zeichen gematcht. Daher gibt der Zugriff auf /admin/. unter Standardbedingungen 404 zurück.
Im Szenario mit vollständigem Pfad (setAlwaysUseFullPath(true)) wird jedoch korrekt gematcht.

Die URL-Erfassung und -Zuordnung in Shiro erfolgt in org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain.
Schauen wir kurz auf diese getChain-Methode:


Diese Methode prüft zuerst, ob die requestURI mit einem / endet. Wenn ja, wird das letzte / entfernt.
Dann, in der Schleife zum Pfad-Matching, wird zuerst geprüft, ob das Pfadmuster pathPattern mit einem / endet. Wenn ja, wird es ebenfalls entfernt. Anschließend wird die Methode pathMatches() zum Pfadvergleich aufgerufen.
Daher spielt es bei beiden Nutzungsmethoden keine Rolle, ob sie mit / enden, da dies zu Beginn der getChain-Methode entfernt wird.
Betrachten wir die pathMatches()-Methode:
Rufen Sie Evaluate auf und berechnen Sie jeweils pathMatches("/admin/*","/admin/1") und pathMatches("/admin/*","/admin/ "). Ersteres matcht normal, letzteres schlägt fehl.


Beginnen Sie das Debugging. Nach einer längeren F7-Sequenz erreichen Sie doMatch("/admin/*","/admin/ "). Es ist ersichtlich, dass das von tokenizeToStringArray zurückgegebene pathDirs keine zweite Pfadebene mehr enthält. Dies führt dazu, dass /admin/* und /admin nicht matchen.

Verfolgen Sie die Methode tokenizeToStringArray. Es zeigt sich, dass beim Aufruf von tokenizeToStringArray der Parameter trimTokens auf true gesetzt ist.

Die Methode tokenizeToStringArray führt bei trimTokens = true eine trim()-Behandlung durch, wodurch Leerzeichen entfernt werden. Nach der Rückkehr zu getChain wird das letzte / gelöscht. Daher enthält das von tokenizeToStringArray zurückgegebene pathDirs keine zweite Pfadebene.

Zusammenfassung: In der anfälligen Shiro-Version ist der trimTokens-Parameter der Methode tokenizeToStringArray standardmäßig auf true gesetzt, sodass Leerzeichen durch trim() entfernt werden. Nach der Rückkehr zu getChain wird das letzte / gelöscht, weshalb /admin nicht mit /admin/* matcht, was die Authentifizierung umgeht. Spring empfängt jedoch den Zugriffspfad /admin/%20 und gibt die Antwort gemäß der normalen Logik zurück, was zur Umgehung der Berechtigungsprüfung führt.
Wenn Sie die zweite Methode mit /. und /./ sehen, erinnert Sie das an eine vertraute Methode? Richtig, es ist normalize().

Eine einfache Übersetzung:
| Bedingung | Beispiel |
|---|---|
| Schrägstrich wird zu Backslash | \ -> / |
| Doppelbackslash wird zu Backslash | // -> / |
Daher wird /admin/. zuerst in /admin/./ verarbeitet und dann zu /admin/.

Nach der Verarbeitung durch org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain wird das letzte / entfernt (weil es mit / endet), sodass es zu /admin wird. /admin matcht nicht mit /admin/*, daher wird die Shiro-Authentifizierung umgangen.

Zu diesem Zeitpunkt empfängt Spring die Anfrage als /admin/.. Wenn der vollständige Pfad-Modus nicht aktiviert ist, werden . und / in Spring als Pfadtrennzeichen betrachtet und nicht am Pfad-Matching beteiligt. Daher wird kein Mapping gefunden und 404 zurückgegeben.

Wenn der vollständige Pfad-Modus aktiviert ist, wird die gesamte URL gematcht, und Spring gibt 200 zurück.
Hier der Code zur Aktivierung des vollständigen Pfad-Modus:
@SpringBootApplication
public class SpringbootShiroApplication extends SpringBootServletInitializer implements BeanPostProcessor {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringbootShiroApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(SpringbootShiroApplication.class, args);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
((RequestMappingHandlerMapping) bean).setAlwaysUseFullPath(true);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
Basierend auf der obigen Analyse gibt es zwei Ursachen für die Shiro-Berechtigungsumgehung:
tokenizeToStringArray behandelt Leerzeichen nicht korrekt./ sollte nicht vor der Logik des Pfad-Matchings in der Schleife ausgeführt werden.Daher lautet der offizielle Fix:
https://github.com/apache/shiro/commit/0842c27fa72d0da5de0c5723a66d402fe20903df
trimTokens-Parameter von tokenizeToStringArray auf false. 
/. Zuerst wird der ursprüngliche Pfad gematcht, und nur wenn das Matching fehlschlägt, wird das letzte / entfernt. 
Prinzipiell entfernt trim() alle Whitespace-Zeichen am Anfang und Ende eines Strings. Leerzeichen sind nur eine Art davon. Bei Tests mit anderen Whitespace-Zeichen wie %08, %09, %0a gab Spring+Tomcat jedoch 400 zurück.
Daher wurde bei Methode 1 außer Leerzeichen noch kein anderer nutzbarer Payload gefunden.
https://github.com/apache/shiro/commit/0842c27fa72d0da5de0c5723a66d402fe20903df
| Endet mit /. oder /.., dann / anhängen | /. -> /./ /.. -> /../ |
| Normalisierung von /./ | /./ -> / |
| Pfadsprung | /aaa/../bbb -> /bbb |