[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
→ wget https://github.com/guyinatuxedo/nightmare/raw/master/modules/03-beginner_re/helithumper_re/rev
--2021-02-22 17:19:05-- https://github.com/guyinatuxedo/nightmare/raw/master/modules/03-beginner_re/helithumper_re/rev
Resolving github.com (github.com)... 140.82.121.4
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/03-beginner_re/helithumper_re/rev [following]
--2021-02-22 17:19:05-- https://raw.githubusercontent.com/guyinatuxedo/nightmare/master/modules/03-beginner_re/helithumper_re/rev
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.108.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 16704 (16K) [application/octet-stream]
Saving to: ‘rev’
rev 100%[===============================================================================>] 16.31K --.-KB/s in 0s
2021-02-22 17:19:05 (37.3 MB/s) - ‘rev’ saved [16704/16704]
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
→ file rev
rev: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e4dbcb1281821db359d566c68fea7380aeb27378, for GNU/Linux 3.2.0, not stripped
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
→ chmod +x rev
Run the binary
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
→ ./rev
Welcome to the Salty Spitoon™, How tough are ya?
very though
Yeah right. Back to Weenie Hut Jr™ with ya
here the binary prints some text, then lets us input our text (here its 'very though') and then prints some text again. It is safe to assume that we will need to type the correct passphrase to get the correct output. So let's inspect the binary file from ghidra:
now from here we want to check out the main function of our binary file, so go into the symbol tree tab, into functions, into main, and we get the following code:
bool main(void)
{
int iVar1;
void *pvVar2;
pvVar2 = calloc(0x32,1);
puts(&DAT_00102008);
__isoc99_scanf(&DAT_0010203b,pvVar2);
iVar1 = validate(pvVar2);
if (iVar1 == 0) {
puts(&DAT_00102050);
}
else {
puts("Right this way...");
}
return iVar1 == 0;
}
now here we see something, first of all it does a scanf (to prompt for our input) and then moves our text into pvVar2, then, it calls a function called 'validate' and the result of that function gets put into iVar1 which determines if we get a correct answer or not. So let's inspect the 2 possibilities of the if statement:
if (iVar1 == 0) {
puts(&DAT_00102050);
}
else {
puts("Right this way...");
}
From ghidra we see that this '&DAT_00102050' is the string of characters we saw earlier:
therefore, we do not want iVar1 to be equal to 0, we want iVar1 to be equal to 1
So the hint here is, what is being validated ? How is our input being validated ? we inspect the validate function which HAS TO return 1, if we want our iVar1 to be equal to 1:
Which gives us the following code:
undefined8 validate(char *param_1)
{
size_t sVar1;
undefined8 uVar2;
long in_FS_OFFSET;
int local_50;
int local_48 [4];
undefined4 local_38;
undefined4 local_34;
undefined4 local_30;
undefined4 local_2c;
undefined4 local_28;
undefined4 local_24;
undefined4 local_20;
undefined4 local_1c;
undefined4 local_18;
undefined4 local_14;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_48[0] = 0x66;
local_48[1] = 0x6c;
local_48[2] = 0x61;
local_48[3] = 0x67;
local_38 = 0x7b;
local_34 = 0x48;
local_30 = 0x75;
local_2c = 0x43;
local_28 = 0x66;
local_24 = 0x5f;
local_20 = 0x6c;
local_1c = 0x41;
local_18 = 0x62;
local_14 = 0x7d;
sVar1 = strlen(param_1);
local_50 = 0;
do {
if ((int)sVar1 <= local_50) {
uVar2 = 1;
LAB_001012b7:
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return uVar2;
}
if ((int)param_1[local_50] != local_48[local_50]) {
uVar2 = 0;
goto LAB_001012b7;
}
local_50 = local_50 + 1;
} while( true );
}
So first of all our input text gets passed into the validate function via the param_1 parameter. It enters a do{} while(); loop, with each iteration of that while loop, there is a value that gets incremented, the only return statements of that function are either return uVar2 or either not making the function return anything at all, instead going to the __stack_chk_fail() function. therefore the important value here is uVar2
int local_48 [4];
[...]
local_48[0] = 0x66;
local_48[1] = 0x6c;
local_48[2] = 0x61;
local_48[3] = 0x67;
[...]
if ((int)param_1[local_50] != local_48[local_50]) {
uVar2 = 0;
goto LAB_001012b7;
}
local_50 = local_50 + 1;
Here we see that our each character of our input (param1) gets checked against the corresponding character of the local_48 string. Therefore we need to make sure our input matches the values inside of local_48's 0,1,2,3 characters. so we know we have to look at the following addresses:
0x66
0x6c
0x61
0x67
From Ghidra, we see the following assembly code:
00101205 c7 45 c0 MOV dword ptr [RBP + local_48],0x66
66 00 00 00
0010120c c7 45 c4 MOV dword ptr [RBP + local_44],0x6c
6c 00 00 00
00101213 c7 45 c8 MOV dword ptr [RBP + local_40],0x61
61 00 00 00
0010121a c7 45 cc MOV dword ptr [RBP + local_3c],0x67
67 00 00 00
00101221 c7 45 d0 MOV dword ptr [RBP + local_38],0x7b
7b 00 00 00
00101228 c7 45 d4 MOV dword ptr [RBP + local_34],0x48
48 00 00 00
0010122f c7 45 d8 MOV dword ptr [RBP + local_30],0x75
75 00 00 00
00101236 c7 45 dc MOV dword ptr [RBP + local_2c],0x43
43 00 00 00
0010123d c7 45 e0 MOV dword ptr [RBP + local_28],0x66
66 00 00 00
00101244 c7 45 e4 MOV dword ptr [RBP + local_24],0x5f
5f 00 00 00
0010124b c7 45 e8 MOV dword ptr [RBP + local_20],0x6c
6c 00 00 00
00101252 c7 45 ec MOV dword ptr [RBP + local_1c],0x41
41 00 00 00
00101259 c7 45 f0 MOV dword ptr [RBP + local_18],0x62
62 00 00 00
00101260 c7 45 f4 MOV dword ptr [RBP + local_14],0x7d
7d 00 00 00
Now from here we can get the list of specific bytes our input needs to be:
0x66
0x6c
0x61
0x67
0x7b
0x48
0x75
0x43
0x66
0x5f
0x6c
0x41
0x62
0x7d
Now let's move over to python:
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
→ ls -lash
total 788K
4.0K drwxr-xr-x 2 nothing nothing 4.0K Feb 22 17:19 .
4.0K drwxr-xr-x 4 nothing nothing 4.0K Feb 22 17:23 ..
20K -rwxr-xr-x 1 nothing nothing 17K Feb 22 17:19 rev
760K -rwxr-xr-x 1 nothing nothing 759K Feb 22 17:12 strings
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
→ file rev
rev: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e4dbcb1281821db359d566c68fea7380aeb27378, for GNU/Linux 3.2.0, not stripped
[ 192.168.100.126/24 ] [ /dev/pts/1 ] [~/binexp/1]
→ python3
Python 3.9.1+ (default, Feb 5 2021, 13:46:56)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = [0x66, 0x6c, 0x61, 0x67, 0x7b, 0x48, 0x75, 0x43, 0x66, 0x5f, 0x6c, 0x41, 0x62, 0x7d]
>>> input = ""
>>> for i in x:
... input += chr(i)
...
>>> input
'flag{HuCf_lAb}'
And here we see that the first 4 addresses were 'flag' the next 10 were '{HuCf_lAb}', this obviously was fairly easy
text
text