CTF tooling on Darwin, powered by Nix.
This could very possibly also be helpful for Linux users, but things should mostly work out of the box for you already (especially if you’re on x86).
nix develop github:feyorsh/pwnypus#crypto -c "sage"nix develop github:feyorsh/pwnypus#rev -c "ghidra"This is to ensure shell one-liners work.
This is actually motivated less from a free software perspective and more from a usability standpoint: most unfree Nix packages use something called requireFile, which requires
a) The user has a copy of the software
b) The user adds that to the store manually
For example, let’s say I try to run Binary Ninja:
$ nix develop .#rev
trace: evaluation warning: pacakges binary-ninja-4.1.5747, ida-pro-9.0.240807 are unfree and won't be evaluated (set PWNYPUS_ALLOW_UNFREE=1 to allow)
bash-5.2$ binja
bash: binja: command not foundSo this shell still has Ghidra and Radare2 and whatnot, but not Binary Ninja.
What happens if I set PWNYPUS_ALLOW_UNFREE=1?
$ PWNYPUS_ALLOW_UNFREE=1 nix develop .#rev --impure
error: builder for '/nix/store/md4kbm4r2j1w6lfcimg76q9bkr61mhhy-binaryninja_personal_macosx.dmg.drv' failed with exit code 1;
last 10 log lines:
> ***
> Binary Ninja is proprietary software and requires a license to install.
> (Alternatively, a freeware version can be installed from https://binary.ninja/free/.)
> Visit https://binary.ninja/recover/ to receive an email with a link to an installer; then add it to the Nix store like so:
>
> nix-prefetch-url file://$PWD/binaryninja_personal_macosx.dmg
> rm -rf binaryninja_personal_macosx.dmg
>
> ***
>
For full logs, run 'nix log /nix/store/md4kbm4r2j1w6lfcimg76q9bkr61mhhy-binaryninja_personal_macosx.dmg.drv'.
error: 1 dependencies of derivation '/nix/store/y0zz2pcx156p8ms0r466q9vyzygxs8gv-binary-ninja-4.1.5747.drv' failed to build
error: 1 dependencies of derivation '/nix/store/5rml98b5i7mzbwxls0h89x440rq5vnl0-nix-shell-env.drv' failed to buildSo I need to run nix-prefetch-url with the installer I get after actually buying Binary Ninja.
But of course, you probably won’t have version 4.1.5747 (as it were), so there will be a hash mismatch—what to do?
The solution is to use an override with your version:
binary-ninja.overrideAttrs {
version = "4.2.0";
src = pkgs.requireFile {
name = "binaryninja_personal_dev_macosx.dmg";
message = "irrelevant";
sha256 = "sha256-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=";
};
}This requires making a flake to compose your overlay with fyshpkgs/pwnypus; also, I recommend creating a GC root for the installer to prevent it from getting garbage collected accidentally.
tl;dr
If you use nix-darwin, add the following to your configuration.nix to be able to run without root permissions:
security.chmodbpf = {
enable = true;
members = [ YOUR_USERNAME ];
};Wireshark and similar utilities use BPF to capture packets.
However, on macOS /dev/bpf* is restricted to root only; even macOS’s vendored copy of tcpdump needs to be run as root:
which tcpdump
tcpdump -i en0 2>&1#+RESULTS[99833f897b46759958a228fa1cce793879d74854]:
/usr/sbin/tcpdump tcpdump: en0: You don't have permission to capture on that device ((cannot open BPF device) /dev/bpf0: Permission denied)
Even if you wanted to make Wireshark SUID (probably not a good idea), Nix cannot create SUID binaries anyway.
The solution upstream recommends is to manually chgrp all BPF devices on boot using a Launch Daemon to a group your user is in; pwnypus provides a nix-darwin module to do this for you.
See Xenu’s own FAQ.
- fyshpkgs
- My personal Nix package repository, which includes some packages pwnypus uses. I try to contribute upstream whenever possible, so packages in fyshpkgs are there because they’re either unfree or unstable.
- xenu
- Basically, “Bash bindings” for the Apple Virtualization framework. This is what powers ephemeral Linux shells that can run =x86_64-linux= binaries using dynamic Rosetta2 translation (QEMU is great, but can’t use Rosetta because it’s an Apple product). You can use Tart/UTM/Docker for this purpose as well, I mainly made this for fun.
- pwnypus
- This is meant to be kinda like ”Are We Hackers Yet?” for
aarch64-darwin, but in addition to keeping track of packages to add, it serves as a place to accumulate “hacks” to actually make the packages work. Where it makes sense, packages/nix-darwin modules should be upstreamed to nixpkgs.
Nix doesn’t add the /Applications outputPath of a package to any environment variables, but it’s there (it will show up if you install it in home.packages, for instance).
I generally try to add wrappers in /bin to avoid this problem.
Not surprisingly, the best way to do this is to just use Docker/Podman (preferably using Colima as the backend, as Docker behaves weirdly without Docker Desktop on MacOS).
At minimum, must be able to solve all challenges in UIUCTF.
We reached feature parity with the CryptoHack Dockerfile 🥳
Kinda hacky, someone suggested it though
Must be able to install other versions of libc, using Nix of course. In much the same way that pwninit works, this should provide a flake template that can be used to instantiate a new flake for tackling a pwn challenge.
Works as expected when using nixpkgs cross (PR to avoid unsupported platform errors)
Works fine after the switch to pyelftools.
This will also be the best way to use lldb, if I ever get around to that.
I tried to like lldb. Really, I did, but the UX is actually dogwater.
I ought to come back to this once I upgrade to Emacs 30, which adds support for lldb in GUD; until then, I have zero personal incentive to work on this.
(It’s pretty much the only way to debug aarch64-darwin binaries… never seen a CTF include those, though.)
github This is by far the most difficult thing to work on, but this needs to be (at minimum) usable for running userland binaries, and I would eventually like to use it for kernel hacking as well.
I haven’t really figured out a great way to optimize the UX, currently it’s
- Invoke xenu, passing in the disk image and initramfs
- Install whatever you need using
nix shellor similar - Run the binary, and maybe add
ROSETTA_DEBUGSERVER_PORTto allowgdbto connect (this should be, but is not currently, forwarded to be accessible on the host)
Eventually I would like a UX like nix run github:feyorsh/pwnypus#xenu --args <my-bin> -p 1337 and it will spin up in the background, and provide you a shell over SSH.
Generally speaking you don’t want to spinup a bunch of VMs as that will eat disk space like crazy; maybe the suggested use case is one flake.nix/VM per CTF, and you can make a separate devShell and .envrc for a challenge if need be.
There are also some outstanding bugs which are annoying, mainly
- Ctrl-C gets interpreted by the shell running xenu, not the guest VM (problem with
stty) - The serial console is really finnicky and the display can get messed up very easily
- A kernel panic will happen if you leave the guest on for long enough, likely an Apple Virtualization issue
The Xenu derivation currently requires Xcode to build, so I cut a release on GitHub and used the binary from there instead to avoid bootstrapping issues.
This is meant to be a stopgap measure until SDK 13 lands in nixpkgs This is done thanks to the 🐐 reckenrode!
I need to switch the build process over soon.
Talk to Ravi; also see building XNU, OSX in KVM, and osxcross.
QEMU covers a lot of use cases that I can’t anticipate, but my focus is on xenu for the time being.
I imagine QEMU is much more suitable for kernel stuff than xenu.
Further investigation required… I’ve tried:
- Using Darwin native installer
undmgcan’t handle the compression PortSwigger uses, so currently a non-starter. The closure size is also quite large because they vendor Chromium for the proxy browser.- Using JAR
- This builds fine, but doesn’t come with a browser, so I tried bundling Firefox with a custom enterprise configuration to allow Burp to proxy requests. I ran into trouble with the Java keystore, but I might look into this later.
Fixed in nixpkgs, I also wrote a nix-darwin module to allow unprivileged BPF access
The basics nmap, curl, all work.
I personally prefer restclient.el to Postman, but the latter also works.
Still have to test connecting to a gdbserver.
Pretty sure this can only run on Intel chips; they dropped macOS support a while ago. May or may not be possible with xenu (I’ve heard weird stuff about Pin on Rosetta).
I don’t own a copy, so I can’t/won’t support it. Feel free to open a PR.
Knock knock, it’s the Ida Pro 9 Beta 😳
Currently broken in nixpkgs as upstream hasn’t updated unicorn.
The version in nixpkgs actually works better out of the box than when installing directly due to default macOS permissions behaviour. The one saving grace of Java is that it runs everywhere, including, unfortunately, on my device!
I have no idea what you could need besides stock Python for pyjails; this is currently a stub.
Eh, I don’t really care about this, but I get the feeling that some people might.
This is not a priority item, but at some point it might be nice to add stuff like metasploit, rockyou.txt, stuff like that.
No.
