Tags: engineering reverse 

Rating:

This is one of the reversing problems that I solved during SarCTF a couple months back in February of 2020. I decided to write this up since I believe it to a good beginner problem for people looking to get into reverse engineering.

# Problem Description
> While the children were playing toys, Sherlock was solving crosswords in large volumes.

# Reversing the Binary
Using `file` we can see we're given an unstripped 64-bit ELF file: `ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=2e45dc319e3736db1643abb283b0ed9a18681261, not stripped`. Running the binary, we're presented with the following output:

```
➜ sarctf ./crossw0rd
Welcome. You're in function check. Please Enter a password to continue. 1 attempt remaining:
password
Wrong password! Your attempt is over.
```

Since we need to reverse engineer the executable to get the password, let's go ahead and throw this in a disassembler/decompiler of your choice, I'll be using Ghidra.

After analyzing the binary, we can see several exported symbols. Let's start with `main()`:

*Main*

```c
undefined8 main(void)
{
check();
return 0;
}
```

Only one call to follow so let's look at `check()`. We can clearly see that this function is responsible for prompting the user, taking input, and finally verifying said input. `check()` also calls another function named `e()`, and checks against it's return value. If it's **FALSE**, we lose.

*check*
```c
void check(void)

{
char cVar1;
long in_FS_OFFSET;
char local_28 [24];
long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28);
puts(
"Welcome. You\'re in function check. Please Enter a password to continue. 1 attemptremaining:"
);
scanf("%s",local_28);
cVar1 = e(local_28);
if (cVar1 == '\0') {
puts("Wrong password! Your attempt is over.");
}
else {
puts("You cracked the system!");
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
```

*E*

```c
ulong e(char *param_1)

{
byte bVar1;
char cVar2;

if ((((param_1[7] == '5') && (param_1[0x11] == 'g')) && (param_1[2] == 'A')) &&
(cVar2 = b(param_1), cVar2 != '\0')) {
bVar1 = 1;
}
else {
bVar1 = 0;
}
return (ulong)bVar1;
}
```

`e()` checks 4 things: 8th character must equal **5**, 18th character must equal **g**, string passed into this function must not be **NULL**, and `b()` must return **TRUE**. Let's take a look at what `b()` expects.

This time, `b()` checks 5 things: 16th character must equal **i**, 10th character must equal **r**, 2nd character must equal **L**, `d()` must return **TRUE**, and string passed in cannot be **NULL**.

*B*
```c
ulong b(char *param_1)

{
byte bVar1;
char cVar2;

if ((((param_1[0xf] == 'i') && (param_1[9] == 'r')) && (param_1[1] == 'L')) &&
(cVar2 = d(param_1), cVar2 != '\0')) {
bVar1 = 1;
}
else {
bVar1 = 0;
}
return (ulong)bVar1;
}
```

This continues on for 4 more functions, the steps moving forward are exactly the same. There are 8 functions in total worth looking at: `main()`, `check()`, and `a()-f()`.

*D*
```c
ulong d(char *param_1)

{
byte bVar1;
char cVar2;

if ((((param_1[10] == '3') && (param_1[0x12] == '}')) && (param_1[6] == 'a')) &&
(cVar2 = f(param_1), cVar2 != '\0')) {
bVar1 = 1;
}
else {
bVar1 = 0;
}
return (ulong)bVar1;
}
```

*F*
```c
ulong f(char *param_1)

{
byte bVar1;
char cVar2;

if ((((*param_1 == 'F') && (param_1[0xe] == '5')) && (param_1[0x10] == 'n')) &&
(cVar2 = c(param_1), cVar2 != '\0')) {
bVar1 = 1;
}
else {
bVar1 = 0;
}
return (ulong)bVar1;
}
```

*C*
```c
ulong c(char *param_1)

{
byte bVar1;
char cVar2;

if ((((param_1[3] == 'G') && (param_1[0xb] == 'v')) && (param_1[5] == '3')) &&
(cVar2 = a(param_1), cVar2 != '\0')) {
bVar1 = 1;
}
else {
bVar1 = 0;
}
return (ulong)bVar1;
}
```

*A*
```c
undefined8 a(char *param_1)

{
undefined8 uVar1;

if ((((param_1[4] == '{') && (param_1[0xc] == '3')) && (param_1[8] == 'y')) &&
(param_1[0xd] == 'r')) {
uVar1 = 1;
}
else {
uVar1 = 0;
}
return uVar1;
}
```

# Putting It Together
After reversing through functions `a()-f()`, you should be able to string together the expected password. We run the binary one more time, giving it the correct password, and we get the flag: `FLAG{3a5yr3v3r5ing}`

Author: [medarkus](https://twitter.com/v0idptr_)