Pondre mas info en la wiki (y avisare cuando lo haga), pero mientras tanto..
Este NO ES EL UNICO bug de este tipo y estos problemas seguiran apareciendo no solo en SMF sino en todos lados que usen expresiones regulares en PHP con PREG con cosas tan sensibles como lo es parsear codigo que pasara a HTML.
Es una vulnerabilidad bastante complicada de entender... y cabe mencionar que por donde la veas es un dolor de cabeza.
Se llama ReDoS, (Regular Expression Denial of Service), fue presentada en Septiembre en OWASP 2009 - Israel.
http://www.checkmarx.com/NewsDetails.aspx?id=23
Basicamente la idea es abusar de la feature de backtracking de las expresiones regulares:
http://en.wikipedia.org/wiki/Backtracking
Voy a poner un ejemplo, la siguiente expresion regular:
Código:
([abc]+[def]|[cde]+[fgh])*i
lo que hace es buscar una cadena del tipo:
aaaadegafei
adadccfcfi
adi
cfi
Es muy facil generar ejemplos que funcionen con esta expresion regular, pueden probarlo en su navegador:
Citar
javascript:alert(/([abc]+[def]|[cde]+[fgh])*i/("adi"));
Ahora, el problema viene cuando una cadena NO coincide con dicha expresion regular como lo es:
adcd
el algoritmo en esos casos, cuando se da cuenta que la expresion regular no coincide, debe regresar a la ultima posicion que matcheo correctamente (recursivamente) y probar con la otra opcion (si es que hay).
es decir:
/([abc]+[def]|[cde]+[fgh])*i/("adcd") hace:
1.- a (coincide con primer caracter de primer grupo)
2.- d (coincide con segundo caracter de primer grupo)
-- entrando en repeticion -- *
3.- c (coincide con primer caracter de primer grupo)
4.- d (coincide con segundo caracter de primer grupo)
5.- [fin] (no coindide!! necesitamos una i, regresa a 4)
4.- (no hay mas opciones! regresa a 3)
3.- c (coincide con primer caracter de segundo grupo)
4.- d (no coincide!! regresa a 3)
3.- (no hay mas opciones! regresa a 2)
2.- (no hay mas opciones! regresa a 1)
1.- (no hay mas opciones! regresa a 0)
no hay 0, no hay match.
ahora, asi es como funciona un engine de expresiones regulares.. y si vemos esta expresion regular en especifico, podemos ver que hay un problema.
([abc]+[def]|[cde]+[fgh])
los strings:
cf
ccccccccccf
cfcf
cccf
etc..
coinciden con ambos grupos ([abc]+[def] y [cde]+[fgh]), ahora porque esto es relevante?
porque el siguiente string:
cfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfcfQ
no va a matchear, pero el parser tiene que probar todas las opciones posibles para estar seguro.
1.- c (coincide con primer caracter de primer grupo)
2.- f (coincide con segundo caracter de primer grupo)
-- entrando en repeticion -- *
3.- c (coincide con primer caracter de primer grupo)
4.- f (coincide con segundo caracter de primer grupo)
-- repeticion --
5.- c (coincide con primer caracter de primer grupo)
6.- f (coincide con segundo caracter de primer grupo)
-- repeticion --
etc..
--repeticion--
50.- c (coincide con primer caracter de primer grupo)
51.- f (coincide con segundo caracter de primer grupo)
52.- Q (no coincide, regresa a 51)
51.- (no hay mas opciones, regresa a 50)
50.- c (coincide con primer caracter se segundo grupo)
51.- f (coincide con segundo caracter de segundo grupo)
52.- Q (no coincide, regresa a 51)
51.- (no hay mas opciones, regresa a 50)
50.- (no hay mas opciones, regresa a 49)
49.- (no hay mas opciones, regresa a 48)
48.- c (coincide con primer caracter de segundo grupo)
49.- f (coincide con segundo caracter de segundo grupo)
50.- c (coincide con primer caracter de primer grupo)
51.- f (coincide con segundo caracter de primer grupo)
52.- Q (no coincide, regresa a 51)
51.- (no hay mas opciones, regresa a 50)
50.- c (coincide con primer caracter se segundo grupo)
51.- f (coincide con segundo caracter de segundo grupo)
52.- Q (no coincide, regresa a 51)
51.- (no hay mas opciones, regresa a 50)
50.- (no hay mas opciones, regresa a 49)
49.- (no hay mas opciones, regresa a 48)
48.- (no hay mas opciones, regresa a 47)
...
etc..
como ven, el hecho de que cf coincidiera en 2 grupos, hizo que el engine repitiera la comparacion en 50 y 51, y luego, en 48 y 49.. lo que hizo que 50 y 51 se repitieran de nuevo.
Ahora, esto hace que 50 y 51 se re-prueben 2 VECES cada que 48 matchee, y 48 va a re-probarse 2 VECES cada que 46 matchee (aqui van 2*2 = 4 veces paraa 50/51), y 46 se re-probara 2 VECES cada que 44 matchee, lo que hace 50 (2*2*2), etc..
eso se repite 25 veces:
2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2=16,777,216
ahora, esto es nadamas la cantidad de veces que matcheara 50/51, ahora hay que sumar la cantidad de veces que matcheara 48/49
2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2*2=8,388,608
etc.... esto es a lo que en algoritmia se le llama un problema de complejidad exponencial, y hace que sea bastante tardado de resolver.
ahora, aqui estamos explotando un bug en una expresion regular que trata de detectar URLs... www.a((((((((((((((((((((((((((((((((((((((((((((.
donde "(" toma el papel de "cf" en nuestro ejemplo anterior.
en PHP, PREG va a suspender la ejecucion despues de N iteraciones y alterara la memoria donde esta almacenada la variable de tal forma, que se borraran los ultimos 3 bytes dentro de esta.
Esto tiene repercusiones bastante malas a largo plazo, como lo podemos ver, y se debe tener mucho cuidado...
Recomendaciones.. lean el PDF, y aprendan a detectar ReDoS y evitenlas en su codigo!
Saludos!!










Autor




En línea






(sin ofender)







