← Back to Writeups
CUSTOMMediumCrypto

Напиши мне

XESXOR9/10/20263 min read
#crypto#custom#medium#known_plaintext_attack#shift_cipher_break

Напиши мне

Platform: hackerlab | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-01-21 | Status: Solved Techniques: known_plaintext_attack, shift_cipher_break

Summary

Task: Encryption service uses shift cipher with same key per session. Solution: Known-plaintext attack - send known character, calculate key from encrypted response, decrypt the flag.

Напиши мне — hackerlab

Description

Вы хотите связаться со мной? Используйте данный сервис для шифрования сообщений, которые адресованы мне, пожалуйста. Они слушают и следят за нами.

IP: 62.173.140.174:11004

Analysis

When connecting to the service:

  1. A banner is displayed with "Пример зашифрованного сообщения" (encrypted message example) — this is the encrypted flag
  2. The service allows encrypting arbitrary messages
  3. Encryption is a simple shift of Unicode codes by a random key
  4. The key is the same for the entire session ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Vulnerability: Classic shift cipher (Caesar cipher for Unicode) with the ability to encrypt arbitrary text — ideal conditions for a Known-Plaintext Attack.

Solution

Attack Idea

  1. Send a known character (e.g., 'A')
  2. Receive the encrypted result
  3. Calculate the key: key = ord(encrypted_A) - ord('A')
  4. Decrypt the example: decrypted = chr(ord(c) - key) for each character

Exploit

python
#!/usr/bin/env python3
"""
Solution for "Напиши мне" (hackerlab CTF)

Vulnerability: Simple shift cipher with known plaintext
Attack: Known-plaintext attack - send a known character, calculate the key
"""
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

import socket
import re

def solve():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('62.173.140.174', 11004))
    s.settimeout(5)
    
    # Receive banner
    data = b""
    while True:
        try:
            chunk = s.recv(4096)
            if not chunk:
                break
            data += chunk
            if b"> " in data:
                break
        except:
            break
    
    banner = data.decode('utf-8')
    
    # Extract encrypted message example (this is the flag!)
    match = re.search(r'Пример зашифрованного сообщения: ([^\n.]+)', banner)
    if not match:
        print("[-] Could not find encrypted example")
        return None
    
    encrypted_example = match.group(1).strip()
    if encrypted_example.endswith('.'):
        encrypted_example = encrypted_example[:-1]
    
    # Send 'A' to find the key
    s.send(b"A\n")
    
    # Receive response
    data = b""
    while True:
        try:
            chunk = s.recv(4096)
            if not chunk:
                break
            data += chunk
            if b"> " in data:
                break
        except:
            break
    
    response = data.decode('utf-8')
    
    # Extract encrypted character
    match2 = re.search(r'< ([^\n]+)', response)
    if not match2:
        print("[-] Could not find encrypted 'A'")
        return None
    
    enc_A = match2.group(1).strip()
    
    if len(enc_A) < 1:
        print("[-] Empty encrypted response")
        return None
    
    # Calculate key: enc(A) = A + key => key = enc(A) - A
    key = ord(enc_A[0]) - ord('A')
    
    # Decrypt example: dec(c) = c - key
    decrypted = ""
    for c in encrypted_example:
        dec_char = chr(ord(c) - key)
        decrypted += dec_char
    
    s.close()
    return decrypted
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

if __name__ == "__main__":
    flag = solve()
    if flag:
        print(f"[+] FLAG: {flag}")
    else:
        print("[-] Failed to solve")

Theory

Shift Cipher (Caesar Cipher):

  • Each character is shifted by a fixed value: E(m) = m + k
  • Decryption: D(c) = c - k
  • Works the same for Unicode as for ASCII

Known-Plaintext Attack:

  • If we know a pair (plaintext, ciphertext), we can calculate the key
  • For shift cipher: key = ciphertext - plaintext
  • A single character is enough to fully break the cipher ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍