我正在尝试将命令注入到我拥有的进程中 - 具体来说,我目前正在尝试将 ptraced 进程的文件描述符传递给 ptracing 进程。我成功地创建了一个 unix 域套接字并连接它(据我所见),如下所示:
#define stack_pointer(regs) regs.uregs[13]
#define jumpback_pointer(regs) regs.uregs[14]
#define ip(regs) regs.uregs[15]
#define arg0(regs) regs.uregs[0]
#define arg1(regs) regs.uregs[1]
#define arg2(regs) regs.uregs[2]
#define arg3(regs) regs.uregs[3]
#define ROUND_SIZE_TO_WORD(n) ((((n) - 1) / 4 + 1) * 4)
// create a socket on the other side
jumpback_pointer(regs) = 0; // please crash after completion
ip(regs) = libc_offset + 0x0000cfd4/*socket*/;
arg0(regs) = PF_UNIX;
arg1(regs) = SOCK_STREAM;
arg2(regs) = 0;
ptrace(PTRACE_SETREGS, pid, NULL, ®s);
assert(ptrace(PTRACE_CONT, pid, NULL, 0) == 0);
int waitpid_result;
assert(waitpid(pid, &waitpid_result, 0) == pid);
assert(WIFSTOPPED(waitpid_result));
printf("child was killed with signal %i, we expected it to be killed with SIGSEGV (%i)\n", WSTOPSIG(waitpid_result), SIGSEGV);
struct pt_regs after_socket_regs;
assert(ptrace(PTRACE_GETREGS, pid, NULL, &after_socket_regs) == 0);
int remote_socket_id = arg0(after_socket_regs);
printf("remote socket created, id=%i\n", remote_socket_id);
// create a socket on our side and bind it
int attacker_unix_socket = socket(PF_UNIX, SOCK_STREAM, 0);
printf("local socket created, id=%i\n", attacker_unix_socket);
int sockaddr_un_size_r = ROUND_SIZE_TO_WORD(sizeof(struct sockaddr_un)); // round up to words
if ((sockaddr_un_size_r / 4) * 4 != sockaddr_un_size_r) {
puts("struct sockaddr_un not properly sized");
exit(1);
}
struct sockaddr_un *sockaddr = malloc(sockaddr_un_size_r);
sockaddr->sun_family = AF_UNIX;
strcpy(sockaddr->sun_path, "/tmp/ownage");
unlink(sockaddr->sun_path);
assert(bind(attacker_unix_socket, (struct sockaddr *) sockaddr, sizeof(struct sockaddr_un)) == 0);
assert(listen(attacker_unix_socket, 5) == 0);
// connect the victim socket
int child_pid;
if ((child_pid = fork())) {
// the parent process takes care of running commands on the victim
// side - the child isn't allowed to do this
regs = orig_regs;
stack_pointer(regs) -= sockaddr_un_size_r; // prepare stack push
masspoke(pid, sockaddr, stack_pointer(regs), sockaddr_un_size_r); // stack push
jumpback_pointer(regs) = 0;
ip(regs) = libc_offset + 0x0000d030/*connect*/;
arg0(regs) /*sockfd*/ = remote_socket_id;
arg1(regs) /*addr*/ = stack_pointer(regs);
arg2(regs) /*addr_len*/ = sizeof(struct sockaddr_un);
assert(ptrace(PTRACE_SETREGS, pid, NULL, ®s) == 0);
assert(ptrace(PTRACE_CONT, pid, NULL, 0) == 0);
assert(waitpid(pid, NULL, 0) == pid);
struct pt_regs after_connect_regs;
assert(ptrace(PTRACE_GETREGS, pid, NULL, &after_connect_regs) == 0);
if (arg0(after_connect_regs)) {
printf("remote socket connect() failed with return value %li\n", arg0(after_connect_regs));
exit(1);
}
printf("remote socket connected\n");
但是,实际上发送带有以下代码的文件描述符(这是this的修改形式)失败了:
regs = orig_regs;
int fd = 10;
char byte = 0;
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
char buf[CMSG_SPACE (sizeof fd)];
/* send at least one char */
memset (&msg, 0, sizeof msg);
iov.iov_base = &byte;
iov.iov_len = 1;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = buf;
msg.msg_controllen = sizeof buf;
cmsg = (struct cmsghdr *) buf;
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN (sizeof fd);
/* Initialize the payload: */
memcpy (CMSG_DATA (cmsg), &fd, sizeof fd);
// copy structs over
long r_buf = stack_pointer(regs) -= ROUND_SIZE_TO_WORD(sizeof(buf));
masspoke(pid, buf, r_buf, sizeof(buf));
msg.msg_control = (char **) r_buf;
long r_byte = stack_pointer(regs) -= ROUND_SIZE_TO_WORD(sizeof(byte));
masspoke(pid, &byte, r_byte, sizeof(byte));
iov.iov_base = (char *) r_byte;
long r_iov = stack_pointer(regs) -= ROUND_SIZE_TO_WORD(sizeof(iov));
masspoke(pid, &iov, r_iov, sizeof(iov));
msg.msg_iov = (struct iovec *) r_iov;
long r_msg = stack_pointer(regs) -= ROUND_SIZE_TO_WORD(sizeof(msg));
masspoke(pid, &msg, r_msg, sizeof(msg));
// call it
jumpback_pointer(regs) = 0;
ip(regs) = libc_offset + 0x0000d188;
arg0(regs) = remote_socket_id;
arg1(regs) = r_msg;
arg2(regs) = 0;
ptrace(PTRACE_SETREGS, pid, NULL, ®s);
assert(ptrace(PTRACE_CONT, pid, NULL, 0) == 0);
assert(waitpid(pid, NULL, 0) == pid);
struct pt_regs after_sendmsg_regs;
assert(ptrace(PTRACE_GETREGS, pid, NULL, &after_sendmsg_regs) == 0);
if (arg0(after_sendmsg_regs) != iov.iov_len) {
printf("sendmsg() failed (returned %li)\n", arg0(after_sendmsg_regs));
exit(1);
}
printf("successfully called sendmsg() on the other side!\n");
sendmsg()
总是失败-1
。所以,我的问题是,有谁知道为什么这不起作用,或者至少能够告诉我如何尝试找到 errno 以获取有关错误的一些线索?我试过这个来找到errno,但PTRACE_PEEKDATA
总是失败:
void print_error(int pid, struct pt_regs regs, unsigned int libc_offset) {
// call __errno to get a pointer to the errno
jumpback_pointer(regs) = 0; // crash afterwards
ip(regs) = libc_offset + LIBC_ERRNO;
ptrace(PTRACE_SETREGS, pid, NULL, ®s);
assert(ptrace(PTRACE_CONT, pid, NULL, 0) == 0);
int waitpid_result;
assert(waitpid(pid, &waitpid_result, 0) == pid);
struct pt_regs after___errno_regs;
assert(ptrace(PTRACE_GETREGS, pid, NULL, &after___errno_regs) == 0);
long errno_fn_addr = arg0(after___errno_regs);
long child_errno = ptrace(PTRACE_PEEKDATA, pid, errno_fn_addr, NULL);
int peek_errno = errno;
printf("ERROR IN PTRACED PROCESS: %s (errno@%lx=%li), peek errno: %s\n", strerror(child_errno), errno_fn_addr, child_errno, strerror(peek_errno));
}