mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
141 lines
10 KiB
Markdown
141 lines
10 KiB
Markdown
# Time Namespace
|
||
|
||
{{#include ../../../../banners/hacktricks-training.md}}
|
||
|
||
## Basic Information
|
||
|
||
Часовий простір у Linux дозволяє використовувати зсуви для системних монотонних і завантажувальних годинників для кожного простору. Він зазвичай використовується в контейнерах Linux для зміни дати/часу всередині контейнера та коригування годинників після відновлення з контрольної точки або знімка.
|
||
|
||
## Lab:
|
||
|
||
### Create different Namespaces
|
||
|
||
#### CLI
|
||
```bash
|
||
sudo unshare -T [--mount-proc] /bin/bash
|
||
```
|
||
Монтування нової інстанції файлової системи `/proc`, якщо ви використовуєте параметр `--mount-proc`, забезпечує, що новий простір монтування має **точний та ізольований вигляд інформації про процеси, специфічної для цього простору**.
|
||
|
||
<details>
|
||
|
||
<summary>Помилка: bash: fork: Не вдалося виділити пам'ять</summary>
|
||
|
||
Коли `unshare` виконується без параметра `-f`, виникає помилка через те, як Linux обробляє нові PID (ідентифікатори процесів) простори. Основні деталі та рішення наведені нижче:
|
||
|
||
1. **Пояснення проблеми**:
|
||
|
||
- Ядро Linux дозволяє процесу створювати нові простори за допомогою системного виклику `unshare`. Однак процес, який ініціює створення нового PID простору (який називається "процесом unshare"), не входить до нового простору; лише його дочірні процеси входять.
|
||
- Виконання `%unshare -p /bin/bash%` запускає `/bin/bash` в тому ж процесі, що й `unshare`. Відповідно, `/bin/bash` та його дочірні процеси знаходяться в оригінальному PID просторі.
|
||
- Перший дочірній процес `/bin/bash` у новому просторі стає PID 1. Коли цей процес завершується, це викликає очищення простору, якщо немає інших процесів, оскільки PID 1 має особливу роль усиновлення сирітських процесів. Ядро Linux тоді вимкне виділення PID у цьому просторі.
|
||
|
||
2. **Наслідок**:
|
||
|
||
- Вихід PID 1 у новому просторі призводить до очищення прапора `PIDNS_HASH_ADDING`. Це призводить до того, що функція `alloc_pid` не може виділити новий PID при створенні нового процесу, що викликає помилку "Не вдалося виділити пам'ять".
|
||
|
||
3. **Рішення**:
|
||
- Проблему можна вирішити, використовуючи параметр `-f` з `unshare`. Цей параметр змушує `unshare` створити новий процес після створення нового PID простору.
|
||
- Виконання `%unshare -fp /bin/bash%` забезпечує, що команда `unshare` сама стає PID 1 у новому просторі. `/bin/bash` та його дочірні процеси тоді безпечно містяться в цьому новому просторі, запобігаючи передчасному виходу PID 1 та дозволяючи нормальне виділення PID.
|
||
|
||
Забезпечивши, що `unshare` виконується з прапором `-f`, новий PID простір правильно підтримується, що дозволяє `/bin/bash` та його підпроцесам працювати без виникнення помилки виділення пам'яті.
|
||
|
||
</details>
|
||
|
||
#### Docker
|
||
```bash
|
||
docker run -ti --name ubuntu1 -v /usr:/ubuntu1 ubuntu bash
|
||
```
|
||
### Перевірте, в якому просторі імен знаходиться ваш процес
|
||
```bash
|
||
ls -l /proc/self/ns/time
|
||
lrwxrwxrwx 1 root root 0 Apr 4 21:16 /proc/self/ns/time -> 'time:[4026531834]'
|
||
```
|
||
### Знайти всі простори часу
|
||
```bash
|
||
sudo find /proc -maxdepth 3 -type l -name time -exec readlink {} \; 2>/dev/null | sort -u
|
||
# Find the processes with an specific namespace
|
||
sudo find /proc -maxdepth 3 -type l -name time -exec ls -l {} \; 2>/dev/null | grep <ns-number>
|
||
```
|
||
### Увійти в простір часу
|
||
```bash
|
||
nsenter -T TARGET_PID --pid /bin/bash
|
||
```
|
||
## Маніпулювання часовими зсувами
|
||
|
||
Починаючи з Linux 5.6, два годинники можуть бути віртуалізовані для кожного простору часу:
|
||
|
||
* `CLOCK_MONOTONIC`
|
||
* `CLOCK_BOOTTIME`
|
||
|
||
Їхні делти на кожен простір часу відкриті (і можуть бути змінені) через файл `/proc/<PID>/timens_offsets`:
|
||
```
|
||
$ sudo unshare -Tr --mount-proc bash # -T creates a new timens, -r drops capabilities
|
||
$ cat /proc/$$/timens_offsets
|
||
monotonic 0
|
||
boottime 0
|
||
```
|
||
Файл містить два рядки – по одному для кожного годинника – з зсувом у **наносекундах**. Процеси, які мають **CAP_SYS_TIME** _в часовому просторі_, можуть змінювати значення:
|
||
```
|
||
# advance CLOCK_MONOTONIC by two days (172 800 s)
|
||
echo "monotonic 172800000000000" > /proc/$$/timens_offsets
|
||
# verify
|
||
$ cat /proc/$$/uptime # first column uses CLOCK_MONOTONIC
|
||
172801.37 13.57
|
||
```
|
||
Якщо вам потрібно, щоб настінний годинник (`CLOCK_REALTIME`) також змінювався, вам все ще потрібно покладатися на класичні механізми (`date`, `hwclock`, `chronyd`, …); він **не** є просторовим.
|
||
|
||
### `unshare(1)` допоміжні прапори (util-linux ≥ 2.38)
|
||
```
|
||
sudo unshare -T \
|
||
--monotonic="+24h" \
|
||
--boottime="+7d" \
|
||
--mount-proc \
|
||
bash
|
||
```
|
||
Довгі параметри автоматично записують обрані дельти в `timens_offsets` одразу після створення простору імен, що заощаджує ручне `echo`.
|
||
|
||
---
|
||
|
||
## Підтримка OCI та Runtime
|
||
|
||
* **OCI Runtime Specification v1.1** (листопад 2023) додала спеціальний тип простору імен `time` та поле `linux.timeOffsets`, щоб контейнерні движки могли запитувати віртуалізацію часу у переносимий спосіб.
|
||
* **runc >= 1.2.0** реалізує цю частину специфікації. Мінімальний фрагмент `config.json` виглядає так:
|
||
```json
|
||
{
|
||
"linux": {
|
||
"namespaces": [
|
||
{"type": "time"}
|
||
],
|
||
"timeOffsets": {
|
||
"monotonic": 86400,
|
||
"boottime": 600
|
||
}
|
||
}
|
||
}
|
||
```
|
||
Потім запустіть контейнер за допомогою `runc run <id>`.
|
||
|
||
> ЗАУВАЖТЕ: runc **1.2.6** (лютий 2025) виправив помилку "exec into container with private timens", яка могла призвести до зависання та потенційного DoS. Переконайтеся, що ви використовуєте версію ≥ 1.2.6 у виробництві.
|
||
|
||
---
|
||
|
||
## Розгляди безпеки
|
||
|
||
1. **Необхідна можливість** – Процес потребує **CAP_SYS_TIME** всередині свого простору імен користувача/часу, щоб змінити зсуви. Відмова від цієї можливості в контейнері (за замовчуванням у Docker та Kubernetes) запобігає маніпуляціям.
|
||
2. **Без змін годинника** – Оскільки `CLOCK_REALTIME` спільний з хостом, зловмисники не можуть підробити терміни дії сертифікатів, терміни дії JWT тощо лише за допомогою timens.
|
||
3. **Уникнення журналювання/виявлення** – Програмне забезпечення, яке покладається на `CLOCK_MONOTONIC` (наприклад, обмежувачі швидкості на основі часу безперервної роботи), може бути заплутаним, якщо користувач простору імен регулює зсув. Віддавайте перевагу `CLOCK_REALTIME` для часових міток, що мають значення для безпеки.
|
||
4. **Поверхня атаки ядра** – Навіть якщо `CAP_SYS_TIME` видалено, код ядра залишається доступним; підтримуйте хост в актуальному стані. Linux 5.6 → 5.12 отримав кілька виправлень помилок timens (NULL-deref, проблеми з підписом).
|
||
|
||
### Контрольний список посилення безпеки
|
||
|
||
* Відмовтеся від `CAP_SYS_TIME` у вашому профілі за замовчуванням контейнерного виконання.
|
||
* Підтримуйте виконання в актуальному стані (runc ≥ 1.2.6, crun ≥ 1.12).
|
||
* Закріпіть util-linux ≥ 2.38, якщо ви покладаєтеся на допоміжні програми `--monotonic/--boottime`.
|
||
* Аудит програмного забезпечення в контейнері, яке читає **uptime** або **CLOCK_MONOTONIC** для логіки, критично важливої для безпеки.
|
||
|
||
## Посилання
|
||
|
||
* man7.org – Сторінка посібника по просторах імен часу: <https://man7.org/linux/man-pages/man7/time_namespaces.7.html>
|
||
* Блог OCI – "OCI v1.1: нові простори імен часу та RDT" (15 листопада 2023): <https://opencontainers.org/blog/2023/11/15/oci-spec-v1.1>
|
||
|
||
{{#include ../../../../banners/hacktricks-training.md}}
|