What is left unexplored?
Could we use these keys to attack features that were protected until then?
Two hacked 3DS running Luma3DS ❤
|
Alfa Network AWUS036NHA
|
||
To sniff and analyze packets...❤ |
IDA/Ghidra Whichever you like to reverse engineer CECD... |
59 59 30 00 AD DE AF BE 00 00 00 00 00 00 00 00
0C 10 B8 38 00 00 00 00
59 59 68 00 AD DE AF BE 00 00 00 00 00 00 00 00
0C 38 B8 38 00 00 00 00 63 63 00 00 43 00 00 00
00 00 00 00 10 00 00 00 1B 00 10 00 1A 80 0F 00
FF FF FF FF 74 54 12 00
59 59 56 00 AD DE AF BE 00 00 00 00 00 00 00 00
0C 38 B8 38 00 00 00 00 63 63 FF FF 12 00 00 00
00 00 00 00 16 00 00 00 11 0A 00 05 16 00 30 00
02 08 00 00 F0 08 A3 E1 84 BD C0 05 BF 49
header | data |
header magic value | data magic value |
59 59 30 00 AD DE AF BE 00 00 00 00 00 00 00 00
0C 10 B8 38 00 00 00 00
magic 0x5959 |
constants 0xdead 0xbeaf |
flags frame types |
ACK|PSH: 0 1 1 0 0 0 = 0x18
SYN|ACK: 0 1 0 0 1 0 = 0x12
ACK | PSH | RST | SYN | FIN |
63 63 00 00 43 00 00 00 00 00 00 00 10 00 00 00
1B 00 10 00 1A 80 0F 00 FF FF FF FF 74 54 12 00
61 61 00 00 80 30 00 00 00 16 05 00 00 00 00 00
01 00 00 00 60 60 00 00 6C 30 00 00 48 0D 00 00
04 23 00 00 00 16 05 00 00 00 00 00 00 00 00 00
00 00 00 00 12 1D AA 64 90 76 5A CB 00 00 ...
info packet share information like part of a handshake |
message box packet contains messages for a specific application! |
void copy_box_info_list(box_info_list* dst, box_info_list* src)
{
memcpy(dst, src, sizeof(box_info_list_header));
if(dst->header.magic == 0x6565)
{
dst->box_count = dst->header.count;
for(int i = 0; i < dst->box_count; i++)
memcpy(&dst->box_info[i], &src->box_info[i],
sizeof(box_info_entry));
}
}
They do not check the number of entries in the list!NX Bit | Stack Cookie | ASLR |
int load_TMP_file(tmp_box* dst, tmp_file* file_buffer,
size_t file_size) {
...
message* current_msg = file_buffer->messages;
while(dst->header.msg_count > i && end_of_file > current_msg) {
uint32_t msg_size = message_get_size(current_msg);
dst->msg_pointers[i] = current_msg;
dst->msg_sizes[i] = msg_size;
current_msg += msg_size;
i++;
glob_tmp_box_alloc_mode = POINTER_MODE; // POINTER_MODE = 0
}
dst->header.msg_count = i;
return 0;
}
They do not check the number of messages in the box!
int load_TMP_file(tmp_box* dst, tmp_file* file_buffer,
size_t file_size) {
...
message* current_msg = file_buffer->messages;
while(dst->header.msg_count > i && end_of_file > current_msg) {
uint32_t msg_size = message_get_size(current_msg);
dst->msg_pointers[i] = current_msg;
dst->msg_sizes[i] = msg_size;
current_msg += msg_size;
i++;
glob_tmp_box_alloc_mode = POINTER_MODE; // POINTER_MODE = 0
}
dst->header.msg_count = i;
return 0;
}
The "size" of the last message can be an arbitrary value!
void parse_all_TMP() {
[...]
tmp_box tmp_box;
critical_section* lock; // overwritten by overflow in parse_TMP_file!
[...]
for(int i = 0; i < TMP_file_count; i++) {
enter_critical_section(&lock, &global_lock); // restore lock value!
file_buffer = malloc(TMP_file_size[i]);
[...] // file reading, etc.
parse_TMP_file(&tmp_box, file_buffer, TMP_file_size[i]);
write_messages_from_tmp_box(...);
[...] // deleting file, etc.
free_tmp_box(tmp_box);
leave_critical_section(&lock); // lock = arbitrary value!
}
[...]
}
void leave_critical_section(critical_section** lock_ptr) {
*lock_ptr->count--;
[...] // actual unlocking code...
}
By overwriting lock_ptr we can decrement a value at an arbitrary address!
void free_tmp_box(tmp_box* box) {
if(box->header.msg_count && glob_tmp_box_alloc_mode != POINTER_MODE){
for(int i = 0; i < box->header.msg_count; i++) {
if(box->msg_pointers[i]) {
free(box->msg_pointers[i]);
box->msg_pointers[i] = NULL;
}
}
[...]
}
}
We can decrement glob_tmp_box_alloc_mode...
int load_TMP_file(tmp_box* dst, tmp_file* file_buffer,
size_t file_size) {
[...]
memcpy(dst, file_buffer, sizeof(tmp_box_header)); // copy header
if(dst->header.size != file_size) // if invalid size
return 0xC8E1086A; // return error
while(dst->header.msg_count > i && end_of_file > current_msg) {
[...]
glob_tmp_box_alloc_mode = POINTER_MODE;
}
[...]
}
We could bypass this by crafting an invalid header, but an error is returned...
void parse_all_TMP() {
[...]
tmp_box tmp_box;
critical_section* lock;
[...]
for(int i = 0; i < TMP_file_count; i++) {
enter_critical_section(&lock, &global_lock);
file_buffer = malloc(TMP_file_size[i]);
[...] // file reading, etc.
parse_TMP_file(&tmp_box, file_buffer, TMP_file_size[i]);
write_messages_from_tmp_box(...);
[...] // deleting file, etc.
free_tmp_box(tmp_box);
leave_critical_section(&lock);
}
[...]
}
...they do not check the return value anyway!
size_t index_file_size;
CECD_open_file(..., &size); //done through IPC
[...]
void* index_buffer = malloc(0x800); //what if size is > 0x800?
[...]
CECD_read_file(..., index_buffer, size, ...); //done through IPC
We are CECD now, we can provide a file larger than 0x800 bytes... and trigger a heap overflow!Access to the internet | ✓ |
Access to the SD card | ✓ |
Drawing on screen | ✓ |
... |
Twitter: | @MrNbaYoh |
Email: | mrnbayoh(at)gmail(dot)com |