141 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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}}