La explotación de los insectos exóticos: Tipo PHP Malabarismo

Escribiendo Exploits Para las clases de bichos exóticos:
PHP Tipo Malabares

Tyler Borland (TurboBorland)


Intro

Bienvenidos a la segunda parte de la serie sobre la explotación de las clases de insectos exóticos. Me gustaría reiterar por qué existe esta serie. Estaba cansado de la lectura de artículos constantes en nuevos xss, sqli, LFI, y etc, así que empecé una serie de más clases de errores «exóticos». Esta es una serie en la explotación de los errores que no se habla tan a menudo como deberían ser, pero todavía tienen graves repercusiones. Esta es la parte 2 de una serie de 4 partes. Espero que lo disfruten!

Parte 2.) PHP Tipo Malabares

Este artículo está en explotar otra característica mágica de PHP, el tipo de malabarismo. Vamos a ir sobre lo que es, ejemplos de problemas de comparación, ejemplos de otras cuestiones de operación matemática, el problema con str (n)? Cmp / str (n)? Casecmp, y finalmente abusan de una aplicación en el mundo real. La aplicación en el mundo real es simples Foros Machine (SMF) para un error que se dio a conocer públicamente el inicio de este año (2013). En este artículo se dará a conocer con una más fácil de consumir presentación ODP y código python para un primer exploit público de lanzamiento para esta vulnerabilidad. Busque en la parte inferior para los enlaces a estos materiales adicionales. Si usted no entiende algo en el código, vuelva a leer este artículo.

Qué es manipulación de tipos?

Tipo de malabarismo en PHP es causado por un problema de las operaciones sueltas frente a operaciones estrictas. Comparaciones estrictas compararán tanto los valores de los datos y los tipos asociados a ellos. Una comparación floja usar el contexto para entender qué tipo será la información. Según la documentación de PHP para operaciones de comparación en http://php.net/manual/en/language.operators.comparison.php:

«Si se compara un número con una cuerda o la comparación implica numérica cadenas, cada cadena se convierte en un número y la comparación realizada numéricamente. Estas reglas también se aplican a la sentencia switch. La conversión de tipo no se produce cuando la comparación es === o! == ya que esto implica comparar el tipo así como el valor. »

Todo esto será cubierto posteriormente con ejemplos, pero la comida para llevar principal es si las cadenas son numéricos, entonces se convierten en enteros o flotadores. Esto puede cambiar la forma en que la operación funciona completamente. Por supuesto, hay ciertos requisitos que se deben tomar con el fin de que una cadena se considera una «cadena numérica».

calificaciones de conversión

Según php.net sobre reglas de conversión de cadena a http://www.php.net/manual/en/language . types.string.php # language.types.string.conversion:

«Si la cadena no contiene ninguno de los caracteres, ‘e’ o ‘E’ y el valor numérico ‘.’ cabe en los límites de tipo entero (como lo define PHP_INT_MAX), la cadena se evaluará como un entero. En todos los demás casos, será evaluado como un float.

El valor viene dado por la porción inicial de la cadena. Si la cadena comienza con datos numérico válido, este será el valor utilizado. De lo contrario, el valor será 0 (cero). dato numérico válido es un signo opcional, seguido de uno o más dígitos (que contiene opcionalmente un punto decimal ), seguidos por un exponente opcional. El exponente es una ‘e’ o ‘E’ seguida de uno o más dígitos. »

Así que si una cadena es todos los números, cabe dentro de PHP_INT_MAX (firmado int max 2147483647), y no contiene un punto o un exponente caracteres, entonces se trata como un entero. De lo contrario, se trata como un valor float. El valor flotante es la parte interesante de la situación. Usted puede aprender mucho más acerca de las reglas de la conversión del flotador con prudencia se utiliza strtod para convertir una cadena ascii flotar valores. Si HOMBRE strtod, las reglas son:

«La forma esperada de la (porción inicial de) la cadena es opcional líder espacio en blanco como se reconoce en isspace (3), un plus opcional (‘+’ ) o un signo menos («-«.) y luego o bien (i) un número decimal, o (ii) un número hexadecimal, o (iii) una infinidad, o (iv) a (not-a-number NAN) Un decimal número se compone de una secuencia no vacía de dígitos decimales conteniendo posiblemente un carácter radical (punto decimal, dependiente de la localización, por lo general ‘.’), seguido opcionalmente por un exponente decimal. Un exponente decimal consiste en una ‘E’ o ‘e’, ​​seguido por un signo más o menos opcional, seguido por una secuencia no vacía de dígitos decimales, e indica la multiplicación por una potencia de 10. Un número hexadecimal consiste en un «0x» o «0X» seguido por una secuencia no vacía de dígitos hexadecimales que posiblemente contiene un carácter radical, seguido opcionalmente por un exponente binario. Un exponente binario consiste en una ‘P’ o ‘p’, seguido de un signo más o menos opcional, seguido por una secuencia no vacía de dígitos decimales, e indica multiplicación por una potencia de 2 . Al menos uno de carácter radical y el exponente binario deben estar presentes «.

Una nota muy importante aquí es cómo funciona el exponente. Los valores del exponente se tratan en la actualidad como la multiplicación por una potencia de 10. Así que si el valor era 00e13242 a continuación, el valor real es 0 como cualquier cosa multiplicado por 0 es 0. Sin embargo, si el valor es 1e2 a continuación, el valor es 100 o 1 * 10 * 10. Con este entendimiento, echemos un vistazo a los temas que se forman en torno a esta característica.

Problemas y Ejemplos comparativos

Si una cadena se convierte a un float entonces podemos coincidir mucho más que posiblemente lo esperado por la aplicación. Todo lo que sigue a la expresión regular de 0 + [eE] d + se traducirá en 0. La gente pensaría que 1 [eE] d + también trabajaría como 1 a la potencia de cualquier cosa es 1, pero esto no es cómo funciona realmente la función strtod (). Ahora, echemos un vistazo a algunos ejemplos de comparación a ver visualmente este trabajo.

  
$ entrada = $ _GET ['input'];
if ($ entrada == "0e94323") {
print ("$ entrada == 0e94323");}

if ($ entrada == "00e19384") {
print ("$ entrada == 00e19384");
}

localhost / test.php entrada? = 0 />
== 0 0e94323
0 == 00e19384

Recuerde, esto es causado por el uso de la operación de comparación suelto con ==. Para ver ejemplos de los diferentes cambios contextuales que PHP se puede hacer con las operaciones sueltas, le sugiero que busque en la hoja de cálculo proporcionada por Gynvael Coldwind disponible en https://docs.google.com/spreadsheet/pub?key=0Apy5AGVPzpIOdHREMVpyU0JBak5GcURZZGpQbGRqb0E&output=html. PHP también proporciona una tabla mucho más simplista en http://www.php.net/manual/en/types.comparisons.php.

matemáticas son Crazy

Después de la diversión operación de comparación que algunos lectores podrían preguntarse acerca de otras operaciones matemáticas. Bueno, no te preocupes, hay un grado de locura que participan en otras operaciones también. Tome este ejemplo de código PHP:

  
if ($ entrada <"30") {
print ("$ entrada <30 t ");
print ((int) $ entrada);}

Se ve como un ejemplo bastante cuerdo. De hecho, incluso se actúa de esa manera la mayor parte del tiempo: entrada = 1
1 <30 1
localhost / test.php entrada

localhost / test.php? = 111 />

str (n)? cmp / str (n)? Problema casecmp

Si bien esto no es necesariamente un problema de malabarismo tipo este se cubrirá para la integridad bien de cuestiones de comparación. Cuando nos enfrentamos a estos problemas, o procedentes de un fondo C, la gente tiende a mirar a otras funciones como strcasecmp. Usted ve esto en silencio un poco de código PHP para la evaluación de cadenas. En un caso normal, y de acuerdo a php.net / strcasecmp, la función sigue esta pauta:

«Devuelve <0 si cadena1 es menor que cadena2;> 0 si cadena1 es mayor que cadena2, y 0 si son iguales «.

Esto parece cuerdo, compruebe en contra 0 o hacer una! para ver si las dos cadenas proporcionadas coinciden. Al igual que:

if (strcasecmp ($ _GET [‘pass’], «pasar») == 0) {
Si (strcmp ($ _GET [‘pass’], «pasar «)) {/>
Ambas funciones parecen que sólo se evalúan si las dos cadenas son verdaderos como la función debe devolver 0 si son iguales. Curiosamente, hay otro caso que será totalmente aceptado. Si se pasa una matriz en la variable y luego se le da un valor NULL. De acuerdo con las tablas de comparación como dados anteriormente, NULL es realmente 0. Así que esta función se puede anular por completo si un atacante para proporcionar:

https://securesite.com/login.php?user=admin&pass [] = lo que sea

real Ataque Mundial – Simples Foros Machine

Como siempre, es el momento para divertirse y crear un exploit para una vulnerabilidad recientemente en software del mundo real. Esta vulnerabilidad afecta a las versiones de malabarismo tipo SMF <= 2.0.3 y <= 1.1.17. Se trata de dos ramas diferentes de SMF que se mantienen de forma activa. La vulnerabilidad fue encontrada por Arseni Reutov y usted puede encontrar a su asesor en http://en.securitylab.ru/lab/PT-2012-29. El vendedor publique una solución a principios de este año el 02 de enero de 2013. La vulnerabilidad

En Fuentes / Reminder.php, línea 199 en 2.0.3, el inicio de la función setPassword2 vulnerables () se ve :

  checkSession /> 
[1]

if (empty ($ _POST [ 'u']) | |! isset ($ _POST ['passwrd1']) | |! isset ($ _POST ['passwrd2'])) [2]
fatal_lang_error ('no_access ', false);

... / / Es la contraseña realidad válida
require_once ($ sourcedir?'. / Subs-auth.php ');
$ passwordError = validatePassword ($ _POST ['passwrd1'], $ usuario, array ($ email)); [3]

... if (($ _POST [código vacío ' ']) | | substr (! $ realCode, 0, 10) = substr (md5 ($ _POST [' código ']), 0, 10)) [4]
{
/ / Detener ataques de fuerza bruta como esto
validatePasswordFlood ($ _POST ['u'], $ flood_value, false);. [5]

fatal_error ($ txt ['invalid_activation_code'], false);}


...

setPassword2 () es una función que se utiliza para validar un ‘ ¿Olvidó su contraseña simbólico ‘. Un usuario haga clic en «¿Olvidó su contraseña ‘, será enviado por correo electrónico una ficha, y luego hizo para validar que ficha en un formulario web. Si somos capaces de completar el proceso de validación, se puede restablecer la contraseña de cualquier usuario con lo que elegimos sin llegar a necesitar la dirección de correo electrónico. Pero vamos a necesitar saber cómo activar esta vulnerabilidad mediante la siguiente y completando la ruta anterior primero.

Camino a la Explotación

La ruta de acceso a la explotación es mucho más simple que cualquiera de los otros artículos. En [1] se requiere una sesión válida y el acaparamiento y el uso de la cookie PHPSESSID es todo lo que se necesita:

  
if ($ tipo == 'mensaje ') {
$ check = isset ($ _POST [$ _SESSION [' session_var ']])? $ _POST [$ _SESSION ['Session_var']]: (empty ($ modSettings ['strictSessionCheck']) && isset ($ _POST ['sc']) $ _POST ['sc']:? Null);

Los valores de POST necesarios se ven en [2]. $ _POST [‘U’] es el member_id. Si usted no está atacando id de usuario 1, al igual que si decide atacar a todos los admins / mods a la vez durante un tiempo mayor al éxito, a continuación, sólo tiene que hacer clic en el nombre de usuario y buscar en el URI. El valor u = # será visto y le dan la identificación del usuario que necesita. Los otros valores son passwrd1 y passwrd2. Deben coincidir con la función validatePassword () en [3], que no vamos a entrar en detalles de aquí. El valor final del poste se ve en la línea de código vulnerable en [4] en el setPassword2 arriba () función.

En [4] es donde la vulnerabilidad malabares tipo se lleva a cabo. Vemos una operación de comparación suelta entre 10 bytes de una variable llamada $ realCode y 10 bytes de un hash md5 de nuestro valor de código publicado. En primer lugar, echemos un vistazo a lo que $ realCode es y cómo se genera. Mediante la búsqueda en las fuentes / Reminder.php, este es el primer paso:

  /> 
...
if (($ fila vacía ['openid_uri']))
/ / Establecer la contraseña en la base de datos.
updateMemberData ($ row ['id_member'], array ('validation_code' => substr (md5 ($ password), 0, 10)));

... $ peticion = $ smcFunc ['db_query'] ('', '
validation_code SELECT, member_name, email_address,
passwd_flood ...
list ($ realCode, $ usuario, $ email, $ flood_value) = $ smcFunc ['db_fetch_row'] ($ peticion);

Aquí $ password es creado por el generateValidationCode (función), guardada en la base de datos de hasta 10 caracteres de un hash md5 de $ password, luego recuperado y guardado como $ realCode. Así que la siguiente pregunta termina siendo, lo que hace generateValidationCode () parecerse a la respuesta se encuentra en Fuentes / Subs-Members.php:?

  función /> 
{
global de $ smcFunc, $ modSettings;

$ peticion = $ smcFunc ['db_query'] ('get_random_number', '
SELECT RAND ()',
array (
)
);
lista
($ dbRand) = $ smcFunc ['db_fetch_row'] ($ peticion);
$ smcFunc ['db_free_result'] ($ peticion) ;...

retorno substr (preg_replace ('/ W /','', sha1 (microtime () mt_rand () $ $ dbRand modSettings ['rand_seed'])), 0, 10 );}

No soy muy bueno para ataques criptográficos, pero con las semillas de la base de datos, esto no es algo que podemos calcular fácilmente localmente su lugar,. ‘ll tiene que martillar lejos solicitudes. Ahora que sabemos cómo se genera $ realCode, y lo más importante que es 10 caracteres de un hash md5, ¿cómo se escribe un exploit para esta vulnerabilidad?

Generación A Tipo malabares

Una vez más, si nos fijamos en [4] anterior, observe que el código publicado es en realidad md5 hash y se comparó con otro de hash md5. La mayor ventaja es que sólo 10 bytes de los hashes sean utilizadas efectivamente. Por lo tanto, si ambas partes logren 0 + [eE] d + entonces la comparación se caerá. Uno de los valores de la operación es controlada por el usuario, que es substr (md5 (código $ _POST [‘ ‘]), 0,10) ¿Cuál es requerido siguiente es asegurarse de que el código de control generará un adecuado 0 + [eE] d + Para hacer esto un simple script de python fue creado:..

  hashlib importación />  
f = open (" lista de palabras "," r ") readlines ()
findit = re.compile ( "^ 0e [0-9] {8}") # d soporta unicode, 0-9 es más rápido
para el ingreso en f:
entrada = entry.rstrip (" n") # strip nueva línea
m = hashlib.md5 (str (entrada)). hexdigest () # guardar hash MD5 en lugar de referencia
m = m [doce y diez] # substr (m, 0,10)
if (findit.search (m) = Nadie!): # if se encontró coincidencia de impresión
("% s:"% str (m)), el hash # print:
print (str ( entrada)) # print lista de palabras de entrada

Con una lista de palabras que contiene 000000-999999, y la expresión regular demasiado simplista, se encontraron 101 resultados. Si la expresión regular es ^ 00e [0-9] { 7} y luego se encontraron 8 resultados. Finalmente, si era ^ 000e [0-9] {6} entonces se encontraron 2 resultados. Esto da 111 valores hábiles a partir del ejemplo de lista de palabras a utilizar. fue elegida Un valor elegido al azar «190539» para la explotación de que, cuando se md5 y substr’d, es en realidad «0e25261622» o 0. Ahora sólo necesitamos $ realCode para que coincida también, para que podamos completar la comparación. Para hacer esto mantenga solicitar un nuevo token mediante la presentación de ¿Olvidó su contraseña peticiones y cotejándolas con nuestro código publicado de 190539. Si coinciden entonces se cambia la contraseña, si no seguir adelante. Desgraciadamente, nosotros golpeamos una pega a las 5 de la función setPassword2 () por encima de donde validatePasswordFlood () reside.

validatePasswordFlood ()

Antes de continuar, si el objetivo es de la rama 1.1.x, no se utiliza esta función y la cantidad de conjeturas no se limitan. Usted puede spam conjeturas tan rápido como usted y el servidor web puede manejar. Si el destino es de la rama 2.0.x entonces hay una función de limitación de adivinar. La función validatePasswordFlood () se encuentra en Fuentes / LogInOut.php :.

  
/ / Destruye cualquier sesión o datos de la cookie acerca de esta persona, ya que validan mal require_once />
setLoginCookie (-3600, 0); [1]

... / / derecha, es lo que tenemos un valor de inundaciones ?
if ($ password_flood_value == false!)
@ list ($ indicación_horaria, $ number_tries) = explode ('|', $ password_flood_value);

/ / Marca de tiempo no válido ? o inexistentes
if (empty ($ number_tries) | | $ indicación_horaria <(time () - 10)) [2]

{/ / Si no fuera * que * hace mucho tiempo, no les dan otros cinco va
$ number_tries = vacías (number_tries $ ) && $ indicación_horaria <(time () - 20).!? 2: 0; [3]
$ indicación_horaria = time ();}


$ number_tries + +;

/ / Broken la ley
si (number_tries> 5 $ ) [4]
fatal_lang_error ('login_threshold_brute_fail', 'crítico');?

Para una visión general de alto nivel, si nos vamos a los 5 intentos en 10 segundos, no se le permitirá volver a intentarlo hasta que los 10 segundos desde el primer intento de petición está terminado. Para una visión más técnica, en [1], el se eliminan las cookies de sesión. Esto significa que después de cada intento de adivinar símbolo tendremos que coger el valor de la cookie PHPSESSID nuevo. Esto se puede hacer directamente desde la respuesta en el intento de adivinar. En [2] es el inicio del procedimiento de validación de tiempo. Si $ indicación_horaria (un valor de time () en el primer ciclo de prueba) es inferior a las actuales de tiempo-10s o no hay intentos try actuales, entrar en la serie. Lo que esto realmente significa es que si somos más de 10 segundos desde que generado la marca de tiempo, entrar en el bucle. Primero, ¿qué ocurre si no se entra en el bucle? Los $ number_tries se incrementa y el cheque en [4] es golpeado. Esto simplemente comprueba si el número de intentos en este período de 10 segundos es superior a 5. Si es así, entonces fallan y obligar al usuario a esperar a que el resto de los 10 segundo temporizador. Humerously, el mensaje de error indica que se ven obligados a esperar 30 segundos, sin embargo, esto no es cierto y sólo tiene que esperar a los 10 segundos desde el primer intento.

Ahora, si el período de 10 segundos ha finalizado, el bucle en [2] se introduce. El cheque dentro del bucle en [3] es para ver si el $ indicación_horaria es más de 20 segundos. Aquí hay otra discrepancia cómico. Si se trata de más de 20 segundos, lo que realmente establece el número de intentos para 2 y si es menos de 20 segundos y luego se pone a 0. Por lo tanto, cuando entramos en el bucle después del 10 segundo período y antes de 20 segundos han aumentado, nos pondremos restablece a 0 permitiendo cinco intentos más. Si se introduce después de un período de 20 segundos, conseguiremos restablecer a 2. Por ello, si hay una marca de tiempo ahorrado desde hace mucho tiempo en la db, la persona sólo obtiene 3 intentos en lugar de la normal de 5 en el primer ciclo. Esta es la lógica opuesta a continuación, se indica en el comentario anterior el cheque en [3].

Mensaje Compromiso

Si bien esto no ha sido incorporado en la explotación en sí, después de compromiso para la ejecución de código arbitrario es fácil una vez que tenga credenciales de administrador. Basta con crear su propio paquete de puerta trasera o cargar un paquete vulnerable conocida de su elegir. Honestamente, haciendo su propio paquete de puerta trasera para descargar es muy fácil. Incluso puede alojarlo en el exterior y ponerla en medio de URL por lo que es mucho más fácil de añadir a la hazaña.

Conclusión y explotar

Después de leer todo esto usted debería ser capaz de entender la explotan y todas las etapas necesarias para ello. El explotan sí mismo toma un tiempo tranquilo a menos que usted es afortunado usted pudo. consigue el juggle tipo en el primer intento o la diezmilésima intentarlo. Después de probar una variedad de pruebas, el importe máximo de intentos de la hazaña tuvo fue 8.6k. Sería mejor para imaginar momentos en que la víctima no está en línea, al igual que durmiendo o fuera de su alcance, ya que mis propias pruebas han demostrado sobre los intentos 1-1.4K en una hora determinada. Por supuesto, apuntando a la rama 1.1.x será mucho más rápido

smf_juggle.py

PHP Tipo malabares Presentación (ODP

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *