4
0
mirror of https://github.com/QuasarApp/LIEF.git synced 2025-05-09 10:19:34 +00:00

Authenticode-related bug fixes and improvements

Part of Authenticode verification consists of:
 - Comparing the computed Authenticode hash to the digest
   stored in the ContentInfo section
 - Comparing hash(ContentInfo) to the digest stored in the
   AuthenticatedAttributes section
 - Verifying signed(hash(AuthenticatedAttributes)) using a
   certificate identified by the issuer and serial number
   specified in the SignerInfo section

This commit makes it so that the raw bytes needed to
calculate hash(ContentInfo) and hash(AuthenticatedAttributes)
are available for use.

============================================================

Allow missing [0] in SpcSpOpusInfo

Some executables have MoreInfo but not a ProgramName (and the documentation
lists both as OPTIONAL), so handle this case correctly.

Example:

```
01416b1730218454c99b13592650cb170402b86742b4bab971565903b841829b

SEQUENCE(2 elem)
OBJECT IDENTIFIER1.3.6.1.4.1.311.2.1.12spcSpOpusInfo(Microsoft code signing)
SET(1 elem)
  SEQUENCE(1 elem)
  [1](1 elem)
   [0]http://www.mozilla.com
```

============================================================

Improve consistency of parsed serial numbers

When parsing the issuer serial number, call mbedtls_x509_get_serial instead of
parsing it as an integer directly with mbedtls_asn1_get_mpi. These two functions
differ in how they treat serial numbers prepended with '00' to prevent them from
being negative (the former preserves the '00', and the latter discards it). The
embedded certs are parsed via a call to mbedtls_x509_crt_parse_der, which uses
mbedtls_x509_get_serial behind the scenes, so there was an inconsistency between
lief_obj.signature.signer_info.issuer[1] and
lief_obj.signature.certificates[x].serial_number.  Example:

8bf57d97dd917c4f823659266caaa33e7398406daf11ba8318e3f7414ee3fb24

============================================================

Handle SpcLink and SpcString CHOICEs in SpcSpOpusInfo

The Authenticode spec doc says that these can be CHOICES, so
handle the easy ones and safely skip the others.

============================================================

Allow Authenticode sig to be parsed even if cert parsing fails

By default, mbedtls doesn't support MD2 certs, which are fairly
common in older signed executables.  Ex:

1cb16f94cebdcad7dd05c8537375a6ff6379fcdb08528fc83889f26efaa84e2a

============================================================

Enable mbed TLS MD2 and MD4 support; add Unix debug options

By default, mbedtls doesn't support MD2 certs, which are fairly
common in older signed executables.  Ex:

1cb16f94cebdcad7dd05c8537375a6ff6379fcdb08528fc83889f26efaa84e2a

============================================================

Set MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION flag

Some older signed executables use certs with the SpcSpAgencyInfo
Critical Extension, which mbed TLS doesn't support, so set
MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION to have it
skip this extension.  Example:

781ca31416ec708a587851dafd90c661b86f244ab8b8475c4185e958e54ff838

============================================================

Support accessing non-utf8 issuer names via Python

For a few signatures where the issuer name contained non-utf8
characters, accessing the issuer name field in Python would raise
a UnicodeDecodeError exception. Now this field is handled the
same way the names in the individual certs are (I'm not sure if
they get represented 100% correctly, but at least they are
consistent, which is good enough for me). Example:

048f91b9302c88380fc66adac1e314d82733089ef3a31eadca5f0cb4169b195f
This commit is contained in:
Andrew 2019-07-26 17:51:14 -04:00
parent 208264cb66
commit 08d31be932
9 changed files with 150 additions and 51 deletions

@ -160,6 +160,10 @@ if (CMAKE_BUILD_TYPE MATCHES Debug AND WINDOWS)
)
endif()
if (CMAKE_BUILD_TYPE MATCHES Debug AND UNIX)
target_compile_options(LIB_LIEF PRIVATE -g -O0)
endif()
if (BUILD_SHARED_LIBS)
target_compile_definitions(LIB_LIEF PRIVATE -DLIEF_EXPORTS)
else()
@ -244,7 +248,7 @@ endif()
# VDEX part
# =========
# =========
if (LIEF_VDEX)
include(${CMAKE_CURRENT_SOURCE_DIR}/src/VDEX/CMakeLists.txt)
set(ENABLE_VDEX_SUPPORT 1)
@ -254,7 +258,7 @@ endif()
# ART part
# ========
# ========
if (LIEF_ART)
include(${CMAKE_CURRENT_SOURCE_DIR}/src/ART/CMakeLists.txt)
set(ENABLE_ART_SUPPORT 1)
@ -267,7 +271,7 @@ endif()
include(${CMAKE_CURRENT_SOURCE_DIR}/src/platforms/CMakeLists.txt)
# LIEF includes
# =============
# =============
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/include/LIEF/version.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/include/LIEF/version.h"
@ -336,6 +340,16 @@ set_property(TARGET LIB_LIEF PROPERTY CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(LIB_LIEF PUBLIC -D_GLIBCXX_USE_CXX11_ABI=1)
# Enable support for MD2 and MD4 for parsing the Authenticode sigs
# of older executables. Also, some older signed executables use certs
# with the SpcSpAgencyInfo Critical Extension, which mbed TLS doesn't
# support, so set MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION to
# have it skip this extension.
add_definitions(
-DMBEDTLS_MD2_C
-DMBEDTLS_MD4_C
-DMBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
)
# ASAN - LSAN - TSAN - USAN
@ -494,7 +508,7 @@ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND UNIX)
endif()
# Installation
# ============
# ============
install(TARGETS LIB_LIEF
ARCHIVE

@ -58,6 +58,10 @@ void create<AuthenticatedAttributes>(py::module& m) {
},
"Return an URL to website with more information about the signer")
.def_property_readonly("raw",
&AuthenticatedAttributes::raw,
"Return the raw bytes associated with the AuthenticatedAttributes")
.def("__str__",
[] (const AuthenticatedAttributes& authenticated_attributes)
{

@ -52,6 +52,9 @@ void create<ContentInfo>(py::module& m) {
&ContentInfo::digest,
"The digest")
.def_property_readonly("raw",
&ContentInfo::raw,
"Return the raw bytes associated with the ContentInfo")
.def("__str__",
[] (const ContentInfo& content_info)

@ -41,9 +41,12 @@ void create<SignerInfo>(py::module& m) {
"Should be 1")
.def_property_readonly("issuer",
&SignerInfo::issuer,
[] (const SignerInfo& object) {
const issuer_t& issuer = object.issuer();
return std::pair<py::object, std::vector<uint8_t>>{safe_string_converter(std::get<0>(issuer)), std::get<1>(issuer)};
},
"Issuer and serial number",
py::return_value_policy::reference)
py::return_value_policy::copy)
.def_property_readonly("digest_algorithm",
&SignerInfo::digest_algorithm,

@ -49,6 +49,9 @@ class LIEF_API AuthenticatedAttributes : public Object {
//! @brief Return an URL to website with more information about the signer
const std::string& more_info(void) const;
//! @brief Return the raw bytes associated with the AuthenticatedAttributes
const std::vector<uint8_t>& raw(void) const;
virtual void accept(Visitor& visitor) const override;
virtual ~AuthenticatedAttributes(void);
@ -62,6 +65,7 @@ class LIEF_API AuthenticatedAttributes : public Object {
std::u16string program_name_;
std::string more_info_;
std::vector<uint8_t> raw_;
};

@ -50,6 +50,9 @@ class LIEF_API ContentInfo : public Object {
//! @brief The digest
const std::vector<uint8_t>& digest(void) const;
//! @brief Return the raw bytes associated with the ContentInfo
const std::vector<uint8_t>& raw(void) const;
virtual void accept(Visitor& visitor) const override;
virtual ~ContentInfo(void);
@ -64,7 +67,7 @@ class LIEF_API ContentInfo : public Object {
oid_t digest_algorithm_; // algorithm used to hash the file (should match Signature::digest_algorithms_)
std::vector<uint8_t> digest_; //hash value
std::vector<uint8_t> raw_; // raw bytes
};

@ -44,6 +44,10 @@ const std::string& AuthenticatedAttributes::more_info(void) const {
return this->more_info_;
}
const std::vector<uint8_t>& AuthenticatedAttributes::raw(void) const {
return this->raw_;
}
void AuthenticatedAttributes::accept(Visitor& visitor) const {
visitor.visit(*this);
}

@ -44,6 +44,9 @@ const std::vector<uint8_t>& ContentInfo::digest(void) const {
return this->digest_;
}
const std::vector<uint8_t>& ContentInfo::raw(void) const {
return this->raw_;
}
void ContentInfo::accept(Visitor& visitor) const {
visitor.visit(*this);

@ -188,6 +188,9 @@ ContentInfo SignatureParser::parse_content_info(void) {
throw corrupted("Signature corrupted");
}
// Save off raw now so that it covers everything else in the ContentInfo
content_info.raw_ = {this->p_, this->p_ + tag};
// SpcAttributeTypeAndOptionalValue
// |_ SPC_PE_IMAGE_DATAOBJ
// |_ SpcPeImageData
@ -332,6 +335,12 @@ void SignatureParser::parse_certificates(void) {
ca.release();
}
// If one of the certificates failed to parse for some reason, skip past
// the certificate section so that we have a chance of parsing the rest
// of the signature.
if (this->p_ < cert_end) {
this->p_ = cert_end;
}
}
AuthenticatedAttributes SignatureParser::get_authenticated_attributes(void) {
@ -346,11 +355,16 @@ AuthenticatedAttributes SignatureParser::get_authenticated_attributes(void) {
mbedtls_asn1_buf content_type_oid;
AuthenticatedAttributes authenticated_attributes;
uint8_t *p_start = this->p_;
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}
authenticated_attributes.raw_ = {p_start, p_start + (this->p_ - p_start) + tag};
uint8_t* p_end = this->p_ + tag;
while(this->p_ < p_end) {
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
@ -430,52 +444,104 @@ AuthenticatedAttributes SignatureParser::get_authenticated_attributes(void) {
uint8_t *seq_end = this->p_ + tag;
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}
if (*this->p_ == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED)) {
this->p_ += 1;
if ((ret = mbedtls_asn1_get_len(&(this->p_), this->end_, &tag)) != 0) {
VLOG(VDEBUG) << "Unexpected format for SpcSpOpusInfo [0] block ";
throw corrupted("Authenticated attributes corrupted");
}
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag, MBEDTLS_ASN1_CONTEXT_SPECIFIC)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}
VLOG(VDEBUG) << "Offset: " << std::dec << this->current_offset();
VLOG(VDEBUG) << "Size: " << std::dec << tag;
// Two cases to handle here:
// SpcString ::= CHOICE {
// unicode [0] IMPLICIT BMPSTRING,
// ascii [1] IMPLICIT IA5STRING
// }
// u8 -> u16 due to endiness
std::string u8progname{reinterpret_cast<char*>(this->p_), tag};
std::u16string progname;
try {
utf8::unchecked::utf8to16(std::begin(u8progname), std::end(u8progname), std::back_inserter(progname));
} catch (const utf8::exception&) {
LOG(WARNING) << "utf8 error when parsing progname";
}
if (*this->p_ == (MBEDTLS_ASN1_CONTEXT_SPECIFIC) ||
*this->p_ == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | 1)) {
this->p_ += 1;
if ((ret = mbedtls_asn1_get_len(&(this->p_), this->end_, &tag)) != 0) {
VLOG(VDEBUG) << "Unexpected format for SpcString block ";
throw corrupted("Authenticated attributes corrupted");
}
authenticated_attributes.program_name_ = progname;
VLOG(VDEBUG) << "ProgName " << u16tou8(progname);
this->p_ += tag;
VLOG(VDEBUG) << "Offset: " << std::dec << this->current_offset();
VLOG(VDEBUG) << "Size: " << std::dec << tag;
if (this->p_ >= seq_end) {
VLOG(VDEBUG) << "No more info specified ";
authenticated_attributes.more_info_ = "";
continue;
// u8 -> u16 due to endiness
std::string u8progname{reinterpret_cast<char*>(this->p_), tag};
std::u16string progname;
try {
utf8::unchecked::utf8to16(std::begin(u8progname), std::end(u8progname), std::back_inserter(progname));
} catch (const utf8::exception&) {
LOG(WARNING) << "utf8 error when parsing progname";
}
authenticated_attributes.program_name_ = progname;
VLOG(VDEBUG) << "ProgName " << u16tou8(progname);
this->p_ += tag;
} else {
VLOG(VDEBUG) << "Unexpected format for SpcString block ";
throw corrupted("Authenticated attributes corrupted");
}
if (this->p_ >= seq_end) {
VLOG(VDEBUG) << "No more info specified ";
authenticated_attributes.more_info_ = "";
continue;
}
} else {
VLOG(VDEBUG) << "No program name specified ";
authenticated_attributes.program_name_ = u"";
}
// moreInfo
// ++++++++
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_BOOLEAN )) != 0) {
throw corrupted("Authenticated attributes corrupted");
}
if (*this->p_ == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_BOOLEAN)) {
this->p_ += 1;
if ((ret = mbedtls_asn1_get_len(&(this->p_), this->end_, &tag)) != 0) {
VLOG(VDEBUG) << "Unexpected format for SpcSpOpusInfo [1] block ";
throw corrupted("Authenticated attributes corrupted");
}
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag, MBEDTLS_ASN1_CONTEXT_SPECIFIC)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}
uint8_t p_val = *this->p_;
std::string more_info{reinterpret_cast<char*>(this->p_), tag}; // moreInfo
authenticated_attributes.more_info_ = more_info;
VLOG(VDEBUG) << more_info;
this->p_ += tag;
continue;
this->p_ += 1;
if ((ret = mbedtls_asn1_get_len(&(this->p_), this->end_, &tag)) != 0) {
VLOG(VDEBUG) << "Unexpected format for SpcLink block ";
throw corrupted("Authenticated attributes corrupted");
}
// Three cases to handle here:
// SpcLink ::= CHOICE {
// url [0] IMPLICIT IA5STRING,
// moniker [1] IMPLICIT SpcSerializedObject,
// file [2] EXPLICIT SpcString
// }
if (p_val == (MBEDTLS_ASN1_CONTEXT_SPECIFIC)) {
std::string more_info{reinterpret_cast<char*>(this->p_), tag}; // moreInfo
authenticated_attributes.more_info_ = more_info;
VLOG(VDEBUG) << more_info;
this->p_ += tag;
} else if (p_val == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | 1)) {
VLOG(VDEBUG) << "Parsing MoreInfo 'moniker' option not currently supported ";
authenticated_attributes.more_info_ = "";
} else if (p_val == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2)) {
VLOG(VDEBUG) << "Parsing MoreInfo 'file' option not currently supported ";
authenticated_attributes.more_info_ = "";
} else {
VLOG(VDEBUG) << "Unexpected format for SpcLink block ";
throw corrupted("Authenticated attributes corrupted");
}
continue;
}
} else {
VLOG(VDEBUG) << "Skipping OID " << oid_str;
@ -560,19 +626,14 @@ SignerInfo SignatureParser::get_signer_info(void) {
// CertificateSerialNumber (issuer SN)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mbedtls_mpi certificate_number;
mbedtls_mpi_init(&certificate_number);
if ((ret = mbedtls_asn1_get_mpi(&(this->p_), this->end_, &certificate_number)) != 0) {
mbedtls_x509_buf serial;
if ((ret = mbedtls_x509_get_serial(&(this->p_), this->end_, &serial)) != 0) {
throw corrupted("Signer info corrupted");
}
std::vector<uint8_t> certificate_sn(mbedtls_mpi_size(&certificate_number), 0);
mbedtls_mpi_write_binary(&certificate_number, certificate_sn.data(), certificate_sn.size());
mbedtls_mpi_free(&certificate_number);
std::vector<uint8_t> certificate_sn = {serial.p, serial.p + serial.len};
signer_info.issuer_ = {issuer_name, certificate_sn};
// digestAlgorithm
// ---------------
VLOG(VDEBUG) << "Parsing digestAlgorithm (offset: "