Analysis of the bug =================== The seed read from the unitialized variable is not really random: it is determined by the functions that have been called before `do_exec()`. If the attacker can control the contents of the stack frame of a function called immediately before `do_exec()`, it can set the seed to a known value, essentially fixing the value of the canary. If the attacker knows the canary, it can exploit the `gets()` call to overflow the `buffer` array and overwrite the `permtd` array to grant himself any right. Attack plan =========== The idea is exploit the overflow in `gets()` to write a `G` character in the `permtd` array. This will allow the attacker to pass an arbitrary pattern to `grep` and thus read the flag, e.g., by passing the `.` wildcard, or some string known to be contained in the flag (e.g., `SNH`). To fix the value of the canary we can execute the `do_login()` function immediately before `do_exec()` (e.g., by sending `le\n`), since its `pw` array certainly overlaps the local variables of `do_exec()`. By executing the attack locally, we can easily read the computed value of the canary, with the help of the debugger. Attack implementation ===================== We run `server` in a debugger and send the `le\n` string, to cause a call to `do_login()` immediately followed by a call do `do_exec()`. We set a breakpoint in `do_exec()` and send send 79 `A`s of input. These will satisfy the `fgets()` in `do_login()`; then, the program will move to `do_exec()`. We can now verify that `canary_seed` is set to 0x41414141 and we can take note of the contents of the `canary` array after the call to `setup_canary()` (e.g., with `x/xg canary`). If we repeat these steps on the actual server, the same canary will be generated. We then want to overflow `buffer` with the following: ``` sh +---+---+---+---+---+---+---+---+ | G | S | N | H | \0| x | x | x | buffer +---+---+---+---+---+---+---+---+ | the canary | canary +---+---+---+---+---+---+---+---+ | G | | permtd +---+---+---+---+---+---+---+---+ ``` I.e., we try to execute the `G` command with the `SNH` pattern (note that we need to zero-terminate the pattern). Then we pad until we overwrite the canary, and finally we add the `G` permission to the `permtd` array. We write the following in a `xpl.py` file: ``` python import sys import struct # first part: fix the canayy p1 = b"le\n" p1+= b"A"*79 # second part: overflow buffer p2 = b"GSNH\0 " p2+= struct.pack('Q', 0x3b9717ae79ef55a0) p2+= b"G\n" sys.stdout.buffer.write(p1+p2); ``` The final attack: ``` sh python3 xpl.y | nc host port ``` Suggested Fix ============= The gets() call should of course be replaced by fgets(). It may make sense to have an additional stack-canary protection, since the `permtd` array is not protected by the standard canary mechanism (the compiler provided canary is located after all the array variables). However, we should look for a proper random source for the seed (e.g., `/dev/random`).