HTB Flight. Повышаем привилегии в Windows

HTB Flight. Повышаем привилегии в Windows

the Matrix • Cyber News

Пе­ред тобой уже во вто­рой раз обновлен­ная вер­сия цик­ла «Фун­дамен­таль­ные осно­вы хакерс­тва». В 2018 году Юрий Язев изме­нил текст Кри­са Кас­пер­ски для соот­ветс­твия новым вер­сиям Windows и Visual Studio, а теперь внес прав­ки с уче­том отладки прог­рамм для 64-раз­рядной архи­тек­туры.

Чи­тай так­же улуч­шенные вер­сии прош­лых ста­тей цик­ла:

Все новые вер­сии ста­тей дос­тупны без плат­ной под­писки.

Цикл «Фун­дамен­таль­ные осно­вы хакерс­тва» со все­ми обновле­ниями опуб­ликован в виде кни­ги, ку­пить ее по выгод­ной цене ты можешь на сай­те изда­тель­ства «Солон‑пресс».

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

Ма­тери­алы к статье на GitHub

Па­роль, хра­нящий­ся в теле прог­раммы откры­тым тек­стом, — ско­рее из ряда вон выходя­щее исклю­чение, чем пра­вило. К чему услу­ги хакера, если пароль и без того виден нево­ору­жен­ным гла­зом? Поэто­му раз­работ­чики защиты вся­чес­ки пыта­ются скрыть его (о том, как имен­но они это дела­ют, мы погово­рим поз­же).

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

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

Взгля­нем еще раз на исходный текст лома­емо­го нами при­мера passCompare1.cpp:

Об­рати вни­мание — в buff чита­ется вве­ден­ный поль­зовате­лем пароль, срав­нива­ется с ори­гина­лом, затем (при неудач­ном срав­нении) зап­рашива­ется еще раз, но (!) при этом buff не очи­щает­ся! Отсю­да сле­дует, что, если пос­ле выдачи ругатель­ства «Wrong password» выз­вать отладчик и прой­тись по памяти кон­текс­тным поис­ком, мож­но обна­ружить тот завет­ный buff, а осталь­ное уже дело тех­ники!

Итак, прис­тупим (мы еще не зна­ем, во что мы ввя­зыва­емся, — но, увы, в жиз­ни все слож­нее, чем в теории). На этот раз запус­тим passCompare1.exe отдель­но от отладчи­ка. Затем под­клю­чим­ся к про­цес­су из отладчи­ка («Attach to process» в WinDbg). Обра­ти вни­мание: в окне выбора про­цес­са отоб­ража­ются все запущен­ные про­цес­сы и для каж­дого из них выводит­ся его раз­рядность в стол­бце Platform. Вво­дим любой при­шед­ший на ум пароль (нап­ример, KPNC Kaspersky++), про­пус­каем воз­мущен­ный вопль Wrong мимо ушей и в отладчи­ке нажима­ем Break (сочета­ние кла­виш Alt-Del).

Поп­робу­ем отыс­кать в памяти вве­ден­ный пароль:

Пер­вый параметр пос­ле коман­ды s — флаг -a — опре­деля­ет цель поис­ка как набор ASCII-сим­волов. Вто­рой параметр — сме­щение, по которо­му начать искать. Вооб­ще‑то начинать поиск с нулево­го сме­щения — идея глу­пая. Судя по кар­те памяти, здесь рас­положен слу­жеб­ный код и иско­мого пароля быть не может. Впро­чем, это ничему не вре­дит, и так гораз­до быс­трее, чем раз­бирать­ся, с какого адре­са заг­ружена прог­рамма и отку­да имен­но начинать поиск.

Тре­тий параметр — вер­хний пре­дел поис­ка, то есть докуда надо искать. Так как в 64-бит­ной Windows адресное прос­транс­тво про­цес­са огра­ниче­но 8 Тбайт, вер­хний лимит сос­тавля­ет 0x7FFFFFFFFFF. Пос­ледний параметр — собс­твен­но иско­мая стро­ка. Обра­ти вни­мание, что мы ищем не всю стро­ку, а толь­ко ее часть (KPNC Kaspersky++ про­тив KPNC Kaspersky). Это поз­воля­ет изба­вить­ся от лож­ных сра­баты­ваний, воз­ника­ющих из‑за ссы­лок на внут­ренние буфера.

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

На помощь при­ходит кар­та памяти — зная вла­дель­ца реги­она, которо­му при­над­лежит буфер, мож­но очень мно­гое ска­зать об этом буфере. Нас­коро набив уже зна­комую коман­ду !dh passCompare1, мы получим приб­лизитель­но сле­дующее (выб­раны све­дения толь­ко о сек­циях .data и .rdata):

За­одно опре­делим базовый адрес модуля при­ложе­ния: lmf m passCompare1 (в моем кон­крет­ном слу­чае он равен 0x7ff7d78f0000, а у тебя зна­чение, ско­рее все­го, будет дру­гим). Узна­ем, куда в памяти заг­ружена сек­ция .rdata:

И куда заг­ружена сек­ция .data:

Это гораз­до выше най­ден­ных адре­сов рас­положе­ния буферов с вве­ден­ным паролем. Сле­дова­тель­но, най­ден­ные адре­са не ука­зыва­ют в области .data и .rdata.

Ду­маем даль­ше. Адрес 0x1dcd30f2580 выходит далеко за пре­делы лома­емо­го при­ложе­ния, и вооб­ще непонят­но, чему он при­над­лежит. Почесав затылок, мы вспом­ним о такой «вкус­ности» Windows, как куча (heap). С помощью коман­ды !heap пос­мотрим, где она начина­ется:

Из это­го зак­люча­ем, что адрес 0x1dcd30f2580 явно находит­ся в куче.

Раз­бира­емся даль­ше. Пос­коль­ку стек рас­тет свер­ху вниз (то есть от стар­ших адре­сов к млад­шим), адрес 0x2f10effe30 явно находит­ся в сте­ке. Уве­рен­ность подог­рева­ет тот факт, что боль­шинс­тво прог­раммис­тов раз­меща­ет буфера в локаль­ных перемен­ных, ну а локаль­ные перемен­ные, в свою оче­редь, раз­меща­ются ком­пилято­ром в сте­ке.

Ну что, поп­робу­ем пос­тавить бряк по пер­вому адре­су?

На вто­ром зап­росе пароля сно­ва вво­дим KPNC Kaspersky++. Жмем Enter и дожида­емся сиюми­нут­ной акти­вации отладчи­ка. Бряк про­изо­шел на вто­рой из этих строк:

Смот­рим, что находит­ся в регис­тре rsi:

Впро­чем, это­го и сле­дова­ло ожи­дать. Поп­робу­ем вый­ти из текущей фун­кции по Shift-F11. И мы сно­ва попадем на эту же стро­ку. Вновь пос­мотрим содер­жимое это­го регис­тра:

Ага, один сим­вол отку­сан. Сле­дова­тель­но, мы находим­ся в срав­нива­ющей про­цеду­ре. Вый­дем из нее нажати­ем на F5, так как при нажатии на Shift-F11 мы перей­дем на сле­дующую ите­рацию перебо­ра сим­волов.

И вот мы в теле уже хорошо нам зна­комой (раз­вивай зри­тель­ную память!) про­цеду­ры срав­нения ори­гиналь­ного и вве­ден­ного поль­зовате­лем паролей. На вся­кий слу­чай для пущей убеж­деннос­ти выведем зна­чение ука­зате­лей [RDX+RCX] и RCX, что­бы узнать, что с чем срав­нива­ется:

Как раз то, что мы ищем!

Ну а осталь­ное мы уже про­ходи­ли. Записы­ваем адрес условно­го перехо­да (клю­чевую пос­ледова­тель­ность для поис­ка), с помощью све­дений из прош­лой статьи находим на носите­ле адрес инс­трук­ции, соот­ветс­тву­ющей спро­еци­рован­ной в памяти, пра­вим исполня­емый файл, и всё окей.

Итак, мы поз­накоми­лись с одним более или менее уни­вер­саль­ным спо­собом взло­ма защит, осно­ван­ных на срав­нении пароля (поз­же мы уви­дим, что он под­ходит и для защит, осно­ван­ных на регис­тра­цион­ных номерах). Его основное дос­тоинс­тво — прос­тота. А недос­татки... недос­татков у него мно­го:

Нас­тала пора раз­нооб­разить наш объ­ект взло­ма. Теперь поп­робу­ем заломить при­ложе­ние с гра­фичес­ким интерфей­сом. В качес­тве тре­ниров­ки раз­берем passCompare3. Это то же самое, что и passCompare1.exe, толь­ко с гра­фичес­ким интерфей­сом на осно­ве MFC Dialog Based App (ищи в ска­чива­емых матери­алах к статье).

Так­же обра­ти вни­мание на то, что работа с тек­стом в этом при­мере орга­низо­вана по‑дру­гому. Если рань­ше мы работа­ли с базовым типом char, то здесь исполь­зует­ся обер­тка — класс CString, что, ско­рее все­го, при взло­ме про­фес­сиональ­ных при­ложе­ний будет встре­чать­ся нам чаще. Кро­ме двух кно­пок, иду­щих в заготов­ке по умол­чанию, добавь на фор­му эле­мент Edit Control. Свя­жи его с перемен­ной m_password и соз­дай событие обра­бот­ки нажатия на кноп­ке OK. Это и будет клю­чевая про­цеду­ра при­ложе­ния, про­веря­ющая вве­ден­ный пароль на равенс­тво эта­лон­ному:

Ка­жет­ся, никаких сюр­при­зов не пред­видит­ся.

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

На самом деле одно и то же дей­ствие может быть выпол­нено все­го лишь нес­коль­кими фун­кци­ями и их перебор не зай­мет мно­го вре­мени. В час­тнос­ти, содер­жимое окна редак­тирова­ния обыч­но добыва­ется при помощи либо фун­кции GetWindowTextW (чаще все­го), либо фун­кции GetDlgItemTextW (а это зна­читель­но реже). Все вер­сии Windows NT пред­почита­ют работать с юни­кодом, поэто­му на кон­це фун­кций работы с тек­стом W (wide), а не A (ASCII).

Раз уж речь заш­ла об окнах, запус­тим наш GUI «кряк­мис» и уста­новим точ­ку оста­нова на фун­кцию GetWindowTextWbp User32!GetWindowTextW. Хотя эта фун­кция сис­темная, точ­ка оста­нова не будет гло­баль­ной и не зат­ронет все при­ложе­ния в сис­теме, а будет фун­кци­они­ровать толь­ко в кон­тек­сте дан­ного при­ложе­ния.

Вво­дим какой‑нибудь пароль (KPNC Kaspersky++, по обык­новению), нажима­ем кла­вишу Enter, и отладчик незамед­литель­но всплы­вает:

Как вид­но, мы попали в фун­кцию USER32!GetWindowTextW. Из нее надо вый­ти на более высокий уро­вень, нажав Shift-F11. Теперь мы попали в фун­кцию mfc140u!CWnd::GetWindowTextW:

Те­перь надо еще пот­расси­ровать эту фун­кцию нажати­ями Shift-F11. Наконец, мы попадем в фун­кцию, которая явля­ется обра­бот­чиком нажатия кноп­ки OK на фор­ме или Enter на кла­виату­ре:

Сей­час мы можем узнать зна­чение в регис­тре RAX:

Хо­рошо, видим вве­ден­ный пароль. Есть кон­такт! Толь­ко почему пос­ле каж­дого сим­вола сто­ит точ­ка? Думаю, ты уже догадал­ся, что она озна­чает двух­бай­товую при­роду сим­вола перед ней. Отхлеб­нув пив­ка, ква­са или лимона­да (по желанию), вспо­мина­ем, что, хоть класс CString и может работать с типами char (одно­бай­товое пред­став­ление сим­волов) и wchar_t (мно­гобай­товое пред­став­ление до четырех байт, то есть юни­код в UTF-8, -16 или -32), это зависит от нас­тро­ек ком­пилято­ра. А имен­но от того, какой сим­вол вклю­чен: MBCS — char, UNICODE — wchar_t. Чаще все­го исполь­зует­ся вто­рой набор сим­волов, так как по умол­чанию вклю­чены имен­но широкие сим­волы.

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

Об­рати вни­мание вот на этот опе­ратор из лис­тинга:

Пос­ле его выпол­нения зна­чение в регис­тре RCX будет ука­зывать на буфер с эта­лон­ным паролем:

И прав­да! Инту­иция нас не под­вела, эта­лон­ный пароль тут как тут.

Вве­ден­ная поль­зовате­лем стро­ка и эта­лон­ный пароль — как на блю­деч­ке с голубой каемоч­кой! Замеча­тель­но! Вот так, безо вся­ких лож­ных сра­баты­ваний, эле­ган­тно, быс­тро и кра­сиво, мы победи­ли защиту!

Этот спо­соб уни­вер­сален, и впос­ледс­твии мы еще не раз им вос­поль­зуем­ся. Вся соль — опре­делить клю­чевую фун­кцию защиты и пос­тавить на нее бряк. В Windows все попол­зно­вения (обра­щения к клю­чево­му фай­лу, реес­тру и про­чее) сво­дят­ся к вызову фун­кций API, перечень которых хоть и велик, но все же конечен и известен заранее.


Источник

Наши проекты:

- Кибер новости: the Matrix • Cyber News
- Хакинг: /me Hacker
👁 Пробить человека? Легко через нашего бота: Мистер Пробиватор

Report Page