Rating:


I opened the binary in IDA, and looked at the import table. I looked at the XRef of CryptDecrypt.
The function calling it didn't seem to have much of a verification, so I jump to the XRef of this function.

The interesting part of the function is
```c
v35 = GetDlgItem(hWndParent, 4);
sub_406920(&v58, 0, 50);
GetWindowTextA(v35, (LPSTR)&Paint, 32);
v36 = strlen((const char *)&Paint);
if ( v36 >= 6 )
{
v37 = SLOBYTE(Paint.fErase) % 7;
v38 = 1;
for ( i = 2; i < v37; ++i )
v38 *= (_BYTE)i;
v40 = 0;
if ( (unsigned int)v36 >= 0x40 )
{
v41 = _mm_cvtsi32_si128(v38);
v42 = _mm_unpacklo_epi8(v41, v41);
v43 = _mm_shuffle_epi32(_mm_unpacklo_epi16(v42, v42), 0);
do
{
*(__int128 *)((char *)&v58 + v40) = (__int128)_mm_xor_si128(*(__m128i *)((char *)&Paint.hdc + v40), v43);
*(__int128 *)((char *)&v59 + v40) = (__int128)_mm_xor_si128(
*(__m128i *)((char *)&Paint.rcPaint.right + v40),
v43);
*(__int128 *)((char *)&v60 + v40) = (__int128)_mm_xor_si128(*(__m128i *)&Paint.rgbReserved[v40], v43);
*(__m128i *)((char *)&v61 + v40) = _mm_xor_si128(*(__m128i *)&Paint.rgbReserved[v40 + 16], v43);
v40 += 64;
}
while ( v40 < v36 - v36 % 64 );
}
for ( ; v40 < v36; ++v40 )
*((_BYTE *)&v58 + v40) = v38 ^ *((_BYTE *)&Paint.hdc + v40);
*((_BYTE *)&v58 + v40) = 0;
v44 = 0;
do
{
*((_BYTE *)&v58 + v44) = __ROL1__(*((_BYTE *)&v58 + v44) ^ byte_420AE0[v44], v44);
++v44;
}
while ( v44 < v36 );
v45 = &v5;;
v46 = (char *)&unk_4278D8;
v47 = 27;
while ( *(_DWORD *)v45 == *(_DWORD *)v46 )
{
v45 = (__int128 *)((char *)v45 + 4);
v46 += 4;
v48 = v47 < 4;
v47 -= 4;
if ( v48 )
{
if ( *(_WORD *)v45 == *(_WORD *)v46 && *((_BYTE *)v45 + 2) == v46[2] )
{
MessageBoxA(hWndParent, "orz", "orz", 0);
function_calling_decrypt((BYTE *)&Paint);
}
return 0;
}
}
}
```

There still is much to look at. `v58` is the user input, and `v36` is its length.
By trying some things here and there, I found out that the user input can't be more than 0x40.
Reducing the code to
```c
v35 = GetDlgItem(hWndParent, 4);
sub_406920(&v58, 0, 50);
GetWindowTextA(v35, (LPSTR)&Paint, 32);
v36 = strlen((const char *)&Paint);
if ( v36 >= 6 )
{
v37 = SLOBYTE(Paint.fErase) % 7;
v38 = 1;
for ( i = 2; i < v37; ++i )
v38 *= (_BYTE)i;
v40 = 0;
for ( ; v40 < v36; ++v40 )
*((_BYTE *)&v58 + v40) = v38 ^ *((_BYTE *)&Paint.hdc + v40);
*((_BYTE *)&v58 + v40) = 0;
v44 = 0;
do
{
*((_BYTE *)&v58 + v44) = __ROL1__(*((_BYTE *)&v58 + v44) ^ byte_420AE0[v44], v44);
++v44;
}
while ( v44 < v36 );
v45 = &v5;;
v46 = (char *)&unk_4278D8;
v47 = 27;
while ( *(_DWORD *)v45 == *(_DWORD *)v46 )
{
v45 = (__int128 *)((char *)v45 + 4);
v46 += 4;
v48 = v47 < 4;
v47 -= 4;
if ( v48 )
{
if ( *(_WORD *)v45 == *(_WORD *)v46 && *((_BYTE *)v45 + 2) == v46[2] )
{
MessageBoxA(hWndParent, "orz", "orz", 0);
function_calling_decrypt((BYTE *)&Paint);
}
return 0;
}
}
}
```

`SLOBYTE(Paint.fErase)` is the 7th character of the user input.
`v38` value depends then on this character value modulo 7.
The whole user input is then xored with `v38`.
Then it is xored again, with `ANNAWGALFYBKVIAHMXTFCAACLAAAAYK`.

The result is then compared with a fixed value.

I made the following script from these observations
```c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>

uint8_t rotr32 (uint8_t value, unsigned int count) {
// From wikipedia
const unsigned int mask = CHAR_BIT * sizeof(value) - 1;
count &= mask;
return (value >> count) | (value << (-count & mask));
}

const unsigned char correct[] = {0x4E, 0xAE, 0x61, 0x0BA, 0x0E4, 0x2B, 0x55, 0x0AA, 0x59, 0x0FC, 0x4D, 0x2, 0x17, 0x6B, 0x13, 0x0A1, 0x41, 0x0FE, 0x35, 0x0B, 0x0B4, 0x0B, 0x52, 0x2F, 0x46, 0x0CC, 0x35, 0x82, 0x0E5, 0x88, 0x50};
const unsigned char modif[] = "ANNAWGALFYBKVIAHMXTFCAACLAAAAYK";

int main() {
char final[32] = {0};
for (unsigned char i = 0; i < 32; i++) {
unsigned char tmp = rotr32(correct[i], i) ^ 0x78 ^ modif[i]; // After a few attemps v38 was 0x78
final[i] = tmp;
}
printf("%s\n", final);
return 0;
}
```

which output `wannaflag_is_just_a_paper_tigerx`
Entering this into the binary give you the flag
```
GACTF{WannaFlag_is_just_a_easy_re_with_a_beautiful_appearance}
```

Original writeup (https://caillou.eu/2020/gactf/).