Ghost Wrangler
4 minutes to read
We are given a binary file called ghost
:
$ file ghost
ghost: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=810d0f9271ec04d80a2eee6ff2afd9367da3c3dd, for GNU/Linux 3.2.0, not stripped
Reverse engineering
If we open the binary in Ghidra, we will see this decompiled main
function in C:
int main() {
undefined8 flag;
flag = get_flag();
printf("%s\r|\x1b[4m%*.c\x1b[24m| I\'ve managed to trap the flag ghost in this box, but it\'s turn ed invisible!\nCan you figure out how to reveal them?\n", flag, 0x28, 0x5f);
return 0;
}
It calls a function named get_flag
and outputs a string containing the flag
variable. This is get_flag
:
void* get_flag() {
void* __s;
uint i;
__s = malloc(0x29);
memset(__s, 0, 0x29);
for (i = 0; i < 0x28; i = i + 1) {
*(byte *) ((long) __s + (long) (int) i) = _[(int) i] ^ 0x13;
}
return __s;
}
XOR cipher
If we look closely, it is doing XOR operations using 0x13
as key and a global variable called _
as encrypted data. In Ghidra, we have these bytes assigned to _
:
_ XREF[3]: Entry Point(*),
get_flag:0010118f(*),
get_flag:00101196(R)
00102020 5b 47 51 undefine
68 7b 27
66 7d 67
00102020 5b undefined15Bh [0] XREF[3]: Entry Point(*),
get_flag:0010118f(*),
get_flag:00101196(R)
00102021 47 undefined147h [1]
00102022 51 undefined151h [2]
00102023 68 undefined168h [3]
00102024 7b undefined17Bh [4]
00102025 27 undefined127h [5]
00102026 66 undefined166h [6]
00102027 7d undefined17Dh [7]
00102028 67 undefined167h [8]
00102029 20 undefined120h [9]
0010202a 77 undefined177h [10]
0010202b 4c undefined14Ch [11]
0010202c 71 undefined171h [12]
0010202d 6a undefined16Ah [13]
0010202e 4c undefined14Ch [14]
0010202f 67 undefined167h [15]
00102030 7b undefined17Bh [16]
00102031 20 undefined120h [17]
00102032 4c undefined14Ch [18]
00102033 74 undefined174h [19]
00102034 7b undefined17Bh [20]
00102035 23 undefined123h [21]
00102036 60 undefined160h [22]
00102037 67 undefined167h [23]
00102038 26 undefined126h [24]
00102039 4c undefined14Ch [25]
0010203a 23 undefined123h [26]
0010203b 75 undefined175h [27]
0010203c 4c undefined14Ch [28]
0010203d 70 undefined170h [29]
0010203e 67 undefined167h [30]
0010203f 75 undefined175h [31]
00102040 26 undefined126h [32]
00102041 4c undefined14Ch [33]
00102042 63 undefined163h [34]
00102043 27 undefined127h [35]
00102044 26 undefined126h [36]
00102045 67 undefined167h [37]
00102046 32 undefined132h [38]
00102047 6e undefined16Eh [39]
Flag
At this point, I took those bytes and performed the same XOR cipher as in get_flag
to read the flag:
$ python3 -q
>>> data = bytes([
... 0x5b,
... 0x47,
... 0x51,
... 0x68,
... 0x7b,
... 0x27,
... 0x66,
... 0x7d,
... 0x67,
... 0x20,
... 0x77,
... 0x4c,
... 0x71,
... 0x6a,
... 0x4c,
... 0x67,
... 0x7b,
... 0x20,
... 0x4c,
... 0x74,
... 0x7b,
... 0x23,
... 0x60,
... 0x67,
... 0x26,
... 0x4c,
... 0x23,
... 0x75,
... 0x4c,
... 0x70,
... 0x67,
... 0x75,
... 0x26,
... 0x4c,
... 0x63,
... 0x27,
... 0x26,
... 0x67,
... 0x32,
... 0x6e,
... ])
>>> from pwn import xor
>>> xor(data, b'\x13')
b'HTB{h4unt3d_by_th3_gh0st5_0f_ctf5_p45t!}'
Another way of solving it was simply processing the output of the binary with xxd
, head
or tr
:
$ ./ghost | xxd
00000000: 4854 427b 6834 756e 7433 645f 6279 5f74 HTB{h4unt3d_by_t
00000010: 6833 5f67 6830 7374 355f 3066 5f63 7466 h3_gh0st5_0f_ctf
00000020: 355f 7034 3574 217d 0d7c 1b5b 346d 2020 5_p45t!}.|.[4m
00000030: 2020 2020 2020 2020 2020 2020 2020 2020
00000040: 2020 2020 2020 2020 2020 2020 2020 2020
00000050: 2020 2020 205f 1b5b 3234 6d7c 2049 2776 _.[24m| I'v
00000060: 6520 6d61 6e61 6765 6420 746f 2074 7261 e managed to tra
00000070: 7020 7468 6520 666c 6167 2067 686f 7374 p the flag ghost
00000080: 2069 6e20 7468 6973 2062 6f78 2c20 6275 in this box, bu
00000090: 7420 6974 2773 2074 7572 6e65 6420 696e t it's turned in
000000a0: 7669 7369 626c 6521 0a43 616e 2079 6f75 visible!.Can you
000000b0: 2066 6967 7572 6520 6f75 7420 686f 7720 figure out how
000000c0: 746f 2072 6576 6561 6c20 7468 656d 3f0a to reveal them?.
$ ./ghost | head -c 40
HTB{h4unt3d_by_th3_gh0st5_0f_ctf5_p45t!}
$ ./ghost | tr -d '\r'
HTB{h4unt3d_by_th3_gh0st5_0f_ctf5_p45t!}| _| I've managed to trap the flag ghost in this box, but it's turned invisible!
Can you figure out how to reveal them?
The issue was a carriage return character (\r
, \x0d
), which makes the subsequent characters to overwrite the flag.