## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-JWENY-SHIRO-CVE-2020-17523
# Apache Shiro два способа обхода аутентификации (CVE-2020-17523)
## 0x01 Описание уязвимости
Apache Shiro — это мощная и простая в использовании платформа безопасности Java, выполняющая аутентификацию, авторизацию, управление паролями и сессиями. Благодаря понятному API Shiro вы можете быстро и легко создавать любые приложения — от самых маленьких мобильных до крупнейших корпоративных.
При совместном использовании с Spring и определённых правилах сопоставления прав доступа злоумышленник может обойти аутентификацию, создав специальный HTTP-запрос.
Затронутые версии: Apache Shiro < 1.7.1
## 0x02 Настройка среды для уязвимости
shiro 1.7.0
https://github.com/jweny/shiro-cve-2020-17523 Среда для обоих методов уже обновлена.
## 0x03 Тестирование PoC
**Метод 1:**
http://127.0.0.1:8080/admin/%20 или http://127.0.0.1:8080/admin/%20/
Использование пробелов и других пустых символов позволяет обойти аутентификацию Shiro.

**Метод 2:**
После общения с коллегой p0desta был обнаружен ещё один метод использования в особых сценариях.
http://127.0.0.1:8080/admin/%2e или http://127.0.0.1:8080/admin/%2e/
Однако `.` (а также `/`) в правилах сопоставления путей Spring являются разделителями путей и не сопоставляются как обычные символы. Поэтому по умолчанию доступ к `/admin/.` возвращает 404.
Но если включён полный путь (`setAlwaysUseFullPath(true)`), сопоставление работает корректно.

## 0x04 Анализ уязвимости
В Shiro получение и сопоставление URL происходит в `org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain`.
Кратко рассмотрим метод `getChain`:


Этот метод сначала проверяет, заканчивается ли requestURI на `/`, и если да, удаляет последний `/`.
Затем в цикле сопоставления путей сначала проверяется, заканчивается ли правило пути pathPattern на `/`, и если да, он также удаляется. После этого вызывается метод `pathMatches()` для сопоставления пути.
**Поэтому для обоих методов не имеет значения, заканчивается ли путь на`/`, так как после прохождения `getChain` он будет удалён.**
### 4.1 Анализ обхода с пробелом
Обратим внимание на метод `pathMatches()`:
Вызовем Evaluate и вычислим `pathMatches("/admin/*","/admin/1")` и `pathMatches("/admin/*","/admin/ ")`. Первый соответствует нормально, второй — нет.


Начинаем отладку. После долгого F7 доходим до `doMatch("/admin/*","/admin/ ")`. Видно, что `tokenizeToStringArray` вернул pathDirs уже без второго уровня пути. Это приводит к несоответствию `/admin/*` и `/admin`.

Следуем за методом `tokenizeToStringArray`. Обнаруживаем, что при вызове `tokenizeToStringArray` параметр `trimTokens` равен true.

В методе `tokenizeToStringArray` при значении `trimTokens` true выполняется обработка `trim()`, что удаляет пробелы. После возврата в `getChain` последний `/` удаляется. Поэтому pathDirs, возвращённые `tokenizeToStringArray`, не содержат второго уровня пути.

Резюме: в уязвимой версии Shiro при вызове `tokenizeToStringArray` по умолчанию `trimTokens` равен true, и пробелы удаляются через `trim()`. После возврата в `getChain` последний `/` удаляется, поэтому `/admin` не соответствует `/admin/*`, что приводит к обходу аутентификации. В то же время Spring получает путь доступа `/admin/%20` и обрабатывает его как обычно, что позволяет обойти авторизацию.
### 4.2 Анализ обхода с /./
Глядя на второй метод с `/.` и `/./`, вспоминаете ли вы знакомый метод? Да, это `normalize()`.

Кратко переведём:
Условие| Пример
---|---
Обработка обратной косой черты в прямую| \ -> /
Двойной слеш обрабатывается как один
Таким образом, `/admin/.` сначала обрабатывается в `/admin/./`, затем становится `/admin/`.

После обработки в `org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain` последний `/` удаляется, и получается `/admin`. `/admin` не соответствует `/admin/*`, поэтому аутентификация Shiro обходится.

В то же время Spring получает запрос `/admin/.`. **Если полный путь не включён, в Spring`.` и `/` являются разделителями путей и не участвуют в сопоставлении.** Поэтому маппинг не находится и возвращается 404.

Если включён полный путь, Spring сопоставляет весь URL и возвращает 200.
Здесь приведён код для включения полного пути:
root@kitploit:~
@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;
}
}
## 0x05 Официальное исправление
На основе анализа можно выделить две причины обхода авторизации в Shiro:
1. Функция `tokenizeToStringArray` некорректно обрабатывает пробелы.
2. Логика удаления последнего `/` не должна выполняться до цикла сопоставления путей.
Официальное исправление:
https://github.com/apache/shiro/commit/0842c27fa72d0da5de0c5723a66d402fe20903df
1. Установить параметр `trimTokens` в значение false в `tokenizeToStringArray`. 
2. Изменить порядок удаления последнего `/`. Сначала сопоставлять исходный путь, и только если сопоставление не удалось, удалять последний `/`. 
## 0x06 О trim
Теоретически `trim()` удаляет все пробельные символы в начале и конце строки; пробел — лишь один из них. Однако в тестах другие пробельные символы, такие как `%08`, `%09`, `%0a`, при обработке Spring+Tomcat возвращают 400.
Таким образом, для первого метода, кроме пробела, других полезных payload пока не обнаружено.
## 0x07 Ссылки
https://github.com/apache/shiro/commit/0842c27fa72d0da5de0c5723a66d402fe20903df
https://www.anquanke.com/post/id/216096
https://www.cnblogs.com/syp172654682/p/9257282.html