Symbolic Needs 2

Symbolic Needs 2

November 28, 2022
2 min read
symbolic-needs-2

Symbolic Needs 2

Author
Battlemonger
Category
Forensics
Points
482
Solves
18
Files
memdump
Flag
SEKAI{0x81c458e9fae445de18385a3379513acc8e191e4c2667c85aa0a52a32ec4e6d55}

Recover the private key of the wallet address 0xACa5872e497F0Cc626d1E9bA28bAEC149315266e.
Submit the key wrapped with SEKAI{}.

Let’s follow up from the last proglem with the linux.psaux plugin, to gather and display all processes:

Terminal window
$ python3 vol.py -f dump.mem linux.psaux
Volatility 3 Framework 2.3.1
Progress: 100.00 Stacking attempts finished
PID PPID COMM ARGS
...
1731 985 gsd-xsettings /usr/libexec/gsd-xsettings
1787 985 ibus-x11 /usr/libexec/ibus-x11
1845 985 gnome-terminal- /usr/libexec/gnome-terminal-server
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
1918 1911 sudo sudo insmod LiME/src/lime-5.15.0-43-generic.ko path=dump.mem format=lime
...

It looks like the scammer was serving some base32 through Netcat. We also notice that it’s piped into a .pyc file, which is Python bytecode.

Run the command echo [PUT YOUR BASE32 HERE] | base32 -d > file.pyc to convert this base32 into a binary. Let’s run the .pyc with Python3:

Terminal window
$ python3 file.pyc
Usage: ./wallet password

Passing a random argument results in a FileNotFoundError:

Terminal window
$ python3 file.pyc password
Traceback (most recent call last):
File "test2.py", line 12, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'bip39list.txt'

We can find this wordlist in the bitcoin/bips repository. If you run the binary again with the same argument it just outputs “Wrong.” We’ll need to disassemble this.

Disassemble the bytecode with the dis module:

disassembled.py
import dis
import marshal
with open('file.pyc', 'rb') as f:
f.seek(16)
print(dis.dis(marshal.load(f)))
Terminal window
$ python3 disassembled.py
1 0 LOAD_CONST 0 (0)
2 LOAD_CONST 1 (None)
4 IMPORT_NAME 0 (sys)
6 STORE_NAME 0 (sys)
3 8 SETUP_FINALLY 7 (to 24)
4 10 LOAD_NAME 0 (sys)
12 LOAD_ATTR 1 (argv)
14 LOAD_CONST 2 (1)
16 BINARY_SUBSCR
18 STORE_NAME 2 (password)
20 POP_BLOCK
22 JUMP_FORWARD 11 (to 46)
5 >> 24 POP_TOP
26 POP_TOP
28 POP_TOP
6 30 LOAD_NAME 3 (print)
32 LOAD_CONST 3 ('Usage: ./wallet password')
34 CALL_FUNCTION 1
36 POP_TOP
7 38 LOAD_NAME 4 (exit)
40 CALL_FUNCTION 0
42 POP_TOP
44 POP_EXCEPT
10 >> 46 BUILD_LIST 0
48 STORE_NAME 5 (words)
12 50 LOAD_NAME 6 (open)
52 LOAD_CONST 4 ('bip39list.txt')
54 LOAD_CONST 5 ('r')
56 CALL_FUNCTION 2
58 SETUP_WITH 14 (to 88)
60 STORE_NAME 7 (f)
13 62 LOAD_NAME 7 (f)
64 LOAD_METHOD 8 (read)
66 CALL_METHOD 0
68 LOAD_METHOD 9 (splitlines)
70 CALL_METHOD 0
72 STORE_NAME 5 (words)
74 POP_BLOCK
12 76 LOAD_CONST 1 (None)
78 DUP_TOP
80 DUP_TOP
82 CALL_FUNCTION 3
84 POP_TOP
86 JUMP_FORWARD 8 (to 104)
>> 88 WITH_EXCEPT_START
90 POP_JUMP_IF_TRUE 47 (to 94)
92 RERAISE 1
>> 94 POP_TOP
96 POP_TOP
98 POP_TOP
100 POP_EXCEPT
102 POP_TOP
15 >> 104 LOAD_CONST 6 (75673125099835840306362297188218306412669859836254678874904603942583570317024638985472)
106 STORE_NAME 10 (code)
18 108 LOAD_NAME 11 (bin)
110 LOAD_NAME 10 (code)
112 CALL_FUNCTION 1
114 LOAD_CONST 7 (2)
116 LOAD_CONST 1 (None)
118 BUILD_SLICE 2
120 BINARY_SUBSCR
122 STORE_NAME 10 (code)
19 124 LOAD_NAME 12 (str)
126 LOAD_NAME 10 (code)
128 LOAD_METHOD 13 (zfill)
130 LOAD_NAME 14 (len)
132 LOAD_NAME 10 (code)
134 CALL_FUNCTION 1
136 LOAD_CONST 8 (12)
138 LOAD_NAME 14 (len)
140 LOAD_NAME 10 (code)
142 CALL_FUNCTION 1
144 LOAD_CONST 8 (12)
146 BINARY_MODULO
148 BINARY_SUBTRACT
150 BINARY_ADD
152 CALL_METHOD 1
154 CALL_FUNCTION 1
156 STORE_NAME 10 (code)
22 158 BUILD_LIST 0
160 STORE_NAME 15 (mnemonic)
24 162 LOAD_NAME 16 (range)
164 LOAD_CONST 0 (0)
166 LOAD_NAME 14 (len)
168 LOAD_NAME 10 (code)
170 CALL_FUNCTION 1
172 LOAD_CONST 8 (12)
174 CALL_FUNCTION 3
176 GET_ITER
>> 178 FOR_ITER 20 (to 220)
180 STORE_NAME 17 (i)
25 182 LOAD_NAME 15 (mnemonic)
184 LOAD_METHOD 18 (append)
186 LOAD_NAME 5 (words)
188 LOAD_NAME 19 (int)
190 LOAD_NAME 10 (code)
192 LOAD_NAME 17 (i)
194 LOAD_NAME 17 (i)
196 LOAD_CONST 8 (12)
198 BINARY_ADD
200 BUILD_SLICE 2
202 BINARY_SUBSCR
204 LOAD_CONST 7 (2)
206 CALL_FUNCTION 2
208 LOAD_CONST 2 (1)
210 BINARY_SUBTRACT
212 BINARY_SUBSCR
214 CALL_METHOD 1
216 POP_TOP
218 JUMP_ABSOLUTE 89 (to 178)
27 >> 220 LOAD_NAME 3 (print)
222 LOAD_CONST 9 ('Wrong')
224 CALL_FUNCTION 1
226 POP_TOP
228 LOAD_CONST 1 (None)
230 RETURN_VALUE
None

Let’s analyze this:

  1. The program loads the bip39 wordlist. It’s a standard wordlist used to secure crypto wallets with a mnemonic.
  2. It then stores a hardcoded integer in the variable code, converts to binary and zfills it so that length is multiple of 12.
  3. Next, it converts each 12 bits to decimal, and subtracts one. This number is used as an index, and appends the corresponding word from bip39 in an array called mnemonic.
  4. No matter what, the code will always print “Wrong”! :D

Let’s write a simple script to find the mnemonics with this information:

mnemonic.py
#!/usr/bin/python3
words = []
with open('bip39list.txt', 'r') as f:
words = f.read().splitlines()
code = 75673125099835840306362297188218306412669859836254678874904603942583570317024638985472
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(mnemonic)

Running the script:

Terminal window
$ python3 mnemonic.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']

Looks like we’ve got our mnemonic!

Now we can visit MyEtherWallet and enter the 24-word mnemonic phrase. Look for the 0xACa5872e497F0Cc626d1E9bA28bAEC149315266e wallet and gain access to the dashboard:

MEW dashboard

To access the private key, go to My personal account -> View paper wallet:

Viewing wallet

The flag is SEKAI{0x81c458e9fae445de18385a3379513acc8e191e4c2667c85aa0a52a32ec4e6d55}.