Penetration Test Report (TrailOfBits): Zlib report

Zlib
Automated Security Assessment
Zlib version 1.2.8
September 30th, 2016
Prepared For:
Gervase Markham, Chris Riley
Secure Open Source
Prepared By:
Artem Dinaburg | Trail of Bits
​
artem@trailofbits.com
Pascal Cuoq | TrustInSoft
​
cuoq@trust-in-soft.com
Changelog
Synopsis
Engagement Goals
Areas of Focus
Executive Summary
Recommendations Summary
Methodology
Testing Methodology
Verification Methodology
Findings Summary
Software Testing
Software Verification
Table of Findings
Findings
Finding 1: Incompatible declarations for external linkage function deflate
Finding 2: Accessing a buffer of char via a pointer to unsigned int
Finding 3: Out-of-bounds pointer arithmetic in inftrees.c
Finding 4: Undefined Left Shift of Negative Number
Finding 5: Big Endian Out Of Bounds Pointer
Appendix A. Generated Inputs and Coverage Data
Appendix B. Fix Log
Alternate Solution for Finding 2
About Trail of Bits
About TrustInSoft
Changelog
August 25, 2016: Initial report delivered
September 28, 2016: Added Appendix B with Retest Results
September 30, 2016: Publication version
Synopsis
In August 2016, Mozilla engaged Trail of Bits to perform a security assessment of
Zlib as part of the Mozilla Secure Open Source (SOS) Fund. Zlib is an open source
compression library with a history of security vulnerabilities. Mozilla is interested in
the security of zlib because zlib is used in virtually every software package that
requires compression or decompression. Securing zlib will help secure the core
infrastructure of the Internet.
To perform this audit, Trail of Bits used the automated vulnerability discovery tools
​ ​
developed for the DARPA Cyber Grand Challenge1. These tools automatically
audited zlib for security vulnerabilities, delivering confidence while reducing costs.
Additionally, we teamed with colleagues at TrustInSoft to augment our assessment
​ ​
with the unique capabilities of their verification toolkit.
Engagement Goals
The goal of the engagement was to evaluate zlib for security vulnerabilities and to
serve as a proof-of-concept for automated software audits. The audit used a
two-pronged approach: testing to find bugs in the final compiled zlib library, and
verification to find latent bugs in the zlib source code.
The testing tools were used to search for memory safety vulnerabilities, such as
buffer overflows, heap overflows, out of bounds array access, and so on. Similar
bugs had been previously identified in zlib. The verification tools were used to
identify violations of the C standard; specifically, the use of undefined behavior.
Areas of Focus
The audit focused on the compression and decompression functionality provided
by zlib. While zlib exposes several APIs, it is the compression and decompression
functionality that typically processes untrusted input. This code may be reached via
two general paths: processing raw data input and processing GZip formatted input.
We tested both paths.
We did not inspect functions that query or set internal library state, provide
replacements for C file I/O functionality (other than necessary to test GZip
decoding), nor functions that deal with very specific zlib use cases, such as forcing
specific decompression strategies. These functions are unlikely to accept user input
or are very unlikely to be used in an application.
1 https://www.cybergrandchallenge.com/
Executive Summary
Application Summary
Name Zlib
Version 1.2.8 (http://zlib.net/zlib-1.2.8.tar.gz)
​ ​
Type Userspace Compression Library
Platform Cross-Platform (tested on Linux)
Engagement Summary
Engagement Type Bug Hunting + Verification
Engagement Tools Trail of Bits CRS + TrustInSoft TIS-Interpreter2
Consultants Engaged 2
Total Engagement Effort 5 days
Vulnerability Summary
Total High Severity Issues 0
Total Medium Severity Issues 1
Total Low Severity Issues 4
Total Informational Severity Issues 0
Total 5
Category Breakdown
Undefined Behavior 5
2 https://github.com/TrustInSoft/tis-interpreter
Recommendations Summary
Do not rely on undefined behavior. We have identified five areas where code in
​
zlib invokes undefined behavior in the C standard. Use of this code does not
currently generate buggy binaries, but it is possible that with future compilers or
platforms these latent bugs may manifest in compiled code.
Opt-in support for legacy compilers and micro-optimizations. Cases of
​
undefined behavior mostly appear in code that is designed to work around old,
buggy compilers or to provide micro-optimizations for specific architectures. Making
this behavior optional rather than default would result in cleaner builds for
common zlib usage.
Deprecate support for ancient compilers. Support for ancient buggy compilers
​
and hacks to work around their bugs needlessly increases the complexity of zlib's
codebase. Rarely used platforms should be deprecated to simplify the zlib
codebase, making the code easier to read, understand, and audit.
Deprecate architecture-specific micro-optimizations. Architecture-specific
​
micro-optimizations should be eliminated. They needlessly increase complexity,
may introduce undefined behavior, and are likely unnecessary with modern
optimizing compilers.
Conduct regular security audits. Zlib is a great candidate for periodic security
​
audits: the code base is very stable and updates are infrequent. The long release
cycle allows ample time to perform and respond to an automated security audit.
Periodic audits can prevent the introduction of new bugs into the zlib codebase.
Methodology
Testing Methodology
The Trail of Bits Cyber Reasoning Systems (CRS) uses a combination of symbolic and
concrete vulnerability hunting strategies to overcome roadblocks that stymie other
security tools. Developed to compete in the DARPA Cyber Grand Challenge, it has
since been extended to operate on Linux binaries. The CRS uses a new technique
we call analysis boosting to combine our novel low-latency fuzzer with two symbolic
execution engines. Using this approach, we are able to gather a fine-grained picture
of program execution and identify subtle flaws that a human auditor would likely
miss.
The only prior requirement is the creation of a “driver” program to exercise target
functionality in zlib. We created drivers for four zlib APIs -compress, uncompress,
​ ​ ​ ​
gzread, gzwrite- to exercise the compression, decompression, and GZip file format
​ ​ ​
functionality inside zlib. These drivers were linked with zlib and run inside the CRS.
The CRS then investigated various program states, switching between concrete and
symbolic execution as needed to explore deeper in the zlib code. Each tested
function was run through the CRS for two weeks on a 48-core machine.
The tested functions accept arguments that affect how zlib will process inputs. The
chief of these are the input buffer size and the destination buffer size (for compress
​
and uncompress) and the file mode for gzread and gzwrite. These were set to fixed
​ ​ ​ ​ ​ ​
values shown in Table 1. The buffer sizes were chosen to be large enough to test a
variety of inputs, yet small enough to be analyzed in a reasonable timeframe.
Tested Function Input Buffer Size Output Buffer Size File
(bytes) (bytes) Mode
compress 4096 2048 n/a
uncompress 4096 163839 n/a
gzread n/a 4096 “rb”
gzwrite 4096 n/a “wb”
Table 1: Buffer sizes used when testing zlib functions
The file mode used with gzread and gzwrite can specify a compression or
​ ​ ​ ​
decompression strategy in addition to whether the file is opened for reading or
writing (e.g. the file mode “wbR” would use the RLE compression strategy). The
modes we selected will use the default compression strategies -- this is the most
probable use-case of zlib, but will miss code coverage that is dependent on specific
strategies.
To enhance the CRS’s thoroughness, the uncompress and gzread tests were seeded
​ ​
with data compressed at different compression levels. The compress and gzwrite
​ ​ ​
tests were not seeded. All inputs were generated by the CRS with no prior
knowle