juice-shop

Форк
0
/
fileUpload.ts 
111 строк · 4.6 Кб
1
/*
2
 * Copyright (c) 2014-2024 Bjoern Kimminich & the OWASP Juice Shop contributors.
3
 * SPDX-License-Identifier: MIT
4
 */
5

6
import os from 'os'
7
import fs = require('fs')
8
import challengeUtils = require('../lib/challengeUtils')
9
import { type NextFunction, type Request, type Response } from 'express'
10
import path from 'path'
11
import * as utils from '../lib/utils'
12
import { challenges } from '../data/datacache'
13

14
const libxml = require('libxmljs')
15
const vm = require('vm')
16
const unzipper = require('unzipper')
17

18
function ensureFileIsPassed ({ file }: Request, res: Response, next: NextFunction) {
19
  if (file != null) {
20
    next()
21
  }
22
}
23

24
function handleZipFileUpload ({ file }: Request, res: Response, next: NextFunction) {
25
  if (utils.endsWith(file?.originalname.toLowerCase(), '.zip')) {
26
    if (((file?.buffer) != null) && utils.isChallengeEnabled(challenges.fileWriteChallenge)) {
27
      const buffer = file.buffer
28
      const filename = file.originalname.toLowerCase()
29
      const tempFile = path.join(os.tmpdir(), filename)
30
      fs.open(tempFile, 'w', function (err, fd) {
31
        if (err != null) { next(err) }
32
        fs.write(fd, buffer, 0, buffer.length, null, function (err) {
33
          if (err != null) { next(err) }
34
          fs.close(fd, function () {
35
            fs.createReadStream(tempFile)
36
              .pipe(unzipper.Parse())
37
              .on('entry', function (entry: any) {
38
                const fileName = entry.path
39
                const absolutePath = path.resolve('uploads/complaints/' + fileName)
40
                challengeUtils.solveIf(challenges.fileWriteChallenge, () => { return absolutePath === path.resolve('ftp/legal.md') })
41
                if (absolutePath.includes(path.resolve('.'))) {
42
                  entry.pipe(fs.createWriteStream('uploads/complaints/' + fileName).on('error', function (err) { next(err) }))
43
                } else {
44
                  entry.autodrain()
45
                }
46
              }).on('error', function (err: unknown) { next(err) })
47
          })
48
        })
49
      })
50
    }
51
    res.status(204).end()
52
  } else {
53
    next()
54
  }
55
}
56

57
function checkUploadSize ({ file }: Request, res: Response, next: NextFunction) {
58
  if (file != null) {
59
    challengeUtils.solveIf(challenges.uploadSizeChallenge, () => { return file?.size > 100000 })
60
  }
61
  next()
62
}
63

64
function checkFileType ({ file }: Request, res: Response, next: NextFunction) {
65
  const fileType = file?.originalname.substr(file.originalname.lastIndexOf('.') + 1).toLowerCase()
66
  challengeUtils.solveIf(challenges.uploadTypeChallenge, () => {
67
    return !(fileType === 'pdf' || fileType === 'xml' || fileType === 'zip')
68
  })
69
  next()
70
}
71

72
function handleXmlUpload ({ file }: Request, res: Response, next: NextFunction) {
73
  if (utils.endsWith(file?.originalname.toLowerCase(), '.xml')) {
74
    challengeUtils.solveIf(challenges.deprecatedInterfaceChallenge, () => { return true })
75
    if (((file?.buffer) != null) && utils.isChallengeEnabled(challenges.deprecatedInterfaceChallenge)) { // XXE attacks in Docker/Heroku containers regularly cause "segfault" crashes
76
      const data = file.buffer.toString()
77
      try {
78
        const sandbox = { libxml, data }
79
        vm.createContext(sandbox)
80
        const xmlDoc = vm.runInContext('libxml.parseXml(data, { noblanks: true, noent: true, nocdata: true })', sandbox, { timeout: 2000 })
81
        const xmlString = xmlDoc.toString(false)
82
        challengeUtils.solveIf(challenges.xxeFileDisclosureChallenge, () => { return (utils.matchesEtcPasswdFile(xmlString) || utils.matchesSystemIniFile(xmlString)) })
83
        res.status(410)
84
        next(new Error('B2B customer complaints via file upload have been deprecated for security reasons: ' + utils.trunc(xmlString, 400) + ' (' + file.originalname + ')'))
85
      } catch (err: any) { // TODO: Remove any
86
        if (utils.contains(err.message, 'Script execution timed out')) {
87
          if (challengeUtils.notSolved(challenges.xxeDosChallenge)) {
88
            challengeUtils.solve(challenges.xxeDosChallenge)
89
          }
90
          res.status(503)
91
          next(new Error('Sorry, we are temporarily not available! Please try again later.'))
92
        } else {
93
          res.status(410)
94
          next(new Error('B2B customer complaints via file upload have been deprecated for security reasons: ' + err.message + ' (' + file.originalname + ')'))
95
        }
96
      }
97
    } else {
98
      res.status(410)
99
      next(new Error('B2B customer complaints via file upload have been deprecated for security reasons (' + file?.originalname + ')'))
100
    }
101
  }
102
  res.status(204).end()
103
}
104

105
module.exports = {
106
  ensureFileIsPassed,
107
  handleZipFileUpload,
108
  checkUploadSize,
109
  checkFileType,
110
  handleXmlUpload
111
}
112

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.