# 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;


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://evanluke.gitbook.io/appsec/ssrf-1/ssrf/source-code-review.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
