Open-source project
bkerler/exploit_me avatar
bkerler/exploit_me

exploit_me: a deliberately broken ARM binary for learning

Very vulnerable ARM/AARCH64 application (CTF style exploitation tutorial with 29 vulnerability techniques)

1,115 stars159 forksC++MIT

At a glance

What is it?
exploit_me is a CTF style training application with 29 stacked vulnerability levels for ARM and AArch64, built so that each level can be solved with a different technique and chained by a password. It suits someone learning binary exploitation on ARM, and it is a lab target rather than a tool you point at anything else.
Who is it for?
Adopt exploit_me if you are learning exploitation on ARM and want one target that covers the classic memory corruption classes in order, because the level list is a curriculum and the per-level password means you cannot skip ahead. Do not adopt it as a fuzzing target or a proof of concept host for anything you did not write, since every level is a real vulnerability and the 64-bit and 32-bit binaries are built with the same source.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 117 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Twenty nine levels, ordered as a curriculum

exploit_me is a very vulnerable ARM and ARM64, also called AArch64, application, described as a CTF style exploitation tutorial that is portable to other platforms. It is by B.Kerler, copyright 2018 to 2026, MIT licensed.

The reason for it is given plainly. Some of the author's friends asked for examples of exploitable things he had seen in the real world over the years for ARM, ARM64 and other platforms, and for training purposes he thought: why not.

The level list is the substance of the project, and it is worth reading as a sequence rather than a set. The early levels are the arithmetic and memory ones: integer overflow, stack overflow, array overflow, off by one, stack cookie, format string, heap overflow, structure redirection or type confusion, and zero pointers.

Then the injection and control flow ones: command injection, path traversal, return oriented programming, use-after-free, jump oriented programming, stable format string write, and double free.

Then the subtler classes: signedness bug, integer multiplication overflow, null pointer bug, uninitialized memory read, out-of-bounds read, TOCTOU path race, insecure temporary file or symlink attack, environment variable injection, unsafe parser or deserialization, filtered command injection or quoting bypass, heap metadata corruption, ROP-style function-pointer control flow, and dangling stack pointer.

The order is pedagogical. You start with a bug that is arithmetic, and you finish with bugs that need real understanding of allocator state or of lifetime rules. A single application that grows in difficulty, rather than twenty unrelated captures, is a much better way to learn the progression.

How the levels chain, and why that matters

The levels are not independent. After you have exploited one correctly, you are shown the password for the next level, and that password is the argument you pass to the next binary. The README's example is that if the level 2 password were Level2, you would run:

code
$ ./bin/exploit Level2

and for the 64-bit build:

code
$ ./bin/exploit64 Level2

That design turns the application into a single progression rather than a collection of exercises. The password is proof that you solved the previous level, which means you cannot skip to the interesting one, and it gives the whole thing the shape of a capture the flag challenge without needing a separate scoreboard.

The two binaries cover both architectures from one source, and the README notes the project is portable to other platforms as well. The gdb session examples show how the architecture is set per binary, with set architecture arm for the 32-bit build and set architecture aarch64 for the 64-bit one, and the suggestion to put the same into your .gdbinit so it applies automatically. The example .gdbinit also sets endian little and targets a remote port with target remote :1234.

For a learner, that matters, because debugging ARM code with a native x86 toolchain is a friction point that has nothing to do with exploitation. gdb-multiarch is the tool that removes it, and the README shows the exact commands.

Building it, and the toolchain the setup script handles

The installation is two steps on a Debian or Ubuntu host. You download the repository:

code
git clone https://github.com/bkerler/exploit_me

and then run the setup script from inside it:

code
~/exploit_me $ ./script/setup.sh

The script is the part that does the work, and the README does not detail what it installs. Given that the project needs a cross compiler for both architectures, a gdb with multiarch support, and the pwndbg extension for the rop gadget listing, the toolchain is substantial, and the one-line setup is a convenience rather than something to inspect.

The repository layout shows what is actually there. There is a bin/ directory with the compiled binaries, a src/ directory with the source, including src/exploit.cpp named in the README as where the vulnerable code lives, a solution/ directory, a scripts/ directory containing the setup and the ASLR disabling script, a tests/ directory, a hints.txt file, a .gdbinit file and a .github directory.

The .gdbinit at the top level is worth noting. The README's example .gdbinit block is effectively what belongs in it, so the repository ships one already set up for the multiarch debugging flow.

The debugging workflow, and the ASLR question

The README documents a working session in enough detail to reproduce, which is unusual and useful for a training project.

You start the target in the background and attach the debugger to it. For the 32-bit build:

code
$ ./bin/arm exploit [levelpassword] [options] &
$ gdb-multiarch ./exploit
pwndbg> set architecture arm

and for the 64-bit build:

code
$ ./arm64 exploit64 [levelpassword] [options] &
$ gdb-multiarch ./exploit64
pwndbg> set architecture aarch64

The reason for running the target as a separate process and attaching is that the binaries read the password from the command line and also expose it over a port, so the debugger attaches to a live process rather than launching it.

The ASLR step is explicit and comes with a warning. There is a script:

code
$ sudo ./scripts/disableaslr.sh

with the comment that you should disable ASLR but not run it if you want more fun. The reasoning is sound for a teaching tool: address randomisation changes the addresses between runs, which adds a moving target to every level, and for a beginner that obscures the actual vulnerability. It also means the target is much easier to script against.

The gdb basics section is the part to read even if you already know gdb, because it is a reference to the pwndbg feature set. It lists si and so for stepping, info functions, p for printing a function address, b and b *0x1234 for breakpoints, c to continue, and x/[dwords]x forms for printing offsets and register contents, with the example of x/4x 0x1234 and x/4x $sp. The pwndbg specific capability is rop, used to list gadgets, with the example rop --grep 'pop {r3'} to find gadgets that pop values into r3, and a link to the pwndbg features document.

Hints, solutions, and the deliberate leaks

The project is unusually open about how much help it gives you, and each tier is a deliberate choice.

There is a hints.txt file for a start. There are solutions in solution/solutions.txt, and the README calls that the route for cheaters or for people trying to understand with less instruction knowledge, and it points at the source code in src/exploit.cpp as the other reference.

And there are console hints. The README states that some hints are printed to the console as information leaks which you would normally not have, that these make things easier for beginners, and that this is why they were added.

That last point is the design philosophy of the whole project in one line. A real exploit course would not print the address of the buffer it wants you to overflow. Here it does, so that a beginner spends their effort on the technique rather than on the setup. It is a deliberate weakening of the target in exchange for a shorter path to the lesson, and it is why the ASLR note exists too, since the two are the same decision taken twice.

The README also says there are more solutions possible than the ones given, even with rop chains, not just the example solutions provided, and the ToDo section says more vulnerabilities will be added as the author sees them or has spare time, and invites contributions. The referrals at the end point beginners to ARM reversing resources: the Azeria labs site, the Beginner's Guide to Exploitation on ARM by Billy Ellis with his video tutorials, the ARM exploitation for IoT series on quequero.org, A Bug Hunter's Diary by Tobias Klein, and the ARMv8 opcode manual.

Editorial conclusion

Adopt exploit_me if you are learning exploitation on ARM and want one target that covers the classic memory corruption classes in order, because the level list is a curriculum and the per-level password means you cannot skip ahead. Do not adopt it as a fuzzing target or a proof of concept host for anything you did not write, since every level is a real vulnerability and the 64-bit and 32-bit binaries are built with the same source. Before you start, read hints.txt, and note that the recommended debugging setup disables ASLR and uses gdb-multiarch with the architecture set per binary, so run it in a container or a throwaway machine rather than on a host you care about.

Frequently asked questions

How do I build and run exploit_me?

On Debian or Ubuntu, clone the repository with `git clone https://github.com/bkerler/exploit_me`, change into the directory, and run `./script/setup.sh` to install the needed tools. The 32-bit binary is at ./bin/exploit and the 64-bit one at ./bin/exploit64, and hints.txt is the recommended starting point.

What vulnerability classes does exploit_me teach?

Twenty nine levels covering integer overflow, stack overflow, array overflow, off by one, stack cookie, format string, heap overflow, type confusion, zero pointers, command injection, path traversal, return oriented programming, use-after-free, jump oriented programming, double free, signedness bugs, uninitialized memory read, TOCTOU races, symlink attacks, environment variable injection, unsafe deserialization, heap metadata corruption, function-pointer control flow and dangling stack pointers.

How do I debug the ARM binaries with gdb?

Run the target in the background and attach gdb-multiarch to it, then set the architecture with `set architecture arm` for the 32-bit binary or `set architecture aarch64` for the 64-bit one. The README also suggests putting the same setting in your .gdbinit, and shows an example .gdbinit with set endian little and target remote :1234.

Are there solutions available for exploit_me?

Yes. The README points at solutions/solutions.txt and the source in src/exploit.cpp, describing them as the route for cheaters or for people who want to understand with less instruction knowledge. It also notes there are more solutions possible than the ones given, and that some hints are printed to the console as information leaks to make things easier for beginners.

Official sources

  1. bkerler/exploit_me on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bkerler-exploit-me.svg)](https://hysenlabs.com/projects/bkerler-exploit-me)

Was this useful?