Ред-флаг
Категория: Linux / Logic / Bash
Сложность: Easy
Суть: ошибка бизнес-логики при присоединении к тир-листу + некорректная проверка прав доступа к вложенным файлам.
Описание задачи
Дан SSH-доступ к консольному сервису на bash, который реализует тир-листы через whiptail.
- выбрать имя пользователя;
- просматривать публичные тир-листы;
- создавать свои тир-листы;
- присоединяться к чужим;
- добавлять/удалять тиры и элементы;
- просматривать элементы, если они не ограничены владельцем.
Флаг спрятан в элементе flag, который отображается как:
text[LOCKED] flag
При попытке открыть его сервис сообщает:
textДоступ ограничен владельцем тир-листа.
Анализ исходного кода
Главные данные хранятся в директории:
bashTIERLISTS_DIR="/data/tierlists"
Каждый тир-лист — это директория с .meta:
textowner=<user> restricted=<file1,file2,...> collaborators=<user1,user2,...>
bashis_owner() {
local owner
owner=$(meta_get "$1" "owner") || return 1
[[ "$owner" == "$CURRENT_USER" ]]
}bashis_restricted() {
local restricted
restricted=$(meta_get "$1" "restricted") || return 1
[[ -z "$restricted" ]] && return 1
local IFS=','
for r in $restricted; do
[[ "$r" == "$2" ]] && return 0
done
return 1
}bashcan_read_file() {
if is_restricted "$1" "$2"; then
is_owner "$1"
else
return 0
fi
}Если имя файла есть в restricted, читать его может только владелец тир-листа.
Уязвимость №1: небезопасный join
При присоединении к чужому тир-листу вызывается функция:
bashjoin_tierlist_by_name() {
local tl_name="$1"
local new_name="${tl_name}__with__${CURRENT_USER}"
mv "$TIERLISTS_DIR/$tl_name" "$TIERLISTS_DIR/$new_name"
local collabs
collabs=$(meta_get "$TIERLISTS_DIR/$new_name" "collaborators") || collabs=""
if [[ -z "$collabs" ]]; then
meta_set "$TIERLISTS_DIR/$new_name" "collaborators" "$CURRENT_USER"
else
meta_set "$TIERLISTS_DIR/$new_name" "collaborators" "${collabs},${CURRENT_USER}"
fi
msg "Вы присоединились к тир-листу."
}bashmv "$TIERLISTS_DIR/$tl_name" "$TIERLISTS_DIR/$new_name"
Если директория назначения уже существует, mv не перезаписывает её, а перемещает исходную директорию внутрь неё.
text/data/tierlists/gorpcore_redflags__with__pwn2
text/data/tierlists/gorpcore_redflags
bashmv /data/tierlists/gorpcore_redflags /data/tierlists/gorpcore_redflags__with__pwn2
text/data/tierlists/gorpcore_redflags__with__pwn2/
├── .meta
└── gorpcore_redflags/
├── .meta
└── tier_d/
└── flag.txtВнешняя директория остаётся нашей, а чужой тир-лист оказывается вложенным внутрь.
Уязвимость №2: рекурсивный поиск тиров
При отображении тир-листа сервис ищет директории с тирами так:
bashfind "$tl_dir" -type d -name 'tier_*' -print0 2>/dev/null | sort -z
Используется рекурсивный поиск. После предыдущего бага сервис начинает находить не только наши тиры, но и тиры вложенного чужого тир-листа:
textgorpcore_redflags/tier_d
Элементы из этих вложенных директорий попадают в меню.
Уязвимость №3: проверка доступа идёт по неправильной .meta
Когда пользователь выбирает элемент, вызывается:
bashview_item "$tl_dir" "$item_path"
Внутри view_item проверка доступа выглядит так:
bashif ! can_read_file "$tl_dir" "$item_fname"; then
msg "[LOCKED] $item_name\\n\\nДоступ ограничен владельцем тир-листа."
return
fican_read_file получает именно внешний $tl_dir. То есть если мы открываем файл:
text/data/tierlists/gorpcore_redflags__with__pwn2/gorpcore_redflags/tier_d/flag.txt
проверка ограничений всё равно смотрит в:
text/data/tierlists/gorpcore_redflags__with__pwn2/.meta
textowner=pwn2 restricted= collaborators=
Поскольку restricted пустой, файл считается доступным — даже если во вложенном админском тир-листе он был restricted.
Эксплуатация
В публичных тир-листах был найден интересный список:
textgorpcore_redflags @admin
В нём присутствовал закрытый элемент:
text[LOCKED] flag
Краткая последовательность действий:
- Подключиться по SSH.
- Войти под новым пользователем, например
pwn2. - Создать тир-лист с именем
gorpcore_redflags__with__pwn2.
Это имя важно — функцияjoin_tierlist_by_nameпри присоединении строит имя новой директории именно так:"${tl_name}__with__${CURRENT_USER}". - Открыть публичные тир-листы.
- Выбрать
gorpcore_redflags @admin. - Нажать «Присоединиться».
Вместо нормального переименования произойдёт перенос админского тир-листа внутрь директории-ловушки. - Перейти в «Мои тир-листы» и открыть
gorpcore_redflags__with__pwn2.
Если всё сделано правильно, заголовок покажет владельца внешнего тир-листа:textgorpcore_redflags__with__pwn2 [@pwn2]А внутри станут видны тиры и элементы из вложенногоgorpcore_redflags. - Открыть элемент
[LOCKED] flag.
Сервис откроет его содержимое, потому что проверяет ограничения по нашей внешней.meta, а не по.metaисходного админского тир-листа. - Получить флаг.
Корневая причина
В задаче объединились три логические ошибки:
join_tierlist_by_nameиспользуетmvбез проверки существования директории назначения.view_tierlistрекурсивно ищетtier_*внутри всего дерева тир-листа.view_itemпроверяет доступ к файлу по.metaвнешней директории, а не по.metaреального тир-листа, которому принадлежит файл.
Вместе это позволяет поместить чужой тир-лист внутрь своего и обойти restricted-проверку.