Update crypto key search to find keys between blocks ##search (#17323)

This commit is contained in:
Sylvain Pelissier 2020-08-11 06:31:55 +02:00 committed by GitHub
parent a023fe210c
commit 2d53f7083b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 52 deletions

View File

@ -11,6 +11,9 @@ static int cmd_search(void *data, const char *input);
#define USE_EMULATION 0
#define AES_SEARCH_LENGTH 40
#define PRIVATE_KEY_SEARCH_LENGTH 11
static const char *help_msg_search_esil[] = {
"/E", " [esil-expr]", "search offsets matching a specific esil expression",
"/Ej", " [esil-expr]", "same as above but using the given magic file",
@ -40,7 +43,7 @@ static const char *help_msg_slash[] = {
"//", "", "repeat last search",
"/a", "[?][1aoditfmsltf] jmp eax", "assemble opcode and search its bytes",
"/b", "", "search backwards, command modifier, followed by other command",
"/c", "[ar]", "search for crypto materials",
"/c", "[?][adr]", "search for crypto materials",
"/d", " 101112", "search for a deltified sequence of bytes",
"/e", " /E.F/i", "match regular expression",
"/E", " esil-expr", "offset matching given esil expressions $$ = here",
@ -160,7 +163,6 @@ struct search_parameters {
const char *cmd_hit;
int outmode; // 0 or R_MODE_RADARE or R_MODE_JSON
bool inverse;
bool crypto_search;
bool aes_search;
bool privkey_search;
};
@ -2355,7 +2357,7 @@ static void do_string_search(RCore *core, RInterval search_itv, struct search_pa
if (param->inverse) {
core->search->maxhits = 1;
}
if (core->search->n_kws > 0 || param->crypto_search) {
if (core->search->n_kws > 0) {
/* set callback */
/* TODO: handle last block of data */
/* TODO: handle ^C */
@ -2399,10 +2401,10 @@ static void do_string_search(RCore *core, RInterval search_itv, struct search_pa
}
const ut64 from = itv.addr, to = r_itv_end (itv),
from1 = search->bckwrds ? to : from,
to1 = search->bckwrds ? from : to;
from1 = search->bckwrds? to: from,
to1 = search->bckwrds? from: to;
ut64 len;
for (at = from1; at != to1; at = search->bckwrds ? at - len : at + len) {
for (at = from1; at != to1; at = search->bckwrds? at - len: at + len) {
print_search_progress (at, to1, search->nhits, param);
if (r_cons_is_breaked ()) {
eprintf ("\n\n");
@ -2422,23 +2424,21 @@ static void do_string_search(RCore *core, RInterval search_itv, struct search_pa
}
(void)r_io_read_at (core->io, at, buf, len);
}
if (param->crypto_search) {
// TODO support backward search
int t = 0;
if (param->aes_search) {
t = r_search_aes_update (core->search, at, buf, len);
} else if (param->privkey_search) {
t = r_search_privkey_update (core->search, at, buf, len);
r_search_update (core->search, at, buf, len);
if (param->aes_search) {
// Adjust length to search between blocks.
if (len == core->blocksize) {
len -= AES_SEARCH_LENGTH - 1;
}
if (!t || t > 1) {
break;
}
} else {
(void)r_search_update (core->search, at, buf, len);
if (core->search->maxhits > 0 && core->search->nhits >= core->search->maxhits) {
goto done;
} else if (param->privkey_search) {
// Adjust length to search between blocks.
if (len == core->blocksize) {
len -= PRIVATE_KEY_SEARCH_LENGTH - 1;
}
}
if (core->search->maxhits > 0 && core->search->nhits >= core->search->maxhits) {
goto done;
}
}
print_search_progress (at, to1, search->nhits, param);
r_cons_clear_line (1);
@ -2447,7 +2447,7 @@ static void do_string_search(RCore *core, RInterval search_itv, struct search_pa
eprintf ("hits: %" PFMT64d "\n", search->nhits - saved_nhits);
}
}
done:
done:
r_cons_break_pop ();
free (buf);
} else {
@ -2929,7 +2929,6 @@ static int cmd_search(void *data, const char *input) {
.cmd_hit = r_config_get (core->config, "cmd.hit"),
.outmode = 0,
.inverse = false,
.crypto_search = false,
.aes_search = false,
.privkey_search = false,
};
@ -3271,7 +3270,6 @@ reread:
break;
case 'c': { // "/c"
dosearch = true;
param.crypto_search = true;
switch (input[1]) {
case 'c': // "/cc"
{
@ -3326,7 +3324,6 @@ reread:
break;
case 'd': // "cd"
{
param.crypto_search = false;
RSearchKeyword *kw;
kw = r_search_keyword_new_hex ("308200003082", "ffff0000ffff", NULL);
if (kw) {
@ -3343,6 +3340,9 @@ reread:
{
RSearchKeyword *kw;
kw = r_search_keyword_new_hexmask ("00", NULL);
// AES search is done over 40 bytes
kw->keyword_length = AES_SEARCH_LENGTH;
r_search_reset (core->search, R_SEARCH_AES);
r_search_kw_add (search, kw);
r_search_begin (core->search);
param.aes_search = true;
@ -3352,6 +3352,9 @@ reread:
{
RSearchKeyword *kw;
kw = r_search_keyword_new_hexmask ("00", NULL);
// Private key search is at least 11 bytes
kw->keyword_length = PRIVATE_KEY_SEARCH_LENGTH;
r_search_reset (core->search, R_SEARCH_PRIV_KEY);
r_search_kw_add (search, kw);
r_search_begin (core->search);
param.privkey_search = true;
@ -3359,7 +3362,6 @@ reread:
}
default: {
dosearch = false;
param.crypto_search = false;
r_core_cmd_help (core, help_msg_slash_c);
}
}

View File

@ -20,6 +20,7 @@ enum {
R_SEARCH_STRING,
R_SEARCH_XREFS,
R_SEARCH_AES,
R_SEARCH_PRIV_KEY,
R_SEARCH_DELTAKEY,
R_SEARCH_MAGIC,
R_SEARCH_LAST

View File

@ -12,6 +12,13 @@
#include <r_search.h>
#include <r_crypto/r_aes.h>
#define AES128_SEARCH_LENGTH 24
#define AES192_SEARCH_LENGTH 32
#define AES256_SEARCH_LENGTH 40
#define AES128_KEY_LENGTH 16
#define AES192_KEY_LENGTH 24
#define AES256_KEY_LENGTH 32
static bool aes256_key_test(const unsigned char *buf) {
bool word1 = buf[32] == (buf[0] ^ Sbox[buf[29]] ^ 1) \
&& buf[33] == (buf[1] ^ Sbox[buf[30]]) \
@ -49,31 +56,49 @@ static bool aes128_key_test(const unsigned char *buf) {
}
R_API int r_search_aes_update(RSearch *s, ut64 from, const ut8 *buf, int len) {
int i, last = len - 20;
int i, t, last = len - AES128_SEARCH_LENGTH;
RListIter *iter;
RSearchKeyword *kw;
const int old_nhits = s->nhits;
r_list_foreach (s->kws, iter, kw) {
if (last > 0) {
if (last >= 0) {
for (i = 0; i < last; i++) {
if (aes128_key_test (buf + i)) {
kw->keyword_length = 16;
return r_search_hit_new (s, kw, from + i);
}
if (len - i - 28 > 0) {
if (aes192_key_test (buf + i)) {
kw->keyword_length = 24;
return r_search_hit_new (s, kw, from + i);
kw->keyword_length = AES128_KEY_LENGTH;
t = r_search_hit_new (s, kw, from + i);
if (!t) {
return -1;
}
}
if (len - i - 36 > 0) {
if (aes256_key_test (buf + i)) {
kw->keyword_length = 32;
return r_search_hit_new (s, kw, from + i);
if (t > 1) {
return s->nhits - old_nhits;
}
i += AES128_SEARCH_LENGTH;
}
if (len - i - AES192_SEARCH_LENGTH >= 0 && aes192_key_test (buf + i)) {
kw->keyword_length = AES192_KEY_LENGTH;
t = r_search_hit_new (s, kw, from + i);
if (!t) {
return -1;
}
if (t > 1) {
return s->nhits - old_nhits;
}
i = i + AES192_SEARCH_LENGTH;
}
if (len - i - AES256_SEARCH_LENGTH >= 0 && aes256_key_test (buf + i)) {
kw->keyword_length = AES256_KEY_LENGTH;
t = r_search_hit_new (s, kw, from + i);
if (!t) {
return -1;
}
if (t > 1) {
return s->nhits - old_nhits;
}
i = i + AES256_SEARCH_LENGTH;
}
}
}
}
return -1;
}
}

View File

@ -5,6 +5,11 @@
#include <r_search.h>
/* The minimal length to perform a search is the sizes of
the sequence tag, the minimal length of the sequence,
the version marker and the minimal key length. */
#define PRIVKEY_SEARCH_MIN_LENGTH (1 + 1 + 4 + 1)
/*Baby BER parser, just good enough for private keys.
This is not robust to errors in the memory image, but if we added
@ -60,21 +65,24 @@ static int check_fields(const ut8 *start) {
// As defined in RFC 3447 for RSA, as defined in RFC 5915 for
// elliptic curves and as defined in 7 of RFC 8410 for SafeCurves
R_API int r_search_privkey_update(RSearch *s, ut64 from, const ut8 *buf, int len) {
int i, k, max, index;
int i, k, max, index, t;
RListIter *iter;
RSearchKeyword *kw;
const size_t old_nhits = s->nhits;
const ut8 rsa_versionmarker[] = { 0x02, 0x01, 0x00, 0x02 };
const ut8 ecc_versionmarker[] = { 0x02, 0x01, 0x01, 0x04 };
const ut8 safecurves_versionmarker[] = { 0x02, 0x01, 0x00, 0x30 };
if (len < sizeof (rsa_versionmarker)) {
if (len < PRIVKEY_SEARCH_MIN_LENGTH) {
return -1;
}
r_list_foreach (s->kws, iter, kw) {
for (i = 2; i < len - sizeof (rsa_versionmarker); i++) {
if (memcmp (&buf[i], rsa_versionmarker, sizeof (rsa_versionmarker)) &&
memcmp (&buf[i], ecc_versionmarker, sizeof (ecc_versionmarker)) &&
memcmp (&buf[i], safecurves_versionmarker, sizeof (safecurves_versionmarker))) {
// Iteration until the remaining length is too small to contain a key.
for (i = 2; i < len - PRIVKEY_SEARCH_MIN_LENGTH; i++) {
if (memcmp (buf + i, rsa_versionmarker, sizeof (rsa_versionmarker)) &&
memcmp (buf + i, ecc_versionmarker, sizeof (ecc_versionmarker)) &&
memcmp (buf + i, safecurves_versionmarker, sizeof (safecurves_versionmarker))) {
continue;
}
@ -98,7 +106,11 @@ R_API int r_search_privkey_update(RSearch *s, ut64 from, const ut8 *buf, int len
if (check_fields (buf + index)) {
parse_next_field(buf + index, &kw->keyword_length);
return r_search_hit_new (s, kw, from + index);
t = r_search_hit_new (s, kw, from + index);
if (t > 1) {
return s->nhits - old_nhits;
}
}
}
}

View File

@ -80,6 +80,7 @@ R_API int r_search_set_mode(RSearch *s, int mode) {
case R_SEARCH_KEYWORD: s->update = r_search_mybinparse_update; break;
case R_SEARCH_REGEXP: s->update = r_search_regexp_update; break;
case R_SEARCH_AES: s->update = r_search_aes_update; break;
case R_SEARCH_PRIV_KEY: s->update = r_search_privkey_update; break;
case R_SEARCH_STRING: s->update = r_search_strings_update; break;
case R_SEARCH_DELTAKEY: s->update = r_search_deltakey_update; break;
case R_SEARCH_MAGIC: s->update = r_search_magic_update; break;
@ -110,13 +111,14 @@ R_API int r_search_hit_new(RSearch *s, RSearchKeyword *kw, ut64 addr) {
if (!s->contiguous) {
if (kw->last && addr == kw->last) {
kw->count--;
kw->last = s->bckwrds ? addr : addr + kw->keyword_length;
kw->last = s->bckwrds? addr: addr + kw->keyword_length;
eprintf ("0x%08"PFMT64x" Sequential hit ignored.\n", addr);
return 1;
}
}
// kw->last is used by string search, the right endpoint of last matcch (forward search), to honor search.overlap
// kw->last is used by string search, the right endpoint of last match (forward search), to honor search.overlap
kw->last = s->bckwrds ? addr : addr + kw->keyword_length;
if (s->callback) {
int ret = s->callback (kw, s->user, addr);
kw->count++;
@ -474,9 +476,6 @@ R_API int r_search_update(RSearch *s, ut64 from, const ut8 *buf, long len) {
return 0;
}
ret = s->update (s, from, buf, len);
if (s->mode == R_SEARCH_AES) {
ret = R_MIN (R_SEARCH_AES_BOX_SIZE, len);
}
} else {
eprintf ("r_search_update: No search method defined\n");
}

View File

@ -152,6 +152,14 @@ EXPECT=<<EOF
EOF
RUN
NAME=cmd.hit for /ca
FILE=bins/other/aes_192.dump
CMDS=/ca
EXPECT=<<EOF
0x000000fa hit0_0 000102030405060708090a0b0c0d0e0f1011121314151617
EOF
RUN
NAME=cmd.hit for /cr
FILE=bins/other/rsa-private-4096.key
CMDS=/cr