$ cat writeup.md…
$ cat writeup.md…
HackTheBox
ELF 64-bit LSB PIE executable, x86-64, dynamically linked, with debug_info, not stripped. Rust binary `rauth`, compiled with Rust 1.47.0, implements authentication by encrypting the password with Salsa20 algorithm and comparing it to a reference ciphertext. A remote service is also provided at `154.
My implementation of authentication mechanisms in C turned out to be failures. But my implementation in Rust is unbreakable. Can you retrieve my password?
ELF 64-bit LSB PIE executable, x86-64, dynamically linked, with debug_info, not stripped. Rust binary rauth, compiled with Rust 1.47.0, implements authentication by encrypting the password with Salsa20 algorithm and comparing it to a reference ciphertext. A remote service is also provided at 154.57.164.83:30723.
$ file rauth ELF 64-bit LSB PIE executable, x86-64, dynamically linked, with debug_info, not stripped $ strings rauth | grep -i salsa salsa20-0.8.0 cipher-0.3.0 $ strings rauth | grep -E "password|auth|flag" Welcome to secure login portal! Enter the password to access the system: Successfully Authenticated You entered a wrong password! Flag:
The binary is not stripped and contains debug_info — this significantly simplifies analysis. The strings show references to salsa20-0.8.0 and cipher-0.3.0 crates, which immediately points to the encryption algorithm.
Suspicious hex string in .rodata: ef39f4f20e76e33bd25f4db338e81b10
$ nm rauth | grep -E "salsa|rauth" 0000000000006460 T _ZN5rauth4main17h7d7aed61ae7734f4E _ZN7salsa204core13Core$LT$R$GT$3new17h06163fbcdf79ba51E _ZN79_$LT$salsa20..salsa..Salsa$LT$R$GT$..cipher..stream..StreamCipher$GT$19try_apply_keystream17hdbdc0561b68e3b6aE
Key functions:
rauth::main @ 0x6460 — main logicSalsa20::Core::new(key, nonce) — cipher initializationStreamCipher::try_apply_keystream — applying keystream (encryption/decryption)Execution flow of main:
.rodata at address 0x39ca0: ASCII string ef39f4f20e76e33bd25f4db338e81b10 (used as raw key bytes, not as hex)movabsq $0x3361303732633464 @ 0x65e3 → little-endian ASCII d4c270a3Salsa20::Core::new(key, nonce) @ 0x6652try_apply_keystream @ 0x6759.rodata @ 0x39cc0, comparison via SSE instructions pcmpeqb + pmovmskb (byte-wise SIMD comparison)Key (32 bytes @ 0x39ca0): b'ef39f4f20e76e33bd25f4db338e81b10' (ASCII hex string as raw bytes)
Nonce (8 bytes, immediate): b'd4c270a3' (ASCII string as raw bytes)
Ciphertext (32 bytes @ 0x39cc0): 05055fb1a329a8d558d9f556a6cb31f324432a31c99dec72e33eb66f62ad1bf9
Important observation: the key is an ASCII string of hex characters (32 characters = 32 bytes), not decoded 16 bytes. The Rust salsa20 crate accepts &[u8; 32], and the program passes the string as-is.
Salsa20 is a stream cipher. Encryption and decryption are the same operation: XOR with keystream. If encrypt(plaintext) = ciphertext, then encrypt(ciphertext) = plaintext. Therefore, to recover the password, we just need to "encrypt" the reference ciphertext with the same key and nonce.
#!/usr/bin/env python3 """ RAuth — Salsa20 password recovery Decrypt the expected ciphertext to recover the password. """ from Crypto.Cipher import Salsa20 # Extracted from binary .rodata and immediates key = b'ef39f4f20e76e33bd25f4db338e81b10' # 32 bytes ASCII (raw key) nonce = b'd4c270a3' # 8 bytes ASCII (raw nonce) ct = bytes.fromhex('05055fb1a329a8d558d9f556a6cb31f324432a31c99dec72e33eb66f62ad1bf9') cipher = Salsa20.new(key=key, nonce=nonce) password = cipher.decrypt(ct) print(f"Password: {password.decode()}") # Output: TheCrucialRustEngineering@2021;)
$ echo 'TheCrucialRustEngineering@2021;)' | nc 154.57.164.83 30723 Welcome to secure login portal! Enter the password to access the system: Successfully Authenticated Flag: "HTB{REDACTED}"
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar