Logo

Writeup

Ralia ASIS CTF 2025 Finals — Writeup

0 0xAsta
December 28, 2025
6 min read
crypto ppc math

Ralia Challenge — Writeup

This writeup details the solution for the “Ralia” cryptography/PPC challenge from ASIS CTF 2025 Finals. The challenge involves interacting with a remote “weird machine” and solving a system of equations to generate integers that satisfy specific bit-length, primality, and perfect square constraints.

Summary

The target is a remote Python script accessible via netcat. To retrieve the flag, we must survive 19 levels of increasing difficulty.

In each level, the server provides a random bit length, bitel. We are required to provide three integers—xx, yy, and zz—that satisfy the following conditions:

  1. Bit Length: All three numbers (x,y,zx, y, z) must have exactly bitel bits.
  2. Primality: xx and zz must be prime numbers.
  3. Perfect Squares: The following expressions must result in perfect squares:
    • x+yx + y
    • y+z+1y + z + 1
    • z+x+5z + x + 5

Key Code Snippets

The core logic of the challenge is found in ralia.py.

ralia.py (Constraint Checking):

# The server parses our input
try:
x, y, z = [abs(int(_)) for _ in ans]
except:
die(border, f"The input you provided is not valid!")
_b = False
# The Mathematical Constraints
# 1. Check if the sums are perfect squares
if is_persquat(x + y) and is_persquat(y + z + 1) and is_persquat(z + x + 5):
# 2. Check if x and z are prime
if isPrime(x) and isPrime(z):
# 3. Check if they match the required bit length
if x.bit_length() == y.bit_length() == z.bit_length() == bitel:
_b = True

Analysis

This problem can be modeled as a system of linear equations. If we define the square roots of the required sums as integers AA, BB, and CC, we get:

x+y=A2y+z+1=B2    y+z=B21z+x+5=C2    z+x=C25\begin{align*} x + y &= A^2 \\ y + z + 1 &= B^2 \implies y + z = B^2 - 1 \\ z + x + 5 &= C^2 \implies z + x = C^2 - 5 \end{align*}

We have a system of 3 equations with 3 variables (xx, yy, zz). We can solve for xx, yy, zz algebraically in terms of AA, BB, CC.

Solving for xx: Sum equation (1) and (3), then subtract (2):

(x+y)+(z+x)(y+z)=A2+(C25)(B21)2x=A2B2+C24x=A2B2+C242(x+y)+(z+x)-(y+z) = A^2 + (C^2-5) - (B^2-1) \\ 2x = A^2 - B^2 + C^2 - 4 \\ x = \frac{A^2 - B^2 + C^2 - 4}{2}

Solving for yy: Sum equation (1) and (2), then subtract (3):

(x+y)+(y+z)(z+x)=A2+(B21)(C25)2y=A2+B2C2+4y=A2+B2C2+42(x+y)+(y+z)-(z+x) = A^2 + (B^2-1) - (C^2-5) \\ 2y = A^2 + B^2 - C^2 + 4 \\ y = \frac{A^2 + B^2 - C^2 + 4}{2}

Solving for zz: Sum equation (2) and (3), then subtract (1):

(y+z)+(z+x)(x+y)=(B21)+(C25)A22z=A2+B2+C26z=A2+B2+C262(y+z)+(z+x)-(x+y) = (B^2-1)+(C^2-5)-A^2 \\ 2z = -A^2 + B^2 + C^2 - 6 \\ z = \frac{-A^2 + B^2 + C^2 - 6}{2}

Integer Constraints

For xx, yy, zz to be integers, the numerators must be divisible by 2 (even). Looking at 2x=A2B2+C242x = A^2 - B^2 + C^2 - 4, since 4 is even, we need A2B2+C2A^2 - B^2 + C^2 to be even. In modular arithmetic modulo 2, n2nn^2 \equiv n. Therefore, the constraint simplifies to:

A+B+C0(mod2)A + B + C \equiv 0 \pmod{2}

The sum of the three roots must be even.

Exploitation and Flag Recovery

We cannot brute-force xx, yy, zz directly because finding primes that sum to squares is computationally expensive. However, generating xx, yy, zz from random values of AA, BB, CC is very fast.

1. Estimating the Range

We need x2bitelx \approx 2^{\text{bitel}}. Since x+y=A2x + y = A^2, and xyx \approx y, then 2xA22x \approx A^2. So, A22bitelA \approx \sqrt{2 \cdot 2^{\text{bitel}}}. This gives us the center of the search range for our random squares.

2. The Solver Script

The script performs the following steps for each level:

  • Calculates the search center based on bitel.
  • Generates random integers AA and BB near the center.
  • Calculates the required parity for CC (so that A+B+CA+B+C is even).
  • Iterates through possible values of CC.
  • Calculates xx, yy, zz and checks if they match the bitel and primality requirements.
#!/usr/bin/env python3
from pwn import *
from Crypto.Util.number import isPrime
from math import isqrt
import random
def solve_level(bitel):
# Calculate search center: sqrt(2 * 2^bitel)
center = isqrt(2 * (1 << bitel))
width = center // 20
while True:
# Pick A and B randomly around the target center
A = random.randint(center - width, center + width)
B = random.randint(center - width, center + width)
# C must satisfy (A + B + C) % 2 == 0
parity_target = (A + B) % 2
# Start searching for C
C_start = random.randint(center - width, center + width)
if C_start % 2 != parity_target:
C_start += 1
# Iterate C to find valid primes
for C in range(C_start, C_start + 5000, 2):
# Calculate numerators
num_x = A*A + C*C - B*B - 4
num_y = A*A + B*B - C*C + 4
num_z = B*B + C*C - A*A - 6
# Ensure positive results
if num_x <= 0 or num_y <= 0 or num_z <= 0: continue
x = num_x // 2
y = num_y // 2
z = num_z // 2
# Check bit length constraints
if x.bit_length() != bitel: continue
if y.bit_length() != bitel: continue
if z.bit_length() != bitel: continue
# Check primality (most expensive op, do last)
if isPrime(x) and isPrime(z):
return x, y, z
def main():
host = '65.109.194.34'
port = 13377
r = remote(host, port)
try:
while True:
line = r.recvline().decode().strip()
print(f"[Server] {line}")
if "Congratz" in line:
print(f"\n[+] Flag: {line}")
break
if "Please send" in line and "bitel =" in line:
parts = line.split()
try:
idx = parts.index("bitel")
bitel = int(parts[idx + 2].replace(":", ""))
print(f"[*] Solving for bitel: {bitel}...")
x, y, z = solve_level(bitel)
r.sendline(f"{x},{y},{z}".encode())
except ValueError:
print("[!] Error parsing")
finally:
r.close()
if __name__ == '__main__':
main()

3. Execution

We run the solver and wait for it to process all 19 levels.

Terminal window
[+] Opening connection to 65.109.194.34 on port 13377: Done
[Server] ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
...
[*] many levels and bitel are random for eac one and each level here but when solving level 19 you get the flag
[Server] Congratz! You got the flag: ASIS{n1C3_IMO_fr0m_4U5Tr4LiA!}

Final Result

Flag: ASIS{n1C3_IMO_fr0m_4U5Tr4LiA!}

Notes

The challenge demonstrates how arithmetic constraints can often be reversed using algebra to generate solutions efficiently, rather than filtering random numbers.

By controlling the generator variables (AA, BB, CC), we ensured the perfect square constraints were met implicitly, leaving only the primality check as the computational hurdle.