Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Update the PE Authenticode parsing code
 - Allow UTF8String and IA5String types when parsing the Issuer fields
   (previously only PrintableString types were handled).  Handling these
   three types should be sufficient, based on the default types indicated
   at https://github.com/ARMmbed/mbedtls/blob/master/library/x509_create.c#L52.
   The approach used is similar to what's done in mbedtls's internal
   x509_get_attr_type_value function.

   Examples:
```
     8a364e0881fd7201cd6f0a0ff747451c9b93182d5699afb28ad8466f7f726660:
     SEQUENCE (4 elem)
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 2.5.4.6 countryName (X.520 DN component)
           PrintableString PL
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 2.5.4.10 organizationName (X.520 DN component)
           UTF8String Unizeto Technologies S.A.
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 2.5.4.11 organizationalUnitName (X.520 DN component)
           UTF8String Certum Certification Authority
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component)
           UTF8String Certum Code Signing CA SHA2

     From a test binary compiled with osslsigncode:
     SEQUENCE (6 elem)
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 2.5.4.6 countryName (X.520 DN component)
           PrintableString US
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 2.5.4.8 stateOrProvinceName (X.520 DN component)
           UTF8String State
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 2.5.4.7 localityName (X.520 DN component)
           UTF8String City
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 2.5.4.10 organizationName (X.520 DN component)
           UTF8String Cisco Talos
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 2.5.4.11 organizationalUnitName (X.520 DN component)
           UTF8String Test CA 1704a8ea9e24d8ed
       SET (1 elem)
         SEQUENCE (2 elem)
           OBJECT IDENTIFIER 1.2.840.113549.1.9.1 emailAddress (PKCS #9. Deprecated, use an altName extension instead)
           IA5String rfc2606@example.net
```

 - Allow any order for the AuthenticatedAttributes, and ignore ones that
   aren't recognized. The code was looking for a strict ordering of the
   attributes, but Windows seems to be OK with any order.
   Example:
```
     From f91e258ea71dcbfc82371b2ee3e20852e45bef0cb946223d1141a6ef1dfb793f:
     SEQUENCE (2 elem)
       OBJECT IDENTIFIER 1.3.6.1.4.1.311.2.1.12 spcSpOpusInfo (Microsoft code signing)
       SET (1 elem)
         SEQUENCE (0 elem)
     SEQUENCE (2 elem)
       OBJECT IDENTIFIER 1.2.840.113549.1.9.3 contentType (PKCS #9)
       SET (1 elem)
         OBJECT IDENTIFIER 1.3.6.1.4.1.311.2.1.4 spcIndirectDataContext (Microsoft code signing)
     SEQUENCE (2 elem)
       OBJECT IDENTIFIER 1.3.6.1.4.1.311.2.1.11 spcStatementType (Microsoft code signing)
       SET (1 elem)
         SEQUENCE (1 elem)
           OBJECT IDENTIFIER 1.3.6.1.4.1.311.2.1.21 individualCodeSigning (Microsoft)
     SEQUENCE (2 elem)
       OBJECT IDENTIFIER 1.2.840.113549.1.9.4 messageDigest (PKCS #9)
       SET (1 elem)
         OCTET STRING (20 byte) 7C87D331C6E62C0EC840BC23CA63FBC2CE68586F
```

 - Allow the program name and/or more info to be missing from SpcSpOpusInfo.
   Examples:
```
     8a364e0881fd7201cd6f0a0ff747451c9b93182d5699afb28ad8466f7f726660:
     SEQUENCE (2 elem)
       OBJECT IDENTIFIER 1.3.6.1.4.1.311.2.1.12 spcSpOpusInfo (Microsoft code signing)
       SET (1 elem)
         SEQUENCE (0 elem)

     0059fb3f225c5784789622eeccb97197d591972851b63d59f5bd107ddfdb7a21:
     SEQUENCE (2 elem)
       OBJECT IDENTIFIER 1.3.6.1.4.1.311.2.1.12 spcSpOpusInfo (Microsoft code signing)
       SET (1 elem)
         SEQUENCE (1 elem)
           [0] (1 elem)
             [0] (22 byte) 0047006F0054006F0020004F00700065006E00650072
```

 - I removed the #if 0 ContentInfo from being populated, and the code worked
   with all of the test samples I used... Is there another reason this code
   is commented out?

 - The authenticated_attribytes content_name_ member now gets populated. This
   was commented out with a TODO by it... Is there a reason this field wasn't
   being populated?

 - When the SignerInfo is printed, it will now show 'N/A' if no value was
   parsed out from the executable
  • Loading branch information
recvfrom committed Jul 9, 2019
1 parent 91eb478 commit 535623d
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 161 deletions.
18 changes: 15 additions & 3 deletions src/PE/signature/AuthenticatedAttributes.cpp
Expand Up @@ -50,10 +50,22 @@ void AuthenticatedAttributes::accept(Visitor& visitor) const {

std::ostream& operator<<(std::ostream& os, const AuthenticatedAttributes& authenticated_attributes) {
constexpr uint8_t wsize = 30;
std::string content_type = authenticated_attributes.content_type();
if (content_type.empty()) {
content_type = "N/A";
}
std::string program_name = u16tou8(authenticated_attributes.program_name());
if (program_name.empty()) {
program_name = "N/A";
}
std::string url = authenticated_attributes.more_info();
if (url.empty()) {
url = "N/A";
}
os << std::hex << std::left;
os << std::setw(wsize) << std::setfill(' ') << "Content type: " << authenticated_attributes.content_type() << std::endl;
os << std::setw(wsize) << std::setfill(' ') << "Program name: " << u16tou8(authenticated_attributes.program_name()) << std::endl;
os << std::setw(wsize) << std::setfill(' ') << "URL : " << authenticated_attributes.more_info() << std::endl;
os << std::setw(wsize) << std::setfill(' ') << "Content type: " << content_type << std::endl;
os << std::setw(wsize) << std::setfill(' ') << "Program name: " << program_name << std::endl;
os << std::setw(wsize) << std::setfill(' ') << "URL : " << url << std::endl;

return os;
}
Expand Down
1 change: 1 addition & 0 deletions src/PE/signature/OIDToString.cpp
Expand Up @@ -997,6 +997,7 @@ const char* oid_to_string(const oid_t& oid) {
{ "1.3.6.1.4.1.311.2.2.1", "CTL_TRUSTED_CODESIGNING_CA_LIST" },
{ "1.3.6.1.4.1.311.2.2.2", "CTL_TRUSTED_CLIENT_AUTH_CA_LIST" },
{ "1.3.6.1.4.1.311.2.2.3", "CTL_TRUSTED_SERVER_AUTH_CA_LIST" },
{ "1.3.6.1.4.1.311.2.4.1", "SPC_NESTED_SIGNATURES" },
{ "1.3.6.1.4.1.311.3.2.1", "TIMESTAMP_REQUEST" },
{ "1.3.6.1.4.1.311.10.1", "CERT_TRUST_LIST" },
{ "1.3.6.1.4.1.311.10.1.1", "SORTED_CTL" },
Expand Down
299 changes: 141 additions & 158 deletions src/PE/signature/SignatureParser.cpp
Expand Up @@ -156,8 +156,11 @@ std::string SignatureParser::get_signed_data_digest_algorithms(void) {
ContentInfo SignatureParser::parse_content_info(void) {
VLOG(VDEBUG) << "Parse signed data - content info";

mbedtls_asn1_buf content_type_oid;
mbedtls_asn1_buf alg_oid;
int ret = 0;
size_t tag;
char oid_str[256] = { 0 };

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
Expand Down Expand Up @@ -185,9 +188,6 @@ ContentInfo SignatureParser::parse_content_info(void) {
throw corrupted("Signature corrupted");
}

this->p_ += tag; // skip
return content_info;
#if 0
// SpcAttributeTypeAndOptionalValue
// |_ SPC_PE_IMAGE_DATAOBJ
// |_ SpcPeImageData
Expand Down Expand Up @@ -270,7 +270,6 @@ ContentInfo SignatureParser::parse_content_info(void) {
this->p_ += tag;

return content_info;
#endif
}


Expand Down Expand Up @@ -352,166 +351,139 @@ AuthenticatedAttributes SignatureParser::get_authenticated_attributes(void) {
throw corrupted("Authenticated attributes corrupted");
}

// contentType (1.2.840.113549.1.9.3)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}

content_type_oid.tag = *this->p_;
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &content_type_oid.len, MBEDTLS_ASN1_OID)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}
content_type_oid.p = this->p_;

std::memset(oid_str, 0, sizeof(oid_str));
mbedtls_oid_get_numeric_string(oid_str, sizeof(oid_str), &content_type_oid);

VLOG(VDEBUG) << oid_str; // 1.2.840.113549.1.9.3 (PKCS #9 contentType)
if (std::string(oid_str) != "1.2.840.113549.1.9.3") {
throw corrupted("Authenticated attributes corrupted: Wrong Content type OID (" + std::string(oid_str) + ")");
}

this->p_ += content_type_oid.len;

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SET | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}

content_type_oid.tag = *this->p_;
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &content_type_oid.len, MBEDTLS_ASN1_OID)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}

content_type_oid.p = this->p_;

std::memset(oid_str, 0, sizeof(oid_str));
mbedtls_oid_get_numeric_string(oid_str, sizeof(oid_str), &content_type_oid);
VLOG(VDEBUG) << oid_str; // 1.2.840.113549.1.9.4
this->p_ += content_type_oid.len;
//authenticated_attributes.content_type_ = oid_str;

// TODO
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Signature corrupted");
}
this->p_ += tag;


// messageDigest (Octet string)
// |_ OID (PKCS #9 Message Disgest)
// |_ SET -> OCTET STING
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VLOG(VDEBUG) << "Parsing messageDigest (offset: "
<< std::dec << this->current_offset()
<< ")";

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}

content_type_oid.tag = *this->p_;
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &content_type_oid.len, MBEDTLS_ASN1_OID)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}
content_type_oid.p = this->p_;

std::memset(oid_str, 0, sizeof(oid_str));
mbedtls_oid_get_numeric_string(oid_str, sizeof(oid_str), &content_type_oid);
VLOG(VDEBUG) << oid_str << " (" << oid_to_string(oid_str) << ")"; // 1.2.840.113549.1.9.4
this->p_ += content_type_oid.len;

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SET | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag, MBEDTLS_ASN1_OCTET_STRING)) != 0) {
throw corrupted("Signature corrupted: Can't read 'ASN1_OCTET_STRING'");
}
authenticated_attributes.message_digest_ = {this->p_, this->p_ + tag};
this->p_ += tag;


// SpcSpOpusInfo
// |_ programName (utf16)
// |_ moreInfo
// ~~~~~~~~~~~~~~~~~~~~~~
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Signature corrupted");
}

content_type_oid.tag = *this->p_;

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &content_type_oid.len, MBEDTLS_ASN1_OID)) != 0) {
throw corrupted("Signature corrupted");
}

content_type_oid.p = this->p_;
std::memset(oid_str, 0, sizeof(oid_str));
mbedtls_oid_get_numeric_string(oid_str, sizeof(oid_str), &content_type_oid);
VLOG(VDEBUG) << oid_str; // 1.3.6.1.4.1.311.2.1.12 (SpcSpOpusInfoObjId)
this->p_ += content_type_oid.len;

// programName
// +++++++++++
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SET | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}


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 ((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;
uint8_t* p_end = this->p_ + tag;
while(this->p_ < p_end) {
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}

// 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";
}
content_type_oid.tag = *this->p_;
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &content_type_oid.len, MBEDTLS_ASN1_OID)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}
content_type_oid.p = this->p_;

authenticated_attributes.program_name_ = progname;
VLOG(VDEBUG) << "ProgName " << u16tou8(progname);
this->p_ += tag;
std::memset(oid_str, 0, sizeof(oid_str));
mbedtls_oid_get_numeric_string(oid_str, sizeof(oid_str), &content_type_oid);

// 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");
}
this->p_ += content_type_oid.len;

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SET | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
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");
if (std::string(oid_str) == "1.2.840.113549.1.9.3") {
// contentType
// |_ OID (PKCS #9 Message Digest)
// |_ SET -> OID
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VLOG(VDEBUG) << "Parsing contentType (offset: "
<< std::dec << this->current_offset()
<< ")";
content_type_oid.tag = *this->p_;
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &content_type_oid.len, MBEDTLS_ASN1_OID)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}
content_type_oid.p = this->p_;

std::memset(oid_str, 0, sizeof(oid_str));
mbedtls_oid_get_numeric_string(oid_str, sizeof(oid_str), &content_type_oid);
authenticated_attributes.content_type_ = oid_str;
this->p_ += content_type_oid.len;
continue;

} else if (std::string(oid_str) == "1.2.840.113549.1.9.4") {
// messageDigest (Octet string)
// |_ OID (PKCS #9 Message Digest)
// |_ SET -> OCTET STING
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
VLOG(VDEBUG) << "Parsing messageDigest (offset: "
<< std::dec << this->current_offset()
<< ")";
VLOG(VDEBUG) << oid_str << " (" << oid_to_string(oid_str) << ")"; // 1.2.840.113549.1.9.4

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag, MBEDTLS_ASN1_OCTET_STRING)) != 0) {
throw corrupted("Signature corrupted: Can't read 'ASN1_OCTET_STRING'");
}
authenticated_attributes.message_digest_ = {this->p_, this->p_ + tag};
this->p_ += tag;
continue;

} else if (std::string(oid_str) == "1.3.6.1.4.1.311.2.1.12") {
// SpcSpOpusInfo
// |_ programName (utf16)
// |_ moreInfo
// ~~~~~~~~~~~~~~~~~~~~~~
if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag,
MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}

if (tag == 0) {
VLOG(VDEBUG) << "No program name or more info specified ";
authenticated_attributes.program_name_ = u"";
authenticated_attributes.more_info_ = "";
continue;
}

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 ((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;

// 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;

if (this->p_ >= seq_end) {
VLOG(VDEBUG) << "No more info specified ";
authenticated_attributes.more_info_ = "";
continue;
}

// 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 ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag, MBEDTLS_ASN1_CONTEXT_SPECIFIC)) != 0) {
throw corrupted("Authenticated attributes corrupted");
}

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;

} else {
VLOG(VDEBUG) << "Skipping OID " << oid_str;
this->p_ += tag;
continue;
}
}

std::string more_info{reinterpret_cast<char*>(this->p_), tag}; // moreInfo
authenticated_attributes.more_info_ = more_info;
VLOG(VDEBUG) << more_info;
this->p_ += tag;

return authenticated_attributes;
}

Expand Down Expand Up @@ -587,10 +559,21 @@ SignerInfo SignatureParser::get_signer_info(void) {
VLOG(VDEBUG) << "Component ID: " << oid_str;
this->p_ += content_type_oid.len;

if ((ret = mbedtls_asn1_get_tag(&(this->p_), this->end_, &tag, MBEDTLS_ASN1_PRINTABLE_STRING)) != 0) {
if ( (p_end - this->p_) < 1 ) {
throw corrupted("Signer info corrupted");
}

if (*this->p_ != MBEDTLS_ASN1_UTF8_STRING &&
*this->p_ != MBEDTLS_ASN1_PRINTABLE_STRING &&
*this->p_ != MBEDTLS_ASN1_IA5_STRING) {
throw corrupted("Signer info corrupted");
}

this->p_ += 1;

if ((ret = mbedtls_asn1_get_len(&(this->p_), this->end_, &tag)) != 0) {
throw corrupted("Signer info corrupted");
}
std::string name{reinterpret_cast<char*>(this->p_), tag};
issuer_name.emplace_back(oid_str, name);
VLOG(VDEBUG) << "Name: " << name;
Expand Down

0 comments on commit 535623d

Please sign in to comment.