blog.ferling.eu by Benedikt Ferling


 

exploit-exercises.com/nebula -- Level 04

Problem

  1. Fundamental misinterpretation of how the filesystem works!

The programm assumes, that one may not access a file with the substring “token” if it checks for the substring and terminates on success. Internally the filesystem works with inodes, not names!

Exploit

The sourcecode reveals, that the filename may not include the substring token – thats all. Create a symlink(without the substring ‘token’) that points to the token. Then pass the link as parameter to the program.

1
2
ln -s /home/flag04/token /tmp/tok
/home/flag04/flag04 /tmp/tok

One can now login with the credentials and execute getflag.

Lesson / How to fix

  1. Do not use string-comparision in order to compare the files identity!

In order to fix this problem one has to do some work.

  1. Use filedescriptors in order to prevent TOCTOU(see level 10 for more details).
  2. Check file permissions

Assuming, that the caller should only be prevented to read the token, this is a possible fix(otherwise the stickybit is pointless). Note that the stickybit in general is bad practice. There are very few cases in which the stickybit is usefull, i.e. /bin/passwd.

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
int main(int argc, char **argv, char **envp)
{
  struct stat st, st_token;
  char buf[1024];
  int fd_token, fd, rc;

  if(argc == 1) {
      printf("%s [file to read]\n", argv[0]);
      exit(EXIT_FAILURE);
  }

  if ((fd_token = open("/home/flag04/token", O_RDONLY)) == -1){
    fprintf(stderr, "Cannot open file");
    exit(EXIT_FAILURE);
  }
  if (fstat(fd_token, &st_token) < 0) exit(EXIT_FAILURE);

  if ((fd = open(argv[1], O_RDONLY)) == -1) {
    fprintf(stderr, "Cannot open file\n");
    exit(EXIT_FAILURE);
  }
  if (fstat(fd, &st) < 0) exit(EXIT_FAILURE);

  if ((st.st_dev == st_token.st_dev) && (st.st_ino == st_token.st_ino)) {
    fprintf(stderr, "You shall not read the token\n");
    exit(EXIT_FAILURE);
  }

  rc = read(fd, buf, sizeof(buf));

  if(rc == -1) {
      err(EXIT_FAILURE, "Unable to read fd %d", fd);
  }

  write(1, buf, rc);
}

other levels…

00 01 02 03 04 05 06 07 08 09
10 11 12 13 14 15 16 17 18 19