diff --git a/.github/workflows/package-pr.yaml b/.github/workflows/package-pr.yaml index ac940d9..d5e2595 100644 --- a/.github/workflows/package-pr.yaml +++ b/.github/workflows/package-pr.yaml @@ -74,6 +74,7 @@ jobs: output_max_size: 1024 max_process_count: 64 max_open_files: 2048 + max_file_size: 1000000 repo_url: http://localhost:8000/index write-mode: overwrite diff --git a/api/src/config.js b/api/src/config.js index d98a4b4..c97b64c 100644 --- a/api/src/config.js +++ b/api/src/config.js @@ -102,6 +102,12 @@ const options = [ default: 2048, validators: [] }, + { + key: 'max_file_size', + desc: 'Max file size in bytes for a file', + default: 1000000, //1MB + validators: [] + }, { key: 'repo_url', desc: 'URL of repo index', diff --git a/api/src/globals.js b/api/src/globals.js index 300558e..e632a88 100644 --- a/api/src/globals.js +++ b/api/src/globals.js @@ -16,5 +16,11 @@ module.exports = { }, version: require('../package.json').version, platform, - pkg_installed_file: '.ppman-installed' //Used as indication for if a package was installed + pkg_installed_file: '.ppman-installed', //Used as indication for if a package was installed + clean_directories: [ + "/dev/shm", + "/run/lock", + "/tmp", + "/var/tmp" + ] }; diff --git a/api/src/job.js b/api/src/job.js index 2692bc1..b711ac1 100644 --- a/api/src/job.js +++ b/api/src/job.js @@ -74,7 +74,8 @@ class Job { const prlimit = [ 'prlimit', '--nproc=' + config.max_process_count, - '--nofile=' + config.max_open_files + '--nofile=' + config.max_open_files, + '--fsize=' + config.max_file_size ]; const proc_call = [ @@ -182,12 +183,10 @@ class Job { }; } - async cleanup() { - logger.info(`Cleaning up job uuid=${this.uuid}`); - await fs.rm(this.dir, { recursive: true, force: true }); - let processes = [1] - while(processes.length > 0){ + async cleanup_processes(){ + let processes = [1]; + while(processes.length > 0){ processes = await ps_list(); processes = processes.filter(proc => proc.uid == this.uid); @@ -211,8 +210,32 @@ class Job { wait_pid(proc.pid); } } - + } + async cleanup_filesystem(){ + + for (const clean_path of globals.clean_directories) { + const contents = await fs.readdir(clean_path); + + for (const file of contents) { + const file_path = path.join(clean_path, file); + const stat = await fs.stat(file_path); + if(stat.uid == this.uid) + await fs.rm(file_path, { recursive: true, force: true }); + } + + } + + await fs.rm(this.dir, { recursive: true, force: true }); + } + + async cleanup() { + logger.info(`Cleaning up job uuid=${this.uuid}`); + + await Promise.all([ + this.cleanup_processes(), + this.cleanup_filesystem() + ]); } } diff --git a/docker-compose.yaml b/docker-compose.yaml index 764a55b..4080638 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -11,3 +11,4 @@ services: - ./data/piston:/piston tmpfs: - /piston/jobs:exec + - /tmp diff --git a/packages/cobol/3.1.2/build.sh b/packages/cobol/3.1.2/build.sh new file mode 100755 index 0000000..1156fa8 --- /dev/null +++ b/packages/cobol/3.1.2/build.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# Put instructions to build your package in here +PREFIX=$(realpath $(dirname $0)) + +mkdir -p build + +cd build + +curl -OL "https://downloads.sourceforge.net/project/gnucobol/gnucobol/3.1/gnucobol-3.1.2.tar.xz" + +tar xf gnucobol-3.1.2.tar.xz --strip-components=1 + +# === autoconf based === +./configure --prefix "$PREFIX" --without-db + +make -j$(nproc) +make install -j$(nproc) +cd ../ +rm -rf build diff --git a/packages/cobol/3.1.2/compile b/packages/cobol/3.1.2/compile new file mode 100755 index 0000000..051eb75 --- /dev/null +++ b/packages/cobol/3.1.2/compile @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +cobc -o binary --free -x -L lib "$@" +chmod +x binary + diff --git a/packages/cobol/3.1.2/environment b/packages/cobol/3.1.2/environment new file mode 100644 index 0000000..ca711d7 --- /dev/null +++ b/packages/cobol/3.1.2/environment @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +export PATH=$PWD/bin:$PATH +export LD_LIBRARY_PATH=$PWD/lib + diff --git a/packages/cobol/3.1.2/metadata.json b/packages/cobol/3.1.2/metadata.json new file mode 100644 index 0000000..cf3e7e1 --- /dev/null +++ b/packages/cobol/3.1.2/metadata.json @@ -0,0 +1,5 @@ +{ + "language": "cobol", + "version": "3.1.2", + "aliases": ["cob"] +} diff --git a/packages/cobol/3.1.2/run b/packages/cobol/3.1.2/run new file mode 100755 index 0000000..9dcedfa --- /dev/null +++ b/packages/cobol/3.1.2/run @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +shift +./binary "$@" + diff --git a/packages/cobol/3.1.2/test.cob b/packages/cobol/3.1.2/test.cob new file mode 100644 index 0000000..1a03e66 --- /dev/null +++ b/packages/cobol/3.1.2/test.cob @@ -0,0 +1,8 @@ +*> Test Program +identification division. +program-id. ok-test. + +procedure division. +display "OK" +goback. +end program ok-test. diff --git a/packages/gcc/10.2.0/build.sh b/packages/gcc/10.2.0/build.sh index 0f4be80..0ed183a 100755 --- a/packages/gcc/10.2.0/build.sh +++ b/packages/gcc/10.2.0/build.sh @@ -17,7 +17,7 @@ tar xzf gcc.tar.gz --strip-components=1 cd ../obj # === autoconf based === -../build/configure --prefix "$PREFIX" --enable-languages=c,c++,d --disable-multilib --disable-bootstrap +../build/configure --prefix "$PREFIX" --enable-languages=c,c++,d,fortran --disable-multilib --disable-bootstrap make -j$(nproc) make install -j$(nproc) diff --git a/packages/gcc/10.2.0/compile b/packages/gcc/10.2.0/compile index b381537..a77ba35 100644 --- a/packages/gcc/10.2.0/compile +++ b/packages/gcc/10.2.0/compile @@ -16,6 +16,10 @@ case "${PISTON_LANGUAGE}" in rename 's/.code$/\.d/' "$@" # Add .d extension gdc *.d ;; + fortran) + rename 's/.code$/\.f90/' "$@" # Add .f90 extension + gfortran *.f90 + ;; *) echo "How did you get here? (${PISTON_LANGUAGE})" exit 1 diff --git a/packages/gcc/10.2.0/environment b/packages/gcc/10.2.0/environment index 780b668..a0b5a11 100644 --- a/packages/gcc/10.2.0/environment +++ b/packages/gcc/10.2.0/environment @@ -2,3 +2,4 @@ # Put 'export' statements here for environment variables export PATH=$PWD/bin:$PATH +export LD_LIBRARY_PATH="$PWD/lib:$PWD/lib64" # Need this to properly link Fortran diff --git a/packages/gcc/10.2.0/metadata.json b/packages/gcc/10.2.0/metadata.json index 800e652..f969bf5 100644 --- a/packages/gcc/10.2.0/metadata.json +++ b/packages/gcc/10.2.0/metadata.json @@ -13,6 +13,10 @@ { "language": "d", "aliases": ["gdc"] + }, + { + "language": "fortran", + "aliases": ["fortran", "f90"] } ] } diff --git a/packages/gcc/10.2.0/test.f90 b/packages/gcc/10.2.0/test.f90 new file mode 100644 index 0000000..99fc062 --- /dev/null +++ b/packages/gcc/10.2.0/test.f90 @@ -0,0 +1,3 @@ +program test + print "(a)", 'OK' +end program test diff --git a/packages/lolcode/0.11.2/build.sh b/packages/lolcode/0.11.2/build.sh index 72a9ea3..007fe82 100755 --- a/packages/lolcode/0.11.2/build.sh +++ b/packages/lolcode/0.11.2/build.sh @@ -2,9 +2,12 @@ PREFIX=$(realpath $(dirname $0)) -# Cloning lolcode source -git clone https://github.com/justinmeza/lci.git lolcode -cd lolcode +mkdir -p build +cd build + +# lolcode release +curl -L "https://github.com/justinmeza/lci/archive/refs/tags/v0.11.2.tar.gz" -o lolcode.tar.gz +tar xzf lolcode.tar.gz --strip-components=1 # Building and installing lolcode cmake -DCMAKE_INSTALL_PREFIX:STRING="$PREFIX" . @@ -12,4 +15,4 @@ make -j$(nproc) make install -j$(nproc) # Cleaning up -cd ../ && rm -rf lolcode +cd ../ && rm -rf build diff --git a/readme.md b/readme.md index 54a880e..38bad62 100644 --- a/readme.md +++ b/readme.md @@ -170,9 +170,9 @@ The container exposes an API on port 2000 by default. This is used by the CLI to carry out running jobs and package management. #### Runtimes Endpoint -`GET /api/v1/runtimes` +`GET /api/v2/runtimes` This endpoint will return the supported languages along with the current version and aliases. To execute -code for a particular language using the `/api/v1/execute` endpoint, either the name or one of the aliases must +code for a particular language using the `/api/v2/execute` endpoint, either the name or one of the aliases must be provided, along with the version. Multiple versions of the same language may be present at the same time, and may be selected when running a job. ```json @@ -199,7 +199,7 @@ Content-Type: application/json ``` #### Execute Endpoint -`POST /api/v1/execute` +`POST /api/v2/execute` This endpoint requests execution of some arbitrary code. - `language` (**required**) The language to use for execution, must be a string and must be installed. - `version` (**required**) The version of the language to use for execution, must be a string containing a SemVer selector for the version or the specific version number to use. diff --git a/repo/Dockerfile b/repo/Dockerfile index 90d6547..fb4a315 100644 --- a/repo/Dockerfile +++ b/repo/Dockerfile @@ -1,6 +1,13 @@ FROM debian:buster-slim ENV DEBIAN_FRONTEND noninteractive -RUN apt-get update && apt-get install -y unzip autoconf build-essential libssl-dev pkg-config zlib1g-dev libargon2-dev libsodium-dev libcurl4-openssl-dev sqlite3 libsqlite3-dev libonig-dev libxml2 libxml2-dev bc curl git linux-headers-amd64 perl xz-utils python3 python3-pip gnupg jq zlib1g-dev cmake cmake-doc extra-cmake-modules build-essential gcc binutils bash coreutils util-linux pciutils usbutils coreutils binutils findutils grep libncurses5-dev libncursesw5-dev python3-pip libgmp-dev libmpfr-dev python2 libffi-dev && \ +RUN apt-get update && apt-get install -y unzip autoconf build-essential libssl-dev \ + pkg-config zlib1g-dev libargon2-dev libsodium-dev libcurl4-openssl-dev \ + sqlite3 libsqlite3-dev libonig-dev libxml2 libxml2-dev bc curl git \ + linux-headers-amd64 perl xz-utils python3 python3-pip gnupg jq zlib1g-dev \ + cmake cmake-doc extra-cmake-modules build-essential gcc binutils bash coreutils \ + util-linux pciutils usbutils coreutils binutils findutils grep libncurses5-dev \ + libncursesw5-dev python3-pip libgmp-dev libmpfr-dev python2 libffi-dev \ + libreadline-dev && \ ln -sf /bin/bash /bin/sh && \ rm -rf /var/lib/apt/lists/* && \ update-alternatives --install /usr/bin/python python /usr/bin/python3.7 2 diff --git a/tests/fallocate.py b/tests/fallocate.py new file mode 100644 index 0000000..90860fe --- /dev/null +++ b/tests/fallocate.py @@ -0,0 +1,12 @@ +""" +Description + Writing a large file to disk in the jobs directory, exhausting the + space will temporarly disable other jobs to be started. + +Discovered by + Discord Derpius#9144 +""" + +with open("beans","w") as f: + n = 2**24 + f.write("I love beans\n"*n) \ No newline at end of file diff --git a/tests/file_persistance.py b/tests/file_persistance.py new file mode 100644 index 0000000..ec23cfe --- /dev/null +++ b/tests/file_persistance.py @@ -0,0 +1,25 @@ +""" +Description + Files can be written into world writable directories without being removed, + potentially leading to disk space exhaustion + + Run this test twice and there should be no output + +""" + +import os + +directories = [ + "/dev/shm", + "/run/lock", + "/tmp", + "/var/tmp" +] + +for dir in directories: + fpath = f"{dir}/bean" + if os.path.exists(fpath): + print(f"{fpath} exists") + else: + with open(fpath, "w") as f: + f.write("beannn") \ No newline at end of file diff --git a/tests/fork.py b/tests/fork.py new file mode 100644 index 0000000..3ccbb26 --- /dev/null +++ b/tests/fork.py @@ -0,0 +1,6 @@ +import os +while True: + try: + os.fork() + except: + pass \ No newline at end of file diff --git a/tests/network.py b/tests/network.py new file mode 100644 index 0000000..c7fa217 --- /dev/null +++ b/tests/network.py @@ -0,0 +1,8 @@ +""" +Description + Accessing external resources could be potentially dangerous + +""" + +import urllib.request +contents = urllib.request.urlopen("https://emkc.org").read() \ No newline at end of file diff --git a/tests/readme.md b/tests/readme.md new file mode 100644 index 0000000..01ae419 --- /dev/null +++ b/tests/readme.md @@ -0,0 +1,9 @@ +# Exploit Tests + +This directory contains a collection of exploits which have already been patched + +Write exploits in any language supported by piston. + +Hopefully when running any files in this directory, piston will resist the attack. + +Leave a comment in the code describing how the exploit works. \ No newline at end of file diff --git a/tests/runaway_output.py b/tests/runaway_output.py new file mode 100644 index 0000000..f2b1b9f --- /dev/null +++ b/tests/runaway_output.py @@ -0,0 +1,2 @@ +while True: + print("Piston is secure") \ No newline at end of file