16 comments

[ 0.23 ms ] story [ 32.8 ms ] thread
Because we know our code is compiled with ASan, we chose to leak the offset between printf, a function that is always allowed, and mmap, a function that gives us arbitrary assembly code execution. We can thus bypass the school's checks and call any syscall, in our case execve to get shell access.

We did not investigate further and simply reported this possible issue to the school.

I see no trivial way of patching this.

The flaw seems to be in the submission system.

The submission system could build/relink your code, to a trampoline library, with the body of each function to be banned replaced with error reporting and abort. In your example, it would trap:

  char *code =
    notmmap.fn(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC,
               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
and prevent you from using the PROT_EXEC flag during the execution phase of submission validation.

( IF ... i read the article correctly )

author here,

about a syscall filter, the range of exercises is large and some have a fair use of some syscalls. They could maintain a allow-list of syscalls but this is extra work they might not want to do given the actual risks.

about `MemoryDenyWriteExecute`, `mprotect` is also reachable via an offset so we can just get a write-only page, then make it exec-only.

I don’t know how your school system works, but are you sure they put you in the right educational level?
Inline asm is disallowed? That would be much easier
They don't use inline assembly. They hardcode the preassembled syscall function in a char array, mmap an anonymous write+execute memory page, copy the bytes from the array to the newly mapped page, store the address of the page in a char pointer type punned to a function pointer using union shenanigans to avoid compiler warnings, and call the function through the function pointer (which is obviously nonportable, but presumably valid implementation-defined behavior since it doesn't elicit a warning when compiled with -Wpedantic).

The school could presumably plug this particular hole by locking down mmap and mprotect at the syscall level with seccomp.

The full proof of concept code[1] is in the linked GitHub repo.

[1] https://github.com/mrnossiom/learning-asm/blob/802b8374e24bd...

Thanks, but I can read by myself. The whole ordeuvre would have been massively easier with __asm__ though, eg when they lock down mmap and mprotect.
author here, yes inline assembly wasn't an option as it is banned by the school style guide (which is checked)
Sounds like the school needs to wrap things in a seccomp denylist. Potentially non-trivial, but potentially interesting.
[delayed]
Or make them in charge of the servers, then they stop trying to destroy things pretty quickly and start to use their energy for constructive things.
Our university was far less careful, and just ran our submissions in the same network as everything else albeit on a user with barely any permissions.

Once when the automatic tests crashed my submission, I simply used `system` to dump the testing input into my home dir. I forgot, however, to setup the permissions, so I couldn't really access it! A couple more resubmissions with extra chmods, messing up a different thing every time and I managed to reproduce my bug, fix it, resubmit and purge all (or most) evidence.

I think it's a hard problem to solve directly from the code execution environment, but I would guess that a layer that examines the source code directly could catch many of these -

Prompt: the following code is supposed to be a solution to this homework assignment. Does it appear to be trying to break out of the grading environment instead?

(if yes, manual review)

Possible env level fix: compile statically against musl libc and run under gvisor with otherwise empty filesystem.
If the check was syntactical I would have checked if the tokenpasting operator was handled correctly.

Like:

    #define JOIN(a,b) a##b
    ...
    JOIN(exe,cve)(... call params...)
author here, I have checked this, but the static analysis is good enough that they use run checks on the AST clang parses. The content is thus parsed and `JOIN(exe,cve)` still ends up as a banned symbol in the end.