Generic POSIX printf itself can be Turing complete as shown in Control-Flow Bending. Here we take printf-oriented programming one step further and preset a brainfuck interpreter inside a single printf statement.
An attacker can control a printf statement through a format string vulnerability (where an attacker-controlled string is used as first parameter to a printf-like statement) or if the attacker can control the first argument to a printf statement through, e.g., a generic memory corruption. See the disclaimer below for practical ``in the wild'' considerations.
Brainfuck is a Turing-complete language that has the following commands (and their mapping to format strings):
-
== dataptr++ (%1$65535d%1$.*1$d%2$hn)
- < == dataptr-- (%1$.*1$d %2$hn)
-
- == *dataptr++ (%3$.*3$d %4$hhn)
-
- == *dataptr-- (%3$255d%3$.*3$d%4$hhn -- plus check for ovfl)
- . == putchar(*dataptr) (%3$.*3$d%5$hn)
- , == getchar(dataptr) (%13$.*13$d%4$hn)
- [ == if (*dataptr == 0) goto ']' (%1$.*1$d%10$.*10$d%2$hn)
- ] == if (*dataptr != 0) goto '[' (%1$.*1$d%10$.*10$d%2$hn)
Have a look at the bf_pre.c sources to see what is needed to setup the interpreter and also look at the tokenizer in toker.py.
Run make in ./src to generate a couple of sample programs (in ./src).
Keep in mind that this printbf interpreter is supposed to be a fun example of Turing completeness that is available in current programs and not a new generic attack vector. This demo is NOT intended to be a generic FORTIFY_SOURCE bypass.
Current systems often either (i) disable %n (which is used to write to memory and allowed according to the standard but rarely used in practicy) or (ii) through a set of of patches that test for attack-like conditions, e.g., if the format string is in writable memory.
To use printbf in the wild an attacker will first use memory corruption to modify the internal state of printf (by setting the corresponding flags in the file struct when it is spilled on the stack) to enable %n and format strings in writable memory. The attacker model for printbf assumes that the attacker can use memory corruption vulnerabilities to set-up the attack or that the sources are compiled without enabled FORTIFY_SOURCE defenses.