VishwaCTF 2023

Hello mates! I played VishwaCTF 2023 which was happened from 31 Mar to 3 Apr. I played it with the team Invaders0x1. These are the challenges i have solved during CTF.

Progess

WEB-Eeezzy

Eeezzy

The challenge is at https://ch41272110703.ch.eng.run. It is a login page with fields username and password.

First, I did ctrl+u 😉. It shows a view.php in onclick function.

Eeezzy src

Visiting https://ch41272110703.ch.eng.run/view.php results the following code

<?php
    session_start();
    $_SESSION['status']=null;
    $flag="";
    try {
        if (isset($_GET['username']) && isset($_GET['password'])) {
            if (strcmp($_GET['username'], $flag)==0 && strcmp($_GET['password'], $flag)==0)
                $_SESSION['status']=$flag;
            else
                $_SESSION['status']="Invalid username or password";
        }
    } catch (Throwable $th) {
        $_SESSION['status']=$flag;
    }
?>

This php code is checking for the username and password parameters which are entered in the login page. The username and flag are compared using strcmp() and similarly password and flag. If the condition satisfied we can get the flag.

Exploiting strcmp() vulnerabilities.

If I set $_GET['password'] equal to an empty array, then strcmp would return a NULL. Due to some inherent weaknesses in PHP’s comparisons, NULL == 0 will return true.

Tried by inputing some default credentials in the login page. It says incorrect details, then i captured the request headers. I found that this https://ch41272110703.ch.eng.run/view.php?username=admin&password=1234&submit=Login has been sent to the server for the authentication.

The payload would be : ?username=admin&password[]=34&submit=Login

https://ch41272110703.ch.eng.run/view.php??username=admin&password[]=34&submit=Login returns an error in the login page with the flag.

Eeezzy flag

Flag : VishwaCTF{5t0p_c0mp4r1ng}


Steganography-Guatemala

Eeezzy flag

Attached files : a gif file named AV

I tried exiftool to view metadata of the gif.

┌─[intruder@parrot]─[/Guatemala]
└──╼ $exiftool AV
ExifTool Version Number         : 12.16
File Name                       : AV
Directory                       : .
File Size                       : 1086 KiB
File Modification Date/Time     : 2023:03:31 17:17:04+05:30
File Access Date/Time           : 2023:04:04 10:58:55+05:30
File Inode Change Date/Time     : 2023:04:04 10:58:51+05:30
File Permissions                : rwxrwx---
File Type                       : GIF
File Type Extension             : gif
MIME Type                       : image/gif
GIF Version                     : 89a
Image Width                     : 498
Image Height                    : 498
Has Color Map                   : Yes
Color Resolution Depth          : 8
Bits Per Pixel                  : 8
Background Color                : 0
Animation Iterations            : Infinite
Comment                         : dmlzaHdhQ1RGe3ByMDczYzdfdXJfM1gxRn0=
Frame Count                     : 17
Duration                        : 2.04 s
Image Size                      : 498x498
Megapixels                      : 0.248

It looks there is a base64 encoded text in the comments. By decoding it we can get the flag.

┌─[intruder@parrot]─[/Guatemala]
└──╼ $echo "dmlzaHdhQ1RGe3ByMDczYzdfdXJfM1gxRn0=" | base64 -d
vishwaCTF{pr073c7_ur_3X1F}

Flag : vishwaCTF{pr073c7_ur_3X1F}


Steganography-Can you see me?

can you see me

Attached files : havealook.jpg

First of all, lets try to extract if any data is present in the image using steghide.

┌─[✗]─[intruder@parrot]─[/canyouseeme]
└──╼ $steghide extract -sf havealook.jpg 
Enter passphrase: 
steghide: could not extract any data with that passphrase!

Okay, we dont have password. Lets use unzip if we can extract data out of it without password.

┌─[intruder@parrot]─[/canyouseeme]
└──╼ $unzip havealook.jpg
Archive:  havealook.jpg
warning [havealook.jpg]:  134855 extra bytes at beginning or within zipfile
  (attempting to process anyway)
  inflating: hereissomething.wav     

Yes, we got an audio file hereissomething.wav. The audio is not understandable by the humans. It seems to be a morsecode. Then i used this site to decode the morse code. In the sonic visualizer i found the flag.

Flag

Flag : vishwaCTF{n0w_y0u_533_m3}


Forensics - The Sender Conundrum

The Sender Conundrum

Attached files : TheEmail.eml and unzipme.zip

The zip file is password protected. So, lets analyze email raw data for the password.

There is a riddle sent by the sender.

<p></p>Hello Marcus Cooper,<br>
 You are one step behind from finding your flag. <br>
 Here is a Riddle: <br>
 I am a noun and not a verb or an adverb.<br>
 I am given to you at birth and never taken away,<br>
 You keep me until you die, come what may.<br>
 What am I?<br>

I am too bad to solve riddles, so I found a solution on google as Name

Tried Name as the password to extract zip file. Its the wrong password. Then i tried few names present in the eml file. I found the correct name on this line Return-Path: BrandonLee@anonymousemail.me. I extracted the flag file from the zip with the password BrandonLee.

┌─[intruder@parrot]─[thesenderconundrum]
└──╼ $unzip unzipme.zip 
Archive:  unzipme.zip
   creating: unzipme/
[unzipme.zip] unzipme/flag.txt password: 
 extracting: unzipme/flag.txt        
┌─[intruder@parrot]─[/thesenderconundrum]
└──╼ $cat unzipme/flag.txt
vishwaCTF{1d3n7i7y_7h3f7_is_n0t_4_j0k3}

Flag : vishwaCTF{1d3n7i7y_7h3f7_is_n0t_4_j0k3}


REV-Phi-Calculator

Phi - calc

Attached files : Phi_Calculator.py

#============================================================================#
#============================Phi CALCULATOR===============================#
#============================================================================#
import hashlib
from cryptography.fernet import Fernet
import base64

# GLOBALS --v
arcane_loop_trial = True
jump_into_full = False
full_version_code = ""

username_trial = "vishwaCTF"
bUsername_trial = b"vishwaCTF"

key_part_static1_trial = "VishwaCTF{m4k3_it_possibl3_"
key_part_dynamic1_trial = "xxxxxxxx"
key_part_static2_trial = "}"
key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial

print(len(key_full_template_trial))
star_db_trial = {
  "Sharuk Khan": 4.38,
  "Bollywood Star": 5.95,
  "Rohan 16": 6.57,
  "WISH 0855-0714": 7.17,
  "Tiger 007": 7.78,
  "Lalande 21185": 8.29,
  "UV Ceti": 8.58,
  "Sirius": 8.59,
  "Boss 154": 9.69,
  "Yin Sector CL-Y d127": 9.86,
  "Duamta": 9.88,
  "Ross 248": 10.37,
  "WISE 1506+7027": 10.52,
  "Epsilon Eridani": 10.52,
  "Lacaille 9352": 10.69,
  "Ross 128": 10.94,
  "EZ Aquarii": 11.10,
  "61 Cygni": 11.37,
  "Procyon": 11.41,
  "Struve 2398": 11.64,
  "Groombridge 34": 11.73,
  "Epsilon Indi": 11.80,
  "SPF-LF 1": 11.82,
  "Tau Ceti": 11.94,
  "YZ Ceti": 12.07,
  "WISE 0350-5658": 12.09,
  "Luyten's Star": 12.39,
  "Teegarden's Star": 12.43,
  "Kapteyn's Star": 12.76,
  "Talta": 12.83,
  "Lacaille 8760": 12.88
}

def intro_trial():
    print("\n===============================================\n\
Welcome to the Phi Calculator, " + username_trial + "!\n")    
    print("This is the trial version of Phi Calculator.")
    print("The full version may be purchased in person near\n\
the galactic center of the Milky Way galaxy. \n\
Available while supplies last!\n\
=====================================================\n\n")

def menu_trial():
    print("___Phi Calculator___\n\n\
Menu:\n\
(1) Estimate Projection Burn\n\
(2) [LOCKED] Estimate  Slingshot Approach Vector\n\
(3) Enter License Key\n\
(4) Exit Phi Calculator")
    choice = input("What would you like to do, "+ username_trial +" (1/2/3/4)? ")
    if not validate_choice(choice):
        print("\n\nInvalid choice!\n\n")
        return
    if choice == "1":
        estimate_burn()
    elif choice == "2":
        locked_estimate_vector()
    elif choice == "3":
        enter_license()
    elif choice == "4":
        global arcane_loop_trial
        arcane_loop_trial = False
        print("Bye!")
    else:
        print("That choice is not valid. Please enter a single, valid \
lowercase letter choice (1/2/3/4).")
def validate_choice(menu_choice):
    if menu_choice == "1" or \
       menu_choice == "2" or \
       menu_choice == "3" or \
       menu_choice == "4":
        return True
    else:
        return False
def estimate_burn():
  print("\n\nSOL is detected as your nearest star.")
  target_system = input("To which system do you want to travel? ")
  if target_system in star_db_trial:
      ly = star_db_trial[target_system]
      mana_cost_low = ly**2
      mana_cost_high = ly**3
      print("\n"+ target_system +" will cost between "+ str(mana_cost_low) \
+" and "+ str(mana_cost_high) +" stone(s) to project to\n\n")
  else:
      # TODO : could add option to list known stars
      print("\nStar not found.\n\n")
def locked_estimate_vector():
    print("\n\nYou must buy the full version of this software to use this \
feature!\n\n")
def enter_license():
    user_key = input("\nEnter your license key: ")
    user_key = user_key.strip()

    global bUsername_trial
    
    if check_key(user_key, bUsername_trial):
        decrypt_full_version(user_key)
    else:
        print("\nKey is NOT VALID. Check your data entry.\n\n")
def check_key(key, username_trial):
    global key_full_template_trial
    if len(key) != len(key_full_template_trial):
        return False
    else:
        # Check static base key part --v
        i = 0
        for c in key_part_static1_trial:
            if key[i] != c:
                return False
            i += 1
        # TODO : test performance on toolbox container
        # Check dynamic part --v
        if key[i] != hashlib.sha256(username_trial).hexdigest()[4]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[5]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[3]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[6]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[2]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[7]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[1]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[8]:
            return False
        return True
def decrypt_full_version(key_str):
    key_base64 = base64.b64encode(key_str.encode())
    f = Fernet(key_base64)
    try:
        with open("keygenme.py", "w") as fout:
          global full_version
          global full_version_code
          full_version_code = f.decrypt(full_version)
          fout.write(full_version_code.decode())
          global arcane_loop_trial
          arcane_loop_trial = False
          global jump_into_full
          jump_into_full = True
          print("\nFull version written to 'keygenme.py'.\n\n"+ \
                 "Exiting trial version...")
    except FileExistsError:
        sys.stderr.write("Full version of keygenme NOT written to disk, "+ \
                      "ERROR: 'keygenme.py' file already exists.\n\n"+ \
              "ADVICE: If this existing file is not valid, "+ \
              "you may try deleting it and entering the "+ \
              "license key again. Good luck")
def ui_flow():
    intro_trial()
    while arcane_loop_trial:
        menu_trial()
# Encrypted blob of full version
full_version = \
b"""
gAAAAABgT_nvHAwPaWal_64Giubfb7I87ML4ANp4g-eUbMTqsc4asWygnpXcaJ5FLahXXDcul9xPDqIPPytiZ9aMm25S6dgfi4ZPvM5IUSnnNjk6dxYAKsX5Yd72BV4ERrqdNNn2jZrphzlV4a4gY-XV_0ZHovFlHhEpPQnTtG_5RTETId0xAD5K5iActkI9a3P4sx6ExBQ082EuPFlnWtUGl0dsEDHher3xT_lZe9JP5UAcOJsoC9AJ7N3Y1KjWXATzaBkXw6XTnzqDHu9Ycffw-i-GfQP-16hF_f2WBE9nQqniFu6THNuAvqwg0XBnsfvV0Fo3MTpON6HpeI3eIXqd4tLtsfhNcPa99ugrucf0l19Z5eFvrtMMgmfW_9lgvO7UcCft79ShvQWEHjhVeiDKBZo7TgTJ-1wpB92obH_bFGJpMcsp1w42tDEJmavnRSKXl39ph9-cgVXUKTfsjUbJCgtZfR8yj28JFCdmETu2kkt_dW4aLN8BTeRHLUpCEod9xBUFxzQJZNxey6ISn2j-PTR-yxCXrC2_A3TCBcqwUJYviP6emLKPSBRJB8dkRlWmylnMH4aYd6YXPnY457tk6UpGO6Ezw4K4DEhFtMSO4Vq2UhAS85j8kokc9_GG2v315uqVZ-TY7nU7xkhsrtEFK0h-0jiiLbTKLvOb3zpXq0ELdX9_WEK681LsIuFErhsvvPmmXx1K7IDlmjIWkYw--7lqpXPVrl9LalI-7npOF4MYet3jlH9v5Y83K3VDCrNZjH8uqK4pTKo0_I4HOmEtfe8pghAYDldmQ8wvphHRh4UEM34QcgCJa3VH1XAu2MRDwbEWcnXxumt_xL2wXBTFAPZWxrnioRunzw5HnrqW6Nzi871XiJ0OHQzt_ulvgxDmFMxAiSpzm9YJoxspTG1hpSqLe5IUICBXEhofgTAhHePGff-Qi20rDYQMQio8zoyV2ZPRjKVk8YDGZdhuSQaKLx-DRdvKBzmAYqjbvmbC_4Tt3_amXlqxeLRLA9YP7vtxv9Y65WZ-8DeGdZTinUgjh6xqJH0xJvfEhXITOEiFGZseX2kPPG4pX1nDCZ8R9ksgHxkpnW24sQSVGmx5DP0xGihfmABc98bag-qrs-QIb7YqwJqK0-0N0t7hFKF671doX07XWcIGLJuFZ0MHxPOIjVIWN8Xb7mKJiL7goQH8xuy9FcE6w8_GVw5N9nfWUCFdZKENYJ5WY3iX20OtJgiYTwvCTetf-wDWj_FH6z_hpufI1sDh9lO9EnAhxpoNo4jMjC3eZUKPkkUf_gfvjWmnA89Gvsuxj70lzwZ1650isi0_JPtDIWKaksprzIW8YN-MeuBYy_f3JJOtU4cCg5sInTM3YW5GupJMO4h6Y6vk1QPxWYM5Nr5_cB7i2FSyt7DY72L_ede8YNJxcRCBkf3eD-3aO6KmPAbbf_48aaM3L1axNVKwubW9Mec6YRoV0JwgJM0Km3YAGL_ybtYX5JeoPQzoOQBw_Vue2k8PsnbO6n2p3acaY8Y-6ZhKnrSBaeuACSZtTqJT4_WXYslQyX-Pgl-ljcq84H0AAPNnJ9AlNZwvGL6fKbdcxpcQ7RN8fdoU6bJ2q2XecXred2XfE2UHK-QTacm-amF5Mt4WrNlF8RGRuCagny7o6XyEYO_-xyowpUYsOA9c5j6u8qpju0donhdr0OWWKHpIWvOsDdzX-YEcQvfdXfLdDLDSDqGJUyB5giQK3IVqUeBAN2ZHdKyFAACUog4U0RJLJ3tEedF_PLZ5eqHyo_jwfBmqRi_bK2cwuYU_psxTBB6o2a2o1vx-nprP4QFVxdWD7by4VTFKCVW2yAGkL1OHPAc6hcoVhysAIMQhhqJF0SXXdqeXzFrM7pexr3sV9uL_R_CcknOk28VE0IyrvJLMj1sI-MkXqRFTdwjump6fneQizBHAy2Kk7GgU7JLwSvgUVGBS581ITxuQ-jeZW5od1m9z6xYLMKXNSV-EUZXhGPOz4kd87gTRxDMd9S5pkSqfiBQgrIrEuNtaDYJsc22r5MAGNpe-ouGhE_QRMPDaEVP8CibNu0wnjgrt_4Qf8M6ZURy6fzssBsqIjfFymJDe8uSmz5yosvTuRfsjcC_mhyeVFrjiHSzH5OEfNS0ihPMI6H9j4vdid0Wk3ewjfT3rpsadbuHBJTRxPcBN-dc8vaLvLzcJehyGQhvEVwmiycjAJ16pgOT5-rR0-ZLKoiaaa3OHcs8RB3ZXLe9LkJHsqCvjGeI4qqlkLfAhG08gNsAxtcbYAEVzKDvNDPdbWOsioIX3lKKiiGNztZMruThMwycUQ1zRN_5sRC5DTQDv0l3ka0OELW2U04Og5Sj5x1u2rdWSBa9nEI6LJ7nnp-pLTGo-C7tsq1boTz4WdHNMvAP2GWv99NFN37pa8UY6mjmdMAg0Ppw9rfxeGKq60jh-VcBuY3Yvu1g2_Ntv1e8CeK1jNXl06zLGLBO35hLwix4UcQmU_9M0v1QsYfjYBRW5sUnjcB3lGF8KJg6PYGbHcvAEDlqw12ZtITFOaIqhkpvSzbfG1LQF7e_NfhXijgBMhJBug9tTayv0g5U2CPuZ-B4z_SkmEvN_eU5G1rht3Zv0ygTuOXW0Iig0XxFO02QugZSqIRg7fGRj6fxDVWXvQT6k3zXlXxN6LrHYHbcW7Irs0pLxm7pfAPBYlnFwRTHXI4HhnMUsPiK3v0oPU7IB67y4XCUMncMcGstRB4zqnaZI7dR8YPQfAZQ0CUjT5Z_H52Qp9ek5H_G48vb0DFC1qzgpNlfHcXrBLuhf_Gcc_1dzn9E4ZwoiF16aJhHPHSAGhOwclLy7xxy22ZenZVeKXcXrA7jUbPGcS-SWmUjF1IPe_Pkpfcgi5rIaxUhCWX5jK0c_n0_q2UAv9KAKJBaWstjcYBxtuUtHTFJD0ky9VDOqVJx1-V6tD1lNsnF1FfNrfIpB9YkoCxRIXDuBiCSagiwa830S6-1bREZMZug-etzjr1Wf7cO1PTDd61JSM252DWqHVVLQs8yYKmhzsZxfeI_uV5G7Y8fvwIYBB4krFRjpFR4-fGwF4Vma-xZlr6y9ziILNUyqz2u4FBmMjc1V8YgeqqXsLIuSHF6GDhvGXq-mEqLTWnxSAE-G_zeX7qPDAlsSv_dRLByQ0ZekBEQ1YbCpmnbZIPTJ_IyZLX0ZBOz3oc0ju5mFUFAzN8sJlwuZFH2GQeC9T2GJO8lJEhn4NqiudzmXVMerdRaL1C9ZbJfGSEkuEKQL2I2NeW5Nm7d4MStHdtZhO190_lXP2PQ8Tuz5BrPlYKgGf76NZshAU0XKXglyTWQKzONVv6251qh4wpMgWWFm8Va_zGlXNFd8QmQWpbhkWTLmo9ixI4W92hkw4oheJVE5n9LB1HWz50oSajV_2jJW_5Bd5Gtz6S3Q2X_xfA_TgRyeT0DXgbQ8mYx_N_43S_D94ud66-NnRA_A1KG-uu31KH5btUg6f3-oxoO4waPW8-hM0arNlGjREg0_LhAMALknhfJlno2VnQo2ExgXj6v-kaBlTuh4jt5vbhepD5EgtGvbXT4mypQbS49LA3SxCxEq7vDSxHfnLKWI84IlAeU8NQE6drQd9IGQ9lRWZDzHgvz7dO6Og4pIt7Q6UA2NEIc6ZNDTsghtKFVep19d7nGJDt-4-UCFJSHWBhTKeqb_A34XO4T5U7x-CXqphsBwIdMoPXHrWxhoFYaP6lPJVOryz8TEYDLsHbVdmhYJtA0bPgMPC1rNI-SqcyUqvZFGpIJwDcGghTTS1u8XjMlRkxOxuEMDO364AdLtruslkXjpd2NuUBUFwNWbQbfYIC5mePqxc_PhcaVxMXHYrFh2CLqXX7UhcZxHT9C8RQ==
"""
# Enter main loop
ui_flow()
if jump_into_full:
    exec(full_version_code)

By looking at the challenge code. The first part of the flag is present in the code as plaintext. key_part_static1_trial = "VishwaCTF{m4k3_it_possibl3_"

The remaining part of the flag is stored in key_part_dynamic1_trial. The full version of the flag is in key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial. We have the full version and the initial part of the flag.

Lets look at the decryption function. This is going to print the decrypted full version if we enter the license which is the middle part of the flag.

Okay, lets see how the key/license has been validated here.

def check_key(key, username_trial):
    global key_full_template_trial
    if len(key) != len(key_full_template_trial):
        return False
    else:
        # Check static base key part --v
        i = 0
        for c in key_part_static1_trial:
            if key[i] != c:
                return False
            i += 1
        # TODO : test performance on toolbox container
        # Check dynamic part --v
        if key[i] != hashlib.sha256(username_trial).hexdigest()[4]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[5]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[3]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[6]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[2]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[7]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[1]:
            return False
        else:
            i += 1
        if key[i] != hashlib.sha256(username_trial).hexdigest()[8]:
            return False
        return True

The key/license characters are compared with the hash characters of the username_trail.

username_trail = username_trial = b"vishwaCTF" hashlib.sha256(username_trial).hexdigest() = a1ccb7d574518024795268ab284efdd93787b7cb741038437f24bff749a7aa0c Now the key[0] = hashlib.sha256(username_trial).hexdigest()[4] = b

So, i wrote this script to print the flag rather than doing it manually.

import hashlib
from cryptography.fernet import Fernet
import base64

username_trial = b"vishwaCTF"
key_part_static1_trial = "VishwaCTF{m4k3_it_possibl3_"
key_part_dynamic1_trial = "xxxxxxxx"
key_part_static2_trial = "}"

key =[]
index = [4,5,3,6,2,7,1,8]
for i in index:
    key.append(hashlib.sha256(username_trial).hexdigest()[i])

key_part_dynamic1_trial = "".join(key)

key_full_template_trial = key_part_static1_trial + key_part_dynamic1_trial + key_part_static2_trial
print(key_full_template_trial)
#output
#VishwaCTF{m4k3_it_possibl3_b7cdc517}

Flag : VishwaCTF{m4k3_it_possibl3_b7cdc517}


WEB-aLive

Alive

The website that is given in challenge is checking the live hosts.

I tried to insert some html code in the input.

Alive

As i expected the site was processed the html code.

Alive

So , it seems to have a Reflected XSS vulnerability. I tried multiple payloads to done my work.

');</script><script>alert(document.cookie)</script><b>

',</script><script>x=new XMLHttpRequest; alert(this.responseText); x.open("GET","file:///etc/passwd"); x.send()</script>;

Sadly, They are not worked. But I just tried to access /flag.txt of the site from the searchbar. And surprisingly it returned the flag.

Alive

I dont know wheather it is intended or not, but many teams solved it in this way only!

Flag : VishwaCTF{blinD_cmd-i}


Crypto-Indecipherable Cipher

Indecipherable

First, I used this site to identify the cipher type. It shows that it is mostly a vigenere cipher. Then i used this site to decode the cipher.

Indecipherable Indecipherable

The flag is the decrypted text in format.

Flag : VishwaCTF{friedrichwilhelmkasiskiwastheonewhodesignedtheaaakasiskiexaminationtodecodevignerecipher}