103 lines
3.1 KiB
Markdown
103 lines
3.1 KiB
Markdown
# Reversible Logic
|
|
|
|
`Reversible Logic` is a cryptography challenge based on the properties of the XOR operation. We are provided with a service that encrypts our input using a hidden flag as the key.
|
|
|
|
## Information Gathering
|
|
|
|
We connect to the challenge service and are greeted with a prompt:
|
|
|
|
```
|
|
--- Secure XOR Encryption Service ---
|
|
Enter a message to encrypt:
|
|
```
|
|
|
|
The description states: "This program implements a simple XOR cipher using a hidden flag as the key."
|
|
|
|
Let's test it by sending a simple input, like "AAAA":
|
|
|
|
```
|
|
Enter a message to encrypt: AAAA
|
|
|
|
Encrypted Result (Hex): 3a272d20
|
|
```
|
|
|
|
## Vulnerability Analysis
|
|
|
|
The service implements a standard XOR cipher where:
|
|
$$Ciphertext = Plaintext \oplus Key$$
|
|
|
|
We control the **Plaintext** (our input) and we receive the **Ciphertext** (the hex output). The **Key** is the hidden flag we want to recover.
|
|
|
|
A fundamental property of the XOR operation is that it is its own inverse (reversible):
|
|
$$A \oplus B = C \implies C \oplus B = A$$
|
|
|
|
Therefore, we can recover the Key by XORing the Ciphertext with our Known Plaintext:
|
|
$$Key = Ciphertext \oplus Plaintext$$
|
|
|
|
To recover the full flag, we just need to send a plaintext that is at least as long as the flag. Since we don't know the exact length, sending a long string (e.g., 100 characters) ensures we cover it entirely.
|
|
|
|
## Solution
|
|
|
|
We can automate this process with a Python script:
|
|
1. Connect to the server.
|
|
2. Send a long string of known characters (e.g., 100 'A's).
|
|
3. Receive the hex-encoded ciphertext.
|
|
4. Decode the hex and XOR it with our string of 'A's to reveal the flag.
|
|
|
|
### Solver Script
|
|
|
|
```python
|
|
from pwn import *
|
|
|
|
# Set the log level so we can see the "Opening connection" messages
|
|
context.log_level = 'info'
|
|
|
|
def main():
|
|
# 1. Connect to the challenge instance
|
|
# (Matches the IP/Port from your previous message)
|
|
io = remote('127.0.0.1', 1315)
|
|
|
|
# 2. Handle the server prompts
|
|
# We read until the server asks for input
|
|
io.recvuntil(b"Enter a message to encrypt: ")
|
|
|
|
# 3. Send our "Known Plaintext"
|
|
# We send a long string of 'A's (0x41) to ensure we capture the full flag.
|
|
# If the flag is longer than 100 chars, just increase this number.
|
|
plaintext = b"A" * 100
|
|
io.sendline(plaintext)
|
|
|
|
# 4. Receive the response
|
|
io.recvuntil(b"Encrypted Result (Hex): ")
|
|
|
|
# Read the hex string line and strip whitespace/newlines
|
|
hex_output = io.recvline().strip().decode()
|
|
|
|
log.info(f"Received Hex Ciphertext: {hex_output}")
|
|
|
|
# 5. Decode the Hex
|
|
cipher_bytes = bytes.fromhex(hex_output)
|
|
|
|
# 6. XOR to recover the key
|
|
# pwntools has a built-in xor() function that is very robust.
|
|
# Logic: Key = Cipher ^ Plaintext
|
|
recovered_key = xor(cipher_bytes, plaintext)
|
|
|
|
# 7. Output the Flag
|
|
# We use 'errors=ignore' just in case of weird bytes,
|
|
# but for a text flag it should be clean.
|
|
log.success(f"Recovered Flag: {recovered_key.decode('utf-8', errors='ignore')}")
|
|
|
|
io.close()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
```
|
|
|
|
### Execution
|
|
|
|
Running the logic manually or via script reveals the flag.
|
|
`{flag: xor_logic_is_reversible_123}`
|
|
|
|
```
|