Let's do something simple. This media unit has a Wifi access point and looks like remote access is possible. But only if you know the right password.
The python (garbage) scripts are doing pretty good right now.
I setup an idb starting with the hex file and auto-analysis disabled.
The codatify scripts didn't make 100% sense though when applied because there were many 16-bit elements in RAM which did not align on 16bit address boundaries.
After importing function names/comments using bindiff (it's actually working pretty well), I started looking at the strings.
There are many strings in here that are also in the full compromise challenge.
Then I started poking at main
. There is an obvious series of branches which would lead to printing 'your flag is'. But it's not clear one could ever get the code to take that path. And... it's an exploitation challenge. So that's probably not the point anyways.
ldi r22, 0x42 ; 'B'
ROM:0A7F ldi r23, 0x21 ; '!' ; aYourFlagIs
ROM:0A80 call j_usart_print
ROM:0A82 call prob_safe_get_rand
ROM:0A84 ldd r24, Y+0x1D
ROM:0A85 cpi r24, 0x3F ; '?'
ROM:0A86 breq loc_A8A
ROM:0A87 call sub_3B6
ROM:0A89 rjmp loc_AA9
ROM:0A8A ; ---------------------------------------------------------------------------
ROM:0A8A
ROM:0A8A loc_A8A: ; CODE XREF: main+102j
ROM:0A8A call prob_safe_get_rand
ROM:0A8C ldd r24, Y+0x1D
ROM:0A8D cpi r24, 0x3F ; '?'
ROM:0A8E breq loc_A92
ROM:0A8F call sub_3B6
ROM:0A91 rjmp loc_AA9
ROM:0A92 ; ---------------------------------------------------------------------------
ROM:0A92
ROM:0A92 loc_A92: ; CODE XREF: main+10Aj
ROM:0A92 call prob_safe_get_rand
ROM:0A94 movw r24, YL
ROM:0A95 subi r24, 0x19
ROM:0A96 sbci r25, -1
ROM:0A97 movw ZL, r24
ROM:0A98 ld r24, Z
ROM:0A99 cpi r24, 1
ROM:0A9A breq loc_A9E
ROM:0A9B call sub_3B6
ROM:0A9D rjmp loc_AA9
ROM:0A9E ; ---------------------------------------------------------------------------
ROM:0A9E
ROM:0A9E loc_A9E: ; CODE XREF: main+116j
ROM:0A9E lds r24, fn_ptr_ptr_for247A
ROM:0AA0 lds r25, fn_ptr_ptr_for247A+1
ROM:0AA2 call sub_247A ; does eicall rx24
The function sub_247A
prepares some arguments and then does a dispatch to a function point in globals of RAM
I found this function pointer pointer which would contain the address to the print flag function.
RAM:183A fn_ptr_ptr_for247A_0:.byte 2 ; DATA XREF: sub_68A+2DAt
RAM:183A ; fn ptr ptr to print flag function
Maybe part of the challenge is to get the challenge binary to print or otherwise load this function address and jump to it.
I got an updated full compromise idb from Jonathan and used bindiff to import some function names.
I poked around the write eeprom functions and noticed that detect fault injection is writing a flag to address 0 of the eeprom.
I found also that what was marked as eeprom_mapen is actually implementing a read of eeprom address
I annotated all the callsites of the write and read of eeprom with the addresses to/from and the values when known. Eeprom is being used to preserve RNG test parameters and also to preserve FI tests.
The code that is testing someting and then optionally calling what I'm pretty sure is a flag printer, ROM:068A test_something_and_set_flag_printer
is referring to malloc'd buffers of 100 34 byte structures; the last of which points to a 9byte buffer that gets the contents 'backdoor' as setup by ROM:05DD setup_100_34b_structs_and_one_backdoor
The function that reads in characters up to an expected terminator takes a maximum size; it won't let you overflow the buffer that is passed to it.
When we supply input, it wants the line to be of the form [name_length]:[password_length]:[name][password]
. We can't supply more than 200 bytes to read_str_until
; but we can set the size of both name and password buffers when parsed... maybe?
Not really sure about this AVR construct; there is a mix of places in the code where carry is handled in subtraction of immediates from 16bit variables stored in pairs of registers. Sometimes with a subci -1
like below; sometimes not. I'm guess that the net effect is as I marked up in the comments but I can't convince myself that it is
movw r22, YL
subi r22, -0x1F
sbci r23, -1 ; buffer = Y+0x1f
parse_and_maybe_set_flag_printer(input){
input: Yx+0x4B
first_colon: Yx+2
saved_position: Yx+0x4A
first_colon = strchr(input, ':') || return -1
saved_position = 0
saved_position ^= 1
second_colon: Yx+4
second_colon = strchr(first_colon + 1, ':') || return -1
saved_position ^= 2
next_dash: Yx+6
next_dash = strchr(input, '-')
if (!next_dash)
next_dash > second_colon || return -1
saved_position ^= 4
first_colon_distance: Yx+8
first_colon_distance = first_colon - input - 1
weird_thing: Yx+0xA
weird_thing = 0
other_thing: Yx+0xC
other_thing = first_colon
first_colon_buffer = alloca(-(first_colon_distance + 1)) + 1
first_colon_buffer: Y+0xE
saved_position ^= 8
distance_between_colons: Y+0x10
distance_between_colons = second_colon - first_colon - 1
high_test: r0
high_test = (distance_between_colons & 0xff00) >> 8
high_test = high_test << 1
ya_weird_thing: Y+0x12
ya_weird_thing = 0
ya_distance_between_colons: Y+0x14
ya_distance_between_colons = distance_between_colons
second_colon_buffer = alloca(-(distance_between_colons + 1)) + 1
second_colon_buffer: Y+0x16
memcpy(first_colon_buffer, input, first_colon_distance)
first_colon_buffer[first_colon_distance] = 0
place_1: Y+0x28
first_number: Y+0x1A
after_1: Y+0x18
first_number = strtoi(first_colon_buffer, place_1, 10)
after_1=place_1
saved_position ^= 16
memcpy(second_colon_buffer, first_colon, distance_between_colons)
second_colon_buffer[distance_between_colons] = 0
second_number: Y+0x1E
after_2: Y+0x1C
second_number = strtoi(second_colon_buffer, place_1, 10)
after_2 = place_1
get_valid_rand(illegal_rand=saved_position)
saved_position ^= 32
before_second_digit_end = second_digit_end - 1
later_buffer = alloca(-second_digit_end) + 1
memcpy(name_field_buffer, input + first_colon_distance + distance_between_colons + 2, first_digit_end)
if (saved_position != 0x3f)
die_and_remember
get_valid_rand(saved_position) // saved_position == 0x3f
y = 100
ret = 0
while (y>0)
{
get_valid_rand(saved_position)
saved_position = 0
//mallocd_ptr: global
rx24 = ( ((Y+saved_position)&0xff00 >> 8) + high_test) << 8 | high_test
if (mallocd_ptr[rx24 + 32] == 0)
continue
...
y--;
}
return ret
}
... later_buffer = X - second_digit_end + 1
This function is called by the parse_and_maybe_set_flag_printer
function above. At this point I couldn't handle keeping track of Y+NN
anymore so I wrote a stack-variable making script basing Y as the stack pointer (which avr-gcc appears to use).
get_valid_rand(illegal_rand) {
word last_rand;
for (i=0; i<= 0xff; i++)
last_rand = prob_get_rand();
if (last_rand == illegal_rand) {
illegal_rand = last_rand;
for (i=0; i<0x400; i++) {
last_rand = prob_get_rand();
}
if (last_rand == illegal_rand)
die();
}
if (illegal_rand > 0x21 ) {
illegal_rand =- 0x30;
illegal_rand[H] = -1 * (illegal_rand[L] << 1 - illegal_rand[L] << 1);
illegal_rand[L] = illegal_rand[L] << 2;
}
illegal_rand_copy = illegal_rand;
j=0;
while(j < illegal_rand && illegal_rand_copy != 0) {
busy_mux();
j++;
busy_mux();
illegal_rand_copy--;
}
if (j != illegal_rand || illegal_rand_copy == 0)
die_and_remember();
busy_mux();
return illegal_rand_copy;
}
Imported 6 or so functions from Jonathan's work in the other challenges
parse_and_maybe_set_flag_printer(input){
input: Yx+0x4B
first_colon: Yx+2
saved_position: Yx+0x4A
first_colon = strchr(input, ':') || return -1
saved_position = 0
saved_position ^= 1
second_colon: Yx+4
second_colon = strchr(first_colon + 1, ':') || return -1
saved_position ^= 2
next_dash: Yx+6
next_dash = strchr(input, '-')
if (!next_dash) {
if (next_dash >= second_colon)
return -1;
}
saved_position ^= 4
first_colon_distance: Yx+8
first_colon_distance = first_colon - input - 1
weird_thing: Yx+0xA
weird_thing = 0
other_thing: Yx+0xC
other_thing = first_colon
first_colon_buffer = alloca(-(first_colon_distance + 1)) + 1
first_colon_buffer: Y+0xE
saved_position ^= 8
distance_between_colons: Y+0x10
distance_between_colons = second_colon - first_colon - 1
high_test: r0
high_test = (distance_between_colons & 0xff00) >> 8
high_test = high_test << 1
ya_weird_thing: Y+0x12
ya_weird_thing = 0
ya_distance_between_colons: Y+0x14
ya_distance_between_colons = distance_between_colons
second_colon_buffer = alloca(-(distance_between_colons + 1)) + 1
second_colon_buffer: Y+0x16
memcpy(first_colon_buffer, input, first_colon_distance)
first_colon_buffer[first_colon_distance] = 0
place_1: Y+0x28
first_number: Y+0x1A
after_1: Y+0x18
first_number = strtoi(first_colon_buffer, place_1, 10)
after_1=place_1
saved_position ^= 16
memcpy(second_colon_buffer, first_colon, distance_between_colons)
second_colon_buffer[distance_between_colons] = 0
second_number: Y+0x1E
after_2: Y+0x1C
second_number = strtoi(second_colon_buffer, place_1, 10)
after_2 = place_1
get_valid_rand(illegal_rand=saved_position)
saved_position ^= 32
before_second_digit_end = second_digit_end - 1
later_buffer = alloca(-second_digit_end) + 1
memcpy(name_field_buffer, input + first_colon_distance + distance_between_colons + 2, (uint16_t) first_digit_end)
if (saved_position != 0x3f)
die_and_remember
get_valid_rand(saved_position) // saved_position == 0x3f
y = 100
ret = 0
while (true)
{
if (y < 0) {
return 2 ; badUser
}
get_valid_rand(saved_position);
saved_position = 0;
//mallocd_ptr: global
if (mallocd_ptr[y * 34 + 32] == 0)
continue;
if ( (uint32_t) strlen(mallocd_prt[y * 34 + 32]) != (uint32_t) first_digit)
continue;
if ( !strncmp(mallocd_ptr[y * 34 + 32], name_field_buffer, (uint16_t) first_digit))
continue;
if ( saved_position != 0 )
die_and_remember();
get_valid_rand(saved_position);
saved_position++;
memcpy(later_buffer, input + first_colon_distance + distance_between_colons + first_digit + 2, second_digit);
saved_position++;
//hash: Y+42 (len == 32)
prob_sha56_pbkdf(hash, later_buffer, work_factor = second_digit * 8);
saved_position++;
get_valid_rand(saved_position);
if (saved_position == 3) {
if( strncmp(mallocd_ptr[y * 34], hash, 32) ) {
be_150_of_die_rng_artefact = 18 + 150;
}
}
get_valid_rand(saved_position);
saved_position++;
get_valid_rand(saved_position);
if (saved_position != 4)
return 2; /badPassword;
if( strncmp(mallocd_ptr[y * 34], hash, 32) ) {
saved_position++;
get_valid_rand(saved_position);
if (saved_position != 5)
die_and_remember();
be_150_of_die_rng_artefact -= 18;
fn_ptr_ptr_for247A = usart_send_byte_USARTC0;
}
y--;
}
return ret
}
setup_100_structs_and_one_backdoor() { y == 100; while (y > 0) { mallocd_ptr + y34 + 32 = 0 mallocd_ptr + y34 + 33 = 0 }
starts_at_99 = 99;
memcpy(arg_0, copied_array, 32);
memcpy(malloc_ptr + 99 * 34, arg_0, 32);
mallocd_ptr + 99 * 34 + 32 = malloc(9);
memcpy(mallocd_ptr + 99 * 34 + 32, 'backdoor', 9);
}
char copied_array[32] = {
0x55, 0x03, 0x0C, 0x34, 0x9F, 0xC9, 0x5E, 0x13,
0x85, 0x93, 0x5E, 0xA2, 0x33, 0x66, 0xB5, 0xA9,
0x99, 0x45, 0xD8, 0xBF, 0x35, 0xD3, 0x72, 0xC3,
0xAA, 0x72, 0x2B, 0xB9, 0x74, 0x92, 0xCA, 0x26
}
void setup_100_structs_and_one_backdoor(void) {
char y1; // Y+1
char arg_0[32]; // Y+2..33 - end of frame
starts_at_100 = 100;
mallocd_ptr = malloc(starts_at_100 * 34);
for (y1 = 0; y1 < starts_at_100; y1++) {
Z = mallocd_ptr[y1 * 34];
Z[32:33] = NULL;
}
starts_at_99 = 99;
for (r24 = 32; r24 != 0; r24--) {
arg_0[r24] = copied_array[r24];
}
for (r24 = 32; r24 != 0; r24--) {
mallocd_ptr[starts_at_99*34 + r24] = arg_0[r24];
}
Z = mallocd_ptr[starts_at_99 * 34]
Z[32:33] = malloc(9);
for (r18 = 9; r18 != 0; r18--) {
Z[32:33][r18] = "backdoor"[r18];
}
}
char parse_and_maybe_set_flag_printer(char *input) {
// stack frame 76
char rop_check; // alias saved_position
long first_digit; // "digit" is a misnomer, should be first number
long second_digit; // "digit" is a misnomer, should be second number
// input is shadowed at Y+0x4b..0x4c
rx16 = $sp;
first_colon = strchr(input, ':');
if (first_colon == NULL) return -1;
second_colon = strchr(first_colon, ':');
if (second_colon == NULL) return -1;
rx14 = $sp;
next_dash = strchr(input, '-');
if (next_dash != NULL && next_dash < second_colon) return -1;
/* first block */ {
first_colon_distance = first_colon - input;
weird_thing = (first_colon_distance < 0); // length overflow test?
other_thing = (short)((long)first_colon_distance + 1) - 1;
first_colon_buffer = stack_alloc(first_colon_distance + 1);
distance_between_colons = second_colon - first_colon - 1;
// repeat of the BS above
second_colon_buffer = stack_alloc(first_colon_distance + 1);
memcpy(first_colon_buffer, input, first_colon_distance);
first_colon_buffer[first_colon_distance] = '\0';
first_digit = strtol(first_colon_buffer, &place_1, 10);
memcpy(second_colon_buffer, first_colon, distance_between_colons);
second_colon_buffer[distance_between_colons] = '\0';
second_digit = strtol(second_colon_buffer, &place_1, 10);
}
// if (rop_check != 0x1f) die_and_remember();
/* second block */ {
second_digit_less_one = (short)second_digit - 1;
later_buffer = stack_alloc( (short)second_digit );
first_digit_less_one = (short)first_digit - 1;
name_field_buffer = stack_alloc( (short)first_digit );
memcpy(name_field_buffer, input[first_colon_distance + distance_between_colons + 2], (short)first_digit);
}
// if (rop_check != 0x3f) die_and_remember();
for (y1 = starts_at_100; y1 > 0; y1--) {
// rop_check = 0;
Z = mallocd_ptr[y1 * 34];
if (Z[32:33] == NULL) continue;
if (first_digit != (long)strlen(Z[32:33])) continue;
if (0 != strncmp(Z[32:33], name_field_buffer, first_digit)) continue;
// if (rop_check != 0x0) die_and_remember();
// rop_check += 1
memcpy(later_buffer, input[first_colon_distance + distance_between_colons + 2 + first_digit], (short)second_digit);
// rop_check += 1
prob_sha256_pbkdf(hash, later_buffer, (long)second_digit*8);
// rop_check += 1
if (rop_check == 0x3 && 0 == strncmp(mallocd_ptr[y1*34], hash, 32)) {
be_150_or_die_rng_artefact = 18+150;
}
// rop_check += 1
// if (rop_check != 4) return 2; // bad password
if (0 != strncmp(mallocd_ptr[y1*34], hash, 32)) return 2; // bad password
// rop_check += 1
// if (rop_check != 5) die_and_remember();
be_150_or_die_rng_artefact -= 18;
fn_ptr_ptr_for_247A = *usart_send_byte_USARTC0;
return 1;
}
return 0; // unknown user
}
void main(void) {
// stack frame: 231
char y1[14]; // Y+0x1..0xe
char y15[14]; // Y+0xf..0x1c
char checks; // Y+0x1d
char mask; // Y+0x1e
char y1f[200]; // Y+0x1f..0xe6
char result; // Y+0xe7
// bunch of init
printf("Initializing...");
// bunch of RNG tests
printf("Initialized");
setup_100_structs_and_one_backdoor();
mask = 0;
while (true) {
read_str_until(y1f, 200);
result = parse_and_maybe_set_flag_printer(y1f);
if (result==1) {
if (be_150_or_die_rng_artefact != 150) die();
mask = 0xff;
// if (checks!=7) die_and_remember();
set_flag_mask(mask);
// if (checks!=0xf) die_and_remember();
}
if (result==255) {
printf("Expected format: [name_length]:[password_length]:[name][password");
} else if (result==0) {
printf("Unknown user!");
} else if (result==2) {
printf("Wrong password!");
} else if (result==1) {
if (be_150_or_die_rng_artefact != 150) die();
printf("Your flag is:");
// if (checks!=0x3f) die_and_remember();
// if (checks!=0x3f) die_and_remember();
// if (result!=1) die_and_remember();
demask_and_print_flag();
return 0;
}
}
}
void prob_sha256_pbkdf(char *hash, char *buffer, long lenT8) {
// stack frame 44
char state[36]; // Y+0x01..0x24
char *hash; // Y+0x25..0x26
char *buffer; // Y+0x27..0x28
long lenT8; // Y+0x29..0x2c -- end of frame
sha256_init_state(state); // h0, 0x00000000
while (true) {
erx24 = lenT8;
if (lenT8 >= 0x200) { // length >= 64
sub_b46(state, buffer);
buffer += 0x40;
lenT8 -= 0x200;
} else {
sub_ed3(state, lenT8);
sub_1110(hash, state);
return;
}
}
}
break: 0x09f4: read_str_until - buffer at rx22 == 0x3f32,200 0x184e: usart_print - buffer at rx22 0x10c3: prob_sha256_pbkdf - below 0x0810: in parse_and_maybe_set_flag_printer, delivery
Delays: time between "Initializing..." and "Initialized" too slow patching out: 0x26da: cf93 -> 0895: jj_test_rng_rx24_times 0x2b84: ff92 -> 0895: prob_more_rng_tests 0x08da: cf93 -> 0895: get_valid_rand
ready and waiting...
Initializing...
Initialized
2018-04-30 10:04:05 -0400
prompt found, proceeding...
00000000 33 32 3a 33 37 33 34 3a d9 fb 92 e3 bb e6 5b e1 │32:3│734:│····│··[·│
00000010 f1 aa d4 a8 2e ef 45 67 f7 a1 eb e2 cd 11 0c 80 │····│.·Eg│····│····│
00000020 49 b9 69 8b e7 a7 0c 88 │I·i·│····│
00000028
32:3734:......[.......Eg........I.i.....
Unknown user!
2018-04-30 10:04:21 -0400
00000000 38 3a 31 30 3a 62 61 63 6b 64 6f 6f 72 6f 70 65 │8:10│:bac│kdoo│rope│
00000010 6e 73 65 73 61 6d 65 │nses│ame│
00000017
8:10:backdooropensesame
Your flag is:
18c495dbe625cd39544fc6e3bab81a2d