В этой статье я рас­ска­жу, как при про­веде­нии пен­теста вскрыл целый ряд уяз­вимос­тей одно­го веб‑сер­виса, которые в ито­ге дали мне дос­туп к акка­унтам любых поль­зовате­лей. Я вкрат­це опи­шу каж­дую уяз­вимость, а затем пос­мотрим, что надо сде­лать, что­бы зак­рыть подоб­ные дыры.

Pentest Award

Этот текст получил пер­вое мес­то на пре­мии Pentest Award 2024 в катего­рии «Про­бив web». Это сорев­нование еже­год­но про­водит­ся ком­пани­ей Awillix.

warning

Статья име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для спе­циалис­тов по безопас­ности, про­водя­щих тес­тирова­ние в рам­ках кон­трак­та. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Рас­простра­нение вре­донос­ных прог­рамм, наруше­ние работы сис­тем и наруше­ние тай­ны перепис­ки прес­леду­ются по закону.

Перечисление пользователей

На сай­те, который я тес­тировал, была воз­можность зай­ти в лич­ный кабинет. А на стра­нице вхо­да наш­лась воз­можность переби­рать поль­зовате­лей и смот­реть ответ сер­вера: в слу­чае если поль­зователь с вве­ден­ным име­нем сущес­тву­ет, сер­вер отве­чает ина­че.

Ес­ли ука­зыва­ем сущес­тву­ющую учет­ку, получа­ем сооб­щение «Вве­ден невер­ный пароль» либо «Пре­выше­но количес­тво попыток вхо­да с невер­ным паролем». Если же поль­зовате­ля с таким логином нет, то в отве­те сер­вера будет сооб­щение «Ваши дан­ные не най­дены в сис­теме. Про­верь­те пра­виль­ность ука­зан­ных дан­ных».

Сообщение для валидного логина
Со­обще­ние для валид­ного логина
Сообщение для невалидного логина
Со­обще­ние для невалид­ного логина

Я запус­тил авто­мати­зиро­ван­ный под­бор по сло­варю и нашел некото­рое количес­тво валид­ных логинов.

Спреинг паролей

Даль­ше я нашел воз­можность под­бирать по сло­варю пароли най­ден­ных поль­зовате­лей — опять же по отве­ту сер­вера на вве­ден­ные дан­ные. Это ата­ка password spraying. Новые пароли я про­бовал каж­дые 30 минут, так как пос­ле каж­дого сле­дующе­го невер­ного вво­да учет­ных дан­ных пери­од бло­киров­ки не уве­личи­вал­ся. В ито­ге уда­лось подоб­рать пароли для трех поль­зовате­лей сер­виса.

Обход 2FA

Сра­зу зай­ти в лич­ный кабинет не уда­лось из‑за того, что сер­вис про­верял вто­рой фак­тор аутен­тифика­ции и про­сил ввес­ти код из SMS.

Страница ввода кода из SMS
Стра­ница вво­да кода из SMS

Изу­чив код этой стра­ницы, я нашел инте­рес­ный параметр — mode4. Здесь он име­ет зна­чение sms.

Параметры GET-запроса страницы ввода кода из SMS
Па­рамет­ры GET-зап­роса стра­ницы вво­да кода из SMS

Я переб­рал воз­можные зна­чения это­го парамет­ра и нашел нес­коль­ко таких, при которых нас не перенап­равля­ет обратно на стра­ницу вво­да логина и пароля.

Интересные значения, которые может принять параметр mode4
Ин­терес­ные зна­чения, которые может при­нять параметр mode4

Здесь инте­рес­нее все­го зна­чение mode4=registration, которое перенап­равля­ет на стра­ницу завер­шения регис­тра­ции в лич­ном кабине­те. Там поль­зователь зада­ет себе логин и пароль, но при этом уже задал и под­твер­дил номер телефо­на.

Вид страницы при mode4=registration
Вид стра­ницы при mode4=registration

Вво­дим и под­твержда­ем пароль одно­го из получен­ных ранее поль­зовате­лей, и нам уда­ется зай­ти в лич­ный кабинет, минуя двух­фактор­ную авто­риза­цию. Так­же, если изме­нить логин на этой же стра­нице, он сох­раня­ется изме­нен­ным.

Похищение учетки

Итак, мы вош­ли в сис­тему без регис­тра­ции и SMS. Но на этом мы, конеч­но же, иссле­дова­ние не прек­ратим и про­дол­жим копать.

Мое вни­мание прив­лекли cookie на стра­нице сме­ны пароля. Выяс­нилось, что содер­жимое стра­ницы не зависит ни от каких зна­чений, кро­ме UserStatus. А это зна­чение пер­манен­тно и уни­каль­но для каж­дого поль­зовате­ля.

Cookie UserStatus
Cookie UserStatus

Что будет, если под­менить UserStatus одно­го поль­зовате­ля ана­логич­ным зна­чени­ем дру­гого? Логин и пароль оста­вим преж­ними. Пос­ле отправ­ки зап­роса при­ходит ответ, что все прош­ло успешно. Спо­кой­но извле­каем из него новые cookie, логин, пароль и UserToken, но уже для сес­сии дру­гого юзе­ра — того, чей UserStatus исполь­зовал­ся. Так­же при­ходит редирект обратно на стра­ницу вхо­да. Если бы сме­на не была успешной, этим парамет­рам из cookie прис­ваива­лись бы зна­чения deleted и тоже про­исхо­дил бы редирект.

Те­перь можем вой­ти сно­ва — исполь­зуя новые логин и пароль. Нас опять попыта­ется оста­новить 2FA, то есть зап­рос кода из SMS. Но мы уже зна­ем, что делать в этом слу­чае, и без тру­да про­ника­ем в лич­ный кабинет поль­зовате­ля, UserStatus которо­го мы исполь­зовали.

Изменение логина и пароля пользователя
Из­менение логина и пароля поль­зовате­ля

Мы не зна­ем UserStatus для дру­гих поль­зовате­лей, но можем поп­робовать подоб­рать это зна­чение. Его дли­на — 40 бит. Это 240 вари­антов, то есть око­ло 1,1 трил­лиона зна­чений. Мно­гова­то для перебо­ра!

Но если разоб­рать любое извес­тное нам зна­чение UserStatus побито­во, то мож­но уви­деть, из чего оно сос­тоит. Полови­на каж­дого бай­та опре­деля­ет поле, а вто­рая полови­на — зна­чение это­го поля.

Побитовый разбор значений cookie UserStatus
По­бито­вый раз­бор зна­чений cookie UserStatus

Те­перь мы можем под­бирать зна­чения лишь 20 бит. Чис­ло вари­антов сок­раща­ется до 220, то есть око­ло 1,05 мил­лиона воз­можных зна­чений перемен­ной UserStatus.

Ре­зуль­тат экс­плу­ата­ции: нес­коль­ко зах­вачен­ных акка­унтов, потен­циаль­ная воз­можность зах­вата всех поль­зователь­ских акка­унтов за чуть более чем мил­лион зап­росов.

Выводы

Итак, мы поняли, что есть воз­можность менять логины и пароли всех поль­зовате­лей, UserStatus которых содер­жит те же поля, что извес­тные нам. Затем мы можем прой­ти аутен­тифика­цию от име­ни этих поль­зовате­лей в обход 2FA. Во вре­мя тес­тирова­ния мне уда­лось зах­ватить нес­коль­ко акка­унтов и уста­новить, что мож­но получить дос­туп к любой учет­ной записи чуть более чем за мил­лион зап­росов.

Вот что я пореко­мен­довал сде­лать заказ­чику, что­бы устра­нить уяз­вимос­ти: