Post

Cryptoversing Write-up - CTFLearn

Cryptoversing is a medium level reverse engineering challenge on CTFLearn. Basically, we are given a binary file and we need to find the flag somehow.

First Look

Let’s start by running the binary file.

1
2
3
4
sarp@IdeaPad:~/Desktop$ ./xor.bin 
[*] Hello! Welcome to our Program!
Enter the password to contiune:  test12345
[-] Wrong Password

The program asks for a password. Eventually, we need to see the source code of the program.

Source Code Analysis

I tried to open it with Ghidra but it didn’t create a proper decompiled code. So, I decided to use an online decompiler. I used dogBolt to decompile the binary file. DogBolt decompiles the binary file using several decompilers and shows all of them. So, I can choose the best one. It supports angr,BinaryNinja Boomerang, dewolf, Ghidra, Hex-Rays, RecStudio, Reko, Relyze, RetDec, Snowman decompilers. I chosed Hex-Rays decompiler and it gave me a good decompiled code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int __fastcall main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+4h] [rbp-CCh]
  int j; // [rsp+8h] [rbp-C8h]
  int k; // [rsp+Ch] [rbp-C4h]
  int v7[2]; // [rsp+18h] [rbp-B8h]
  int v8[2]; // [rsp+20h] [rbp-B0h]
  int v9[2]; // [rsp+28h] [rbp-A8h]
  char v10[64]; // [rsp+30h] [rbp-A0h] BYREF
  char s[72]; // [rsp+70h] [rbp-60h] BYREF
  unsigned __int64 v12; // [rsp+B8h] [rbp-18h]

  v12 = __readfsqword(0x28u);
  qmemcpy(v10, "h_bO}EcDOR+G)uh(jl,vL", 21);
  printf("[*] Hello! Welcome to our Program!\nEnter the password to contiune:  ");
  __isoc99_scanf("%s", s);
  v7[0] = 16;
  v7[1] = 24;
  v8[0] = strlen(s) >> 1;
  v8[1] = strlen(s);
  v9[0] = 0;
  v9[1] = strlen(s) >> 1;
  for ( i = 0; i <= 1; ++i )
  {
    for ( j = v9[i]; j < v8[i]; ++j )
      v10[j + 32] = v7[i] ^ s[j];
  }
  for ( k = 0; k < strlen(v10) - 1; ++k )
  {
    if ( v10[k + 32] != v10[k] )
    {
      puts("[-] Wrong Password");
      exit(0);
    }
  }
  puts("[+] Successful Login");
  return 0;
}

Now, we need to figure out how the program works. Let’s analyze the code step by step.

1
qmemcpy(v10, "h_bO}EcDOR+G)uh(jl,vL", 21);

The program copies the string h_bO}EcDOR+G)uh(jl,vL to the v10 variable.

1
2
3
4
5
6
v7[0] = 16;
v7[1] = 24;
v8[0] = strlen(s) >> 1;
v8[1] = strlen(s);
v9[0] = 0;
v9[1] = strlen(s) >> 1;

The program initializes some variables. Realize that the flag length is probably 21 because the length of the string h_bO}EcDOR+G)uh(jl,vL is 21. So here the str(len(s)) is 21.

1
2
3
4
5
for ( i = 0; i <= 1; ++i )
{
  for ( j = v9[i]; j < v8[i]; ++j )
    v10[j + 32] = v7[i] ^ s[j];
}

The program xors the password with the values in the v7 array and stores the result in the v10 array.

1
2
3
4
5
6
7
8
9
10
for ( k = 0; k < strlen(v10) - 1; ++k )
{
  if ( v10[k + 32] != v10[k] )
  {
    puts("[-] Wrong Password");
    exit(0);
  }
}
puts("[+] Successful Login");

Finally, the program checks if the xored password is equal to the string h_bO}EcDOR+G)uh(jl,vL. If it is equal, the program prints the flag.

Solution

Here we can figure out that the program XORs the password with the values in the v7 array. The values in the v7 array are 16 and 24. Also, for the first half of the password, the program xors it with 16 and for the second half, it xors it with strlens(s) >> 1. So, we can write a python script to find the password.

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import xor

encrypted_flag = "h_bO}EcDOR+G)uh(jl,vL"
first_part_key = 0x10
second_part_key = 0x18

second_part_len = (len(encrypted_flag) >> 1)  
first_part_len = len(encrypted_flag) - second_part_len - 1

first_part = encrypted_flag[:first_part_len]
second_part = encrypted_flag[second_part_len:]
 
flag = xor(first_part, first_part_key) + xor(second_part, second_part_key)

When we run the script, we get the password.

1
2
sarp@IdeaPad:~/Desktop$ python3 solve.py
b'xOr_mUsT_B3_1mp0rt4nT'

Alternative Solution

Also, we can perform the XOR operation on the fly by editing the binary source code as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>

int main(int argc, const char **argv, const char **envp) {
    int i; 
    int j; 
    int v7[2]; 
    int v8[2]; 
    int v9[2]; 
    char v10[64];
    char s[72];

    memcpy(v10, "h_bO}EcDOR+G)uh(jl,vL", 21);
    memcpy(s,   "h_bO}EcDOR+G)uh(jl,vL", 21);
    //printf("[*] Hello! Welcome to our Program!\nEnter the password to continue:  ");
    
 //scanf("%71s", s);

    v7[0] = 16;
    v7[1] = 24;
    v8[0] = strlen(s) >> 1;
    v8[1] = strlen(s);
    v9[0] = 0;
    v9[1] = strlen(s) >> 1;
    print("Flag: ")
    for (i = 0; i <= 1; ++i) {
        for (j = v9[i]; j < v8[i]; ++j) {
            v10[j + 32] = v7[i] ^   s[j];
            printf("%c", v10[j + 32]); 
        }
    }
    printf("\n");
   
}

When we compile and run the program, we get the flag.

1
2
sarp@IdeaPad:~/Desktop$ g++ -o xor xor.cpp && ./xor
Flag: xOr_mUsT_B3_1mp0rt4nT

Conclusion

Cryptoversing was a decent reverse engineering challenge. Though it doesn’t require advanced reverse engineering skills, understanding the XOR operation was crucial to solve the challenge. I hope you enjoyed the write-up. If you have any questions, feel free to ask me on Twitter.

This post is licensed under CC BY 4.0 by the author.