Skip to content

Commit 195d9f8

Browse files
committed
Radius format improvements
In mode 1, crack all passwords immediately using a get_key() trick, but also try to avoid false positives (from incorrect shared key). Add a "cost" for mode [0=find secret 1=find password]. Also various optimizations and bug fixes.
1 parent 957d3a5 commit 195d9f8

File tree

1 file changed

+65
-40
lines changed

1 file changed

+65
-40
lines changed

src/radius_fmt_plug.c

Lines changed: 65 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
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+
*/
6067
static 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

7077
static char (*saved_key)[PLAINTEXT_LENGTH + 1];
78+
static char (*recovered_key)[MD5_DIGEST_LENGTH + 1];
7179
static int *saved_len;
7280
static int any_cracked, *cracked;
7381
static 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

97106
static 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

292306
static 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+
297318
struct 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

Comments
 (0)