GhostInTheMachine
Challenge
- CTF: Hack The Boo 2023 - Practice Official Writeup
- Name: GhostInTheMachine
- Category: Reversing
- Difficulty: Very Easy
- Points: 325
- Description: I bought this flag generator from a strange shop, but I think it’s haunted by an angry spirit - can you exorcise it?
- Objective: Binary Patching
Files
Download: rev_ghostinthemachine.zip
Writeup
The challenge contains a single file of machine
. Analyzing using the file
utility, we see its a Linux ELF binary.
1
2
$ file *
machine: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e5eff6eb6722f66059e9b6eca11cc28c292eef71, for GNU/Linux 3.2.0, not stripped
Running the binary, we are presented with 28 bytes of binary data.
1
2
3
./machine | xxd
00000000: ede3 c80d a161 429a 2a1c 4859 06f8 e523 .....aB.*.HY...#
00000010: 5e61 5607 113f 7dbc f5fd cd0a ^aV..?}.....
We can use dogbolt or ghidra to reverse the binary back to C as shown below:
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
int64_t ghost(char* arg1)
{
char* var_20 = arg1;
int32_t fd = open("/dev/urandom", 0);
while (*var_20 != 0)
{
char buf;
do
{
read(fd, &buf, 1);
} while (*var_20 == buf);
char* rax_4 = var_20;
var_20 = &rax_4[1];
*rax_4 = (buf ^ *rax_4);
}
return close(fd);
}
void* getflag(char* arg1)
{
arg1[0xf] = 0x74;
arg1[1] = 0x54;
arg1[0x18] = 0x6e;
arg1[2] = 0x42;
arg1[4] = 0x65;
arg1[0x11] = 0x33;
arg1[0x15] = 0x63;
arg1[0x19] = 0x33;
arg1[6] = 0x30;
arg1[0x12] = 0x5f;
arg1[7] = 0x72;
arg1[9] = 0x31;
arg1[8] = 0x63;
arg1[5] = 0x78;
arg1[0x14] = 0x34;
arg1[0xd] = 0x67;
arg1[0xa] = 0x73;
arg1[0x1a] = 0x7d;
*arg1 = 0x48;
arg1[3] = 0x7b;
arg1[0x13] = 0x6d;
arg1[0xc] = 0x6e;
arg1[0xb] = 0x31;
arg1[0x17] = 0x31;
arg1[0x10] = 0x68;
arg1[0xe] = 0x5f;
arg1[0x16] = 0x68;
return &arg1[0x16];
}
int32_t main(int32_t argc, char** argv, char** envp)
{
int64_t var_88;
__builtin_memset(&var_88, 0, 0x80);
getflag(&var_88);
ghost(&var_88);
puts(&var_88);
return 0;
}
Analyzing the source code, it appears the flag is fetched and then randomized from /dev/urandom
.
We can run the binary in debug mode and break right after the getflag()
function is returned. Looking at the memory registers, the actual flag should be still in memory from the previous getflag()
function execution.
1
2
3
4
5
6
7
8
9
gdb ./machine
b ghost
r
[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffd7a0 ("HTB{ex0rc1s1ng_th3_m4ch1n3}")
...[snip]...
RDI: 0x7fffffffd7a0 ("HTB{ex0rc1s1ng_th3_m4ch1n3}")
...[snip]...
Breakpoint 1, 0x0000555555555169 in ghost ()
We also could perform binary patching to change the binary itself, but i’ll leave that for the reader.
Flag: HTB{ex0rc1s1ng_th3_m4ch1n3}
This post is licensed under CC BY 4.0 by the author.