Tags: engineering reverse 

Rating:

# unknown

Let's take a look at the binary:

```
$ file unknown
unknown: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=53ec94bd1406ec6b9a28f5308a92e4d906444edb, stripped
$ ./unknown
Try again.
$ ./unknown gimme_flag
Still nope.
```

So this is a 64 bit elf, that appears to take input as an argument. Let's look at the code:

```
signed __int64 __fastcall main(int argc, char **argv, char **a3)
{
signed __int64 result; // rax@2
unsigned int i; // [sp+14h] [bp-Ch]@5
char *argument; // [sp+18h] [bp-8h]@5

if ( argc == 2 )
{
if ( strlen(argv[1]) == 56 )
{
argument = argv[1];
for ( i = 0; i < 0x38; ++i )
{
if ( (unsigned int)first_layer((__int64)argument, i) )
check = 1;
}
if ( check )
puts("Nope.");
else
printf("Congraz the flag is: %s\n", argument, argv);
result = 0LL;
}
else
{
puts("Still nope.");
result = 0xFFFFFFFELL;
}
}
else
{
puts("Try again.");
result = 0xFFFFFFFFLL;
}
return result;
}
```

So we can see here, it checks to see that the length of the first argument is 56 bytes. It then runs a for loop 56 (0x38) times in which it runs the function `first_layer` with the arguments being our input, and the iteration count. We see that it is ran in an if then statement, and if it is true `check` is set equal to one. We can see that if `check` is set equal to one, then we don't have the flag. So we need to make sure `first_layer` always outputs false. Let's take a look at `first_layer`:

```
__int64 __fastcall first_layer(__int64 argument, __int64 i)
{
char *v2; // r15@1
__int64 v3; // rdx@1
int v4; // ebx@1
signed __int64 v5; // rcx@1
__int64 v6; // rax@3
int v7; // eax@3
char v9; // [sp+0h] [bp-8h]@1

v2 = &v9 - 6004;
v3 = 0LL;
v4 = 0;
v5 = 0LL;
do
{
++v5;
v4 += 666;
}
while ( v5 < 0x2F );
LOBYTE(v3) = *(_BYTE *)(argument + i);
*((_QWORD *)v2 + 1) = v3;
v6 = sub_400A1C((__int64)(v2 + 8), 1u);
v7 = __ROL4__((v4 + 35) * (unsigned __int64)sub_401BDD((const char *)(v6 + 24), 16), 21);
return v7 != (unsigned int)*(_QWORD *)((char *)&desired_output + 4 * i);
}
```

We can see there is a good bit going on here. However at the end of the day, that return value is all that matters. Let's set a breakpoint for that compare instruction in gdb (0x401f20) and see what the values are:

```
gdb-peda$ b *0x401f20
Breakpoint 1 at 0x401f20
gdb-peda$ r 00000000000000000000000000000000000000000000000000000000
Starting program: /Hackery/tuctf/unkown/unknown 00000000000000000000000000000000000000000000000000000000

[----------------------------------registers-----------------------------------]
RAX: 0x63e13f5f
RBX: 0x7a69 ('iz')
RCX: 0x32449a7fdfab57a
RDX: 0x0
RSI: 0x0
RDI: 0x7ffff7b85860 --> 0x2000200020002
RBP: 0x7fffffffdf30 --> 0x401ce0 (push r15)
RSP: 0x7fffffffdf00 --> 0x7fffffffdf30 --> 0x401ce0 (push r15)
RIP: 0x401f20 --> 0x1b80a74c839
R8 : 0x0
R9 : 0x0
R10: 0x7ffff7b84f60 --> 0x100000000
R11: 0x7ffff7b85860 --> 0x2000200020002
R12: 0x0
R13: 0x7fffffffe010 --> 0x2
R14: 0x7fffffffdf00 --> 0x7fffffffdf30 --> 0x401ce0 (push r15)
R15: 0x7fffffffc78c --> 0x0
EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x401f0f: rol eax,0x15
0x401f12: movabs rcx,0x401dac
0x401f1c: mov rcx,QWORD PTR [rcx+rsi*4]
=> 0x401f20: cmp eax,ecx
0x401f22: je 0x401f2e
0x401f24: mov eax,0x1
0x401f29: mov rsp,r14
0x401f2c: pop rbp
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf00 --> 0x7fffffffdf30 --> 0x401ce0 (push r15)
0008| 0x7fffffffdf08 --> 0x401c82 (test eax,eax)
0016| 0x7fffffffdf10 --> 0x7fffffffe018 --> 0x7fffffffe345 ("/Hackery/tuctf/unkown/unknown")
0024| 0x7fffffffdf18 --> 0x200400600
0032| 0x7fffffffdf20 --> 0xffffe010
0040| 0x7fffffffdf28 --> 0x7fffffffe363 ('0' <repeats 56 times>)
0048| 0x7fffffffdf30 --> 0x401ce0 (push r15)
0056| 0x7fffffffdf38 --> 0x7ffff7a303f1 (<__libc_start_main+241>: mov edi,eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x0000000000401f20 in ?? ()
gdb-peda$ p $eax
$1 = 0x63e13f5f
gdb-peda$ p $ecx
$2 = 0xfdfab57a
gdb-peda$
```
So the `eax` register hold the result of our input, and the `ecx` register holds the value that it is being compared against. We can tell this by the fact that `ecx` holds hex characters that are in `desired_output` when we check the data in it. In addition to that, when we go the next check, we see that the value in `ecx` changes while `eax` does not (all 56 bytes of our input were the same). This also tells us that the position of a specific character in our input doesn't change it's value.

Remember we need the function to output false, and the check is if the two values are not equal, so we need the two values to be equal.

So instead of going through and reversing this challenge, we can just skip that, look at at if the values it is comparing it against, and the result of what the output of characters we give it and use that to figure out what characters we need to in order to pass the checks.

Looking at the format of previous flags, we would probably have the eight characters `TUCTF{_}`, undercase characters and digits in the flag. It holds true with this. This is the output of all of those characters:

```
alphabet:
!: 0xba165ea7
_: 0xff20bdef
{: 0x59e2eb0d
}: 0xef1b84cd
q: 0x2c6485d5
w: 0x5ed756d7
e: 0xb623c6c1
r: 0xcd78354e
t: 0xc863df45
y: 0x12a92a61
u: 0xb59d1071
i: 0x67258d77
o: 0x408a2c4b
p: 0xf2184419
l: 0x9239bdf3
c: 0xf62c7f9b
m: 0xd6338e84
f: 0x388d9870
0: 0x63e13f5f
1: 0x3ca8bfdc
2: 0xdd0e6ec0
3: 0x5cfff023
4: 0xceecc5ba
5: 0xcc1be317
6: 0x2ff35144
7: 0xc51f928e
8: 0xb6705910
9: 0x26552da4
T: 0xfdfab57a
U: 0x032449a7
C: 0x5f383821
F: 0x25435e02
```

here is the list of the 56 checks it does:

```
checks:
0xfdfab57a TUCFT{
0x032449a7
0x5f383821
0xfdfab57a
0x25435e02
0x59e2eb0d

0x5ed756d7 w3lc0m3_
0x5cfff023
0x9239bdf3
0xf62c7f9b
0x63e13f5f
0xd6338e84
0x5cfff023
0xff20bdef

0xc51f928e 70_
0x63e13f5f
0xff20bdef

0xc51f928e 7uc7f_
0xb59d1071
0xf62c7f9b
0xc51f928e
0x388d9870
0xff20bdef

0xceecc5ba 4nd_
0xa952136b
0x96710841
0xff20bdef

0xc51f928e 7th4nk_ check 7
0xf536dffd
0xceecc5ba
0xa952136b
0xc5d7dac4
0xff20bdef

0x12a92a61 y0u_
0x63e13f5f
0xb59d1071
0xff20bdef

0x388d9870 f0r_
0x63e13f5f
0xcd78354e
0xff20bdef

0xf2184419 p4r71c1p4y1n6!}
0xceecc5ba
0xcd78354e
0xc51f928e
0x3ca8bfdc
0xf62c7f9b
0x3ca8bfdc
0xf2184419
0xceecc5ba
0xc51f928e
0x3ca8bfdc
0xa952136b
0x2ff35144
0xba165ea7
0xef1b84cd
```

and putting all together (and skipping a lot of the reversing work):

```
$ ./unknown TUCTF{w3lc0m3_70_7uc7f_4nd_7h4nk_y0u_f0r_p4r71c1p471n6\!}
Congraz the flag is: TUCTF{w3lc0m3_70_7uc7f_4nd_7h4nk_y0u_f0r_p4r71c1p471n6!}
```

We had to add a backslack in order to escape the `!`. Just like that, we captured the flag!

Original writeup (https://github.com/guyinatuxedo/ctf/tree/master/tuctf/unknown).