Avatar

LIEF - Release 0.11.1

ionicons-v5-k Romain Thomas February 22, 2021
Wave

Tl;DR

LIEF v0.11.1 fixes some issues related to PE Authentihash computation. The new packages are available on PyPI and the SDKs can be downloaded on the official website.

Enjoy!

LIEF 0.11.0 missed handling some cases in the processing of the PE Authentihash. This new release addresses these issues and the following blog post explains the cases we did not handle.

Section name

PE section’s names are stored in a fixed char array (8 bytes) which means that a section’s name can contain trailing bytes after the null char:

1struct pe_section {
2  char     name[8];
3  uint32_t RVA;
4  // ...
5};

Before v0.11.1, LIEF didn’t take into account the trailing bytes and stopped to read the section’s name on the first null char:

1this->name_ = std::string(header->name, sizeof(header->name)).c_str();

This implementation has two drawbacks. First, we lose information since we don’t store the extra trailing bytes. Regular binaries have zero trailing bytes after the first null char but some of them might use this spot to hide data.

Section name with trailing bytes

Secondly, the full section name (i.e the whole 8 bytes) is used to compute the Authentihash. Therefore, if the first null char is followed by trailing bytes different from zero, the computed hash is inconsistent.

Data directory

According to the PE specifications 1 the last entry of the data directory table must contain a null entry (i.e. an entry with an RVA and size set to 0).

PE specifications require a last zero entry

It turns out that this requirement is not enforced by the loader. In the case of the binary (bc203f2b6a…) the last entry is set to 0x02b7bc68/0x01a7a0 (used for watermarking?).

Last data directory with non-zero entry

In the previous versions of LIEF we assumed that the last entry of the data directory table was always zero. Since the last entry is used to compute the Authentihash value, it led to a bad signature while it was effectively correct.

This issue has been addressed in the commit 3c65ffe

Return value of verify_signature()

As noticed by Cedric Halbronn in the issue issues/532, the return value of LIEF::PE::Binary::verify_signature lacks of information when the verification failed. The return value was either VERIFICATION_FLAGS.OK or VERIFICATION_FLAGS.BAD_SIGNATURE because of a fail-fast implementation of the verification flag.

The function now returns flags as follows:

VERIFICATION_FLAGS.BAD_DIGEST | VERIFICATION_FLAGS.BAD_SIGNATURE | VERIFICATION_FLAGS.CERT_EXPIRED

Other issues

One of the critical issues raised by imidoriya and fixed in the new version is the processing of the overlay data when the “data directory signature” is located in this area (c.f. 463bb0ec3…). This kind of layout triggers a memory error on this part of the processing Binary.cpp#L1174-L1187. It has been addressed in the commit 05103f5

Acknowledgment

Thanks to Andrew Williams for providing the different samples that raised some of these errors! Thank you also to Cedric Halbronn and the CERT Gouvernemental of Luxembourg for their feedback about the API.

Avatar
Romain Thomas Posted on February 22, 2021