The Challenge:
Optional Cats - Web 150 PointsOur agent has found out that GAI loves animals. It has created a banner network to support animal welfare organizations.
Reveal its secrets!
The Solution:
This was a fun challenge to tackle! Going to the provided link simply yields a site with an input box for a domain.
Testing a few domains, it seems that a valid domain must be inserted. This already makes me think that the use of a lookup is involved...perhaps a "dig" against the target. To test this theory I do a insert of the invalid domain "swag" and receive an error:
"We cannot verify your host!"
Repeating the submission with a semicolon in front -- ";swag" seems to work and moves on to the next page:
This is a good sign! It means that I see Remote Command Execution in the future :) If the use of a semicolon terminates the "dig" command then there is a good chance we can exploit this. The next step is to obtain the output of the command.
The returned script code references /js/plugin.js , so a quick navigation returns the content of this script:
Awesome, so it looks like this script is using a XMLHttpRequest in the background to retrieve /nextBanner?domain= along with a custom header "X-USER-ID".
(function(e,c){var d=function(a,b){this.element=a;this.id=b;this.init()};d.prototype.init=function(){this.loadImg();this.interval=setInterval(this.loadImg.bind(this),3E3)};d.prototype.loadImg=function(){var a=new XMLHttpRequest;a.open("GET","http://optcat.2016.volgactf.ru:8080/nextBanner?domain="+c.location.hostname);a.setRequestHeader("X-USER-ID",this.id);a.responseType="blob";a.onload=function(b){b=(c.URL||c.webkitURL).createObjectURL(a.response);this.element.attr("src",b)}.bind(this);a.onerror=function(){clearInterval(this.interval)}.bind(this);
a.send()};e.fn.catbanner=function(a){return this.each(function(){var b=e(this),c=new d(b,a);b.data("stickyInstance",c)})}})($,window);$(document).ready(function(){$("#cat").catbanner(1)});
I played around with this custom header for a while, as well as with various values for domain. After a lot of testing I came to the conclusion the the domain value must already exist in the database. AKA we must have submitted this domain previously. Still, we must get a more verbose output from the server if we are to successfully grab this flag.
Finally I replay the request in Burp Suite but using the HTTP method of "OPTIONS" in attempt to get some more information. Low and behold, it returns the ever-generous header of "X-DEBUG-ANSWER:" along with some juicy details of a dig output.
The header contents URL-Decoded:
Server: 188.93.16.19That's it :D That's the verbosity we needed. The final steps are to test the RCE hypothesis and make a submission on the homepage for the domain ";ls;", then to check the output of this by using out OPTIONS header trick.
Address: 188.93.16.19#53
Non-authoritative+answer:
Name: google.com
Address:+64.233.161.101
Name: google.com
Address:+64.233.161.113
Name: google.com
Address:+64.233.161.102
Name: google.com
Address:+64.233.161.100
Name: google.com
Address:+64.233.161.138
Name: google.com
Address:+64.233.161.139
Hi
nobody:~$+nobody:~$
The URL Decoded content of the debug header with this new domain:
banners
cats.mv.db
flag.txt
naughty-options-1.0-SNAPSHOT-standalone.jar
public
Hi
nobody:~$+nobody:~$Last but not least, we repeat this process but with the domain ";cat flag.txt" and use the OPTIONS header to request ";cat+flag.txt".
URL decoding the contents of the resulting debug header reveals our flag :)