Тихий страж (Silent Guardian)
Тихий страж (Silent Guardian)
Platform: bug-makers | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-05-27 | Status: Solved Techniques: xor_decryption, static_string_extraction, pipe_fd_redirection, rodata_extraction
Summary
Task: stripped ELF binary that creates a pipe, redirects to fd 69, reads a password, and XOR-decrypts the flag. Solution: extract encrypted data and XOR key from .rodata, perform repeating-key XOR decryption statically.
Silent Guardian — bug-makers CTF
Description
В глубинах системы, затерянный среди множества других, дремлет одинокий процесс. Он не говорит и не слушает обычные пути. Его единственная цель — хранить тайну и ждать того, кто знает волшебное слово. Шепни ему на ухо правильную команду, и он откроет тебе свои секреты.
English summary: A lonely process hides deep in the system. It doesn't speak or listen through ordinary paths. Its only purpose is to guard a secret and wait for someone who knows the magic word. Whisper the right command in its ear, and it will reveal its secrets. Given: a single ELF binary silent_guardian.
Analysis
Initial Recon
$ file silent_guardian
ELF 64-bit LSB PIE executable, x86-64, dynamically linked (musl libc), stripped
The binary is stripped and linked against musl libc (not glibc). Running strings reveals key artifacts:
/flag— the password stringYnJ1aA==— a base64-encoded string (decodes to "bruh"), but used as a raw XOR key- Function names in imports:
pipe,dup2,read,strcmp,putchar,malloc,close,exit
Disassembly (Ghidra/radare2)
The binary's logic follows five stages:
-
Pipe creation: Calls
pipe()to create a pipe, yielding two file descriptors (read endpipefd[0]and write endpipefd[1]). -
FD redirection: Calls
dup2(pipefd[0], 69)— redirects the pipe's read end to file descriptor 69 (0x45). This is the "unusual path" referenced in the description. The binary doesn't read from stdin (fd 0) at all. -
Close original: Closes the original
pipefd[0]since fd 69 now points to the same pipe. -
Password check loop: Reads up to 99 bytes from fd 69 via
read(69, buffer, 0x63), strips trailing newline, then compares with the string"/flag"usingstrcmp(). If the input doesn't match, it loops back. If it matches, proceeds to flag output. -
Flag decryption: Allocates memory, XOR-decrypts 24 bytes of encrypted data from
.rodatausing the key"YnJ1aA=="(8 bytes, repeating), outputs each character viaputchar(), frees memory, and exits.
Key Insight: Base64 String as Raw Key
The string "YnJ1aA==" is valid base64 that decodes to "bruh", but the binary uses the raw ASCII bytes 59 6e 4a 31 61 41 3d 3d as the XOR key — it never calls any base64 decode function. This is a deliberate misdirection.
Extracted Data
From the binary's .rodata section:
| Item | File Offset | Hex | ASCII |
|---|---|---|---|
| XOR key | 0x2018 | 59 6e 4a 31 61 41 3d 3d | YnJ1aA== |
| Encrypted flag | 0x2030 | 1b 1b 2d 72 35 07 46 7b 1d 31 7c 08 3e 08 6e 62 0d 26 0f 6e 2a 04 64 40 | (binary) |
Solution
The flag can be recovered entirely through static analysis — no need to run the binary or interact with fd 69.
Extract the encrypted bytes and XOR key from .rodata, then perform repeating-key XOR:
python read-only • copy allowed#!/usr/bin/env python3 """ Silent Guardian - Static XOR Decryption The binary XOR-encrypts the flag with the raw string "YnJ1aA==" (not base64-decoded). Extract both from .rodata and decrypt. """ # Encrypted flag data from binary offset 0x2030 (24 bytes) encrypted_flag = bytes.fromhex('1b1b2d723507467b1d317c083e086e620d260f6e2a046440') # XOR key from binary offset 0x2018 - raw base64 string, NOT decoded key = b"YnJ1aA==" # Repeating-key XOR decryption decrypted = bytes([encrypted_flag[i] ^ key[i % len(key)] for i in range(len(encrypted_flag))]) print(decrypted.decode()) # BugCTF{REDACTED}
Verification
B = 0x1b ^ 0x59 = 0x42 ('B')
u = 0x1b ^ 0x6e = 0x75 ('u')
g = 0x2d ^ 0x4a = 0x67 ('g')
C = 0x72 ^ 0x31 = 0x43 ('C')
T = 0x35 ^ 0x61 = 0x54 ('T')
F = 0x07 ^ 0x41 = 0x46 ('F')
{ = 0x46 ^ 0x3d = 0x7b ('{')
F = 0x7b ^ 0x3d = 0x46 ('F')
...
The flag is self-referential: REDACTED tells you that file descriptor 69 was the key to understanding the binary's communication mechanism.