February 16, 2021

Вам труба! Как независимый исследователь взломал YouTube

Ес­ли ты ког­да‑нибудь заливал на YouTube при­ват­ное видео и подумы­вал, что оно может все рав­но стать дос­тоянием общес­твен­ности, то наход­ка незави­симо­го баг­ханте­ра Да­вида Шюца (David Shütz) уси­лит твои опа­сения. Шюц нашел нес­коль­ко спо­собов получить дос­туп к при­ват­ным видео на YouTube и извлечь инфу из акка­унта. Мы рас­ска­жем о том, какие баги сер­виса к это­му при­вели.

КАК УКРАСТЬ ПРИВАТНОЕ ВИДЕО

В декаб­ре 2019 года в ходе работы по прог­рамме баг­баун­ти Google Vulnerability Reward Program (VRP) Давид Шюц заин­тересо­вал­ся воз­можностью прос­мотра при­ват­ных видео на YouTube. В нас­трой­ках каж­дого заг­ружен­ного на виде­охос­тинг ролика поль­зователь дол­жен ука­зать парамет­ры дос­тупа к нему. Все­го пре­дус­мотре­но три вари­анта:

  1. «Откры­тый дос­туп» (видео дос­тупно всем без исклю­чения и сво­бод­но индекси­рует­ся поис­ковыми сис­темами);
  2. «Дос­туп по ссыл­ке» (для прос­мотра ролика нуж­но перей­ти на сайт YouTube по спе­циаль­ной ссыл­ке);
  3. «Огра­ничен­ный дос­туп» (кон­тент видят толь­ко отдель­ные зарегис­три­рован­ные поль­зовате­ли, которых отме­тил вла­делец акка­унта).
Нас­трой­ки YouTube поз­воля­ют огра­ничить дос­туп к кон­тенту

Са­мая инте­рес­ная, с точ­ки зре­ния хакера, безус­ловно, третья катего­рия виде­оро­ликов. В попыт­ке прос­мотреть заг­ружен­ное им на собс­твен­ный акка­унт видео со ста­тусом «Огра­ничен­ный дос­туп» из‑под дру­гой учет­ки Давид щел­кал по всем кноп­кам и ссыл­кам на сай­те виде­охос­тинга и пытал­ся вся­чес­ки модифи­циро­вать URL ролика. Но попыт­ки ока­зыва­лись тщет­ными: YouTube с завид­ным упорс­твом воз­вра­щал сооб­щение об ошиб­ке дос­тупа.

Тог­да иссле­дова­тель решил пой­ти обходным путем. Он руководс­тво­вал­ся прос­той иде­ей, которую час­то берут на воору­жение пен­тесте­ры: если основной сер­вис в дос­таточ­ной сте­пени защищен от ком­про­мета­ции, могут отыс­кать­ся свя­зан­ные с ним сто­рон­ние сер­висы, исполь­зующие его API, или ресур­сы, у которых дела с безопас­ностью обсто­ят не так хорошо. В качес­тве цели он выб­рал сер­вис кон­текс­тной рек­ламы Google Ads.

Да­вид обра­тил вни­мание на то, что Google Ads вза­имо­дей­ству­ет со мно­гими служ­бами Google. Нап­ример, рек­лама, которая демонс­три­рует­ся при прос­мотре видео на YouTube, нас­тра­ивает­ся с помощью Google Ads. Ради экспе­римен­та Шюц зарегис­три­ровал акка­унт в рек­ламном кабине­те Google и попытал­ся нас­тро­ить рек­ламу с исполь­зовани­ем ID его при­ват­ного видео. Безус­пешно — сис­тема не поз­волила про­вер­нуть такой трюк.

Пос­ле это­го Шюц при­нял­ся изу­чать име­ющиеся в его рас­поряже­нии нас­трой­ки рек­ламно­го кабине­та. Целью поис­ков было что‑нибудь, име­ющее непос­редс­твен­ное отно­шение к YouTube. Сре­ди про­чего в рек­ламном акка­унте обна­ружил­ся раз­дел «Видео» (Video), в котором отоб­ража­ются исполь­зуемые для рек­ламы ролики. Щел­чок мышью на превью откры­вает раз­дел «Ана­лити­ка» (Analytics), где при­водят­ся све­дения о ролике и рас­полага­ется встро­енный про­игры­ватель. Давид обра­тил вни­мание на фун­кцию «Момен­ты» (Moments) — она поз­воля­ет рек­ламода­телю отме­чать опре­делен­ные эпи­зоды на видео. С ее помощью мож­но выделить кадр, в котором на экра­не демонс­три­рует­ся рек­ламиру­емый про­дукт или логотип ком­пании, и изу­чить дей­ствия поль­зовате­лей в этот момент. Луч­ше все­го механизм дей­ствия дан­ного инс­тру­мен­та демонс­три­рует ани­мация, соз­данная самим Давидом Шюцем.

Инс­тру­мент «Момен­ты» в рек­ламном кабине­те Google

Про­ана­лизи­ровав логи прок­си, Давид обна­ружил, что вся­кий раз, ког­да он соз­дает «момент» в ролике, сайт Google Ads отсы­лает POST-зап­рос на эндпой­нт /GetThumbnails, тело которо­го содер­жит ID целево­го виде­оро­лика и име­ет сле­дующий вид:

POST /aw_video/_/rpc/VideoMomentService/GetThumbnails HTTP/1.1

Host: ads.google.com

User-Agent: Internet-Explorer-6

Cookie: [redacted]

__ar={"X":"kCTeqs1F4ME","Y":"12240","3":"387719230"}

Па­раметр X в стро­ке __ar — это ID целево­го ролика, а Y — вре­мен­ная мет­ка отме­чен­ного момен­та в мил­лисекун­дах. В ответ на этот зап­рос сер­вер воз­вра­щает закоди­рован­ную в Base64 кар­тинку — мини­атю­ру кад­ра, который выб­ран в рек­ламном кабине­те с помощью инс­тру­мен­та «Момен­ты». Заменив в зап­росе параметр X на ID сво­его «при­ват­ного» видео, Давид неожи­дан­но для себя получил ответ с закоди­рован­ным кад­ром. Таким обра­зом, он обна­ружил ошиб­ку IDOR (Insecure Direct Object Reference), поз­воля­ющую получить дос­туп к защищен­ным нас­трой­ками при­ват­ности дан­ным. Но с исполь­зовани­ем уяз­вимос­ти иссле­дова­телю уда­лось раз­добыть все­го лишь один кадр из час­тно­го видео. Хороший резуль­тат, но явно недос­таточ­ный.

Да­вид решил написать скрипт на Python, который авто­мати­чес­ки отправ­ляет соот­ветс­тву­ющие зап­росы, сох­раня­ет кад­ры из откли­ков сер­вера, а затем заново собира­ет из них пол­ноцен­ное видео. Нес­ложные под­сче­ты показа­ли: если ролик демонс­три­рует 24 кад­ра в секун­ду, то каж­дый кадр при­сутс­тву­ет на экра­не 33 мс. Таким обра­зом, скрипт дол­жен пос­ледова­тель­но сох­ранять кад­ры с интерва­лом в 33 мс, а потом ском­поновать из получен­ных изоб­ражений какое‑нибудь инте­рес­ное кино.

Ра­боту соз­данно­го им proof of concept Давид Шюц запечат­лел в виде­оро­лике, который выложил на том же YouTube. Скрипт вытас­кива­ет кад­ры из пред­варитель­но сох­ранен­ного Шюцем при­ват­ного ролика и вос­созда­ет его пер­вый трех­секун­дный отре­зок. Изоб­ражение получа­ется мел­ким, раз­мытым и не слиш­ком качес­твен­ным, но детали на нем рас­смот­реть все рав­но мож­но. Это успех!

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

Да­вид Шюц немед­ленно сооб­щил в Google о най­ден­ной им ошиб­ке. Через месяц «Кор­порация доб­ра» вып­латила ему за наход­ку 5000 дол­ларов, а фун­кция «Момен­ты» в рек­ламном кабине­те была исправ­лена — теперь дви­жок про­веря­ет пра­ва дос­тупа к видео при обра­бот­ке зап­росов из рек­ламно­го кабине­та Google. Но Давид решил не оста­нав­ливать­ся на дос­тигну­том и вско­ре обна­ружил в сер­висах YouTube еще один инте­рес­ный баг.

Я ЗНАЮ, ЧТО ТЫ СМОТРИШЬ!

Вто­рой най­ден­ный Давидом Шюцем баг в гуг­лов­ских сер­висах работа­ет еще забав­нее. Дос­таточ­но отпра­вить потен­циаль­ной жер­тве ссыл­ку на спе­циаль­но соз­данную веб‑стра­ницу, и все тай­ное вне­зап­но ста­новит­ся явным. Зло­умыш­ленни­ку откры­вает­ся дос­туп к исто­рии прос­мотра видео на YouTube, ссыл­кам на при­ват­ные ролики, а так­же он получа­ет в свое рас­поряже­ние спи­сок пон­равив­шихся кли­пов и содер­жимое раз­дела «Пос­мотреть поз­же» дан­ного поль­зовате­ля. Как работа­ет эта уяз­вимость и как Давид ее нашел? Что­бы понять прин­цип дей­ствия бага, нуж­но разоб­рать­ся в некото­рых осо­бен­ностях работы YouTube.

Невидимые плей-листы

Да­же если ты заходил на YouTube лишь пару раз в жиз­ни, что­бы пос­мотреть на котиков, будь уве­рен: в тво­ем акка­унте все рав­но име­ется нес­коль­ко авто­мати­чес­ки соз­данных плей‑лис­тов. Пер­вый из них, с иден­тифика­тором HL (History List), содер­жит исто­рию прос­мотра. Туда добав­ляют­ся ссыл­ки на все ролики, которые ты ког­да‑либо прос­матри­вал на YouTube. Дру­гой плей‑лист, озаг­лавлен­ный WL (Watch Later), — это «избран­ное», в которое помеща­ются ссыл­ки на видео при нажатии поль­зовате­лем кноп­ки «Пос­мотреть поз­же».

Еще один плей‑лист, содер­жащий ссыл­ки на пон­равив­шиеся поль­зовате­лю видео, тре­бует отдель­ного опи­сания. Давид обна­ружил его в ходе экспе­римен­тов с URL собс­твен­ного канала на YouTube. Иден­тифика­тор канала пред­став­ляет собой стро­ку дли­ной 24 сим­вола: его мож­но узнать, вни­матель­но пос­мотрев на адрес стра­ницы канала. Нап­ример, если URL име­ет вид https://www.youtube.com/channel/UCOvX9uEO0a3fZNCK12MAabc, то иден­тифика­тор пред­став­ляет собой стро­ку UCOvX9uEO0a3fZNCK12MAabc.

Пос­ле ряда опы­тов со сво­ими акка­унта­ми Давид при­шел к выводу, что дос­туп к спис­ку пон­равив­шегося видео откры­вает­ся, если заменить пер­вые три сим­вола в иден­тифика­торе канала зна­чени­ем LLB или LLD. Ины­ми сло­вами, если иден­тифика­тор канала име­ет зна­чение UCOvX9uEO0a3fZNCK12MAgug, то плей‑лист пон­равив­шихся кли­пов будет иметь имя LLBvX9uEO0a3fZNCK12MAgug или LLDvX9uEO0a3fZNCK12MAgug.

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

Ка­залось бы, дос­таточ­но наб­рать в адресной стро­ке бра­узе­ра URL вида https://www.youtube.com/playlist?list=xxx, где xxx — иден­тифика­тор плей‑лис­та с заг­ружен­ными поль­зовате­лем ролика­ми, и мы получим ID его видео, вклю­чая при­ват­ные. Одна­ко все не так прос­то. Вла­делец канала, которо­му при­над­лежит акка­унт, дей­стви­тель­но уви­дит пол­ный спи­сок заг­ружен­ного им кон­тента. А вот дру­гой поль­зователь смо­жет прос­мотреть толь­ко ссыл­ки на пуб­личные видео, в то вре­мя как при­ват­ные ролики и ролики с дос­тупом по ссыл­ке оста­нут­ся для него скры­тыми.

Оче­вид­но, что эти плей‑лис­ты содер­жат боль­шой объ­ем информа­ции, которая может быть инте­рес­на зло­умыш­ленни­кам. И на пер­вый взгляд кажет­ся, что доб­рать­ся до нее не так‑то прос­то.

Встраиваемый плеер

Как извес­тно, добав­ленные на виде­охос­тинг ролики мож­но прос­матри­вать не толь­ко на самом YouTube, но и на сто­рон­них сай­тах, если заг­рузив­ший видео поль­зователь не зап­ретил подоб­ное дей­ствие в нас­трой­ках. Спе­циаль­но для это­го сущес­тву­ет при­ложе­ние YouTube Iframe Player. Добавить пле­ер на сайт очень прос­то: для это­го дос­таточ­но лишь вста­вить в веб‑стра­ницу спе­циаль­ный HTML-код, содер­жащий тег iframe.

Нес­мотря на кажущу­юся прос­тоту, про­игры­ватель име­ет собс­твен­ный API, поз­воля­ющий делать с ним вся­кие полез­ные вещи: нап­ример, авто­мати­чес­ки оста­нав­ливать и возоб­новлять вос­про­изве­дение видео или начинать показ с опре­делен­ного момен­та. Реали­зует­ся это с исполь­зовани­ем JavaScript, а для вызова фун­кций API раз­работ­чик дол­жен под­клю­чить на сво­ем сай­те спе­циаль­ную JS-биб­лиоте­ку.

Для вза­имо­дей­ствия меж­ду веб‑стра­ницей и про­игры­вате­лем исполь­зует­ся встро­енная в бра­узер тех­нология PostMessageAPI. Она поз­воля­ет обме­нивать­ся дан­ными сай­там с айфрей­мами, содер­жимое которых заг­ружа­ется с дру­гого домена. Работа­ет это так. В пле­ер встро­ен спе­циаль­ный слу­шатель PostMessage, ожи­дающий вхо­дящих команд. Коман­ды отправ­ляет по защищен­ному каналу под­клю­чен­ная к стра­нице JS-биб­лиоте­ка, ког­да поль­зователь выпол­няет то или иное дей­ствие, нап­ример ста­вит видео на паузу. Но эта связь двус­торон­няя: пле­ер так­же может переда­вать биб­лиоте­ке сооб­щения о событи­ях. Давид Шюц утвер­жда­ет, что YouTube Iframe Player бол­тает без умол­ку, даже ког­да его ник­то ни о чем не спра­шива­ет, и рас­ска­зыва­ет всем и каж­дому о сво­ем внут­реннем мире. Это, в свою оче­редь, поз­воля­ет помес­тить на веб‑стра­нице слу­шате­ли, нас­тро­енные на раз­личные события. Нап­ример, слу­шатель может вызывать­ся, ког­да поль­зователь откры­вает веб‑стра­ницу и запус­кает вос­про­изве­дение ролика.

Да­вид Шюц при­водит такой при­мер подоб­ного вза­имо­дей­ствия. Ког­да в айфрей­ме на веб‑стра­нице встро­енный пле­ер начина­ет вос­про­изве­дение видео, вызыва­ется коман­да API player.playVideo(). При этом сайт переда­ет в айфрейм такое сооб­щение:

{

"event":"command",

"func":"playVideo",

"args":[],

"id":1,

"channel":"widget"

}

В ответ пле­ер воз­вра­щает сай­ту целую кучу информа­ции:

{

"event":"infoDelivery",

"info":{

"playerState":-1,

"currentTime":0,

"duration":1344,

"videoData":{

"video_id":"M7lc1UVf-VE",

"author":"",

"title":"YouTube Developers Live: Embedded Web Player Customization"

},

"videoStartBytes":0,

"videoBytesTotal":1,

"videoLoadedFraction":0,

"playbackQuality":"unknown",

"availableQualityLevels":[],

"currentTimeLastUpdated_":1610191891.79,

"playbackRate":1,

"mediaReferenceTime":0,

"videoUrl":"https://www.youtube.com/watch?v=M7lc1UVf-VE",

"playlist":null,

"playlistIndex":-1

},

"id":1

,"channel":"widget"

}

{

"event":"onStateChange",

"info":-1,

"id":1,

"channel":"widget"

}

JS-биб­лиоте­ка от YouTube слу­жит сво­его рода уров­нем абс­трак­ции, поз­воля­ющим веб‑раз­работ­чику прос­то помес­тить про­игры­ватель на стра­нице сво­его сай­та и боль­ше ни о чем не бес­поко­ить­ся. Весь упо­мяну­тый выше «обмен любез­ностя­ми» про­исхо­дит скры­то, «под капотом», и мно­гие поль­зовате­ли о сущес­тво­вании подоб­ных вещей даже не догады­вают­ся. Но при желании мож­но выз­вать нуж­ные фун­кции API без вся­ких биб­лиотек, нап­рямую, с исполь­зовани­ем обыч­ного JavaScript. Что и попытал­ся сде­лать Давид Шюц.

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

window.addEventListener("message", function(event){console.log(event.data)})

Меж­ду тем API про­игры­вате­ля от YouTube поз­воля­ет делать мно­го дру­гих любопыт­ней­ших вещей, кро­ме управле­ния вос­про­изве­дени­ем ролика. Ска­жем, с помощью встро­енно­го в биб­лиоте­ку метода player.loadPlaylist(playlist_id) мож­но заг­ружать во встра­иваемый про­игры­ватель плей‑лис­ты с опре­делен­ным ID. Пос­ле это­го дос­таточ­но выз­вать фун­кцию playVideo(), и в айфрей­ме нач­нет вос­про­изво­дить­ся пер­вое видео из плей‑лис­та, по окон­чании которо­го будет авто­мати­чес­ки заг­ружено сле­дующее, и так далее по спис­ку.

В JS-биб­лиоте­ке име­ется еще один полез­ный метод, поз­воля­ющий получить дан­ные виде­оро­ликов из опре­делен­ного плей‑лис­та, — он называ­ется player.getPlaylist(). Он воз­вра­щает мас­сив, сос­тоящий из ID всех видео в заг­ружен­ном плей‑лис­те, нап­ример так:

> player.getPlaylist()

Array(20) [ "KxgcVAuem8g", "U_OirTVxiFE", "rbez_1MEhdQ", "VpC9qeKUJ00",

"LnDjm9jhkoc", "BQIOEdkivao", "layKyzA1ABc", "-Y9gdQnt7zs",

"U_OX5vQ567Y", "ghOqpVet1uQ", ...]

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

> player.getVideoData()

Object { video_id: "KxgcVAuem8g", author: "LiveOverflow2",

title: "Astable 555 timer - Clock Module", video_quality: "medium",

video_quality_features: [], list: "PLGPckJAmiZCTyI72iI2KaJxkp-vUKBlTi" }

При­меча­тель­но, что све­дения об этом объ­екте отсутс­тву­ют в офи­циаль­ной докумен­тации YouTube, пос­коль­ку некото­рое вре­мя назад обра­баты­вающая его фун­кция была уда­лена из JS-биб­лиоте­ки. Тем не менее про­игры­ватель по зап­росу все рав­но отправ­ляет дан­ные VideoData() на веб‑стра­ницу. Соот­ветс­твен­но, они могут быть получе­ны и обра­бота­ны с помощью обыч­ного JavaScript. Давид Шюц нашел информа­цию о том, что ука­зан­ная фун­кция все еще жива (хоть и дав­ным‑дав­но выпиле­на из биб­лиоте­ки), на Stack Overflow. Чем и решил вос­поль­зовать­ся в сво­их экспе­римен­тах.

То самое сооб­щение, вдох­новив­шее Давида про­дол­жать иссле­дова­ния

А теперь самое инте­рес­ное. Если поль­зователь авто­ризо­ван на YouTube, ког­да он откро­ет любой сайт со встро­енным пле­ером, этот пле­ер так­же авто­мати­чес­ки авто­ризу­ется в его акка­унте. И все прос­мотрен­ные юзе­ром на сто­рон­нем сай­те ролики тут же попада­ют в плей‑лист HL (History List), о котором уже рас­ска­зыва­лось чуть рань­ше. Ины­ми сло­вами, про­игры­ватель име­ет пол­ный дос­туп к акка­унту юзе­ра на виде­охос­тинге. А соз­датель веб‑стра­ницы име­ет фак­тичес­ки пол­ный дос­туп к самому пле­еру с исполь­зовани­ем его внут­ренне­го API. Сме­каешь, к чему все это ведет?

Срывая покровы

В замет­ке на сво­ем сай­те Давид Шюц отме­чал, что этот хак не тре­бует исполь­зования каких‑то спе­циаль­ных «хакер­ских при­емов», тут в ход идет исклю­читель­но сооб­разитель­ность и уме­ние обра­щать­ся с JavaScript. Дей­стви­тель­но: дос­туп к авто­мати­чес­ки генери­руемым плей‑лис­там име­ет толь­ко вла­делец акка­унта на YouTube, а встра­иваемый пле­ер может работать на любом сай­те с при­виле­гиями это­го вла­дель­ца. Прос­то как дваж­ды два.

Для про­вер­ки сво­ей гипоте­зы Давид соз­дал однос­тра­нич­ный сайт со встро­енным YouTube Iframe Player и зас­тавил его вос­про­извести плей‑лист с исто­рией прос­мотра HL (History List) дру­гого сво­его акка­унта. Все получи­лось! Ког­да плей‑лист заг­рузил­ся, Давид выз­вал фун­кцию player.getPlaylist() и получил спи­сок ID всех видео в этом плей‑лис­те. То есть любой зло­умыш­ленник, прис­лав кому‑нибудь ссыл­ку на свою веб‑стра­нич­ку, может получить спи­сок роликов, которые этот кто‑то прос­матри­вал на YouTube. Неп­лохо, но встра­иваемый пле­ер поз­воля­ет без тру­да добывать и дру­гую полез­ную информа­цию.

Да­вид соз­дал proof of concept, поз­воля­ющий с помощью player.getPlaylist() получить спи­сок видео, добав­ленных в плей‑лист WL (Watch Later) — «Пос­мотреть поз­же» и HL — спи­сок прос­мотрен­ных видео. Ана­логич­ным обра­зом пле­ер на его стра­нице под­гру­жал плей‑лист пон­равив­шихся видео, а из его наз­вания вос­ста­нав­ливал ID канала поль­зовате­ля YouTube, под­менив пер­вые три сим­вола в 24-сим­воль­ном иден­тифика­торе. Зная ID канала, скрипт получал плей‑лист с заг­ружен­ными поль­зовате­лем видео и пар­сил его с помощью недоку­мен­тирован­ной фун­кции player.getVideoData(), получая заголов­ки роликов и про­чую полез­ную информа­цию о них.

Proof of concept Давида Шюца

Не­кото­рая замин­ка воз­никла у Давида Шюца с лич­ными видео поль­зовате­ля, зак­рытыми нас­трой­ками при­ват­ности. Встра­иваемый пле­ер поз­воля­ет получить плей‑лист «Заг­рузки», теоре­тичес­ки содер­жащий спи­сок всех роликов, которые юзер залил на YouTube. Но на сто­рон­них сай­тах ID при­ват­ных видео оста­ются скры­тыми. Зато в этом спис­ке отоб­ража­ется кое‑что дру­гое. Если ты пом­нишь, помимо обще­дос­тупных и при­ват­ных видео, на YouTube еще име­ются ролики, дос­тупные исклю­читель­но по ссыл­ке. Юзе­ры исполь­зуют эту фун­кцию, если не хотят, что­бы заг­ружен­ное ими видео попало в паб­лик и индекси­рова­лось поис­ковика­ми, но вмес­те с тем жела­ют поделить­ся им со сво­ими зна­комы­ми. Так вот, ID таких роликов тоже отоб­ража­ется в плей‑лис­те «Заг­рузки». А зна­чит, мож­но прос­мотреть «полуп­риват­ные» видео юзе­ра, ведь пле­ер обла­дает теми же при­виле­гиями, что и вла­делец акка­унта! А еще, зная ID, мож­но сге­нери­ровать ссыл­ки на такие ролики и поделить­ся ими с общес­твен­ностью. Неп­лохо, прав­да?

Да­вид сооб­щил о сво­их наход­ках в Google и спус­тя некото­рое вре­мя получил за свои изыс­кания воз­награж­дение в раз­мере 1337 дол­ларов. Теперь при заг­рузке плей‑лис­тов встра­иваемый пле­ер обра­щает­ся за кон­тентом к эндпой­нту /list_ajax?list=[playlist-id]. Если про­игры­ватель пыта­ется заг­рузить при­ват­ный или «спе­циаль­ный» плей‑лист из перечис­ленных в этой статье, эндпой­нт воз­вра­щает ошиб­ку. Вмес­те с тем баг с недоку­мен­тирован­ной фун­кци­ей player.getVideoData() был акту­ален еще какое‑то вре­мя, но пос­ле пов­торно­го пись­ма в Google раз­работ­чики залата­ли и эту дыру.

ВЫВОДЫ

Об­наружен­ные и опи­сан­ные Давидом Шюцем баги показы­вают, что источни­ком уяз­вимос­тей может стать вза­имо­дей­ствие нес­коль­ких про­дук­тов, раз­работ­кой которых занима­ются раз­ные коман­ды одной и той же ком­пании — как это было в слу­чае с YouTube и Google Ads. Это тот самый слу­чай, ког­да пра­вая рука не зна­ет, что дела­ет левая. В резуль­тате воз­можны утеч­ки, которые зло­умыш­ленник может исполь­зовать в сво­их целях.

Сам Давид пишет: «Пос­ле того как вы некото­рое вре­мя про­тес­тиру­ете про­дукт и пой­мете, как он работа­ет внут­ри, ста­новит­ся более эффектив­ной (и увле­катель­ной) попыт­ка выпол­нить какие‑нибудь неожи­дан­ные дей­ствия, которых раз­работ­чики, воз­можно, не пре­дус­мотре­ли. Чем луч­ше вы понима­ете сис­тему, тем боль­ше у вас появ­ляет­ся идей, как ее сло­мать».

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