Sekai CTF 2022 : Symbolic Needs 1 & 2
A linux memory forensics challenge in two parts.
Symbolic Needs 1
Description
We recently got hold of a cryptocurrency scammer and confiscated his laptop.
Analyze the memdump. Submit the string you find wrapped with SEKAI{}.
Attachment md5sum: 4be69c88e6f19dd9c9f8e6c52bc93c28
Author: BattleMonger
https://storage.googleapis.com/sekaictf/Forensics/memdump.zip
- Points: 467
- Solves: 24
Writeup
Judging from the magic numbers, the provided file is a linux memory dump. We’ll use volatility3 for analysis (for some reasons vol2 doesn’t support this particular OS / kernel). Before we can get volatility to work, we need to build a profile for the memory dump using some kernel debugging info. We can run this command:
$ python3 volatility3/vol.py -s ./profile/ -f dump.mem banner
Volatility 3 Framework 2.4.0
Progress: 100.00 PDB scanning finished
Offset Banner
0x42400200 Linux version 5.15.0-43-generic (buildd@lcy02-amd64-076) (gcc (Ubuntu 11.2.0-19ubuntu1) 11.2.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #46-Ubuntu SMP Tue Jul 12 10:30:17 UTC 2022 (Ubuntu 5.15.0-43.46-generic 5.15.39)
0x437c3718 Linux version 5.15.0-43-generic (buildd@lcy02-amd64-076) (gcc (Ubuntu 11.2.0-19ubuntu1) 11.2.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #46-Ubuntu SMP Tue Jul 12 10:30:17 UTC 2022 (Ubuntu 5.15.0-43.46-generic 5.15.39)9)
Great, so our memory dump is from Ubuntu, with kernel version 5.15.0-43-generic
. Let’s grab our debugging kernel at http://ddebs.ubuntu.com/pool/main/l/linux/linux-image-unsigned-5.15.0-43-generic-dbgsym_5.15.0-43.46_amd64.ddeb, unzip it and run dwarf2json
to create our profile:
$ mkdir profile
$ dwarf2json linux --elf usr/lib/debug/boot/vmlinux-5.15.0-43-generic > ./profile/vmlinux-5.15.0-43-generic.json
With our profile working let’s try some plugins:
$ python3 volatility3/vol.py -s ./profile/ -f dump.mem linux.bash.Bash
Volatility 3 Framework 2.4.0
PID Process Command Time Command
1863 bash 2022-08-29 13:45:56.000000 72.48.117.53.84.48.110.95.119.51.95.52.114.51.95.49.110.33.33.33
Looks like ASCII codes. Decode it and we have the flag!
> String.fromCharCode(72,48,117,53,84,48,110,95,119,51,95,52,114,51,95,49,110,33,33,33)
'H0u5T0n_w3_4r3_1n!!!'
flag: SEKAICTF{H0u5T0n_w3_4r3_1n!!!}
Symbolic Needs 2
Description
Recover the private key of the wallet address
0xACa5872e497F0Cc626d1E9bA28bAEC149315266e
. Submit the key wrapped withSEKAI{}
.Attachment md5sum:
4be69c88e6f19dd9c9f8e6c52bc93c28
Author: BattleMonger
Hint: Stick to what you used for Part 1, you all are also missing a very basic but extremely useful command.
- Points: 482
- Solves: 18
Writeup
We decided to run all plugins to see what’s left. My teammate found something interesting in linux.psaux.PsAux
:
$ python3 volatility3/vol.py -s ./profile/ -f dump.mem linux.psaux.PsAux
...
1863 1845 bash bash
1878 1863 ncat ncat -lvnp 1234 -c echo N4GQ2CQAAAAAAEFG5JRPEAIAADRQAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAEAAAAABZ6QAAAABSAAZABNQAFUAD2A5SQA2QBMQBBSAC2AJLQA3QLAEAACAABABSQGZADQMAQCADFASBQAAIALEAGOAC2AVSQMZAEMQCYGAUPBZNAOZIHUAEKCAFABGQQAWQFK4AGIAIEAACABAYDAEAG4CBRABZS65YBAEAACAABABMQAAIAMQDFUCTFBNSQVAYBMQDWIAMFAIMQAWQKMUGGKCVABVSQ4ZIKQMAWICDFBZSQVAYBMQEBMAAYAALQBIIBQMAVUCTHABNA6ZIQMQAGKDTFBKBQCZAIQMBUIAC5CRNBCZIPUAJGKBLFCNSQUZIRMUIWICAXACCQEGIAMQDYGATEAIMAAGIAUEAQCADRLFSQGZAJQMAQCADEAFJQAKIK5EAAAAAAJ3UQCAAAAB5BQVLTMFTWKORAFYXXOYLMNRSXIIDQMFZXG53POJSHUDLCNFYDGOLMNFZXILTUPB2NUALSNQKAAAAAABS6KHWNHGMBH4AWTJ3BE3XKCYFWPVQQSR6WWFSHC2WEUE3P5AP7G46OA3IBWAIA5EBAAAAA5EGAAAAA3ICVO4TPNZTSSFG2ANZXS462ARQXEZ3W3IEHAYLTON3W64TE3ICXA4TJNZ2NUBDFPBUXJWQFO5XXEZDT3ICG64DFN3NACZW2ARZGKYLE3IFHG4DMNF2GY2LOMVZ5UBDDN5SGLWQDMJUW5WQDON2HFWQFPJTGS3DM3IBWYZLO3IEG23TFNVXW42LD3ICXEYLOM5S5UALJ3IDGC4DQMVXGJWQDNFXHJKIAOINQAAAAOINQAAAA7IEHIZLTOQZC44DZ3IEDY3LPMR2WYZJ6AEAAAADTEIAAAAAIAABAEDQBAYAQQAIIAECAGDACBYARZ7YEAMIAGIQBAQBRIARGAEGAE=== | base32 -d > file.pyc
1886 1147 update-notifier update-notifier
1911 1863 sudo sudo insmod LiME/src/lime-5.15.0-43-generic.ko path=dump.mem format=lime
...
That’s a python bytecode file and we can decompile it. Let’s try pycdc:
$ cmake . && make -j 30 # build pycdc
$ ./pycdc ./file.pyc
# Source Generated with Decompyle++
# File: file.pyc (Python 3.10)
Unsupported opcode: WITH_EXCEPT_START
import sys
# WARNING: Decompyle incomplete
It failed. Python 3.10 is rather new and pycdc isn’t ready for it yet. So we did it in a hacky way: modify the source code in ASTree.cpp
and force it to continue, instead of throwing errors:
/* ASTree.cpp */
/* Remove the return statements in default and insert a break ;) */
...
case Pyc::SETUP_ANNOTATIONS:
variable_annotations = true;
break;
default:
break;
// fprintf(stderr, "Unsupported opcode: %s\n", Pyc::OpcodeName(opcode & 0xFF));
// cleanBuild = false;
// return new ASTNodeList(defblock->nodes());
...
With ./pycdc ./file.pyc > test2.py
, that’ll give us much more decompiled code. Not perfect, but almost!
# Source Generated with Decompyle++
# File: file.pyc (Python 3.10)
import sys
try:
password = sys.argv[1]
finally:
pass
print('Usage: ./wallet password')
exit()
words = []
with open('bip39list.txt', 'r') as f:
words = f.read().splitlines()
None(None, None, None)
if not None:
pass
code = 0x26F4036773F33FD1BC4E55616472CD7F65086B670B2DD5B84BB4D16F02730E734F72E500L
code = bin(code)[2:]
code = str(code.zfill(len(code) + (12 - len(code) % 12)))
mnemonic = []
for i in range(0, len(code), 12):
mnemonic.append(words[int(code[i:i + 12], 2) - 1])
print('Wrong')
The program seems to be generating a passphrase from a code. If you look up bip39
, you’ll realize it’s a way to use a passphrase to store wallet address and private keys. Since we don’t have access to the wordlist bip39list.txt
in the dump, we’ll assume the standard one is used. Let’s clean up the code and generate the passphrase:
words = []
with open('bip39list.txt', 'r') as f:
words = f.read().splitlines()
code = 0x26F4036773F33FD1BC4E55616472CD7F65086B670B2DD5B84BB4D16F02730E734F72E500
code = bin(code)[2:]
code = str(code.zfill(len(code) + (12 - len(code) % 12)))
mnemonic = []
for i in range(0, len(code), 12):
mnemonic.append(words[int(code[i:i + 12], 2) - 1])
print(' '.join(mnemonic))
$ python3 test2.py
evidence leopard solution layer legend danger orient project silver flower wrong path stove throw fortune report nuclear old target exact broom hawk toss paper
We’ve got the mnemonics! Let’s head to https://iancoleman.io/bip39/:
The first derived address (0xACa5872e497F0Cc626d1E9bA28bAEC149315266e
) matches the description. We need the private key:
flag: SEKAICTF{0x81c458e9fae445de18385a3379513acc8e191e4c2667c85aa0a52a32ec4e6d55}