> For the complete documentation index, see [llms.txt](https://evanluke.gitbook.io/appsec/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://evanluke.gitbook.io/appsec/ssrf-1/ssrf/source-code-review.md).

# Source Code Review

## OWASP Juice Shop&#x20;

There is a SSRF vulnerability on OWASP Juice Shop on the profile page via the image upload functionality. When clicking on the Link Gravatar button with a user supplied input, the backend server reaches out to the supplied URL to download the image. When a user supplies a URL and clicks 'Link Gravatar' the server sends a HTTP request with the imageUrl parameter that is vulnerable to SSRF.&#x20;

![Juice Shop Gravatar Link Input](/files/-MPCD4bBzSrOWi7r4a5b)

The code below is profileImageUrlUpload.js. This code handles the functionality for URL image upload.&#x20;

The profileImageUrlUpload function checks if the user is logged in on line 18, if so it sends a HTTP request to the specified URL. There are no input checks done on the URL such as an allow-list check to make sure the URL being accessed is explicitly allowed. In the challenge you need to supply a backend URL in the imageUrl parameter to complete the challenge and perform SSRF on a backend server. The URL to supply is `http://localhost:3000/solve/challenges/server-side?key=tRy_H4rd3r_n0thIng_iS_Imp0ssibl3`

```
/*
 * Copyright (c) 2014-2021 Bjoern Kimminich.
 * SPDX-License-Identifier: MIT
 */

const fs = require('fs')
const models = require('../models/index')
const insecurity = require('../lib/insecurity')
const request = require('request')
const logger = require('../lib/logger')

module.exports = function profileImageUrlUpload () {
  return (req, res, next) => {
    if (req.body.imageUrl !== undefined) {
      const url = req.body.imageUrl
      if (url.match(/(.)*solve\/challenges\/server-side(.)*/) !== null) req.app.locals.abused_ssrf_bug = true
      const loggedInUser = insecurity.authenticatedUsers.get(req.cookies.token)
      if (loggedInUser) {
        const imageRequest = request
          .get(url)
          .on('error', function (err) {
            models.User.findByPk(loggedInUser.data.id).then(user => { return user.update({ profileImage: url }) }).catch(error => { next(error) })
            logger.warn('Error retrieving user profile image: ' + err.message + '; using image link directly')
          })
          .on('response', function (res) {
            if (res.statusCode === 200) {
              const ext = ['jpg', 'jpeg', 'png', 'svg', 'gif'].includes(url.split('.').slice(-1)[0].toLowerCase()) ? url.split('.').slice(-1)[0].toLowerCase() : 'jpg'
              imageRequest.pipe(fs.createWriteStream(`frontend/dist/frontend/assets/public/images/uploads/${loggedInUser.data.id}.${ext}`))
              models.User.findByPk(loggedInUser.data.id).then(user => { return user.update({ profileImage: `/assets/public/images/uploads/${loggedInUser.data.id}.${ext}` }) }).catch(error => { next(error) })
            } else models.User.findByPk(loggedInUser.data.id).then(user => { return user.update({ profileImage: url }) }).catch(error => { next(error) })
          })
      } else {
        next(new Error('Blocked illegal activity by ' + req.connection.remoteAddress))
      }
    }
    res.location(process.env.BASE_PATH + '/profile')
    res.redirect(process.env.BASE_PATH + '/profile')
  }
}

//profileImageUrlUpload.js
//https://github.com/bkimminich/juice-shop/blob/master/routes/profileImageUrlUpload.js
```

## Damn Vulnerable Web App&#x20;
