README.md (9874B)
1 # @ronomon/pure 2 3 Pure is a **static analysis file format checker** that checks ZIP files for dangerous compression ratios, spec deviations, malicious archive signatures, mismatching local and central directory headers, ambiguous UTF-8 filenames, directory and symlink traversals, invalid MS-DOS dates, overlapping headers, overflow, underflow, sparseness, accidental buffer bleeds etc. 4 5 Pure's goal is to **narrow the semantic gap available to attackers** attempting to exploit vulnerable software, and to **reduce the probability of zero-days**, for example David Fifield's [A better zip bomb](https://www.usenix.org/system/files/woot19-paper_fifield_0.pdf), which was [detected by an early version of Pure as a zero-day](https://news.ycombinator.com/item?id=20352439). 6 7 ## Acknowledgements 8 9 Pure was commissioned and sponsored by the **Product Release And Security Team** at **Microsoft**. 10 11 A special thanks to **Maxim Vainstein** and his team at Microsoft for their vision and encouragement. 12 13 Looking to the future, we want to add support for more archive containers (GZ, TAR, RAR etc.) and other file formats (MS-CFBF or OOXML Office files, RTF, PDF, image formats etc.). Please contact [Joran Dirk Greef](mailto:joran@ronomon.com) if you want to support new file formats in Pure. 14 15 ## Files 16 17 * **pure.h**: C interface to library source code. 18 19 * **pure**: CLI script for development testing. Usage: `./pure <file>`. 20 21 * **test.js**: Node.js test runner to run Pure against all test files in `./tests`. 22 23 * **binding.c**: Node.js binding used by CLI and test runner. 24 25 * **make-errors.js**: Script to recreate C error enums, error codes and error strings dynamically. 26 27 * **make-signatures.js**: Script to recreate C signature strings dynamically. 28 29 * **make-tests.js**: Script to recreate test files dynamically. 30 31 ## Installation 32 33 ```shell 34 npm install @ronomon/pure 35 ``` 36 37 ## Usage 38 39 At the command line, you can run Pure on any zip file: 40 41 ```shell 42 $ ./pure <file.zip> 43 ``` 44 45 For example: 46 47 ```shell 48 $ ./pure samples/zbsm.zip 49 PURE_E_ZIP_BOMB_FIFIELD: zip bomb: local file header overlap (see research by David Fifield) 50 ``` 51 52 As an embedded C library, **pure.h** provides: 53 54 ```c 55 int pure_zip( 56 const uint8_t* buffer, // Zip file buffer 57 const uint64_t size, // Size of zip file buffer in bytes 58 const uint64_t flags // Bit flags (optional) 59 ) 60 61 int pure_zip_bomb(const int error) 62 63 const char* pure_error_code(const int error) 64 65 const char* pure_error_string(const int error) 66 ``` 67 68 * **pure_zip()** returns a non-zero error return code, or a zero return code if the zip file is clean and has no file format anomalies. A buffer instead of a path is passed to pure_zip() for portability, for reduced surface area for bugs, and to avoid forcing unnecessary IO if file contents are already in memory. 69 70 * **pure_zip_bomb()** returns 1 if the error return code indicates a zip bomb, otherwise 0. 71 72 * **pure_error_code()** returns the constant name of the error return code. 73 74 * **pure_error_string()** returns the error message string corresponding the error return code. 75 76 ## File Format Anomalies 77 78 Pure detects **more than 150 zip file format anomalies**. 79 80 Pure provides defenses against directory and symlink traversal exploits, dangerous unix mode permissions, parser ambiguity, and many other defenses against known (and unknown) exploits, including zip bombs and buffer overflows. 81 82 For example, Pure detects **all variants of known (and unknown) zip bombs**: 83 84 1. Overlapping local file headers (cf. [David Fifield](https://www.usenix.org/system/files/woot19-paper_fifield_0.pdf)). 85 2. Quines (cf. [Russ Cox](https://research.swtch.com/zip)). 86 3. Dangerous compression ratios for individual files and across an archive. 87 4. "Lying" zip metadata vs actual compressed data. 88 5. Excessive archive recursion. 89 6. Excessive number of files. 90 91 As a static analysis file format checker, Pure reduces the surface area for zero-day exploits by orders of magnitude. **If a file can get past Pure, it's "pure"... or at least 99% pure**. 92 93 Here is the exhaustive list of what Pure can already detect: 94 95 * PURE_E_SIZE_MAX 96 * PURE_E_MALLOC 97 * PURE_E_STRING_NOT_FOUND 98 * PURE_E_UINT64_OVERFLOW 99 * PURE_E_ZIP_BOMB_ARCHIVES 100 * PURE_E_ZIP_BOMB_DEPTH 101 * PURE_E_ZIP_BOMB_FIFIELD 102 * PURE_E_ZIP_BOMB_FILES 103 * PURE_E_ZIP_BOMB_RATIO 104 * PURE_E_ZIP_BOMB_INFLATE_COMPRESSED_OVERFLOW 105 * PURE_E_ZIP_BOMB_INFLATE_UNCOMPRESSED_OVERFLOW 106 * PURE_E_ZIP_TOO_SMALL 107 * PURE_E_ZIP_SIZE_4GB 108 * PURE_E_ZIP_RAR 109 * PURE_E_ZIP_TAR 110 * PURE_E_ZIP_XAR 111 * PURE_E_ZIP_SIGNATURE 112 * PURE_E_ZIP_EOCDR_NOT_FOUND 113 * PURE_E_ZIP_EOCDR_OVERFLOW 114 * PURE_E_ZIP_EOCDR_COMMENT_OVERFLOW 115 * PURE_E_ZIP_EOCDR_SIGNATURE 116 * PURE_E_ZIP_EOCDR_RECORDS 117 * PURE_E_ZIP_EOCDR_SIZE_OVERFLOW 118 * PURE_E_ZIP_EOCDR_SIZE_UNDERFLOW 119 * PURE_E_ZIP_MULTIPLE_DISKS 120 * PURE_E_ZIP_APPENDED_DATA_ZEROED 121 * PURE_E_ZIP_APPENDED_DATA_BUFFER_BLEED 122 * PURE_E_ZIP_PREPENDED_DATA 123 * PURE_E_ZIP_PREPENDED_DATA_ZEROED 124 * PURE_E_ZIP_PREPENDED_DATA_BUFFER_BLEED 125 * PURE_E_ZIP_CDH_OVERFLOW 126 * PURE_E_ZIP_CDH_SIGNATURE 127 * PURE_E_ZIP_CDH_RELATIVE_OFFSET_OVERFLOW 128 * PURE_E_ZIP_CDH_RELATIVE_OFFSET_OVERLAP 129 * PURE_E_ZIP_CDH_FILE_NAME_OVERFLOW 130 * PURE_E_ZIP_CDH_EXTRA_FIELD_OVERFLOW 131 * PURE_E_ZIP_CDH_FILE_COMMENT_OVERFLOW 132 * PURE_E_ZIP_LFH_OVERFLOW 133 * PURE_E_ZIP_LFH_SIGNATURE 134 * PURE_E_ZIP_LFH_FILE_NAME_OVERFLOW 135 * PURE_E_ZIP_LFH_EXTRA_FIELD_OVERFLOW 136 * PURE_E_ZIP_LFH_UNDERFLOW_ZEROED 137 * PURE_E_ZIP_LFH_UNDERFLOW_BUFFER_BLEED 138 * PURE_E_ZIP_LFH_DATA_OVERFLOW 139 * PURE_E_ZIP_DDR_OVERFLOW 140 * PURE_E_ZIP_LF_OVERFLOW 141 * PURE_E_ZIP_LF_UNDERFLOW_ZEROED 142 * PURE_E_ZIP_LF_UNDERFLOW_BUFFER_BLEED 143 * PURE_E_ZIP_CD_OVERFLOW 144 * PURE_E_ZIP_CD_UNDERFLOW_ZEROED 145 * PURE_E_ZIP_CD_UNDERFLOW_BUFFER_BLEED 146 * PURE_E_ZIP_CD_EOCDR_OVERFLOW 147 * PURE_E_ZIP_CD_EOCDR_UNDERFLOW_ZEROED 148 * PURE_E_ZIP_CD_EOCDR_UNDERFLOW_BUFFER_BLEED 149 * PURE_E_ZIP_DIFF_LFH_GENERAL_PURPOSE_BIT_FLAG 150 * PURE_E_ZIP_DIFF_LFH_COMPRESSION_METHOD 151 * PURE_E_ZIP_DIFF_LFH_LAST_MOD_FILE_TIME 152 * PURE_E_ZIP_DIFF_LFH_LAST_MOD_FILE_DATE 153 * PURE_E_ZIP_DIFF_LFH_CRC32 154 * PURE_E_ZIP_DIFF_LFH_COMPRESSED_SIZE 155 * PURE_E_ZIP_DIFF_LFH_UNCOMPRESSED_SIZE 156 * PURE_E_ZIP_DIFF_LFH_FILE_NAME_LENGTH 157 * PURE_E_ZIP_DIFF_LFH_FILE_NAME 158 * PURE_E_ZIP_DIFF_LFH_DDR_CRC32 159 * PURE_E_ZIP_DIFF_LFH_DDR_COMPRESSED_SIZE 160 * PURE_E_ZIP_DIFF_LFH_DDR_UNCOMPRESSED_SIZE 161 * PURE_E_ZIP_DIFF_DDR_CRC32 162 * PURE_E_ZIP_DIFF_DDR_COMPRESSED_SIZE 163 * PURE_E_ZIP_DIFF_DDR_UNCOMPRESSED_SIZE 164 * PURE_E_ZIP_FLAG_OVERFLOW 165 * PURE_E_ZIP_FLAG_TRADITIONAL_ENCRYPTION 166 * PURE_E_ZIP_FLAG_ENHANCED_DEFLATE 167 * PURE_E_ZIP_FLAG_COMPRESSED_PATCHED_DATA 168 * PURE_E_ZIP_FLAG_STRONG_ENCRYPTION 169 * PURE_E_ZIP_FLAG_UNUSED_BIT_7 170 * PURE_E_ZIP_FLAG_UNUSED_BIT_8 171 * PURE_E_ZIP_FLAG_UNUSED_BIT_9 172 * PURE_E_ZIP_FLAG_UNUSED_BIT_10 173 * PURE_E_ZIP_FLAG_ENHANCED_COMPRESSION 174 * PURE_E_ZIP_FLAG_MASKED_LOCAL_HEADERS 175 * PURE_E_ZIP_FLAG_RESERVED_BIT_14 176 * PURE_E_ZIP_FLAG_RESERVED_BIT_15 177 * PURE_E_ZIP_COMPRESSION_METHOD_DANGEROUS 178 * PURE_E_ZIP_COMPRESSION_METHOD_ENCRYPTED 179 * PURE_E_ZIP_COMPRESSION_METHOD_UNSUPPORTED 180 * PURE_E_ZIP_STORED_COMPRESSION_SIZE_MISMATCH 181 * PURE_E_ZIP_DANGEROUS_NEGATIVE_COMPRESSION_RATIO 182 * PURE_E_ZIP_TIME_OVERFLOW 183 * PURE_E_ZIP_TIME_HOUR_OVERFLOW 184 * PURE_E_ZIP_TIME_MINUTE_OVERFLOW 185 * PURE_E_ZIP_TIME_SECOND_OVERFLOW 186 * PURE_E_ZIP_DATE_OVERFLOW 187 * PURE_E_ZIP_DATE_YEAR_OVERFLOW 188 * PURE_E_ZIP_DATE_MONTH_OVERFLOW 189 * PURE_E_ZIP_DATE_DAY_OVERFLOW 190 * PURE_E_ZIP_FILE_NAME_LENGTH 191 * PURE_E_ZIP_FILE_NAME_CONTROL_CHARACTERS 192 * PURE_E_ZIP_FILE_NAME_TRAVERSAL_DRIVE_PATH 193 * PURE_E_ZIP_FILE_NAME_TRAVERSAL_RELATIVE_PATH 194 * PURE_E_ZIP_FILE_NAME_TRAVERSAL_DOUBLE_DOTS 195 * PURE_E_ZIP_FILE_NAME_COMPONENT_OVERFLOW 196 * PURE_E_ZIP_FILE_NAME_BACKSLASH 197 * PURE_E_ZIP_EXTRA_FIELD_MAX 198 * PURE_E_ZIP_EXTRA_FIELD_MIN 199 * PURE_E_ZIP_EXTRA_FIELD_ATTRIBUTE_OVERFLOW 200 * PURE_E_ZIP_EXTRA_FIELD_OVERFLOW 201 * PURE_E_ZIP_EXTRA_FIELD_UNDERFLOW_ZEROED 202 * PURE_E_ZIP_EXTRA_FIELD_UNDERFLOW_BUFFER_BLEED 203 * PURE_E_ZIP_EXTRA_FIELD_UNICODE_PATH_OVERFLOW 204 * PURE_E_ZIP_EXTRA_FIELD_UNICODE_PATH_VERSION 205 * PURE_E_ZIP_EXTRA_FIELD_UNICODE_PATH_DIFF 206 * PURE_E_ZIP_UNIX_MODE_OVERFLOW 207 * PURE_E_ZIP_UNIX_MODE_BLOCK_DEVICE 208 * PURE_E_ZIP_UNIX_MODE_CHARACTER_DEVICE 209 * PURE_E_ZIP_UNIX_MODE_FIFO 210 * PURE_E_ZIP_UNIX_MODE_SOCKET 211 * PURE_E_ZIP_UNIX_MODE_PERMISSIONS_STICKY 212 * PURE_E_ZIP_UNIX_MODE_PERMISSIONS_SETGID 213 * PURE_E_ZIP_UNIX_MODE_PERMISSIONS_SETUID 214 * PURE_E_ZIP_DIRECTORY_COMPRESSED 215 * PURE_E_ZIP_DIRECTORY_UNCOMPRESSED 216 * PURE_E_ZIP_SYMLINK_COMPRESSED 217 * PURE_E_ZIP_SYMLINK_LENGTH 218 * PURE_E_ZIP_SYMLINK_CONTROL_CHARACTERS 219 * PURE_E_ZIP_SYMLINK_TRAVERSAL_DRIVE_PATH 220 * PURE_E_ZIP_SYMLINK_TRAVERSAL_RELATIVE_PATH 221 * PURE_E_ZIP_SYMLINK_TRAVERSAL_DOUBLE_DOTS 222 * PURE_E_ZIP_SYMLINK_COMPONENT_OVERFLOW 223 * PURE_E_ZIP_STRING_MAX 224 * PURE_E_ZIP_STRING_NULL_BYTE 225 * PURE_E_ZIP_INFLATE 226 * PURE_E_ZIP_INFLATE_DICTIONARY 227 * PURE_E_ZIP_INFLATE_STREAM 228 * PURE_E_ZIP_INFLATE_DATA 229 * PURE_E_ZIP_INFLATE_MEMORY 230 * PURE_E_ZIP_INFLATE_COMPRESSED_UNDERFLOW 231 * PURE_E_ZIP_INFLATE_UNCOMPRESSED_UNDERFLOW 232 * PURE_E_ZIP_AD_NIHILO 233 * PURE_E_ZIP_EX_NIHILO 234 * PURE_E_ZIP_CRC32 235 * PURE_E_ZIP_EOCDL_64_OVERFLOW 236 * PURE_E_ZIP_EOCDL_64_SIGNATURE 237 * PURE_E_ZIP_EOCDL_64_NEGATIVE_OFFSET 238 * PURE_E_ZIP_EOCDL_64_DISK 239 * PURE_E_ZIP_EOCDL_64_DISKS 240 * PURE_E_ZIP_EOCDR_64_OVERFLOW 241 * PURE_E_ZIP_EOCDR_64_SIGNATURE 242 * PURE_E_ZIP_EOCDR_EOCDL_64_OVERFLOW 243 * PURE_E_ZIP_EOCDR_EOCDL_64_UNDERFLOW_ZEROED 244 * PURE_E_ZIP_EOCDR_EOCDL_64_UNDERFLOW_BUFFER_BLEED 245 * PURE_E_ZIP_DIFF_EOCDR_DISK 246 * PURE_E_ZIP_DIFF_EOCDR_CD_DISK 247 * PURE_E_ZIP_DIFF_EOCDR_CD_DISK_RECORDS 248 * PURE_E_ZIP_DIFF_EOCDR_CD_RECORDS 249 * PURE_E_ZIP_DIFF_EOCDR_CD_SIZE 250 * PURE_E_ZIP_DIFF_EOCDR_CD_OFFSET 251 * PURE_E_ZIP_EIEF_64_COMPRESSED_SIZE 252 * PURE_E_ZIP_EIEF_64_DISK 253 * PURE_E_ZIP_EIEF_64_RELATIVE_OFFSET 254 * PURE_E_ZIP_EIEF_64_UNCOMPRESSED_SIZE 255 * PURE_E_ZIP_EIEF_64_UNDERFLOW_ZEROED 256 * PURE_E_ZIP_EIEF_64_UNDERFLOW_BUFFER_BLEED 257 * PURE_E_ZIP_EIEF_64_LFH 258 * PURE_E_ZIP_DIRECTORY_HAS_NO_LFH 259 260 ## ...and this is only for zip files, we want to add static analysis for more file formats, please consider asking your company to [sponsor open-source work on Pure](mailto:joran@ronomon.com).