← Back to Writeups
CUSTOMEasyCrypto

Esoteric Language?

XESXOR9/10/20264 min read
#crypto#custom#easy#block_permutation#lcg_keystream#rotate_left_right#sum_optimization#xor_decryption

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

  1. EsotericLanguage.txt - 1C:Enterprise code implementing the encryption algorithm
  2. Records.txt - 5 encrypted records with partially masked parameters

Analysis

Encryption Algorithm (1C:Enterprise)

The code implements a custom encryption scheme: ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  1. Seed generation: SeedFromKeyMaterial(Code, Ref, Created) - creates seed from concatenated parameters, using the sum of ASCII codes multiplied by 2654435761 modulo 2^31

  2. LCG keystream: Uses Linear Congruential Generator with parameters:

    • a = 1103515245
    • c = 12345
    • m = 2^31
  3. Byte-by-byte encryption:

    • XOR plaintext byte with keystream byte (extracted as (seed >> 17) & 0xFF)
    • Rotate left by variable amount
  4. Block permutation: Groups of 3 bytes are permuted (b1,b2,b3 -> b3,b2,b1) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  5. 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
import 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
def 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

RecordFlag Part
1grodno{
2obvious
3ly_devi
4ls_lan
5guage}

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

  1. When seed depends on character sum, optimize search through delta values
  2. 1C:Enterprise code can be analyzed despite unusual syntax
  3. Splitting flag across multiple records is a typical CTF pattern
  4. Block permutation by 3 bytes is related to Base64 (3 bytes -> 4 characters) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍