11 KiB
Raw Blame History

Χρονικό Namespace

{{#include ../../../../banners/hacktricks-training.md}}

Βασικές Πληροφορίες

Το χρονικό namespace στο Linux επιτρέπει για ανά namespace offsets στους συστήματος μονοτονικούς και χρόνους εκκίνησης ρολόγια. Χρησιμοποιείται συνήθως σε κοντέινερ Linux για να αλλάξει την ημερομηνία/ώρα εντός ενός κοντέινερ και να ρυθμίσει τα ρολόγια μετά την αποκατάσταση από ένα checkpoint ή snapshot.

Εργαστήριο:

Δημιουργία διαφορετικών Namespaces

CLI

sudo unshare -T [--mount-proc] /bin/bash

Με την τοποθέτηση μιας νέας παρουσίας του συστήματος αρχείων /proc αν χρησιμοποιήσετε την παράμετρο --mount-proc, διασφαλίζετε ότι το νέο namespace τοποθέτησης έχει μια ακριβή και απομονωμένη άποψη των πληροφοριών διαδικασίας που είναι συγκεκριμένες για αυτό το namespace.

Σφάλμα: bash: fork: Cannot allocate memory

Όταν εκτελείται το unshare χωρίς την επιλογή -f, προκύπτει ένα σφάλμα λόγω του τρόπου που το Linux χειρίζεται τα νέα PID (Process ID) namespaces. Οι βασικές λεπτομέρειες και η λύση περιγράφονται παρακάτω:

  1. Εξήγηση Προβλήματος:
  • Ο πυρήνας του Linux επιτρέπει σε μια διαδικασία να δημιουργεί νέα namespaces χρησιμοποιώντας την κλήση συστήματος unshare. Ωστόσο, η διαδικασία που ξεκινά τη δημιουργία ενός νέου PID namespace (αναφερόμενη ως η διαδικασία "unshare") δεν εισέρχεται στο νέο namespace; μόνο οι παιδικές της διαδικασίες το κάνουν.
  • Η εκτέλεση %unshare -p /bin/bash% ξεκινά το /bin/bash στην ίδια διαδικασία με το unshare. Ως εκ τούτου, το /bin/bash και οι παιδικές του διαδικασίες βρίσκονται στο αρχικό PID namespace.
  • Η πρώτη παιδική διαδικασία του /bin/bash στο νέο namespace γίνεται PID 1. Όταν αυτή η διαδικασία τερματίσει, ενεργοποιεί την καθαριότητα του namespace αν δεν υπάρχουν άλλες διαδικασίες, καθώς το PID 1 έχει τον ειδικό ρόλο της υιοθέτησης ορφανών διαδικασιών. Ο πυρήνας του Linux θα απενεργοποιήσει στη συνέχεια την κατανομή PID σε αυτό το namespace.
  1. Συνέπεια:
  • Η έξοδος του PID 1 σε ένα νέο namespace οδηγεί στον καθαρισμό της σημαίας PIDNS_HASH_ADDING. Αυτό έχει ως αποτέλεσμα τη αποτυχία της συνάρτησης alloc_pid να κατανοήσει ένα νέο PID κατά τη δημιουργία μιας νέας διαδικασίας, παράγοντας το σφάλμα "Cannot allocate memory".
  1. Λύση:
  • Το πρόβλημα μπορεί να επιλυθεί χρησιμοποιώντας την επιλογή -f με το unshare. Αυτή η επιλογή κάνει το unshare να δημιουργήσει μια νέα διαδικασία μετά τη δημιουργία του νέου PID namespace.
  • Η εκτέλεση %unshare -fp /bin/bash% διασφαλίζει ότι η εντολή unshare γίνεται PID 1 στο νέο namespace. Το /bin/bash και οι παιδικές του διαδικασίες είναι στη συνέχεια ασφαλώς περιεχόμενες μέσα σε αυτό το νέο namespace, αποτρέποντας την πρόωρη έξοδο του PID 1 και επιτρέποντας την κανονική κατανομή PID.

Διασφαλίζοντας ότι το unshare εκτελείται με την επιλογή -f, το νέο PID namespace διατηρείται σωστά, επιτρέποντας στο /bin/bash και τις υπο-διαδικασίες του να λειτουργούν χωρίς να αντιμετωπίζουν το σφάλμα κατανομής μνήμης.

Docker

docker run -ti --name ubuntu1 -v /usr:/ubuntu1 ubuntu bash

Έλεγχος σε ποιο namespace βρίσκεται η διαδικασία σας

ls -l /proc/self/ns/time
lrwxrwxrwx 1 root root 0 Apr  4 21:16 /proc/self/ns/time -> 'time:[4026531834]'

Βρείτε όλα τα Time namespaces

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>

Είσοδος σε ένα Χρονικό namespace

nsenter -T TARGET_PID --pid /bin/bash

Manipulating Time Offsets

Αρχίζοντας με το Linux 5.6, δύο ρολόγια μπορούν να εικονικοποιηθούν ανά χρονικό namespace:

  • CLOCK_MONOTONIC
  • CLOCK_BOOTTIME

Οι διαφορές τους ανά namespace εκτίθενται (και μπορούν να τροποποιηθούν) μέσω του αρχείου /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 στο χρονικό namespace μπορούν να αλλάξουν την τιμή:

# 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) helper flags (util-linux ≥ 2.38)

sudo unshare -T \
--monotonic="+24h"  \
--boottime="+7d"    \
--mount-proc         \
bash

Οι μακροχρόνιες επιλογές γράφουν αυτόματα τις επιλεγμένες δέλτα στο timens_offsets αμέσως μετά τη δημιουργία του namespace, αποθηκεύοντας μια χειροκίνητη echo.


OCI & Υποστήριξη Runtime

  • Η OCI Runtime Specification v1.1 (Νοέμβριος 2023) πρόσθεσε έναν ειδικό τύπο namespace time και το πεδίο linux.timeOffsets ώστε οι μηχανές κοντέινερ να μπορούν να ζητούν εικονικοποίηση χρόνου με φορητό τρόπο.
  • runc >= 1.2.0 υλοποιεί αυτό το μέρος της προδιαγραφής. Ένα ελάχιστο τμήμα config.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 μέσα στο namespace χρήστη/χρόνου της για να αλλάξει τις μετατοπίσεις. Η αφαίρεση αυτής της ικανότητας στο κοντέινερ (προεπιλογή στο Docker & Kubernetes) αποτρέπει την παραχάραξη.
  2. Καμία αλλαγή ρολογιού Επειδή το CLOCK_REALTIME μοιράζεται με τον κεντρικό υπολογιστή, οι επιτιθέμενοι δεν μπορούν να παραποιήσουν τις διάρκειες πιστοποιητικών, την λήξη JWT, κ.λπ. μέσω timens μόνο.
  3. Αποφυγή ανίχνευσης / καταγραφής Λογισμικό που βασίζεται στο CLOCK_MONOTONIC (π.χ. περιοριστές ρυθμού βασισμένοι σε χρόνο λειτουργίας) μπορεί να μπερδευτεί αν ο χρήστης του namespace ρυθμίσει τη μετατόπιση. Προτιμήστε το CLOCK_REALTIME για χρονικές σφραγίδες που σχετίζονται με την ασφάλεια.
  4. Επιφάνεια επίθεσης πυρήνα Ακόμα και με την αφαίρεση του CAP_SYS_TIME, ο κώδικας του πυρήνα παραμένει προσβάσιμος. Κρατήστε τον κεντρικό υπολογιστή ενημερωμένο. Το Linux 5.6 → 5.12 έλαβε πολλές διορθώσεις σφαλμάτων timens (NULL-deref, ζητήματα υπογραφής).

Λίστα ελέγχου σκληροποίησης

  • Αφαιρέστε το CAP_SYS_TIME στο προεπιλεγμένο προφίλ runtime του κοντέινερ σας.
  • Κρατήστε τα runtimes ενημερωμένα (runc ≥ 1.2.6, crun ≥ 1.12).
  • Κλειδώστε το util-linux ≥ 2.38 αν βασίζεστε στους βοηθούς --monotonic/--boottime.
  • Ελέγξτε το λογισμικό εντός του κοντέινερ που διαβάζει uptime ή CLOCK_MONOTONIC για κρίσιμη λογική ασφαλείας.

Αναφορές

{{#include ../../../../banners/hacktricks-training.md}}