- El fuzz testing automatitza l'enviament d'entrades malformades per descobrir fallades de seguretat en programari i sistemes.
- AFL i AFL++ són fuzzers intel·ligents que usen compiladors instrumentats per guiar-se per cobertura i trobar noves rutes d'execució.
- L'anàlisi de crashes amb eines com AFLTriage i GDB permet valorar l'explotabilitat de vulnerabilitats com ara Integer Underflow.
- Integrar el fuzzing al cicle de desenvolupament millora la robustesa, ajuda a complir normes de seguretat i redueix el risc d'incidents crítics.
Si t'interessa la ciberseguretat orientada a binaris i explotació, tard o d'hora et creuaràs amb el fuzzing, i en particular amb AFL i AFL++. No parlem d'una eina teòrica de laboratori, sinó d'una cosa que es fa servir diàriament per trobar errors molt reals en programes molt coneguts, igual que ha passat amb vulnerabilitats a aplicacions tan populars com 7-Zip.
A les següents línies veurem, de forma detallada i amb un to proper, com utilitzar AFL i AFL++ per fuzzejar binaris, què és exactament el fuzz testing, per què és tan potent a nivell de seguretat i com integrar-lo en un flux de desenvolupament modern. A més, prendrem com a referència un cas real de vulnerabilitat (un Integer Underflow que provoca execució remota de codi) i repassarem eines complementàries com GDB amb extensions, AFLTriage i mòduls dexplotació.
Què és AFL / AFL++ i per què es consideren smart fuzzers
AFL (American Fuzzy Lop) i la seva evolució AFL++ són eines de fuzzing de codi obert dissenyades per provar binaris de forma automàtica, buscant errors com desbordaments, errors de memòria o comportaments inesperats que derivin en crashes o fins i tot en vulnerabilitats explotables.
La gràcia d'AFL no és només enviar dades aleatòries sense més ni més, sinó que es tracta d'un fuzzer intel·ligent o coverage-guided fuzzer. A partir d'una o diverses entrades vàlides d'exemple (els anomenats casos de prova), AFL genera variacions constantment i, gràcies a la instrumentació del programa, detecta quan una nova mutació fa que el binari recorri un camí diferent d'execució.
Per aconseguir aquesta instrumentació, AFL compta amb els seus propis compiladors, com ara afl-gcc i afl-g++, que insereixen petits “ganxos” al codi durant la compilació. Aquests ganxos informen AFL que certa entrada ha descobert rutes noves al flux del programa, el que us permet centrar-se en mutacions que realment aporten cobertura addicional i no perdre el temps amb dades que no canvien res.
Aquest enfocament converteix AFL en una cosa així com el “dirb dels binaris”: una eina molt coneguda, senzilla d'usar un cop s'entén el flux bàsic, i prou potent per descobrir fallades serioses en programes de producció, encara que existeixin fuzzers més avançats o especialitzats.
Introducció al fuzz testing i el seu paper en ciberseguretat
El fuzz testing, o simplement fuzzing, és una tècnica de proves automatitzades de seguretat i robustesa. La idea consisteix a enviar a una aplicació una allau de dades malformades, aleatòries o poc habituals, amb l'objectiu de veure si el programa es trenca, es penja o es comporta de manera inesperada.
Quan se sotmet un programari a fuzzing, es persegueix localitzar errors com ara desbordaments de memòria intermèdia, errors de validació d'entrades, problemes de maneig de memòria o condicions de carrera. Moltes de les fallades poden desembocar en execució de codi arbitrari, filtració d'informació o denegacions de servei.
En un flux de treball típic, el fuzz testing segueix tres fases clares: en primer lloc es genera el conjunt de dades de prova (ja siguin completament aleatòries, generades seguint un format concret o mutant entrades vàlides); després es executa el binari sota prova mentre es monitoritza el seu comportament; i finalment es registren i analitzen els craixos o comportaments rars perquè els desenvolupadors puguin investigar lorigen del problema.
Hi ha diferents estratègies de fuzzing. Algunes eines se centren en fuzz generacional, creant entrades que respecten un format ben definit (per exemple, una estructura de fitxer o un protocol de xarxa); altres es basen en mutació d'entrades vàlides, que és precisament l'enfocament on AFL i AFL++ destaquen. També hi ha enfocaments més aleatoris i altres que es recolzen en coneixement específic del sistema a provar per apuntar just als punts més fràgils.
En ciberseguretat moderna, el fuzz testing és un aliat fonamental: ajuda a trobar vulnerabilitats de manera proactiva, abans que el programari arribi a producció, i contribueix al compliment de normatives i estàndards com ISO 27001 o requisits associats a protecció de dades. A més, redueix el risc reputacional que suposaria que un error greu explotable arribi a mans d'atacants.
Un cas real: Integer Underflow i vulnerabilitat RCE en un descompressor
Per entendre el valor real d'AFL i el fuzzing, és molt útil fixar-se en una vulnerabilitat concreta basada en un Integer Underflow dins un descompressor darxius molt popular. Es tracta d'una fallada d'execució remota de codi (RCE) que es dispara en processar fitxers comprimits amb l'algorisme Zstandard, amb una puntuació CVSS alta, encara que no màxima, precisament perquè requereix un context una mica més específic (per exemple, que es processin fitxers concrets).
Un Integer Underflow apareix quan una variable sencera amb límits inferiors ben definits s'utilitza en operacions de resta fins a traspassar-ne el valor mínim. Com que molts tipus sencers no admeten valors negatius, en “passar-se per baix”, en lloc d'obtenir un nombre per sota de zero, es produeix un salt al valor màxim possible del rang, degut a com es representen internament aquests números.
Imagina una variable sencera que, en teoria, mai no hauria de ser negativa. Si vas restant una constant fins arribar a 0 i segueixes restant, no obtindràs -1, -2, etc., sinó que el valor donarà la volta i passarà a ser un nombre gegantí a lextrem superior del rang permès. Aquesta xifra absurda, si es fa servir com a índex o mida en operacions de memòria, pot provocar lectures o escriptures fora dels límits previstos.
El problema es torna crític quan aquesta variable sencera que pot fer underflow s'usa per manejar un buffer o per calcular offsets. Un pseudo-exemple típic seria utilitzar una variable controlada indirectament per l'usuari com a desplaçament en una funció tipus memcpy, en un bucle que descompta una constant a cada iteració fins que suposadament arribi a zero oa un nombre no positiu.
En un escenari així, si el valor inicial mai aconsegueix que la condició de sortida es compleixi de forma “neta”, l'underflow acabarà convertint el nombre en una cosa enorme, el bucle continuarà corrent i es produirà una corrupció massiva de memòria. Això es tradueix en crashes i, amb prou treball d'explotació, en la possibilitat d'executar codi arbitrari.
Com AFL ajuda a localitzar aquest tipus de vulnerabilitats
Un cop entès el concepte d'Integer Underflow, es veu clar per què AFL i AFL++ són tan útils: a partir de un o pocs fitxers d'entrada perfectament vàlids, el fuzzer pot generar desenes de milers de variacions, fins ensopegar amb aquella que dispara el bug concret a la ruta de descompressió afectada.
Durant el fuzzing, AFL monitoritza el binari instrumentat per saber quan una entrada fa que s'executi un camí diferent del control de flux. Cada cop que detecta un nou recorregut, marca aquesta entrada com a interessant i la conserva com a base per a futures mutacions. Així s'exploren no només els camins habituals d'execució, sinó també les combinacions de dades que activen rutes rares del codi, com ara el procés de descompressió amb paràmetres estranys.
En el cas de l'Integer Underflow, AFL pot trobar una mostra que provoqui que la variable implicada mai no assoleixi el valor de sortida esperat al bucle. El resultat és un crash detectable, que quedarà emmagatzemat a la carpeta corresponent de resultats d'AFL, llestos per ser analitzats amb més calma.
Al cap d'uns minuts o hores d'execució, és comú que AFL assenyali el primer crash rellevant. No tots els errors que trobeu seran explotables, però sí que són un indicador clar que alguna cosa en el maneig de memòria, la validació d'entrades o la lògica interna del binari no està funcionant com hauria de.
Aquest és el moment en què entren en joc eines de suport com AFLTriage o un depurador com GDB, que permeten aprofundir en el crash, observar l'estat dels registres i la memòria, i valorar si la fallada pot explotar-se de manera fiable o si és simplement un penjoll poc útil des del punt de vista de l'atacant.
Instal·lació bàsica d'AFL/AFL++ i entorn d'anàlisi
Muntar l'entorn mínim per fuzzejar amb AFL en un sistema Linux estàndard és força directe. En moltes distribucions només cal instal·lar el paquet corresponent als compiladors instrumentats, per exemple mitjançant una ordre com apt install afl-g++ o el paquet equivalent d'AFL++ que porti tota la suite.
En fer-ho, s'instal·len tant les eines de fuzzing (com afl-fuzz) com els compiladors especialitzats (afl-gcc, afl-g++, i les seves variants modernes a AFL++). Aquests compiladors són els que es faran servir quan vulguis recompilar el teu objectiu amb instrumentació, una cosa imprescindible per treure tot el partit al fuzzing guiat per cobertura.
A més del fuzzer en si, resulta pràcticament obligatori comptar amb un bon entorn de debugging. Un GDB estès amb plugins com PEDA o GEF fa la vida molt més fàcil gràcies a ordres extra, format còmode de memòria, visualitzacions del stack i dreceres per revisar registres i mapes de memòria.
Si encara no teniu GDB, es pot instal·lar amb el gestor de paquets de la distribució. Després, només cal integrar el plugin triat (per exemple, descarregant GEF o PEDA des dels seus repositoris i carregant-los a l'arxiu de configuració de GDB o amb l'ordre source dins del mateix debugger).
Comptar amb aquest “GDB amb esteroides” et permetrà analitzar els binaris instrumentats que s'esfondren sota fuzzing, descarregar els inputs que provoquen el crash i seguir pas a pas què està passant en l'execució exactament al punt de la decisió.
Compilar un binari objectiu amb AFL per a fuzzing
Quan tens AFL o AFL++ llest, el següent pas per a un fuzzing seriós és compilar l'objectiu amb els compiladors d'AFL, i fer-ho amb les opcions adequades per facilitar tant lanàlisi com lexplotació potencial, en cas que lobjectiu de lexercici sigui de recerca ofensiva.
En projectes grans, com ara un descompressor o una suite d'utilitats de fitxer, és bona idea habilitar símbols de depuració (opció DEBUG o flags com -g) i reduir al mínim les optimitzacions (per exemple, fer servir -O0 en lloc de -O2). Això ajuda moltíssim a l'hora d'entendre què està passant al codi font en relació amb el crash observat.
Normalment hauràs de localitzar el makefile o fitxer de construcció adequat i modificar les línies on es defineixen les flags de compilació. Això pot incloure activar una variable de DEBUG, forçar un nivell d'optimització concret i, sobretot, substituir CC i CXX per afl-gcc i afl-g++ (o eines equivalents d'AFL++).
Per exemple, podries llançar la compilació amb una mica de l'estil de CC=afl-gcc i CXX=afl-g++ juntament amb el makefile apropiat. El resultat serà un binari instrumentat que incorporarà els ganxos necessaris perquè AFL pugui mesurar la cobertura al llarg de lexecució.
Quan acabi la compilació, convé comprovar que el binari resultant té realment símbols de depuració i l'arquitectura adequada, usant una ordre tipus file sobre l'executable. Així t'assegures que no estàs fuzzing a cegues i que podràs depurar de manera còmoda els crashs que apareguin.
Preparació de testcasos i execució d'AFL
Abans de llançar AFL contra el teu binari, necessites preparar un conjunt de entrades mínimes vàlides. Si estàs provant un descompressor que falla amb fitxers comprimits amb un algoritme concret, hauràs de crear almenys un fitxer ben format que utilitzi aquest mètode de compressió.
El més habitual és crear un directori, per exemple anomenat testcases, i guardar-hi una o diverses mostres funcionals. Aquestes serviran de punt de partida perquè AFL generi mutacions cada cop més agressives, tractant de descobrir nous camins d'execució i, eventualment, crashes.
La comanda típica per arrencar AFL s'assembla a alguna cosa com: indicar un nom de sessió o sincronicació, la carpeta d'entrada amb testcasos, la ruta de sortida on s'emmagatzemaran els resultats i alguns paràmetres addicionals com el timeout per evitar quedar encallat en bucles infinits.
A la línia d'ordres, s'especifica també com s'executa el programa objectiu usant el marcador @@, que AFL reemplaçarà automàticament per la ruta de cada testcase que vagi generant. Així, pots indicar alguna cosa com ./binari argumentoss @@ i AFL s'encarregarà d'anar llançant l'executable milers de vegades amb diferents fitxers.
És important saber que AFL es pot queixar de la configuració del sistema al principi (per temes de límits de recursos, core dumps, etc.). En aquest cas, sol existir un script o eina auxiliar que ajusta paràmetres del kernel i de la sessió per optimitzar l'entorn de fuzzing, cosa que convé executar amb permisos adequats abans de repetir la prova.
Anàlisi de crashes amb AFLTriage i GDB
Després d'un temps de fuzzing, AFL anirà emplenant la carpeta de sortida amb noves entrades interessants i, sobretot, amb arxius que han provocat crashes. Aquests solen guardar-se en un subdirectori específic (per exemple, crashes dins de la carpeta de la sessió).
Per no tornar-se boig revisant un per un aquests inputs, hi ha eines com AFLTriage que permeten executar automàticament el binari contra cada arxiu de crash i generar informes estructurats amb detalls sobre on i com s'ha produït la decisió.
En llançar AFLTriage, se us indica el directori d'entrades (la carpeta de crashes), un directori de sortida on desar els reports i l'ordre per executar el binari amb @@. L'eina analitzarà cada cas i generarà fitxers de text amb informació d'interès per a l'analista.
El contingut típic d'aquests informes inclou el tipus de senyal que ha provocat el crash, l'adreça on ha fallat, un stacktrace bàsic i, en molts casos, la funció o macro on s'ha trencat el codi (per exemple, una operació concreta de còpia, un bucle de compressió o una macro tipus COPY_CHUNKS que resta mides a cada iteració).
Un cop localitzat un crash interessant, el pas següent és reproduir-lo en GDB. Per això es llança el depurador amb el binari i el fitxer d'entrada culpable com a arguments, i s'executa el programa des de dins de GDB. Quan es reprodueix el crash, es pot inspeccionar l'estat de registres clau com ara RAX, RDX, etc., revisar la instrucció exacta que s'està executant i consultar mapes de memòria (vmmap) per veure a quina regió s'està intentant llegir o escriure.
De crash a possible explotació: mòduls danàlisi en GDB
Veure que un binari cau no sempre significa que la fallada sigui explotable. Aquí entren en joc extensions de GDB com el mòdul factible, dissenyat per analitzar el context d'un crash i oferir una classificació aproximada sobre si la fallada podria aprofitar-se o no.
Aquests tipus d'extensions estudien aspectes com la validesa del punter d'instrucció, l'estat de l'stack pointer, els permisos de memòria de la direcció on s'intenta llegir o escriure i la naturalesa del senyal que ha provocat el crash. Amb tot això, proporcionen un veredicte orientatiu sobre el potencial dexplotació (per exemple, categoritzant el bug com probablement explotable, possiblement explotable o poc interessant).
En el cas de l'Integer Underflow que acaba escrivint en memòria de només lectura, el plugin pot suggerir que es tracta d'un cas amb potencial explotable perquè s'està fent una escriptura fora dels límits previstos en una regió on no s'hauria de tocar res.
No tots els escenaris seran tan directes, però aquesta combinació d'AFL per descobrir el crash, AFLTriage per agrupar i descriure els casos i GDB amb extensions per valorar l'explotabilitat, forma un pipeline molt sòlid d'auditoria de binaris. A partir d'aquí, el treball restant és purament de recerca i desenvolupament d'exploit, cosa que pot variar enormement en complexitat segons el binari i les proteccions actives.
Una altra part clau de l'anàlisi, especialment quan es publica un CVE i un pegat associat, és revisar com s'ha corregit la decisió al codi font. Això sol implicar canvis en el tipus de dades usat (per exemple, passar d'un byte amb signe a un sense signe, ampliant rang) i la inclusió de comprovacions addicionals (ifs que tornen codis d'error si certs límits se sobrepassen) per evitar l'underflow o la corrupció de memòria.
Altres fuzzers i eines rellevants a l'ecosistema
Encara que AFL i AFL++ són de les opcions més conegudes per fuzzejar binaris nadius, l'ecosistema de fuzz testing és força més ampli i convé tenir-lo al radar si treballes de forma habitual amb seguretat de programari.
Entre les opcions més utilitzades hi ha LibFuzzer, una llibreria desenvolupada originalment per Google que s'integra directament amb el codi C/C++ i realitza fuzzing a nivell de funcions concretes, ideal per testejar components individuals amb entrades molt controlades.
També hi ha Peach Fuzzer, una plataforma més generalista i configurable que permet definir models precisos de protocols, formats d'arxiu i estructures complexes. És molt popular en sectors on la fiabilitat és crítica, com a sistemes industrials o entorns IoT.
A l'àmbit web, eines com OWASP ZAP inclouen mòduls de fuzzing orientats a aplicacions web, capaces d'enviar peticions manipulades, càrregues malformades i proves d'injecció a endpoints HTTP per detectar vulnerabilitats com injeccions SQL, problemes de validació d'inputs o errors al processament de formularis.
Tot plegat complementa el fuzzing de binaris tipus AFL/AFL++, ja que permet cobrir des del codi nadiu de baix nivell fins a les capes d'aplicació més exposades a l'usuari final. L'objectiu comú en tots els casos és el mateix: automatitzar la cerca de fallades abans que ho facin els atacants.
Com integrar el fuzz testing al cicle de desenvolupament
Perquè el fuzzing aporti valor real de forma constant, no n'hi ha prou amb llançar AFL o AFL++ de manera puntual i oblidar-se'n. És molt més eficaç integrar el fuzz testing al cicle de desenvolupament de programari, de manera similar a com es fa amb els tests unitaris o d'integració.
El primer pas és definir quines parts del sistema són més crítiques: parsers de fitxers, mòduls de xarxa, components que tractin dades d'usuaris o serveis exposats a l'exterior. Sobre aquests objectius es poden dissenyar campanyes de fuzzing específiques, amb testcas curosament escollits.
Després toca triar l'eina adequada per a cada cas. Per a binaris nadius complexos, AFL++ o LibFuzzer solen ser candidats naturals, mentre que per a APIs web o aplicacions HTTP, una eina orientada a web tindrà més sentit. El més important és que el fuzzing s'executi de forma repetible i automatitzable.
La integració amb sistemes de CI/CD resulta molt útil: es pot configurar el pipeline perquè, en determinades branques o abans de certs desplegaments, es llanci una bateria de fuzzing amb un temps limitat, guardant qualsevol crash nou trobat i fallant la build si apareixen vulnerabilitats potencialment greus.
Finalment, cal assumir que el fuzzing és un procés iteratiu. Cada cop que es corregeix una fallada, s'hauria de tornar a fuzzejar el component afectat per verificar que el pegat funciona i que no s'ha introduït cap regressió o vulnerabilitat nova. D'aquesta manera, el fuzz testing esdevé una capa més de protecció contínua i no una auditoria aïllada.
Tot plegat mostra com AFL, AFL++ i el fuzz testing en general permeten passar d'una seguretat basada només en revisions manuals a un enfocament molt més automatitzat i exhaustiu, capaç de descobrir errors que altrament es quedarien ocults. Treballar amb casos reals, com vulnerabilitats d'underflow en descompressors molt utilitzats, ajuda a més a entendre que no es tracta de teoria, sinó de problemes que ja estan sent explotats, per la qual cosa mantenir el programari actualitzat i sotmès a fuzzing periòdic pot marcar la diferència entre un sistema compromès i un de resistent.
Redactor apassionat del món dels bytes i la tecnologia en general. M'encanta compartir els meus coneixements a través de l'escriptura, i això és el que faré en aquest bloc, mostrar tot el més interessant sobre gadgets, programari, maquinari, tendències tecnològiques, i més. El meu objectiu és ajudar-te a navegar pel món digital de forma senzilla i entretinguda.

