misc

Challenges

Read The Rules

Read the rules. They can be found in the #rules channel in discord, or here. The rules will contain a link, which will ultimately contain the flag.

View Hint: Hint 1

The final page will just display the flag.

View Hint: Hint 2

If you can't find the flag, you can make a ticket on our discord.

View Hint: Hint 3

You shouldn't need three hints to solve this challenge, come on!

  • Go to the saide rules page...you should be able to found the links at the very last line

  • scriptCTF{600D_1ucK_5011D3r1}

Div

I love division

import os
import decimal
decimal.getcontext().prec = 50

secret = int(os.urandom(16).hex(),16)
num = input('Enter a number: ')

if 'e' in num.lower():
    print("Nice try...")
    exit(0)

if len(num) >= 10:
    print('Number too long...')
    exit(0)

fl_num = decimal.Decimal(num)
div = secret / fl_num

if div == 0:
    print(open('flag.txt').read().strip())
else:
    print('Try again...')

The answer is inf because:

secret / inf = 0

  • "inf" passes both checks (no 'e', length < 10)

  • decimal.Decimal("inf") creates infinity

  • secret / ∞ = 0 triggers the flag condition

  • scriptCTF{70_1nf1n17y_4nd_b3y0nd_00ec1b195af8}

emoji

Emojis everywhere! Is it a joke? Or something is hiding behind it.

🁳🁣🁲🁩🁰🁴🁃🁔🁆🁻🀳🁭🀰🁪🀱🁟🀳🁮🁣🀰🁤🀱🁮🁧🁟🀱🁳🁟🁷🀳🀱🁲🁤🁟🀴🁮🁤🁟🁦🁵🁮🀡🀱🁥🀴🀶🁤🁽
  • This one was quite vauge, so some llm magic and kaboom...we know the type of shennanigen behinds the "emoji"

  • So the emojis are encoding ASCII text using playing card symbols as a substitution cipher. Each emoji maps to a specific character based on its position in the playing card Unicode block.

  • Conversion:

    • ord(char) gets the Unicode value of each emoji

    • - 0x1F030 removes the base playing card offset

    • + 48 shifts to ASCII range

    • chr() converts back to readable characters

  • scriptCTF{3m0j1_3nc0d1ng_1s_w31rd_4nd_fun!1e46d}

Script

# emoji_string = "🁳🁣🁲🁩🁰🁴🁃🁔🁆🁻🀳🁭🀰🁪🀱🁟🀳🁮🁣🀰🁤🀱🁮🁧🁟🀱🁳🁟🁷🀳🀱🁲🁤🁟🀴🁮🁤🁟🁦🁵🁮🀡🀱🁥🀴🀶🁤🁽"
with open('emoji.txt', 'r') as file:
    emoji_string = file.read()

flag = ''.join(chr(ord(char) - 0x1F030 + 48) for char in emoji_string)
print(flag)

Enchant

I was playing minecraft, and found this strange enchantment on the enchantment table. Can you figure out what it is? Wrap the flag in scriptCTF{}

ᒲ╎リᒷᓵ∷ᔑ⎓ℸ ̣ ╎ᓭ⎓⚍リ
  • tbh, I did a quick paste into Google to see what it is, and I got Galatic Alphabet.

  • scriptCTF{Minecraftisfun}

Div 2

Some might call this a programming challenge...

import secrets
import decimal
decimal.getcontext().prec = 50
secret =  secrets.randbelow(1 << 127) + (1 << 127) # Choose a 128 bit number

for _ in range(1000):
    print("[1] Provide a number\n[2] Guess the secret number")
    choice = int(input("Choice: "))
    if choice == 1:
        num = input('Enter a number: ')
        fl_num = decimal.Decimal(num)
        assert int(fl_num).bit_length() == secret.bit_length()
        div = secret / fl_num
        print(int(div))
    if choice == 2:
        guess = int(input("Enter secret number: "))
        if guess == secret:
            print(open('flag.txt').read().strip())
        else:
            print("Incorrect!")
        exit(0)

What the program does is generate a secret 128-bit number and allows us to

  1. Provide a number and get int(secret / our_number)

  2. Guess the secret number

The vulnerability is at line

  • div = secret / fl_num
    print(int(div))
  • This gives you secret // fl_num (integer division result), which leaks information about the secret's magnitude relative to your input.

  • We also know the secret's range from this line in the challenge:

    • secret =  secrets.randbelow(1 << 127) + (1 << 127) # Choose a 128 bit number
    • That is why we can perform a search to narrow down the 128-bit space logarithmically

Script

from pwn import *

def solve():
    p = process(['python3', 'chall.py'])
    # p = remote('play.scriptsorcerers.xyz', 10311)

    # Binary search for the secret
    # Secret is 128-bit: between 2^127 and 2^128-1

    low = 1 << 127
    high = (1 << 128) - 1

    while low <= high:
        mid = (low + high) // 2
        print(f"Trying mid = {mid}")

        # Query: what is secret // mid ?
        p.sendlineafter(b'Choice: ', b'1')
        p.sendlineafter(b'Enter a number: ', str(mid).encode())

        quotient = int(p.recvline().strip())

        if quotient == 0:
            # secret < mid
            high = mid - 1
        elif quotient == 1:
            # secret >= mid and secret < 2 * mid
            # Check if secret >= mid + 1
            if mid + 1 <= high:
                p.sendlineafter(b'Choice: ', b'1')
                p.sendlineafter(b'Enter a number: ', str(mid + 1).encode())
                quotient2 = int(p.recvline().strip())

                if quotient2 == 0:
                    # secret < mid + 1, so secret == mid
                    secret = mid
                    break
                else:
                    # secret >= mid + 1
                    low = mid + 1
            else:
                secret = mid
                break
        else:
            # quotient >= 2, so secret >= 2 * mid
            low = 2 * mid

    # Guess the secret
    p.sendlineafter(b'Choice: ', b'2')
    p.sendlineafter(b'Enter secret number: ', str(secret).encode())

    result = p.recvline().strip().decode()
    print(f"Result: {result}")
    p.close()

solve()
```python
❯ python solve.py
[+] Starting local process '/home/x/miniconda3/envs/ctfs/bin/python3': pid 1176860
Trying mid = 255211775190703847597530955573826158591
Trying mid = 297747071055821155530452781502797185023
Trying mid = 276479423123262501563991868538311671807
Trying mid = 287113247089541828547222325020554428415
...
Trying mid = 281308022660211790137507177938664385631
Trying mid = 281308022660211790137507177938664385647
Trying mid = 281308022660211790137507177938664385639
Trying mid = 281308022660211790137507177938664385643
Trying mid = 281308022660211790137507177938664385641
Result: scriptCTF{b1n4ry_s34rch_u51ng_d1v1s10n?!!_5ad3d0d2dee7}
[*] Stopped process '/home/x/miniconda3/envs/ctfs/bin/python3' (pid 1176860)

scriptCTF{b1n4ry_s34rch_u51ng_d1v1s10n?!!_5ad3d0d2dee7}

Subtract

The image size is 500x500. You might want to remove some stuff... Note: Some may call it guessy!

❯ unzip -l coords.zip
Archive:  coords.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
  2645453  2025-07-12 09:06   coordinates.txt
---------                     -------
  2645453                     1 file
  • The description said it...the coords is to form an 500x500 image, and we have to remove the noise.

    • Upon closer inspection, many coordinates appear multiple times in the file.

When we count how many times each coordinate appears:

  • Most pixels appear an even number of times (typically twice)

  • A smaller subset appears an odd number of times (typically once)

  • By keeping only odd-parity pixels, the noise cancels out and the hidden message emerges

Script

import re
import numpy as np
from PIL import Image
from collections import Counter

def solve():
    # Read coordinates
    with open("coordinates.txt", 'r') as f:
        content = f.read()

    # Extract all (x,y) pairs
    pairs = re.findall(r'\((\d+),\s*(\d+)\)', content)
    coords = [(int(x), int(y)) for x, y in pairs]

    # Count occurrences of each coordinate
    counter = Counter(coords)

    # Create image: only plot coordinates with ODD occurrences
    img = np.zeros((500, 500), dtype=np.uint8)

    for (x, y), count in counter.items():
        if count % 2 == 1:  # Keep odd parity only
            if 0 <= x < 500 and 0 <= y < 500:
                img[y, x] = 255

    # Find content bounds and crop
    white_pixels = np.where(img == 255)
    if len(white_pixels[0]) > 0:
        min_y, max_y = white_pixels[0].min(), white_pixels[0].max()
        min_x, max_x = white_pixels[1].min(), white_pixels[1].max()

        # Add padding
        padding = 10
        min_y = max(0, min_y - padding)
        max_y = min(499, max_y + padding)
        min_x = max(0, min_x - padding)
        max_x = min(499, max_x + padding)

        # Crop and save
        cropped = img[min_y:max_y+1, min_x:max_x+1]
        Image.fromarray(cropped).save('flag.png')
    else:
        Image.fromarray(img).save('flag.png')

solve()
  • What really tripped me up was the character that looked like a 6. I must have tried it at least 10 different ways (which was all wrong and tanked my accuracy), thinking there was some hidden trick to remove the “noise.”

  • Turns out, it was just plain leet speak (1337) where 6 should actually be read as 5. I don’t know if this is the intended way to deduce the solution, but it’s the best explanation I have.

  • scriptCTF{5ub7r4c7_7h3_n01s3}

Modulo

Modulo is so cool!

tbd.

Last updated