root bez roota

Zima przestała być przytulna i puszysta. Dla odmiany stała się wyjąca i siekąca po oczach. Brnąc przez zaspy, sapiąc i walcząc z kondycją wywinąłem orła, a obserwując z parteru wirujących nade mną braci Mroczków postanowiłem, że zamiast opiewania piękna zimy napiszę dziś o czymś bliższym pozycji, w której aktualnie się znalazłem, mianowicie o libcap, czyli pozbywaniu się praw roota z serwisów uruchamianych z roota i nie tylko.

Na przykładzie Gentoo.

Ponieważ openrc w Gentoo nie posiada wsparcia dla libcap'a musimy posłużyć się sporządzoną na prędce protezą. I tak przykładowe apache2 serwujące tę stronę można odpalić za pomocą prostego skryptu, ktory możemy sobie wrzucić, załóżmy, do /root/sbin/

#!/bin/bash

source /etc/conf.d/apache2

stop() {
/usr/sbin/apache2 -k stop
# sprawdzamy ponieważ nie zawsze chce wyłączyć wszystkie wątki
ps -C apache2 >/dev/null 2>&1
if [ "$?" == 0 ]; then
        kill -9 `ps -C apache2|awk '{print $1}'|grep -v PID`  >/dev/null 2>&1
fi
}

start() {
capsh --drop="\
cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,\
cap_kill,cap_setpcap,cap_linux_immutable,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\
"
-- -c "/usr/sbin/apache2 ${APACHE2_OPTS} -k start"
}

case $1 in
        start)
        start ;;
        stop)
        stop ;;
*) ;;
esac

Ponieważ apache2 posiada w systemie własnego użytkownika i grupę, oraz binduje się na porcie poniżej 1024 dropujemy mu wszystko poza

cap_setgid
cap_setuid
cap_net_bind_service

restartujemy usługę i sprawdzamy:
getpcaps `pidof apache2`
Capabilities for `8620': =
Capabilities for `8619'
: =
Capabilities for `8618': =
Capabilities for `8617'
: =
Capabilities for `8616': =
Capabilities for `8615'
: =
Capabilities for `8601': =
Capabilities for `8600'
: =
Capabilities for `8599': =
Capabilities for `8598'
: =
Capabilities for `8597': =ep cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,
cap_kill,cap_setpcap,cap_linux_immutable,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-ep

zapis taki jest mało czytelny, inaczej można to zrobić tak:
for i in $(ps -C apache2|awk '{print $1}'|grep -v PID); do grep CapBnd /proc/$i/status; done
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0
CapBnd: fffffffc000004c0

capsh --decode=c000004c0
0x0000000c000004c0=cap_setgid,cap_setuid,cap_net_bind_service,34,35

czyli proces ma możliwe jedynie cap_setgid, cap_setuid, cap_net_bind_service; 34 i 35 to kolejne, nie wyzerowane, ale nieużywane obecnie bity
Listę wszystkich capabilities można uzyskać przy pomocy komendy
capsh --print
Current: =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_context
Securebits: 00/0x0
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
uid=0

Zerowanie możliwości następuje w tym rozwiązaniu w zbiorze bound (oraz inherited).

Oznacza to, że zarówno dany proces jak i wszelkie jego potomstwo będzie ich już pozbawione. Uruchomienie sshd bez któregoś spowoduje, że proces ten, procesy usera, ktory się zaloguje, a nawet powłoka roota uzyskana przez su, nie odzyska już danego capability. Zdropowanie cap_sys_admin dla sshd będzie wręcz oznaczało, że zostawimy sobie możliwość administrowania maszyną tylko lokalnie. Należy zatem pozostawić największy możliwy potrzebny zbiór, lub szukać innych rozwiązań. Może być to postrzegane jako plus lub minus w zależności od punktu widzenia.

Usługi uruchamiane ze zwykłych użytkowników potrzebują conajmniej cap_setgid,cap_setuid do przełączenia się na ów uid.