From 65929200e4f62298d15996a9b3bd234b30dffaff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Sat, 14 Oct 2023 16:46:20 +0200 Subject: oc challs --- iv/oc_challs/ov/.gitignore | 4 ++ iv/oc_challs/ov/Dockerfile | 13 +++++ iv/oc_challs/ov/README.md | 19 +++++++ iv/oc_challs/ov/challenge.yml | 23 ++++++++ iv/oc_challs/ov/docker-compose.yml | 9 +++ iv/oc_challs/ov/hi.html | 1 + iv/oc_challs/ov/index.html | 1 + iv/oc_challs/ov/makefile | 21 +++++++ iv/oc_challs/ov/server.php | 112 +++++++++++++++++++++++++++++++++++++ 9 files changed, 203 insertions(+) create mode 100644 iv/oc_challs/ov/.gitignore create mode 100644 iv/oc_challs/ov/Dockerfile create mode 100644 iv/oc_challs/ov/README.md create mode 100644 iv/oc_challs/ov/challenge.yml create mode 100644 iv/oc_challs/ov/docker-compose.yml create mode 100644 iv/oc_challs/ov/hi.html create mode 100644 iv/oc_challs/ov/index.html create mode 100644 iv/oc_challs/ov/makefile create mode 100755 iv/oc_challs/ov/server.php (limited to 'iv/oc_challs/ov') diff --git a/iv/oc_challs/ov/.gitignore b/iv/oc_challs/ov/.gitignore new file mode 100644 index 0000000..a43297e --- /dev/null +++ b/iv/oc_challs/ov/.gitignore @@ -0,0 +1,4 @@ +*.asm +*.out +*.log +*.tar.gz diff --git a/iv/oc_challs/ov/Dockerfile b/iv/oc_challs/ov/Dockerfile new file mode 100644 index 0000000..4ba2b06 --- /dev/null +++ b/iv/oc_challs/ov/Dockerfile @@ -0,0 +1,13 @@ +FROM debian:latest + +MAINTAINER anton@sijanec.eu + +EXPOSE 6844/tcp + +WORKDIR /app + +RUN apt-get update && apt-get install --no-install-recommends -y php-cli nasm python3 make socat && apt-get clean + +COPY ./ /app + +CMD ["make", "run" ] diff --git a/iv/oc_challs/ov/README.md b/iv/oc_challs/ov/README.md new file mode 100644 index 0000000..4dd2e2c --- /dev/null +++ b/iv/oc_challs/ov/README.md @@ -0,0 +1,19 @@ +# 1bitvm + +## Details +* points: 8 +* category: misc +* author: Adrian & Anton +* flags: `cnctf{hard2pwnUnknownArch}` or `cnctf{hard2pwnUnknownArch}---` (same flag) + + +## Description: +1bitvm is a cursed virtual machine for a cursed imaginary architecture. A program is written that combines the horrors of PHP, python and architecture specific assembly language into a giant mess that somehow servers HTTP requests. It's your job to convince the "webserver" to read you the flag from it's RAM, totaling not more than 128 bits. + +## Deployment +* Deploy the docker image with `docker compose up`. It is listening on port 6844, map it to whatever port is most convenient. + - alternatively you can just run `make run` +* Players should get 1bitvm.tar.gz created with `make 1bitvm.tar.gz`. It doesn't contain solutions and flags. + +## Solution / writeup +The `win` subroutine reads the flag from the virtual ROM. Since content is delivered based on the first two bytes of the path (control "jumps" to first two bytes of request path casted to address ORed with 0x8000), players can also jump to the `win` subroutine. The address of `win:` label can be read from `asm.log` file (`win na 0xe339`). Since the first bit of the address will always be set to 1, 0x63 (ASCII 'c') and 0x39 (ASCII '9') can be set as request path to jump to the `win` subroutine and print the flag. diff --git a/iv/oc_challs/ov/challenge.yml b/iv/oc_challs/ov/challenge.yml new file mode 100644 index 0000000..1662add --- /dev/null +++ b/iv/oc_challs/ov/challenge.yml @@ -0,0 +1,23 @@ +# ctfcli ctfd challenge spec: https://github.com/CTFd/ctfcli/blob/master/ctfcli/spec/challenge-example.yml + +name: "1bitVM" +author: "anton@sijanec.eu" +category: misc +description: 1bitVM is an idea for an architecture with one instruction bit. It is currently serving a simple website and is storing the flag somewhere in it's mere 128 bits of RAM. Local setup: run make run to start the server on port 6844 locally +value: 8 +type: standard + +image: healthcheck + +protocol: tcp + +connection_info: nc b.4a.si 6844 + +flags: + - cnctf{hard2pwnUnknownArch}--- + - cnctf{hard2pwnUnknownArch} + +files: + - 1bitvm.tar.gz + +version: 0.1 diff --git a/iv/oc_challs/ov/docker-compose.yml b/iv/oc_challs/ov/docker-compose.yml new file mode 100644 index 0000000..0cf2c27 --- /dev/null +++ b/iv/oc_challs/ov/docker-compose.yml @@ -0,0 +1,9 @@ +services: + 1bitvm: + build: . + ports: + - "6844:6844" + restart: + always + environment: + - flag=cnctf{hard2pwnUnknownArch} # if challenge breaks, you may have made the flag too long (128b RAM!) diff --git a/iv/oc_challs/ov/hi.html b/iv/oc_challs/ov/hi.html new file mode 100644 index 0000000..87a2780 --- /dev/null +++ b/iv/oc_challs/ov/hi.html @@ -0,0 +1 @@ +

Created by Adrian & Anton 2023-09 diff --git a/iv/oc_challs/ov/index.html b/iv/oc_challs/ov/index.html new file mode 100644 index 0000000..02aa964 --- /dev/null +++ b/iv/oc_challs/ov/index.html @@ -0,0 +1 @@ +

One-Bit virtual machine

GET the flag!

READ SOURCE in TAR!

authors1bitvm diff --git a/iv/oc_challs/ov/makefile b/iv/oc_challs/ov/makefile new file mode 100644 index 0000000..f3690f4 --- /dev/null +++ b/iv/oc_challs/ov/makefile @@ -0,0 +1,21 @@ +default: 1bitvm.tar.gz run + +server.out: 1bitvm server.asm + 1bitvm/asm.py -d server.asm 2>&1 > asm.log + +server.asm: server.php index.html hi.html + ./server.php > $@ + +run: server.out + socat TCP6-LISTEN:6844,fork,reuseaddr 'exec:1bitvm/main.py server.out' + +1bitvm: + git clone https://ass.si/git/adrian/1bitvm + +clean: + rm -fr `cat .gitignore` + +1bitvm.tar.gz: server.out docker-compose.yml Dockerfile + f=`grep -o cnctf{.*} docker-compose.yml` && sed -i s/$$f/cnctf{placeholder}/ docker-compose.yml && cd .. && tar --exclude *README.md --exclude *challenge.yml --exclude *1bitvm.tar.gz -zc `rev <<<$$OLDPWD | cut -d/ -f1 | rev` | sponge $$OLDPWD/1bitvm.tar.gz; cd $$OLDPWD; sed -i s/cnctf{placeholder}/$$f/ docker-compose.yml + +.PHONY: default clean run diff --git a/iv/oc_challs/ov/server.php b/iv/oc_challs/ov/server.php new file mode 100755 index 0000000..88034e8 --- /dev/null +++ b/iv/oc_challs/ov/server.php @@ -0,0 +1,112 @@ +#!/usr/bin/env php +;; This VM has 128 bits of RAM so creating loops is hard. Instad this "HTTP" server works by unrolling all loops in assembly. To achieve that, assembly is generated by preprocessing with PHP. You can see the output file after running make in server.asm. + + +;; start writing the following code to address 4 in ROM +.org 4 + +;; include standard library +%include "1bitvm/std.asm" + +;; assembly macro that prints two bytes (black box) +%macro print2 2 + c16 %1, %2, 1 + set_out_b %2 + 0 , 0x14 + set_out_b %2 + 1 , 0x14 + set_out_b %2 + 2 , 0x14 + set_out_b %2 + 3 , 0x14 + set_out_b %2 + 4 , 0x14 + set_out_b %2 + 5 , 0x14 + set_out_b %2 + 6 , 0x14 + set_out_b %2 + 7 , 0x14 + set_out_b %2 + 8 , 0x14 + set_out_b %2 + 9 , 0x14 + set_out_b %2 + 10, 0x14 + set_out_b %2 + 11, 0x14 + set_out_b %2 + 12, 0x14 + set_out_b %2 + 13, 0x14 + set_out_b %2 + 14, 0x14 + set_out_b %2 + 15, 0x14 +%endm + +;; header that is sent in every response, 38 bytes +header: + +.db b"" + +;; label that points to defined bytes as a string literal in ROM +flag: +.db b"" + +;; storage for hi page +hi: +.db b"" + +;; label that points to defined bytes as a string literal in ROM +;; PHP is only used as a preprocessor for generating assembly with this bytestring read from a file +index: +.db b"" + +print_header_pointer: +.orgr 2 + +;; prints header and jumps to address stored in 0x30 + 1 +print_header: + + ret + +.org labels["print_header_pointer"]*2 +.db by2(labels["print_header"]) + +;; the string b' H' will be read as two byte request-path (GET /< H>TTP/1.0) when requesting with empty path +.org (int.from_bytes(b' H') | 0x8000)*2 +;; PHP generated assembly subroutine that prints bytes at label word. creating a loop on this VM would be hard. +print_index: + call labels["print_header_pointer"], 1 + + exit + +;; hi page, the & 0xfffe means last bit will always be 0 +.org (int.from_bytes(b'YO') | 0x8000)*2 +print_hi: + call labels["print_header_pointer"], 1 + + exit + +;; sends the flag to the client +win: + call labels["print_header_pointer"], 1 + + exit + +main: + ;; we set first bit of address to 1 -- all pages are on addresses 0x8000 and above, first two letters of request path are casted to an address and ORed with 0x8000 + + set1 0x30 + ;; jump to print subroutine + c16 0x30, 0, 0 + +init "main" -- cgit v1.2.3