diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..782c751 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Build with CMake + run: | + mkdir build + cd build + cmake .. -G "Unix Makefiles" + make + + - name: Upload artifacts + uses: actions/upload-artifact@master + with: + name: vuln-artifact + path: build/vuln + retention-days: 1 + + test: + runs-on: ubuntu-latest + + needs: build + + env: + FNETD_PASSWORD: 1234 + + steps: + - uses: actions/checkout@v3 + + - uses: actions/download-artifact@master + name: Download build artifacts + with: + name: vuln-artifact + path: build/ + + - name: Install fnetd + run: | + wget https://cloud.sec.in.tum.de/index.php/s/n5cJnDqnnpSeEpd/download/fnetd.tar.xz -O fnetd.tar.xz + tar -xf fnetd.tar.xz + mkdir fnetd/build + cd fnetd/build + cmake .. -G "Unix Makefiles" + make + cd ../.. + + - name: Setup get_flag + run: gcc tests/get_flag.c -o get_flag -O3 + + - uses: JarvusInnovations/background-action@v1 + name: Start fnetd + with: + run: |- + chmod +x build/vuln + fnetd/build/fnetd -p 1337 -lt 2 -lm 536870912 "strace -f build/vuln" & + + tail: true + wait-on: tcp:localhost:1337 + wait-for: 1m + + - name: Setup python libs + run: pip install -r tests/requirements.txt + + - name: Tests + run: python -m unittest discover tests/ diff --git a/tests/common.py b/tests/common.py new file mode 100644 index 0000000..0a00f7f --- /dev/null +++ b/tests/common.py @@ -0,0 +1,74 @@ +#! /usr/bin/env python3 + +from pwn import * +from enum import IntEnum + + +class Opcode(IntEnum): + ADD = 0 + ADDI = 1 + SUB = 2 + COPY = 3 + LOADI = 4 + + +class Register(IntEnum): + A = 0 + B = 1 + C = 2 + D = 3 + E = 4 + F = 5 + G = 6 + H = 7 + + +INSTR_LEN = 8 + + +def instr_i(opcode: Opcode, reg1: Register, imm: int): + assert (opcode == Opcode.ADDI or opcode == Opcode.LOADI) + assert (imm < 2 ** 32) + return bytes([opcode, reg1, 0, 0]) + imm.to_bytes(4, byteorder='little') + + +def instr_r(opcode, reg1, reg2): + assert (opcode == Opcode.ADD or opcode == Opcode.SUB or opcode == Opcode.COPY) + return bytes([opcode, reg1, 0, 0, reg2, 0, 0, 0]) + + +def exec_program(program: bytes) -> int | None: + context.log_level = 'debug' + with remote("localhost", 1337, fam="ipv4") as p: + p.recvuntil(b"Password: ", timeout=1) + p.sendline(b"1234") + + msg = p.recvuntil(b"always a Surprise)", timeout=1) + if msg == b'': + return None + print(msg.decode()) + + msg = p.recvuntil(b"should it bee?", timeout=1) + if msg == b'': + return None + print(msg.decode()) + + len_msg = str(len(program) // INSTR_LEN).encode() + log.info(f"Sending: {len_msg}") + p.sendline(len_msg) + + msg = p.recvuntil(b"Now your program:", timeout=1) + if msg == b'': + return None + print(msg.decode()) + + log.info(f"Sending program: {list(program)}") + p.send(program) + + msg = p.recvuntil(b"Your program exited with ", timeout=1) + if msg == b'': + return None + print(msg.decode()) + + exit_code = int(p.recvuntil(b"!", drop=True, timeout=1)) + return exit_code diff --git a/tests/get_flag.c b/tests/get_flag.c new file mode 100644 index 0000000..610ed48 --- /dev/null +++ b/tests/get_flag.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("flag_0123456789abcdef0123456789abcdef\n"); + return 0; +} diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..7669e68 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,3 @@ +# requirements.txt + +pwntools>=4.11.1 diff --git a/tests/test_add.py b/tests/test_add.py new file mode 100644 index 0000000..aeb4583 --- /dev/null +++ b/tests/test_add.py @@ -0,0 +1,15 @@ +import unittest +from common import * + + +class BasicInstructionTest(unittest.TestCase): + def test_addi(self): + for val in [0, 1, 50, 256, 0x543210, 0xFFFFFFFF]: + program = instr_i(Opcode.ADDI, Register.A, val) + exit_code = exec_program(program) + self.assertIsNotNone(exit_code, "Connection timeout!") + self.assertEqual(exit_code, val & 0xFF, "Computed the wrong result!") + + +if __name__ == '__main__': + unittest.main()