Esoteric Language?
Esoteric Language?
Platform: grodno_new_year_2026 | Category: Crypto | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-01-10 | Status: Solved Techniques: block_permutation, lcg_keystream, rotate_left_right, sum_optimization, xor_decryption
Summary
Task: Custom encryption implemented in 1C:Enterprise code using LCG keystream, XOR, rotation, and block permutation, with partially masked parameters across 5 records. Solution: Exploited that the seed depends only on the sum of characters (not positions) to optimize bruteforce through delta values, decrypting all 5 flag parts.
Esoteric Language? - Grodno New Year CTF 2026
Description
Author: @vvanuss
Можно ли назвать язык программирования системы 1С:Предприятие эзотерическим языком?
Can the programming language of the 1C:Enterprise system be called an esoteric language?
Files
EsotericLanguage.txt- 1C:Enterprise code implementing the encryption algorithmRecords.txt- 5 encrypted records with partially masked parameters
Analysis
Encryption Algorithm (1C:Enterprise)
The code implements a custom encryption scheme:
-
Seed generation:
SeedFromKeyMaterial(Code, Ref, Created)- creates seed from concatenated parameters, using the sum of ASCII codes multiplied by 2654435761 modulo 2^31 -
LCG keystream: Uses Linear Congruential Generator with parameters:
- a = 1103515245
- c = 12345
- m = 2^31
-
Byte-by-byte encryption:
- XOR plaintext byte with keystream byte (extracted as
(seed >> 17) & 0xFF) - Rotate left by variable amount
- XOR plaintext byte with keystream byte (extracted as
-
Block permutation: Groups of 3 bytes are permuted (b1,b2,b3 -> b3,b2,b1)
-
Base64 encoding
Records Analysis
5 records with partially masked data:
- Record 1: Full timestamp, only 3 hex characters of Ref masked
- Record 2: Timestamp up to hour (MMSS missing), 3 hex characters masked
- Record 3: Timestamp up to day (HHMMSS missing), 3 hex characters masked
- Record 4: Timestamp up to day, 3 hex characters masked
- Record 5: Timestamp up to hour, 3 hex characters masked
Key Optimization
Discovered that seed depends only on the sum of characters in key_material, not their positions. This allows searching by delta values instead of brute-forcing all combinations.
Solution
Step 1: Decryption Implementation
python read-only • copy allowedimport base64 def rotate_right_byte(byte, shift): """Reverse operation of rotate_left""" if shift == 0: return byte return (byte >> shift) | ((byte << (8 - shift)) & 0xFF) def decrypt_with_delta(code, ref_masked, created_base, cipher_b64, orig_len, ref_delta, time_delta): cipher_bytes = list(base64.b64decode(cipher_b64)) # Reverse block permutation (b3,b2,b1 -> b1,b2,b3) bytes_temp = [] j = 0 while j < len(cipher_bytes): b3 = cipher_bytes[j] if j < len(cipher_bytes) else 0 b2 = cipher_bytes[j+1] if j+1 < len(cipher_bytes) else 0 b1 = cipher_bytes[j+2] if j+2 < len(cipher_bytes) else 0 bytes_temp.append(b1) bytes_temp.append(b2) bytes_temp.append(b3) j += 3 bytes_temp = bytes_temp[:orig_len] # Calculate seed key_mat = code.strip() + "|" + ref_masked + "|" + created_base total_sum = sum(ord(c) for c in key_mat) + ref_delta + time_delta seed = (total_sum * 2654435761) % (2 ** 31) # LCG parameters a, c, m = 1103515245, 12345, 2 ** 31 # Initial value of prev_plain prev_plain = (sum(ord(c) for c in ref_masked) + ref_delta) % 256 # Decryption plain_bytes = [] for i in range(len(bytes_temp)): seed = (a * seed + c) % m ks = (seed >> 17) & 0xFF shift = (len(code) * 3 + (prev_plain & 7) + i) % 8 x = rotate_right_byte(bytes_temp[i], shift) plain_byte = x ^ ks plain_bytes.append(plain_byte) prev_plain = plain_byte return bytes(plain_bytes)
Step 2: Parameter Bruteforce
For each record, iterate through ref_delta and time_delta values, checking if the result is readable ASCII text:
python read-only • copy alloweddef is_valid_flag_part(data): """Check for valid flag part""" try: text = data.decode('ascii') return all(c.isprintable() or c in '\n\r\t' for c in text) except: return False def bruteforce_record(code, ref_masked, created_base, cipher_b64, orig_len, expected_pattern=None): """Bruteforce delta values for a record""" # ref_delta: 3 hex chars = 0x000 - 0xFFF (sum 0-45 for each char) # time_delta: depends on time mask for ref_delta in range(0, 46*3 + 1): # Max sum of 3 hex chars for time_delta in range(0, 86400): # Seconds in a day result = decrypt_with_delta(code, ref_masked, created_base, cipher_b64, orig_len, ref_delta, time_delta) if is_valid_flag_part(result): if expected_pattern is None or expected_pattern in result.decode('ascii'): return result, ref_delta, time_delta return None, None, None
Step 3: Found Flag Parts
| Record | Flag Part |
|---|---|
| 1 | grodno{ |
| 2 | obvious |
| 3 | ly_devi |
| 4 | ls_lan |
| 5 | guage} |
Flag Meaning
"Obviously devil's language" - a wordplay hinting that the 1C:Enterprise programming language can indeed be called an "esoteric" or "devilish" language, answering the question from the task description.
Tools
- Python 3
- Base64 decoding
- Custom bruteforce script
Lessons
- When seed depends on character sum, optimize search through delta values
- 1C:Enterprise code can be analyzed despite unusual syntax
- Splitting flag across multiple records is a typical CTF pattern
- Block permutation by 3 bytes is related to Base64 (3 bytes -> 4 characters)