⏪
CTFs
TwitterGithub
  • 👋Introduction
  • 📚Write Up
    • 2024
      • 📖1337UP LIVE CTF
        • Reverse Engineering
        • Mobile
        • Forensic
        • Misc
      • 📖HKCERT CTF Quals
        • Reverse Engineering
        • Binary Exploitation
      • 📖Flare-On 11
        • Challenge #1 - frog
      • 📖Intechfest
        • Reverse Engineering
        • Forensic
        • Cryptography
        • Mobile
      • 📖Cyber Breaker Competition (1v1)
        • Reverse Engineering
        • Web Exploitation
        • Cryptography
        • Binary Exploitation
      • 📖Cyber Breaker Competition Quals
        • Reverse Engineering
        • Web Exploitation
        • Cryptography
      • 📖BlackHat MEA Quals
        • Reverse Engineering
        • Forensic
      • 📖TFC CTF
        • Reverse Engineering
        • Forensic
        • Misc
      • 📖DeadSec CTF
        • Reverse Engineering
        • Web Exploitation
      • 📖Aptos - Code Collision CTF
        • Reverse Engineering
        • Misc
      • 📖DownUnder CTF
        • Reverse Engineering
      • 📖JustCTF
        • Reverse Engineering
        • Forensic
        • Misc
      • 📖Akasec CTF
        • Reverse Engineering
        • Forensic
      • 📖Codegate CTF Preliminary
        • Reverse Engineering
      • 📖NahamCon CTF
        • Cryptography
        • Reverse Engineering
        • Malware
        • Misc
        • Mobile
        • Scripting
        • Web Exploitation
        • Forensic
      • 📖SAS CTF Quals
        • Reverse Engineering
      • 📖SwampCTF
        • Reverse Engineering
        • Misc
        • Cryptography
      • 📖UNbreakable International
        • Reverse Engineering
        • Network
        • Cryptography
      • 📖ACSC
        • Reverse Engineering
        • Hardware
        • Web Exploitation
      • 📖0xL4ugh
        • Mobile
    • 2023
      • 📖BlackHat MEA Final
        • Reverse Engineering
        • Web Exploitation
      • 📖Flare-On 10
        • Challenge #1 - X
        • Challenge #2 - ItsOnFire
        • Challenge #3 - mypassion
        • Challenge #4 - aimbot
        • Challenge #5 - where_am_i
        • Challenge #6 - FlareSay
        • Challenge #7 - flake
        • Challenge #8 - AmongRust
        • Challenge #9 - mbransom
        • Challenge #10 - kupo
        • Challenge #11 - over_the_rainbow
        • Challenge #12 - HVM
        • Challenge #13 - y0da
      • 📖LakeCTF Quals
        • Reverse Engineering
        • Cryptography
      • 📖TSG CTF
        • Reverse Engineering
        • Cryptography
      • 📖ISITDTU Quals
        • Web Exploitation
        • Misc
        • Reverse Engineering
      • 📖BlackHat MEA Quals
        • Reverse Engineering
      • 📖ASCIS Final
        • Reverse Engineering
        • Web Exploitation
        • Cryptography
      • 📖ASCIS Quals
        • Reverse Engineering
        • Forensic
        • Cryptography
      • 📖IFest
        • Reverse Engineering
        • Cryptography
        • Misc
      • 📖Cyber Jawara International
        • Reverse Engineering
        • Forensic
        • Cryptography
        • Web Exploitation
      • 📖Intechfest
        • Reverse Engineering
        • Forensic
        • Cryptography
        • Mobile
      • 📖CSAW Quals
        • Reverse Engineering
      • 📖SECCON Quals
        • Reverse Engineering
      • 📖CTFZone Quals
        • Reverse Engineering
      • 📖Securinets Quals
        • Reverse Engineering
      • 📖Compfest Final (Attack Defense)
        • Web Exploitation
        • Cryptography
      • 📖Compfest Quals
        • Reverse Engineering
        • Cryptography
        • Forensic
        • Misc
      • 📖Tenable
        • Reverse Engineering
        • Cryptography
        • Steganography
      • 📖ASCWG Quals
        • Reverse Engineering
        • Cryptography
      • 📖Gemastik Quals
        • Reverse Engineering
      • 📖BSides Indore
        • Reverse Engineering
        • Cryptography
      • 📖NahamCon CTF
        • Cryptography
      • 📖HSCTF
        • Reverse Engineering
        • Cryptography
        • Web Exploitation
        • Misc
      • 📖ACSC
        • Reverse Engineering
      • 📖HackTM Quals
        • Reverse Engineering
    • 2022
      • 📖Intechfest
        • Reverse Engineering
        • Mobile
        • Cryptography
      • 📖NCW Final
        • Reverse Engineering
      • 📖NCW Quals
        • Reverse Engineering
        • Misc
        • Cryptography
      • 📖Compfest Final
        • Reverse Engineering
        • Forensic
      • 📖Compfest Quals
        • Reverse Engineering
        • Cryptography
      • 📖IFest
        • Reverse Engineering
        • Cryptography
        • Forensic
    • 2021
      • 📖Cyber Jawara Final
        • Reverse Engineering
      • 📖Cyber Jawara Quals
        • Reverse Engineering
        • Cryptography
      • 📖DarkCon CTF
        • Reverse Engineering
      • 📖Wreck IT Quals
        • Mobile
      • 📖MDT4.0 Final
        • Reverse Engineering
        • Cryptography
        • Forensic
      • 📖MDT4.0 Quals
        • Reverse Engineering
        • Cryptography
      • 📖IFest
        • Reverse Engineering
        • Cryptography
      • 📖Compfest Final
        • Reverse Engineering
      • 📖Compfest Quals
        • Reverse Engineering
        • Cryptography
    • 2020
      • 📖Deep CTF
        • Reverse Engineering
  • 🚩Lifetime CTF
    • 📖Hack The Box
      • Reverse Engineering
        • TBU
Powered by GitBook
On this page
  • WARMUP (244 pts)
  • Description
  • Solution
  • OXYGEN (481 pts)
  • Description
  • Solution
  1. Write Up
  2. 2023
  3. ASCIS Quals

Reverse Engineering

PreviousASCIS QualsNextForensic

Last updated 1 year ago

Challenge
Link

WARMUP (244 pts)

OXYGEN (481 pts)

WARMUP (244 pts)

Description

-

Solution

Given PE file, open it using IDA.

We can see on main function most of the function called looks like "stripped". If we take a look on one of those called functions (sub_140004d60) we can see that it looks like statically compiled then stripped.

To recover the function name i did debugging then analyze the input and output. After knowing what the function do i rename the function name to make it easier to understand.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  void *v4; // rax
  __int64 v6; // rax
  __int64 v7; // rdx
  __int64 v8; // rdx
  void *v9; // rax
  __int64 value_array; // rax
  _QWORD *v11; // rax
  _BYTE *v12; // rax
  void *v13; // rax
  __int64 v14; // rax
  _QWORD *v15; // rax
  _QWORD *v16; // rax
  _BYTE *v17; // rax
  void *v18; // rax
  __int64 v19; // rax
  __int64 v20; // rax
  void *v21; // rax
  __int64 v22; // rax
  _QWORD *v23; // rax
  _QWORD *v24; // rax
  _BYTE *v25; // rax
  void *v26; // rax
  __int64 v27; // rax
  _QWORD *v28; // rax
  _QWORD *v29; // rax
  _BYTE *v30; // rax
  void *v31; // rax
  char v32; // [rsp+20h] [rbp-2E8h]
  char v33; // [rsp+25h] [rbp-2E3h]
  int j; // [rsp+28h] [rbp-2E0h]
  int k; // [rsp+2Ch] [rbp-2DCh]
  int m; // [rsp+30h] [rbp-2D8h]
  int i; // [rsp+34h] [rbp-2D4h]
  bool v38; // [rsp+38h] [rbp-2D0h]
  __int64 v39; // [rsp+40h] [rbp-2C8h]
  char v40; // [rsp+54h] [rbp-2B4h]
  char v41; // [rsp+60h] [rbp-2A8h]
  char v42; // [rsp+68h] [rbp-2A0h]
  __int64 v43; // [rsp+78h] [rbp-290h]
  char *Str1; // [rsp+88h] [rbp-280h]
  char *v45; // [rsp+98h] [rbp-270h]
  __int64 v46; // [rsp+A0h] [rbp-268h]
  __int64 v47; // [rsp+B0h] [rbp-258h]
  __int64 v48; // [rsp+E0h] [rbp-228h]
  __int64 v49; // [rsp+F8h] [rbp-210h]
  __int64 key; // [rsp+108h] [rbp-200h]
  __int64 ciphertext; // [rsp+110h] [rbp-1F8h]
  char *plaintext_; // [rsp+120h] [rbp-1E8h]
  char v53[32]; // [rsp+158h] [rbp-1B0h] BYREF
  char v54[32]; // [rsp+178h] [rbp-190h] BYREF
  char v55[32]; // [rsp+198h] [rbp-170h] BYREF
  char v56[32]; // [rsp+1B8h] [rbp-150h] BYREF
  char plaintext[32]; // [rsp+1D8h] [rbp-130h] BYREF
  char v58[32]; // [rsp+1F8h] [rbp-110h] BYREF
  char v59[32]; // [rsp+218h] [rbp-F0h] BYREF
  char v60[24]; // [rsp+238h] [rbp-D0h] BYREF
  char v61[32]; // [rsp+250h] [rbp-B8h] BYREF
  char v62[32]; // [rsp+270h] [rbp-98h] BYREF
  char v63[32]; // [rsp+290h] [rbp-78h] BYREF
  char v64[32]; // [rsp+2B0h] [rbp-58h] BYREF
  char v65[32]; // [rsp+2D0h] [rbp-38h] BYREF

  sub_7FF661CD4150(v61);
  printf_(&Format, "Enter flag: ");
  sub_7FF661CD50F0(&qword_7FF661CE79B0, v61);
  Str1 = (char *)substring_(v61, v54, 0i64, 6i64);
  v32 = 1;
  v38 = 1;
  if ( !(unsigned __int8)strcmp_(Str1, "ASCIS{") )
  {
    v3 = unknown_libname_55(v61);
    v45 = (char *)substring_(v61, v53, v3 - 1, 1i64);
    v32 = 3;
    if ( !(unsigned __int8)strcmp_(v45, "}") )
      v38 = 0;
  }
  if ( (v32 & 2) != 0 )
  {
    v32 &= ~2u;
    sub_7FF661CD3F30(v53);
  }
  if ( (v32 & 1) != 0 )
    sub_7FF661CD3F30(v54);
  if ( v38 )
  {
    v4 = (void *)printf_(&Format, "Incorrect!");
    _CallMemberFunction0(v4, sub_7FF661CD5180);
    sub_7FF661CD3F30(v61);
    return 0;
  }
  else
  {
    sub_7FF661CD3B30(v60, 24i64);
    sub_7FF661CD3C00(v60);
    v43 = 0i64;
    v6 = unknown_libname_55(v61);
    v46 = substring_(v61, v55, 6i64, v6 - 7);
    sub_7FF661CD3F60(v61, v46);
    sub_7FF661CD3F30(v55);
    while ( 1 )
    {
      LOBYTE(v7) = '-';
      v39 = sub_7FF661CD3D70(v61, v7, v43);
      if ( v39 == -1 )
        break;
      LOBYTE(v8) = '-';
      v43 = sub_7FF661CD3DB0(v61, v8, v39);
      v47 = substring_(v61, v56, v39, v43 - v39);
      sub_7FF661CD3BB0((__int64)v60, v47);
      sub_7FF661CD3F30(v56);
    }
    if ( sub_7FF661CD3B80(v60) == 6 )           // split by - and check length
    {
      sub_7FF661CD4040(v62, &unk_7FF661CE2B15);
      for ( i = 0; ; ++i )
      {
        value_array = get_value_at_index(v60, 5i64);
        if ( i >= (unsigned __int64)unknown_libname_55(value_array) )
          break;
        v11 = (_QWORD *)get_value_at_index(v60, 5i64);
        v12 = (_BYTE *)get_byte(v11, i);
        std::string::operator+=(v62, *v12 ^ 0x69u);
      }
      if ( !(unsigned __int8)strcmp_(v62, byte_7FF661CE2B44) )
      {
        sub_7FF661CD4040(v63, &unk_7FF661CE2B16);
        for ( j = 0; ; ++j )
        {
          v14 = get_value_at_index(v60, 4i64);
          if ( j >= (unsigned __int64)unknown_libname_55(v14) )
            break;
          v15 = (_QWORD *)get_value_at_index(v60, 4i64);
          v40 = *(_BYTE *)get_byte(v15, j);
          v16 = (_QWORD *)get_value_at_index(v60, 5i64);
          v17 = (_BYTE *)get_byte(v16, j);
          std::string::operator+=(v63, (unsigned __int8)(*v17 ^ v40));
        }
        if ( !(unsigned __int8)strcmp_(v63, &byte_7FF661CE2B5C) )
        {
          v48 = get_value_at_index(v60, 5i64);
          v19 = get_value_at_index(v60, 4i64);
          key = string_concat_((__int64)v59, v19, v48);
          v49 = get_value_at_index(v60, 3i64);
          v20 = get_value_at_index(v60, 2i64);
          ciphertext = string_concat_((__int64)v58, v20, v49);
          plaintext_ = (char *)RC4((__int64)plaintext, ciphertext, key);
          v33 = strcmp_(plaintext_, byte_7FF661CE2B78);
          sub_7FF661CD3F30(plaintext);
          if ( v33 )
          {
            v21 = (void *)printf_(&Format, "Incorrect!");
            _CallMemberFunction0(v21, sub_7FF661CD5180);
            sub_7FF661CD3F30(v63);
            sub_7FF661CD3F30(v62);
            sub_7FF661CD3BE0(v60);
            sub_7FF661CD3F30(v61);
            return 0;
          }
          else
          {
            sub_7FF661CD4040(v64, &unk_7FF661CE2B17);
            for ( k = 0; ; ++k )
            {
              v22 = get_value_at_index(v60, 1i64);
              if ( k >= (unsigned __int64)unknown_libname_55(v22) )
                break;
              v23 = (_QWORD *)get_value_at_index(v60, 1i64);
              v41 = *(_BYTE *)get_byte(v23, k);
              v24 = (_QWORD *)get_value_at_index(v60, 2i64);
              v25 = (_BYTE *)get_byte(v24, k);
              std::string::operator+=(v64, (unsigned __int8)(*v25 + v41));
            }
            if ( !(unsigned __int8)strcmp_(v64, &byte_7FF661CE2B94) )
            {
              sub_7FF661CD4040(v65, &unk_7FF661CE2B1F);
              for ( m = 0; ; ++m )
              {
                v27 = get_value_at_index(v60, 0i64);
                if ( m >= (unsigned __int64)unknown_libname_55(v27) )
                  break;
                v28 = (_QWORD *)get_value_at_index(v60, 0i64);
                v42 = *(_BYTE *)get_byte(v28, m);
                v29 = (_QWORD *)get_value_at_index(v60, 3i64);
                v30 = (_BYTE *)get_byte(v29, m);
                std::string::operator+=(v65, (unsigned __int8)(v42 - *v30));
              }
              if ( !(unsigned __int8)strcmp_(v65, &byte_7FF661CE2BAC) )
                v31 = (void *)printf_(&Format, "Correct!");
              else
                v31 = (void *)printf_(&Format, "Incorrect!");
              _CallMemberFunction0(v31, sub_7FF661CD5180);
              sub_7FF661CD3F30(v65);
              sub_7FF661CD3F30(v64);
              sub_7FF661CD3F30(v63);
              sub_7FF661CD3F30(v62);
              sub_7FF661CD3BE0(v60);
              sub_7FF661CD3F30(v61);
              return 0;
            }
            else
            {
              v26 = (void *)printf_(&Format, "Incorrect!");
              _CallMemberFunction0(v26, sub_7FF661CD5180);
              sub_7FF661CD3F30(v64);
              sub_7FF661CD3F30(v63);
              sub_7FF661CD3F30(v62);
              sub_7FF661CD3BE0(v60);
              sub_7FF661CD3F30(v61);
              return 0;
            }
          }
        }
        else
        {
          v18 = (void *)printf_(&Format, "Incorrect!");
          _CallMemberFunction0(v18, sub_7FF661CD5180);
          sub_7FF661CD3F30(v63);
          sub_7FF661CD3F30(v62);
          sub_7FF661CD3BE0(v60);
          sub_7FF661CD3F30(v61);
          return 0;
        }
      }
      else
      {
        v13 = (void *)printf_(&Format, "Incorrect!");
        _CallMemberFunction0(v13, sub_7FF661CD5180);
        sub_7FF661CD3F30(v62);
        sub_7FF661CD3BE0(v60);
        sub_7FF661CD3F30(v61);
        return 0;
      }
    }
    else
    {
      v9 = (void *)printf_(&Format, "Incorrect!");
      _CallMemberFunction0(v9, sub_7FF661CD5180);
      sub_7FF661CD3BE0(v60);
      sub_7FF661CD3F30(v61);
      return 0;
    }
  }
}
  • Program validate if input has preifx ASCIS and suffix }

  • Split value wrapped by ASCIS by -

    • There must be 6 value after splitted

  • Each index on splitted value will be processed with several operation like xor, add, rc4, and substraction

Because all operation are reversible, we just need to reverse the operation and leak value index by index to get the flag.

  1. Get flag5

  2. Get flag4 because we know flag5

  3. Get flag2 and flag3 because we know flag4 and flag5

  4. Get flag1 because we know flag2

  5. Get flag0 because we know flag3

Here is my solver

from arc4 import ARC4

flag = ["" for i in range(6)]

byte_7FF661CE2B44 = [0x5A, 0x5B, 0x0B, 0x0A, 0x5E, 0x5F]

for i in byte_7FF661CE2B44:
	flag[5] += chr(i ^ 0x69)

byte_7FF661CE2B5C = [0x5,0x1,0x6,0x5B,0x5,0x2]

for i in range(len(byte_7FF661CE2B5C)):
	flag[4] += chr(ord(flag[5][i]) ^ byte_7FF661CE2B5C[i])

byte_7FF661CE2B78 = [0x60, 0xE0, 0xE4, 0x2D, 0xFF, 0x97, 0xDD, 0x13, 0xEE, 0xA0, 0x55, 0xF4]
ct = bytes(byte_7FF661CE2B78)

key = flag[4] + flag[5]
rc4 = ARC4(key.encode())

tmp = rc4.decrypt(ct).decode()
flag[2] = tmp[:6]
flag[3] = tmp[6:12]

byte_7FF661CE2B94 = [0x95, 0xC8, 0x95, 0x9D, 0x69, 0x68]

for i in range(len(byte_7FF661CE2B94)):
	flag[1] += chr(byte_7FF661CE2B94[i] - ord(flag[2][i]))

byte_7FF661CE2BAC = [0x01, 0xFA, 0x06, 0xD2, 0xFF, 0xCE]

for i in range(len(byte_7FF661CE2BAC)):
	flag[0] += chr((ord(flag[3][i]) + byte_7FF661CE2BAC[i])&0xff)

print("ASCIS{" + "-".join(flag) + "}")

Flag: ASCIS{829872-bccd38-3e2960-783f8d-63d824-32bc76}

OXYGEN (481 pts)

Description

-

Solution

Given PE file, open it using IDA. IDA can't detect main function but there is start function.

Analyzing start function i can't find "main" function, so i tried to look at available strings.

Go to address for each reference, we will find out that the main function is sub_7FF61CE8D7A0.

There is something weird on decompiled code (sub_7FF61CE8B8E0).

Through trial and error i found the solution, we can just put change the second call to nop then decompile again.

int __fastcall __noreturn main_0(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  __int64 i; // rcx
  int v6; // eax
  char v7; // [rsp+30h] [rbp+0h] BYREF
  struct WSAData WSAData; // [rsp+40h] [rbp+10h] BYREF
  int v9; // [rsp+1F4h] [rbp+1C4h]
  SOCKET v10; // [rsp+218h] [rbp+1E8h]
  SOCKET s; // [rsp+238h] [rbp+208h]
  PADDRINFOA ppResult; // [rsp+258h] [rbp+228h] BYREF
  ADDRINFOA pHints; // [rsp+278h] [rbp+248h] BYREF
  int v14; // [rsp+2C4h] [rbp+294h]
  char buf[532]; // [rsp+2F0h] [rbp+2C0h] BYREF
  int len; // [rsp+504h] [rbp+4D4h]
  DWORD flOldProtect[9]; // [rsp+524h] [rbp+4F4h] BYREF
  __int64 (__fastcall *v18)(char *, _QWORD, char *); // [rsp+548h] [rbp+518h]
  int v19; // [rsp+564h] [rbp+534h]

  v3 = &v7;
  for ( i = 342i64; i; --i )
  {
    *(_DWORD *)v3 = -858993460;
    v3 += 4;
  }
  j___CheckForDebuggerJustMyCode((__int64)&unk_1400340FE, (__int64)argv, (__int64)envp);
  sub_1400113C0(*argv);
  v10 = -1i64;
  s = -1i64;
  ppResult = 0i64;
  len = 512;
  v9 = WSAStartup(0x202u, &WSAData);
  if ( v9 )
    return 1;
  j_memset(&pHints, 0, sizeof(pHints));
  pHints.ai_family = 2;
  pHints.ai_socktype = 1;
  pHints.ai_protocol = 6;
  pHints.ai_flags = 1;
  v9 = getaddrinfo(0i64, "1337", &pHints, &ppResult);
  if ( v9 )
  {
    WSACleanup();
    return 1;
  }
  else
  {
    v10 = socket(ppResult->ai_family, ppResult->ai_socktype, ppResult->ai_protocol);
    if ( v10 == -1i64 )
    {
      freeaddrinfo(ppResult);
      WSACleanup();
      return 1;
    }
    else
    {
      v9 = bind(v10, ppResult->ai_addr, ppResult->ai_addrlen);
      if ( v9 == -1 )
      {
        freeaddrinfo(ppResult);
        closesocket(v10);
        WSACleanup();
        return 1;
      }
      else
      {
        puts("gimme oxygen!");
        freeaddrinfo(ppResult);
        v9 = listen(v10, 0x7FFFFFFF);
        if ( v9 == -1 || (s = accept(v10, 0i64, 0i64), s == -1i64) )
        {
          closesocket(v10);
          WSACleanup();
          return 1;
        }
        else
        {
          closesocket(v10);
          VirtualProtect(&unk_14002D000, 0x7Bui64, 0x40u, flOldProtect);
          v18 = (__int64 (__fastcall *)(char *, _QWORD, char *))&unk_14002D000;
          do
          {
            v9 = recv(s, buf, len, 0);
            if ( v9 <= 0 )
              goto LABEL_24;
            v19 = v18(buf, (unsigned int)len, aNCQrm);
            if ( v19 == 1 )
            {
              puts("congratz");
              sub_1400114C9((unsigned int)&unk_14002D090, 39, (unsigned int)buf, 11, (__int64)::buf);
              v6 = j_strlen(::buf);
              v14 = send(s, ::buf, v6, 0);
              if ( v14 == -1 )
                goto LABEL_24;
            }
            else
            {
              puts("oops");
            }
          }
          while ( v9 > 0 );
          v9 = shutdown(s, 1);
          if ( v9 == -1 )
          {
LABEL_24:
            closesocket(s);
            WSACleanup();
            return 1;
          }
          closesocket(s);
          WSACleanup();
          return 0;
        }
      }
    }
  }
}
  • Program will listening at port 1337

  • If there is connection, it will print "gimmy oxygen!"

    • Program will receive our input and call function unk_14002D000

    • if function unk_14002D000 return 1, it will call sub_1400114C9 (RC4)

From above analysis we can conclude that we need to find input that return 1 for function unk_14002D000. At first i tried to disassemble unk_14002D000 and it looks like not a valid assembly.

So lets take a look on cross reference.

Looks like it just basic xor, use idapython to automatically patch the values.

start = 0x14002D000

for i in range(0x7b):
	val = get_bytes(start+i, 1)
	new_val = val[0] ^ 0x69
	patch_byte(start+i, new_val)

Now it looks like valid assembly, lets decompile the code and rename known variables.

  • v9 = input - target

    • target[v9] == target + input - target

    • target[v9] == input

  • if counter is odd, substract

  • if counter is even, add

Now, lets write the script to bruteforce valid input.

import string

target = b"n[}>}C]qRm["

poss = []
for i in range(len(target)):
	poss_tmp = []
	for j in string.printable[:-6]:
		tmp = ord(j) | 0xa
		tmp2 = ord(j) & 0xa
		tmp3 = (tmp - tmp2)&0xff
		tmp4 = (tmp + tmp2)&0xff
		if(i%2 == 0):
			if(tmp3 == target[i]):
				poss_tmp.append(j)
		else:
			if(tmp4 == target[i]):
				poss_tmp.append(j)
	poss.append(poss_tmp)

key = ""
for i in poss:
	key += i[0]
print(key)

Finally put the generated key to the service.

Flag: ASCIS{W3_g0nn4_m33t_4t_th3_f1naL_r0uND}

📚
📖
Here
Here