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:
$ python3 vol.py -f dump.mem linux.psauxVolatility 3 Framework 2.3.1Progress: 100.00 Stacking attempts finishedPID PPID COMM ARGS...1731 985 gsd-xsettings /usr/libexec/gsd-xsettings1787 985 ibus-x11 /usr/libexec/ibus-x111845 985 gnome-terminal- /usr/libexec/gnome-terminal-server1863 1845 bash bash1878 1863 ncat ncat -lvnp 1234 -c echo N4GQ2CQAAAAAAEFG5JRPEAIAADRQAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAEAAAAABZ6QAAAABSAAZABNQAFUAD2A5SQA2QBMQBBSAC2AJLQA3QLAEAACAABABSQGZADQMAQCADFASBQAAIALEAGOAC2AVSQMZAEMQCYGAUPBZNAOZIHUAEKCAFABGQQAWQFK4AGIAIEAACABAYDAEAG4CBRABZS65YBAEAACAABABMQAAIAMQDFUCTFBNSQVAYBMQDWIAMFAIMQAWQKMUGGKCVABVSQ4ZIKQMAWICDFBZSQVAYBMQEBMAAYAALQBIIBQMAVUCTHABNA6ZIQMQAGKDTFBKBQCZAIQMBUIAC5CRNBCZIPUAJGKBLFCNSQUZIRMUIWICAXACCQEGIAMQDYGATEAIMAAGIAUEAQCADRLFSQGZAJQMAQCADEAFJQAKIK5EAAAAAAJ3UQCAAAAB5BQVLTMFTWKORAFYXXOYLMNRSXIIDQMFZXG53POJSHUDLCNFYDGOLMNFZXILTUPB2NUALSNQKAAAAAABS6KHWNHGMBH4AWTJ3BE3XKCYFWPVQQSR6WWFSHC2WEUE3P5AP7G46OA3IBWAIA5EBAAAAA5EGAAAAA3ICVO4TPNZTSSFG2ANZXS462ARQXEZ3W3IEHAYLTON3W64TE3ICXA4TJNZ2NUBDFPBUXJWQFO5XXEZDT3ICG64DFN3NACZW2ARZGKYLE3IFHG4DMNF2GY2LOMVZ5UBDDN5SGLWQDMJUW5WQDON2HFWQFPJTGS3DM3IBWYZLO3IEG23TFNVXW42LD3ICXEYLOM5S5UALJ3IDGC4DQMVXGJWQDNFXHJKIAOINQAAAAOINQAAAA7IEHIZLTOQZC44DZ3IEDY3LPMR2WYZJ6AEAAAADTEIAAAAAIAABAEDQBAYAQQAIIAECAGDACBYARZ7YEAMIAGIQBAQBRIARGAEGAE=== | base32 -d > file.pyc1886 1147 update-notifier update-notifier1911 1863 sudo sudo insmod LiME/src/lime-5.15.0-43-generic.ko path=dump.mem format=lime1918 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:
$ python3 file.pycUsage: ./wallet password
Passing a random argument results in a FileNotFoundError
:
$ python3 file.pyc passwordTraceback (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:
import disimport marshal
with open('file.pyc', 'rb') as f: f.seek(16) print(dis.dis(marshal.load(f)))
$ 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_VALUENone
Let’s analyze this:
- The program loads the
bip39
wordlist. It’s a standard wordlist used to secure crypto wallets with a mnemonic. - It then stores a hardcoded integer in the variable
code
, converts to binary andzfill
s it so that length is multiple of 12. - 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 calledmnemonic
. - No matter what, the code will always print “Wrong”! :D
Let’s write a simple script to find the mnemonics with this information:
#!/usr/bin/python3words = []
with open('bip39list.txt', 'r') as f: words = f.read().splitlines()
code = 75673125099835840306362297188218306412669859836254678874904603942583570317024638985472code = 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:
$ 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:
To access the private key, go to My personal account
-> View paper wallet
:
The flag is SEKAI{0x81c458e9fae445de18385a3379513acc8e191e4c2667c85aa0a52a32ec4e6d55}
.