Skip to main content

Write-up REEZS

·733 words·4 mins

Hello guys🤗! today i will write about a REV challenge that met at the CSCV2025 that named:Reezs.

image

First when you install the file and extract the folder you will have an .exe file like this:

image
When you run that file, you will see an CMD that told you enter the flag:
image
Since there were no clues at first, I typed some random input to see how the program executes. As expected, it shut down immediately.
Then I opened IDA and loaded the file to read the main function and understand the execution flow. In IDA View-A, the main function looks like this:

image

Let F5 to see that pseudocode:



int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rdx
  __int64 v4; // r8
  __m128i si128; // xmm0
  const char *v6; // rcx
  _BYTE v8[32]; // [rsp+0h] [rbp-58h] BYREF
  char Str[16]; // [rsp+20h] [rbp-38h] BYREF
  __m128i v10; // [rsp+30h] [rbp-28h]
  __int64 v11; // [rsp+40h] [rbp-18h]
  __int64 v12; // [rsp+48h] [rbp-10h]

  v10 = 0i64;
  *(_OWORD *)Str = 0i64;
  v11 = 0i64;
  sub_1400010F0("Enter flag: ", argv, envp);
  sub_140001170("%32s", Str);
  if ( strlen(Str) != 32 )
  {
    puts("No");
    if ( ((unsigned __int64)v8 ^ v12) == _security_cookie )
      return 0;
LABEL_5:
    __debugbreak();
  }
  si128 = _mm_load_si128((const __m128i *)&xmmword_14001E030);
  *(__m128i *)Str = _mm_xor_si128(_mm_load_si128((const __m128i *)Str), si128);
  v10 = _mm_xor_si128(si128, v10);
  if ( _mm_movemask_epi8(
         _mm_and_si128(
           _mm_cmpeq_epi8(*(__m128i *)Str, (__m128i)xmmword_140029000),
           _mm_cmpeq_epi8(v10, (__m128i)xmmword_140029010))) == 0xFFFF )
    v6 = (const char *)&unk_140023E7C;
  else
    v6 = "No";
  sub_1400010F0(v6, v3, v4);
  if ( ((unsigned __int64)v8 ^ v12) != _security_cookie )
    goto LABEL_5;
  return 0;
}

What are the important parts of that code we should care about? Let me explain.


 sub_1400010F0("Enter flag: ", argv, envp);
  sub_140001170("%32s", Str);

This line ask us to type 32-byte strings input 32-byte strings(or we know the flag will have 32-byte strings not include the format flag)

 
si128 = _mm_load_si128((const __m128i *)&xmmword_14001E030);

This line point that it takes 16-byte constant of the xmmord_14001E030 adress into si128


*(__m128i *)Str = _mm_xor_si128(_mm_load_si128((const __m128i *)Str), si128);

This line is load 16-byte of Str XOR with si128 and the result is the first stage 16-byte strings of the Str


v10 = _mm_xor_si128(si128, v10);

v10 at first is assigned to 0i64, which is full of 0. If we XOR with si128, we will receive si128

if ( _mm_movemask_epi8(
         _mm_and_si128(
           _mm_cmpeq_epi8(*(__m128i *)Str, (__m128i)xmmword_140029000),
           _mm_cmpeq_epi8(v10, (__m128i)xmmword_140029010))) == 0xFFFF )

This comparision can be analyze to smaller like this:


 _mm_cmpeq_epi8(*(__m128i *)Str, (__m128i)xmmword_140029000),

=>Return 16-byte, each byte = 0xFF if (__m128i )Str == (__m128i)xmmword_140029000, else = 0x00


_mm_and_si128(

=>Only keep if both comparisons are equal at the same position

_mm_movemask_epi8(

=>take top 16-bit of each byte,if the result is 0xFFFF it means the 16-bit verified(or all 16 bytes were equal in both comparisons).

If both of conditions are true, the program will print:

(const char *)&unk_140023E7C

That is yes:

.rdata:0000000140023E7C unk_140023E7C   db  59h ; Y             ; DATA XREF: main:loc_1400010CA↑o
.rdata:0000000140023E7D                 db  65h ; e
.rdata:0000000140023E7E                 db  73h ; s
.rdata:0000000140023E7F                 db    0

And i find this when point to xmmword_140029010 and press X(jump to xref)

{
  BOOL result; // eax

  result = IsDebuggerPresent();
  if ( !result )
  {
    xmmword_140029010 = xmmword_14001E010;
    xmmword_140029000 = xmmword_14001E000;
  }
  return result;
}

=>If you run the binary in a debugger, those constants at 0x140029010 and 0x140029000 won’t be initialized=>an anti-debugger trick. And if you don’t run in a debugger, xmmword_140029010 will be xmmword_14001E010 and xmmword_140029000 will be xmmword_14001E000

So i will use what i explained below to extract the flag:

.rdata:000000014001E000 xmmword_14001E000 xmmword 939FCF9C9B9998C99DC8C9989ECFCB9Ah
.rdata:000000014001E010 xmmword_14001E010 xmmword 9F9D9D9DCB989A9B999A98CF9DCFCFCFh
.rdata:000000014001E030 xmmword_14001E030 xmmword 0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAh

+The first stage we take first 16-bytes of xmmword_14001E000 change into hex, then XOR with xmmword_14001E030

The the first 16-byte of xmmword_14001E000 is:

unsigned char ida_chars[] = {
  0x9A, 0xCB, 0xCF, 0x9E, 0x98, 0xC9, 0xC8, 0x9D,
  0xC9, 0x98, 0x99, 0x9B, 0x9C, 0xCF, 0x9F, 0x93
};

XOR with 0xaa(xmmword_14001E030) into hex:

30 61 65 34 32 63 62 37 63 32 33 31 36 65 35 39

ASCII:

0ae42cb7c2316e59

+The second stage we take first 16-bytes of xmmword_14001E010 change into hex, then XOR with xmmword_14001E030

The the first 16-byte of xmmword_14001E000 is:

unsigned char ida_chars[] = { 0xCF, 0xCF, 0xCF, 0x9D, 0xCF, 0x98, 0x9A, 0x99, 0x9B, 0x9A, 0x98, 0xCB, 0x9D, 0x9D, 0x9D, 0x9F };

XOR with 0xaa(xmmword_14001E030) into hex:

65 65 65 37 65 32 30 33 31 30 32 61 37 37 37 35

ASCII:

eee7e203102a7775

And the flag is CSCV2025{0ae42cb7c2316e59eee7e203102a7775}

That’s all of this challenge. Thanks for reading😘!