Scan zip for multiple threats
This commit is contained in:
parent
a38b5d4ec4
commit
88becd6097
4 changed files with 168 additions and 37 deletions
|
|
@ -6,7 +6,25 @@
|
||||||
#include "libmineziper_huffman_tree.h"
|
#include "libmineziper_huffman_tree.h"
|
||||||
#include "libmineziper_zip.h"
|
#include "libmineziper_zip.h"
|
||||||
|
|
||||||
int get_uncompressed_size(zip* in);
|
#define MAX_UNCOMPRESSED_SIZE 0x10000000
|
||||||
|
#define MAX_INT 0xffffffff
|
||||||
|
|
||||||
|
typedef struct data
|
||||||
|
{
|
||||||
|
unsigned int size;
|
||||||
|
void (*clean)(void*);
|
||||||
|
char* buffer;
|
||||||
|
} data;
|
||||||
|
|
||||||
|
int get_uncompressed_size(zip zip);
|
||||||
|
bool detect_overlaps(zip zip);
|
||||||
|
bool scan_decoded_files(zip zip);
|
||||||
bool scan_zip(char* zip_data, int zip_size);
|
bool scan_zip(char* zip_data, int zip_size);
|
||||||
|
|
||||||
|
static const char* sigs[] = {
|
||||||
|
"\x7f"
|
||||||
|
"ELF",
|
||||||
|
"MZ"};
|
||||||
|
static const int sigs_size = sizeof(sigs) / sizeof(char*);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -9,7 +9,8 @@
|
||||||
#define EOCD_SIG "PK\05\06"
|
#define EOCD_SIG "PK\05\06"
|
||||||
#define LFH_SIG "PK\03\04"
|
#define LFH_SIG "PK\03\04"
|
||||||
#define CDH_SIG "PK\01\02"
|
#define CDH_SIG "PK\01\02"
|
||||||
#define DEFLATE 8
|
#define COMP_NONE 0
|
||||||
|
#define COMP_DEFLATE 8
|
||||||
|
|
||||||
#define END_OF_BLOCK 256
|
#define END_OF_BLOCK 256
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,37 +5,89 @@
|
||||||
|
|
||||||
#include "libmineziper.h"
|
#include "libmineziper.h"
|
||||||
|
|
||||||
int get_uncompressed_size(zip* in)
|
int get_uncompressed_size(zip zip)
|
||||||
{
|
{
|
||||||
int size = 0;
|
unsigned int size = 0;
|
||||||
|
|
||||||
for (int i = 0; i < in->entries; i++)
|
for (int i = 0; i < zip.entries; i++)
|
||||||
{
|
{
|
||||||
LFH* lfh = &in->start[in->lfh_off[i]];
|
unsigned int file_size =
|
||||||
size += lfh->uncompressed_size;
|
((LFH*) zip.start + zip.lfh_off[i])->uncompressed_size;
|
||||||
|
|
||||||
|
if (size > MAX_INT / 2 && file_size > MAX_INT / 2)
|
||||||
|
{
|
||||||
|
return MAX_INT;
|
||||||
|
}
|
||||||
|
size += file_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool scan_zip(char* zip_data, int zip_size)
|
bool detect_overlaps(zip zip)
|
||||||
{
|
{
|
||||||
zip zip = init_zip(zip_data, zip_size);
|
// TODO sort only deals with char*
|
||||||
|
int* lfh_order = sort_int(zip.lfh_off, zip.entries);
|
||||||
|
bool overlap = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < zip.entries - 1; i++)
|
||||||
|
{
|
||||||
|
LFH* lfh = (LFH*) zip.start + zip.lfh_off[i];
|
||||||
|
unsigned int lf_size = sizeof(LFH) + lfh->filename_length +
|
||||||
|
lfh->extraf_length + lfh->compressed_size;
|
||||||
|
|
||||||
|
if (lf_size > zip.lfh_off[i + 1] - zip.lfh_off[i])
|
||||||
|
{
|
||||||
|
overlap = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(lfh_order);
|
||||||
|
return overlap;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scan_decoded_files(zip zip)
|
||||||
|
{
|
||||||
for (int i = 0; i < zip.entries; i++)
|
for (int i = 0; i < zip.entries; i++)
|
||||||
{
|
{
|
||||||
LFH* lfh = zip.start + zip.lfh_off[i];
|
LFH* lfh = (LFH*) (zip.start + zip.lfh_off[i]);
|
||||||
|
|
||||||
// Verify CDH/LFH parsed sizes to avoid undefined behavior
|
// Verify CDH/LFH parsed sizes to avoid undefined behavior
|
||||||
if (lfh->filename_length != zip.cdh_filename_length[i])
|
if (lfh->filename_length != zip.cdh_filename_length[i])
|
||||||
{
|
|
||||||
printf("[ERROR] Mismatch in CDH/LFH filename lengths.\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* decoded_data = "";
|
|
||||||
if (lfh->compression_method == DEFLATE)
|
|
||||||
{
|
{
|
||||||
|
printf("[ERROR] Mismatch in CDH/LFH filename lengths.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
data* decoded = malloc(sizeof(data));
|
||||||
|
decoded->buffer = decoded->clean = decoded->size = 0;
|
||||||
|
|
||||||
|
if (lfh->compression_method == COMP_NONE)
|
||||||
|
{
|
||||||
|
printf("[FILE %d] Scanning stored data...\n", i);
|
||||||
|
int lfh_length = sizeof(LFH) + lfh->filename_length + lfh->extraf_length;
|
||||||
|
char* block = &((char*) lfh)[lfh_length];
|
||||||
|
decoded->size = lfh->uncompressed_size;
|
||||||
|
|
||||||
|
decoded->buffer = (char*) malloc(decoded->size);
|
||||||
|
if (!decoded->buffer)
|
||||||
|
{
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"[FILE %d] Failed to allocate buffer. Skipping this block.\n",
|
||||||
|
i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
decoded->clean = &free;
|
||||||
|
|
||||||
|
memcpy(decoded->buffer, block, decoded->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (lfh->compression_method == COMP_DEFLATE)
|
||||||
|
{
|
||||||
|
printf("[FILE %d] Scanning first block of DEFLATED data...\n", i);
|
||||||
|
|
||||||
int lfh_length = sizeof(LFH) + lfh->filename_length + lfh->extraf_length;
|
int lfh_length = sizeof(LFH) + lfh->filename_length + lfh->extraf_length;
|
||||||
char* encoded_block = &((char*) lfh)[lfh_length];
|
char* encoded_block = &((char*) lfh)[lfh_length];
|
||||||
|
|
||||||
|
|
@ -46,54 +98,114 @@ bool scan_zip(char* zip_data, int zip_size)
|
||||||
if (deflate_header.block_type == 0)
|
if (deflate_header.block_type == 0)
|
||||||
{
|
{
|
||||||
align_to_next_byte(&bs);
|
align_to_next_byte(&bs);
|
||||||
short block_size = get_bits(&bs, 16);
|
decoded->size = get_bits(&bs, 16);
|
||||||
short inv_block_size = get_bits(&bs, 16);
|
short inv_block_size = get_bits(&bs, 16);
|
||||||
assert(block_size == ~inv_block_size);
|
if ((short) decoded->size != ~inv_block_size)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "-> Sizes mismatch in block type 0.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
decoded_data = malloc(block_size);
|
decoded->buffer = (char*) malloc(decoded->size);
|
||||||
|
if (!decoded->buffer)
|
||||||
|
{
|
||||||
|
fprintf(
|
||||||
|
stderr, "-> Failed to allocate buffer. Skipping this block.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
decoded->clean = &free;
|
||||||
|
|
||||||
memcpy(decoded_data, &bs.data[bs.current_data_offset], block_size);
|
memcpy(
|
||||||
|
decoded->buffer, &bs.data[bs.current_data_offset], decoded->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fixed Huffman Codes
|
// Fixed Huffman Codes
|
||||||
else if (deflate_header.block_type == 1)
|
else if (deflate_header.block_type == 1)
|
||||||
{
|
{
|
||||||
printf("[FILE %d] Scanning 1 block...\n", i);
|
decoded->size = lfh->uncompressed_size;
|
||||||
decoded_data = malloc(lfh->uncompressed_size);
|
|
||||||
|
|
||||||
decode_type1_block_vuln(&bs, decoded_data);
|
decoded->buffer = (char*) malloc(decoded->size);
|
||||||
|
if (!decoded->buffer)
|
||||||
|
{
|
||||||
|
fprintf(
|
||||||
|
stderr, "-> Failed to allocate buffer. Skipping this block.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
decoded->clean = &free;
|
||||||
|
|
||||||
|
decode_type1_block_vuln(&bs, decoded->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic Huffman Codes
|
// Dynamic Huffman Codes
|
||||||
else if (deflate_header.block_type == 2)
|
else if (deflate_header.block_type == 2)
|
||||||
{
|
{
|
||||||
fprintf(
|
fprintf(stderr, "-> Dynamic Huffman codes block type not supported\n");
|
||||||
stderr,
|
|
||||||
"[FILE %d] Dynamic Huffman codes block type not supported\n",
|
|
||||||
i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid type
|
// Invalid type
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, "[FILE %d] Error in compressed data\n", i);
|
fprintf(stderr, "-> Error in compressed data\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Unknown decompression algorithm. Skipping...\n");
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"[FILE %d] Unknown decompression algorithm. Skipping...\n",
|
||||||
|
i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the decoded data
|
if (decoded->size)
|
||||||
if (strcmp("VIRUS", decoded_data) == 0)
|
|
||||||
{
|
{
|
||||||
printf("-> VIRUS FOUND\n");
|
// Test magic bytes of the decoded data
|
||||||
return true;
|
unsigned int cmp_size;
|
||||||
|
for (int s = 0; s < sigs_size; s++)
|
||||||
|
{
|
||||||
|
cmp_size = (decoded->size < strlen(sigs[s])) ? decoded->size
|
||||||
|
: strlen(sigs[s]);
|
||||||
|
|
||||||
|
if (memcmp(sigs[s], decoded->buffer, cmp_size) == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// If allocated with malloc, free the buffer
|
||||||
|
if (decoded->clean)
|
||||||
{
|
{
|
||||||
printf("-> OK\n\n");
|
decoded->clean(decoded->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scan_zip(char* zip_data, int zip_size)
|
||||||
|
{
|
||||||
|
zip zip = init_zip(zip_data, zip_size);
|
||||||
|
|
||||||
|
if (get_uncompressed_size(zip) > MAX_UNCOMPRESSED_SIZE)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Uncompressed size too big. Could be a zip bomb.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (detect_overlaps(zip))
|
||||||
|
{
|
||||||
|
fprintf(
|
||||||
|
stderr, "Potential overlaps found in zip file. Could be a zip bomb.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scan_decoded_files(zip))
|
||||||
|
{
|
||||||
|
fprintf(
|
||||||
|
stderr, "Decompressed files looks malicious. Could contain a virus.\n");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ void main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (lfh->compression_method == DEFLATE)
|
if (lfh->compression_method == COMP_DEFLATE)
|
||||||
{
|
{
|
||||||
char* data = ((char*) lfh) + sizeof(LFH) + lfh->filename_length +
|
char* data = ((char*) lfh) + sizeof(LFH) + lfh->filename_length +
|
||||||
lfh->extraf_length;
|
lfh->extraf_length;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue