Successful Zip Bomb attacks occur when an application expands untrusted archive files without controlling the size of the expanded data, which can
lead to denial of service. A Zip bomb is usually a malicious archive file of a few kilobytes of compressed data but turned into gigabytes of
uncompressed data. To achieve this extreme compression ratio, attackers will
compress irrelevant data (eg: a long string of repeated bytes).
Ask Yourself Whether
Archives to expand are untrusted and:
- There is no validation of the number of entries in the archive.
- There is no validation of the total size of the uncompressed data.
- There is no validation of the ratio between the compressed and uncompressed archive entry.
There is a risk if you answered yes to any of those questions.
Recommended Secure Coding Practices
- Define and control the threshold for maximum total size of the uncompressed data.
- Count the number of file entries extracted from the archive and abort the extraction if their number is greater than a predefined threshold, in
particular it’s not recommended to recursively expand archives (an entry of an archive could be also an archive).
Sensitive Code Example
#include <archive.h>
#include <archive_entry.h>
// ...
void f(const char *filename, int flags) {
struct archive_entry *entry;
struct archive *a = archive_read_new();
struct archive *ext = archive_write_disk_new();
archive_write_disk_set_options(ext, flags);
archive_read_support_format_tar(a);
if ((archive_read_open_filename(a, filename, 10240))) {
return;
}
for (;;) {
int r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF) {
break;
}
if (r != ARCHIVE_OK) {
return;
}
}
archive_read_close(a);
archive_read_free(a);
archive_write_close(ext);
archive_write_free(ext);
}
Compliant Solution
#include <archive.h>
#include <archive_entry.h>
// ...
int f(const char *filename, int flags) {
const int max_number_of_extraced_entries = 1000;
const int64_t max_file_size = 1000000000; // 1 GB
int number_of_extraced_entries = 0;
int64_t total_file_size = 0;
struct archive_entry *entry;
struct archive *a = archive_read_new();
struct archive *ext = archive_write_disk_new();
archive_write_disk_set_options(ext, flags);
archive_read_support_format_tar(a);
int status = 0;
if ((archive_read_open_filename(a, filename, 10240))) {
return -1;
}
for (;;) {
number_of_extraced_entries++;
if (number_of_extraced_entries > max_number_of_extraced_entries) {
status = 1;
break;
}
int r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF) {
break;
}
if (r != ARCHIVE_OK) {
status = -1;
break;
}
int file_size = archive_entry_size(entry);
total_file_size += file_size;
if (total_file_size > max_file_size) {
status = 1;
break;
}
}
archive_read_close(a);
archive_read_free(a);
archive_write_close(ext);
archive_write_free(ext);
return status;
}
See