HTB Reversing Writeup: RAuth

Another straightforward reversing challenge writeup.

My implementation of authentication mechanisms in C turned out to be failures. But my implementation in Rust is unbreakable. Can you retrieve my password?

This challenge gives us a binary to play with, but also has a remote instance. This means we’ll have to use the binary to work out how to pwn it, and then perform the exploit on the remote. The usual step 1: run the binary, and see what checksec says:

» ./rauth
Welcome to secure login portal!
Enter the password to access the system: 
p4$$w0rd
You entered a wrong password!
» checksec ./rauth       
[*] '/home/░░░/src/hackthebox/re/RAuth/rauth'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

So we have a login field, and we need to figure out how to convince it we’re authenticated. The description suggests we’re actually trying to find the password, rather than trying to find a way to sidestep the check. Firing up r2 and inspecting main shows a bunch of reference to salsa20.

» r2 -e bin.cache=true -AAA -c 'afl~main' rauth
<...>
0x00006460   48   1756 sym.rauth::main::h7d7aed61ae7734f4
0x00006bd0    1     36 main
[0x00005510]> s 0x6460
[0x00006460]> pds 1000
0x00000040 std::io::stdio::LOCAL_STDERR::__getit::__KEY::h55971ca06aadbf8e
0x00000060 arg3
0x00000068 arg3 std::sys_common::thread_info::THREAD_INFO::__getit::__KEY::hf9db2bb34e0285b1
0x000000a0 std::panicking::panic_count::LOCAL_PANIC_COUNT::__getit::__KEY::h90abb3c6a9811087
0x00006460 rauth::main::h7d7aed61ae7734f4
0x000064b3 "AWAVATSH\x81\xec\xc8"
0x000064c2 call rbx
0x000064c4 "\nEnter the password to access the system: \nFailed to read input"
0x00006507 call rbx
0x0000651c call qword [dbg.stdin]
0x0000653f call qword [dbg.read_line]
0x00006562 int64_t arg1
0x0000656a call sym alloc::sync::Arc<T>::drop_slow::h574c4d6eea32be79
0x000065a0 call qword [dbg.clone]
0x000065c3 "ef39f4f20e76e33bd25f4db338e81b10\x05\x05_\xb1\xa3)\xa8\xd5X\xd9\xf5V\xa6\xcb1\xf3$C*1\u025d\xecr\xe3>\xb6ob\xad\x1b\xf9 "
0x000065f5 fcn.00000020 fcn.00000020
0x000065ff call qword [sym.__rust_alloc]
0x0000661b "$C*1\u025d\xecr\xe3>\xb6ob\xad\x1b\xf9 "
0x0000662e fcn.00000020 fcn.00000020
0x0000664a int64_t arg3
0x00006652 call sym salsa20::core::Core<R>::new::h06163fbcdf79ba51
0x000066e6 call qword [sym.__rust_alloc]
0x000066f9 call qword [dbg.handle_alloc_error]
0x00006719 int64_t arg1
0x0000671e int64_t arg2
0x00006720 uint32_t arg3
0x00006723 call sym alloc::raw_vec::RawVec<T,A>::reserve::h37e7c2e36f33cad8
0x0000673b call qword [reloc.memcpy]
0x00006749 int64_t arg2
0x0000674e int64_t arg1
0x00006756 uint32_t arg3
0x00006759 call sym <salsa20::salsa::Salsa<R> as cipher::stream::StreamCipher>::try_apply_keystream::hdbdc0561b68e3b6a
0x0000677d call qword [sym.__rust_alloc]
0x00006790 call qword [dbg.handle_alloc_error]
0x000067af int64_t arg1
0x000067b2 int64_t arg2
0x000067b4 uint32_t arg3
0x000067b7 call sym alloc::raw_vec::RawVec<T,A>::reserve::h37e7c2e36f33cad8
0x000067ce call qword [reloc.memcpy]
0x000067e0 fcn.00000020 fcn.00000020
0x00006838 call qword [sym.__rust_dealloc]
[0x00039ca0]> afl~salsa
0x000056b0    2    106 sym.salsa20::core::Core_R_::apply_keystream::hef17b96ea3e4062c
0x00005900    1    149 sym.salsa20::core::Core_R_::new::h06163fbcdf79ba51
0x000059a0    3    769 sym._ZN7salsa204core13Core_LT_R_GT_6rounds17hefc4fe96e1017fc7E.llvm.10452565349845262793
0x00005d10   82   1553 sym._salsa20::salsa::Salsa_R__as_cipher::stream::StreamCipher_::try_apply_keystream::hdbdc0561b68e3b6a

Google tells us this is an encryption scheme. With the symbols we’re seeing, it’s reasonable to assume they’re using the rust-crypto crate (more on this later). Let’s see if we can work out how the calls are being used. Rust comes with a GDB wrapper which gives us extra info about rust variables. We boot into that, and see what our arguments are at the time of the calls to salsa20 functions. This also means we don’t have to manually account for PIE, which is nice.

» rust-gdb ./rauth           
GNU gdb (GDB) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
92 commands loaded for GDB 12.1 using Python engine 3.10
Reading symbols from ./rauth...
gef➤  b salsa20::core::Core<R>::new 
Breakpoint 1 at 0x5900
gef➤  b salsa20::core::Core<R>::apply_keystream 
Breakpoint 2 at 0x56b0
gef➤  r
Starting program: /home/░░░/src/hackthebox/re/RAuth/rauth 
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Welcome to secure login portal!
Enter the password to access the system: 
abcdefg

And we hit breakpoint 1. Here are the registers:

$rax   : 0x000055555564fd30  →  0xd5a829a3b15f0505
$rbx   : 0x0000555555408530  →  <std::io::stdio::_print+0> push r15
$rcx   : 0x000055555564fd30  →  0xd5a829a3b15f0505
$rdx   : 0x00007fffffffd790  →  0x3361303732633464 ("d4c270a3"?)
$rsp   : 0x00007fffffffd6c8  →  0x0000555555406657  →  <rauth::main+503> movdqu xmm0, XMMWORD PTR [rsp]
$rbp   : 0x1               
$rsi   : 0x00007fffffffd760  →  0x3266346639336665 ("ef39f4f2"?)
$rdi   : 0x00007fffffffd6d0  →  0x0000000000000000
$rip   : 0x0000555555405900  →  <salsa20::core::Core<R>::new+0> sub rsp, 0x40
$r8    : 0x00007ffff7f4caa8  →  0x0000000000000000
$r9    : 0x30              
$r10   : 0x8080808080808080
$r11   : 0x1               
$r12   : 0x0               
$r13   : 0x0000555555439e28  →  "Flag: \nsrc/main.rsYou entered a wrong password!\n[...]"
$r14   : 0x0000555555649090  →  0x0000555555405cf0  →  <_ZN4core3ptr13drop_in_place17h0da78d1a234d5b45E.llvm.15711927856082507290+0> ret 
$r15   : 0x000055555564fd30  →  0xd5a829a3b15f0505
$eflags: [zero carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]

$rdx and $rsi look interesting; salsa20 is a cipher, so from the docs of rust-crypto we can expect these to be key and nonce.

gef➤  x/s $rdx
0x7fffffffd790:	"d4c270a3\020\375dUUU"
gef➤  x/s $rsi
0x7fffffffd760:	"ef39f4f20e76e33bd25f4db338e81b10p\332dUUU"

With our extracted key and nonce, we want to know how to decrypt them. This is where things got a little annoying for me. My first thought was to just pull the same cargo crate the author of the challenge would have used, and use that. I assumed it was this one, but when I’d written a decryptor, I got panics because the nonce was the wrong length. Shopping around for other options, the top few results had specific requirements on the key or nonce length which our values here don’t meet. For example, this crate looked promising, but requires a 32-byte key; this one accept a 16-byte key, but require an 8-byte nonce. I ended up turning to a python implementation instead (from PyCryptodome), which didn’t seem to care.

» python3                 
Python 3.10.9 (main, ░░░) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from Crypto.Cipher import Salsa20
>>> ciphertext = "0505 5fb1 a329 a8d5 58d9 f556 a6cb 31f3 2443 2a31 c99d ec72 e33e b66f 62ad 1bf9"
>>> Salsa20.new(key=b"ef39f4f20e76e33bd25f4db338e81b10", nonce=b"d4c270a3").decrypt(bytes.fromhex(ciphertext.replace(' ', '')))
b'TheCrucialRustEngineering@2021;)'

Passing this to our local testing binary gives us a (fake) flag. We initialise the remote, open a channel to it, and send off our decrypted password to get the real key.

» nc 178.62.8.249 31592
Welcome to secure login portal!
Enter the password to access the system: 
TheCrucialRustEngineering@2021;)
Successfully Authenticated
Flag: "HTB{░░░}"