# Web: SSRF 101

![image](https://user-
images.githubusercontent.com/74207547/160583998-d4882e9c-07f9-43a8-870d-3a732eb78781.png)

We are given the source code

![image](https://user-
images.githubusercontent.com/74207547/160570547-102b82f1-1b7b-41a0-912a-5213fb8b9617.png)

## Source code review

>## public.js

```js  
const { URL } = require('url')  
const http = require('http')  
const express = require('express')  
const app = express()  
const publicPort = 80  
const private1Port = 1001

app.get('/', (req, res) => {  
res.sendFile(__dirname + '/public.js')  
})

// Use this endpoint to reach a web server which  
// is only locally accessible. Try: /ssrf?path=/  
app.get('/ssrf', (req, res) => {  
const path = req.query.path  
if (typeof path !== 'string' || path.length === 0) {  
res.send('path must be a non-empty string')  
}  
else {  
const url = `http://localhost:${private1Port}${path}`  
const parsedUrl = new URL(url)

if (parsedUrl.hostname !== 'localhost') {  
// Is it even possible to get in here???  
res.send('sorry, you can only talk to localhost')  
}  
else {  
// Make the request and return its content as our content.  
http.get(parsedUrl.href, ssrfRes => {  
let contentType = ssrfRes.headers['content-type']

let body = ''  
ssrfRes.on('data', chunk => {  
body += chunk  
})

ssrfRes.on('end', () => {  
if (contentType) {  
res.setHeader('Content-Type', contentType)  
}  
res.send(body)  
})  
}).on('error', function(e) {  
res.send("Got error: " + e.message)  
})  
}  
}  
})

// this port is exposed publicly  
app.listen(publicPort, () => {  
console.log(`Listening on ${publicPort}`)  
})  
```

Looks like we've got two endpoints:  
* `/`- prints the source of public.js file  
* `/ssrf` - with **path** param we can request a file from web root and the server will return its content

\---  
Let's search how we can obtain the flag

>## private2.js  
```js  
const express = require('express')  
const app = express()  
const private2Port = 10011

app.get('/', (req, res) => {  
res.sendFile(__dirname + '/private2.js')  
})

app.get('/flag', (req, res) => {  
res.sendFile(__dirname + '/flag.txt')  
})

// this port is only exposed locally  
app.listen(private2Port, () => {  
console.log(`Listening on ${private2Port}`)  
})  
```

Request to the server on localport 10011 with /flag path will return flag
content.  
```js  
const private1Port = 1001  
...  
// this port is exposed publicly  
app.listen(publicPort, () => {  
console.log(`Listening on ${publicPort}`)  
})  
```  
Main web app is running on port 1001 which is weirdly similar. Let's see if we
can leverage ssrf endpoint to have the server send a request to our flag.

```js  
const path = req.query.path  
if (typeof path !== 'string' || path.length === 0) {  
res.send('path must be a non-empty string')  
}  
else {  
const url = `http://localhost:${private1Port}${path}`  
const parsedUrl = new URL(url)  
```

The server only checks if the path is a string and is not empty. There is no
slash between port and path values which can easily lead to ssrf

## Finding a way to read the flag

Because there is no proper validation of user input we can inject additional
`1` to the port number and then enter the path to the endpoint with our flag
`/flag`. As the result of that server will make get request to
`http://localhost:10011/flag`

![image](https://user-
images.githubusercontent.com/74207547/160579438-a61416ea-024c-493e-923e-3324c627d608.png)

## FLAG: wsc{ssrf_c4n_b3_fun_xl9m782}  

Original writeup
(https://github.com/Dom0nS/ctf/blob/main/CTF_writeups/Wolvsec-
ctf-2022/web_ssrf_101.md).