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:
parent
208264cb66
commit
08d31be932
@ -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: "
|
||||
|
Loading…
x
Reference in New Issue
Block a user