PicoCTF Some Assembly Required 4
This is the last and the most difficult challenge in the series of web Assembly from Picoctfs. The name of the challenge is Some Assembly Required 4.
In the previous challenge we had a weak copy function implementation which we reversed easily. Today the challenge has leveled up. The issue is somewhere else, and the source code is bigger.
Let's download the Wasm binary using wget and decompile the wasm core using Wasm-decompile.
We are left with a .dcmp file which is incomprehensible at a single shot. Lets convert it into a simpler language like python using LLMs.
This is the check_flag function with a lot of code. This is where the issues reside. Let me explain it step by step.
It takes an input and stores in at memory address 1072. Does some series of transformations on each byte. Compares it to the value in 1024 address and returns true/false.
Here are the series of transformation done on the input:
So these are the transitions getting done for comparison. So it uses XOR operations. XOR operations are reversible. a⊕b=c then c⊕b=a, this has been a core idea in solving many crypto challenges.
So let's create a script that does all this operation in reverse order using a python script.
import copy
transformed_flag = [
0x18, 0x6a, 0x7c, 0x61, 0x11, 0x38, 0x69, 0x37, 0x16, 0x0a,
0x73, 0x00, 0x67, 0x4f, 0x5f, 0x3b, 0x01, 0x47, 0x48, 0x7e,
0x30, 0x66, 0x1b, 0x55, 0x77, 0x05, 0x68, 0x45, 0x0d, 0x39,
0x0d, 0x4b, 0x4a, 0x2d, 0x3f, 0x3c, 0x49, 0x5a, 0x78, 0x02,
0x57, 0x00
]
def reverse_flag(transformed):
# Step 1: Undo byte-swapping
unshuffled = transformed[:]
for i in range(0, len(unshuffled) - 1, 2):
unshuffled[i], unshuffled[i + 1] = unshuffled[i + 1], unshuffled[i]
# Step 2: Reverse the transformations
encrypted_copy = unshuffled[:]
output = []
for idx, char in enumerate(encrypted_copy):
orig = char
# Reverse 7/6/5 based on idx % 3
if idx % 3 == 0:
orig ^= 7
elif idx % 3 == 1:
orig ^= 6
else:
orig ^= 5
# Reverse 9 or 8
if idx % 2 == 0:
orig ^= 9
else:
orig ^= 8
# Reverse XOR with idx % 10
orig ^= idx % 10
# Reverse XOR with idx - 3 (only if idx > 2)
if idx > 2:
orig ^= encrypted_copy[idx - 3]
# Reverse XOR with idx - 1 (only if idx > 0)
if idx > 0:
orig ^= encrypted_copy[idx - 1]
# Reverse XOR with 20
orig ^= 20
output.append(orig)
return output
# Run reversal
recovered = reverse_flag(transformed_flag)
print("[+] Recovered bytes:")
print(recovered)
print("\n[+] Recovered flag:")
print("".join(chr(b) for b in recovered))
Hope this series was useful, see you later.