# WGMY 2024 -- RmRf
> "What happened to my system? It has been working perfectly for more than 20 years."
We are given a zip that contains a **bin** and **cue** file. If we run strings, we see that it points to a playstation/PSX program.
I extracted the **ISO** file by running `binwalk --extract rmrf.bin`. From the **ISO** file, we can extract the playstation **EXE** by using **unar**.
```cs
rmrf > binwalk --extract rmrf.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
4888 0x1318 ISO 9660 Primary Volume,
rmrf > unar _rmrf.bin.extracted/1318.iso
_rmrf.bin.extracted/1318.iso: ISO 9660
PROGRAM.EXE (45056 B)... OK.
SYSTEM.CNF (59 B)... OK.
Successfully extracted to "1318".
rmrf > ls
1318 _rmrf.bin.extracted rmrf.bin rmrf.cue
rmrf > ls 1318/
PROGRAM.EXE SYSTEM.CNF
rmrf > strings 1318/PROGRAM.EXE
# PS-X EXE
# Not Licensed or Endorsed by Sony Computer Entertainment Inc.
# Built using GCC and PSn00bSDK libraries
# [ERROR] Multiple buttons pressed at once!
# Welcome to PSX OS v0.0.1
# Initializing.......
# Loading modules....OK
# Starting services..OK
# Starting shell.....OK
# jonny
# 909321251121f77557c8c8fad239de4f
# Welcome to PSX OS v0.0.1
# PSX login:
# password:
# su root
# [jonny@psx]$
# Password:
# Invalid access to the system detected!
# Attack discovered!!
# Self destructing...!
# rm -rf /
# [root@psx]$
# The whole system was deleted...
# except for...
# wgmy{
# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{};':",./<>?\|`~
# $Id: sys.c,v 1.140 1998/01/12 07:52:27 noda Exp yos $
```
We can throw this **PROGRAM.EXE** into IDA/Ghidra to investigate and reverse engineer the executable.
Instead of starting from the entry point which seems tedious to reverse, we can find cross references to relevant strings such as **"wgmy{"** which would bring us to the flag generation portion of the code.
```c
sub_80011158(dword_8001A590, "The whole system was deleted...\nexcept for... \n");
memset(v13, 0, sizeof(v13));
v14 = 0;
((void (__fastcall *)(_DWORD, int *, int *, int))sub_8001124C)(0, dword_80019000, v13, 32);
sub_80011158(dword_8001A590, "wgmy{");
sub_80011158(dword_8001A590, v13);
sub_80011158(dword_8001A590, "}!\n");
```
As we can see, `sub_8001124c` seems to generate the flag.
```c
int __fastcall sub_8001124C(char *ct, char *a2, char *pt, int a4)
{
int v7; // $v0
int i; // $v1
char *v10; // $t1
signed int v11; // $v1
int v12; // $t0
int v13; // $a2
bool v14; // dc
char *v15; // $s3
int v16; // $a0
int v17; // $v1
char v18; // $v0
char v19; // $a3
char sbox[260]; // [sp+10h] [-104h] BYREF
v7 = strlen(ct);
for ( i = 0; i != 256; ++i ) // init sbox
sbox[i] = i;
v10 = sbox;
v11 = 0;
v12 = 0;
do
{
if ( !v7 )
_break(7u, 0);
v13 = (unsigned __int8)*v10;
v11 = (ct[v12 % v7] + v13 + v11) & (unsigned int)&unk_800000FF;
++v12;
if ( v11 < 0 )
v11 = ((v11 - 1) | 0xFFFFFF00) + 1;
*v10++ = sbox[v11];
sbox[v11] = v13;
}
while ( v12 != 256 );
v14 = a4 == 0;
v15 = &a2[a4];
if ( !v14 )
{
LOBYTE(v16) = 0;
LOBYTE(v17) = 0;
do
{
v17 = (unsigned __int8)(v17 + 1);
v18 = sbox[v17];
v16 = (unsigned __int8)(v18 + v16);
v19 = *a2;
sbox[v17] = sbox[v16];
sbox[v16] = v18;
++pt;
++a2;
*(pt - 1) = sbox[(unsigned __int8)(v18 + sbox[v17])] ^ v19;
}
while ( v15 != a2 );
}
return 0;
}
```
If you have seen implementation for RC4 encryption/decryption before, this will look familiar to you. Essentiually, it seems like the flag is encrypted with `dword_80019000` being the encrypted flag and ... `0` as the pointer to the key...?
Let's look more closely at the assembly when the parameters are passed into the `rc4_decrypt` function.
```asm
lw $a0, dword_8001A520
li $a1, dword_80019000
addiu $a2, $sp, 0x3C+var_2C
li $a3, 0x20
jal sub_8001124C
```
Although the decompilation showed that the first parameter to the `rc4_decrypt` function is **0**, the assembly says otherwise. Let's rename the stuff we have identified so far to `key_ptr`, `enc_flag` and `rc4_decrypt`.
We are most keen in finding out what the correct `key_ptr` value is required to decrypt the flag.
In one of the cross references, we can find the `key_ptr` pointer being initialized.
```c
sub_800110D0(dword_8001A52C, 100, &key);
```
```c
int __fastcall sub_800110D0(int a1, int a2, _DWORD *a3)
{
int result; // $v0
result = memset(a1, 0, a2);
*a3 = a1;
a3[1] = a2;
a3[2] = 0;
return result;
}
```
As we can see, it seems like it initializes the key_ptr with some sort of a struct. We can define a struct as such
```c
struct psx_str {
char* buf;
int max_size;
int cur_size;
}
```
If we look at other cross references,
```c
if ( ((v0 - 1) & v0) != 0 )
{
sub_80016580("[ERROR] Multiple buttons pressed at once!\n");
return 0;
}
result = 1;
if ( (v2 & 0x800) == 0 )
{
if ( (v2 & 0x10) != 0 )
append(&key, 0x54);
if ( (v2 & 0x40) != 0 )
append(&key, 0x58);
if ( (v2 & 0x80) != 0 )
append(&key, 0x53);
if ( (v2 & 0x20) != 0 )
append(&key, 0x43);
if ( (v2 & 0x1000) != 0 )
append(&key, 0x55);
if ( (v2 & 0x4000) != 0 )
append(&key, 0x44);
if ( (v2 & 0x8000) != 0 )
append(&key, 0x4C);
if ( (v2 & 0x2000) != 0 )
{
append(&key, 82);
*(_DWORD *)(v4 - 32688) = v2;
return 0;
}
```
It seems like its parsing console inputs and appending it to the key stream. As we can see, there are 8 possible values here, `[0x54, 0x58, 0x53, 0x43, 0x55, 0x44, 0x4c]`.
Finally at the last interesting cross reference, we find some sort of validation for the key. This is where we start figuring out the correct key.
```c
int __fastcall sub_800107CC(_BYTE *a1)
{
unsigned __int16 v3; // [sp+10h] [-20h] BYREF
__int16 v4; // [sp+12h] [-1Eh]
__int16 v5; // [sp+14h] [-1Ch]
__int16 v6; // [sp+16h] [-1Ah]
__int16 v7; // [sp+18h] [-18h]
__int16 v8; // [sp+1Ah] [-16h]
__int16 v9; // [sp+1Ch] [-14h]
__int16 v10; // [sp+1Eh] [-12h]
int v11; // [sp+20h] [-10h]
int v12; // [sp+24h] [-Ch]
int v13; // [sp+28h] [-8h]
int v14; // [sp+2Ch] [-4h]
v12 = 0;
v13 = 0;
v14 = 0;
v3 = 32 * MEMORY[0];
v4 = 32 * MEMORY[1];
v5 = 32 * MEMORY[2];
v6 = 32 * MEMORY[3];
v7 = 32 * MEMORY[4];
v8 = 32 * MEMORY[5];
v10 = 32 * MEMORY[7];
v9 = 32 * MEMORY[6];
v11 = (unsigned __int16)(32 * MEMORY[8]);
MulMatrix2(dword_80019020, &v3);
v3 *= 4;
v4 *= 4;
v5 *= 4;
v6 *= 4;
v7 *= 4;
v8 *= 4;
v9 *= 4;
v10 *= 4;
LOWORD(v11) = 4 * v11;
if ( sub_80011D98(&v3) )
*a1 = 1;
else
sub_80011158(dword_8001A590, "Invalid access to the system detected!\nAttack discovered!!\nSelf destructing...!\n");
return 1;
}
```
Although the decompilation fails, we recognize that the MEMORY correspond with our key, and that there is some matrix multiplication going on with another value before it is validated.
I spent significant time trying to solve this, but without much success. Finally, knowing that the validation function checks for 9 characters, and that there are only 8 possible characters, I decided to write a brute force script to brute force the `8**9` number of permutations.
```py
from Crypto.Cipher import ARC4
from tqdm import tqdm
from itertools import product
ct = bytes.fromhex("85BB8F174AC63EA0958916A79ED692F27E8AF6F4C2235B7B042C65E364D9945D")
possible = list("TXSCUDLR")
for a in tqdm(product(possible, repeat=9)):
rc4 = ARC4.new("".join(a).encode())
flag = rc4.decrypt(ct)
try:
print(f"\n{flag.decode()}\n"))
except:
continue
# output: 22954993it [02:44, 140627.19it/s]
# output: a92a70e1f4935d0a7dbb729368829848
```