Analysis of the bug =================== The program contains several bugs: * relative paths are used for the `password` and `database` files; * the `scanf()` call at line 52 can write an arbitrary amount of chars into the `db.cmd` buffer; Attack plan =========== The scenario assumes that the attacker has unprivileged access to the system where the vulnerable `mycmd` binary is installed. The `password` file is only accessible to the owner of the `mycmd` binary, which is installed with the setuid bit set. The two bugs can be independently exploited to reveal the contents of the password file. To exploit the relative-paths bug, the attacker can create a symlink to the `password` file and call it `database`. The attacker needs to find a directory where she can create new files. If the attacker is a normal user of the system, her home directory will do. Even if the attacker has only restricted access, she will normally be able to create files in the world-writable `/tmp` directory. The `scanf`-related bug can be exploited to overwrite the `fd` field in the `db_struct` object. This field contains the file descriptor of the open `database` file, but we can let it point to the file descriptor of the `password` file, instead. This is possible because the `password` file is still open when the user input is processed. Of course we need to "guess" the file descriptor number, but this is usually very easy to do, since file descriptors are assigned sequentially. Attack implementation ===================== * relative-paths bug (assume that `mycmd`, the `database` and `password` files are all installed in the `/home/snh` directory): ``` sh cd /tmp ln -s /home/snh/password ln -s /home/snh/password database echo 0:64 | /home/snh/mycmd ``` * `scanf`-bug. The file descriptor of the open `password` file will be 4. ``` sh export PYTHONIOENCODING=iso-8859-1 python3 -c 'print("0:64" + "A"*(32-4) + "\x04")' | ./mycmd ``` Suggested fix ============= Let us consider the relative-paths first. Of course, the problem can be fixed by using absolute paths of trusted directories. However, if the attacker can only write into `/tmp`, the system administrator can protect the unmodified binary by enabling the "protected symlinks" feature of Linux. When this feature is enabled, symlinks found in directories with the sticky-bit set and not belonging to the owner of the directory can only be followed by the owner of the symlink. Since the sticky bit is normally set for the `/tmp` directory, and this directory is owned by root, the above attack would not be effective, since the attacker-created symlinks would belong to the attacker and the `mycmd` program would not be able to open them. The system administrator can enable this security measure by writing a 1 into the `/proc/sys/fs/protected_symlinks` pseudo-file. Let us now turn to the scanf bug. The program can be fixed by adding a width field to the format string: ``` c scanf("%31s\n", db.cmd); ``` Note the need to reserve one byte to accommodate the string terminator inserted by `scanf()`. As a defensive measure, it is also a good idea to close the `password` file as soon as it is no longer needed.