44 * http://www.untruth.org/~josh/security/radius/radius-auth.html
55 *
66 * This software is Copyright (c) 2018, Dhiru Kholia <dhiru [at] openwall.com>,
7+ * and (c) 2025 magnum,
78 * and it is hereby released to the general public under the following terms:
89 *
910 * Redistribution and use in source and binary forms, with or without
@@ -35,6 +36,7 @@ john_register_one(&fmt_radius);
3536#include "params.h"
3637#include "options.h"
3738#include "md5.h"
39+ #include "unicode.h"
3840
3941#define FORMAT_LABEL "radius"
4042#define FORMAT_NAME "RADIUS authentication"
@@ -57,6 +59,11 @@ john_register_one(&fmt_radius);
5759#define MD5_DIGEST_LENGTH 16
5860#endif
5961
62+ /*
63+ * 0 -> attack shared secret (3.3), 1 -> attack user password
64+ * password2 is the shared secret, Passw0rd2 is the password.
65+ * Hashes are <authenticator>*<ciphertext>.
66+ */
6067static struct fmt_tests tests [] = {
6168 // tshark -q -Xlua_script:network2john.lua -X lua_script1:Passw0rd2 -X lua_script1:0 -r freeradius.pcap
6269 {"$radius$1*0*Passw0rd2*acf627289be7e214c8e328d0d01fcea2*fe18a76eec4abeeea09eb90e99b359f7" , "password2" },
@@ -68,6 +75,7 @@ static struct fmt_tests tests[] = {
6875};
6976
7077static char (* saved_key )[PLAINTEXT_LENGTH + 1 ];
78+ static char (* recovered_key )[MD5_DIGEST_LENGTH + 1 ];
7179static int * saved_len ;
7280static int any_cracked , * cracked ;
7381static size_t cracked_size ;
@@ -88,6 +96,7 @@ static void init(struct fmt_main *self)
8896 omp_autotune (self , OMP_SCALE );
8997
9098 saved_key = mem_calloc (sizeof (* saved_key ), self -> params .max_keys_per_crypt );
99+ recovered_key = mem_calloc (sizeof (* recovered_key ), self -> params .max_keys_per_crypt );
91100 saved_len = mem_calloc (self -> params .max_keys_per_crypt , sizeof (* saved_len ));
92101 cracked_size = sizeof (* cracked ) * self -> params .max_keys_per_crypt ;
93102 any_cracked = 0 ;
@@ -96,6 +105,7 @@ static void init(struct fmt_main *self)
96105
97106static void done (void )
98107{
108+ MEM_FREE (recovered_key );
99109 MEM_FREE (saved_key );
100110 MEM_FREE (saved_len );
101111 MEM_FREE (cracked );
@@ -188,52 +198,56 @@ static int check_password(int index, struct custom_salt *cs)
188198{
189199 int i ;
190200
191- if (cs -> mode == 1 ) { // recover user password
192- unsigned char out [16 ], rec [17 ];
201+ if (cs -> mode == 1 ) {
202+ int has_8bit = 0 ;
203+
204+ /*
205+ * Just recover (decode) user password.
206+ * precalculated_digest = md5(secret.authenticator)
207+ */
208+ for (i = 0 ; i < MD5_DIGEST_LENGTH ; i ++ ) {
209+ char c = cs -> ciphertext [i ] ^ cs -> precalculated_digest [i ];
210+ if (!c )
211+ break ;
212+ else if (c < ' ' || c == 0x7f ) {
213+ recovered_key [index ][0 ] = 0 ;
214+ return 0 ;
215+ }
216+ else if (c >= 0x80 )
217+ has_8bit = 1 ;
218+ recovered_key [index ][i ] = c ;
219+ }
220+ recovered_key [index ][i ] = 0 ;
193221
194- memset (out , 0 , 16 );
195- memcpy (out , saved_key [index ], saved_len [index ] > 16 ? 16 : saved_len [index ]);
196- for (i = 0 ; i < 16 ; i ++ )
197- rec [i ] = cs -> ciphertext [i ] ^ cs -> precalculated_digest [i ];
198- if (!memcmp (out , rec , 16 ))
222+ /*
223+ * Try to avoid some false negatives (from incorrect shared secret).
224+ */
225+ if (!has_8bit )
226+ return 1 ;
227+ else if (options .target_enc == UTF_8 )
228+ return valid_utf8 ((UTF8 * )recovered_key [index ]);
229+ else if (options .target_enc != ASCII )
199230 return 1 ;
200231
201- /*
202- * This is silly: we could just output all plaintext passwords, but we have no
203- * mechanism to get them into john.pot from here. Arguably, this isn't a task
204- * for JtR at all.
205- */
206- if (!bench_or_test_running ) {
207- #ifdef _OPENMP
208- #pragma omp critical
209- #endif
210- {
211- static int rec_count = 0 ;
212- int rec_max = 10 ;
213- rec_count ++ ;
214- if (rec_count <= rec_max ) {
215- rec [16 ] = 0 ;
216- printf ("%s: Recovered password '%s'\n" , FORMAT_LABEL , (char * )rec );
217- } else if (rec_count == rec_max + 1 ) {
218- printf ("%s: Further messages suppressed\n" , FORMAT_LABEL );
219- }
220- }
221- }
222- } else if (cs -> mode == 0 ) { // recover shared secret
232+ recovered_key [index ][0 ] = 0 ;
233+ return 0 ;
234+ } else if (cs -> mode == 0 ) { // 3.3 recover shared secret
223235 MD5_CTX ctx ;
224- unsigned char digest [16 ];
225- unsigned char out [16 ];
236+ unsigned char out [MD5_DIGEST_LENGTH ];
237+
238+ /* Clear any remains of previous mode 1 crack */
239+ recovered_key [index ][0 ] = 0 ;
226240
227241 MD5_Init (& ctx );
228242 MD5_Update (& ctx , saved_key [index ], saved_len [index ]);
229243 MD5_Update (& ctx , cs -> authenticator , cs -> authenticator_len );
230- MD5_Final (digest , & ctx );
231- memset ( out , 0 , 16 );
232- memcpy ( out , cs -> secret_or_password , cs -> secret_or_password_len > 16 ? 16 : cs -> secret_or_password_len );
233- for ( i = 0 ; i < 16 ; i ++ )
234- out [ i ] = out [ i ] ^ digest [ i ] ;
235- if (! memcmp ( out , cs -> ciphertext , 16 ))
236- return 1 ;
244+ MD5_Final (out , & ctx );
245+
246+ for ( i = 0 ; i < MD5_DIGEST_LENGTH ; i ++ )
247+ if (( out [ i ] ^ cs -> secret_or_password [ i ]) != cs -> ciphertext [ i ] )
248+ return 0 ;
249+
250+ return 1 ;
237251 }
238252
239253 return 0 ;
@@ -291,9 +305,16 @@ static void set_key(char *key, int index)
291305
292306static char * get_key (int index )
293307{
308+ if (* recovered_key [index ])
309+ return recovered_key [index ];
310+
294311 return saved_key [index ];
295312}
296313
314+ static unsigned int mode_cost (void * salt ) {
315+ return ((struct custom_salt * )salt )-> mode ;
316+ }
317+
297318struct fmt_main fmt_radius = {
298319 {
299320 FORMAT_LABEL ,
@@ -310,7 +331,9 @@ struct fmt_main fmt_radius = {
310331 MIN_KEYS_PER_CRYPT ,
311332 MAX_KEYS_PER_CRYPT ,
312333 FMT_CASE | FMT_8_BIT | FMT_OMP ,
313- { NULL },
334+ {
335+ "mode [0=find secret 1=find password]"
336+ },
314337 { FORMAT_TAG },
315338 tests
316339 }, {
@@ -322,7 +345,9 @@ struct fmt_main fmt_radius = {
322345 fmt_default_split ,
323346 fmt_default_binary ,
324347 get_salt ,
325- { NULL },
348+ {
349+ mode_cost
350+ },
326351 fmt_default_source ,
327352 {
328353 fmt_default_binary_hash
0 commit comments