├── .dockerignore ├── .gitignore ├── Dockerfile ├── README.md ├── rpmsquirt.dat ├── rpmsquirt.sh └── utils.js /.dockerignore: -------------------------------------------------------------------------------- 1 | README.md 2 | .git 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | wget-1.15.tar.gz 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos7.2.1511 2 | MAINTAINER adrianp@stindustries.net 3 | 4 | # If you need to use a proxy to get to the internet, build with: 5 | # docker build --build-arg CURL_OPTIONS="..." 6 | # 7 | # The default is empty (no special options). 8 | # 9 | ARG CURL_OPTIONS="" 10 | 11 | # Prep environment 12 | # 13 | RUN yum -y install deltarpm && yum -y update 14 | 15 | # Install build utils 16 | # 17 | RUN touch /var/lib/rpm/* && \ 18 | yum -y install bison gnutls-devel gcc libidn-devel gcc-c++ bzip2 && \ 19 | yum clean all 20 | 21 | # wget - command line utility (installed via. RPM) 22 | # 23 | # https://www.cvedetails.com/cve/CVE-2014-4877/ 24 | # 25 | RUN curl -LO ${CURL_OPTIONS} \ 26 | http://vault.centos.org/7.0.1406/os/x86_64/Packages/wget-1.14-10.el7.x86_64.rpm && \ 27 | yum -y install wget-1.14-10.el7.x86_64.rpm && \ 28 | rm *.rpm 29 | 30 | # wget - command line utility (manual install) 31 | # 32 | # https://www.cvedetails.com/cve/CVE-2014-4877/ 33 | # 34 | RUN curl -LO ${CURL_OPTIONS} \ 35 | http://www.mirrorservice.org/sites/ftp.gnu.org/gnu/wget/wget-1.15.tar.gz && \ 36 | tar zxf wget-1.15.tar.gz && \ 37 | cd wget-1.15 && \ 38 | ./configure --prefix=/opt/wget && \ 39 | make && \ 40 | make install && \ 41 | cd .. && \ 42 | rm -rf wget-1.15 && \ 43 | rm *.tar.gz 44 | 45 | # p7zip - command line utility (manual install) 46 | # 47 | # https://www.cvedetails.com/cve/CVE-2015-1038/ 48 | # 49 | RUN curl -LO ${CURL_OPTIONS} \ 50 | https://sourceforge.net/projects/p7zip/files/p7zip/9.20.1/p7zip_9.20.1_src_all.tar.bz2 && \ 51 | bzcat p7zip_9.20.1_src_all.tar.bz2 | tar x && \ 52 | cd p7zip_9.20.1 && \ 53 | cp install.sh install.sh.orig && \ 54 | cat install.sh.orig | sed -e 's|DEST_HOME=/usr/local|DEST_HOME=/opt/p7zip|g' > install.sh && \ 55 | make && \ 56 | ./install.sh && \ 57 | cd - && \ 58 | rm -rf p7zip_9.20.1 && \ 59 | rm *.tar.bz2 60 | 61 | # drupal - PHP application (manual install) 62 | # 63 | # http://www.cvedetails.com/vulnerability-list/vendor_id-1367/product_id-2387/version_id-192973/Drupal-Drupal-7.42.html 64 | # 65 | RUN curl -LO ${CURL_OPTIONS} \ 66 | https://ftp.drupal.org/files/projects/drupal-7.42.tar.gz && \ 67 | tar zxf drupal-7.42.tar.gz && \ 68 | mkdir /opt/drupal && \ 69 | cd drupal-7.42 && \ 70 | cp -R . /opt/drupal && \ 71 | cd - && \ 72 | rm -rf drupal-7.42 && \ 73 | rm -f *.tar.gz 74 | 75 | # tomcat - Java application (manual install) 76 | # 77 | # https://www.cvedetails.com/cve/CVE-2016-3092/ 78 | # 79 | RUN curl -LO ${CURL_OPTIONS} \ 80 | http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.69/bin/apache-tomcat-7.0.69.tar.gz && \ 81 | tar zxf apache-tomcat-7.0.69.tar.gz && \ 82 | mkdir /opt/tomcat && \ 83 | cd apache-tomcat-7.0.69 && \ 84 | cp -R . /opt/tomcat && \ 85 | cd - && \ 86 | rm -rf apache-tomcat-7.0.69 && \ 87 | rm -f *.tar.gz 88 | 89 | # OpenJDK - Java (RPM install) 90 | # 91 | # 92 | RUN curl -LO ${CURL_OPTIONS} \ 93 | http://www.nic.funet.fi/pub/Linux/INSTALL/scientific/7.0/x86_64/updates/security/java-1.8.0-openjdk-1.8.0.91-0.b14.el7_2.x86_64.rpm && \ 94 | curl -LO ${CURL_OPTIONS} \ 95 | http://www.nic.funet.fi/pub/Linux/INSTALL/scientific/7.0/x86_64/updates/security/java-1.8.0-openjdk-headless-1.8.0.91-0.b14.el7_2.x86_64.rpm && \ 96 | touch /var/lib/rpm/* && \ 97 | yum -y install java-1.8.0-openjdk-1.8.0.91-0.b14.el7_2.x86_64.rpm java-1.8.0-openjdk-headless-1.8.0.91-0.b14.el7_2.x86_64.rpm && \ 98 | rm -f *.rpm && \ 99 | echo "exclude=java-1.8.0-openjdk java-1.8.0-openjdk-headless" >> /etc/yum.conf 100 | 101 | # tomcat - Java application (RPM install) 102 | # 103 | # CVE-2013-4590, CVE-2014-0119, CVE-2014-0099, CVE-2014-0096, CVE-2014-0075 104 | # 105 | RUN curl -LO ${CURL_OPTIONS} \ 106 | http://vault.centos.org/7.0.1406/os/x86_64/Packages/tomcat-7.0.42-4.el7.noarch.rpm && \ 107 | curl -LO ${CURL_OPTIONS} \ 108 | http://vault.centos.org/7.0.1406/os/x86_64/Packages/tomcat-el-2.2-api-7.0.42-4.el7.noarch.rpm && \ 109 | curl -LO ${CURL_OPTIONS} \ 110 | http://vault.centos.org/7.0.1406/os/x86_64/Packages/tomcat-jsp-2.2-api-7.0.42-4.el7.noarch.rpm && \ 111 | curl -LO ${CURL_OPTIONS} \ 112 | http://vault.centos.org/7.0.1406/os/x86_64/Packages/tomcat-lib-7.0.42-4.el7.noarch.rpm && \ 113 | curl -LO ${CURL_OPTIONS} \ 114 | http://vault.centos.org/7.0.1406/os/x86_64/Packages/tomcat-servlet-3.0-api-7.0.42-4.el7.noarch.rpm && \ 115 | touch /var/lib/rpm/* && \ 116 | yum -y install yum install tomcat-7.0.42-4.el7.noarch.rpm tomcat-lib-7.0.42-4.el7.noarch.rpm tomcat-servlet-3.0-api-7.0.42-4.el7.noarch.rpm tomcat-el-2.2-api-7.0.42-4.el7.noarch.rpm tomcat-jsp-2.2-api-7.0.42-4.el7.noarch.rpm && \ 117 | rm -f *.rpm 118 | 119 | # hpack-2.1.1 - Python lib 120 | # 121 | # https://www.cvedetails.com/cve/CVE-2016-6581/ 122 | # 123 | RUN curl -LO ${CURL_OPTIONS} \ 124 | https://pypi.python.org/packages/8c/2b/e6e2f554368785c7eb68d618fd6457625be1535e807f6abf11c7db710f34/hpack-2.1.1.tar.gz && \ 125 | tar xvf hpack-2.1.1.tar.gz && \ 126 | mkdir /opt/hpack && \ 127 | cd hpack-2.1.1 && \ 128 | cp -R . /opt/hpack && \ 129 | cd - && \ 130 | rm -rf hpack-2.1.1 && \ 131 | rm -f *.tar.gz 132 | 133 | # commons-beanutils-1.8 - Jar file 134 | # 135 | # https://www.cvedetails.com/cve/CVE-2014-0114/ 136 | # 137 | RUN curl -LO ${CURL_OPTIONS} \ 138 | http://repo1.maven.org/maven2/commons-beanutils/commons-beanutils/1.8.0/commons-beanutils-1.8.0-sources.jar 139 | 140 | # activesupport 4.2.1 - GEM package (Ruby) 141 | # 142 | # CVE-2015-3227, CVE-2015-3226 143 | # 144 | RUN curl -LO ${CURL_OPTIONS} \ 145 | http://rubygems.org/downloads/activesupport-4.2.1.gem 146 | 147 | # utils.js - Javascript file 148 | # 149 | # CVE-2015-3227,CVE-2015-3226 150 | # 151 | COPY utils.js /tmp/utils.js 152 | # nodejs - Javascript (installed manually) 153 | # 154 | # https://www.cvedetails.com/vulnerability-list/vendor_id-12113/product_id-30764/version_id-192848/Nodejs-Node.js-0.10.41.html 155 | # 156 | RUN curl -LO ${CURL_OPTIONS} \ 157 | https://nodejs.org/dist/v0.10.41/node-v0.10.41-linux-x64.tar.gz && \ 158 | tar zxf node-v0.10.41-linux-x64.tar.gz && \ 159 | mkdir /opt/nodejs && \ 160 | cd node-v0.10.41-linux-x64 && \ 161 | cp -R . /opt/nodejs && \ 162 | cd - && \ 163 | rm -rf node-v0.10.41-linux-x64 && \ 164 | rm -rf *.tar.gz 165 | 166 | # bash - command line utility (installed manually) 167 | # 168 | # https://www.cvedetails.com/vulnerability-list/vendor_id-72/product_id-21050/version_id-172000/GNU-Bash-4.3.html 169 | # 170 | RUN curl -LO ${CURL_OPTIONS} \ 171 | https://ftp.heanet.ie/mirrors/ftp.gnu.org/gnu/bash/bash-4.3.tar.gz && \ 172 | tar zxf bash-4.3.tar.gz && \ 173 | mkdir /opt/bash && cd bash-4.3 && \ 174 | ./configure --prefix=/opt/bash && \ 175 | make && \ 176 | make install && \ 177 | cd .. && \ 178 | rm -rf bash-4.3 && \ 179 | rm -rf *.tar.gz 180 | 181 | # rpmsquirt 182 | # 183 | RUN touch /var/lib/rpm/* && \ 184 | yum -y install rpm-build redhat-rpm-config rpmdevtools 185 | COPY rpmsquirt.sh /rpmsquirt.sh 186 | COPY rpmsquirt.dat / 187 | RUN /rpmsquirt.sh 188 | 189 | # Precautionary failure with messages 190 | # 191 | CMD echo 'Vulnerable image' && /bin/false 192 | 193 | # Basic labels. 194 | # http://label-schema.org/ 195 | # 196 | LABEL \ 197 | org.label-schema.name="bad-dockerfile" \ 198 | org.label-schema.description="Reference Dockerfile containing software with known vulnerabilities." \ 199 | org.label-schema.url="http://www.stindustries.net/docker/bad-dockerfile/" \ 200 | org.label-schema.vcs-type="Git" \ 201 | org.label-schema.vcs-url="https://github.com/ianmiell/bad-dockerfile" \ 202 | org.label-schema.docker.dockerfile="/Dockerfile" 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Bad Dockerfile 2 | ============== 3 | 4 | Overview 5 | -------- 6 | 7 | Reference Dockerfile containing software with known vulnerabilities. 8 | 9 | Includes vulnerable binaries (bash shellshock, wget directory traversal) with 10 | CVE entries for testing Docker image scanning solutions. 11 | 12 | For full details see: http://www.stindustries.net/docker/bad-dockerfile/ 13 | 14 | Created by Adrian Portelli. 15 | 16 | Image available here: https://hub.docker.com/r/imiell/bad-dockerfile 17 | 18 | But note that it's insecure! 19 | 20 | 21 | How to 22 | ====== 23 | 24 | Pull an already-built image 25 | --------------------------- 26 | 27 | docker pull imiell/bad-dockerfile 28 | 29 | 30 | Build locally 31 | ------------- 32 | 33 | If your build host can reach the Internet: 34 | 35 | docker build -t imiell/bad-dockerfile ./ 36 | 37 | 38 | If your build host needs proxy settings to reach the Internet: 39 | 40 | # Replace ... with exotic curl options for your build environment. 41 | docker build -t imiell/bad-dockerfile --build-arg CURL_OPTIONS="..." ./ 42 | 43 | 44 | View labels 45 | ----------- 46 | 47 | Each built image has labels that generally follow http://label-schema.org/ 48 | 49 | View a specific label, such as the image description: 50 | 51 | docker inspect \ 52 | -f '{{ index .Config.Labels "org.label-schema.description" }}' \ 53 | imiell/bad-dockerfile 54 | 55 | Query all the labels inside a built image: 56 | 57 | docker inspect imiell/bad-dockerfile | jq -M '.[].Config.Labels' 58 | -------------------------------------------------------------------------------- /rpmsquirt.dat: -------------------------------------------------------------------------------- 1 | vulnwebapp 1.0 2 | vulncliapp 2.0 3 | -------------------------------------------------------------------------------- /rpmsquirt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | RPMSQUIRT=/rpmsquirt.dat 4 | RPMBASE=/root/rpmbuild 5 | 6 | createpkg() { 7 | PKGNAME=$1 8 | PKGVERS=$2 9 | 10 | if [ -f ${RPMBASE}/SPECS/${PKGNAME}.spec ]; then 11 | rm -f ${RPMBASE}/SPECS/${PKGNAME}.spec 12 | fi 13 | 14 | cd / 15 | mkdir -p ${RPMBASE}/{BUILD,RPMS,SOURCES,SPECS,SRPMS,tmp} 16 | 17 | cd ${RPMBASE}/SPECS 18 | rpmdev-newspec -t dummy ${PKGNAME}.spec 19 | mv ${PKGNAME}.spec ${PKGNAME}.spec.pre 20 | cat ${PKGNAME}.spec.pre | \ 21 | sed -e 's|1%{?dist}|0|g' | \ 22 | sed -e "s|1.0|${PKGVERS}|g" > \ 23 | ${PKGNAME}.spec 24 | 25 | cd ${RPMBASE}/SOURCES 26 | touch ${PKGNAME} 27 | tar zcf ${PKGNAME}-${PKGVERS}.tar.gz ${PKGNAME} 28 | 29 | cd ${RPMBASE} 30 | rpmbuild -ba SPECS/${PKGNAME}.spec 31 | } 32 | 33 | installpkg() { 34 | PKGNAME=$1 35 | PKGVERS=$2 36 | 37 | rpm -ivh ${RPMBASE}/RPMS/x86_64/${PKGNAME}-${PKGVERS}-0.x86_64.rpm 38 | } 39 | 40 | if [ ! -f /rpmsquirt.dat ]; then 41 | echo Error: /rpmsquirt.dat not found 42 | exit 1 43 | fi 44 | 45 | while read -r line 46 | do 47 | _pkgname=`echo $line | awk -F ' ' '{print $1}'` 48 | _pkgvers=`echo $line | awk -F ' ' '{print $2}'` 49 | 50 | createpkg ${_pkgname} ${_pkgvers} 51 | installpkg ${_pkgname} ${_pkgvers} 52 | done < "${RPMSQUIRT}" 53 | 54 | rm -rf /root/rpmbuild /rpmbuild/ 55 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | // Load modules 2 | 3 | var Sntp = require('sntp'); 4 | var Boom = require('boom'); 5 | 6 | 7 | // Declare internals 8 | 9 | var internals = {}; 10 | 11 | 12 | exports.version = function () { 13 | 14 | return require('../package.json').version; 15 | }; 16 | 17 | 18 | // Extract host and port from request 19 | 20 | // $1 $2 21 | internals.hostHeaderRegex = /^(?:(?:\r\n)?\s)*((?:[^:]+)|(?:\[[^\]]+\]))(?::(\d+))?(?:(?:\r\n)?\s)*$/; // (IPv4, hostname)|(IPv6) 22 | 23 | 24 | exports.parseHost = function (req, hostHeaderName) { 25 | 26 | hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host'); 27 | var hostHeader = req.headers[hostHeaderName]; 28 | if (!hostHeader) { 29 | return null; 30 | } 31 | 32 | var hostParts = hostHeader.match(internals.hostHeaderRegex); 33 | if (!hostParts) { 34 | return null; 35 | } 36 | 37 | return { 38 | name: hostParts[1], 39 | port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80)) 40 | }; 41 | }; 42 | 43 | 44 | // Parse Content-Type header content 45 | 46 | exports.parseContentType = function (header) { 47 | 48 | if (!header) { 49 | return ''; 50 | } 51 | 52 | return header.split(';')[0].trim().toLowerCase(); 53 | }; 54 | 55 | 56 | // Convert node's to request configuration object 57 | 58 | exports.parseRequest = function (req, options) { 59 | 60 | if (!req.headers) { 61 | return req; 62 | } 63 | 64 | // Obtain host and port information 65 | 66 | if (!options.host || !options.port) { 67 | var host = exports.parseHost(req, options.hostHeaderName); 68 | if (!host) { 69 | return new Error('Invalid Host header'); 70 | } 71 | } 72 | 73 | var request = { 74 | method: req.method, 75 | url: req.url, 76 | host: options.host || host.name, 77 | port: options.port || host.port, 78 | authorization: req.headers.authorization, 79 | contentType: req.headers['content-type'] || '' 80 | }; 81 | 82 | return request; 83 | }; 84 | 85 | 86 | exports.now = function (localtimeOffsetMsec) { 87 | 88 | return Sntp.now() + (localtimeOffsetMsec || 0); 89 | }; 90 | 91 | 92 | exports.nowSecs = function (localtimeOffsetMsec) { 93 | 94 | return Math.floor(exports.now(localtimeOffsetMsec) / 1000); 95 | }; 96 | 97 | 98 | // Parse Hawk HTTP Authorization header 99 | 100 | exports.parseAuthorizationHeader = function (header, keys) { 101 | 102 | keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg']; 103 | 104 | if (!header) { 105 | return Boom.unauthorized(null, 'Hawk'); 106 | } 107 | 108 | var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something] 109 | if (!headerParts) { 110 | return Boom.badRequest('Invalid header syntax'); 111 | } 112 | 113 | var scheme = headerParts[1]; 114 | if (scheme.toLowerCase() !== 'hawk') { 115 | return Boom.unauthorized(null, 'Hawk'); 116 | } 117 | 118 | var attributesString = headerParts[2]; 119 | if (!attributesString) { 120 | return Boom.badRequest('Invalid header syntax'); 121 | } 122 | 123 | var attributes = {}; 124 | var errorMessage = ''; 125 | var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) { 126 | 127 | // Check valid attribute names 128 | 129 | if (keys.indexOf($1) === -1) { 130 | errorMessage = 'Unknown attribute: ' + $1; 131 | return; 132 | } 133 | 134 | // Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9 135 | 136 | if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) { 137 | errorMessage = 'Bad attribute value: ' + $1; 138 | return; 139 | } 140 | 141 | // Check for duplicates 142 | 143 | if (attributes.hasOwnProperty($1)) { 144 | errorMessage = 'Duplicate attribute: ' + $1; 145 | return; 146 | } 147 | 148 | attributes[$1] = $2; 149 | return ''; 150 | }); 151 | 152 | if (verify !== '') { 153 | return Boom.badRequest(errorMessage || 'Bad header format'); 154 | } 155 | 156 | return attributes; 157 | }; 158 | 159 | 160 | exports.unauthorized = function (message) { 161 | 162 | return Boom.unauthorized(message, 'Hawk'); 163 | }; 164 | 165 | --------------------------------------------------------------------------------