Пътуването е моята страст и аз съм голям фен на Couchsurfing. Couchsurfing е глобална общност от пътешественици, където можете да намерите място за отсядане или да споделите собствения си дом с други туристи. На всичкото отгоре Couchsurfing ви помага да се насладите на истинско пътуване, докато общувате с местните жители. Участвам в общността на Couchsurfing повече от 3 години. Отначало присъствах на срещи, а след това най-накрая успях да приема хора. Какво невероятно пътуване беше! Срещнах толкова много невероятни хора от цял свят и намерих много приятели. Цялото това преживяване наистина промени живота ми.
Аз бях домакин много пътешественици себе си, много повече, отколкото съм сърфирал досега. Докато живеех в една от основните туристически дестинации на Френската Ривиера, получих огромно количество заявки за диван (до 10 на ден през високия сезон). Като разработчик на заден план на свободна практика , Веднага забелязах, че проблемът с уебсайта couchsurfing.com е, че той наистина не се справя правилно с такива случаи с „голямо натоварване“. Няма информация за наличността на вашия диван - когато получите нова заявка за диван, не можете да сте сигурни дали вече хоствате някого по това време. Трябва да има визуално представяне на вашите приети и чакащи заявки, така че да можете да ги управлявате по-добре. Освен това, ако можете да направите достъпността на дивана си публична, можете да избегнете ненужните заявки за диван. За да разбера по-добре какво имам предвид, погледнете календара на Airbnb.
Много компании са известни с това, че не слушат своите потребители. Познавайки историята на Couchsurfing, не можех да разчитам на тяхното внедряване на тази функция в скоро време. Откакто уебсайтът стана компания с нестопанска цел, общността се влоши. За да разберете по-добре за какво говоря, предлагам да прочетете тези две статии:
Знаех, че много членове на общността ще се радват да имат тази функционалност. И така, реших да направя приложение за решаване на този проблем. Оказва се, че няма публичен API за Couchsurfing. Ето отговора, който получих от техния екип за поддръжка:
„За съжаление трябва да ви информираме, че нашият API всъщност не е публичен и в момента не се планира да го правим публичен.“
Време беше да използвам някои от любимите ми техники за обратно инженерство на софтуера, за да проникна в Couchsurfing.com. Предположих, че техните мобилни приложения трябва да използват някакъв API за заявка на бекенда. И така, трябваше да прехващам HTTP заявките, идващи от мобилно приложение към бекенда. За тази цел настроих прокси в локалната мрежа и свързах моя iPhone към нея, за да прихваща HTTP заявки. По този начин успях да намеря точки за достъп на техния частен API и да разбера техния формат на JSON полезен товар.
Накрая създадох уебсайт, който служи с цел да помогне на хората да управляват техните заявки за диван и да покаже на сърфистите календар за наличност на дивана. Публикувах линк към него на форумите на общността (които също са доста сегментирани според мен и е трудно да се намери информация там). Приемът беше предимно положителен, въпреки че някои хора не харесваха идеята, че уебсайтът изисква удостоверения за couchsurfing.com, което наистина беше въпрос на доверие.
Уебсайтът работи по следния начин: влизате в уебсайта с идентификационните си данни на couchsurfing.com и след няколко кликвания получавате html кода, който можете да вградите във вашия профил на couchsurfing.com, и voila - имате автоматично актуализиран календар в твоят профил. По-долу е екранната снимка на календара и тук статиите за това как го направих:
Създадох страхотна функция за Couchsurfing и естествено предположих, че те ще оценят работата ми - може би дори ще ми предложат позиция в техния екип за разработка. Изпратих имейл до работни места (на) couchsurfing.com с връзка към уебсайта, моето резюме и справка. Благодарност, оставена от един от моите гости на каучсърфинг:
Няколко дни по-късно те проследиха усилията ми за обратно инженерство. В отговора стана ясно, че единственото нещо, за което те са загрижени, е тяхната собствена сигурност, така че те ме помолиха да премахна публикациите в блога, които съм писал за API, и в крайна сметка уебсайта. Веднага свалих публикациите, тъй като намерението ми не беше да нарушавам условията за използване и да търся идентификационни данни на потребителите, а по-скоро да помогна на общността за каучсърфинг. Имах впечатлението, че съм третиран като престъпник и компанията се фокусира единствено върху факта, че уебсайтът ми изисква потребителски идентификационни данни.
Предложих да им дам приложението си безплатно. Те биха могли да го хостват в своята среда и да го свържат чрез удостоверяване във Facebook. В крайна сметка това е страхотна функция и общността се нуждаеше от нея. Ето окончателната резолюция, която получих:
„Влизаме отново в разгара на нещата тук след празниците и искахме да продължим.
Проведохме вътрешна дискусия относно вашето приложение и как бихме могли едновременно да уважим креативността и инициативата, които той показва, като същевременно не застрашаваме поверителността и сигурността на данните на потребителите на Couchsurfing, когато те въвеждат своите идентификационни данни в сайт на трета страна.
Календарът явно запълва дупка с функции на нашия сайт, функция, която е част от по-голям проект, по който работим сега.
Но въпросът за събирането на потребителски имена и пароли остава. Не успяхме да измислим лесен начин да го настроим, за да можем да го хостваме или поддържаме от наша страна, без нито да ви позволяваме достъп до тези данни, нито сайта ви да се разглежда като наш работен продукт.
API, който е наличен в момента, скоро ще бъде заменен с версия, която ще изисква удостоверяване / упълномощаване от приложения, които имат достъп до него. '
Днес, докато пиша този урок за софтуер за обратно инженерство (една година след събитията), функцията за календар все още не е внедрена в Couchsurfing.
Преди няколко седмици бях вдъхновен да напиша статия за техниките за обратно проектиране на частни API. Естествено, реших да обобщя предишните статии, които съм написал по тази тема, и да добавя още няколко подробности. Тъй като започнах да пиша новата статия, исках да покажа обратния инженерингов процес с актуален API и да вложа още един удар в API хакерството. Въз основа на предишния ми опит и факта, че Couchsurfing наскоро обяви изцяло ново wesbite и мобилно приложение http://blog.couchsurfing.com/the-future-of-couchsurfing-is-on-the-way/ , Реших отново да хакна техния API.
Защо правя този обратен инженерен процес? Е, на първо място е много забавно да се реверсира софтуер като цяло. Това, което особено ми харесва в него, е, че не включва само вашите технически умения, но и вашата интуиция. Понякога най-добрият начин да разберете нещата е да направите образовано предположение - това ще ви спести много време в сравнение с грубата сила. Наскоро чух история от компания, която трябваше да работи със собствени API и малко или никаква документация. Те се мъчеха да дешифрират полезния товар на отговора на API в неизвестен формат в продължение на дни, след което някой реши да опита ?decode=true
в края на URL адреса и те имаха подходящ JSON. Понякога, ако имате късмет, всичко, което трябва да направите, е измислете отговора на JSON .
Друга причина да правя този урок е, че на някои компании са необходими възрасти, за да приемат определена функция, поискана от техните потребители. Вместо да чакате да бъде внедрен, можете да използвате силата на техния частен API и да го изградите сами.
И така, с новия API на couchsurfing.com започнах с подобен подход и инсталирах най-новото им приложение за iOS.
Първо, трябва да настроите прокси във вашата LAN, за да фалшифицирате HTTP заявки, идващи от приложението към API, като извършите атака „човек в средата“ (MITM).
За некриптирани връзки атаката е съвсем проста - клиент се свързва с проксито и вие препращате входящите заявки към целевия сървър напред-назад. Ако е необходимо, можете да промените полезния товар. В публична WLAN е доста лесно да се направи това под прикритие, като се представя за WiFi рутера.
За криптирани връзки има малка разлика: всички заявки са криптирани от край до край. не е възможно нападателят да дешифрира съобщението, освен ако по някакъв начин не получи достъп до частния ключ (който, разбира се, не се изпраща по време на тези взаимодействия). Като каза, че въпреки че API комуникационният канал е сигурен, крайните точки - особено клиентът - не са толкова безопасни.
За да работи правилно SSL, трябва да бъдат изпълнени следните условия:
За да преодолее криптирането в MITM атака, нашият прокси трябва да действа като CA (Certificate Authority) и да генерира сертификати в движение. Например, ако клиент се опита да се свърже с www.google.com, проксито динамично създава сертификат за www.google.com и го подписва. Сега клиентът смята, че проксито всъщност е www.google.com
За да приложа прокси прокси, използван за обратно проектиране на частния API, ще използвам инструмента, наречен mitmproxy . Можете да използвате всеки друг прозрачен HTTPS прокси. Чарлз е друг пример с приятен GUI. За да работим това, трябва да настроим следните неща:
Конфигурирайте шлюза по подразбиране на WiFi връзката на телефона си да бъде прокси (така че проксито да е в средата и всички пакети да преминат) Инсталирайте сертификата на проксито на телефона (така че клиентът да има публичния ключ на проксито в своя магазин за доверие)
Проверете документацията на вашия прокси за инсталиране на сертификата. Тук са инструкциите за mitmproxy. И тук е сертификат PEM файл за iOS.
За да наблюдавате прихващаните HTTP заявки, просто стартирате mitmproxy и се свързвате с него от мобилния си телефон (портът по подразбиране е 8080).
Отворете уебсайт в мобилния си браузър. На този етап трябва да можете да видите трафика в mitmproxy.
След като се уверите, че всичко работи по план, е време да започнете да проучвате частния API по ваш избор. По принцип на този етап можете просто да отворите приложението, да си поиграете с него и да получите представа за крайните точки на API и структурата на заявките.
Няма строг алгоритъм за това как да реверсирате софтуерния API - през повечето време разчитате на интуицията си и правите предположения.
Моят подход е да репликирам извикванията на API и да играя с различни опции. Добро начало е да преиграете заявка, която сте хванали в mitmproxy, и да видите дали тя работи (натиснете ‘r’, за да повторите заявка). Първата стъпка е да разберете кои заглавки са задължителни. Доста удобно е да играете със заглавки с mitmproxy: натиснете „e“, за да влезете в режим на редактиране, след това „h“, за да промените заглавките. С преките пътища, които използват, зависимите от vim биха се чувствали като у дома си. Можете също да използвате разширения на браузъра като Postman, за да тествате API, но те са склонни да добавят ненужни заглавки, затова предлагам да се придържате към mitmproxy или curl.
Направих скрипт, който чете mitmproxy dump файл и генерира низ на къдрици - https://gist.github.com/nderkach/bdb31b04fb1e69fa5346
Нека започнем с изпратената заявка, когато влизате в системата.
POST https://hapi.couchsurfing.com/api/v2/sessions ← 200 application/json
Първото нещо, което забелязах, е, че всяка заявка съдържа задължителен заглавие X-CS-Url-Signature
което всеки път е различно. Също така се опитах да възпроизведа заявка след известно време, за да проверя дали на сървъра има проверка на клеймото за време и няма такава. Следващото нещо, което трябва да направите, е да разберете как се изчислява този подпис.
В този момент реших да направя обратен инженеринг на двоичния файл и да разбера алгоритъма. Естествено, имайки опит в разработването за iPhone и разполагайки с iPhone, реших да започна с iPhone ipa (приложение за iPhone). Оказва се, че ще дешифрирам един, имам нужда от прекъснат телефон. Спри се! Време за чук.
След това си спомних, че и те имат приложение за Android. Бях малко колеблив да опитам този подход, тъй като не знам нищо за Android или Java. Тогава си помислих, че би било добър шанс да науча нещо ново. Оказа се, че е по-лесно да се получи читав квази изходен код чрез декомпилиране на Java байт код, отколкото силно оптимизиран iphone машинен код.
Apk (приложение за Android) е основно zip файл. Можете да използвате всеки екстрактор на цип, за да разопаковате съдържанието му. Ще намерите файл, наречен classes.dex, който е байт код на Dalvik. Dalvik е виртуална машина, използвана за стартиране на преведен байт код на Java на Android.
За да декомпилирам файла .dex в изходния код на .java, използвах инструмента, наречен dex2jar. Резултатът от този инструмент е jar файл, който можете да декомпилирате с различни инструменти. Можете дори да отворите буркан в Eclipse или IntelliJ IDEA и той ще свърши цялата работа вместо вас. Повечето от тези инструменти дават подобен резултат. Наистина не ни интересува дали можем да го компилираме обратно, за да го стартираме, а просто го използваме за анализ на изходния код.
Ето списък с инструменти, които съм изпробвал:
CFR и FernFlower работиха най-добре за мен. JD-GUI не успя да декомпилира някои критични части на кода и беше безполезен, докато останалите бяха с приблизително същото качество. За щастие изглежда, че кодът на Java кодът не е бил объркан, но има инструменти като ProGuard http://developer.android.com/tools/help/proguard.html за да ви помогне да деобфускирате кода.
Декомпилацията на Java всъщност не е обхватът на този урок за обратен инженеринг - има много написани по тази тема, така че нека приемем, че сте успешно декомпилирали и дефасфукирали своя Java код.
Комбинирах всички съответни кодове, използвани за изчисляване на X-CS-Url-Signature в следната същност: https://gist.github.com/nderkach/d11540e9af322f1c1c74
Първо, търсих споменавания на X-CS-Url-Signature
, които намерих в RetrofitHttpClient
. Едно конкретно обаждане изглеждаше интересно - на EncUtils
модул. Ровейки се в него, разбрах, че те използват HMAC SHA1. HMAC е код за удостоверяване на съобщение, който използва криптографска функция (SHA1 в този случай) за изчисляване на хеш на съобщение. Използва се за осигуряване на целостта (т.е. за да се предотврати модифицирането на заявката от човек в средата) и удостоверяване.
Имаме нужда от две неща, за да изчислим X-CS-Url-Signature
: частния ключ и кодираното съобщение (вероятно някаква вариация на полезния товар на HTTP заявката и URL адреса).
final String a2 = EncUtils.a(EncUtils.a(a, s)); final ArrayList list = new ArrayList(request.getHeaders()); list.add(new Header('X-CS-Url-Signature', a2));
В кода a
е съобщение и s
е ключът, който се използва за изчисляване на заглавката a2
(двойното повикване към EncUtils
просто изчислява HMAC SHA1 шестнадесетичен дайджест).
Намирането на ключа не е било проблем - той се съхранява в обикновен текст в ApiModule
и се използва за инициализиране на втория параметър на RetrofitHttpClient.
RetrofitHttpClient a(OkHttpClient okHttpClient) { return new RetrofitHttpClient(okHttpClient, 'v3# [email protected] #XreXeGCh'); }
Ако разгледаме извикването към EncUtils
, можем да видим, че литералът на низа по-горе се използва дословно като ключ за изчисляване на HMAC, с изключение на случая, когато this.b
е дефиниран. В последния случай, this.b
се добавя с точка към него.
String s; if (this.b == null) { s = this.a; } else { s = this.a + '.' + this.b; }
Сега, само като разгледах кода, не ми стана ясно къде и как this.b
се инициализира (единственото, което успях да открия, е, че се извиква в метод с подпис this.a(String b)
, но не можах да го извикам никъде в кода).
public void a(final String b) { this.b = b; }
Препоръчвам ви да го декомпилирате и да разберете себе си :)
Определянето на съобщението беше доста право - в кода можете да видите, че това е конкатенация на URL адреса, т.е. /api/v2/sessions
и низ с JSON полезен товар (ако има такъв).
final byte[] b = this.b(request.getUrl()); byte[] a; if (request.getBody() != null && request.getBody() instanceof JsonTypedOutput) { System.out.println('body'); // this.a(x, y) concatenates byte arrays a = this.a(b, ((JsonTypedOutput)request.getBody()).a); } else { a = b; }
Само като разгледахме кода, беше трудно да разберем точния алгоритъм за изчисляване на HMAC. И така, реших да възстановя приложението със символи за отстраняване на грешки, за да разбера как точно работи приложението. Използвал съм инструмент, наречен apktool https://code.google.com/p/android-apktool/ за да разглобите байт кода на Dalvik с помощта на smali https://code.google.com/p/smali/ . Следвах ръководството в https://code.google.com/p/android-apktool/wiki/SmaliDebugging
След като изградите apk, трябва да го подпишете и инсталирате на вашето устройство. Тъй като нямах устройство с Android, използвах емулатора, който се доставя с Android SDK. С малко хранене с лъжица ето как го правите:
jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android androiddebugkey jarsigner -verify -verbose -certs zipalign -v 4
Използвах вграден емулатор на Android, който се доставя с sdk и виртуално изображение Atom x86 с активиран HAXM, за да гарантира, че работи гладко.
tools/emulator -avd mydroid -no-boot-anim -cpu-delay 0
Ето едно хубаво ръководство за това как да настроите виртуален образ: http://jolicode.com/blog/speed-up-your-android-emulator
Уверете се, че виждате линията HAX работи и емулаторът работи в бърз virt режим при стартиране на емулатор, за да се уверите, че имате активиран HAXM.
След това инсталирах apk в емулатора и стартирах приложението. Следвайки ръководството за apktool, използвах отдалечен дебъгер на IntelliJ IDEA, за да се свържа с емулатора и да задам някои точки на прекъсване на линиите:
Играейки с приложението за малко, успях да разбера, че частният ключ, използван за инициализация RetrofitHttpClient
се използва за изчисляване на HMAC на подпис на заявка за вход. В отговор на POST за влизане получавате потребителско име и accessToken (X-Access-Token
). Токенът за достъп се използва за упълномощаване на всички следващи заявки. HMAC за всички заявки за вход след влизане е конструиран по същия начин като заявката за вход, с изключение на това, че ключът е съставен чрез добавяне .
към оригиналния частен ключ.
След като бъдете упълномощени, приложението изпраща следната заявка:
POST https://hapi.couchsurfing.com/api/v2/users/1003669205/registerDevice ← 200 application/json
Тъй като успях да приспада емпирично, тази заявка не е задължителна за удостоверяване. Бонус точки, ако разберете за какво се използва!
След като се удостоверите, можете да изпратите заявка за извличане на вашия (или потребителски профил на някой друг), по следния начин:
GET https://hapi.couchsurfing.com/api/v2/users/1003669205 ← 200 application/json
Не навлизах много в подробности, но забелязах, че даден профил се актуализира с заявка за PUT. За удоволствие се опитах да актуализирам друг профил със същата заявка - той не беше упълномощен, така че очевидно са внедрени основите за сигурност.
Написах прост скрипт на Python, за да вляза с вашите идентификационни данни couchsurfing.com и да получа вашия потребителски профил: https://gist.github.com/nderkach/899281d7e6dd0d497533 . Ето обвивката на Python за API: https://github.com/nderkach/couchsurfing-python с пакет, наличен в хранилището на pypi (pip install couchsurfing).
Не съм сигурен какво точно ще направя този път с API. HTML кодът в потребителските профили вече не е разрешен, така че ще трябва да измисля различен подход към стария проблем. Ще продължа да разработвам и усъвършенствам обвивката на API на python, ако има търсене за нея, и приемайки, че couchsurfing.com няма да създаде твърде много проблеми. Не изследвах API твърде много и просто го тествах за някои основни уязвимости. Изглежда достатъчно сигурно, но би било интересно да разберете дали можете да получите достъп до данните, които не са достъпни чрез уебсайта. Така или иначе, сега можете да използвате обратното ми софтуерно инженерство, за да създадете алтернативен клиент за Windows Phone, Pebble или вашия интелигентен диван.
Има дискусия, която бих искал да открия - защо да не публикувате своя API и да го направите публичен? Дори и да не успях да хакна API, пак щеше да е възможно изстъргването на уебсайта. Би било по-бавно и по-трудно за поддръжка, но със сигурност те биха предпочели потребителите да използват API, а не уеб скрепер. Наличието на приложните програмни интерфейси (API) би позволило на разработчици на трети страни да подобрят продукта на компанията и да изградят около него услуга с добавена стойност. Може да се направи аргумент, че би било по-скъпо да се поддържа публичен API, а не частен; но отново, предимствата на вашите услуги за изграждане на общността върху вашия продукт биха надхвърлили разходите за поддръжка на API.
Възможно ли е напълно да се предотврати използването на частен API от клиенти на трети страни? Не мисля така. Използването на SSL закрепване би предотвратило подсмърчането в заявки за API, използвайки проста прозрачна прокси техника, както е описано по-рано. В крайна сметка, дори ако замазвате двоичния файл, мотивиран хакер с някои ресурси и време винаги ще може да извърши обратно проектиране на двоичния файл на приложението и да получи частния ключ / сертификат. Мисля, че предположението, че клиентската крайна точка е защитена, по своята същност е погрешно. An ПОЖАР клиентът е слабо място.
Като поддържа API частен, компанията основно предава съобщение за недоверие на своите потребители. Със сигурност можете да опитате да защитите частния си API още повече. Не бихте ли предпочели да внедрите основна защита за API, за да предотвратите злонамерена употреба; и вместо това фокусирайте ресурсите си върху подобряване на софтуера, за да осигурите по-добро потребителско изживяване?
Couchsurfing, доста моля, със захар отгоре, отворете API.